作者: zjujoe 转载请注明出处
Email:[email protected]
BLOG:http://blog.csdn.net/zjujoe
一直没有弄明白 dummy hcd 到底有什么用,因为它位于 gadget 目录里,以为是一个软件模拟的 udc 驱动。但是想想 gadget 驱动功能需要主机端触发,不知道这么一个假的 udc 驱动到底怎么触发数据传输。
今天简单的分析了一下代码。发现原来 dummy hcd 包含了 host controller 及 device controller 驱动, 这样, 就可以在同一台机器上进行 host 和 device 端的传输。如果你正在写主机端的 client 驱动或者设备端的 gadget 驱动,使用 dummy hcd 是很高效的。 下面简单分析一下这个驱动。
看一下初始化代码:
2021 static int __init init (void)
2022 {
2023 int retval;
2024
2025 if (usb_disabled ())
2026 return -ENODEV;
2027
2028 retval = platform_driver_register (&dummy_hcd_driver);
2029 if (retval < 0)
2030 return retval;
2031
2032 retval = platform_driver_register (&dummy_udc_driver);
2033 if (retval < 0)
2034 goto err_register_udc_driver;
2035
2036 retval = platform_device_register (&the_hcd_pdev);
2037 if (retval < 0)
2038 goto err_register_hcd;
2039
2040 retval = platform_device_register (&the_udc_pdev);
2041 if (retval < 0)
2042 goto err_register_udc;
2043 return retval;
2044
2045 err_register_udc:
2046 platform_device_unregister (&the_hcd_pdev);
2047 err_register_hcd:
2048 platform_driver_unregister (&dummy_udc_driver);
2049 err_register_udc_driver:
2050 platform_driver_unregister (&dummy_hcd_driver);
2051 return retval;
2052 }
2053 module_init (init);
同时注册了 usb 主机端、设备端 的驱动及设备。再看一下该“设备”提供的 “端点”:
124 static const char *const ep_name [] = {
125 ep0name, /* everyone has ep0 */
126
127 /* act like a net2280: high speed, six configurable endpoints */
128 "ep-a", "ep-b", "ep-c", "ep-d", "ep-e", "ep-f",
129
130 /* or like pxa250: fifteen fixed function endpoints */
131 "ep 1in -bulk", "ep2out-bulk", "ep 3in -iso", "ep4out-iso", "ep 5in -int",
132 "ep 6in -bulk", "ep7out-bulk", "ep 8in -iso", "ep9out-iso", "ep 10in -int",
133 "ep 11in -bulk", "ep12out-bulk", "ep 13in -iso", "ep14out-iso",
134 "ep 15in -int",
135
136 /* or like sa1100: two fixed function endpoints */
137 "ep1out-bulk", "ep 2in -bulk",
138 };
可见,dummy_hcd 提供了丰富的端点类型。可以用来测试各种上层驱动。
再研究一下数据是怎么在host 端和 client 端进行传输的, 我们使用 kft 工具画出一次数据传输的函数调用流程:
数据通过 usb_submit_urb 提交后,函数 dummy_timer 会调用 transfer 函数进行数据传输:
1065 /* transfer up to a frame's worth; caller must own lock */
1066 static int
1067 transfer (struct dummy *dum, struct urb *urb, struct dummy_ep *ep, int limit)
1068 {
1069 struct dummy_request *req;
1070
。。。
1111 /* else transfer packet(s) */
1112 ubuf = urb->transfer_buffer + urb->actual_length;
1113 rbuf = req->req.buf + req->req.actual;
1114 if (to_host)
1115 memcpy (ubuf, rbuf, len);
1116 else
1117 memcpy (rbuf, ubuf, len);
1118 ep->last_io = jiffies;
。。。
这里, transfer 函数会调用 memcpy 进行数据 ”传输”。
再看一下控制传输, 先看调用图:
以及代码:
1238 /* drive both sides of the transfers; looks like irq handlers to
1239 * both drivers except the callbacks aren't in_irq().
1240 */
1241 static void dummy_timer (unsigned long _dum)
1242 {
。。。
1336 /* handle control requests */
1337 if (ep == &dum->ep [0] && ep->setup_stage) {
1338 struct usb_ctrlrequest setup;
1339 int value = 1;
1340 struct dummy_ep *ep2;
1341 unsigned w_index;
1342 unsigned w_value;
1343
1344 setup = *(struct usb_ctrlrequest*) urb->setup_packet;
1345 w_index = le16_to_cpu(setup.wIndex);
1346 w_value = le16_to_cpu(setup.wValue);
1347 if (le16_to_cpu(setup.wLength) !=
1348 urb->transfer_buffer_length) {
1349 maybe_set_status (urb, -EOVERFLOW);
1350 goto return_urb;
1351 }
1352
。。。
1366
1367 /* gadget driver never sees set_address or operations
1368 * on standard feature flags. some hardware doesn't
1369 * even expose them.
1370 */
1371 ep->last_io = jiffies;
1372 ep->setup_stage = 0;
1373 ep->halted = 0;
1374 switch (setup.bRequest) {
1375 case USB_REQ_SET_ADDRESS:
1376 if (setup.bRequestType != Dev_Request)
1377 break;
1378 dum->address = w_value;
1379 maybe_set_status (urb, 0);
1380 dev_dbg (udc_dev(dum), "set_address = %d/n",
1381 w_value);
1382 value = 0;
1383 break;
1384 case USB_REQ_SET_FEATURE:
1385 if (setup.bRequestType == Dev_Request) {
1386 value = 0;
1387 switch (w_value) {
1388 case USB_DEVICE_REMOTE_WAKEUP:
1389 break;
1390 case USB_DEVICE_B_HNP_ENABLE:
1391 dum->gadget.b_hnp_enable = 1;
1392 break;
1393 case USB_DEVICE_A_HNP_SUPPORT:
1394 dum->gadget.a_hnp_support = 1;
1395 break;
1396 case USB_DEVICE_A_ALT_HNP_SUPPORT:
1397 dum->gadget.a_alt_hnp_support
1398 = 1;
1399 break;
1400 default:
1401 value = -EOPNOTSUPP;
1402 }
1403 if (value == 0) {
1404 dum->devstatus |=
1405 (1 << w_value);
1406 maybe_set_status (urb, 0);
1407 }
1408
1409 } else if (setup.bRequestType == Ep_Request) {
1410 // endpoint halt
1411 ep2 = find_endpoint (dum, w_index);
1412 if (!ep2) {
1413 value = -EOPNOTSUPP;
1414 break;
1415 }
1416 ep2->halted = 1;
1417 value = 0;
1418 maybe_set_status (urb, 0);
1419 }
1420 break;
1421 case USB_REQ_CLEAR_FEATURE:
1422 if (setup.bRequestType == Dev_Request) {
1423 switch (w_value) {
1424 case USB_DEVICE_REMOTE_WAKEUP:
1425 dum->devstatus &= ~(1 <<
1426 USB_DEVICE_REMOTE_WAKEUP);
1427 value = 0;
1428 maybe_set_status (urb, 0);
1429 break;
1430 default:
1431 value = -EOPNOTSUPP;
1432 break;
1433 }
1434 } else if (setup.bRequestType == Ep_Request) {
1435 // endpoint halt
1436 ep2 = find_endpoint (dum, w_index);
1437 if (!ep2) {
1438 value = -EOPNOTSUPP;
1439 break;
1440 }
1441 ep2->halted = 0;
1442 value = 0;
1443 maybe_set_status (urb, 0);
1444 }
1445 break;
1446 case USB_REQ_GET_STATUS:
1447 if (setup.bRequestType == Dev_InRequest
1448 || setup.bRequestType
1449 == Intf_InRequest
1450 || setup.bRequestType
1451 == Ep_InRequest
1452 ) {
1453 char *buf;
1454
1455 // device: remote wakeup, selfpowered
1456 // interface: nothing
1457 // endpoint: halt
1458 buf = (char *)urb->transfer_buffer;
1459 if (urb->transfer_buffer_length > 0) {
1460 if (setup.bRequestType ==
1461 Ep_InRequest) {
1462 ep2 = find_endpoint (dum, w_index);
1463 if (!ep2) {
1464 value = -EOPNOTSUPP;
1465 break;
1466 }
1467 buf [0] = ep2->halted;
1468 } else if (setup.bRequestType ==
1469 Dev_InRequest) {
1470 buf [0] = (u8)
1471 dum->devstatus;
1472 } else
1473 buf [0] = 0;
1474 }
1475 if (urb->transfer_buffer_length > 1)
1476 buf [1] = 0;
1477 urb->actual_length = min (2,
1478 urb->transfer_buffer_length);
1479 value = 0;
1480 maybe_set_status (urb, 0);
1481 }
1482 break;
1483 }
1484
1485 /* gadget driver handles all other requests. block
1486 * until setup() returns; no reentrancy issues etc.
1487 */
1488 if (value > 0) {
1489 spin_unlock (&dum->lock);
1490 value = dum->driver->setup (&dum->gadget,
1491 &setup);
1492 spin_lock (&dum->lock);
1493
1494 if (value >= 0) {
1495 /* no delays (max 64KB data stage) */
1496 limit = 64*1024;
1497 goto treat_control_like_bulk;
1498 }
1499 /* error, see below */
1500 }
。。。
在 dummy_timer 函数中,首先处理: set feature, get feature, set address, get status 这几种类型的请求(根据注释,有些甚至是硬件来处理的),剩下的则交给 gadget 驱动的 setup 函数。
首先编译出 dummy_hcd 等各种模块, 然后, 将其拷贝到目标机器。
启动机器,
/lib/modules # insmod ./dummy_hcd.ko
Using ./dummy_hcd.ko
dummy_hcd dummy_hcd: USB Host+Gadget Emulator, driver 02 May 2005
dummy_hcd dummy_hcd: Dummy host controller
dummy_hcd dummy_hcd: new USB bus registered, assigned bus number 1
usb usb1: configuration #1 chosen from 1 choice
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 1 port detected
/lib/modules # usb usb1: dummy_bus_suspe
/lib/modules # insmod ./usbtest.ko
Using ./usbtest.ko
usbcore: registered new interface driver usbtest
/lib/modules # insmod ./g_zero.ko
Using ./g_zero.ko
dummy_udc dummy_udc: binding gadget driver 'zero'
zero gadget: Gadget Zero, version: St Patrick's Day 2004
zero gadget: using dummy_udc, OUT ep-b IN ep-a
dummy_hcd dummy_hcd: port status 0x00010101 has changes
usb usb1: dummy_bus_resume
/lib/modules # dummy_hcd dummy_hcd: port status 0x00010101 has changes
zero gadget: resume
dummy_hcd dummy_hcd: port status 0x00100503 has changes
usb 1-1: new high speed USB device using dummy_hcd and address 2
zero gadget: resume
dummy_hcd dummy_hcd: port status 0x00100503 has changes
dummy_udc dummy_udc: set_address = 2
usb 1-1: configuration #3 chosen from 2 choices
dummy_udc dummy_udc: enabled ep-a (ep 1in -bulk) maxpacket 512
dummy_udc dummy_udc: enabled ep-b (ep2out-bulk) maxpacket 512
zero gadget: buflen 4096
zero gadget: high speed config #3: source and sink data
usbtest 1-1:3.0: Linux gadget zero
usbtest 1-1:3.0: high speed {control in/out bulk-in bulk-out} tests (+alt)
/lib/modules # mount -t usbfs none /proc/bus/usb/
/lib/modules #
/lib/modules # ./testusb -a -c10 -t1 -s4096 -g32 -v32
unknown speed /proc/bus/usb/001/002
/proc/bus/usb/001/002 test 1, 0.096635 secs
测试成功
启动机器,
/lib/modules # insmod ./dummy_hcd.ko
Using ./dummy_hcd.ko
dummy_hcd dummy_hcd: USB Host+Gadget Emulator, driver 02 May 2005
dummy_hcd dummy_hcd: Dummy host controller
dummy_hcd dummy_hcd: new USB bus registered, assigned bus number 1
usb usb1: configuration #1 chosen from 1 choice
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 1 port detected
/lib/modules # usb usb1: dummy_bus_suspe
/lib/modules # insmod ./g_file_storage.ko
Using ./g_file_storage.ko
dummy_udc dummy_udc: binding gadget driver 'g_file_storage'
g_file_storage gadget: File-backed Storage Gadget, version: 28 November 2005
g_file_storage gadget: Number of LUNs=1
g_file_storage gadget-lun0: ro=0, file: /dev/mtdblock5
dummy_hcd dummy_hcd: port status 0x00010101 has changes
usb usb1: dummy_bus_resume
/lib/modules #
/lib/modules # dummy_hcd dummy_hcd: port status 0x00010101 has changes
dummy_hcd dummy_hcd: port status 0x00100503 has changes
usb 1-1: new high speed USB device using dummy_hcd and address 2
dummy_hcd dummy_hcd: port status 0x00100503 has changes
dummy_udc dummy_udc: set_address = 2
usb 1-1: configuration #1 chosen from 1 choice
dummy_udc dummy_udc: enabled ep-a (ep 1in -bulk) maxpacket 512
dummy_udc dummy_udc: enabled ep-b (ep2out-bulk) maxpacket 512
g_file_storage gadget: high speed config #1
usb usb1: dummy_bus_suspend
/lib/modules # insmod ./usb-storage.ko
Using ./usb-storage.ko
Initializing USB Mass Storage driver...
usb usb1: dummy_bus_resume
dummy_hcd dummy_hcd: port status 0x00040503 has changes
scsi0 : SCSI emulation for USB Mass Storage devices
usb-storage: device found at 2
usb-storage: waiting for device to settle before scanning
usbcore: registered new interface driver usb-storage
USB Mass Storage support registered.
/lib/modules #
/lib/modules # scsi 0:0:0:0: Direct-Access Linux File-Stor Gadget 0302 PQ: 0 ANSI: 2
SCSI device sda: 65536 512-byte hdwr sectors (34 MB)
sda: Write Protect is off
sda: Mode Sense: 0f 00 00 00
sda: assuming drive cache: write through
SCSI device sda: 65536 512-byte hdwr sectors (34 MB)
sda: Write Protect is off
sda: Mode Sense: 0f 00 00 00
sda: assuming drive cache: write through
sda: unknown partition table
sd 0:0:0:0: Attached scsi removable disk sda
usb-storage: device scan complete
测试成功。