(轉)Linux設備驅動之HID驅動 源碼分析


//Linux設備驅動之HID驅動 源碼分析

http://blog.chinaunix.net/uid-20543183-id-1930836.html


HID是Human Interface Devices的縮寫.翻譯成中文即為人機交互設備.這里的人機交互設備是一個宏觀上面的概念,任何設備,只要符合HID spec,都可以稱之為HID設備.常見的HID設備有鼠標鍵盤,游戲操縱桿等等.在接下來的代碼分析中,可以參考HID的spec.這份spec可以在www.usb.org上找到.分析的代碼主要集中在linux-2.6.25/drivers/hid目錄下.
 
對此設備結點的處理有兩種接口,一種是read(),另一種是ioctl();
 
  • read(): This is the event interface. When the HID device performs an interrupt transfer, indicating a change of state, data will be made available at the associated hiddev device with the content of a struct hiddev_event:struct hiddev_event { unsigned hid; signed int value; };containing the HID usage identifier for the status that changed, and the value that it was changed to.
  • ioctl(): This is the control interface. There are a number of controls:
    HIDIOCGVERSION int (read) Gets the version code out of the hiddev driver.
    HIDIOCAPPLICATION (none) This ioctl call returns the HID application usage associated with the hid device. The third argument to ioctl() specifies which application index to get. This is useful when the device has more than one application collection. If the index is invalid (greater or equal to the number of application collections this device has) the ioctl returns -1. You can find out beforehand how many application collections the device has from the num_applications field from the hiddev_devinfo structure.
    HIDIOCGDEVINFO struct hiddev_devinfo (read) Gets a hiddev_devinfo structure which describes the device.
    HIDIOCGSTRING struct struct hiddev_string_descriptor (read/write) Gets a string descriptor from the device. The caller must fill in the "index" field to indicate which descriptor should be returned.
    HIDIOCINITREPORT   Instructs the kernel to retrieve all input and feature report values from the device. At this point, all the usage structures will contain current values for the device, and will maintain it as the device changes.
    HIDIOCGNAME string (variable length) Gets the device name
    HIDIOCGREPORT struct hiddev_report_info (write) Instructs the kernel to get a feature or input report from the device, in order to selectively update the usage structures (in contrast to INITREPORT).
    HIDIOCSREPORT struct hiddev_report_info (write) Instructs the kernel to send a report to the device. This report can be filled in by the user throughHIDIOCSUSAGE calls (below) to fill in individual usage values in the report before sending the report in full to the device.
    HIDIOCGREPORTINFO struct hiddev_report_info (read/write) Fills in a hiddev_report_info structure for the user. The report is looked up by type (input, output or feature) and id, so these fields must be filled in by the user. The ID can be absolute -- the actual report id as reported by the device -- or relative -- HID_REPORT_ID_FIRST for the first report, and (HID_REPORT_ID_NEXT | report_id) for the next report after report_id. Without a-priori information about report ids, the right way to use this ioctl is to use the relative IDs above to enumerate the valid IDs. The ioctl returns non-zero when there is no more next ID. The real report ID is filled into the returned hiddev_report_info structure.
    HIDIOCGFIELDINFO struct hiddev_field_info (read/write) Returns the field information associated with a report in a hiddev_field_info structure. The user must fill in report_id and report_type in this structure, as above. The field_index should also be filled in, which should be a number from 0 and maxfield-1, as returned from a previous HIDIOCGREPORTINFO call.
    HIDIOCGUCODE struct hiddev_usage_ref (read/write) Returns the usage_code in a hiddev_usage_ref structure, given that given its report type, report id, field index, and index within the field have already been filled into the structure.
    HIDIOCGUSAGE struct hiddev_usage_ref (read/write) Returns the value of a usage in a hiddev_usage_ref structure. The usage to be retrieved can be specified as above, or the user can choose to fill in the report_type field and specify the report_id asHID_REPORT_ID_UNKNOWN. In this case, the hiddev_usage_ref will be filled in with the report and field infomation associated with this usage if it is found.
    HIDIOCSUSAGE struct hiddev_usage_ref (write) Sets the value of a usage in an output report.

 
 
//利用libusb 實現的hid 讀寫
http://bbs.csdn.net/topics/370027825
寫入用usb_interrupt_write
讀取用usb_interrupt_read
 
 
http://blog.csdn.net/acf/article/details/5431488
在Linux 2.6環境下讀寫HID設備(USB Key)

      Linux 2.6內核中包含了HID驅動,能夠自動把USB Key等HID外設識別成“/dev/hiddev0”之類的設備。但是該驅動沒有實現write接口,因此無法象Windows平台那樣使用 ReadFile和WriteFile來讀寫HID設備,而只能使用ioctl接口。
      網上有各種各樣讀寫HID設備的源代碼例子,有的是通過HIDIOCSUSAGE和HIDIOCGUSAGE來每次收發4個字節,適合鼠標、鍵盤之類數據傳輸量小的設備;有的是通過HIDIOCSUSAGES和HIDIOCGUSAGES來連續接收和發送多個字節,適合USB Key一類的設備。
      在上一篇日志(已刪除)中,介紹了如何利用《USB and PIC: quick guide to an USB HID framework》一文提供的方法與USB Key進行通信(先發送HIDIOCSUSAGES和HIDIOCSREPORT進行寫操作,再發送HIDIOCGREPORT和HIDIOCGUSAGES進行讀操作,從而完成一次通信過程)。但是經過好友測試,發現該方法不論是在PC機上,還是在Cavium Octeon 52XX開發板上均存在問題,讀出的數據始終是第一次通信的結果,除非在每次通信之前都發送HIDIOCINITREPORT控制碼,但這又會造成相當長時間的阻塞。
      進一步的測試表明,如果按照HIDIOCGUCODE、HIDIOCSUSAGES、HIDIOCSREPORT、HIDIOCGUCODE、HIDIOCGUSAGES的順序發送控制碼,那么可以每次都讀出正確數據。不過該方法雖然在PC機上只需400毫秒延時,但是在Octeon開發板上仍會長時間阻塞在usbhid_wait_io函數那里。
      無奈之下,我只好根據Cavium SDK自帶的Linux內核源碼中的usb_skeleton.c寫了一個USB設備驅動程序,試圖通過直接讀寫USB端點來完成通信過程。以下是在開發和調試過程中需要注意的幾個問題:
      首先,必須卸載Linux內核自帶的HID驅動,否則它會自動“接管”新插入的USB Key,導致我們自己編寫的驅動程序找不到設備。對於開發板,可以在編譯內核時去掉HID相關的選項;對於PC機上已經安裝好的Linux,我也不知道該怎么卸載其中的HID驅動。
      其次,端點類型。在usb_skeleton.c中是通過bulk端點來訪問USB設備的,而USB Key作為HID設備,一般只有0號控制端點和一個中斷輸入端點(例如3號)。對於中斷端點,可以用usb_interrupt_msg(其實就是usb_bulk_msg)函數進行訪問;對於控制端點,則稍微麻煩一些,因為除了數據,還需要構造一個8字節的setup包。有關setup包的詳細結構,可以參考USB和HID規范。獲取setup包具體數值最簡單的方法,就是在Windows環境下用BusHound觀察USB Key的通信過程。
      最后,關於Report ID。在Windows環境下通過ReadFile和WriteFile訪問HID設備時,必須在數據開頭附加1字節的Report ID(一般為0)。在Linux環境下,如果使用HID驅動的ioctl接口,那么需要在hiddev_usage_ref結構中指定Report ID;如果使用自己編寫的USB驅動程序,則不需要考慮Report ID,直接發送數據就得了。
      經過測試,利用自己編寫的驅動程序,可以在Octeon開發板上正確讀寫HID類型的USB Key,而且讀寫之間的時間間隔也可以縮短至50毫秒。

從內核2.6.34 的代碼來看,/dev/hidraw0只能操作 endpoint 0,即通用的控制通道,強行向其寫入數據會提示

  write: Broken pipe

在內核 2.6.35以后進行了修正, 參考如下:

https://patchwork.kernel.org/patch/99990/

