Linux usb hub阅读

可以阅读usb-skeleton.c文件,简单的一个usb设备驱动程序的框架

ROOT_HUB:

usb设备的初始化都是hub这边发起的,我们写一个usb设备驱动是已经在得到一个struct usb_interface指针的情况下开始probe工作的
通常芯片会把host controller和root hub集成在一起
从usb子系统的初始化开始,core/usb.c:
subsys_initcall(usb_init);
module_exit(usb_exit);
使用subsys_initcall宏是因为这块代码比较核心,看作一个子系统,代表一类设备,这里是这模块真正的初始化函数

usb_hub_init:

1、向usb core注册一个usb hub驱动
2、起一个线程或者workqueue,khubd
IS_ERR():
判断指针的,每次调用kthread_run之后使用IS_ERR来判断指针是否有效,不然有可能会发生缺页异常
内核空间:所有驱动程序都运行在内核空间,最后一个page专门保留的,不可以使用,是用来放linux内核中的错误码的,所以使用IS_ERR来判断指针是否出错,可以用PTR_ERR宏接口来返回错误码

子进程hub_thread:

LIST_HEAD:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
struct list_head {
struct list_head *next, *prev;
};
定义了一个struct list_head的结构体,hub_event_list,而且两个指针next和prev分别指向自己,就是建立了一个双向链表并做了初始化,初始化成一个空队列,对于这个队列有很多函数可以操作,比如:list_empty、list_add_tail等等;
#define list_entry(ptr, type, member) container_of(ptr, type, member)
一个struct usb_hub代表一个hub,每个usb_hub都有一个event_list事件链表,但hub驱动只有一个,所以所有的event_list都要挂接到全局的hub_event_list中,所以判断hub_event_list是否为空,不为空就去hub_events函数,那如何确定具体是哪个hub出发了事件呢:list_entry来从event_list中得到它所对应的usb_hub结构体变量
struct list_head *tmp; 一个list
struct usb_hub *hub; 一个hub
tmp = hub_event_list.next; 一个list
hub = list_entry(tmp, struct usb_hub, event_list); 从全局链表hub_event_list中取出一个list来,叫做tmp,然后通过tmp获得它对应的struct usb_hub变量
总分总思想:
总:设置一个总的函数hub_events。一个链表 hub_event_list
分:每一个hub都有一个event_list,每当一个hub的event_list发生了变化,就插入到hub_event_list
总:触发函数hub_events
分:hub_events函数中根据event_list来确定是哪个hub有事情,并处理
try_to_freeze:
检测TIF_FREEZE这个flag有没有被设置,TF就是thread info

hub_events:

kzalloc:是kmalloc和memset函数的结合体
对于root_hub,在host控制器HC启动时它会初始化并调用probe进入hub_events
对于普通hub,要建立并初始化一个usb设备的struct usb_interface,至于probe何时调用,继续:
INIT_WORK schedule_work、INIT_DELAY_WORK schedule_delayed_work:工作队列
关于工作队列,内核实现了一个工作线程,events0—events7这些都是处理器的编号,每个处理器对应一个线程,叫做工作者线程,work thread,就是默认的工作者线程,而工作队列work_queue就是把工作推后,交由一个内核线程去执行,
cancel_delayed_work、flush_scheduled_work:取消工作的执行,flush刷新队列防止混乱(完整的events队列突然被取消了一个,所以需要刷新)
usb_set_intfdata(intf,hub):让intf和hub关联起来
hub_configure:
get_hub_descriptor:发送一个request控制请求,以获得hub的描述符 ,usb_control_msg:
有一个特殊的hub描述符
USB_MAXCHILDREN = 31; hub其实可以接255个端口,不过实际遇到的hub最多可以支持10个端口,所以31基本够用;将这个宏改为100也没事
wHubCharacteristics记录hub很多信息,它会判断是不是复合设备,然后给portstr数组记录每一个端口设备是否可以被移除等功能
tt:transation transaction:一块特殊电路,高速hub中的电路,负责高低速设备的数据转换,对设备做兼容,有单TT(一个hub一个电路)和多TT(每个端口一个电路),共享带宽和独立带宽
usb_get_status:控制传输,可以获得设备、接口、端点的状态,USB_DEVICE_SELF_POWERED 这一位表示hub是不是自己供电的,hub两种供电方式:hub自己外接电源供电,还有总线供电; 总线供电的话limited_power也设置为1,端口上最小负载电流是100ma,
hub传输方式是控制和中断传输,hub_configure传递进来的就是一个中断端点,hub的中断端点一定是IN而不是OUT
usb_alloc_urb、usb_fill_int_urb:申请一个urb,填充一个urb,传输的buffer就是端点的最大包size,就是wMaxPacketSize,complate就是hub_irq,context就是hub,interval间隔期就是端点的interval,
Frame、microframe:帧和微帧,而对于interval,中断是没有间隔期的,但是这里是因为对于IN的中断端点,主机会定期向设备询问,所以interval就是设备希望的总线访问周期,
usb_fill_int_urb:hub的中断传输:host发送一个IN token包,看有没有中断,有的话设备会把这个中断相关的数据发送给主机,这就是主机的数据阶段;主机接受到之后发回一个包确认一下,也许ACK(有中断并发送了数据),也许NAK(失败了),也许STALL(设备端点有问题,无法传输)
hub_activate:usb_submit_urb提交前面填充好的urb,然后HC就知道了,之后HC就定期询问hub来有没有中断了
kick_khubd:获取电源管理kref计数,表示hub要工作了,不能让它suspend,然后把hub-<event_list加入到总的队列hub_event_list中,之后开始执行hub_events

hub_events

再次回到hub_events,第一次来的时候,hub_event_list是空的,这一次不是空的,在kick_khubd那里执行了队列操作,
list_entry()宏函数,通过它得到那个出发hub_events()的hub,然后给hub->hdev,再得到interface,device,就都得到了
udev->parent:root hub不能离开HC的,这个parent指针在HC的驱动程序已经赋值了,它是NULL,对于root hub,它不再要有父指针,这个parent就是给root_hub连出来的节点用的,
每一个hub在执行hub_events时,都要获得一把锁把自己锁住,在这之前又要获得parent的锁,说明parent没有执行hub_events,这时子hub才可以执行自己的hub_events,使用interface之前,调用usb_get_intf接口增加引用计数,对应的结束了要usb_put_intf
HUB_STATUS_LOCAL_POWER:用来标志这个hub室友专门的外接电源还是从usb总线上获取电源
portnum:对于root hub它没有portnum,在HC那里给了0,但是对于普通的hub,
usb_reset_composite_device :传入参数device和interface,在里面遍历设备接口,去绑定接口驱动,然后usb_reset_device接口会执行reset操作,但若一个设备有多个接口,不应该全部reset,所以usb_driver定义了pre_reset和post_reset,pre_reset接口告诉驱动要reset了,post告诉驱动reset结束了,poset_reset还有一个作用,因为reset把设备原来的状态给清掉了,所以pos_reset会重新初始化设备
bNbports是hub描述符中一员,表示hub有几个端口
hub_port_status:get_port_status,usb_control_msg,返回两个状态,port status和port change status,和port change相关的判断语句,都要在检测到flag被设置之后去清除掉它,给其他地方使用
portchange和USB_PORT_STAT_XXX相与,走不同处理方式分支
每个端口都有enable和disable开关,有时候hub port变成disable,有可能是电磁干扰EMI引起,端口被disable了,但是连接却没有变化,子设备还连接在端口
portchange和USB_PORT_STAT_C_RESET相与为1,这是一个端口从resetting状态到enable状态,connect_change如果是1的话,执行hub_port_connect_change函数,这就是我们要看的一个usb设备插入usb接口之后和USB驱动程序那个probe如何执行等的问题解答点:
hub_port_connect_change:
三种情况调用:
1、连接有变化
2、端口本身重新使能enable(通常对付电磁干扰)
3、复位设备时发现描述符变化了(通常是硬件本身有了升级)
端口连接有变化两种情况:
1、连接,要对新来的设备进行初始化,配置,分配地址,为设备寻找对应的驱动程序,找到了就调用驱动提供的指针函数来做更深入的初始化
2、断开,直接调用usb_disconnect函数,并把hub的changebits清掉,然后驱动端口没设备了就返回
hub_port_debounce :hub的消抖,让设备插入之后,至少等待100ms之后设备依然在,说明稳定了,
for循环PORT_INIT_TRIES:为一个usb设备申请内存空间——》设置它的状态——》把它复位——》为它设置地址——》获取描述符——》然后device_add向设备模型添加这么一个设备——》之后寻找它的驱动程序,probe被调用——》剩下事情设备模型去处理
所有usb设备都有一个默认控制管道:EP0,它没有专门的端点描述符,因为它的特性基本在usb spec中规定好了,固定的;端点0可以接受或发送的最大包,wMaxPacketSize0,它可以是8,16,32,64;至少要是8,
两种获得设备描述符的方法:
linux:设备描述符的byte7刚好就是wMaxPacketSize0,所以可以先读8个字节,获得wMaxPacketSize0大小之后,在读一次描述符,把整个描述符18字节给读出来(旧策略,old scheme)
windows:直接发送64字节的请求,要求设备返回64字节,如果EP0的maxsize是32或者64,最后只要把18字节传过去就可以了,如果EP0的maxsize是8或者16,而设备描述符是18个字节,一次传递不完,那就先传递8个字节,获得Maxsize,然后reset一下,再按照真正的MaxPacketSize来传递,这样也就获得18个自己描述符了(新策略,new scheme)
现在代码中都有这两种策略,没中retry两次来读取,原来的策略叫做old scheme,

