Subversion Repositories Kolibri OS

Rev

Rev 4598 | Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. ; HID keyboard driver, part of USBHID driver.
  2.  
  3. ; Global constants.
  4. ; They are assembled in a macro to separate code and data;
  5. ; the code is located at the point of "include 'keyboard.inc'",
  6. ; the data are collected when workers_globals is instantiated.
  7. macro workers_globals
  8. {
  9. ; include global constants from previous workers
  10.         workers_globals
  11. align 4
  12. ; Callbacks for HID layer.
  13. keyboard_driver:
  14.         dd      keyboard_driver_add_device
  15.         dd      keyboard_driver_disconnect
  16.         dd      keyboard_driver_begin_packet
  17.         dd      keyboard_driver_array_overflow?
  18.         dd      keyboard_driver_input_field
  19.         dd      keyboard_driver_end_packet
  20. ; Callbacks for keyboard layer.
  21. kbd_functions:
  22.         dd      12
  23.         dd      CloseKeyboard
  24.         dd      SetKeyboardLights
  25. ; Kernel keyboard layer takes input in form of PS/2 scancodes.
  26. ; data for keyboard: correspondence between HID usage keys and PS/2 scancodes.
  27. EX = 80h        ; if set, precede the scancode with special scancode 0xE0
  28. label control_keys byte
  29. ; Usages 700E0h ... 700E7h: LCtrl, LShift, LAlt, LWin, RCtrl, RShift, RAlt, RWin
  30.         db      1Dh, 2Ah, 38h, 5Bh+EX, 1Dh+EX, 36h, 38h+EX, 5Ch+EX
  31. ; Usages 70004h ... 70004h + normal_keys_number - 1
  32. label normal_keys byte
  33.         db      1Eh, 30h, 2Eh, 20h, 12h, 21h, 22h, 23h, 17h, 24h, 25h, 26h, 32h, 31h, 18h, 19h
  34.         db      10h, 13h, 1Fh, 14h, 16h, 2Fh, 11h, 2Dh, 15h, 2Ch, 02h, 03h, 04h, 05h, 06h, 07h
  35.         db      08h, 09h, 0Ah, 0Bh, 1Ch, 01h, 0Eh, 0Fh, 39h, 0Ch, 0Dh, 1Ah, 1Bh, 2Bh, 2Bh, 27h
  36.         db      28h, 29h, 33h, 34h, 35h, 3Ah, 3Bh, 3Ch, 3Dh, 3Eh, 3Fh, 40h, 41h, 42h, 43h, 44h
  37.         db      57h, 58h,37h+EX,46h,0,52h+EX,47h+EX,49h+EX,53h+EX,4Fh+EX,51h+EX,4Dh+EX,4Bh+EX,50h+EX,48h+EX,45h
  38.         db      35h+EX,37h,4Ah,4Eh,1Ch+EX,4Fh,50h, 51h, 4Bh, 4Ch, 4Dh, 47h, 48h, 49h, 52h, 53h
  39.         db      56h,5Dh+EX,5Eh+EX
  40. normal_keys_number = $ - normal_keys
  41. }
  42.  
  43. ; Data that are specific for one keyboard device.
  44. struct keyboard_device_data
  45. handle          dd      ?       ; keyboard handle from RegKeyboard
  46. timer           dd      ?       ; auto-repeat timer handle
  47. repeatkey       db      ?       ; auto-repeat key code
  48.                 rb      3       ; padding
  49. usbdev          dd      ?       ; pointer to device_data of USB and HID layers
  50. modifiers       dd      ?       ; state of LCtrl ... RWin
  51. led_report      dd      ?       ; output report for LEDs state
  52. numlock_bit     dd      ?       ; position of NumLock bit in LED output report
  53. capslock_bit    dd      ?
  54. scrolllock_bit  dd      ?       ; guess what
  55. ends
  56.  
  57. ; This procedure is called when HID layer detects a new keyboard.
  58. ; in: ebx -> usb_device_data, edi -> collection
  59. ; out: eax = device-specific data or NULL on error
  60. proc keyboard_driver_add_device
  61. ; 1. Allocate memory for keyboard_device_data. If failed, return NULL.
  62.         movi    eax, sizeof.keyboard_device_data
  63.         call    Kmalloc
  64.         test    eax, eax
  65.         jz      .nothing
  66. ; 2. Initialize keyboard_device_data: store pointer to USB layer data,
  67. ; zero some fields, initialize bit positions to -1.
  68.         mov     [eax+keyboard_device_data.usbdev], ebx
  69.         xor     ecx, ecx
  70.         mov     [eax+keyboard_device_data.timer], ecx
  71.         mov     [eax+keyboard_device_data.repeatkey], cl
  72.         mov     [eax+keyboard_device_data.modifiers], ecx
  73.         mov     [eax+keyboard_device_data.led_report], ecx
  74.         dec     ecx
  75.         mov     [eax+keyboard_device_data.numlock_bit], ecx
  76.         mov     [eax+keyboard_device_data.capslock_bit], ecx
  77.         mov     [eax+keyboard_device_data.scrolllock_bit], ecx
  78. ; 3. Look for LED report and bits corresponding to indicators.
  79. ; For now, assume that all LEDs are set by the same report.
  80. ; 3a. Save registers.
  81.         push    ebx esi
  82. ; 3b. Prepare for loop over output reports: get the first output report.
  83. ; If there are no output records, skip step 3;
  84. ; default values of led_report and *_bit were set in step 2.
  85.         mov     edx, [edi+collection.output.first_report]
  86.         test    edx, edx
  87.         jz      .led_report_set
  88. .scan_led_report:
  89. ; Process one output report.
  90. ; 3c. Prepare for loop over field groups in the current report:
  91. ; get the first field group.
  92.         mov     ecx, [edx+report.first_field]
  93. .scan_led_field:
  94. ; Process one field group.
  95. ; 3d. If there are no more field groups, exit the loop over field groups.
  96.         test    ecx, ecx
  97.         jz      .next_led_report
  98. ; For now, assume that all LEDs are plain variable fields, not arrays.
  99. ; 3e. Ignore array field groups.
  100.         test    byte [ecx+report_field_group.flags], HID_FIELD_VARIABLE
  101.         jz      .next_led_field
  102. ; 3f. Loop over all fields in the current group.
  103.         push    [ecx+report_field_group.count]
  104. ; esi = pointer to usage of the current field
  105.         lea     esi, [ecx+report_field_group.common_sizeof]
  106. ; ebx = bit position of the current field
  107.         mov     ebx, [ecx+report_field_group.offset]
  108. ; if report is numbered, add extra byte in the start of report
  109.         cmp     [edx+report.id], 0
  110.         jz      .scan_led_usage
  111.         add     ebx, 8
  112. .scan_led_usage:
  113. ; for USAGE_LED_*LOCK, store the current bit position in the corresponding field
  114. ; and store the current report as the LED report
  115.         cmp     dword [esi], USAGE_LED_NUMLOCK
  116.         jz      .numlock
  117.         cmp     dword [esi], USAGE_LED_CAPSLOCK
  118.         jz      .capslock
  119.         cmp     dword [esi], USAGE_LED_SCROLLLOCK
  120.         jnz     .next_field
  121. .scrolllock:
  122.         mov     [eax+keyboard_device_data.scrolllock_bit], ebx
  123.         jmp     @f
  124. .capslock:
  125.         mov     [eax+keyboard_device_data.capslock_bit], ebx
  126.         jmp     @f
  127. .numlock:
  128.         mov     [eax+keyboard_device_data.numlock_bit], ebx
  129. @@:
  130.         mov     [eax+keyboard_device_data.led_report], edx
  131. .next_field:
  132.         add     esi, 4
  133.         add     ebx, [ecx+report_field_group.size]
  134.         dec     dword [esp]
  135.         jnz     .scan_led_usage
  136.         pop     ebx
  137. .next_led_field:
  138. ; 3g. Continue loop over field groups: get next field group.
  139.         mov     ecx, [ecx+report_field_group.next]
  140.         jmp     .scan_led_field
  141. .next_led_report:
  142. ; 3h. If the LED report has been set, break from the loop over reports.
  143. ; Otherwise, get the next report and continue if the current report is not
  144. ; the last for this collection.
  145.         cmp     [eax+keyboard_device_data.led_report], 0
  146.         jnz     .led_report_set
  147.         cmp     edx, [edi+collection.output.last_report]
  148.         mov     edx, [edx+report.next]
  149.         jnz     .scan_led_report
  150. .led_report_set:
  151. ; 3i. Restore registers.
  152.         pop     esi ebx
  153. ; 4. Register keyboard in the kernel.
  154. ; store pointer to keyboard_device_data in the stack
  155.         push    eax
  156. ; call kernel API
  157.         stdcall RegKeyboard, kbd_functions, eax
  158. ; restore pointer to keyboard_device_data from the stack,
  159. ; putting keyboard handle from API to the stack
  160.         xchg    eax, [esp]
  161. ; put keyboard handle from API from the stack to keyboard_device_data field
  162.         pop     [eax+keyboard_device_data.handle]
  163. ; If failed, free keyboard_device_data and return NULL.
  164.         cmp     [eax+keyboard_device_data.handle], 0
  165.         jz      .fail_free
  166. ; 5. Return pointer to keyboard_device_data.
  167. .nothing:
  168.         ret
  169. .fail_free:
  170.         call    Kfree
  171.         xor     eax, eax
  172.         ret
  173. endp
  174.  
  175. ; This procedure is called when HID layer detects disconnect of a previously
  176. ; connected keyboard.
  177. ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
  178. proc keyboard_driver_disconnect
  179. ; 1. If an autorepeat timer is active, stop it.
  180.         cmp     [edi+keyboard_device_data.timer], 0
  181.         jz      @f
  182.         stdcall CancelTimerHS, [edi+keyboard_device_data.timer]
  183. @@:
  184. ; 2. Unregister keyboard in the kernel.
  185.         stdcall DelKeyboard, [edi+keyboard_device_data.handle]
  186. ; We should free data in CloseKeyboard, not here.
  187.         ret
  188. endp
  189.  
  190. ; This procedure is called when HID layer starts processing a new input packet
  191. ; from a keyboard.
  192. ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
  193. proc keyboard_driver_begin_packet
  194. ; Nothing to do.
  195.         ret
  196. endp
  197.  
  198. ; This procedure is called when HID layer processes every non-empty array field group.
  199. ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
  200. ; in: ecx = fields count (always nonzero), edx = pointer to fields values
  201. ; in: esi -> report_field_group
  202. ; out: CF set => group is ok, CF cleared => group should be ignored
  203. proc keyboard_driver_array_overflow?
  204. ; The keyboard signals array overflow by filling the entire array with
  205. ; USAGE_KBD_ROLLOVER codes.
  206.         mov     eax, [edx]      ; eax = first field in the array
  207.         sub     eax, USAGE_KBD_ROLLOVER ; eax = 0 if overflow, nonzero otherwise
  208.         neg     eax     ; CF cleared if eax was zero, CF set if eax was nonzero
  209.         ret
  210. endp
  211.  
  212. ; This procedure is called from HID layer for every field.
  213. ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
  214. ; in: ecx = field usage, edx = value, esi -> report_field_group
  215. proc keyboard_driver_input_field
  216. if HID_DUMP_UNCLAIMED
  217. .unclaimed = default_driver_input_field
  218. end if
  219. ; 1. Process normal keys:
  220. ; from USAGE_KBD_FIRST_KEY to USAGE_KBD_FIRST_KEY + normal_keys_number - 1,
  221. ; excluding zeroes in [normal_keys].
  222. ; 1a. Test whether usage is in the range.
  223.         lea     eax, [ecx-USAGE_KBD_FIRST_KEY]
  224.         cmp     eax, normal_keys_number
  225.         jae     .not_normal_key
  226. ; 1b. If the corresponding entry in [normal_keys] is zero,
  227. ; pass this field to the default handler - if HID_DUMP_UNCLAIMED is enabled,
  228. ; default handler is default_driver_input_field, otherwise just ignore the field.
  229.         cmp     [normal_keys + eax], 0
  230.         jz      .unclaimed
  231. ; 1c. Get the scancode.
  232.         movzx   ecx, [normal_keys + eax]
  233. ; 1d. Further actions are slightly different for key press and key release.
  234. ; Decide what to do.
  235.         test    edx, edx
  236.         jz      .normal_key_released
  237. .normal_key_pressed:
  238. ; The key is pressed.
  239. ; 1e. Store the last pressed key for autorepeat.
  240.         mov     [edi+keyboard_device_data.repeatkey], cl
  241. ; 1f. Copy bit 7 to CF and send scancode with bit 7 cleared.
  242.         btr     ecx, 7
  243.         call    .send_key
  244. ; 1g. Stop the previous autorepeat timer, if any.
  245.         mov     eax, [edi+keyboard_device_data.timer]
  246.         test    eax, eax
  247.         jz      @f
  248.         stdcall CancelTimerHS, eax
  249. @@:
  250. ; 1h. Start the new autorepeat timer with 250 ms initial delay
  251. ; and 50 ms subsequent delays.
  252.         stdcall TimerHS, 25, 5, autorepeat_timer, edi
  253.         mov     [edi+keyboard_device_data.timer], eax
  254. if ~HID_DUMP_UNCLAIMED
  255. .unclaimed:
  256. end if
  257.         ret
  258. .normal_key_released:
  259. ; The key is released.
  260. ; 1i. Stop the autorepeat timer if it is autorepeating the released key.
  261.         cmp     [edi+keyboard_device_data.repeatkey], cl
  262.         jnz     .no_stop_timer
  263.         push    ecx
  264.         mov     [edi+keyboard_device_data.repeatkey], 0
  265.         mov     eax, [edi+keyboard_device_data.timer]
  266.         test    eax, eax
  267.         jz      @f
  268.         stdcall CancelTimerHS, eax
  269.         mov     [edi+keyboard_device_data.timer], 0
  270. @@:
  271.         pop     ecx
  272. .no_stop_timer:
  273. ; 1j. Copy bit 7 to CF and send scancode with bit 7 set.
  274.         bts     ecx, 7
  275.         call    .send_key
  276.         ret
  277. .not_normal_key:
  278. ; 2. USAGE_KBD_NOEVENT is simply a filler for free array fields,
  279. ; ignore it.
  280.         cmp     ecx, USAGE_KBD_NOEVENT
  281.         jz      .nothing
  282. ; 3. Process modifiers: 8 keys starting at USAGE_KBD_LCTRL.
  283. ; 3a. Test whether usage is in range.
  284. ; If not, we don't know what this field means, so pass it to the default handler.
  285.         lea     eax, [ecx-USAGE_KBD_LCTRL]
  286.         cmp     eax, 8
  287.         jae     .unclaimed
  288. ; 3b. Further actions are slightly different for modifier press
  289. ; and modifier release. Decide what to do.
  290.         test    edx, edx
  291.         jz      .modifier_not_pressed
  292. .modifier_pressed:
  293. ; The modifier is pressed.
  294. ; 3c. Set the corresponding status bit.
  295. ; If it was not set, send the corresponding scancode to the kernel
  296. ; with bit 7 cleared.
  297.         bts     [edi+keyboard_device_data.modifiers], eax
  298.         jc      @f
  299.         movzx   ecx, [control_keys+eax]
  300.         btr     ecx, 7
  301.         call    .send_key
  302. @@:
  303. .nothing:
  304.         ret
  305. .modifier_not_pressed:
  306. ; The modifier is not pressed.
  307. ; 3d. Clear the correspodning status bit.
  308. ; If it was set, send the corresponding scancode to the kernel
  309. ; with bit 7 set.
  310.         btr     [edi+keyboard_device_data.modifiers], eax
  311.         jnc     @f
  312.         movzx   ecx, [control_keys+eax]
  313.         bts     ecx, 7
  314.         call    .send_key
  315. @@:
  316.         ret
  317.  
  318. ; Helper procedure. Sends scancode from cl to the kernel.
  319. ; If CF is set, precede it with special code 0xE0.
  320. .send_key:
  321.         jnc     @f
  322.         push    ecx
  323.         mov     ecx, 0xE0
  324.         call    SetKeyboardData
  325.         pop     ecx
  326. @@:
  327.         call    SetKeyboardData
  328.         ret
  329. endp
  330.  
  331. ; This procedure is called when HID layer ends processing a new input packet
  332. ; from a keyboard.
  333. ; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
  334. proc keyboard_driver_end_packet
  335. ; Nothing to do.
  336.         ret
  337. endp
  338.  
  339. ; Timer callback for SetTimerHS.
  340. proc autorepeat_timer
  341. virtual at esp
  342.                 dd      ?       ; return address
  343. .data           dd      ?
  344. end virtual
  345. ; Just resend the last pressed key.
  346.         mov     eax, [.data]
  347.         movzx   ecx, [eax+keyboard_device_data.repeatkey]
  348. ; Copy bit 7 to CF and send scancode with bit 7 cleared.
  349.         btr     ecx, 7
  350.         call    keyboard_driver_input_field.send_key
  351.         ret     4
  352. endp
  353.  
  354. ; This function is called from the keyboard layer
  355. ; when it is safe to free keyboard data.
  356. proc CloseKeyboard
  357. virtual at esp
  358.                 dd      ?       ; return address
  359. .device_data    dd      ?
  360. end virtual
  361.         mov     eax, [.device_data]
  362.         call    Kfree
  363.         ret     4
  364. endp
  365.  
  366. ; This function is called from the keyboard layer
  367. ; to update LED state on the keyboard.
  368. proc SetKeyboardLights stdcall uses ebx esi edi, device_data, led_state
  369. locals
  370. size    dd      ?
  371. endl
  372. ; 1. Get the pointer to the LED report.
  373. ; If there is no LED report, exit from the function.
  374.         mov     ebx, [device_data]
  375.         mov     esi, [ebx+keyboard_device_data.led_report]
  376.         test    esi, esi
  377.         jz      .nothing
  378. ; 2. Get report size in bytes.
  379. ; report.size is size in bits without possible report ID;
  380. ; if an ID is assigned, the size is one byte greater.
  381.         mov     eax, [esi+report.size]
  382.         add     eax, 7
  383.         shr     eax, 3
  384.         cmp     [esi+report.id], 0
  385.         jz      @f
  386.         inc     eax
  387. @@:
  388.         mov     [size], eax
  389. ; 3. Allocate memory for report + 8 bytes for setup packet.
  390. ; Dword-align size for subsequent rep stosd and bts.
  391. ; If failed, exit from the function.
  392.         add     eax, 8 + 3
  393.         and     eax, not 3
  394.         push    eax
  395.         call    Kmalloc
  396.         pop     ecx
  397.         test    eax, eax
  398.         jz      .nothing
  399. ; 4. Zero-initialize output report.
  400.         push    eax
  401.         mov     edi, eax
  402.         shr     ecx, 2
  403.         xor     eax, eax
  404.         rep stosd
  405.         pop     edi
  406.         add     edi, 8
  407. ; 5. Store report ID, if assigned. If not assigned, that would just write zero
  408. ; over zeroes.
  409.         mov     edx, [esi+report.id]
  410.         mov     [edi], edx
  411. ; 6. Set report bits corresponding to active indicators.
  412.         mov     eax, [led_state]
  413.         test    al, 1           ; PS/2 Scroll Lock
  414.         jz      @f
  415.         mov     ecx, [ebx+keyboard_device_data.scrolllock_bit]
  416.         test    ecx, ecx
  417.         js      @f
  418.         bts     [edi], ecx
  419. @@:
  420.         test    al, 2           ; PS/2 Num Lock
  421.         jz      @f
  422.         mov     ecx, [ebx+keyboard_device_data.numlock_bit]
  423.         test    ecx, ecx
  424.         js      @f
  425.         bts     [edi], ecx
  426. @@:
  427.         test    al, 4           ; PS/2 Caps Lock
  428.         jz      @f
  429.         mov     ecx, [ebx+keyboard_device_data.capslock_bit]
  430.         test    ecx, ecx
  431.         js      @f
  432.         bts     [edi], ecx
  433. @@:
  434. ; 7. Fill setup packet.
  435.         shl     edx, 16         ; move Report ID to byte 2
  436.         or      edx, 21h + \    ; Class-specific request to Interface
  437.                 (9 shl 8) + \   ; SET_REPORT
  438.                 (2 shl 24)      ; Report Type = Output
  439.         lea     eax, [edi-8]
  440.         mov     ebx, [ebx+keyboard_device_data.usbdev]
  441.         mov     dword [eax], edx
  442.         mov     edx, [size]
  443.         shl     edx, 16         ; move Size to last word
  444.         or      edx, [ebx+usb_device_data.interface_number]
  445.         mov     [eax+4], edx
  446. ; 8. Submit output control request.
  447.         stdcall USBControlTransferAsync, [ebx+usb_device_data.configpipe], \
  448.                 eax, edi, [size], after_set_keyboard_lights, ebx, 0
  449. ; If failed, free the buffer now.
  450. ; If succeeded, the callback will free the buffer.
  451.         test    eax, eax
  452.         jnz     .nothing
  453.         lea     eax, [edi-8]
  454.         call    Kfree
  455. .nothing:
  456.         ret
  457. endp
  458.  
  459. ; This procedure is called from the USB subsystem when the request initiated by
  460. ; SetKeyboardLights is completed, either successfully or unsuccessfully.
  461. proc after_set_keyboard_lights
  462. virtual at esp
  463.                 dd      ?       ; return address
  464. .pipe           dd      ?
  465. .status         dd      ?
  466. .buffer         dd      ?
  467. .length         dd      ?
  468. .calldata       dd      ?
  469. end virtual
  470. ; Ignore status, just free the buffer allocated by SetKeyboardLights.
  471.         mov     eax, [.buffer]
  472.         sub     eax, 8
  473.         call    Kfree
  474.         ret     20
  475. endp
  476.