Prologue - 20110611

Picked up a toy from a department store sale: a Webmail Notifier. Brand is not mentioned, but the box says it supports Linux, besides almost all MS OSes since Win98, and MAC (which I assumed meant MacOS(X)). Sadly, upon mounting the baby CD-ROM that came with it, I found that only a MS binary and a text document were present. I had to find my Linux support somewhere else. Luckily I had anticipated to do so anyway, since proprietary software cramps my style anyway. I had already envisioned yearning for more freedom in usage than any enclosed software could offer. It took only a matter of minutes, perhaps even tens of seconds, to locate an appropriate software project online. USB mail notifier driver was an end to my initial search. Because it depends on PyUSB, I merged that from an unstable Gentoo ebuild. Naturally, since no stable ebuilds existed, the latest available version was installed (pyusb-1.0.0_alpha1). The python script from the Google project however, turned out to be incompatible with this version. But since it lacked proper error reporting (the script is tiny, after all) I went on to look for alternatives online. What I should have done is try another version of PyUSB: several hours later I merged pyusb-0.4.3 and the setcolor.py script started to operate as expected :) Now I had seen the USB LED device in action and naturally toyed around with its different colours for a while. I changed the timeout set in the setcolor.py script so I could make the device blink at a higher frequency. Later that night I used gnubiff to activate it upon detecting new e-mail.

Development goals - 20110612

- dissect setcolor.py script;

- develop personal libusb driver (based on bash, perhaps);

- research general libusb to kernel module development;

- expand usb_led kernel module by Greg Kroah-Hartman;

- develop personal kernel module

Findings

libusb.org:
"There are currently two separate core projects, with non-compatible APIs:

- libusb-1.0

- libusb-0.1"

That must be the reason the setcolor.py was compatible with pyusb-0.4.3, yet incompatible with pyusb-1.0.0a!

setcolor.py - 20110612

#!/usr/bin/env python

import usb
import sys
import time

class MailNotifier:
	
	def makeData(self, color):
		return (color, 0, 0, 0, 0)
This makeData function can be omitted by changing the second argument to interruptWrite below.
	def __init__(self):
		self.dev=UsbDevice(0x1294, 0x1320)
The idVendor is 1294 (Riso Kagaku Corp.) and the idProduct is 1320. These are passed as arguments to the __init__ section of the UsbDevice class.
		self.dev.open()
		self.dev.handle.reset()
		
	def setColor(self, color):
		self.dev.handle.interruptWrite(0x02, self.makeData(color), 1000)
The first argument is the EP 2 OUT address (0x02).
Here self.makeData(color) can be replaced by (color, 0, 0, 0, 0) or even (int(sys.argv[1]),0).
There is a wait/timeout number at the end of the line. This should at least correspond to bInterval (10) in `lsusb` output, although semi-reliable output is available at 5! (1000 here means 1,000ms = 1s)
The amount of data to transfer is equal to wMaxPacketSize (5 bytes). Since an unsigned char holds 1 byte, an unsigned char array of 5 elements is needed.
class UsbDevice:
	def __init__(self, vendor_id, product_id):
		busses = usb.busses()
		self.handle = None
		count = 0
		for bus in busses:
				devices = bus.devices
				for dev in devices:
					if dev.idVendor==vendor_id and dev.idProduct==product_id:
						self.dev = dev
						self.conf = self.dev.configurations[0]
						self.intf = self.conf.interfaces[0][0]
						self.endpoints = []
						for endpoint in self.intf.endpoints:
							self.endpoints.append(endpoint)
						return
		sys.stderr.write("No mail notifier found\n")

	def open(self):
		if self.handle:
			self.handle = None
		try:
			self.handle = self.dev.open()
			self.handle.detachKernelDriver(0)
			self.handle.detachKernelDriver(1)
			self.handle.setConfiguration(self.conf)
			self.handle.claimInterface(self.intf)
			self.handle.setAltInterface(self.intf)
			return True
		except:
			return False

