gadget就是指一些比較雜的小設備,gadget類的一定是作爲usb device用。
前面一篇提到usb作爲device的情況,註冊完成後,導出usb_gadget_probe_driver函數後就完了。
這個函數就是爲gadget驅動準備的,這就要從gadget/android.c開始分析,先從它的init函數看起:
static int __init init(void)
{
struct android_dev *dev;
int err;
。。。。。。
android_class = class_create(THIS_MODULE, "android_usb");
if (IS_ERR(android_class))
return PTR_ERR(android_class);
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->functions = supported_functions;
INIT_LIST_HEAD(&dev->enabled_functions);
INIT_WORK(&dev->work, android_work);
err = android_create_device(dev);
if (err) {
class_destroy(android_class);
kfree(dev);
return err;
}
_android_dev = dev;
/* Override composite driver functions */
composite_driver.setup = android_setup;
composite_driver.disconnect = android_disconnect;
return usb_composite_probe(&android_usb_driver, android_bind);
}
supported_functions代表了gadget驅動支持的類型,如下:
static struct android_usb_function *supported_functions[] = {
&adb_function,
&acm_function,
&mtp_function,
&ptp_function,
&rndis_function,
&mass_storage_function,
&accessory_function,
NULL
};
他們分別代表adb設備、acm(串口設備)、mtp(ptp的擴展)、ptp(圖片傳輸設備)、rndis(網絡設備)、U盤設備、accessory(其他一些小附件)。
不管上述何種設備,都是把它們模擬成一個usb從設備來看待,所以必須要有端點0作爲控制傳輸、還要有其它非零端點用來傳輸數據。
剛好A10 CPU的usb otg 口有一個控制傳輸端點,4個批量傳輸端點和一箇中斷傳輸端點,可以符合上述的要求。
所以android.c、composite.c 的作用就是依據USB協議模擬出端點0、設備描述符等一個usb設備枚舉過程所要求的功能;至於這個usb設備具體做什麼,那就是由接口描述符和端點的功能決定,所以f_mtp.c、f_accessory.c、f_mass_storage.c、f_adb.c、f_acm.c等文件所做的事情的就是實現具體接口和這個接口下的端點的具體功能。
android_usb_driver代表了一個設備描述符所要具備的信息和響應的動作:
static struct usb_composite_driver android_usb_driver = {
.name = "android_usb",
.dev = &device_desc,
.strings = dev_strings,
.unbind = android_usb_unbind,
};
device_desc類型爲usb_device_descriptor,定義如下:
static struct usb_device_descriptor device_desc = {
.bLength = sizeof(device_desc),
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = __constant_cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.idVendor = __constant_cpu_to_le16(VENDOR_ID),
.idProduct = __constant_cpu_to_le16(PRODUCT_ID),
.bcdDevice = __constant_cpu_to_le16(0xffff),
.bNumConfigurations = 1,
};
這就是一個設備描述符的信息。
接着usb_composite_probe註冊一個usb_composite_driver到composite.c 中,如果這一步能註冊成功,那麼gadget驅動就已經準備好了,隨時可以相應主機的請求:
int usb_composite_probe(struct usb_composite_driver *driver,
int (*bind)(struct usb_composite_dev *cdev))
{
if (!driver || !driver->dev || !bind || composite)
return -EINVAL;
if (!driver->name)
driver->name = "composite";
if (!driver->iProduct)
driver->iProduct = driver->name;
composite_driver.function = (char *) driver->name;
composite_driver.driver.name = driver->name;
composite = driver;
composite_gadget_bind = bind;
return usb_gadget_probe_driver(&composite_driver, composite_bind);
}
composite_driver代表一個gadget驅動:
static struct usb_gadget_driver composite_driver = {
.speed = USB_SPEED_HIGH,
.unbind = composite_unbind,
.setup = composite_setup,
.disconnect = composite_disconnect,
.suspend = composite_suspend,
.resume = composite_resume,
.driver = {
.owner = THIS_MODULE,
},
};
簡單的將driver和bind的值保存下來後調用。
usb_gadget_probe_driver函數就是在具體平臺中定義的。前面一篇文章說過,這是導出來給gadget驅動用的,它是在usb/sun4i_usb/udc/sw_udc.c中:
int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
。。。。。。
if (!bind || !driver->setup || driver->speed < USB_SPEED_FULL) {
DMSG_PANIC("ERR: Invalid driver: bind %p setup %p speed %d\n",
bind, driver->setup, driver->speed);
return -EINVAL;
}
。。。。。。
/* Bind the driver */
if ((retval = device_add(&udc->gadget.dev)) != 0) {
DMSG_PANIC("ERR: Error in device_add() : %d\n",retval);
goto register_error;
}
DMSG_INFO_UDC("[%s]: binding gadget driver '%s'\n", gadget_name, driver->driver.name);
if ((retval = bind (&udc->gadget)) != 0) {
DMSG_PANIC("ERR: Error in bind() : %d\n",retval);
device_del(&udc->gadget.dev);
goto register_error;
}
這裏才用device_add將設備添加到設備層,接着回調bind方法:
static int composite_bind(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev;
int status = -ENOMEM;
cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
if (!cdev)
return status;
spin_lock_init(&cdev->lock);
cdev->gadget = gadget;
set_gadget_data(gadget, cdev);
INIT_LIST_HEAD(&cdev->configs);
/* preallocate control response and buffer */
cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
if (!cdev->req)
goto fail;
cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
if (!cdev->req->buf)
goto fail;
cdev->req->complete = composite_setup_complete;
gadget->ep0->driver_data = cdev;
。。。。。。
if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW)
usb_gadget_set_selfpowered(gadget);
/* interface and string IDs start at zero via kzalloc.
* we force endpoints to start unassigned; few controller
* drivers will zero ep->driver_data.
*/
usb_ep_autoconfig_reset(cdev->gadget);
/* composite gadget needs to assign strings for whole device (like
* serial number), register function drivers, potentially update
* power state and consumption, etc
*/
status = composite_gadget_bind(cdev);
。。。。。。
/* standardized runtime overrides for device ID data */
if (idVendor)
cdev->desc.idVendor = cpu_to_le16(idVendor);
if (idProduct)
cdev->desc.idProduct = cpu_to_le16(idProduct);
if (bcdDevice)
cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
/* string overrides */
if (iManufacturer || !cdev->desc.iManufacturer) {
if (!iManufacturer && !composite->iManufacturer &&
!*composite_manufacturer)
snprintf(composite_manufacturer,
sizeof composite_manufacturer,
"%s %s with %s",
init_utsname()->sysname,
init_utsname()->release,
gadget->name);
cdev->manufacturer_override =
override_id(cdev, &cdev->desc.iManufacturer);
}
if (iProduct || (!cdev->desc.iProduct && composite->iProduct))
cdev->product_override =
override_id(cdev, &cdev->desc.iProduct);
if (iSerialNumber)
cdev->serial_override =
override_id(cdev, &cdev->desc.iSerialNumber);
。。。。。。
}
首先認識usb_gadget這個結構體,這是具體平臺定義的gadget資源,在sun4i_usb/udc/sw_udc.c中:
static struct sw_udc sw_udc = {
.gadget = {
.ops = &sw_udc_ops,
.ep0 = &sw_udc.ep[0].ep,
.name = gadget_name,
.dev = {
.init_name = "gadget",
},
},
/* control endpoint */
.ep[0] = {
.num = 0,
.ep = {
.name = ep0name,
.ops = &sw_udc_ep_ops,
.maxpacket = EP0_FIFO_SIZE,
},
.dev = &sw_udc,
},
/* first group of endpoints */
.ep[1] = {
.num = 1,
.ep = {
.name = "ep1-bulk",
.ops = &sw_udc_ep_ops,
.maxpacket = SW_UDC_EP_FIFO_SIZE,
},
.dev = &sw_udc,
.fifo_size = (SW_UDC_EP_FIFO_SIZE * (SW_UDC_FIFO_NUM + 1)),
.bEndpointAddress = 1,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
。。。。。。
.ep[5] = {
.num = 5,
.ep = {
.name = "ep5-int",
.ops = &sw_udc_ep_ops,
.maxpacket = SW_UDC_EP_FIFO_SIZE,
},
.dev = &sw_udc,
.fifo_size = (SW_UDC_EP_FIFO_SIZE * (SW_UDC_FIFO_NUM + 1)),
.bEndpointAddress = 5,
.bmAttributes = USB_ENDPOINT_XFER_INT,
},
};
一個端點0,4個bulk和一個int傳輸全部定義在這裏,他們的ops指針都指向同一個sw_udc_ep_ops:
static const struct usb_ep_ops sw_udc_ep_ops = {
.enable = sw_udc_ep_enable,
.disable = sw_udc_ep_disable,
.alloc_request = sw_udc_alloc_request,
.free_request = sw_udc_free_request,
.queue = sw_udc_queue,
.dequeue = sw_udc_dequeue,
.set_halt = sw_udc_set_halt,
};
usb_ep_alloc_request是爲端點0分配空間對象,這樣才能使用。composite_gadget_bind指針指向了android.c的bind函數:
static int android_bind(struct usb_composite_dev *cdev)
{
struct android_dev *dev = _android_dev;
struct usb_gadget *gadget = cdev->gadget;
int gcnum, id, ret;
usb_gadget_disconnect(gadget);
ret = android_init_functions(dev->functions, cdev);
。。。。。。
usb_gadget_set_selfpowered(gadget);
dev->cdev = cdev;
return 0;
}
主要是做初始化工作以及爲pid、vid賦值等。
好像到這裏就完成了,都是註冊並填充了一些數據、方法等,那何時才相應主機的請求呢?有中斷到來的時候。
在sun4i_usb/udc/sw_udc.c中,中斷到來就表示主機有請求了,會調用註冊的中斷函數sw_udc_irq:
static irqreturn_t sw_udc_irq(int dummy, void *_dev)
{
。。。。。。
if (tx_irq & USBC_INTTx_FLAG_EP0) {
DMSG_DBG_UDC("USB ep0 irq\n");
/* Clear the interrupt bit by setting it to 1 */
USBC_INT_ClearEpPending(g_sw_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX, 0);
if(dev->gadget.speed == USB_SPEED_UNKNOWN){
if(USBC_Dev_QueryTransferMode(g_sw_udc_io.usb_bsp_hdle) == USBC_TS_MODE_HS){
dev->gadget.speed = USB_SPEED_HIGH;
}else{
dev->gadget.speed= USB_SPEED_FULL;
}
}
sw_udc_handle_ep0(dev);
}
。。。。。。
/* tx endpoint data transfers */
for (i = 1; i < SW_UDC_ENDPOINTS; i++) {
u32 tmp = 1 << i;
if (tx_irq & tmp) {
DMSG_DBG_UDC("USB tx ep%d irq\n", i);
/* Clear the interrupt bit by setting it to 1 */
USBC_INT_ClearEpPending(g_sw_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX, i);
sw_udc_handle_ep(&dev->ep[i]);
}
}
/* rx endpoint data transfers */
for (i = 1; i < SW_UDC_ENDPOINTS; i++) {
u32 tmp = 1 << i;
if (rx_irq & tmp) {
DMSG_DBG_UDC("USB rx ep%d irq\n", i);
/* Clear the interrupt bit by setting it to 1 */
USBC_INT_ClearEpPending(g_sw_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX, i);
sw_udc_handle_ep(&dev->ep[i]);
}
}
。。。。。。
}
主機肯定首先要和ep0通訊的。sw_udc_handle_ep0把請求類型做標記保存下來,最終調用的是dev->driver->setup(&dev->gadget, crq),也就是android.c中的android_setup函數:
static int
android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c)
{
。。。。。。
list_for_each_entry(f, &dev->enabled_functions, enabled_list) {
if (f->ctrlrequest) {
value = f->ctrlrequest(f, cdev, c);
if (value >= 0)
break;
}
}
。。。。。。
/* Special case the accessory function.
* It needs to handle control requests before it is enabled.
*/
if (value < 0)
value = acc_ctrlrequest(cdev, c);
if (value < 0)
value = composite_setup(gadget, c);
spin_lock_irqsave(&cdev->lock, flags);
if (!dev->connected) {
dev->connected = 1;
schedule_work(&dev->work);
}
else if (c->bRequest == USB_REQ_SET_CONFIGURATION && cdev->config) {
schedule_work(&dev->work);
}
spin_unlock_irqrestore(&cdev->lock, flags);
return value;
}
enabled_list鏈表的數據,是上層控制的,上層使用到那個功能纔會添加到鏈表中去,anroid系統常使用到的是mass_storage,adb兩個。ctrlrequest方法可以不定義,看具體設備而定。
如果acc_ctrlrequest方法無法處理,最終調用composite_setup來處理,它會相應主機的獲取描述符,設置地址等請求。比如獲取或者設置接口描述符也在這裏完成:
case USB_REQ_SET_INTERFACE:
if (ctrl->bRequestType != USB_RECIP_INTERFACE)
goto unknown;
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
if (w_value && !f->set_alt)
break;
value = f->set_alt(f, w_index, w_value);
if (value == USB_GADGET_DELAYED_STATUS) {
DBG(cdev,
"%s: interface %d (%s) requested delayed status\n",
__func__, intf, f->name);
cdev->delayed_status++;
DBG(cdev, "delayed_status count %d\n",
cdev->delayed_status);
}
break;
case USB_REQ_GET_INTERFACE:
if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
goto unknown;
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
/* lots of interfaces only need altsetting zero... */
value = f->get_alt ? f->get_alt(f, w_index) : 0;
if (value < 0)
break;
*((u8 *)req->buf) = value;
value = min(w_length, (u16) 1);
break;