USB

Check this post once in a while because I'll publish some tips on writing function drivers using the composite gadget framework :-)

The main documentation, still and will always be, the source code itself, but a simple howto and reusable basic function driver structure won't hurt to check and it'll make some stuff quite easier, right?

On current USB Composite Framework, we already have the TTY glue code there. So any serial-like protocol should use u_serial.[ch] and serial.c. This makes the function drivers way simpler. Let's take a look at one example:

For writing a usb gadget driver you need to provide:

  1. struct usb_gadget_driver;
  2. bind();
  3. unbind();
  4. sleep();
  5. glue code to chosen protocol/layer (serial, ethernet, scsi);
  6. at least 6 different descriptors (you normally use at least 10);
  7. entire buffer handler (alloc, release, clear, get data available and space available, etc);
  8. rest of usb code (request handler, resource allocating and releasing, suspend and resume for PM, etc);
  9. module_init/exit();

When using the TTY glue code, you have a lot less to provide. TTY glue code already gives you:

  1. usb_device_descriptor;
  2. usb_config_descriptor;
  3. tty glue code (of course :-p);
  4. module_init/exit();
  5. buffer handler;

So basically you just provide your usb_interface_descriptors and usb_endpoint_descriptors and a method to bind you usb_function into gadget serial.

It'd look something like:

struct ep_descs {
	struct usb_endpoint_descriptor	*in;
	struct usb_endpoint_descriptor	*out;
};
 
struct my_function {
	struct gserial			port;
	u8				id;
	u8				port_num;
 
	struct usb_descriptor_header	**fs_function;
	struct obex_ep_descs		fs;
	struct usb_descriptor_header	**hs_function;
	struct obex_ep_descs		hs;
 
	struct usb_cdc_line_coding	port_line_coding;
	u16				port_handshake_bits;
};
 
static inline struct my_function *func_to_myfunc(struct usb_function *f)
{
	return container_of(f, struct my_function, port.func);
}
 
#define STRING_IDX	1
 
static struct usb_string string_defs[] = {
	[STRING_IDX].s		= "My Function",
	{  },	/* end of list */
};
 
static struct usb_gadget_strings string_table = {
	.language		= 0x0409,	/* en-US */
	.strings		= string_defs,
};
 
static struct usb_gadget_strings *strings[] = {
	&string_table,
	NULL,
};
 
static struct usb_interface_descriptor
intf_desc __initdata = {
	.bLength		= sizeof(intf_desc),
	.bDescriptorType	= USB_DT_INTERFACE,
	.bInterfaceNumber	= 0,
 
	.bAlternateSetting	= 0,
	.bNumEndpoints		= 2,
	.bInterfaceClass	= USB_CLASS_??,     /* Check include/linux/usb/*.h */
	.bInterfaceSubClass	= USB_CDC_SUBCLASS_??,
};
 
/* Highspeed support */
static struct usb_endpoint_descriptor
hs_ep_out_desc __initdata = {
	.bLength		= USB_DT_ENDPOINT_SIZE,
	.bDescriptorType	= USB_DT_ENDPOINT,
 
	.bEndpointAddress	= USB_DIR_OUT,
	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize		= __constant_cpu_to_le16(512),
};
 
static struct usb_endpoint_descriptor
hs_ep_in_desc __initdata = {
	.bLength		= USB_DT_ENDPOINT_SIZE,
	.bDescriptorType	= USB_DT_ENDPOINT,
 
	.bEndpointAddress	= USB_DIR_IN,
	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize		= __constant_cpu_to_le16(512),
};
 
static struct usb_descriptor_header *hs_function[] __initdata = {
	(struct usb_descriptor_header *) &intf_desc,
	(struct usb_descriptor_header *) &hs_ep_in_desc,
	(struct usb_descriptor_header *) &hs_ep_out_desc,
	NULL,
};
 
/* Fullspeed support */
static struct usb_endpoint_descriptor
fs_ep_out_desc __initdata = {
	.bLength		= USB_DT_ENDPOINT_SIZE,
	.bDescriptorType	= USB_DT_ENDPOINT,
 
	.bEndpointAddress	= USB_DIR_OUT,
	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize		= __constant_cpu_to_le16(512),
};
 
static struct usb_endpoint_descriptor
fs_ep_in_desc __initdata = {
	.bLength		= USB_DT_ENDPOINT_SIZE,
	.bDescriptorType	= USB_DT_ENDPOINT,
 
	.bEndpointAddress	= USB_DIR_IN,
	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize		= __constant_cpu_to_le16(512),
};
 
static struct usb_descriptor_header *fs_function[] __initdata = {
	(struct usb_descriptor_header *) &intf_desc,
	(struct usb_descriptor_header *) &fs_ep_in_desc,
	(struct usb_descriptor_header *) &fs_ep_out_desc,
	NULL,
};
 
static void my_func_complete_set_line_coding(struct usb_ep *ep,
		struct usb_request *req)
{
        [...]
}
 
static int my_func_setup(struct usb_function *f,
		const struct usb_ctrlrequest *ctrl)
{
        [...]
 
        return 0;
}
 
static int my_func_set_alt(struct usb_function *f,
		unsigned intf, unsigned alt)
{
        [...]
 
        return 0;
}
 
static void my_func_disable(struct usb_function *f)
{
        [...]
}
 
static int __init
my_func_bind(struct usb_configuration *c,
		struct usb_function *f)
{
        [...]
 
        return 0;
}
 
static void
my_func_unbind(struct usb_configuration *c,
		struct usb_function *f)
{
        [...]
}
 
/* This function's prototype should be added to u_serial.h and get
 * called by serial.c:serial_bind_config()
 */
int __init my_func_bind_config(struct usb_configuration *c,
		u8 port_num)
{
        struct my_function *my;
        [...]
	/* allocate and initialize one new instance */
	my = kzalloc(sizeof *my, GFP_KERNEL);
	if (!my)
		return -ENOMEM;
 
	my->port_num = port_num;
	my->port.func.name = "my_func";
	my->port.func.strings = my_strings;
	/* descriptors are per-instance copies */
	my->port.func.bind = my_bind;
	my->port.func.unbind = my_unbind;
	my->port.func.set_alt = my_set_alt;
	my->port.func.setup = my_setup;
	my->port.func.disable = my_disable;
 
	status = usb_add_function(c, &my->port.func);
	if (status)
		kfree(my);
 
	return status;
}

Another small note: Your struct usb_function (in case of serial function drivers) will be inside my_function->port.func. Just initialize it's function pointers like shown in my_func_bind_config().

There are some really useful function in composite framework, but these I'll document later :-p

VN:F [1.9.0_1079]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.0_1079]
Rating: 0 (from 0 votes)

Leave a Reply