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

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

 
阅读更多

在调用usb_new_device之前,25552560这一小段,如果说hub已经被撤掉了,那么老规矩,别浪费感情了.否则,udev赋值给hdev->children数组中的对应元素,也正是从此以后,这个设备才算是真正挂上了这棵大树.

Ok,如果status确实为0,(注意,2549刚刚把status赋为了0.)正式调用usb_new_device.

1275 /**

1276 * usb_new_device - perform initial device setup (usbcore-internal)

1277 * @udev: newly addressed device (in ADDRESS state)

1278 *

1279 * This is called with devices which have been enumerated, but not yet

1280 * configured. The device descriptor is available, but not descriptors

1281 * for any device configuration. The caller must have locked either

1282 * the parent hub (if udev is a normal device) or else the

1283 * usb_bus_list_lock (if udev is a root hub). The parent's pointer to

1284 * udev has already been installed, but udev is not yet visible through

1285 * sysfs or other filesystem code.

1286 *

1287 * It will return if the device is configured properly or not. Zero if

1288 * the interface was registered with the driver core; else a negative

1289 * errno value.

1290 *

1291 * This call is synchronous, and may not be used in an interrupt context.

1292 *

1293 * Only the hub driver or root-hub registrar should ever call this.

1294 */

1295 int usb_new_device(struct usb_device *udev)

1296 {

1297 int err;

1298

1299 /* Determine quirks */

1300 usb_detect_quirks(udev);

1301

1302 err = usb_get_configuration(udev);

1303 if (err < 0) {

1304 dev_err(&udev->dev, "can't read configurations, error %d/n",

1305 err);

1306 goto fail;

1307 }

1308

1309 /* read the standard strings and cache them if present */

1310 udev->product = usb_cache_string(udev, udev->descriptor.iProduct);

1311 udev->manufacturer = usb_cache_string(udev,

1312 udev->descriptor.iManufacturer);

1313 udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);

1314

1315 /* Tell the world! */

1316 dev_dbg(&udev->dev, "new device strings: Mfr=%d, Product=%d, "

1317 "SerialNumber=%d/n",

1318 udev->descriptor.iManufacturer,

1319 udev->descriptor.iProduct,

1320 udev->descriptor.iSerialNumber);

1321 show_string(udev, "Product", udev->product);

1322 show_string(udev, "Manufacturer", udev->manufacturer);

1323 show_string(udev, "SerialNumber", udev->serial);

1324

1325 #ifdef CONFIG_USB_OTG

1326 /*

1327 * OTG-aware devices on OTG-capable root hubs may be able to use SRP,

1328 * to wake us after we've powered off VBUS; and HNP, switching roles

1329 * "host" to "peripheral". The OTG descriptor helps figure this out.

1330 */

1331 if (!udev->bus->is_b_host

1332 && udev->config

1333 && udev->parent == udev->bus->root_hub) {

1334 struct usb_otg_descriptor *desc = 0;

1335 struct usb_bus *bus = udev->bus;

1336

1337 /* descriptor may appear anywhere in config */

1338 if (__usb_get_extra_descriptor (udev->rawdescriptors[0],

1339 le16_to_cpu(udev->config[0].desc.wTotalLength),

1340 USB_DT_OTG, (void **) &desc) == 0) {

1341 if (desc->bmAttributes & USB_OTG_HNP) {

1342 unsigned port1 = udev->portnum;

1343

1344 dev_info(&udev->dev,

1345 "Dual-Role OTG device on %sHNP port/n",

1346 (port1 == bus->otg_port)

1347 ? "" : "non-");

1348

1349 /* enable HNP before suspend, it's simpler */

1350 if (port1 == bus->otg_port)

1351 bus->b_hnp_enable = 1;

1352 err = usb_control_msg(udev,

1353 usb_sndctrlpipe(udev, 0),

1354 USB_REQ_SET_FEATURE, 0,

1355 bus->b_hnp_enable

1356 ? USB_DEVICE_B_HNP_ENABLE

1357 : USB_DEVICE_A_ALT_HNP_SUPPORT,

1358 0, NULL, 0, USB_CTRL_SET_TIMEOUT);

1359 if (err < 0) {

1360 /* OTG MESSAGE: report errors here,

1361 * customize to match your product.

1362 */

1363 dev_info(&udev->dev,

1364 "can't set HNP mode; %d/n",

1365 err);

1366 bus->b_hnp_enable = 0;

1367 }

1368 }

1369 }

1370 }

