Subversion Repositories Kolibri OS

Rev

Rev 5051 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. ; standard driver stuff; version of driver model = 5
  2. format PE DLL native 0.05
  3. entry START
  4.  
  5. DEBUG = 1
  6.  
  7. ; this is for DEBUGF macro from 'fdo.inc'
  8. __DEBUG__ = 1
  9. __DEBUG_LEVEL__ = 1
  10.  
  11. include '../../struct.inc'
  12.  
  13. ; Compile-time settings.
  14. ; If set, the code will dump all descriptors as they are read to the debug board.
  15. USB_DUMP_DESCRIPTORS = 1
  16. ; If set, the code will dump any unclaimed input to the debug board.
  17. HID_DUMP_UNCLAIMED = 1
  18.  
  19. ; USB constants
  20. DEVICE_DESCR_TYPE           = 1
  21. CONFIG_DESCR_TYPE           = 2
  22. STRING_DESCR_TYPE           = 3
  23. INTERFACE_DESCR_TYPE        = 4
  24. ENDPOINT_DESCR_TYPE         = 5
  25. DEVICE_QUALIFIER_DESCR_TYPE = 6
  26.  
  27. CONTROL_PIPE     = 0
  28. ISOCHRONOUS_PIPE = 1
  29. BULK_PIPE        = 2
  30. INTERRUPT_PIPE   = 3
  31.  
  32. ; USB HID constants
  33. HID_DESCR_TYPE      = 21h
  34. REPORT_DESCR_TYPE   = 22h
  35. PHYSICAL_DESCR_TYPE = 23h
  36.  
  37. ; USB structures
  38. struct config_descr
  39. bLength                 db      ?
  40. bDescriptorType         db      ?
  41. wTotalLength            dw      ?
  42. bNumInterfaces          db      ?
  43. bConfigurationValue     db      ?
  44. iConfiguration          db      ?
  45. bmAttributes            db      ?
  46. bMaxPower               db      ?
  47. ends
  48.  
  49. struct interface_descr
  50. bLength                 db      ?
  51. bDescriptorType         db      ?
  52. bInterfaceNumber        db      ?
  53. bAlternateSetting       db      ?
  54. bNumEndpoints           db      ?
  55. bInterfaceClass         db      ?
  56. bInterfaceSubClass      db      ?
  57. bInterfaceProtocol      db      ?
  58. iInterface              db      ?
  59. ends
  60.  
  61. struct endpoint_descr
  62. bLength                 db      ?
  63. bDescriptorType         db      ?
  64. bEndpointAddress        db      ?
  65. bmAttributes            db      ?
  66. wMaxPacketSize          dw      ?
  67. bInterval               db      ?
  68. ends
  69.  
  70. ; USB HID structures
  71. struct hid_descr
  72. bLength                 db      ?
  73. bDescriptorType         db      ?
  74. bcdHID                  dw      ?
  75. bCountryCode            db      ?
  76. bNumDescriptors         db      ?
  77. base_sizeof     rb      0
  78. ; now two fields are repeated .bNumDescriptors times:
  79. subDescriptorType       db      ?
  80. subDescriptorLength     dw      ?
  81. ends
  82.  
  83. ; Include macro for parsing report descriptors/data.
  84. macro workers_globals
  85. {}
  86. include 'report.inc'
  87.  
  88. ; Driver data for all devices
  89. struct usb_device_data
  90. hid                     hid_data        ; data of HID layer
  91. epdescr                 dd      ?       ; endpoint descriptor
  92. hiddescr                dd      ?       ; HID descriptor
  93. interface_number        dd      ?       ; copy of interface_descr.bInterfaceNumber
  94. configpipe              dd      ?       ; config pipe handle
  95. intpipe                 dd      ?       ; interrupt pipe handle
  96. input_transfer_size     dd      ?       ; input transfer size
  97. input_buffer            dd      ?       ; buffer for input transfers
  98. control                 rb      8       ; control packet to device
  99. ends
  100.  
  101. section '.flat' code readable writable executable
  102. include '../../macros.inc'
  103. include '../../proc32.inc'
  104. include '../../peimport.inc'
  105. include '../../fdo.inc'
  106. ; The start procedure.
  107. proc START
  108. virtual at esp
  109.         dd      ?       ; return address
  110. .reason dd      ?
  111. .cmdline dd     ?
  112. end virtual
  113. ; 1. Test whether the procedure is called with the argument DRV_ENTRY.
  114. ; If not, return 0.
  115.         xor     eax, eax        ; initialize return value
  116.         cmp     [.reason], 1    ; compare the argument
  117.         jnz     .nothing
  118. ; 2. Register self as a USB driver.
  119. ; The name is my_driver = 'usbhid'; IOCTL interface is not supported;
  120. ; usb_functions is an offset of a structure with callback functions.
  121.         invoke  RegUSBDriver, my_driver, eax, usb_functions
  122. ; 3. Return the returned value of RegUSBDriver.
  123. .nothing:
  124.         ret
  125. endp
  126.  
  127. ; This procedure is called when new HID device is detected.
  128. ; It initializes the device.
  129. proc AddDevice
  130.         push    ebx esi edi     ; save used registers to be stdcall
  131. virtual at esp
  132.                 rd      3       ; saved registers
  133.                 dd      ?       ; return address
  134. .config_pipe    dd      ?
  135. .config_descr   dd      ?
  136. .interface      dd      ?
  137. end virtual
  138.         DEBUGF 1,'K : USB HID device detected\n'
  139. ; 1. Allocate memory for device data.
  140.         movi    eax, sizeof.usb_device_data
  141.         invoke  Kmalloc
  142.         test    eax, eax
  143.         jnz     @f
  144.         mov     esi, nomemory_msg
  145.         invoke  SysMsgBoardStr
  146.         jmp     .return0
  147. @@:
  148. ; zero-initialize it
  149.         mov     edi, eax
  150.         xchg    eax, ebx
  151.         xor     eax, eax
  152.         movi    ecx, sizeof.usb_device_data / 4
  153.         rep stosd
  154.         mov     edx, [.interface]
  155. ; HID devices use one IN interrupt endpoint for polling the device
  156. ; and an optional OUT interrupt endpoint. We do not use the later,
  157. ; but must locate the first. Look for the IN interrupt endpoint.
  158. ; Also, look for the HID descriptor; according to HID spec, it must be
  159. ; located before endpoint descriptors.
  160. ; 2. Get the upper bound of all descriptors' data.
  161.         mov     eax, [.config_descr]
  162.         movzx   ecx, [eax+config_descr.wTotalLength]
  163.         add     eax, ecx
  164. ; 3. Loop over all descriptors until
  165. ; either end-of-data reached - this is fail
  166. ; or interface descriptor found - this is fail, all further data
  167. ;    correspond to that interface
  168. ; or endpoint descriptor for IN endpoint is found
  169. ; (HID descriptor must be located before the endpoint descriptor).
  170. ; 3a. Loop start: edx points to the interface descriptor.
  171. .lookep:
  172. ; 3b. Get next descriptor.
  173.         movzx   ecx, byte [edx] ; the first byte of all descriptors is length
  174.         test    ecx, ecx
  175.         jz      .cfgerror
  176.         add     edx, ecx
  177. ; 3c. Check that at least two bytes are readable. The opposite is an error.
  178.         inc     edx
  179.         cmp     edx, eax
  180.         jae     .cfgerror
  181.         dec     edx
  182. ; 3d. Check that this descriptor is not interface descriptor. The opposite is
  183. ; an error.
  184.         cmp     [edx+endpoint_descr.bDescriptorType], INTERFACE_DESCR_TYPE
  185.         jz      .cfgerror
  186. ; 3e. For HID descriptor, proceed to 4.
  187. ; For endpoint descriptor, go to 5.
  188. ; For other descriptors, continue the loop.
  189. ; Note: bDescriptorType is in the same place in all descriptors.
  190.         cmp     [edx+endpoint_descr.bDescriptorType], ENDPOINT_DESCR_TYPE
  191.         jz      .foundep
  192.         cmp     [edx+endpoint_descr.bDescriptorType], HID_DESCR_TYPE
  193.         jnz     .lookep
  194. ; 4a. Check that the descriptor contains all required data and all data are
  195. ; readable. The opposite is an error.
  196.         movzx   ecx, [edx+hid_descr.bLength]
  197.         cmp     ecx, hid_descr.base_sizeof + 3
  198.         jb      .cfgerror
  199.         add     ecx, edx
  200.         cmp     ecx, eax
  201.         ja      .cfgerror
  202. ; 4b. Store the pointer in usb_device_data structure for further references.
  203.         mov     [ebx+usb_device_data.hiddescr], edx
  204. ; 4c. Continue the loop.
  205.         jmp     .lookep
  206. .foundep:
  207. ; 5a. Check that the descriptor contains all required data and all data are
  208. ; readable. The opposite is an error.
  209.         cmp     byte [edx+endpoint_descr.bLength], sizeof.endpoint_descr
  210.         jb      .cfgerror
  211.         lea     ecx, [edx+sizeof.endpoint_descr]
  212.         cmp     ecx, eax
  213.         jbe     @f
  214. ; 6. An error occured during processing endpoint descriptor.
  215. .cfgerror:
  216. ; 6a. Print a message.
  217.         mov     esi, invalid_config_descr_msg
  218.         invoke  SysMsgBoardStr
  219. ; 6b. Free memory allocated for device data.
  220. .free:
  221.         xchg    eax, ebx
  222.         invoke  Kfree
  223. .return0:
  224. ; 6c. Return an error.
  225.         xor     eax, eax
  226. .nothing:
  227.         pop     edi esi ebx     ; restore used registers to be stdcall
  228.         ret     12
  229. @@:
  230. ; 5b. If this is not IN interrupt endpoint, ignore it and continue the loop.
  231.         test    [edx+endpoint_descr.bEndpointAddress], 80h
  232.         jz      .lookep
  233.         mov     cl, [edx+endpoint_descr.bmAttributes]
  234.         and     cl, 3
  235.         cmp     cl, INTERRUPT_PIPE
  236.         jnz     .lookep
  237. ; 5c. Store the pointer in usb_device_data structure for futher references.
  238.         mov     [ebx+usb_device_data.epdescr], edx
  239. ; 5d. Check that HID descriptor was found. If not, go to 6.
  240.         cmp     [ebx+usb_device_data.hiddescr], 0
  241.         jz      .cfgerror
  242. .descriptors_found:
  243. ; 6. Configuration descriptor seems to be ok.
  244. ; Send SET_IDLE command disabling auto-repeat feature (it is quite useless)
  245. ; and continue configuring in SET_IDLE callback.
  246.         lea     edx, [ebx+usb_device_data.control]
  247.         mov     eax, [.interface]
  248.         mov     dword [edx], 21h + \    ; Class-specific request to Interface
  249.                 (0Ah shl 8) + \         ; SET_IDLE
  250.                 (0 shl 16) + \          ; apply to all input reports
  251.                 (0 shl 24)              ; disable auto-repeat
  252.         movzx   eax, [eax+interface_descr.bInterfaceNumber]
  253.         mov     [ebx+usb_device_data.interface_number], eax
  254.         mov     [edx+4], eax            ; set interface number, zero length
  255.         mov     eax, [.config_pipe]
  256.         mov     [ebx+usb_device_data.configpipe], eax
  257.         xor     ecx, ecx
  258.         invoke  USBControlTransferAsync, eax, edx, ecx, ecx, idle_set, ebx, ecx
  259. ; 7. Return pointer to usb_device_data.
  260.         xchg    eax, ebx
  261.         jmp     .nothing
  262. endp
  263.  
  264. ; This procedure is called by USB stack when SET_IDLE request initiated by
  265. ; AddDevice is completed, either successfully or unsuccessfully.
  266. proc idle_set
  267.         push    ebx esi         ; save used registers to be stdcall
  268. virtual at esp
  269.                 rd      2       ; saved registers
  270.                 dd      ?       ; return address
  271. .pipe           dd      ?
  272. .status         dd      ?
  273. .buffer         dd      ?
  274. .length         dd      ?
  275. .calldata       dd      ?
  276. end virtual
  277. ; Ignore status. Support for SET_IDLE is optional, so the device is free to
  278. ; STALL the request; config pipe should remain functional without explicit cleanup.
  279.         mov     ebx, [.calldata]
  280. ; 1. HID descriptor contains length of Report descriptor. Parse it.
  281.         mov     esi, [ebx+usb_device_data.hiddescr]
  282.         movzx   ecx, [esi+hid_descr.bNumDescriptors]
  283.         lea     eax, [hid_descr.base_sizeof+ecx*3]
  284.         cmp     eax, 100h
  285.         jae     .cfgerror
  286.         cmp     al, [esi+hid_descr.bLength]
  287.         jb      .cfgerror
  288. .look_report:
  289.         dec     ecx
  290.         js      .cfgerror
  291.         cmp     [esi+hid_descr.subDescriptorType], REPORT_DESCR_TYPE
  292.         jz      .found_report
  293.         add     esi, 3
  294.         jmp     .look_report
  295. .cfgerror:
  296.         mov     esi, invalid_config_descr_msg
  297. .abort_with_msg:
  298.         invoke  SysMsgBoardStr
  299.         jmp     .nothing
  300. .found_report:
  301. ; 2. Send request for the Report descriptor.
  302. ; 2a. Allocate memory.
  303.         movzx   eax, [esi+hid_descr.subDescriptorLength]
  304.         test    eax, eax
  305.         jz      .cfgerror
  306.         push    eax
  307.         invoke  Kmalloc
  308.         pop     ecx
  309. ; If failed, say a message and stop initialization.
  310.         mov     esi, nomemory_msg
  311.         test    eax, eax
  312.         jz      .abort_with_msg
  313. ; 2b. Submit the request.
  314.         xchg    eax, esi
  315.         lea     edx, [ebx+usb_device_data.control]
  316.         mov     eax, [ebx+usb_device_data.interface_number]
  317.         mov     dword [edx], 81h + \    ; Standard request to Interface
  318.                 (6 shl 8) + \           ; GET_DESCRIPTOR
  319.                 (0 shl 16) + \          ; descriptor index: there is only one report descriptor
  320.                 (REPORT_DESCR_TYPE shl 24); descriptor type
  321.         mov     [edx+4], ax             ; Interface number
  322.         mov     [edx+6], cx             ; descriptor length
  323.         invoke  USBControlTransferAsync, [ebx+usb_device_data.configpipe], \
  324.                 edx, esi, ecx, got_report, ebx, 0
  325. ; 2c. If failed, free the buffer and stop initialization.
  326.         test    eax, eax
  327.         jnz     .nothing
  328.         xchg    eax, esi
  329.         invoke  Kfree
  330. .nothing:
  331.         pop     esi ebx         ; restore used registers to be stdcall
  332.         ret     20
  333. endp
  334.  
  335. ; This procedure is called by USB stack when the report descriptor queried
  336. ; by idle_set is completed, either successfully or unsuccessfully.
  337. proc got_report stdcall uses ebx esi edi, pipe, status, buffer, length, calldata
  338. locals
  339. parse_descr_locals
  340. if ~HID_DUMP_UNCLAIMED
  341. has_driver      db      ?
  342.                 rb      3
  343. end if
  344. endl
  345. ; 1. Check the status; if the request has failed, say something to the debug board
  346. ; and stop initialization.
  347.         cmp     [status], 0
  348.         jnz     .generic_fail
  349. ; 2. Subtract size of setup packet from the total length;
  350. ; the rest is length of the descriptor, and it must be nonzero.
  351.         sub     [length], 8
  352.         ja      .has_something
  353. .generic_fail:
  354.         push    esi
  355.         mov     esi, reportfail
  356.         invoke  SysMsgBoardStr
  357.         pop     esi
  358.         jmp     .exit
  359. .has_something:
  360. ; 3. Process descriptor.
  361. ; 3a. Dump it to the debug board, if enabled in compile-time setting.
  362. if USB_DUMP_DESCRIPTORS
  363.         mov     eax, [buffer]
  364.         mov     ecx, [length]
  365.         DEBUGF 1,'K : report descriptor:'
  366. @@:
  367.         DEBUGF 1,' %x',[eax]:2
  368.         inc     eax
  369.         dec     ecx
  370.         jnz     @b
  371.         DEBUGF 1,'\n'
  372. end if
  373. ; 3b. Call the HID layer.
  374.         parse_descr
  375.         cmp     [report_ok], 0
  376.         jz      got_report.exit
  377.         mov     ebx, [calldata]
  378.         postprocess_descr
  379. ; 4. Stop initialization if no driver is assigned.
  380. if ~HID_DUMP_UNCLAIMED
  381.         cmp     [has_driver], 0
  382.         jz      got_report.exit
  383. end if
  384. ; 5. Open interrupt IN pipe. If failed, stop initialization.
  385.         mov     edx, [ebx+usb_device_data.epdescr]
  386.         movzx   ecx, [edx+endpoint_descr.bEndpointAddress]
  387.         movzx   eax, [edx+endpoint_descr.bInterval]
  388.         movzx   edx, [edx+endpoint_descr.wMaxPacketSize]
  389.         invoke  USBOpenPipe, [ebx+usb_device_data.configpipe], ecx, edx, INTERRUPT_PIPE, eax
  390.         test    eax, eax
  391.         jz      got_report.exit
  392.         mov     [ebx+usb_device_data.intpipe], eax
  393. ; 6. Initialize buffer for input packet.
  394. ; 6a. Find the length of input packet.
  395. ; This is the maximal length of all input reports.
  396.         mov     edx, [ebx+usb_device_data.hid.input.first_report]
  397.         xor     eax, eax
  398. .find_input_size:
  399.         test    edx, edx
  400.         jz      .found_input_size
  401.         cmp     eax, [edx+report.size]
  402.         jae     @f
  403.         mov     eax, [edx+report.size]
  404. @@:
  405.         mov     edx, [edx+report.next]
  406.         jmp     .find_input_size
  407. .found_input_size:
  408. ; report.size is in bits, transform it to bytes
  409.         add     eax, 7
  410.         shr     eax, 3
  411. ; if reports are numbered, the first byte is report ID, include it
  412.         cmp     [ebx+usb_device_data.hid.input.numbered], 0
  413.         jz      @f
  414.         inc     eax
  415. @@:
  416.         mov     [ebx+usb_device_data.input_transfer_size], eax
  417. ; 6b. Allocate memory for input packet: dword-align and add additional dword
  418. ; for extract_field_value.
  419.         add     eax, 4+3
  420.         and     eax, not 3
  421.         invoke  Kmalloc
  422.         test    eax, eax
  423.         jnz     @f
  424.         mov     esi, nomemory_msg
  425.         invoke  SysMsgBoardStr
  426.         jmp     got_report.exit
  427. @@:
  428.         mov     [ebx+usb_device_data.input_buffer], eax
  429. ; 7. Submit a request for input packet and wait for input.
  430.         call    ask_for_input
  431. got_report.exit:
  432.         mov     eax, [buffer]
  433.         invoke  Kfree
  434.         ret
  435. endp
  436.  
  437. ; Helper procedure for got_report and got_input.
  438. ; Submits a request for the next input packet.
  439. proc ask_for_input
  440. ; just call USBNormalTransferAsync with correct parameters,
  441. ; allow short packets
  442.         invoke  USBNormalTransferAsync, \
  443.                 [ebx+usb_device_data.intpipe], \
  444.                 [ebx+usb_device_data.input_buffer], \
  445.                 [ebx+usb_device_data.input_transfer_size], \
  446.                 got_input, ebx, \
  447.                 1
  448.         ret
  449. endp
  450.  
  451. ; This procedure is called by USB stack when a HID device responds with input
  452. ; data packet.
  453. proc got_input stdcall uses ebx esi edi, pipe, status, buffer, length, calldata
  454. locals
  455. parse_input_locals
  456. endl
  457. ; 1. Validate parameters: fail on error, ignore zero-length transfers.
  458.         mov     ebx, [calldata]
  459.         cmp     [status], 0
  460.         jnz     .fail
  461.         cmp     [length], 0
  462.         jz      .done
  463. ; 2. Get pointer to report in esi.
  464. ; 2a. If there are no report IDs, use hid.input.data.
  465.         mov     eax, [buffer]
  466.         mov     esi, [ebx+usb_device_data.hid.input.data]
  467.         cmp     [ebx+usb_device_data.hid.input.numbered], 0
  468.         jz      .report_found
  469. ; 2b. Otherwise, the first byte of report is report ID;
  470. ; locate the report by its ID, advance buffer+length to one byte.
  471.         movzx   eax, byte [eax]
  472.         mov     esi, [esi+eax*4]
  473.         inc     [buffer]
  474.         dec     [length]
  475. .report_found:
  476. ; 3. Validate: ignore transfers with unregistered report IDs
  477. ; and transfers which are too short for the corresponding report.
  478.         test    esi, esi
  479.         jz      .done
  480.         mov     eax, [esi+report.size]
  481.         add     eax, 7
  482.         shr     eax, 3
  483.         cmp     eax, [length]
  484.         ja      .done
  485. ; 4. Pass everything to HID layer.
  486.         parse_input
  487. .done:
  488. ; 5. Query the next input.
  489.         mov     ebx, [calldata]
  490.         call    ask_for_input
  491. .nothing:
  492.         ret
  493. .fail:
  494.         mov     esi, transfer_error_msg
  495.         invoke  SysMsgBoardStr
  496.         jmp     .nothing
  497. endp
  498.  
  499. ; This function is called by the USB subsystem when a device is disconnected.
  500. proc DeviceDisconnected
  501.         push    ebx esi edi     ; save used registers to be stdcall
  502. virtual at esp
  503.                 rd      3       ; saved registers
  504.                 dd      ?       ; return address
  505. .device_data    dd      ?
  506. end virtual
  507. ; 1. Say a message.
  508.         mov     ebx, [.device_data]
  509.         mov     esi, disconnectmsg
  510.         invoke  SysMsgBoardStr
  511. ; 2. Ask HID layer to release all HID-related resources.
  512.         hid_cleanup
  513. ; 3. Free the device data.
  514.         xchg    eax, ebx
  515.         invoke  Kfree
  516. ; 4. Return.
  517. .nothing:
  518.         pop     edi esi ebx     ; restore used registers to be stdcall
  519.         ret     4       ; purge one dword argument to be stdcall
  520. endp
  521.  
  522. include 'sort.inc'
  523. include 'unclaimed.inc'
  524. include 'mouse.inc'
  525. include 'keyboard.inc'
  526. include 'multimedia.inc'
  527.  
  528. ; strings
  529. my_driver       db      'usbhid',0
  530. nomemory_msg    db      'K : no memory',13,10,0
  531. invalid_config_descr_msg db 'K : invalid config descriptor',13,10,0
  532. reportfail      db      'K : failed to read report descriptor',13,10,0
  533. transfer_error_msg db   'K : USB transfer error, disabling HID device',13,10,0
  534. disconnectmsg   db      'K : USB HID device disconnected',13,10,0
  535. invalid_report_msg db   'K : report descriptor is invalid',13,10,0
  536. delimiter_note  db      'K : note: alternate usage ignored',13,10,0
  537.  
  538. align 4
  539. ; Structure with callback functions.
  540. usb_functions:
  541.         dd      12
  542.         dd      AddDevice
  543.         dd      DeviceDisconnected
  544.  
  545. ; for DEBUGF macro
  546. include_debug_strings
  547.  
  548. ; Workers data
  549. workers_globals
  550.  
  551. align 4
  552. data fixups
  553. end data
  554.