linux设备驱动(27)usb驱动-热插拔详解
1 热插拔的基本概念
1.1 usb热插拔的硬件原理
在USB集线器(hub)的每个下游端⼝的D+和D-上,分别接了⼀个15K欧姆的下拉电阻到地。这样,在集线器的端⼝悬空时,就被这两个下拉电阻拉到了低电平。
⽽在USB设备端,在D+或者D-上接了1.5K欧姆上拉电阻。对于全速和⾼速设备,上拉电阻是接在D+上;⽽低速设备则是上拉电阻接在D-上。这样,当设备插⼊到集线器时,由1.5K的上拉电阻和15K的下拉电阻分压,结果就将差分数据线中的⼀条拉⾼了。集线器检测到这个状态后,它就报告给USB主控制器(或者通过它上⼀层的集线器报告给USB主控制器),这样就检测到设备的插⼊了。USB⾼速设备先是被识别为全速设备,然后通过HOST和DEVICE两者之间的确认,再切换到⾼速模式的。在⾼速模式下,是电流传输模式,这时将D+上的上拉电阻断开。
1.2 热插拔的概念
热插拔(hot-plugging或Hot Swap)即带电插拔,热插拔功能就是允许⽤户在不关闭系统,不切断电源的情况下取出和更换损坏的硬盘、电源或板卡等部件,从⽽提⾼了系统对灾难的及时恢复能⼒、扩展性
和灵活性等,例如⼀些⾯向⾼端应⽤的磁盘镜像系统都可以提供磁盘的热插拔功能。具体⽤学术的说法就是:热替换(Hot replacement)、热添加(hot expansion)和热升级(hot upgrade)
1.3 热插拔的优点
在系统开机情况下将损坏的模块移除,还可以在开机情况下做更新或扩容⽽不影响系统操作。由于热插拔零件的可靠度提升,还可以将它们⽤做断电器,⽽且因为热插拔能够⾃动恢复,有很多热插拔芯⽚为系统提供线路供电情况的信号,以便系统做故障分析,因此减少了成本。
2 热插拔的实现
2.1 Linux下USB HUB的驱动的实现和分析:英语四级查询
在系统初始化的时候在usb_init函数中调⽤usb_hub_init函数,就进⼊了hub的初始化。
在usb_hub_init函数中完成了注册hub驱动,并且利⽤函数kthread_run创建⼀个内核线程。该线程⽤来管理监视hub的状态,所有的情况都通过该线程来报告。
USB设备是热插拔,这就和PCI设备不同,PCI设备是在系统启动的时候都固定了,因此PCI设备只需要初始化进⾏枚举就可以了,采⽤递归算法即可。⽽USB设备需要热插拔,因此在hub_probe函数中调⽤hub_configure函数来配置hub,在这个函数中主要是利⽤函数现百年前水果蛋糕
usb_alloc_urb函数来分配⼀个urb,利⽤usb_fill_int_urb来初始化这个urb结构,包括hub的中断服务程序hub_irq的,查询的周期等。
每当有设备连接到USB接⼝时,USB总线在查询hub状态信息的时候会触发hub的中断服务程序hub_irq,在该函数中利⽤kick_khubd将hub结构通过event_list添加到khubd的队列hub_event_list,然后唤醒khubd。进⼊hub_events函数,该函数⽤来处理khubd事件队列,从khubd的hub_event_list中的每个usb_hub数据结构。该函数中⾸先判断hub是否出错,然后通过⼀个for循环来检测每个端⼝的状态信息。利⽤
usb_port_status获取端⼝信息,如果发⽣变化就调⽤hub_port_connect_change函数来配置端⼝等。
2.2 软件层次分析
这⾥我们先讲讲USB热插拔事件的处理⼯作。---Khubd守护进程。
特朗普女儿演讲
-Khubd守护进程它是⼀个守护进程,来检查usb port的事件通知HCD和usb core,然后做相应的处理。
主要分析khub的⼯作原理:硬件层次是hub的⼯作,如何和host及其设备间通信及相应事件。
2.3 初始化函数
定义位于:drivers\usb\core\hub.c
1int usb_hub_init(void)
2 {
3if (usb_register(&hub_driver) < 0) {//usb驱动注册
4 printk(KERN_ERR "%s: can't register hub driver\n",
5 usbcore_name);
6return -1;
7 }
8
9 khubd_task = kthread_run(hub_thread, NULL, "khubd");//建⽴usb hub线程
10if (!IS_ERR(khubd_task))
11return0;
12
13/* Fall through if kernel_thread failed */
14 usb_deregister(&hub_driver);
15 printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);
16
17return -1;
18 }
2.4 usb hub线程处理函数
1static int hub_thread(void *__unud)
2 {
3/* khubd needs to be freezable to avoid intefering with USB-PERSIST
4 * port handover. Otherwi it might e that a full-speed device
5 * was gone before the EHCI controller had handed its port over to
6 * the companion full-speed controller.
7*/
8 t_freezable();
9
10do {
11 hub_events();//执⾏⼀次hub事件函数,要想执⾏hub_events(),都要等待khubd_wait这个中断唤醒才⾏
12 wait_event_freezable(khubd_wait,//每次执⾏⼀次hub事件,都会进⼊⼀次等待事件中断函数
13 !list_empty(&hub_event_list) ||
14 kthread_should_stop());
15 } while (!kthread_should_stop() || !list_empty(&hub_event_list));//如果有事件就加⼊hub_event_list
16
17 pr_debug("%s: khubd exiting\n", usbcore_name);
industrialize18return0;
19 }
从上⾯函数中得到, 要想执⾏hub_events(),都要等待khubd_wait这个中断唤醒才⾏。2.5 usb hub事件函数
1static void hub_events(void)
2 {
3struct list_head *tmp;
4struct usb_device *hdev;
5struct usb_interface *intf;
6struct usb_hub *hub;
7struct device *hub_dev;
8 u16 hubstatus;
9 u16 hubchange;
10 u16 portstatus;
11 u16 portchange;
12int i, ret;
13int connect_change, wakeup_change;
14
15/*
16 * We restart the list every time to avoid a deadlock with
17 * deleting hubs downstream from this one. This should be
18 * safe since we delete the hub from the event list.
19 * Not the most efficient, but avoids deadlocks.
20*/
21while (1) {
22
23/* Grab the first entry at the beginning of the list */
24 spin_lock_irq(&hub_event_lock);
25if (list_empty(&hub_event_list)) {
26 spin_unlock_irq(&hub_event_lock);
27break;
28 }
29
30 tmp = hub_;
31 list_del_init(tmp);
32
33 hub = list_entry(tmp, struct usb_hub, event_list);
34 kref_get(&hub->kref);
35 hdev = hub->hdev;
36 usb_get_dev(hdev);
37 spin_unlock_irq(&hub_event_lock);
38
39 hub_dev = hub->intfdev;
40 intf = to_usb_interface(hub_dev);
41 dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
42 hdev->state, hub->descriptor
43 ? hub->descriptor->bNbrPorts
44 : 0,
45/* NOTE: expects max */
46 (u16) hub->change_bits[0],
47 (u16) hub->event_bits[0]);
48
49/* Lock the device, then check to e if we were
50 * disconnected while waiting for the lock to succeed. */
51 usb_lock_device(hdev);
52if (unlikely(hub->disconnected))
53goto loop_disconnected;
透明硬纱54
55/* If the hub has died, clean up after it */
56if (hdev->state == USB_STATE_NOTATTACHED) {
57 hub->error = -ENODEV;
58 hub_quiesce(hub, HUB_DISCONNECT);
59goto loop;
60 }
61
62/* Autoresume */
63 ret = usb_autopm_get_interface(intf);
64if (ret) {
65 dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);prior什么意思
66goto loop;
67 }
68
69/* If this is an inactive hub, do nothing */
70if (hub->quiescing)
71goto loop_autopm;
72
73if (hub->error) {
74 dev_dbg (hub_dev, "retting for error %d\n",
75 hub->error);
76
77 ret = usb_ret_device(hdev);
78if (ret) {
79 dev_dbg (hub_dev,
80"error retting hub: %d\n", ret);
81goto loop_autopm;
82 }
83
84 hub->nerrors = 0;
85 hub->error = 0;
86 }
87
88/* deal with port status changes */
89for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
90if (test_bit(i, hub->busy_bits))
91continue;
92 connect_change = test_bit(i, hub->change_bits);
93 wakeup_change = test_and_clear_bit(i, hub->wakeup_bits); 94if (!test_and_clear_bit(i, hub->event_bits) &&
95 !connect_change && !wakeup_change)
96continue;
97
98 ret = hub_port_status(hub, i,
99 &portstatus, &portchange);
100if (ret < 0)
101continue;
102
103if (portchange & USB_PORT_STAT_C_CONNECTION) { 104 usb_clear_port_feature(hdev, i,
105 USB_PORT_FEAT_C_CONNECTION);
106 connect_change = 1;
107 }
108
109if (portchange & USB_PORT_STAT_C_ENABLE) {
110if (!connect_change)
111 dev_dbg (hub_dev,
112"port %d enable change, "
113"status %08x\n",
114 i, portstatus);
115 usb_clear_port_feature(hdev, i,
116 USB_PORT_FEAT_C_ENABLE);
117
118/*
119 * EM interference sometimes caus badly
120 * shielded USB devices to be shutdown by
121 * the hub, this hack enables them again.
122 * Works at least with mou driver.
123*/
124if (!(portstatus & USB_PORT_STAT_ENABLE)
125 && !connect_change
126 && hub->ports[i - 1]->child) {
127 dev_err (hub_dev,
128"port %i "
129"disabled by hub (EMI?), "
\n",
131 i);
132 connect_change = 1;
133 }
134 }
135
136if (hub_handle_remote_wakeup(hub, i,
137 portstatus, portchange))
138 connect_change = 1;
139
140if (portchange & USB_PORT_STAT_C_OVERCURRENT) { 141 u16 status = 0;
prompts142 u16 unud;
143
144 dev_dbg(hub_dev, "over-current change on port "
145"%d\n", i);
146 usb_clear_port_feature(hdev, i,
147 USB_PORT_FEAT_C_OVER_CURRENT);
148 msleep(100); /* Cool down */
149 hub_power_on(hub, true);
150 hub_port_status(hub, i, &status, &unud);
151if (status & USB_PORT_STAT_OVERCURRENT)
152 dev_err(hub_dev, "over-current "
153"condition on port %d\n", i);
154 }
155
156if (portchange & USB_PORT_STAT_C_RESET) {
157 dev_dbg (hub_dev,
158"ret change on port %d\n",
159 i);
160 usb_clear_port_feature(hdev, i,
161 USB_PORT_FEAT_C_RESET);
162 }
163if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
164 hub_is_superspeed(hub->hdev)) {
165 dev_dbg(hub_dev,
166"warm ret change on port %d\n",
167 i);
168 usb_clear_port_feature(hdev, i,
169 USB_PORT_FEAT_C_BH_PORT_RESET);
170 }
171if (portchange & USB_PORT_STAT_C_LINK_STATE) {
172 usb_clear_port_feature(hub->hdev, i,
173 USB_PORT_FEAT_C_PORT_LINK_STATE);
174 }
175if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
176 dev_warn(hub_dev,
177"config error on port %d\n",
178 i);
179 usb_clear_port_feature(hub->hdev, i,
180 USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
181 }
182
183/* Warm ret a USB3 protocol port if it's in
184 * SS.Inactive state.
185*/
186if (hub_port_warm_ret_required(hub, portstatus)) {
187int status;
188struct usb_device *udev =
189 hub->ports[i - 1]->child;
190
191 dev_dbg(hub_dev, "warm ret port %d\n", i);
192if (!udev ||
193 !(portstatus & USB_PORT_STAT_CONNECTION) ||
194 udev->state == USB_STATE_NOTATTACHED) {
195 status = hub_port_ret(hub, i,
196 NULL, HUB_BH_RESET_TIME,
197true);
198if (status < 0)
199 hub_port_disable(hub, i, 1);
200 } el {
201 usb_lock_device(udev);
202 status = usb_ret_device(udev);
203 usb_unlock_device(udev);
204 connect_change = 0;
205 }
206 }
207
lancelot208if (connect_change)//检测到port状态变化,调⽤以下的函数连接端⼝209 hub_port_connect_change(hub, i,
210 portstatus, portchange);
211 } /* end for i */
212
213/* deal with hub status changes */
214if (test_and_clear_bit(0, hub->event_bits) == 0)
215 ; /* do nothing */
216el if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
217 dev_err (hub_dev, "get_hub_status failed\n");
218el {
219if (hubchange & HUB_CHANGE_LOCAL_POWER) {
220 dev_dbg (hub_dev, "power change\n");
221 clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
222if (hubstatus & HUB_STATUS_LOCAL_POWER)
223/* FIXME: Is this always true? */
224 hub->limited_power = 1;
225el
226 hub->limited_power = 0;
227 }
228if (hubchange & HUB_CHANGE_OVERCURRENT) {
229 u16 status = 0;
230 u16 unud;
231
232 dev_dbg(hub_dev, "over-current change\n");
233 clear_hub_feature(hdev, C_HUB_OVER_CURRENT); 234 msleep(500); /* Cool down */
235 hub_power_on(hub, true);
236 hub_hub_status(hub, &status, &unud);
237if (status & HUB_STATUS_OVERCURRENT)
238 dev_err(hub_dev, "over-current "
239"condition\n");
240 }
241 }
242
243 loop_autopm:
244/* Balance the usb_autopm_get_interface() above */
245 usb_autopm_put_interface_no_suspend(intf);
246 loop:
247/* Balance the usb_autopm_get_interface_no_resume() in 248 * kick_khubd() and allow autosuspend.
249*/
250 usb_autopm_put_interface(intf);
251 loop_disconnected:
252 usb_unlock_device(hdev);
253 usb_put_dev(hdev);
254 kref_put(&hub->kref, hub_relea);
255
256 } /* end while (1) */
257 }
2.6 函数hub_port_connect_change
作⽤是连接端⼝。
1static void hub_port_connect_change(struct usb_hub *hub, int port1,
2 u16 portstatus, u16 portchange)
3 {
4struct usb_device *hdev = hub->hdev;
5struct device *hub_dev = hub->intfdev;
6struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
7 unsigned wHubCharacteristics =
8 le16_to_cpu(hub->descriptor->wHubCharacteristics);
9struct usb_device *udev;
10int status, i;
11 unsigned unit_load;
12
13 dev_dbg (hub_dev,
14"port %d, status %04x, change %04x, %s\n",
15 port1, portstatus, portchange, portspeed(hub, portstatus));
16
17if (hub->has_indicators) {
18 t_port_led(hub, port1, HUB_LED_AUTO);
19 hub->indicator[port1-1] = INDICATOR_AUTO;
20 }
21
22 #ifdef CONFIG_USB_OTG
23/* during HNP, don't repeat the debounce */
24if (hdev->bus->is_b_host)
25 portchange &= ~(USB_PORT_STAT_C_CONNECTION |
26 USB_PORT_STAT_C_ENABLE);
27#endif
28
29/* Try to resuscitate an existing device */
30 udev = hub->ports[port1 - 1]->child;
31if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
32 udev->state != USB_STATE_NOTATTACHED) {
33 usb_lock_device(udev);
34if (portstatus & USB_PORT_STAT_ENABLE) {
35 status = 0; /* Nothing to do */
36
37 #ifdef CONFIG_PM_RUNTIME
38 } el if (udev->state == USB_STATE_SUSPENDED &&
39 udev->persist_enabled) {
40/* For a suspended device, treat this as a
41 * remote wakeup event.
42*/
43 status = usb_remote_wakeup(udev);
44#endif
45
46 } el {
女生英语名>兼职英语翻译