当前位置: 首页 > 工具软件 > Stat Hub > 使用案例 >

Linux复位usb hub,linux usb hub初始化-steven

隗瑞
2023-12-01

分类: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

上一篇:

 类似资料: