USB Endpoints in Linux

USB endpoints are the logical entities that make up one-way communication channels in a USB system.  Endpoints are unidirectional and the direction is always with respect to the host.  IN endpoints are for transfers to the host from the device (host<-device), and OUT endpoints are for transfers from the host to the device (host->device).  Refer to the USB specification for more information about endpoints, or try this reference from BeyondLogic on endpoint types.

In the Linux kernel the struct usb_host_endpoint, defined in <linux/usb.h>, is used to describe a USB endpoint.

struct usb_host_endpoint {
   struct usb_endpoint_descriptor    desc;
   struct usb_ss_ep_comp_descriptor  ss_ep_comp;
   struct list_head    urb_list;
   void        *hcpriv;
   struct ep_device    *ep_dev;  /* For sysfs info */
   unsigned char *extra;   /* Extra descriptors */
   int extralen;
   int enabled;
};

Among other things, it contains the head of a linked list of URBs (to be discussed later) and a struct usb_endpoint_descriptorwhich is used to describe the endpoint in terms of the USB spec.  In <linux/usb/ch9.h>, the definition of the endpoint descriptor can be seen:

struct usb_endpoint_descriptor {
   __u8  bLength;
   __u8  bDescriptorType;
   __u8  bEndpointAddress;
   __u8  bmAttributes;
   __le16 wMaxPacketSize;
   __u8  bInterval;
   /* NOTE:  these two are _only_ in audio endpoints. */
   /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
   __u8  bRefresh;
   __u8  bSynchAddress;
} __attribute__ ((packed));

In order to determine the direction of the endpoint, a few helper functions are available:

#define USB_ENDPOINT_DIR_MASK   0x80
...
static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
{
  return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
}
...
static inline int usb_endpoint_dir_out(
        const struct usb_endpoint_descriptor *epd)
{
  return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
}

This could all be done manually with the USB_ENDPOINT_DIR_MASK mask itself, but in order to guard against futures changes, it is best to use the abstractions and helper functions defined in the kernel.  The endpoint transfer type can also be retrieved with helper functions.

#define USB_ENDPOINT_XFERTYPE_MASK  0x03
...
static inline int usb_endpoint_type(const struct usb_endpoint_descriptor *epd)
{
  return epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
}
...
static inline int usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd)
{
  return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
    USB_ENDPOINT_XFER_BULK);
}
...
static inline int usb_endpoint_xfer_control(const struct usb_endpoint_descriptor *epd)
{
  return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
    USB_ENDPOINT_XFER_CONTROL);
}
...
static inline int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd)
{
  return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
    USB_ENDPOINT_XFER_INT);
}
...
static inline int usb_endpoint_xfer_isoc(const struct usb_endpoint_descriptor *epd)
{
  return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
    USB_ENDPOINT_XFER_ISOC);
}

Next the Linux kernel representations of USB Interfaces and USB Configurations will be examined.