A10的cpu有两个hub,也就是root hub,作为host用;还有一个otg功能的hub,可以配置成root hub,也可以配置成device,或者两者同时配置(这时候要通过ID脚来决定当前模式),它的端点0支持最大64字节的控制传送,另外具有5个端点(4个批量传输和一个中断传输);root hub下面最多能外接一个hub。如果作为device用,简称udc(USB Device Control);如果作为host用,简称hcd(Host Control Device)。
他们作为平台设备注册进内核,平台资源在drivers/usb/sun4i_usb/manager/usbc0_platform.c中:
switch(port_info->port_type){
case USB_PORT_TYPE_DEVICE:
platform_device_register(&sw_udc_device);
break;
case USB_PORT_TYPE_HOST:
platform_device_register(&sw_hcd_device);
break;
case USB_PORT_TYPE_OTG:
platform_device_register(&sw_udc_device);
platform_device_register(&sw_hcd_device);
break;
default:
DMSG_PANIC("ERR: unkown port_type(%d)\n", port_info->port_type);
}
平台驱动有两个,udc的在udc/sw_udc.c中:
static int __init udc_init(void)
{
。。。。。。
retval = platform_driver_probe(&sw_udc_driver, sw_udc_probe);
。。。。。。
}
platform_driver_probe说明注册的是不支持热拔插的设备,并且device必须已经先注册过了的。
如果最为host用,那么,最终会调用sw_hcd_probe_host_only -> sw_hcd_init_controller函数:
allocate_instance(dev, plat->config, ctrl) -> usb_create_hcd(&sw_hcd_hc_driver, dev, dev_name(dev));//创建hcd
usb_add_hcd(sw_hcd_to_hcd(sw_hcd), -1, 0),注册hcd到USB核心:
int usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags)
{
。。。。。。
if ((retval = usb_register_bus(&hcd->self)) < 0)
goto err_register_bus;
if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {
dev_err(hcd->self.controller, "unable to allocate root hub\n");
retval = -ENOMEM;
goto err_allocate_root_hub;
}
hcd->self.root_hub = rhdev;
。。。。。。
/* starting here, usbcore will pay attention to this root hub */
rhdev->bus_mA = min(500u, hcd->power_budget);
if ((retval = register_root_hub(hcd)) != 0)
goto err_register_root_hub;
retval = sysfs_create_group(&rhdev->dev.kobj, &usb_bus_attr_group);
。。。。。。
}
usb_register_bus表示注册一条usb总线,每个hub对应一条,注册后在/sys/bus/usb/devices下可以看到。如果全部作为host用,则A10有3条usb总线了,分别为usb1、usb2、usb3。
usb_alloc_dev前面一篇文章已经分析过了,不过这次注册的是root_hub;
register_root_hub(hcd):
static int register_root_hub(struct usb_hcd *hcd)
{
。。。。。。
usb_dev->devnum = devnum;
usb_dev->bus->devnum_next = devnum + 1;
memset (&usb_dev->bus->devmap.devicemap, 0,
sizeof usb_dev->bus->devmap.devicemap);
set_bit (devnum, usb_dev->bus->devmap.devicemap);
usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
mutex_lock(&usb_bus_list_lock);
usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
if (retval != sizeof usb_dev->descriptor) {
mutex_unlock(&usb_bus_list_lock);
dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
dev_name(&usb_dev->dev), retval);
return (retval < 0) ? retval : -EMSGSIZE;
}
retval = usb_new_device (usb_dev);
if (retval) {
dev_err (parent_dev, "can't register root hub for %s, %d\n",
dev_name(&usb_dev->dev), retval);
}
。。。。。。
}
可以看到,也和前面一篇文章分析的一样,走usb_get_device_descriptor、usb_new_device的过程,这时候匹配到的device_driver也是usb_generic_driver,接着创建表示接口描述符的device匹配到的device_driver是hub_driver,在drivers/usb/core/hub.c中:
int usb_hub_init(void)
{
if (usb_register(&hub_driver) < 0) {
printk(KERN_ERR "%s: can't register hub driver\n",
usbcore_name);
return -1;
}
khubd_task = kthread_run(hub_thread, NULL, "khubd");
if (!IS_ERR(khubd_task))
return 0;
/* Fall through if kernel_thread failed */
usb_deregister(&hub_driver);
printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);
return -1;
}
和一般的usb设备一样,root hub也是有设备描述符、接口描述符和端点的,也会创建一个device来表示,但是hub还有hub描述符,用来表明这是hub设备;hub只有一个端点,而且必须是输入方向的中断端点。
在probe中最后用创建urb,并用 usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval);来注册一个中断;
注册完成后hub平时处于休眠状态,当有usb设备插入后,hub_irq中断函数响应:
static void hub_irq(struct urb *urb)
{
。。。。。。
/* Something happened, let khubd figure it out */
kick_khubd(hub);
。。。。。。
}
static void kick_khubd(struct usb_hub *hub)
{
unsigned long flags;
spin_lock_irqsave(&hub_event_lock, flags);
if (!hub->disconnected && list_empty(&hub->event_list)) {
list_add_tail(&hub->event_list, &hub_event_list);
/* Suppress autosuspend until khubd runs */
usb_autopm_get_interface_no_resume(
to_usb_interface(hub->intfdev));
wake_up(&khubd_wait);
}
spin_unlock_irqrestore(&hub_event_lock, flags);
}
用wake_up来唤醒hub内核线程:
static int hub_thread(void *__unused)
{
/* khubd needs to be freezable to avoid intefering with USB-PERSIST
* port handover. Otherwise it might see that a full-speed device
* was gone before the EHCI controller had handed its port over to
* the companion full-speed controller.
*/
set_freezable();
do {
hub_events();
wait_event_freezable(khubd_wait,
!list_empty(&hub_event_list) ||
kthread_should_stop());
} while (!kthread_should_stop() || !list_empty(&hub_event_list));
pr_debug("%s: khubd exiting\n", usbcore_name);
return 0;
}
hub内核线程就是睡眠在wait_event_freezable上,唤醒后调用hub_events来创建新设备。
接着看注册为device的过程sw_udc_probe_device_only:
static int sw_udc_probe_device_only(struct platform_device *pdev)
{
。。。。。。
device_initialize(&udc->gadget.dev);
udc->gadget.dev.parent = &pdev->dev;
udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
is_controller_alive = 1;
the_controller = udc;
platform_set_drvdata(pdev, udc);
sw_udc_disable(udc);
sw_udc_reinit(udc);
udc->sw_udc_io = &g_sw_udc_io;
udc->usbc_no = usbd_port_no;
strcpy((char *)udc->driver_name, gadget_name);
udc->irq_no = irq;
if(is_udc_support_dma()){
retval = sw_udc_dma_probe(udc);
if(retval != 0){
DMSG_PANIC("ERR: sw_udc_dma_probe failef\n");
retval = -EBUSY;
goto err;
}
}
retval = request_irq(irq, sw_udc_irq,
IRQF_DISABLED, gadget_name, udc);
。。。。。。
}
创建设备和注册irq,但是并没有把设备添加到设备层中去,因为这个动作是留给其他驱动完成的,在android系统中,如果作为device用,那么就是把硬盘的一个分区模拟成一个u盘设备;或者看成mtp设备;或者adb功能等,而这些功能必须要有usb端点支持才行,所以添加设备动作导出为USB gadget驱动用的。
所以进入drivers/usb/gadget目录下,从Makefile和内核的config文件可以看出,这里涉及到的文件有f_mass_storage.c(对应usb mass storage),android.c,f_mtp.c,f_accessory.c,composite.c等文件,其入口为android.c。
下一篇文章分析gadget驱动。