def main(argv):
Accept argument sys.argv as argument argv.
	if len(argv) != 2:
Test whether exactly one argument is passed after the script name. (accepts both `./setcolor.py <arg>` and `python setcolor.py <arg>`)
		sys.stderr.write("Usage : %s color_number\n" % argv[0])
	else:
		m = MailNotifier()
Creates a new instance of the class MailNotifier and assign this object to the local variable m.
		m.setColor(int(argv[1]))
Sets attribute setColor to the first argument passed after the script name, interpreted/tested as an integer.
if __name__=="__main__":
	main(sys.argv)
Pass argv attribute of sys variable (sys.argv) to function main.

dreamincode.com: libusb-1.0 bulk write example - 20110613

#include <iostream>
#include <libusb.h>

using namespace std;

int main() {
In order to utilize command line arguments, change the above line to: int main(int argc, char* argv[]) {.
	libusb_device **devs;			//pointer to pointer of device, used to retrieve a list of devices
	libusb_device_handle *dev_handle;	//a device handle
	libusb_context *ctx = NULL;		//a libusb session
	int r;					//for return values
	ssize_t cnt;				//holding number of devices in list
	r = libusb_init(&ctx);			//initialize the library for the session we just declared
	if(r < 0) {
		cout<<"Init Error "<<r<<endl;	//there was an error
		return 1;
	}
	libusb_set_debug(ctx, 3);		//set verbosity level to 3, as suggested in the documentation

	cnt = libusb_get_device_list(ctx, &devs);	//get the list of devices
	if(cnt < 0) {
		cout<<"Get Device Error"<<endl;	//there was an error
		return 1;
	}
	cout<<cnt<<" Devices in list."<<endl;

	dev_handle = libusb_open_device_with_vid_pid(ctx, 5118, 7424);	//these are vendorID and productID I found for my usb device
	if(dev_handle == NULL)
		cout<<"Cannot open device"<<endl;
	else
		cout<<"Device Opened"<<endl;
	libusb_free_device_list(devs, 1);			//free the list, unref the devices in it
Above is the initialization of the USB device.
	unsigned char *data = new unsigned char[4];		//data to write
	data[0]='a';data[1]='b';data[2]='c';data[3]='d';	//some dummy values
Instead of dummy values, we could set useful values by utilizing argv.
	int actual;																						//used to find out how many bytes were written
	if(libusb_kernel_driver_active(dev_handle, 0) == 1) {	//find out if kernel driver is attached
		cout<<"Kernel Driver Active"<<endl;
		if(libusb_detach_kernel_driver(dev_handle, 0) == 0)	//detach it
			cout<<"Kernel Driver Detached!"<<endl;
	}
	r = libusb_claim_interface(dev_handle, 0);		//claim interface 0 (the first) of device (mine had jsut 1)
	if(r < 0) {
		cout<<"Cannot Claim Interface"<<endl;
		return 1;
	}
	cout<<"Claimed Interface"<<endl;
	
	cout<<"Data->"<<data<<"<-"<<endl;			//just to see the data we want to write : abcd
	cout<<"Writing Data..."<<endl;
	r = libusb_bulk_transfer(dev_handle, (2 | LIBUSB_ENDPOINT_OUT), data, 4, &actual, 0);	//my device's out endpoint was 2, found with trial- the device had 2 endpoints: 2 and 129
Since the Webmail Notifier responds to interrupt transfers, change the above command to libusb_interrupt_transfer.
	if(r == 0 && actual == 4)				//we wrote the 4 bytes successfully
		cout<<"Writing Successful!"<<endl;
	else
		cout<<"Write Error"<<endl;
Below the USB device is released and the data array cleared.
	r = libusb_release_interface(dev_handle, 0);		//release the claimed interface
	if(r!=0) {
		cout<<"Cannot Release Interface"<<endl;
		return 1;
	}
	cout<<"Released Interface"<<endl;

	libusb_close(dev_handle);				//close the device we opened
	libusb_exit(ctx);					//needs to be called to end the session

	delete[] data;						//delete the allocated memory for data
	return 0;
}

webmailnotifier.cpp - 20110618

// ------------------------------------------------------------------------------------------------------
// This code is heavily based on Anarion's, see http://www.dreamincode.net/forums/user/221503-anarion/ .
// FWIW: as far as I am concerned, this code is free to be used non-commercially. -Martin Paulus
// ------------------------------------------------------------------------------------------------------
// compile with:
// g++ -o binary source.cpp -lusb-1.0
// ------------------------------------------------------------------------------------------------------
// Includes and variable declarations
#include <iostream>
#include <libusb-1.0/libusb.h>
#include <cstdlib>

using namespace std;

int main(int argc, char* argv[]) {
	libusb_device **devs;
	libusb_device_handle *dev_handle;
	libusb_context *ctx = NULL;
	int r;
	ssize_t cnt;
// ------------------------------------------------------------------------------------------------------
// Command line argument sanity checking

	if(argv[1] == NULL) {
		cout<<"Usage: webmailnotifier [0-7]"<<endl;
		return 1;
	}
	if(isdigit(*argv[1])) {
//		cout<<"Argument is digit."<<endl;
//		cout<<"Argument: "<<atoi(argv[1])<<endl;
		if(atoi(argv[1])>7) {
			cout<<"Please use 0=< digit =<7"<<endl;
			return 1;
		}
	} else {
		cout<<"Argument is not a digit, buddy."<<endl;
		return 1;
	}
// ------------------------------------------------------------------------------------------------------
// LibUSB device initialization

	r = libusb_init(&ctx);
	if(r < 0) {
		cout<<"Init Error "<<r<<endl;
		return 1;
	}
//	libusb_set_debug(ctx, 3);

	cnt = libusb_get_device_list(ctx, &devs);
	if(cnt < 0) {
		cout<<"Get Device Error"<<endl;
		return 1;
	}
//	cout<<cnt<<" Devices in list."<<endl;

	dev_handle = libusb_open_device_with_vid_pid(ctx, 4756, 4896);
	if(dev_handle == NULL)
		cout<<"Cannot open device"<<endl;
//	else
//		cout<<"Device Opened"<<endl;
	libusb_free_device_list(devs, 1);

	if(libusb_kernel_driver_active(dev_handle, 0) == 1) {
		cout<<"Kernel Driver Active"<<endl;
		if(libusb_detach_kernel_driver(dev_handle, 0) == 0)
			cout<<"Kernel Driver Detached!"<<endl;
	}
	r = libusb_claim_interface(dev_handle, 0);
	if(r < 0) {
		cout<<"Cannot Claim Interface"<<endl;
		return 1;
	}
//	cout<<"Claimed Interface"<<endl;
// ------------------------------------------------------------------------------------------------------
// LibUSB write data to device

	int actual;
	unsigned char *data = new unsigned char[sizeof(int)];

	*data = atoi(argv[1]);

//	cout<<"Writing Data... ";
	r = libusb_interrupt_transfer(dev_handle, 0x02, data, sizeof(data), &actual, 10);
//	if(r == 0 && actual == (sizeof(data)))
//		cout<<"Successful!"<<endl;
//	else
//		cout<<"Failed."<<endl;
// ------------------------------------------------------------------------------------------------------
// LibUSB device cleanup
	
	r = libusb_release_interface(dev_handle, 0);
	if(r != 0) {
		cout<<"Cannot Release Interface"<<endl;
		return 1;
	}
//	cout<<"Released Interface"<<endl;

	libusb_close(dev_handle);
	libusb_exit(ctx);
	delete[] data;
	return 0;
}

List of references

usbmailnotifier
gnubiff
libusb
linuxforums.org: developing usb device drivers userspace using libusb
matthias.vallentin.net: writing a linux kernel driver for an unknown usb device
dreamincode.net - Anarion: introduction to using libusb-1.0
command line arguments in c++
c++ documentation: pointers