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中图表说明了一切:

从此以后这个设备站起来了,并一举确立了它在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 提供 , 我们这里就不细讲了 . 其作用是很明显的 , 获取设备描述符 . 
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值