1371

1372 if (!is_targeted(udev)) {

1373

1374 /* Maybe it can talk to us, though we can't talk to it.

1375 * (Includes HNP test device.)

1376 */

1377 if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {

1378 err = __usb_port_suspend(udev, udev->bus->otg_port);

1379 if (err < 0)

1380 dev_dbg(&udev->dev, "HNP fail, %d/n", err);

1381 }

1382 err = -ENODEV;

1383 goto fail;

1384 }

1385 #endif

1386

1387 /* export the usbdev device-node for libusb */

1388 udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,

1389 (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));

1390

1391 /* Register the device. The device driver is responsible

1392 * for adding the device files to sysfs and for configuring

1393 * the device.

1394 */

1395 err = device_add(&udev->dev);

1396 if (err) {

1397 dev_err(&udev->dev, "can't device_add, error %d/n", err);

1398 goto fail;

1399 }

1400

1401 /* Increment the parent's count of unsuspended children */

1402 if (udev->parent)

1403 usb_autoresume_device(udev->parent);

1404

1405 exit:

1406 return err;

1407

1408 fail:

1409 usb_set_device_state(udev, USB_STATE_NOTATTACHED);

1410 goto exit;

1411 }

这个函数看似很长,实则不然.幸亏咱们前面作了一个厚颜无耻的假设,即假设不打开支持OTG的代码.在这里13251385行就这么被我们华丽丽的飘过了.而剩下的代码就相对来说简单多了,主要就是调用了几个函数.一个一个来看.

usb_detect_quirks().如果不是因为我们讲过了usb-storage,不是因为在usb-storage里面见过那个unusual_devs.h的故事,也许这里我会耐心的给您讲一讲这个关于quirks的故事.实际上这是两个相似的故事,它们共同印证着列夫托尔斯泰在安娜卡列尼娜中的开篇第一句,幸福的家庭都是相似的,不幸的家庭各有各的不幸.好的USB设备都是相似的,大家遵守同样的游戏规则,而不好的USB设备却各有各的毛病,usb-storage里面我们使用了unusual_devs.h,而在整个usb子系统范围内,我们使用另外两个文件,drivers/usb/core/quirks.c以及include/linux/usb/quirks.h. quirk,金山词霸说,怪癖的意思.说白了就是说白里透红,与众不同.

include/linux/usb/quirks.h,我们看到这个文件超级的短,只有两行有意义,其余几行是注释,

1 /*

2 * This file holds the definitions of quirks found in USB devices.

3 * Only quirks that affect the whole device, not an interface,

4 * belong here.

5 */

6

7 /* device must not be autosuspended */

8 #define USB_QUIRK_NO_AUTOSUSPEND 0x00000001

9

10 /* string descriptors must not be fetched using a 255-byte read */

11 #define USB_QUIRK_STRING_FETCH_255 0x00000002

这个文件总共就是这么11,而其中定义了两个flag,第一个USB_QUIRK_NO_AUTOSUSPEND表明这个设备不能自动挂起,执行自动挂起会对设备造成伤害,确切的说是设备会被crash.而第二个宏,USB_QUIRK_STRING_FETCH_255,是说该设备在获取字符串描述符的时候会crash.

与此同时,drivers/usb/core/quirks.c中定义了这么一张表,

