Linux USB架构浅谈

转载:http://blog.csdn.net/guolele2010/article/details/6430329

我要注册-主控制器与ROOT HUB难分难舍

这里要说明几点,这里只是说明一下框架,对于一些错误处理都没说到,而且一些细节也没说,只是有个认识,具体可参考《linux那些事儿系列从书》。

我要插拔

在讲插拔时,我们先了解一下设备插入到hub里面,会有什么结果。

“USB主机是如何检测到设备的插入的呢?首先,在USB集线器的每个下游端口的D+D-上,

分别接了一个15K欧姆的下拉电阻到地。这样,在集线器的端口悬空时,就被这两个下拉电阻

拉到了低电平。而在USB设备端,在D+或者D-上接了1.5K欧姆上拉电阻。对于全速和高速设备,

上拉电阻是接在D+上;而低速设备则是上拉电阻接在D-上。这样,当设备插入到集线器时,

1.5K的上拉电阻和15K的下拉电阻分压,结果就将差分数据线中的一条拉高了。集线器检测

到这个状态后,它就报告给USB主控制器(或者通过它上一层的集线器报告给USB主控制器),

这样就检测到设备的插入了。USB高速设备先是被识别为全速设备,然后通过HOSTDEVICE

两者之间的确认,再切换到高速模式的。在高速模式下,是电流传输模式,这时将D+上的

上拉电阻断开。引用自《USB入门系列之五》。

对于轮循的话,在usb_create_hcd里有初始化一个内核定时器,设定每隔一段时间就会调用rh_timer_func

/* timer callback */

static void rh_timer_func (unsigned long _hcd)

{

         usb_hcd_poll_rh_status((struct usb_hcd *) _hcd);

}

即调用hcd_poll_rh_status

这个函数就会调用主控制器的驱动程序里的hcd->driver->hub_status_data(hcd, buffer);

这里假设是uhci就会调用uhci_hub_status_data

uhci_hub_status_data

  1. static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)//buf里保存的是端口状态 
  2.  
  3.  
  4.          struct uhci_hcd *uhci = hcd_to_uhci(hcd); 
  5.  
  6.          unsigned long flags; 
  7.  
  8.          int status = 0; 
  9.  
  10.   
  11.  
  12.          spin_lock_irqsave(&uhci->lock, flags); 
  13.  
  14.   
  15.  
  16.          uhci_scan_schedule(uhci); 
  17.  
  18.          if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead) 
  19.  
  20.                    goto done; 
  21.  
  22.          uhci_check_ports(uhci); 
  23.  
  24.   
  25.  
  26.          status = get_hub_status_data(uhci, buf); 
  27.  
  28.   
  29.  
  30.          switch (uhci->rh_state) { 
  31.  
  32.              case UHCI_RH_SUSPENDING: 
  33.  
  34.              case UHCI_RH_SUSPENDED: 
  35.  
  36.                    /* if port change, ask to be resumed */ 
  37.  
  38.                    if (status) 
  39.  
  40.                             usb_hcd_resume_root_hub(hcd); 
  41.  
  42.                    break; 
  43.  
  44.   
  45.  
  46.              case UHCI_RH_AUTO_STOPPED: 
  47.  
  48.                    /* if port change, auto start */ 
  49.  
  50.                    if (status) 
  51.  
  52.                             wakeup_rh(uhci); 
  53.  
  54.                    break; 
  55.  
  56.   
  57.  
  58.              case UHCI_RH_RUNNING: 
  59.  
  60.                    /* are any devices attached? */ 
  61.  
  62.                    if (!any_ports_active(uhci)) { 
  63.  
  64.                             uhci->rh_state = UHCI_RH_RUNNING_NODEVS; 
  65.  
  66.                             uhci->auto_stop_time = jiffies + HZ; 
  67.  
  68.                    } 
  69.  
  70.                    break; 
  71.  
  72.   
  73.  
  74.              case UHCI_RH_RUNNING_NODEVS: 
  75.  
  76.                    /* auto-stop if nothing connected for 1 second */ 
  77.  
  78.                    if (any_ports_active(uhci)) 
  79.  
  80.                             uhci->rh_state = UHCI_RH_RUNNING; 
  81.  
  82.                    else if (time_after_eq(jiffies, uhci->auto_stop_time)) 
  83.  
  84.                             suspend_rh(uhci, UHCI_RH_AUTO_STOPPED); 
  85.  
  86.                    break; 
  87.  
  88.   
  89.  
  90.              default: 
  91.  
  92.                    break; 
  93.  
  94.          } 
  95.  
  96.   
  97.  
  98. done: 
  99.  
  100.          spin_unlock_irqrestore(&uhci->lock, flags); 
  101.  
  102.          return status; 
  103.  
 

主要是执行get_hub_status_data获得hub的状态,然后根据uhci即主控制器状态来分别执行,if (!any_ports_active(uhci)) 判断是否端口有变化,有就返回>0的值,就会填充中断urb,然后调用usb_hcd_giveback_urb返回到hub_irq

Hub_irq里就调用

kick_khubd(hub);

然后就唤醒hub_thread,就调用hub_event

在hub_event里就会看是哪一种唤醒,如果是设备插拔就会调用

hub_port_connect_change

hub_port_connect_change太长就不表了,大家可以去看源码

大概是

hub_port_debounce去抖动后,就会进行设备枚举,下面分析下设备枚举的过程。

设备枚举:

对于linux的策略是很符合usb协议的,但是发现厂家很多不符合,所以这些产品在linux下用不了,但是奇怪的事是在windows下能用,后来发现是windows策略不一样,它是一次性收64字节,而linux只收8字节(可参考《Linux那些事儿之我是HUB》)

一般先是采用新策略再用旧策略

先收一次设备描述符得到bMaxPacketSize(新旧策略),然后复位一次设备,设置地址hub_set_address,再重新获得一次设备描述符usb_get_device_descriptor,

然后调用usb_new_device,再调用usb_configure_device

注意一点:

struct usb_host_interface,事实上这个interface描述符,就是设置描述符,这个描述符的其中一项叫做bInterfaceNumber,指的就是这个设置是属于哪个接口。比如一个接口包含2个设置,那么就会有2个interface描述符,两个描述符里的bInterfaceNumber设置都为0,但是第一个设置的bAlternateSetting为0, 第二个设置的bAlternateSetting为1。这样就区分开来了。

设备的字符描述符,是采用unicode里的UTF-16表示的,而且是little-endpoint,两个字节表示一个字符。获得的字符串是NULL-terminated的字符串,即没有结束符的

主控制器获得配置描述符时,设备是将配置、接口、端点和一些特殊描述符一起返回,所以要先发一次,然后在配置描述符里找到wTotalLength,再申请一个大的缓冲区来保存所有接回来的数据

usb_configure_device这个函数是很长的,因为发送USB_DT_CONFIG命令,设备会一次性把设备将返回的是除了配置描述符以外,与这种配置相关的接口描述符,以及与这

些接口相关的端点描述符,还有一些class的描述符,都会一次性返回,所以拿到后还要解码,就是一些usb_parse_configuration之类的,就不分析了,假设也成功获得所有描述符,就会返回usb_new_device调用device_add就会进入usb_devcie_match的设备那个分支。就会调用generic_probe就会调用usb_choose_configuration选择配置,usb_set_configuration设备配置,然后usb_set_configuration又会注册接口设备,又会再进入usb_device_match,但这次走的是接口的分支,就会先匹配ID table然后调用对应的接口驱动的probe函数,到此就完成了设备的插入初始化以及关联接口驱动的过程。

下面弄个图理一理:

这里,大家看得好像理所当然,但是有一个问题,就是初始化后怎么工作?

我要传输

四大传输:

控制传输(control)

中断传输(int)

等时传输(ISO)

批量传输(bulk)

其中usb spec里说的四种传输就是所说的,但是要注意hub只有两种,一种是中断传输,一种是控制传输(这个好像有点废,因为所以的usb设备都是要支持控制传输的)。

而一般的设备会支持控制传输跟其他三种里的一种或者更多。

对于设备通信的话,linux主要是用了urb的传输,其地位非常高,相当于网卡驱动里的socket。

首先是先struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)

然后是static inline void usb_fill_xxx_urb, 这里的xxx是表示不同类型的传输有不同的函数调用,但是等时传输是没有对应接口函数的,所以要手动一个个元素去赋值。

管道的创建也有对应函数

这几个函数参数都有点不一样,主要是处理的流程不同。其中相同的urb就是要传的urb,pipe是对应端点的管道,transfer_buffer就是传输的缓冲区,setup_packet是控制传输里传输的数据,usb_complete_t complete_fn就是回调函数,context是上下文变量的保存,有时用来保存completion。

批量传输

static inline void usb_fill_bulk_urb(struct urb *urb,

                                          struct usb_device *dev,

                                          unsigned int pipe,

                                          void *transfer_buffer,

                                          int buffer_length,

                                          usb_complete_t complete_fn,

                                          void *context)