點擊(此處)折疊或打開

  1. diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
  2. index 56d06cd..6fd833d 100644
  3. --- a/drivers/hid/usbhid/hid-core.c
  4. +++ b/drivers/hid/usbhid/hid-core.c
  5. static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count,
  6. unsigned char report_type)
  7. {
  8. struct usbhid_device *usbhid = hid->driver_data;
  9. struct usb_device *dev = hid_to_usb_dev(hid);
  10. struct usb_interface *intf = usbhid->intf;
  11. struct usb_host_interface *interface = intf->cur_altsetting;
  12. int ret;
  13. - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
  14. - HID_REQ_SET_REPORT,
  15. - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
  16. - ((report_type + 1) << 8) | *buf,
  17. - interface->desc.bInterfaceNumber, buf + 1, count - 1,
  18. - USB_CTRL_SET_TIMEOUT);
  19. -
  20. - /* count also the report id */
  21. - if (ret > 0)
  22. - ret++;
  23. + if (usbhid->urbout) {
  24. + int actual_length;
  25. + int skipped_report_id = 0;
  26. + if (buf[0] == 0x0) {
  27. + /* Don't send the Report ID */
  28. + buf++;
  29. + count--;
  30. + skipped_report_id = 1;
  31. + }
  32. + ret = usb_interrupt_msg(dev, usbhid->urbout->pipe,
  33. + buf, count, &actual_length,
  34. + USB_CTRL_SET_TIMEOUT);
  35. + /* return the number of bytes transferred */
  36. + if (ret == 0) {
  37. + ret = actual_length;
  38. + /* count also the report id */
  39. + if (skipped_report_id)
  40. + ret++;
  41. + }
  42. + } else {
  43. + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
  44. + HID_REQ_SET_REPORT,
  45. + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
  46. + ((report_type + 1) << 8) | *buf,
  47. + interface->desc.bInterfaceNumber, buf + 1, count - 1,
  48. + USB_CTRL_SET_TIMEOUT);
  49. + /* count also the report id */
  50. + if (ret > 0)
  51. + ret++;
  52. + }
  53. return ret;
  54. }
 
hid-example.c,來演示如何操作
  http://www.emdebian.org/~zumbi/kernel/linux-2.6-3.0.0~rc1/samples/hidraw/hid-example.c