重要函数

1、usb_alloc_dev:为一个struct usb_device结构体指针,申请内存,这个指针不是为hub准备的,它是为hub这个端口所接的设备而申请的

usb设备6种状态:attached、powered、default、address、configured、suspend
sys/bus/usb/devices下面:4-0:1.0
usbn表示有n条usb总线,也即有n个usb主机控制器,4表示4号总线,0是devpath,1表示配置1,0表示接口号是0:4号总线的0号端口的设备,使用的是1号配置,接口号为0
devpath:顶级的设备devpath就是其连接在root hub上的端口号,而次级的设备就是其父HUB的devpath后面加上其端口号,如果4-0:1.0是一个hub,它下面的1号端口的设备就可以是4-0.1:1.0,如果是2号端口的设备就是4-0.2:1.0
proc/bus/usb下面:usbfs使用,usb设备文件系统,mount -t usbfs none /proc/bus/usb

2、usb_set_device_state:用来设置设备的状态,struct usb_device结构体中,有一个成员enum usb_device_state state,这一刻,会把这个设备的状态设置为USB_STATE_POERED,上电状态

3、choose_address:为设备选择一个地址

每条总线设有一个地址映射表,struct usb_bus结构体中的一个成员struct usb_devmap devmap; 还有一个成员 int devnum_next,在总线初始化时; usb_device中有一个int devnum;
choose_address函数的基本思想是一个轮询算法
devicemap,这里有128 bits,对应一条总线可以连接128个设备,每加入一个设备,就先找到下一位为0的bit,然后把该bit设为1,同时把devnum设置为该下标;比如找到第19位为0,就把devnum设置为19,同时bit19设置为1,devnum_next就设置为20

4、hub_port_init:端口初始化,获取设备的描述符

DEFINE_MUTEX :用来定义一把互斥锁
HNP:OTG标准所支持的协议,Host Negotiation Protocol ,主机通令协议
hub_port_reset:端口reset,需要一个延时,一般500ms或者800ms
hub_set_address:发送设置地址的真正请求,udev->devnum,设置好地址之后,设备状态从default变成了address,然后调用ep0_reinit
ep0_reinit:一个设备没有被设置地址时,它使用默认地址0,当设置地址之后,它必须要让主机控制器HC知道,否则HC无法控制它,所以这个函数通知HC

5、usb_get_status:为hub准备的,不是为当前hub,而是当前hub的端口上连接的如果又是一个hub,那么和连接普通设备是不一样的

6、check_highspeed:不同速度待遇不同

7、usb_new_device:寻找驱动程序,调用驱动程序的probe,跟踪这个函数能一直跟踪到设备驱动的probe函数的调用

usb-detect_quirks:quirks.c中,USB黑名单,和usb-storage中的unusual_devs.h是一样的,判断一个设备是不是在这个黑名单中的,如果是,那它具体属于哪种问题
usb_get_configuration:获得配置描述符
device_add:遍历usb总线上的所有驱动程序,找到合适的就去执行probe函数,hub_probe,

8、hub_power_remaining:电源管理,hub永恒的话题

limited_power是在hub_configure中就设置了的,资源有限,返回剩余的电流,没有配置过的设备不能超过100ma电流

hub_irq:

真正有端口变化的时间发生,hub_irq就会被调用,而hub_irq最终会调用kick_khubd,触发hub的event_list,再调用hub_events
在hub_configure中讲过中断传输,调用了usb_fill_int_urb函数,一个参数是hub_irq,最终把urb->complate赋值为hub_irq,然后HC会定期询问hub上端口是否有变化
硬件上:hub会向HC返回一些信息data,”hub and port status change bitmap“
软件上:HC的驱动程序会在处理这个urb之后,调用该urb的complate函数,对于hub就是hub_irq函数
hub_irq(struct urb *urb):

root_hub的生命线:

1、HC驱动一般都是PCI设备——》它都有个struct pci_driver结构体,都有一个自己的probe——》在自己的probe中,都会调用usb_creat_hcd函数来创建一个属于自己的usb_hcd——》都会调用usb_add_hcd函数将这个刚创建的usb_hcd注册到usb组织中;——》root_hub登场了
2、usb_add_hcd函数,调用usb_alloc_dev给root_hub一个struct usb_device结构体,设备类型是usb_device_type,总线类型是usb_bus_type,然后赋值给struct usb_bus的root_hub元素
3、然后调用register_root_hub函数,将root_hub的devnum设置为1,到Address状态,去获得root_hub的设备描述符,调用usb_new_device将root_hub给设备模型去找它的驱动
4、Linux设备模型根据root_hub所属的总线类型usb_bus_type将它添加到usb总线的设备链表中,轮询usb总线的另外一条驱动链表,调用usb总线的match函数usb_device_match;匹配成功的是那个对所有usb_device_type类型设备都友好的,唯一的usb设备驱动,struct device_driver结构体对象usb_generic_driver函数;调用generic_probe函数配置root_hub,然后进入Configured状态
5、generic_probe根据root_hub使用的配置,准备struct usb_interface结构体对象,设备类型变成了usb_if_device_type,interface接口类型
6、spec规定hub上除了端点0,只有一个中断的IN端点,root_hub配置描述符只有一个接口,一个配置,一个端点
7、root_hub两个端点:端点0和IN端点1,中断类型
8、将准备好的接口给设备模型去寻找它的驱动,usb_device_match,root_hub的bDeviceClass是0x09,根据这里bInterfaceClass是0x09确定,其实这个接口驱动就是hub驱动,
9、调用hub驱动的hub_probe函数,不管是root_hub还是普通hub都需要调用这个porbe,但是对于root_hub在HC初始化时就会调用hub_porbe;在hub_probe这里通过hub_configure函数创建并初始化了一个中断的urb,然后在hub_activate函数里调用usb_submit_urb函数将urb提交给core,然后又回到了HCD
10、hub需要中断传输来获得hub的状态变化,hub_events函数,旧方案:HC固定轮询式,usb_hcd结构体中有个rh_timer定时器完成这个事情,root_hub每提交一次urb,在hcd那里都会对它初始化一次,指定250ms去获得root_hub状态,然后让urb返回给root_hub,root_hub再提交,再250ms返回。。。;新方案:root_hub提交一个中断urb,在设备插入时,HC在自己的中断处理函数中调用usb_hcd_poll_rh_status函数去获得root_hub的状态并将urb的所有权归还给hub驱动

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值