18 /* List of quirky USB devices. Please keep this list ordered by:

19 * 1) Vendor ID

20 * 2) Product ID

21 * 3) Class ID

22 *

23 * as we want specific devices to be overridden first, and only after that, any

24 * class specific quirks.

25 *

26 * Right now the logic aborts if it finds a valid device in the table, we might

27 * want to change that in the future if it turns out that a whole class of

28 * devices is broken...

29 */

30 static const struct usb_device_id usb_quirk_list[] = {

31 /* HP 5300/5370C scanner */

32 { USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 },

33 /* Seiko Epson Corp - Perfection 1670 */

34 { USB_DEVICE(0x04b8, 0x011f), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },

35 /* Elsa MicroLink 56k (V.250) */

36 { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },

37

38 { } /* terminating entry must be last */

39 };

这张表被称作usb黑名单.2.6.22.1的内核中这张表里只记录了3个设备,但之所以创建这张表,目的在于将来可以扩充,比如这个夏天,Oliver同学又往这张表里添加了几个扫描仪,比如明基的S2W 3300U,精工爱普生的Perfection 1200,以及另几家公司的一些产品.所以2.6.23的内核里将会看到这张表的内容比现在丰富.而从原理上来说,这张表和当初我们的那个unusual_devs.h是一样的,usb_detect_quirks()函数就是为了判断一个设备是不是在这张黑名单上,然后如果是的,就判断它具体是属于哪种问题,我们注意到,07年之后的内核中,struct usb_device结构体有一个元素u32 quirks,就是用来做这个检测的,usb_detect_quirks会为在黑名单中找得到的设备的struct usb_device结构体中的quirks赋值,然后接下来相关的代码就会判断一个设备的quirks中的某一位是否设置了,目前quirks里面只有两位可以设置,USB_QUIRKS_STRING_FETCH_255所对应的0x00000002USB_QUIRK_NO_AUTOSUSPEND所对应的0x00000001.而今年4月份,Alan同学又提交了一个patch,增加了另一个标志位,USB_QUIRK_RESET_RESUME,值为0x00000004,以表征一个设备不能正确的resume,而只能通过reset才能让它从挂起状态恢复正常.

所以,usb_detect_quirks()所带给我们的就是这么一个故事.它反映的是这样一种现状,即商家只管赚钱,却不管他们家生产出来的产品是否真的合格,只要它的产品差不多就行了,反正是usb设备,能用即可,基本功能满足,用户也鉴别不出好坏了.就好比我上个月买的雕牌洗衣粉洗了几次总觉得不好,后来仔细一看,包装袋上写着周佳牌洗衣粉.还有一次看路边卖五粮液,觉得便宜就一次性买了好几瓶,回去一喝,感觉完全不对,仔细一看吧,人家瓶子上写的是丑粮液.说了这个我就来气,从上海来北京的时候,火车站买一小说,金庸新著,上了火车我怒了,妈的,作者叫金庸新.

1302,usb_get_configuration(),获得配置描述符,我想你如果清楚了如何获得设备描述符,自然就不难知道如何获得配置描述符,知道了配置描述符,自然就不难知道如何获得接口描述符,然后是端点描述符.usb_get_configuration来自drivers/usb/core/config.c,我们不打算深入去讲这个函数,如果你有兴趣自己去看,那我做一点点解释,我们知道一个手机可以有多种配置,比如可以摄像,可以接在电脑里当做一个U,那么这两种情况就属于不同的配置,在手机里面有相应的选择菜单,你选择了哪种它就按哪种配置进行工作,供你选择的这个就叫做配置.很显然,当你摄像的时候你不可以访问这块U,当你访问这块U盘的时候你不可以摄像,因为你做了选择.第二,既然一个配置代表一种不同的功能,那么很显然,不同的配置可能需要的接口就不一样,我假设你的手机里从硬件上来说一共有5个接口,那么可能当你配置成U盘的时候它只需要用到某一个接口,当你配置成摄像的时候,它可能只需要用到另外两个接口,可能你还有别的配置,然后你可能就会用到剩下那两个接口,那么当你选择好一种配置之后,你给设备发送请求,请求去获得配置描述符的时候,设备返回给你的就绝不仅仅是一个配置描述符,它还必须返回更多的信息.usb spec的说法就是,设备将返回的是除了配置描述符以外,与这种配置相关的接口描述符,以及与这些接口相关的端点描述符,都会一次性返回给你.也正是因为如此,你才会知道足够的信息,从此以后你就可以为所欲为了.

另外一点我需要提示的是,一个接口可以有多种setting,即所谓的alternatesetting,比如在打印机驱动程序里,不同的setting可以表明使用不同的通信协议,又比如在声音设备驱动中setting可以决定不同的音频格式.那么我作为usb设备驱动程序我如何知道这些呢?首先,对于任何一个interface来说,usb spec规定了默认的settingsetting zero,0号设置是默认设置,而如果一个interface可以有多种setting,那么每一个setting将对应一个interface描述符,换言之,即便你只有一个interface,但是由于你可能有两种setting,那么你就有两个interface描述符,而它们对应于同一个interface编号,或者说我们知道接口描述符里面有一个成员,bInterfaceNumber和一个bAlternateSetting,就是对于这种情况,两个interface描述符将具有相同的bInterfaceNumber,而不相同的是bAlternateSetting,另一方面,因为不同的setting完全有可能导致需要不同的端点,所以也将有不同的端点描述符.

而总的来说,在我们的usb设备驱动程序可以正常工作之前,我们需要知道的信息是,接口描述符,Setting,以及端点描述符,从软件的角度来说,我们记得当初我们在usb-storage中的probe函数,我们传递给它的一个重要参数就是struct usb_interface指针,同时所有关于端点的信息也必须在调用storage_probe之前知道,而这一切的一切,都在设备里,我们所需要做的就是发送请求,然后设备就把相关信息返回给我们,然后我们就记录下来,填充好我们自己的数据结构,而这些数据结构,对所有的usb设备都是一样的,因为这些都是usb spec里面规定的,也正是因为如此,写代码的兄弟们才把这部分工作交给usb core来完成,而不是纷纷下放给每一个设备单独去执行,因为那样就太浪费了,大家都得干一些重复的工作,显然是没有必要的.

Ok,关于usb_get_configuration()函数我们就说这么多,我们传递的是struct usb_device结构体指针,即我们这个故事中的udev,从此以后你就会发现udev中的各个成员就有值了,这些值从哪来的?正是从设备里面来.

回到usb_new_device中来,13101323,还记得我们说过那个字符串描述符吧,这里就是去获得字符串描述符,并且保存下来,知道为什么你用lsusb命令可以看到诸如下面的内容了吧,

localhost:/usr/src/linux-2.6.22.1/drivers/usb/core # lsusb

Bus 001 Device 001: ID 0000:0000

Bus 002 Device 003: ID 0624:0294 Avocent Corp.

Bus 002 Device 001: ID 0000:0000

Bus 003 Device 001: ID 0000:0000

Bus 004 Device 003: ID 04b4:6560 Cypress Semiconductor Corp. CY7C65640 USB-2.0 "TetraHub"

Bus 004 Device 001: ID 0000:0000

其中那些字符串,就是这里保存起来的.试想如果不保存起来,那么每次你执行lsusb,都要去向设备发送一次请求,那设备还不被你烦死?usb_cache_string()就是干这个的,它来自drivers/usb/core/message.c,从此udev->product,udev->manufacturer,udev->serial里面就有值了.而下面那几行就是打印出来,show_string其实就是变相的printk语句.1315那句注释尤其搞笑,说什么”Tell the world!”告诉世界自己是一个什么样的设备,我有点怀疑写代码的兄弟是不是活得太压抑了一点,要知道在我们国家,跟一个人说隐私那叫倾诉,跟一群人说隐私那叫变态,跟全国人民说隐私那就叫<<艺术人生>>.

接下来,我们已经说过了,OTG的代码我们只能飘过,然后就到了1388,这里就是传统理论中的那两个主设备号和次设备号了.记得去年在恒隆广场面试赛门铁克的时候就被问到Linux中的设备号是怎么回事,分为主设备号和次设备号.按传统理论来说,主设备号表明了一类设备,一般对应着确定的驱动程序,而次设备号通常是因为一个驱动程序要支持多个设备而为了让驱动程序区分它们而设置的.比如如下,偶的硬盘:

localhost:/usr/src/linux-2.6.22.1/drivers/usb/core # ls -l /dev/sd*

brw-r----- 1 root disk 8, 0 Aug 6 18:19 /dev/sda

brw-r----- 1 root disk 8, 1 Aug 6 18:19 /dev/sda1

brw-r----- 1 root disk 8, 2 Aug 6 18:19 /dev/sda2

brw-r----- 1 root disk 8, 3 Aug 6 18:19 /dev/sda3

brw-r----- 1 root disk 8, 4 Aug 6 18:19 /dev/sda4

brw-r----- 1 root disk 8, 16 Aug 6 18:19 /dev/sdb

brw-r----- 1 root disk 8, 32 Aug 6 18:19 /dev/sdc

brw-r----- 1 root disk 8, 48 Aug 6 18:19 /dev/sdd

brw-r----- 1 root disk 8, 64 Aug 6 18:19 /dev/sde

scsi硬盘主设备号都是8,而不同的盘或者不同的分区都有不同的次设备号.次设备号具体为多少并不重要,不过最大不能超过255.usb子系统里使用以下公式安排次设备号的,minor=((dev->bus->busnum-1)*128)+(dev->devnum-1);USB_DEVICE_MAJOR被定义为189,我们看:

localhost:/usr/src/linux-2.6.22.1/drivers/usb/core # cat /proc/devices

Character devices:

1 mem

2 pty

3 ttyp

4 /dev/vc/0

4 tty

4 ttyS

5 /dev/tty

5 /dev/console

5 /dev/ptmx

7 vcs

10 misc

13 input

21 sg

29 fb

128 ptm

136 pts

162 raw

180 usb

189 usb_device

254 megaraid_sas_ioctl

189被称为usb_device,这两行代码是用于与usbfs文件系统相交户的.dev_t记录下了设备的主设备号和次设备号.,dev_t包含两部分,主设备号部分和次设备号部分.12位表征主设备号,20位表示次设备号.

1395,device_add(),Linux 2.6设备模型中最基础的函数之一,这个函数非常了不起.要深入追踪这个函数,足以写一篇专题文章了.这个函数来自drivers/base/core.c,是设备模型那边提供的函数,从作用上来说,这个函数这么一执行,系统里就真正有了咱们这个设备,/sysfs下面也能看到了,而且将会去遍历注册到usb总线上的所有的驱动程序,如果找到合适的,就去调用该驱动的probe函数,对于U盘来说,最终将调用storage_probe()函数,对于hub来说,最终将调用hub_probe()函数,而传递给它们的参数,正是我们此前获得的struct usb_interface指针和一个struct usb_device_id指针.后者我们在usb-storage里面已经非常熟悉了,它正是我们在usb总线上寻找驱动程序的依据,换句话说,每个驱动程序都会usb-storage那样,把自己支持的设备定义在一张表里,表中的每一项就是一个struct usb_device_id,然后当我们获得了一个具体设备,我们就把该设备的实际的信息与这张表去比较,如果找到匹配的了,就认为该驱动支持该设备,从而最终会调用该驱动的probe()函数.而从此,这个设备就被传递到了设备驱动.hub驱动也完成了它最重要的一项工作.

再次回到usb_new_device(),1402,如果该设备不是Root Hub,则调用usb_autoresume_device().这个函数来自drivers/usb/core/driver.c,也是usb core提供的,是电源管理方面的函数,如果设备这时候处于suspended状态,那么这个函数将把它唤醒,因为我们已经要开始用它了,怎么会任凭它睡眠呢.

最后1406,函数终于返回了.正确的话返回值为0.!

返回之后首先判断返回值,如果不为0说明出错了,那么就把hdev->children相应的那位设置为空.要知道我们在调用usb_new_device之前可是把它设置成了udev,怎奈它不思进取,只好放弃把它拉入usb组织的想法.

八大函数只剩下最后一个hub_power_remaining(),这个函数相对来说比较小巧玲珑.这是与电源管理相关的.虽然说刚接入的设备可能已经被设备驱动认领了,但是作为hub驱动,自身的工作还是要处理干净的.

2364 static unsigned

2365 hub_power_remaining (struct usb_hub *hub)

2366 {

2367 struct usb_device *hdev = hub->hdev;

2368 int remaining;

2369 int port1;

2370

2371 if (!hub->limited_power)

2372 return 0;

2373

2374 remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent;

2375 for (port1 = 1; port1 <= hdev->maxchild; ++port1) {

2376 struct usb_device *udev = hdev->children[port1 - 1];

2377 int delta;

2378

2379 if (!udev)

2380 continue;

2381

2382 /* Unconfigured devices may not use more than 100mA,

2383 * or 8mA for OTG ports */

2384 if (udev->actconfig)

2385 delta = udev->actconfig->desc.bMaxPower * 2;

2386 else if (port1 != udev->bus->otg_port || hdev->parent)

2387 delta = 100;

2388 else

2389 delta = 8;

2390 if (delta > hub->mA_per_port)

2391 dev_warn(&udev->dev, "%dmA is over %umA budget "

2392 "for port %d!/n",

2393 delta, hub->mA_per_port, port1);

2394 remaining -= delta;

2395 }

2396 if (remaining < 0) {

2397 dev_warn(hub->intfdev, "%dmA over power budget!/n",

2398 - remaining);

2399 remaining = 0;

2400 }

2401 return remaining;

2402 }

limited_power是咱们当初在hub_configure()中设置的.设置了它说明能源是有限的,希望大家珍惜.所以如果这个变量不为0,我们就要对电源精打细算.要计算出现在还能提供多大电流.即把当前bus_mA减去hub自己需要的电流以及现在连在hub端口上的设备所消耗的电流,求出剩余值来,然后打印出来,告诉世界我们还有多少电流budget.bHubContrCurrent咱们前面在讲hub_configure的时候就已经说过了,Hub控制器本身最大的电流需求,单位是mA,来自hub描述符.

23752395行这段循环,就是上面说的这个思想的具体实现.遍历每个端口进行循环.每个设备的配置描述符中bMaxPower就是该设备从usb总线上消耗的最大的电流.其单位是2mA,所以这里要乘以2.没有配置过的设备是不可能获得超过100mA电流的.

如果deltahub为每个端口提供的平均电流要大,那么至少要警告一下.

然后循环完了,remaining就是如其字面意义一样,还剩下多少电流可供新的设备再接进来.注意到我们从软件的角度来说,是不会强行对设备采取什么措施,我们最多是打印出调试信息,警告警告,而设备如果真的遇到了供电问题,它自然会出现异常,它也许不能工作,这些当然由具体的设备驱动程序去关注,Hub这一层来说,没有必要去干涉人家内部的事情,做到这一步已经是仁至义尽了.

终于我们讲完了这八个函数,可以说到这里为止,我们已经知道了hub驱动是在端口连接有变化的时候如何工作的,并且更重要的是我们知道了hub驱动是如何为子设备驱动服务的.回到hub_port_connect_change之后,一切正常的话我们将会从2579行返回.

剩下的一些行就是错误处理代码.我们就不必再看了.因此我们将返回到hub_events()中来.这个函数还剩下几行,我们下节再看.不过你千万别以为看到这里你就完全明白hub驱动程序了,因为那样无异于你把自己家里的铁锅背在背上就以为自己是忍者神鬼.

别着急,慢慢来,其实懂与不懂,就像黑夜和白天,相隔一瞬间.

分享到:
评论

相关推荐

    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