點擊(此處)折疊或打開

  1. /*
  2.  * Hidraw Userspace Example
  3.  *
  4.  * Copyright (c) 2010 Alan Ott <alan@signal11.us>
  5.  * Copyright (c) 2010 Signal 11 Software
  6.  *
  7.  * The code may be used by anyone for any purpose,
  8.  * and can serve as a starting point for developing
  9.  * applications using hidraw.
  10.  */
  11. /* Linux */
  12. #include <linux/types.h>
  13. #include <linux/input.h>
  14. #include <linux/hidraw.h>
  15. /*
  16.  * Ugly hack to work around failing compilation on systems that don't
  17.  * yet populate new version of hidraw.to userspace.
  18.  *
  19.  * If you need this, please have your distro update the kernel headers.
  20.  */
  21. #ifndef HIDIOCSFEATURE
  22. #define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
  23. #define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
  24. #endif
  25. /* Unix */
  26. #include <sys/ioctl.h>
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <fcntl.h>
  30. #include <unistd.h>
  31. /* C */
  32. #include <stdio.h>
  33. #include <string.h>
  34. #include <stdlib.h>
  35. #include <errno.h>
  36. const char *bus_str(int bus);
  37. int main(int argc, char **argv)
  38. {
  39.     int fd;
  40.     int i, res, desc_size = 0;
  41.     char buf[256];
  42.     struct hidraw_report_descriptor rpt_desc;
  43.     struct hidraw_devinfo info;
  44.     /* Open the Device with non-blocking reads. In real life,
  45.      don't use a hard coded path; use libudev instead. */
  46.     fd = open("/dev/hidraw0", O_RDWR|O_NONBLOCK);
  47.     if (fd < 0) {
  48.         perror("Unable to open device");
  49.         return 1;
  50.     }
  51.     memset(&rpt_desc, 0x0, sizeof(rpt_desc));
  52.     memset(&info, 0x0, sizeof(info));
  53.     memset(buf, 0x0, sizeof(buf));
  54.     /* Get Report Descriptor Size */
  55.     res = ioctl(fd, HIDIOCGRDESCSIZE, &desc_size);
  56.     if (res < 0)
  57.         perror("HIDIOCGRDESCSIZE");
  58.     else
  59.         printf("Report Descriptor Size: %d\n", desc_size);
  60.     /* Get Report Descriptor */
  61.     rpt_desc.size = desc_size;
  62.     res = ioctl(fd, HIDIOCGRDESC, &rpt_desc);
  63.     if (res < 0) {
  64.         perror("HIDIOCGRDESC");
  65.     } else {
  66.         printf("Report Descriptor:\n");
  67.         for (= 0; i < rpt_desc.size; i++)
  68.             printf("%hhx ", rpt_desc.value[i]);
  69.         puts("\n");
  70.     }
  71.     /* Get Raw Name */
  72.     res = ioctl(fd, HIDIOCGRAWNAME(256), buf);
  73.     if (res < 0)
  74.         perror("HIDIOCGRAWNAME");
  75.     else
  76.         printf("Raw Name: %s\n", buf);
  77.     /* Get Physical Location */
  78.     res = ioctl(fd, HIDIOCGRAWPHYS(256), buf);
  79.     if (res < 0)
  80.         perror("HIDIOCGRAWPHYS");
  81.     else
  82.         printf("Raw Phys: %s\n", buf);
  83.     /* Get Raw Info */
  84.     res = ioctl(fd, HIDIOCGRAWINFO, &info);
  85.     if (res < 0) {
  86.         perror("HIDIOCGRAWINFO");
  87.     } else {
  88.         printf("Raw Info:\n");
  89.         printf("\tbustype: %d (%s)\n",
  90.             info.bustype, bus_str(info.bustype));
  91.         printf("\tvendor: 0x%04hx\n", info.vendor);
  92.         printf("\tproduct: 0x%04hx\n", info.product);
  93.     }
  94.     /* Set Feature */
  95.     buf[0] = 0x9; /* Report Number */
  96.     buf[1] = 0xff;
  97.     buf[2] = 0xff;
  98.     buf[3] = 0xff;
  99.     res = ioctl(fd, HIDIOCSFEATURE(4), buf);
  100.     if (res < 0)
  101.         perror("HIDIOCSFEATURE");
  102.     else
  103.         printf("ioctl HIDIOCGFEATURE returned: %d\n", res);
  104.     /* Get Feature */
  105.     buf[0] = 0x9; /* Report Number */
  106.     res = ioctl(fd, HIDIOCGFEATURE(256), buf);
  107.     if (res < 0) {
  108.         perror("HIDIOCGFEATURE");
  109.     } else {
  110.         printf("ioctl HIDIOCGFEATURE returned: %d\n", res);
  111.         printf("Report data (not containing the report number):\n\t");
  112.         for (= 0; i < res; i++)
  113.             printf("%hhx ", buf[i]);
  114.         puts("\n");
  115.     }
  116.     /* Send a Report to the Device */
  117.     buf[0] = 0x1; /* Report Number */
  118.     buf[1] = 0x77;
  119.     res = write(fd, buf, 2);
  120.     if (res < 0) {
  121.         printf("Error: %d\n", errno);
  122.         perror("write");
  123.     } else {
  124.         printf("write() wrote %d bytes\n", res);
  125.     }
  126.     /* Get a report from the device */
  127.     res = read(fd, buf, 16);
  128.     if (res < 0) {
  129.         perror("read");
  130.     } else {
  131.         printf("read() read %d bytes:\n\t", res);
  132.         for (= 0; i < res; i++)
  133.             printf("%hhx ", buf[i]);
  134.         puts("\n");
  135.     }
  136.     close(fd);
  137.     return 0;
  138. }
  139. const char *
  140. bus_str(int bus)
  141. {
  142.     switch (bus) {
  143.     case BUS_USB:
  144.         return "USB";
  145.         break;
  146.     case BUS_HIL:
  147.         return "HIL";
  148.         break;
  149.     case BUS_BLUETOOTH:
  150.         return "Bluetooth";
  151.         break;
  152.     case BUS_VIRTUAL:
  153.         return "Virtual";
  154.         break;
  155.     default:
  156.         return "Other";
  157.         break;
  158.     }
  159. }
  160.  
 
//----- openwrt 下安裝和調試
 