usb_sndbulkpipe(dev,endpoint)

usb_rcvbulkpipe(dev,endpoint)

控制传输

static inline void usb_fill_control_urb(struct urb *urb,

                                               struct usb_device *dev,

                                               unsigned int pipe,

                                               unsigned char *setup_packet,

                                               void *transfer_buffer,

                                               int buffer_length,

                                               usb_complete_t complete_fn,

                                               void *context)

usb_sndctrlpipe(dev,endpoint)

usb_rcvctrlpipe(dev,endpoint)

中断传输

static inline void usb_fill_int_urb(struct urb *urb,

                                         struct usb_device *dev,

                                         unsigned int pipe,

                                         void *transfer_buffer,

                                         int buffer_length,

                                         usb_complete_t complete_fn,

                                         void *context,

                                         int interval)

usb_sndintpipe(dev,endpoint)

usb_rcvintpipe(dev,endpoint)

对于上面几种传输,内核还提供了别一种接口

“于控制/批量/中断传输,实际上很多时候你可以不用创建 urb,不用对

它初始化,不用调用 usb_submit_urb 来提交,core 将这个过程分别封装在了

usb_control_msg、usb_bulk_msg 和 usb_interrupt_msg 这三个函数里,不同的是

它们的实现是同步的,会去等待传输的完全结束。”引用自《Linux那些事儿之我是USB Core》

然后对应的urb就会传输到对应主控制器里,主控制器就会跟设备进行交流。具体的话是跟主控制器类型有关的,像uhci就是采用了frame list机制,就会有QH TD这样的概念。

还要说明一点,一般UHCI主机控制器本身通常是 PCI设备,即通常它会插在 PCI插槽里,或者直接就集成在主板上,不同的控制器会有不同的要求,这就要看控制器的类型了。

还有一点要说明的就是这个usb设备驱动,一般我们编写就只是编写usb interface driver即一个接口对应一个驱动。但是在接口驱动里面,又会用到其他内核子系统,例如usb触摸屏就会用到输入子系统驱动框架,所以说具体还是要具体分析接口驱动,这里的usb主框架基本是定了的,只是内容可能有点不一样。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux USB硬件架构包括四个主要组件:USB控制器、USB设备、USB主机和USB总线。 1. USB控制器:用于管理USB总线,它负责控制数据包传输,管理带宽和处理中断请求。它通常是一个芯片,与主机通过PC接口互连。 2. USB设备:USB控制器通过USB端口与USB设备连接。USB设备可以是外设,如鼠标、键盘或者USB存储器,也可以是其他类型的设备,比如音频设备、网络设备等。当USB设备插入USB端口时,USB控制器会检测它并配置它以进行通信。 3. USB主机:USB主机是计算机的USB端口,可能是物理接口或者更常见的是内置USB控制器。USB主机负责将USB设备与计算机相连,并提供电源和必要的通信支持。 4. USB总线:USB总线包含所有连接到USB主机的USB设备和USB控制器,它负责协调数据传输和通信。USB总线通常基于主从架构,其中USB主机作为主控制器,而USB设备则作为从控制器。 总而言之,Linux USB硬件架构由这四个主要组件组成:USB控制器、USB设备、USB主机和USB总线,它们协同工作来构建一个完整的USB系统。在Linux操作系统中,用户可以通过USB相关的驱动程序与USB设备进行通信,从而使用各种各样的USB功能。 ### 回答2: Linux USB硬件架构是指Linux系统中支持USB设备的软件和硬件资源。 Linux系统通过USB主机控制器驱动程序与硬件交互,并通过设备驱动程序将USB设备与系统绑定。 Linux USB硬件架构包括三个主要组件:USB主机控制器、USB设备以及USB总线。 USB主机控制器是负责控制USB总线的控制器芯片,负责驱动USB总线上的设备。USB设备是指接入USB总线上的所有设备,包括鼠标、键盘、打印机、手机等等。USB总线是连接USB主机控制器和USB设备之间的物理媒介,负责传输数据和控制信号。 在Linux中,USB主机控制器驱动程序负责与硬件交互,将USB设备连接到系统中,并管理USB总线设备的初始化和配置工作。设备驱动程序负责将USB设备与系统中的相关服务和设备匹配,需要向系统内核注册自己的设备代码,以便系统可以识别和管理设备。 总之,Linux USB硬件架构涉及到系统内核驱动程序、USB主机控制器和USB设备之间的交互和通信,为用户提供了高效、稳定、可靠的USB设备支持和管理功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值