gPXE USB Device Driver Guide

This is almost exactly similar to the way in which Linux USB device drivers are written, except that it is simplified greatly. For example, we don't have the ability to change the device configuration, or choose an alternative setting for an interface. We're not entirely sure if we would need it. But we never had devices that support multiple configurations, or alternate settings. So if anyone has such a device and wants to get it to work with gPXE, we are willing to work with you.

A USB device driver is described by struct usb_driver defined in <gpxe/usb.h>. Similar to how a PCI driver is defined, an USB driver is defined by creating an instance of struct usb_driver with fields initialized appropriately. The USB_ROM macro used to specify the device is defined as,

#define USB_ROM( _vendor, _device, _name, _description ) {	\
	.vendor = _vendor,					\
	.device = _device,					\
	.name = _name,						\
}

Here's an example taken from drivers/net/usb/asix.c:

static struct usb_device_id asix_88178_ids[] = {
	USB_ROM (0x0b95, 0x1780, "asix", "ASIX AX88178"),
	USB_ROM (0x1737, 0x0039, "asix", "Linksys USB1000"),
	USB_ROM (0x04bb, 0x0939, "asix", "IO-DATA ETG-US2"),
	USB_ROM (0x050d, 0x5055, "asix", "Belkin F5D5055"),
};
 
struct usb_driver asix_88178_usb_driver __usb_driver = {
	.ids = asix_88178_ids,
	.id_count = (sizeof(asix_88178_ids) / sizeof(asix_88178_ids[0])),
	.probe = asix_88178_probe,
};

Next, the probe method needs to be provided. As expected, the probe method is called for every device the usbcore thinks your driver can handle. The device's control endpoint 0 is usable at this instant. This can be used to configure the device.

Ex :

int asix_88772_probe(struct usb_device *udev,
			const struct usb_device_id *id)
{
	struct net_device *netdev;
	struct asix *asix;
	unsigned int i;
	uint8_t buf[ETH_ALEN];
	uint32_t phyid;
	int ret, embd_phy;
	uint16_t rx_ctl;
 
	netdev = alloc_etherdev(sizeof(*asix));
	netdev_init(netdev, &asix_operations);
        ...
        ..

Now let's have a brief look at the struct usb_device defined in include/gpxe/usb.h.

struct usb_device {
	struct device			dev;
 
	unsigned int 			devnum;		/* Device address */
 
	enum usb_device_speed		speed;
	struct usb_device_descriptor 	descriptor;
 
	struct usb_host_endpoint 	ep_0_in;
	struct usb_host_endpoint	ep_0_out;
 
	/* Arbitrary Limit */
#define USB_MAX_ENDPOINT	8
	struct usb_host_endpoint 	*endpoints[USB_MAX_ENDPOINT];
	unsigned int			num_endpoints;
 
	struct usb_hcd			*hcd;		
	struct usb_driver		*driver;
	const char 			*driver_name;
 
	int				toggle;
	void				*priv;
	struct list_head		list;
};

The fields interesting to a device driver writer is the num_endpoints and the endpoints array. The endpoints array contains num_endpoints number of entries of type usb_host_endpoint. Information about any endpoint can be obtained from its descriptor in struct usb_host_endpoint defined as,

struct usb_host_endpoint {
	struct usb_endpoint_descriptor	desc;
 
	void *hcpriv;
};

For example, you could probe for endpoints in this fashion (taken from asix driver)

for(i = 0;i < udev->num_endpoints; i++) {
	if (usb_ep_xfertype(udev->endpoints[i]) == USB_ENDPOINT_XFER_BULK &&
			usb_ep_dir(udev->endpoints[i]) == USB_DIR_IN)
		asix->in = udev->endpoints[i];
 
		if (usb_ep_xfertype(udev->endpoints[i]) == USB_ENDPOINT_XFER_BULK &&
			usb_ep_dir(udev->endpoints[i]) == USB_DIR_OUT)
		asix->out = udev->endpoints[i];
}

USB Messages

CUrrently only control and bulk transfer types are supported in gPXE. The data to be sent to an endpoint is wrapped in a urb (USB Request Block). The urb is then handed over to the usbcore for transmission to the endpoint via usb_submit_urb. This function returns immediately after enqueuing the URB for transfer. The status of the URB is checked at a later stage using usb_urb_status which returns one of IN_PROGRESS, ERROR or DONE. Currently there is no support for cancelling a submitted URB. As a special case control messages are often sent synchronously. For that reason, a wrapper named usb_control_msg is provided which returns after the request is complete. The prototypes of these functions can be found in include/gpxe/usb.h.

For a full working example, take a look at the asix driver - drivers/net/usb/asix.c


QR Code
QR Code soc:2008:balajirrao:notes:usb_driver_guide (generated for current page)