DAVINCI USB驱动的框架图: USB-CORE HCD(MUSB CONTROLLER) LOW-LEVEL DRIVER 引言:首先make menuconfig选中usb support(CONFIG_USB) 在driver/usb/makefie里定义: obj-$(CONFIG_USB) += core/ 找到driver/usb /core/makefie里定义: usbcore-objs := usb.o hub.o hcd.o urb.o message.o \ config.o file.o buffer.o sysfs.o obj-$(CONFIG_USB) += usbcore.o usb.c这就是USB 总线驱动的核心文件,那现在打开driver/usb/core/usb.c看看usb总线是如何初始化的 subsys_initcall(usb_init); 我们知道subsys_initcall是子系统的初始化,关于subsys__initcall在include/linux/init.h里定义如下: #define subsys_initcall(fn) __define_initcall("4",fn) 而__define_initcall在 #define __define_initcall(level,fn) \ static initcall_t __initcall_##fn __attribute_used__ \ __attribute__((__section__(".initcall" level ".init"))) = fn 相当于".initcall"4 ".init" .initcall4.init在vmlinux.lds __initcall_start = .; *(.initcall1.init) *(.initcall2.init) *(.initcall3.init) *(.initcall4.init) *(.initcall5.init) *(.initcall6.init) *(.initcall7.init) __initcall_end = .; 把函数放到段地址里遍历段时就调用函数了 妙招! __initcall_start在init里do_initcalls引用的-_- for (call = &__initcall_start; call < &__initcall_end; call++) { } 废话不说了,回到usb_init里 最重要的是: bus_register usb_host_init usb_hub_init driver_register(&usb_generic_driver); 这。。。。。。。 subsys_initcall(musb_init); davinci usb-controller 的初始化, 主要是这步driver_register(&musb_driver); usb-controller的核心了 static struct device_driver musb_driver = { .name = (char *)musb_driver_name, .bus = &platform_bus_type, .owner = THIS_MODULE, .probe = musb_probe, .remove = __exit_p(musb_remove), .shutdown = musb_shutdown, .suspend = musb_suspend, .resume = musb_resume, }; 又回到linux驱动的框架了 哈 熟吧 最终还是会去做musb_probe,目标只有一个,在musb_probe里会musb_init_controller,当然只有初始化了才能用controller终于找到组织了。下面来看看musb_init_controller 。。。。。。。。。。。。。。。。。。。。。。。。 。。。。。。。。。。。。。。。。。。。。。。。。 提到HUB了,在usb_init core 早为我们准备了usb_hub_init();这样通过kernel_thread创建一个守护进程khubd轮询USB口的有没有设备接入,一个while(1)搞定简单吧老谭的C有用了, 调用 hub_port_connect_change处理 1 USB硬盘的识别: 接入USB硬盘hub_events就开始工作了,hub_port_connect_change发现有设备连上USB口于是就有了后来的事 usb_new_device device_add bus_add_device device_attach 。。。。。。。。。。。。。。。。。。。。。。。。。 。。。。。。。。。。。。。。。。。。。。。。。。。 storage_probe,USB Mass Storage device? 调用usb_stor_scan_thread 创建守护进程(usb-stor-scan) storage_probe在usb_stor_init引用: usb_register(&usb_storage_driver) struct usb_driver usb_storage_driver = { .owner = THIS_MODULE, .name = "usb-storage", .probe = storage_probe, .disconnect = storage_disconnect, .id_table = storage_usb_ids, }; usb_register是usb 设备驱动向usb core层注册 struct usb_driver { struct module *owner; const char *name; int (*probe) (struct usb_interface *intf, const struct usb_device_id *id); void (*disconnect) (struct usb_interface *intf); int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf); int (*suspend) (struct usb_interface *intf, u32 state); int (*resume) (struct usb_interface *intf); const struct usb_device_id *id_table; struct device_driver driver; }; usb_driver是usb -core的驱动struct device_driver driver在这哈 linux驱动框架是不是很熟啊 ,对 这就是封装 。。。C++ 回首usb core原来帮你做了这么多事 scsi_scan_host到SCSI层了!又看到scsi_scan_channel了 scsi设备都是挂到某个伦上的 比如:Attached scsi disk sda at scsi11, channel 0, id 0, lun 0 因为SCSI controller是数据结构LINUX 内核SCSI core虚拟的 2 USB 硬盘数据传输: 在storage_probe时调用result = usb_stor_acquire_resources(us); 在此函数里创建的usb_stor_control_thread守护进程(usb-storage),在传输数据时用的是us->proto_handler(us->srb, us)决定用的是哪种协议的传输方式SCSI用的是usb_stor_transparent_scsi_command在(get_protocol里定义的), 说到get_protocol主要是得到一些设备信息比如:vender protocol manfatuer等 它用的ID是USB_PROBE是传入的。 get_transport决定用的是哪种的传输模式:硬盘是存储设备用的是BULK的方式,当然USB core里也定义了(Bulk,Control传输模式因设备类型而异)。 一次传输从usb_stor_transparent_scsi_command开始:。。。首先usb_stor_invoke_transport temp_result = us->transport(us->srb, us);还记得吗这就是前面get_transport里定义好的传输模式,哈USB就是这样的 主要提到usb_stor_bulk_transfer_buf和usb_stor_bulk_transfer_sg,前者调用usb_stor_msg_common里面status = usb_submit_urb(us->current_urb, GFP_NOIO);提交URB, URB是USB-core的主要传输单位,像net的packet一样 注意了op->submit_urb (urb, mem_flags)用到的是struct usb_operations *op是 struct usb_operations { int (*allocate)(struct usb_device *); int (*deallocate)(struct usb_device *); int (*get_frame_number) (struct usb_device *usb_dev); int (*submit_urb) (struct urb *urb, int mem_flags); int (*unlink_urb) (struct urb *urb, int status); /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */ void *(*buffer_alloc)(struct usb_bus *bus, size_t size, int mem_flags, dma_addr_t *dma); void (*buffer_free)(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma); void (*disable)(struct usb_device *udev, int bEndpointAddress); /* global suspend/resume of bus */ int (*hub_suspend)(struct usb_bus *); int (*hub_resume)(struct usb_bus *); }; 调用的是musb_submit_urb而这是哪来的的呢? 哈 具体的USB controller的submit方法:我们用的DAVINCI usb-controller 在plat_uds.c里DAVINCI usb-controller device-driver初始化函数musb_init driver_register(&musb_driver); 定义如下: static struct device_driver musb_driver = { .name = (char *)musb_driver_name, .bus = &platform_bus_type, .owner = THIS_MODULE, .probe = musb_probe, .remove = __exit_p(musb_remove), .shutdown = musb_shutdown, .suspend = musb_suspend, .resume = musb_resume, }; 还记得LINUX 驱动框架吗? Driver 的probe必然会去找设备 musb_init_controller这时才登场 bus = usb_alloc_bus(&musb_host_bus_ops)里bus->op = op;连上了 musb_init_controller除了这还做了:比如 platform_get_irq(pdev, 1)为MUSB申请中断 为何是platform呢? USB-controller无家可归的娃啊 musb_platform_init又是另一个比较重要的函数 clkp = clk_get (NULL, "USBCLK") USB的clock在这里申请还记得在CONTROLLER会检查 没时钟怎行 musb->isr = davinci_interrupt 中断处理函数 davinci usb controller通过中断来检测硬盘寄存器复位USB controller 回到usb_stor_bulk_transfer_sg里Transfer an entire SCSI command's worth of data payload over the bulk 这就是scsi协议用到的 2 USB硬盘的移除: 简单的可以说是USB热插拔的实现了,当USB口的设备拔出时,USB是如何知道的呢?还记得前面提到的hub_port_connect_change 吗?对USB有守护进程这个宝 void usb_disconnect(struct usb_device **pdev) { } 原因是这样的慢慢说来,当USB口的设备拔出时,从硬件上看是一个叫MUSB中断(中断号是12),davinci 的驱动就跳到中断处理函数davinci_interrupt里查状态寄存器的位变化,代码如下: tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG); musb_writel(tibase, DAVINCI_USB_INT_SRC_CLR_REG, tmp); musb->int_rx = (tmp & DAVINCI_USB_RXINT_MASK) >> DAVINCI_USB_RXINT_SHIFT; musb->int_tx = (tmp & DAVINCI_USB_TXINT_MASK) >> DAVINCI_USB_TXINT_SHIFT; musb->int_usb = (tmp & DAVINCI_USB_USBINT_MASK) >> DAVINCI_USB_USBINT_SHIFT; 续。。。。。。 if (musb->int_tx || musb->int_rx || musb->int_usb) retval |= musb_interrupt(musb); 这不进到usb-conroller中断处理函数里去le。后面的故事更精彩 然后起一个定时器去查询hub的状态(?。。。。。),同时 hub会向usbcontroller发送Change Bitmap的消息(硬件上做的东东),那么usb控制器就接受这个urb 处理完后调用那个hub_irq处理函数,在它那里再去kick_khubd里做了这一关键的一步,看下 static void kick_khubd(struct usb_hub *hub) { unsigned long flags; spin_lock_irqsave(&hub_event_lock, flags); if (list_empty(&hub->event_list)) { list_add_tail(&hub->event_list, &hub_event_list); wake_up(&khubd_wait); } spin_unlock_irqrestore(&hub_event_lock, flags); } 把事件加到event链表里,这样hub_events就知道了,这就是热插拔。。。 |