分类:LINUX
谨以此文纪念过往的岁月
一.前言
在上文中讲述了usb子系统的初始化,在该文中来看usb hub的驱动。不过在这里需要说明一点,host controller和usb hub是不同的东西。hc是指usb的控制器,而hub是一种usb集线器,其本质还是一种usb设备,只不过这种usb设备有点特殊而已。一般的MCU都会自带一个usb hub,以s3c6410为例,其带有一个hub,该hub有两个下行口,就是俗话所说的两个usb接口。你可以在把两个usb设备插到该hub下,再多就不行了。而该hub就是传说中的root hub,以该root hub为例,如果在该hub下还挂载一个hub,那插入的hub不是root hub而是一个hub设备,对于linux而言其会为每一个hub创建一条总线,用于该hub下的设备与驱动绑定。对于一般系统而言,一般一个hc对应一个root hub。
二.hub设备
关于root hub的设备注册还记否,在上文OHCI中已经涉及到了,不过特意点明而已,就是在register_root_hub中。
三.hub驱动
usb hub其实也是一种usb设备,其驱动不脱离usb驱动的模板。在usb_init中会调用usb_hub_init函数来注册hub的驱动。usb_register(&hub_driver),注册一个hub驱动。
那下面来看该hub_driver的具体每一个函数。
static struct usb_driver hub_driver = {
.name ='hub',
.probe =hub_probe,
.disconnect =hub_disconnect,
.suspend =hub_suspend,
.resume =hub_resume,
.reset_resume =hub_reset_resume,
.pre_reset =hub_pre_reset,
.post_reset =hub_post_reset,
.ioctl =hub_ioctl,
.id_table =hub_id_table,
.supports_autosuspend =1,
};
2.1 hub_probe
hub探测函数,在驱动注册或设备注册时会调用。
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_host_interface *desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *hdev;
struct usb_hub *hub;
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
if (hdev->level == MAX_TOPO_LEVEL) { --只只支持六层嵌套,就是所谓的hub接hub,最多接六层。
return -E2BIG;
}
if ((desc->desc.bInterfaceSubClass != 0) && (desc->desc.bInterfaceSubClass != 1)) {--一些湖北的subclass = 1。根据usb的spec这是不允许的不过这还是可以工作的的,这世界总有些特殊的嘛,即是在协议界。
descriptor_error:
return -EIO;
}
if (desc->desc.bNumEndpoints != 1)--记住hub的端点只能有一个!!
goto descriptor_error;
endpoint = &endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))--而且这个端点还必须是中断in类型。
goto descriptor_error;
hub = kzalloc(sizeof(*hub), GFP_KERNEL); --在上述情况都满足的情况下才能说有一个hub存在。
if (!hub) {
return -ENOMEM;
}
kref_init(kref);--hub参数的初始化。
INIT_LIST_HEAD(event_list); --事件链表,每一次的插拔都会引起hub的中断。会将该链表添加到hub_event_list链表中,同时唤醒khubd_wait即hub守护程序。
hub->intfdev = &dev;--接口设备
hub->hdev = hdev;--hub的实体。
(leds, led_work);
INIT_DELAYED_WORK(init_work, NULL);
usb_get_intf(intf);
usb_set_intfdata (intf, hub);
intf->needs_remote_wakeup = 1;
if (hdev->speed == USB_SPEED_HIGH)
highspeed_hubs++;
if ((hub, endpoint) >= 0)
return 0;
hub_disconnect (intf);
return -ENODEV;
}
在创建一个驱动的hub的实例后,就需要对hub的配置。你会发现这是一个巨大的函数,不要畏惧其的巨大,还是一如既往的去分析他吧!
static int hub_configure(struct usb_hub *hub,struct usb_endpoint_descriptor *endpoint)
{
struct usb_device *hdev = hub->hdev;
struct device *hub_dev = hub->intfdev;
u16 hubstatus, hubchange;
u16 wHubCharacteristics;
unsigned int pipe;
int maxp, ret;
char *message;
hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL,--为hub开辟缓冲区
buffer_dma);
hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
mutex_init(status_mutex);
hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
/* Request the entire hub descriptor.
* hub->descriptor can handle USB_MAXCHILDREN ports,
* but the hub can/will return fewer bytes here.
*/
ret = get_hub_descriptor(hdev, hub->descriptor,sizeof(*hub->descriptor));--获取hub的描述符,记住hub从本质讲还是一种usb设备,只不过是一种特殊的usb设备而已。其设备的描述符与普通的usb设备是不同的。关于hub的描述符的具体说明,可以去看usb2.0协议。
if (ret < 0) {
message = 'can't read hub descriptor';
goto fail;
} else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {--hub的下行端口最多32个。
message = 'hub has too many ports!';
ret = -ENODEV;
goto fail;
}
hdev->maxchild = hub->descriptor->bNbrPorts;
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);--虽说对于usb的hub的描述符可以看spec,但是这里还是不得不来看一下这个描述hub特性的信息。
if (& HUB_CHAR_COMPOUND) { --判断hub是否为混合设备的一部分。如果是的话,就需要存储其每一个下行端口是否可以被移除。
inti;
charportstr [USB_MAXCHILDREN + 1];
for (i = 0; i < hdev->maxchild; i++)
portstr[i] = hub->descriptor->DeviceRemovable
[((i + 1) / 8)] < ((i + 1) % 8))
'F' : 'R';
portstr[hdev->maxchild] = 0;
dev_dbg(hub_dev, 'compound device; port removable status: %s\n', portstr);
} else
dev_dbg(hub_dev, 'standalone hub\n');
switch (wHubCharacteristics & HUB_CHAR_LPSM) { --判断hub电源管理的类型
case 0x00:--统一的电源管理
break;
case 0x01:--每一个端口的单独的电源管理
break;
case 0x02:--保留
case 0x03:
break;
}
switch (wHubCharacteristics & HUB_CHAR_OCPM) {--过流保护类型
case 0x00:--全局的过流保护
break;
case 0x08:--每一个端口的过流保护
break;
case 0x10:--没有过流保护
case 0x18:
break;
}
spin_lock_init (tt.lock);
INIT_LIST_HEAD (tt.clear_list);
INIT_WORK (tt.kevent, hub_tt_kevent);
switch (hdev->descriptor.bDeviceProtocol) { --hub对于高速到低速的支持
case 0:
break;
case 1:
dev_dbg(hub_dev, 'Single TT\n');
hub->tt.hub = hdev;--对于所有的端口而言,公用一个TT
break;
case 2:
ret = usb_set_interface(hdev, 0, 1);
if (ret == 0) {
dev_dbg(hub_dev, 'TT per port\n');--每一端口均有一个TT转换
hub->tt.multi = 1;
} else
dev_err(hub_dev, 'Using single TT (err %d)\n',ret);
hub->tt.hub = hdev;
break;
default:
break;
}
/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
switch (wHubCharacteristics & HUB_CHAR_TTTT) {--定义TT转化时间
case HUB_TTTT_8_BITS:
if (hdev->descriptor.bDeviceProtocol != 0) {
hub->tt.think_time = 666;
}
break;
case HUB_TTTT_16_BITS:
hub->tt.think_time = 666 * 2;
break;
case HUB_TTTT_24_BITS:
hub->tt.think_time = 666 * 3;
break;
case HUB_TTTT_32_BITS:
hub->tt.think_time = 666 * 4;
break;
}
/* probe() zeroes hub->indicator[] */
if (wHubCharacteristics & HUB_CHAR_PORTIND) {--是否支持指示灯
hub->has_indicators = 1;}
--在此说明描述符中的一个成员bPwrOn2PwrGood,该成员*2说明hub上电到电源稳定的时间.
ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);--获得hub的状态
if (ret < 2) {
message = 'can't get hub status';
goto fail;
}
le16_to_cpus(&hubstatus);
if (hdev == hdev->bus->root_hub) {--如果该hub为root hub
if (hdev->bus_mA == 0 || hdev->bus_mA >= 500)
hub->mA_per_port = 500;
else {
hub->mA_per_port = hdev->bus_mA;
hub->limited_power = 1;
}
} else if ((hubstatus < USB_DEVICE_SELF_POWERED)) == 0) {
hub->limited_power = 1;
if (hdev->maxchild > 0) {
int remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent;
if (remaining < hdev->maxchild * 100)
hub->mA_per_port = 100;/* 7.2.1.1 */
}
} else {/* Self-powered external hub */
hub->mA_per_port = 500;
}
--上面是一些hub的电源管理。
ret = hub_hub_status(hub, &hubstatus, &hubchange);
--为hub传输创建urb,记住hub永远是一个usb设备,其传输也是需要通过urb的。
pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
if (maxp > sizeof(*hub->buffer))
maxp = sizeof(*hub->buffer);
hub->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!hub->urb) {
message = 'couldn't allocate interrupt urb';
ret = -ENOMEM;
goto fail;
}
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
hub->urb->transfer_dma = hub->buffer_dma;
hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
--上面是一个标准中断urb的申请
/* maybe cycle the hub leds */
if (hub->has_indicators && blinkenlights)
hub->indicator [0] = INDICATOR_CYCLE;
hub_activate(hub, HUB_INIT);
return 0;
}
这个函数从字面意思来讲,是让hub开始工作,那我们来看,这个具体是怎么干的。对于下面的函数最好是参考usb协议,所谓不识协议,看懂代码也枉然啊!
static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
struct usb_device *hdev = hub->hdev;
int port1;
int status;
bool need_debounce_delay = false;
unsigned delay;
/* Continue a partial initialization */
if (type == HUB_INIT2)
goto init2;
if (type == HUB_INIT3)
goto init3;
if (type != HUB_RESUME) {--判断是否为恢复,因为hub挂起时,电源仍然是打开的。
if (type == HUB_INIT) {
delay = hub_power_on(hub, false); --打开电源不延时
schedule_delayed_work(init_work,msecs_to_jiffies(delay));--其所在的工作队列线程被唤醒后,延时一段时间运行。
to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
return;
} else {
hub_power_on(hub, true);--打开电源,同时延时
}
}
init2:
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
struct usb_device *udev = hdev->children[port1-1];
u16 portstatus, portchange;
portstatus = portchange = 0;
status = hub_port_status(hub, port1, &portstatus, &portchange); --获取hub下行端口状态以及端口改变状态。关于其各个bit位的含义,在usb 2.0 spec中的11.24.2.7说明。在除了hub恢复挂起状态的任何情况下,都需要将所有的端口禁止。如果端口没有设备同样需要禁止。
if ((portstatus & USB_PORT_STAT_ENABLE) && (
type != HUB_RESUME ||
!(portstatus & USB_PORT_STAT_CONNECTION) ||
!udev ||
udev->state == USB_STATE_NOTATTACHED)) {
clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
portstatus &= ~USB_PORT_STAT_ENABLE;
}
if (portchange & USB_PORT_STAT_C_CONNECTION) { --如果有一个设备连接
need_debounce_delay = true;--去抖
clear_port_feature(hub->hdev, port1,); --清除端口连接标志,记住这个其实将HcRhPortStatus[port-1]中第USB_PORT_FEAT_C_CONNECTION位定义为1,下面的是同样的。
}
if (portchange & USB_PORT_STAT_C_ENABLE) {
need_debounce_delay = true;
clear_port_feature(hub->hdev, port1,USB_PORT_FEAT_C_ENABLE);
}
if (!udev || udev->state == USB_STATE_NOTATTACHED) {
if (udev || (portstatus & USB_PORT_STAT_CONNECTION)) –通知khubd有设备移除或有新的设备。
set_bit(port1, hub->change_bits);
} else if (portstatus & USB_PORT_STAT_ENABLE) {
if (portchange)
set_bit(port1, hub->change_bits);
} else if (udev->persist_enabled) {
#ifdef CONFIG_PM
udev->reset_resume = 1;
#endif
set_bit(port1, hub->change_bits);
} else {
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
set_bit(port1, hub->change_bits);
}
}
if (need_debounce_delay) {
delay = HUB_DEBOUNCE_STABLE;
if (type == HUB_INIT2) {
schedule_delayed_work(init_work,msecs_to_jiffies(delay));
return;/* Continues at init3: below */
} else {
msleep(delay);
}
}
init3:
hub->quiescing = 0;
status = usb_submit_urb(hub->urb, GFP_NOIO); --提交urb
if (hub->has_indicators && blinkenlights)
schedule_delayed_work(leds, LED_CYCLE_PERIOD);
kick_khubd(hub); --扫描所有的端口
}
static void kick_khubd(struct usb_hub *hub)
{
unsigned longflags;
to_usb_interface(hub->intfdev)->pm_usage_cnt = 1; --在处理该函数时禁止挂起
spin_lock_irqsave(&hub_event_lock, flags);
if (!hub->disconnected && list_empty(event_list)) {
list_add_tail(event_list, &hub_event_list);
wake_up(&khubd_wait);
}
spin_unlock_irqrestore(&hub_event_lock, flags);
}
也许看上面的代码很困惑,怎么会那么乱呢?其实这个函数会被分为三步来执行的。那我们来一步步来看吧。
话说在hub_probe时会调用hub_active是参数为HUB_INIT,此时会执行下面代码。
delay = hub_power_on(hub, false);
PREPARE_DELAYED_WORK(init_work,);
schedule_delayed_work(init_work,msecs_to_jiffies())
其实上述代码是等待一段时间后再执行,即会在delay个ms后再执行,在该段时间内等待电源稳定。在电源稳定后调用hub_init_func2,在该函数中hub_activate(hub, HUB_INIT2);该函数会直接跳到init2之处的函数来执行代码,即查看各个端口的状态以及端口改变状况。在之后会执行
PREPARE_DELAYED_WORK(init_work, hub_init_func3);
schedule_delayed_work(init_work,msecs_to_jiffies(delay));
在等待一段时间后,继续执行执行hub_init_func3,在该函数中会调用hub_activate(hub, HUB_INIT3);会直接跳到init3中。呵呵,下面就能好懂很多了吧。关于android的电源管理以后再看。
Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-qformat:yes; mso-style-parent:''; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:'Calibri','sans-serif'; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-font-kerning:1.0pt;}
我们再看来hub的urb中断处理hub_irq,在一切都很正常的情况下,将会调用kick_khubd函数。在端口发生状态发生改变时,会调用该中断。
static void hub_irq(struct urb *urb)
{
struct usb_hub *hub = urb->context;
int status = urb->status;
int i;
unsigned long bits;
switch (status) {
case 0:--正常情况下会会保存port改变信息
bits = 0;
for (i = 0; i < urb->actual_length; ++i)
bits |= ((unsigned long) ((*hub->buffer)[i]))<< (i*8);
hub->event_bits[0] = bits;
break;
}
hub->nerrors = 0;
/* Something happened, let khubd figure it out */
kick_khubd(hub);
resubmit:
if (hub->quiescing)
return;
if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
&& status != -ENODEV && status != -EPERM)
dev_err (hub->intfdev, 'resubmit --> %d\n', status);
}
kick_khubd函数如下
static void(struct usb_hub *hub)
{
unsigned longflags;
to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;--禁止自动挂起,知道khubd执行
spin_lock_irqsave(&hub_event_lock, flags);
if (!hub->disconnected && list_empty(event_list)) {
list_add_tail(event_list, &hub_event_list);
wake_up(&khubd_wait);--唤醒守护程序。
}
spin_unlock_irqrestore(&hub_event_lock, flags);
}
三总结
Hub是一种usb设备,只不过这种设备有特殊而已。关于hub具体的处理下文再看。
阅读(2844) | 评论(0) | 转发(0) |
0
上一篇: