====== Design of the USB subsystem ====== The initial plan was to follow linux kernel with this. But it's come out quite differently due to our assumptions which simplify things. ==== Some important Data Structures ==== === Host Controller === The most important one is the abstraction of the USB host controller itself. It's pretty similar to how the Linux kernel does it, but simpler. A ''struct usb_hcd'' describes a generic host controller device. It looks like this. struct usb_hcd { struct hc_driver *driver; unsigned long res_addr; unsigned long res_size; void *hcpriv; }; * hcpriv is a host controller private object for example, ''struct uhci_hcd'' defined for the UHCI host controller. * res_addr/res_size is the Memory/IO resource region containing the registers of the device. === USB Device === struct usb_device { unsigned int devnum; enum usb_device_speed speed; struct usb_device_descriptor descriptor; struct usb_host_endpoint ep_0_in; struct usb_hcd *hcd; }; * ''devnum'' is an address given to the USB device at configuration time. Its assigned by the usb subsystem. * The ''descriptor'' contains information about the USB device and is obtained by querying the device for it during configuration time. Device Descriptors are one of the standard descriptors defined in chapter 9 of the USB reference manual. * ''ep_0_in'' is the IN endpoint numbered zero which is found in all devices. This is used to obtain information such as device descriptor from the USB device. * ''hcd'' is the host controller object to which the device is attached to. Talking to the device is dont in a host controller driver specific way. To delegate it to the hc_driver, we need to maintain a reference for the 'struct usb_hcd' object in ''struct usb_device.'' === Host Controller driver === struct hc_driver { int (*enqueue_urb)(struct usb_hcd *hcd, struct urb *urb); }; This has only one method at the moment. This is used by the USB subsystem to tell the host controller driver to transfer data in a device specific way. === USB request Block (URB) === Drivers communicate with USB devices using an URB. It represents a single request to transfer data to or from the device. It should be host controller agnostic. struct urb { void *hcpriv; struct usb_device *udev; struct usb_host_endpoint *ep; void *transfer_buffer; unsigned long transfer_dma; unsigned int transfer_buffer_length; unsigned int actual_length; unsigned char *setup_packet; unsigned long setup_dma; int type; }; * hcpriv is needed to maintain host controller specific information related to the hub. For example in case of the UHCI host controller, we need to maintain the list of TDs (Transfer Descriptors) associated with a particular URB and similar other house keeping information which are needed to keep track of the after submission. * ''ep'' is the endpoint to which the communication is targeted to. * ''type'' is either control or bulk (only two are supported at the moment). * the DMA addresses of the transfer buffers is obtained via a call to virt_to_bus() on a piece of buffer allocated by malloc_dma. The ''setup'' packet is used in control transactions to send control commands to the usb device. Everything begins in the ''probe'' method of the Host Controller PCI driver which is a non-generic piece of code. The driver looks out for devices on its root hub and tells the USB subsystem about that by creating ''struct usb_device'' objects for every usb device it finds. Then the usb devices and their drivers are matched by calling the probe method. === UHCI Host Controller Driver operation === A descriptor for the UHCI host controller device looks like this, struct uhci_hcd { unsigned int rh_numports; unsigned long io_addr; unsigned int *frame; unsigned int frame_dma_handle; struct uhci_qh *fs_control_skelqh; struct uhci_qh *bulk_skelqh; }; * rh_numports is the number of ports on the root hub. * Once we're in the probe method of our PCI driver, we initialize the Host controller's registers with the FrameList address stored in frame_dma_handle. We create two skeleton QH (queue heads), one for Control transactions and one for Bulk Transactions and store it in ''fs_control_skelqh'' and ''bulk_skelqh'' and link them onto the first Frame List entry. The remaining 1023 frame list entries are marked invalid as they are unused. =='enqueue_urb' method== This is called by the USB subsystem when it needs to transfer information to a device. Here we create TDs for the transaction and link it into the appropriate skeleton QH. For every URB, we maintain a UHCI specific ''hcpriv'' object of type ''struct uhci_urb_priv''. struct uhci_urb_priv { struct list_head td_list; struct uhci_td *first_td; }; This is used to contain the list of TDs the transaction was broken into. (We need to break transactions into TDs because there's a limitation on the maximum data packet that can be transferred in one go). This also contains a pointer to the last TD. This can be used to check for completion of the whole transaction. ==== Using this in device drivers ==== Now let's see how all these fit together. Typically the usb_device will be contained as a reference in a descriptor representing a functional device (most likely a ''struct net_device''). To transfer data, we need to create a URB of the required type and fill its fields with information. It is then submitted to the USB subsystem by calling ''usb_submit_urb''. This is an asynchronous operation. The USB subsystem looks at the URB and identifies the device to which the transfer is targeted to. Once it has a ''struct usb_device'' in hand, it can call the device specific transfer method in this way. int usb_submit_urb(struct urb *urb) { return urb->udev->hcd->driver->enqueue_urb(urb->udev->hcd, urb); }