`
dato0123
  • 浏览: 915484 次
文章分类
社区版块
存档分类
最新评论

Linux那些事儿之我是Hub(20)八大重量级函数闪亮登场(四)

 
阅读更多

接下来我们来到了第四个函数,hub_port_init().这个函数和接下来要遇到的usb_new_device()是最重要的两个函数,也是相对复杂的函数.

2096 /* Reset device, (re)assign address, get device descriptor.

2097 * Device connection must be stable, no more debouncing needed.

2098 * Returns device in USB_STATE_ADDRESS, except on error.

2099 *

2100 * If this is called for an already-existing device (as part of

2101 * usb_reset_device), the caller must own the device lock. For a

2102 * newly detected device that is not accessible through any global

2103 * pointers, it's not necessary to lock the device.

2104 */

2105 static int

2106 hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,

2107 int retry_counter)

2108 {

2109 static DEFINE_MUTEX(usb_address0_mutex);

2110

2111 struct usb_device *hdev = hub->hdev;

2112 int i, j, retval;

2113 unsigned delay = HUB_SHORT_RESET_TIME;

2114 enum usb_device_speed oldspeed = udev->speed;

2115 char *speed, *type;

2116

2117 /* root hub ports have a slightly longer reset period

2118 * (from USB 2.0 spec, section 7.1.7.5)

2119 */

2120 if (!hdev->parent) {

2121 delay = HUB_ROOT_RESET_TIME;

2122 if (port1 == hdev->bus->otg_port)

2123 hdev->bus->b_hnp_enable = 0;

2124 }

2125

2126 /* Some low speed devices have problems with the quick delay, so */

2127 /* be a bit pessimistic with those devices. RHbug #23670 */

2128 if (oldspeed == USB_SPEED_LOW)

2129 delay = HUB_LONG_RESET_TIME;

2130

2131 mutex_lock(&usb_address0_mutex);

2132

2133 /* Reset the device; full speed may morph to high speed */

2134 retval = hub_port_reset(hub, port1, udev, delay);

2135 if (retval < 0) /* error or disconnect */

2136 goto fail;

2138 retval = -ENODEV;

2139

2140 if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {

2141 dev_dbg(&udev->dev, "device reset changed speed!/n");

2142 goto fail;

2143 }

2144 oldspeed = udev->speed;

2145

2146 /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...

2147 * it's fixed size except for full speed devices.

2148 * For Wireless USB devices, ep0 max packet is always 512 (tho

2149 * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].

2150 */

2151 switch (udev->speed) {

2152 case USB_SPEED_VARIABLE: /* fixed at 512 */

2153 udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(512);

2154 break;

2155 case USB_SPEED_HIGH: /* fixed at 64 */

2156 udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);

2157 break;

2158 case USB_SPEED_FULL: /* 8, 16, 32, or 64 */

2159 /* to determine the ep0 maxpacket size, try to read

2160 * the device descriptor to get bMaxPacketSize0 and

2161 * then correct our initial guess.

2162 */

2163 udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);

2164 break;

2165 case USB_SPEED_LOW: /* fixed at 8 */

2166 udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(8);

2167 break;

2168 default:

2169 goto fail;

2170 }

2171

2172 type = "";

2173 switch (udev->speed) {

2174 case USB_SPEED_LOW: speed = "low"; break;

2175 case USB_SPEED_FULL: speed = "full"; break;

2176 case USB_SPEED_HIGH: speed = "high"; break;

2177 case USB_SPEED_VARIABLE:

2178 speed = "variable";

2179 type = "Wireless ";

2180 break;

2181 default: speed = "?"; break;

2182 }

2183 dev_info (&udev->dev,

2184 "%s %s speed %sUSB device using %s and address %d/n",

2185 (udev->config) ? "reset" : "new", speed, type,

2186 udev->bus->controller->driver->name, udev->devnum);

2187

2188 /* Set up TT records, if needed */

2189 if (hdev->tt) {

2190 udev->tt = hdev->tt;

2191 udev->ttport = hdev->ttport;

2192 } else if (udev->speed != USB_SPEED_HIGH

2193 && hdev->speed == USB_SPEED_HIGH) {

2194 udev->tt = &hub->tt;

2195 udev->ttport = port1;

2196 }

2197

2198 /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?

2199 * Because device hardware and firmware is sometimes buggy in

2200 * this area, and this is how Linux has done it for ages.

2201 * Change it cautiously.

2202 *

2203 * NOTE: If USE_NEW_SCHEME() is true we will start by issuing

2204 * a 64-byte GET_DESCRIPTOR request. This is what Windows does,

2205 * so it may help with some non-standards-compliant devices.

2206 * Otherwise we start with SET_ADDRESS and then try to read the

2207 * first 8 bytes of the device descriptor to get the ep0 maxpacket

2208 * value.

2209 */

2210 for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {

2211 if (USE_NEW_SCHEME(retry_counter)) {

2212 struct usb_device_descriptor *buf;

2213 int r = 0;

2214

2215 #define GET_DESCRIPTOR_BUFSIZE 64

2216 buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);

2217 if (!buf) {

2218 retval = -ENOMEM;

2219 continue;

2220 }

2221

2222 /* Retry on all errors; some devices are flakey.

2223 * 255 is for WUSB devices, we actually need to use

2224 * 512 (WUSB1.0[4.8.1]).

2225 */

2226 for (j = 0; j < 3; ++j) {

2227 buf->bMaxPacketSize0 = 0;

2228 r = usb_control_msg(udev, usb_rcvaddr0pipe(),

2229 USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,

2230 USB_DT_DEVICE << 8, 0,

2231 buf, GET_DESCRIPTOR_BUFSIZE,

2232 USB_CTRL_GET_TIMEOUT);

2233 switch (buf->bMaxPacketSize0) {

2234 case 8: case 16: case 32: case 64: case 255:

2235 if (buf->bDescriptorType ==

2236 USB_DT_DEVICE) {

2237 r = 0;

2238 break;

2239 }

2240 /* FALL THROUGH */

2241 default:

2242 if (r == 0)

2243 r = -EPROTO;

2244 break;

2245 }

2246 if (r == 0)

2247 break;

2248 }

2249 udev->descriptor.bMaxPacketSize0 =

2250 buf->bMaxPacketSize0;

2251 kfree(buf);

2252

2253 retval = hub_port_reset(hub, port1, udev, delay);

2254 if (retval < 0) /* error or disconnect */

2255 goto fail;

2256 if (oldspeed != udev->speed) {

2257 dev_dbg(&udev->dev,

2258 "device reset changed speed!/n");

2259 retval = -ENODEV;

2260 goto fail;

2261 }

2262 if (r) {

2263 dev_err(&udev->dev, "device descriptor "

2264 "read/%s, error %d/n",

2265 "64", r);

2266 retval = -EMSGSIZE;

2267 continue;

2268 }

2269 #undef GET_DESCRIPTOR_BUFSIZE

2270 }

2271

2272 for (j = 0; j < SET_ADDRESS_TRIES; ++j) {

2273 retval = hub_set_address(udev);

2274 if (retval >= 0)

2275 break;

2276 msleep(200);

2277 }

2278 if (retval < 0) {

2279 dev_err(&udev->dev,

2280 "device not accepting address %d, error %d/n",

2281 udev->devnum, retval);

2282 goto fail;

2283 }

2284

2285 /* cope with hardware quirkiness:

2286 * - let SET_ADDRESS settle, some device hardware wants it

2287 * - read ep0 maxpacket even for high and low speed,

2288 */

2289 msleep(10);

2290 if (USE_NEW_SCHEME(retry_counter))

2291 break;

2292

2293 retval = usb_get_device_descriptor(udev, 8);

2294 if (retval < 8) {

2295 dev_err(&udev->dev, "device descriptor "

2296 "read/%s, error %d/n",

2297 "8", retval);

2298 if (retval >= 0)

2299 retval = -EMSGSIZE;

2300 } else {

2301 retval = 0;

2302 break;

2303 }

2304 }

2305 if (retval)

2306 goto fail;

2307

2308 i = udev->descriptor.bMaxPacketSize0 == 0xff?

2309 512 : udev->descriptor.bMaxPacketSize0;

2310 if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {

2311 if (udev->speed != USB_SPEED_FULL ||

2312 !(i == 8 || i == 16 || i == 32 || i == 64)) {

2313 dev_err(&udev->dev, "ep0 maxpacket = %d/n", i);

2314 retval = -EMSGSIZE;

2315 goto fail;

2316 }

2317 dev_dbg(&udev->dev, "ep0 maxpacket = %d/n", i);

2318 udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);

2319 ep0_reinit(udev);

2320 }

2321

2322 retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);

2323 if (retval < (signed)sizeof(udev->descriptor)) {

2324 dev_err(&udev->dev, "device descriptor read/%s, error %d/n",

2325 "all", retval);

2326 if (retval >= 0)

2327 retval = -ENOMSG;

2328 goto fail;

2329 }

2330

2331 retval = 0;

2332

2333 fail:

2334 if (retval)

2335 hub_port_disable(hub, port1, 0);

2336 mutex_unlock(&usb_address0_mutex);

2337 return retval;

2338 }

像这种近300行的函数,这些年来,我竟也渐渐习惯了.我现在是以一种看文物的心态看这些变态的函数,竟也慢慢品出了一点周口店遗风.因为我明白,即便我像鲁迅先生那样呐喊,写代码的那些家伙也不会停下写这些函数的手.难怪江湖上称这些人的所作所为为行为艺术.

hub_port_init()这个函数的基本思想就是做初始化,首先是把一个设备reset,然后是分配地址.在然后是获得设备描述符.

首先DEFINE_MUTEX是来自于include/linux/mutex.h中的一个宏,用它可以定义一把互斥锁,Linux内核中,其实是在2005年底才建立比较系统的完善的互斥锁机制,在那年冬天,北京的最后一场雪过后,来自RedHat公司的Ingo Molnar大侠大胆的提出了他所谓的Generic Mutex Subsystem,即通用的互斥锁机制.此前内核中很多地方使用的都是信号量,正如我们在2.6.10内核中usb-storage中所看到的那样,而当时间的箭头指向了2005年末的时候,区里(开源社区,下称区里)很多同志抱怨说信号量不灵,很多时候不好用,当然区里为这事展开了一场轰轰烈烈的讨论.老黑客Ingo Molnar受不了了,在一周之后,愤然提出要对内核进行一场大的革命,这次革命后来被称为一二一九运动.当时Ingo同志提出了诸多理由要求使用新的互斥锁机制,而不是过去普遍出现在内核中的信号量机制,比如新的机制占用更小的内存,代码更为紧凑,更快,更便于调试.在诸多优势的诱惑下,区里的群众将信将疑的便认可了这种做法.忽如一夜春风来,紧接着的几个月里,人民群众纷纷提交patch,把原来用信号量的地方都改成了互斥锁.而这种改变深入到Linuxusb子系统是始于2006年春天,农历二月二十二,Greg同志大旗一挥,usb中的代码中绝大多数的信号量代码换成了互斥锁代码.所以到今天,您看2.6.22.1的代码中,整个usb子系统里几乎没有了down/up这一对函数的使用,取而代之的是mutex_lock()mutex_unlock()函数对.而要初始化,只需像我们这里一样,DEFINE_MUTEX(name)即可.关于这个新的互斥锁的定义在include/linux/mutex.h,而实现在kernel/mutex.c.

Ok,让我们继续.hdev被定义用来记录hub所对应的那个struct usb_device,delay记录延时,因为usb设备的reset工作不可能是瞬间的,通常会有一点点延时,这很容易理解,你的计算机永远不可能说你按一下reset键就立刻能够重起马上就能重新工作的,这里咱们首先给delay设置的初始值为10ms,HUB_SHORT_RESET_TIME这个宏被定义为10ms.这个10ms的来源是usb spec 2.07.1.7.5Reset Signaling里面说的,”The reset signaling must be driven for a minumum of 10ms”,这个10msusb spec中称之为TDRST.一个Hub端口在reset之后将进入Enabled状态.

然后定义一个oldspeed用来记录设备在没有reset之前的速度.

2120,只有root hub没有父亲,usb spec 2.0里说得很清楚,”It is required that resets from root ports have a duration of at least 50ms”,这个50ms被称为TDRSTR.HUB_ROOT_RESET_TIME这个宏被定义为50ms.

21222123行是OTG相关的,HNPOTG标准所支持的协议,HNPHost Negotiation Protocol,坊间俗称主机通令协议.咱们既然不关注OTG,那么这里也就不做解释了.省得把问题复杂化了.当断不断,反受其乱. – 史记 春申君列传.

2128,这两行代码来源于实践.实践表明,某些低速设备要求有比较高的延时才能完成好它们的reset,这很简单,286的机器重起肯定比P4的机器要慢.

,调用mutex_lock获得互斥锁了,即表明下面这段代码一个时间只能被一个进程执行.

然后调用hub_port_reset().

1509 static int hub_port_reset(struct usb_hub *hub, int port1,

1510 struct usb_device *udev, unsigned int delay)

1511 {

1512 int i, status;

1513

1514 /* Reset the port */

1515 for (i = 0; i < PORT_RESET_TRIES; i++) {

1516 status = set_port_feature(hub->hdev,

1517 port1, USB_PORT_FEAT_RESET);

1518 if (status)

1519 dev_err(hub->intfdev,

1520 "cannot reset port %d (err = %d)/n",

1521 port1, status);

1522 else {

1523 status = hub_port_wait_reset(hub, port1, udev, delay);

1524 if (status && status != -ENOTCONN)

1525 dev_dbg(hub->intfdev,

1526 "port_wait_reset: err = %d/n",

1527 status);

1528 }

1529

1530 /* return on disconnect or reset */

1531 switch (status) {

1532 case 0:

1533 /* TRSTRCY = 10 ms; plus some extra */

1534 msleep(10 + 40);

1535 /* FALL THROUGH */

1536 case -ENOTCONN:

1537 case -ENODEV:

1538 clear_port_feature(hub->hdev,

1539 port1, USB_PORT_FEAT_C_RESET);

1540 /* FIXME need disconnect() for NOTATTACHED device */

1541 usb_set_device_state(udev, status

1542 ? USB_STATE_NOTATTACHED

1543 : USB_STATE_DEFAULT);

1544 return status;

1545 }

1546

1547 dev_dbg (hub->intfdev,

1548 "port %d not enabled, trying reset again.../n",

1549 port1);

1550 delay = HUB_LONG_RESET_TIME;

1551 }

1552

1553 dev_err (hub->intfdev,

1554 "Cannot enable port %i. Maybe the USB cable is bad?/n",

1555 port1);

1556

1557 return status;

1558 }

事到如今,有些函数不讲也不行了.这就是set_port_feature.其实之前我们遇见过,只是因为当时属于可讲可不讲,所以就先跳过去了.但现在不讲不行了,我们前面讲过它的搭档clear_port_feature,所以我不讲你也应该知道set_port_feature()干嘛用的,很显然,一个是清楚feature,一个是设置feature.Linux中很多这种成对的函数,刚才讲的那个mutex_lock()mutex_unlock()不也是这样么?其实这种思想是借鉴了我国的黄梅戏<<天仙配>>中所描绘的那种你耕田来我织布,我挑水来你浇园,你我好比鸳鸯鸟,比翼双飞在人间的纯朴的爱情观.

172 /*

173 * USB 2.0 spec Section 11.24.2.13

174 */

175 static int set_port_feature(struct usb_device *hdev, int port1, int feature)

176 {

177 return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),

178 USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,

179 NULL, 0, 1000);

180 }

看明白了clear_port_feature()的一定不会觉得这个函数看不懂.发送一个控制请求,设置一个feature,咱们传递进来的featureUSB_PORT_FEAT_RESET,即对应于usb spec中的reset.发送好了之后就延时等待,调用hub_port_wait_reset():

1457 static int hub_port_wait_reset(struct usb_hub *hub, int port1,

1458 struct usb_device *udev, unsigned int delay)

1459 {

1460 int delay_time, ret;

1461 u16 portstatus;

1462 u16 portchange;

1463

1464 for (delay_time = 0;

1465 delay_time < HUB_RESET_TIMEOUT;

1466 delay_time += delay) {

1467 /* wait to give the device a chance to reset */

1468 msleep(delay);

1469

1470 /* read and decode port status */

1471 ret = hub_port_status(hub, port1, &portstatus, &portchange);

1472 if (ret < 0)

1473 return ret;

1474

1475 /* Device went away? */

1476 if (!(portstatus & USB_PORT_STAT_CONNECTION))

1477 return -ENOTCONN;

1478

1479 /* bomb out completely if something weird happened */

1480 if ((portchange & USB_PORT_STAT_C_CONNECTION))

1481 return -EINVAL;

1482

1483 /* if we`ve finished resetting, then break out of the loop */

1484 if (!(portstatus & USB_PORT_STAT_RESET) &&

1485 (portstatus & USB_PORT_STAT_ENABLE)) {

1486 if (hub_is_wusb(hub))

1487 udev->speed = USB_SPEED_VARIABLE;

1488 else if (portstatus & USB_PORT_STAT_HIGH_SPEED)

1489 udev->speed = USB_SPEED_HIGH;

1490 else if (portstatus & USB_PORT_STAT_LOW_SPEED)

1491 udev->speed = USB_SPEED_LOW;

1492 else

1493 udev->speed = USB_SPEED_FULL;

1494 return 0;

1495 }

1496

1497 /* switch to the long delay after two short delay failures */

1498 if (delay_time >= 2 * HUB_SHORT_RESET_TIME)

1499 delay = HUB_LONG_RESET_TIME;

1500

1501 dev_dbg (hub->intfdev,

1502 "port %d not reset yet, waiting %dms/n",

1503 port1, delay);

1504 }

1505

1506 return -EBUSY;

1507 }

这里HUB_RESET_TIMEOUT是设置的一个超时,这个宏的值为500毫秒,即如果reset500毫秒还没好那么就返回错误值,朽木不可雕也.而循环的步长正是我们前面设置的那个delay.

msleep(delay)就是休眠delay毫秒.

休眠完了就读取端口的状态.hub_port_status()不用说了,咱们前面讲过了.获得端口状态.错误就返回错误码,正确就把信息记录在portstatusportchange.

1476行判断,如果在reset期间设备都被撤掉了,那就返回吧,甭浪费感情了.

1480行判断,如果又一次汇报说有设备插入,那就是见鬼了.返回错误立刻向上级汇报说人鬼情未了.

正如刚才说过的,reset真正完成以后,status就应该是enabled,所以14841485行的if如果满足就说明reset好了.

1486行这个if是判断这是否是一个无线Root hub,即既是Root Hub,又是无线hub,因为在struct usb_hcd中有一个成员unsigned wireless,这个flag标志了该主机控制器是Wireless.我们刚才说过了,Wireless的话,speed就是那个USB_SPEED_VARIABLE.

否则如果portstatusUSB_PORT_STAT_HIGH_SPEED相与为1,则是高速设备,如果与USB_PORT_STAT_LOW_SPEED相与为1则是低速设备,剩下来的可能就是全速设备.到这里,这个hub_port_wait_reset就可以返回了,正常的返回0.总之,注意,经过这里udevspeed就被设置好了.

14971498,走到这里就说明还没有reset.如果已经过了2HUB_SHORT_RESET_TIME,就把步长设置为HUB_LONG_RESET_TIME,200ms,然后打印一条警告信息,继续循环,如果循环完全结束还不行,那就说明超时了,返回-EBUSY.

回到hub_port_reset中来,下面走到了1531,一个switch,根据刚才的返回值做一次选择,如果是0,说明正常, 安全起见,索性再等50ms.如果是错误码,并且错误码表明设备不在了,则首先清掉reset这个feature,然后把这个为刚才这个设备申请的struct usb_device结构体的状态设置为USB_STATE_NOTATTACHED.并且返回status.如果你问那个USB_STATE_DEFAULT是怎么回事?那么说明你没有好好学谭浩强的那本经典教材.switch里面,如果status0,那么由于case 0那一部分后面break语句,所以case –ENOTCONNcase –ENODEV下面的那几句代码都会执行,clear_port_feature是总会执行的,usb_set_device_state()也会执行,而对于status0的情况,属于正常情况,从这时候开始,struct usb_device结构体的状态就将记录为USB_STATE_DEFAULT,即所谓的默认状态,然后返回值就是0.而对于端口reset,我们设置的重复次数是PORT_RESET_TRIES,它等于5,你当然可以把它改为1,没人拦住你.只要你对自己的设备够自信,一次reset就肯定成功.信自己,金莱克!

如果1553行还会执行,那么说明肯定出了大问题了,reset都没法进行,于是返回错误状态吧.

回到hub_init_port()中来,如果刚才失败了,goto fail,否则也暂时将retval这个临时变量设置为-ENODEV,2140,如果oldspeed不是USB_SPEED_UNKNOWN,并且也不等于刚刚设置的这个speed,那么说明reset之前设备已经在工作了,而这次reset把设备原来的速度状态也给改变了.这是不合理的,必须结束函数,并且disable这个端口.于是goto fail,而在fail那边可以看到,由于retval不为0,所以调用hub_port_disable()关掉这个端口.然后释放互斥锁,并且返回错误代码.

2144,如果不是刚才这种情况,那么令oldspeed等于现在这个udev->speed.

2151行开始,又是一个switch,感觉这一段代码的选择出现得太多了点,代码的选择倒是简单,可是人,作为微小而孤独的个体,在人生的选择题前,则总会无可避免地徘徊起来.在一个又一个渡口上,在一次又一次险象中,我们究竟能选择什么,该选择什么?

其实这里是设置一个初始值,我们曾经介绍过ep0.ep0.desc.wMaxPacketSizep0.desc.wMaxPacketSize用来记录端点0的单个包的最大传输size.对于无线设备,无线usb spec规定了,端点0的最大包size就是512,而对于高速设备,这个size也是usb spec规定好了,64bytes,而低速设备同样是usb spec规定好了,8bytes.唯一存在变数的是全速设备,它可能是8,可能是16,可能是32,也可能是64,对于这种设备,没有办法,只能通过读取设备描述符来获得了.也正是这个全速设备引发了我们前面讨论的那个两种策略的问题.不过那时候我们没有点明说是这场PK是因为全速设备引起的,因为那时候说还太早了,说了您也忘了,而现在看到了代码就不会忘了.正如我们说过的,LinuxWindows妥协了,这里Full Speed的设备,默认先把wMaxPacketSize设置为64,而不是以前的那种8.

21722186这些行,仅仅是为了打印一行调试信息,对于写代码的人来说,可能很有用,而对读代码的人来说,也许就没有任何意义了,正如对聋子而言,正版唱片CD的作用只是拿来当照脸的镜子.

Sep 9 11:32:49 localhost kernel: usb 4-5: new high speed USB device using ehci_hcd and address 3

Sep 9 11:32:52 localhost kernel: usb 2-1: new low speed USB device using uhci_hcd and address 3

比如在我的计算机里,就可以在/var/log/messages日志文件里看到上面这样的信息.ehci_hcduhci_hcd就是主机控制器的驱动程序.这个3就是设备的devnum.

2189,不是switch又是if,除了判断还是判断,只不过刚才是选择,现在是如果,如果明天是世界末日,我就会去抢银行,可是既然明天是世界末日了,那要很多钱又能做什么呢?这里如果hdev->tt为真,则如何如何,否则,如果设备本身不是高速的,hub是高速的,那么如何如何.tt我们介绍过,transaction translator,ttportstruct usb_device中的一个成员,int ttport,这个ttport以后会被用到,tt也将在以后会被用到,暂时先不细说,到时候再回来看.

GET_DESCRIPTOR_TRIES等于2,这段代码的思想我们以前就讲过,USB_NEW_SCHEME(retry_counter),retry_counter就是hub_port_init()传递进来的最后一个参数,而我们给它的实参正是那个从0SET_CONFIG_TRIES-1的那个i.假设我们什么也没有设置,都是使用默认值,那么use_both_schemes默认值为1,old_scheme_first默认值为0,于是SET_CONFIG_TRIES4,i将从0变到3,USB_NEW_SCHEME(i)将在i01的时候为1,i23的时候为0.所以也就是说,先进行两次新的策略,如果不行就再进行两次旧的策略.所有这一切只有一个目的,就是为了获得设备的描述符.由于思想已经非常清楚,代码我们就不再一行一行讲了.尤其是那些错误判断的句子.

只是介绍一下其中调用的几个函数.对于新策略,首先定义一个struct usb_device_descriptor的指针buf,然后申请64个字节的空间,发送一个控制传输的请求,然后结束之后,察看buf->bMaxPackSize0,合理值只有8/16/32/64/512,这里255实际上是WUSB协议规定的,毕竟只有8,最大就是255,所以就用这个值来代表WUSB设备.实际上WUSB的大小是512.循环三次是保险起见.因为实践表明这类请求通常成功率很难达到100%.

然后2249行用udev->descriptor.bMaxPacketSize0来记录这个临时获得的值.然后buf的使命结束了,释放它的内存.

然后正如我们曾经分析的那样,把设备reset.

然后是设置地址.hub_set_address()

2076 static int hub_set_address(struct usb_device *udev)

2077 {

2078 int retval;

2079

2080 if (udev->devnum == 0)

2081 return -EINVAL;

2082 if (udev->state == USB_STATE_ADDRESS)

2083 return 0;

2084 if (udev->state != USB_STATE_DEFAULT)

2085 return -EINVAL;

2086 retval = usb_control_msg(udev, usb_sndaddr0pipe(),

2087 USB_REQ_SET_ADDRESS, 0, udev->devnum, 0,

2088 NULL, 0, USB_CTRL_SET_TIMEOUT);

2089 if (retval == 0) {

2090 usb_set_device_state(udev, USB_STATE_ADDRESS);

2091 ep0_reinit(udev);

2092 }

2093 return retval;

2094 }

和前面那个choose_address不同,choose_address是从软件意义上挑选一个地址.而这里要发送真正的请求,因为设置设备地址本身就是usb spec 2.0规定的标准的请求之一,这里我们用宏USB_REQ_SET_ADDRESS来代替,只有真正发送了请求之后硬件上才能真正通过这个地址进行通信.这里最关键的就是传递了udev->devnum,这正是我们前面选择的地址,这里赋给了wIndex,来自usb spec 2.0中图表说明了一切:

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 414.75pt; HEIGHT: 116.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image001.emz"></imagedata></shape>

从此以后这个设备站起来了,并一举确立了它在usb江湖中的地位.

设好了地址之后,把设备的状态从开始的那个USB_STATE_DEFAULT变成了USB_STATE_ADDRESS,从此以后他就算是有户口的主了.usb spec,这一状态被称为Address state,我叫它有地址的状态.

然后调用了ep0_reinit().

2066 static void ep0_reinit(struct usb_device *udev)

2067 {

2068 usb_disable_endpoint(udev, 0 + USB_DIR_IN);

2069 usb_disable_endpoint(udev, 0 + USB_DIR_OUT);

2070 udev->ep_in[0] = udev->ep_out[0] = &udev->ep0;

2071 }

usb_disable_endpointusbcore提供的一个函数,具体到咱们这个端点,它会让ep_out[0]ep_in[0]置为NULL.然后会取消掉任何挂在endpoint上的urb.再然后这里2070行再次把ep_in[0]ep_out[0]指向udev->ep0.须知ep0是真正占内存的数据结构,ep_in[0]ep_out[0]只是指针,而在host controller驱动程序里将被用到的正是这两个指针数组.

回到hub_port_init中来,设置好了地址,然后2289,先睡10ms,然后如果还是新策略,那么就可以结束了,因为该做的都做完了.如果是旧策略,那么执行到这里还刚上路呢,我们说过了,思路就是先获得设备描述符的前8个字节,然后从中得知udev->descriptorbMaxPacketSize0,然后再次调用usb_get_device_descriptor完整的获得一次设备描述符.然后就ok,2336,释放互斥锁,所谓的解铃还须系铃人.

至此,hub_port_init()就可以返回了.

声明一下,usb_get_device_descriptor()是来自drivers/usb/core/message.c中的函数,usbcore提供,我们这里就不细讲了.其作用是很明显的,获取设备描述符.
分享到:
评论

相关推荐

    Linux那些事儿之我是Hub

    Linux那些事儿之我是Hub,是前面Linux那些事之我是U盘,usb等的姐妹篇

    Linux那些事儿之我是HUB.pdf

    非常不错的一本书,LinuxLinuxLinuxLinuxLinuxLinuxLinuxLinuxLinuxLinuxLinuxLinuxLinuxLinux

    Linux那些事儿

    Linux那些事儿之我是Hub Linux那些事儿之我是USB Core Linux那些事儿之我是UHCI Linux那些事儿之我是EHCI控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 Linux那些事儿之我...

    Linux那些事儿1-9合集

    linux那些事儿之我是HUB linux那些事儿之我是USB Core linux那些事儿之我是UHCI Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 linux那些事儿...

    Linux那些事儿之我是USB(第2版)

    Linux那些事儿第二版我是Hub一章,比较第一版修正了一些错误,增加了电源管理部分的分析,推荐阅读

    Linux那些事儿之全集

    导读.doc Linux那些事儿之我是Block层.pdf Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是Hub.pdf Linux那些事儿之我是USB_core.pdf Linux那些事儿之我是U盘.pdf等等 Linux那些事儿系列全在这里了

    Linux那些事儿系列.rar

    》包括《Linux那些事儿之我是Hub》、《Linux那些事儿之我是Sysfs》《Linux那些事儿之我是UHCI》、《Linux那些事儿之我是USB core》、《Linux那些事儿之我是U盘》,令人叹为观止的一个linux系列书籍。只能说,江山代...

    linux那些事儿之我是USB(包括第一版和第二版完整文字)

    本文件包括第一第二完成版,第二版基于2.6.22内核,对USB子系统的大部分源代码逐行进行分析,系统地阐释了Linux内核中USB子系统是如何运转的,子系统内部的各个模块之间是如何互相协作、配合的。本次改版修改了第1版...

    Linux那些事儿之我是USB(第2版).pdf

    本书基于2.6.22内核,对USB子系统的大部分源代码逐行进行分析,系统地阐释了Linux内核中USB子系统是如何运转的,子系统内部的各个模块之间是如何互相协作、配合的。本次改版修改了第1版中出现的错误,增加了一个附录...

    linux的那些事儿全集

    Linux那些事儿之我是Block层 ...Linux那些事儿之我是Hub Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Sysfs Linux那些事儿之我是UHCI Linux那些事儿之我是USB core Linux那些事儿之我是U盘

    linux那些事儿(EHCI Block SCSI Sysfs PCI USB U 盘 UHCI Hub)

    Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是Block层.pdf Linux那些事儿之我是SCSI硬盘.pdf Linux那些事儿之我是Sysfs.pdf ...Linux那些事儿之我是Hub.pdf Linux那些事儿之我是UHCI.pdf

    linux那些事全集

    Linux那些事儿之我是U盘 Linux那些事儿之我是USB_core Linux那些事儿之我是UHCI Linux那些事儿之我是Sysfs ...Linux那些事儿之我是Hub Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Block层

    Linux那些事儿系列

    本人整理的fudan_abc的专栏中以完结的文章,在此向原作者表示感谢,给...内容包括:linux那些事儿之我是U盘,linux那些事儿之我是USB,linux那些事儿之我是HUB,linux那些事儿之我是UHCI,linux那些事儿之我是Sysfs。

    Linux那些事儿之我是XXX全集.rar

    Linux那些事儿之我是XXX全集 包含USB core U盘 UHCI PCI SCSI硬盘 Block Hub EHCI 。 想学驱动的童鞋,不妨看看。该书主要是进行源代码的分析

    Linux那些事儿 系列之2 Block+EHCI+PCI+SCSI

    Linux那些事儿之我是Block层.pdf Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是PCI.pdf Linux那些事儿之我是SCSI硬盘.pdf 注: 之前有人已经上传了《Linux那些事儿 系列》,其已经包含了:hub,sysfs...

    Linux那些事儿.rar

    包括:Linux那些Linux那些事儿之我是SCSI硬盘,Linux那些事儿之我是Block层,Linux那些事儿之我是EHCI主机控制器,Linux那些事儿之我是HUB,Linux那些事儿之我是PCI,Linux那些事儿之我是Sysfs,Linux那些事儿之我是...

    linux那些事儿之我是USB.zip

    里面包含Linux那些事的九个文档,Block层,ECHI主机控制,HUB,PCI,SCSI硬盘,Sysfs,UHCI,USB+core,U盘等九个文档,内容详细,而且全面都有书签,适合系统学习!

    Linux那些事系列

    Linux那些事儿之我是Hub Linux那些事儿之我是Sysfs Linux那些事儿之我是UHCI Linux那些事儿之我是USB core Linux那些事儿之我是U盘 Linux那些事之我是HUB1

Global site tag (gtag.js) - Google Analytics