root@OpenWrt:~# opkg install kmod-usb-hid --force-depends
Installing kmod-usb-hid (3.3.8-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/ar71xx/packages/kmod-usb-hid_3.3.8-1_ar71xx.ipk.
Installing kmod-hid (3.3.8-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/ar71xx/packages/kmod-hid_3.3.8-1_ar71xx.ipk.
Installing kmod-input-evdev (3.3.8-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/ar71xx/packages/kmod-input-evdev_3.3.8-1_ar71xx.ipk.
Configuring kmod-input-evdev.
Configuring kmod-hid.
Configuring kmod-usb-hid.
Collected errors:
 * satisfy_dependencies_for: Cannot satisfy the following dependencies for kmod-usb-hid:
 *      kernel (= 3.3.8-1-231112e548aba152810eababd16134bc) *   kernel (= 3.3.8-1-231112e548aba152810eababd16134bc) *   kernel (= 3.3.8-1-231112e548aba152810eababd16134bc) * 
 
root@OpenWrt:~# lsmod | grep hid
usbhid                 20688  0 [permanent]
hid                    64032  1 usbhid,[permanent]
usbcore                99168 19 usbhid,uvcvideo,gspca_zc3xx,gspca_main,ums_usbat,ums_sddr55,ums_sddr09,ums_karma,ums_jumpshot,ums_isd200,ums_freecom,ums_datafab,ums_cypress,ums_alauda,usb_storage,uhci_hcd,ohci_hcd,ehci_hcd
input_core             20016  7 usbhid,hid,evdev,uvcvideo,gspca_zc3xx,gspca_main
 
# 插上usb hub, 無法發現hid設備(原因: hub的紅線為電源線, 白線為數據線)
root@OpenWrt:~# dmesg | grep hid
[   12.000000] usbcore: registered new interface driver usbhid
[   12.010000] usbhid: USB HID core driver
 
# 無hub, 直接接上hid設備, 可以找到設備
root@OpenWrt:~# dmesg | grep hid
[   12.000000] usbcore: registered new interface driver usbhid
[   12.010000] usbhid: USB HID core driver
[  185.290000] generic-usb 0003:0483:D0D0.0001: hiddev0: USB HID v1.10 Device [STMicroelectronics CR95HF] on usb-ehci-platform-1/input1
 
# hid設備標識符, 不是 標准的 /dev/hiddev0
root@OpenWrt:~# ls /dev/hid*
ls: /dev/hid*: No such file or directory
 
root@OpenWrt:~# ls -al /dev/usb/hid*
crw-r--r--    1 root     root      180,  96 Jan  1 00:03 /dev/usb/hiddev0
 
# 去除設備, 無標識符. 
root@OpenWrt:~# ls -al /dev/usb/hid*
ls: /dev/usb/hid*: No such file or directory
 
# 插拔3次, 
root@OpenWrt:~# dmesg | grep hid
[   12.000000] usbcore: registered new interface driver usbhid
[   12.010000] usbhid: USB HID core driver
[  185.290000] generic-usb 0003:0483:D0D0.0001: hiddev0: USB HID v1.10 Device [STMicroelectronics CR95HF] on usb-ehci-platform-1/input1
[ 1034.170000] generic-usb 0003:0483:D0D0.0002: hiddev0: USB HID v1.10 Device [STMicroelectronics CR95HF] on usb-ehci-platform-1/input1
[ 1085.970000] generic-usb 0003:0483:D0D0.0003: hiddev0: USB HID v1.10 Device [STMicroelectronics CR95HF] on usb-ehci-platform-1/input1
 
 
root@OpenWrt:~# find / -name hid*
/dev/usb/hiddev0
/lib/modules/3.3.8/hid.ko
/overlay/lib/modules/3.3.8/hid.ko
/sys/devices/platform/ehci-platform/usb1/1-1/1-1:1.1/usb/hiddev0
/sys/bus/hid
/sys/class/usb/hiddev0
/sys/kernel/debug/hid
/sys/module/input_core/holders/hid
/sys/module/hid
/sys/module/usbhid/drivers/hid:generic-usb
 
-----------------------------------------------------------------
root@OpenWrt:/xutest# opkg install hid_xu1_ar71xx.ipk
Installing hid (xu1) to root...
Configuring hid.
root@OpenWrt:/xutest# hidtest
/dev/hiddev0 information: 
HIDIOCGVERSION: 1.4
HIDIOCGDEVINFO: bustype=3 busnum=1 devnum=4 ifnum=1
        vendor=0x0483 product=0xd0d0 version=0x0200
        num_applications=1
HIDIOCGNAME: STMicroelectronics CR95HF
Reports of type Input (1):
Report id: 7 (1 fields)
 Field: 0: app: 8c0001 phys 0000 flags 2 (63 usages) unit 0 exp 0
Reports of type Output (2):
Report id: 1 (1 fields)
 Field: 0: app: 8c0001 phys 0000 flags 2 (63 usages) unit 0 exp 0
Report id: 2 (1 fields)
 Field: 0: app: 8c0001 phys 0000 flags 2 (63 usages) unit 0 exp 0
Reports of type Feature (3):
Waiting for events ... (interrupt to exit)
^C
root@OpenWrt:/xutest# 
 
 
 
下位機修改如下:

點擊(此處)折疊或打開

  1. #if 1
  2. ucNeedSend = 1;
  3. Pos = HID_OFFSET_LENGTH + 1;
  4. #if 0
  5. ucSendData = 64;
  6. for (i=0; i<ucSendData-2; i++)
  7. SendBuffer[i+Pos] = 0x70 + i;
  8. #else
  9. ucSendData = RcvBuffer[HID_OFFSET_LENGTH];
  10. for (i=0; i<ucSendData-2; i++)
  11. SendBuffer[i+Pos] = RcvBuffer[i+Pos];
  12. #endif
  13.     if (ucNeedSend) {
  14.         /* SendBuffer format
  15.             1st byte     : ID_SEND_HID_RESPONSE
  16.             2nd byte     : repply or error code flag
  17.             3rd byte     : nb byte
  18.             others bytes: data
  19.             #define HID_SEND_HID_RESPONSE                0x07
  20.             #define HID_OFFSET                            0x00
  21.             #define HID_OFFSET_CMDCODE            HID_OFFSET + 1
  22.             #define HID_OFFSET_LENGTH                HID_OFFSET + 2
  23.         */
  24.         SendBuffer[HID_OFFSET] = HID_SEND_HID_RESPONSE;
  25.         //SendBuffer[HID_OFFSET_LENGTH] = MIN(HID_MAX_BUFFER_SIZE, SendBuffer[HID_OFFSET_LENGTH]);
  26.         SendBuffer[HID_OFFSET_LENGTH] = ucSendData;
  27.         /* Allows the transmission */
  28.         SetEPTxStatus(ENDP3, EP_TX_VALID);
  29.         USB_SIL_Write(EP3_IN, SendBuffer, HID_MAX_BUFFER_SIZE);
  30.     }
 
路由器上調試如下:
root@OpenWrt:/xutest# hidtest /dev/hiddev0
/dev/hiddev0 information: 
HIDIOCGDEVINFO: bustype=3 busnum=1 devnum=11 ifnum=1
        vendor=0x0483 product=0xd0d0 version=0x0200
        num_applications=1
VID = 0x483, PID = 0xffffd0d0
--find device
HIDIOCGNAME: STMicroelectronics CR95HF
 
Reports of type Input (1):
Report id: 0x07 (1 fields)
 
Reports of type Output (2):
Report id: 0x01 (1 fields)
Report id: 0x02 (1 fields)
 
Reports of type Feature (3):
5A 08 01 02 03 04 05 06 
00 08 01 02 03 04 05 06 
 
5A 0A 10 11 12 13 14 15 16 17 
00 0A 10 11 12 13 14 15 16 17 
 
5A 0B 70 71 72 73 74 75 76 77 78 
00 0B 70 71 72 73 74 75 76 77 78 
 
5A 0C 10 11 12 13 14 15 16 17 18 19 
00 0C 10 11 12 13 14 15 16 17 18 19 
 
5A 0D 70 71 72 73 74 75 76 77 78 79 7A 
00 0D 70 71 72 73 74 75 76 77 78 79 7A 
 
5A 0E 10 11 12 13 14 15 16 17 18 19 1A 1B 
00 0E 10 11 12 13 14 15 16 17 18 19 1A 1B 
 
5A 0F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 
00 0F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 
 
5A 10 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 
00 10 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 
 
5A 11 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 
00 11 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 
 
5A 12 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 
00 12 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 
 
5A 13 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F 80 
00 13 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F 80 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM