0,0 → 1,696 |
; standard driver stuff |
format MS COFF |
|
DEBUG = 1 |
|
; this is for DEBUGF macro from 'fdo.inc' |
__DEBUG__ = 1 |
__DEBUG_LEVEL__ = 1 |
|
include 'proc32.inc' |
include 'imports.inc' |
include 'fdo.inc' |
|
public START |
public version |
|
; USB constants |
DEVICE_DESCR_TYPE = 1 |
CONFIG_DESCR_TYPE = 2 |
STRING_DESCR_TYPE = 3 |
INTERFACE_DESCR_TYPE = 4 |
ENDPOINT_DESCR_TYPE = 5 |
DEVICE_QUALIFIER_DESCR_TYPE = 6 |
|
CONTROL_PIPE = 0 |
ISOCHRONOUS_PIPE = 1 |
BULK_PIPE = 2 |
INTERRUPT_PIPE = 3 |
|
; USB structures |
virtual at 0 |
config_descr: |
.bLength db ? |
.bDescriptorType db ? |
.wTotalLength dw ? |
.bNumInterfaces db ? |
.bConfigurationValue db ? |
.iConfiguration db ? |
.bmAttributes db ? |
.bMaxPower db ? |
.sizeof: |
end virtual |
|
virtual at 0 |
interface_descr: |
.bLength db ? |
.bDescriptorType db ? |
.bInterfaceNumber db ? |
.bAlternateSetting db ? |
.bNumEndpoints db ? |
.bInterfaceClass db ? |
.bInterfaceSubClass db ? |
.bInterfaceProtocol db ? |
.iInterface db ? |
.sizeof: |
end virtual |
|
virtual at 0 |
endpoint_descr: |
.bLength db ? |
.bDescriptorType db ? |
.bEndpointAddress db ? |
.bmAttributes db ? |
.wMaxPacketSize dw ? |
.bInterval db ? |
.sizeof: |
end virtual |
|
; Driver data for all devices |
virtual at 0 |
device_data: |
.type dd ? ; 1 = keyboard, 2 = mouse |
.intpipe dd ? ; interrupt pipe handle |
.packetsize dd ? |
.packet rb 8 ; packet with data from device |
.control rb 8 ; control packet to device |
.sizeof: |
end virtual |
|
; Driver data for mouse |
virtual at device_data.sizeof |
mouse_data: |
; no additional data |
.sizeof: |
end virtual |
|
; Driver data for keyboard |
virtual at device_data.sizeof |
keyboard_data: |
.handle dd ? ; keyboard handle from RegKeyboard |
.configpipe dd ? ; config pipe handle |
.prevpacket rb 8 ; previous packet with data from device |
.timer dd ? ; auto-repeat timer handle |
.repeatkey db ? ; auto-repeat key code |
.ledstate db ? ; state of LEDs |
align 4 |
.sizeof: |
end virtual |
|
section '.flat' code readable align 16 |
; The start procedure. |
START: |
; 1. Test whether the procedure is called with the argument DRV_ENTRY. |
; If not, return 0. |
xor eax, eax ; initialize return value |
cmp dword [esp+4], 1 ; compare the argument |
jnz .nothing |
; 2. Register self as a USB driver. |
; The name is my_driver = 'usbhid'; IOCTL interface is not supported; |
; usb_functions is an offset of a structure with callback functions. |
stdcall RegUSBDriver, my_driver, eax, usb_functions |
; 3. Return the returned value of RegUSBDriver. |
.nothing: |
ret 4 |
|
; This procedure is called when new HID device is detected. |
; It initializes the device. |
AddDevice: |
; Arguments are addressed through esp. In this point of the function, |
; [esp+4] = a handle of the config pipe, [esp+8] points to config_descr |
; structure, [esp+12] points to interface_descr structure. |
; 1. Check device type. Currently only mice and keyboards with |
; boot protocol are supported. |
; 1a. Get the subclass and the protocol. Since bInterfaceSubClass and |
; bInterfaceProtocol are subsequent in interface_descr, just one |
; memory reference is used for both. |
mov edx, [esp+12] |
push ebx ; save used register to be stdcall |
mov cx, word [edx+interface_descr.bInterfaceSubClass] |
; 1b. For boot protocol, subclass must be 1 and protocol must be either 1 for |
; a keyboard or 2 for a mouse. Check. |
cmp cx, 0x0101 |
jz .keyboard |
cmp cx, 0x0201 |
jz .mouse |
; 1c. If the device is neither a keyboard nor a mouse, print a message and |
; go to 6c. |
DEBUGF 1,'K : unknown HID device\n' |
jmp .nothing |
; 1d. If the device is a keyboard or a mouse, print a message and continue |
; configuring. |
.keyboard: |
DEBUGF 1,'K : USB keyboard detected\n' |
push keyboard_data.sizeof |
jmp .common |
.mouse: |
DEBUGF 1,'K : USB mouse detected\n' |
push mouse_data.sizeof |
.common: |
; 2. Allocate memory for device data. |
pop eax ; get size of device data |
; 2a. Call the kernel, saving and restoring register edx. |
push edx |
call Kmalloc |
pop edx |
; 2b. Check result. If failed, say a message and go to 6c. |
test eax, eax |
jnz @f |
DEBUGF 1,'K : no memory\n' |
jmp .nothing |
@@: |
xchg eax, ebx |
; HID devices use one IN interrupt endpoint for polling the device |
; and an optional OUT interrupt endpoint. We do not use the later, |
; but must locate the first. Look for the IN interrupt endpoint. |
; 3. Get the upper bound of all descriptors' data. |
mov eax, [esp+8+4] ; configuration descriptor |
movzx ecx, [eax+config_descr.wTotalLength] |
add eax, ecx |
; 4. Loop over all descriptors until |
; either end-of-data reached - this is fail |
; or interface descriptor found - this is fail, all further data |
; correspond to that interface |
; or endpoint descriptor found. |
; 4a. Loop start: eax points to the interface descriptor. |
.lookep: |
; 4b. Get next descriptor. |
movzx ecx, byte [edx] ; the first byte of all descriptors is length |
add edx, ecx |
; 4c. Check that at least two bytes are readable. The opposite is an error. |
inc edx |
cmp edx, eax |
jae .errorep |
dec edx |
; 4d. Check that this descriptor is not interface descriptor. The opposite is |
; an error. |
cmp byte [edx+endpoint_descr.bDescriptorType], INTERFACE_DESCR_TYPE |
jz .errorep |
; 4e. Test whether this descriptor is an endpoint descriptor. If not, continue |
; the loop. |
cmp byte [edx+endpoint_descr.bDescriptorType], ENDPOINT_DESCR_TYPE |
jnz .lookep |
; 5. Check that the descriptor contains all required data and all data are |
; readable. If so, proceed to 7. |
cmp byte [edx+endpoint_descr.bLength], endpoint_descr.sizeof |
jb .errorep |
sub eax, endpoint_descr.sizeof |
cmp edx, eax |
jbe @f |
; 6. An error occured during processing endpoint descriptor. |
.errorep: |
; 6a. Print a message. |
DEBUGF 1,'K : error: invalid endpoint descriptor\n' |
; 6b. Free memory allocated for device data. |
.free: |
xchg eax, ebx |
call Kfree |
.nothing: |
; 6c. Return an error. |
xor eax, eax |
pop ebx |
ret 12 |
@@: |
; 7. Check that the endpoint is IN interrupt endpoint. If not, go to 6. |
test [edx+endpoint_descr.bEndpointAddress], 80h |
jz .errorep |
mov cl, [edx+endpoint_descr.bmAttributes] |
and cl, 3 |
cmp cl, INTERRUPT_PIPE |
jnz .errorep |
; 8. Open pipe for the endpoint. |
; 8a. Load parameters from the descriptor. |
movzx ecx, [edx+endpoint_descr.bEndpointAddress] |
movzx eax, [edx+endpoint_descr.bInterval] |
movzx edx, [edx+endpoint_descr.wMaxPacketSize] |
; 8b. Call the kernel, saving and restoring edx. |
push edx |
stdcall USBOpenPipe, [esp+4+24], ecx, edx, INTERRUPT_PIPE, eax |
pop edx |
; 8c. Check result. If failed, go to 6b. |
test eax, eax |
jz .free |
; We use 12 bytes for device type, interrupt pipe and interrupt packet size, |
; 8 bytes for a packet and 8 bytes for previous packet, used by a keyboard. |
; 9. Initialize device data. |
mov [ebx+device_data.intpipe], eax |
push 8 |
pop ecx |
cmp edx, ecx |
jb @f |
mov edx, ecx |
@@: |
xor eax, eax |
mov [ebx+device_data.packetsize], edx |
mov dword [ebx+device_data.packet], eax |
mov dword [ebx+device_data.packet+4], eax |
mov edx, [esp+12+4] ; interface descriptor |
movzx ecx, [edx+interface_descr.bInterfaceProtocol] |
mov [ebx+device_data.type], ecx |
cmp ecx, 1 |
jnz @f |
mov [ebx+keyboard_data.handle], eax |
mov [ebx+keyboard_data.timer], eax |
mov [ebx+keyboard_data.repeatkey], al |
mov dword [ebx+keyboard_data.prevpacket], eax |
mov dword [ebx+keyboard_data.prevpacket+4], eax |
mov eax, [esp+4+4] |
mov [ebx+keyboard_data.configpipe], eax |
@@: |
; 10. Send the control packet SET_PROTOCOL(Boot Protocol) to the interface. |
lea eax, [ebx+device_data.control] |
mov dword [eax], 21h + (0Bh shl 8) + (0 shl 16) ; class request to interface + SET_PROTOCOL + Boot protocol |
and dword [eax+4], 0 |
mov dl, [edx+interface_descr.bInterfaceNumber] |
mov [eax+4], dl |
; Callback function is mouse_configured for mice and keyboard_configured1 for keyboards. |
mov edx, keyboard_configured1 |
cmp ecx, 1 |
jz @f |
mov edx, mouse_configured |
@@: |
stdcall USBControlTransferAsync, [esp+4+28], eax, 0, 0, edx, ebx, 0 |
; 11. Return with pointer to device data as returned value. |
xchg eax, ebx |
pop ebx |
ret 12 |
|
; This function is called when SET_PROTOCOL command for keyboard is done, |
; either successful or unsuccessful. |
keyboard_configured1: |
xor edx, edx |
; 1. Check the status of the transfer. |
; If the transfer was failed, go to the common error handler. |
cmp dword [esp+8], edx ; status is zero? |
jnz keyboard_data_ready.error |
; 2. Send the control packet SET_IDLE(infinity). HID auto-repeat is not useful. |
mov eax, [esp+20] |
push edx ; flags for USBControlTransferAsync |
push eax ; userdata for USBControlTransferAsync |
add eax, device_data.control |
mov dword [eax], 21h + (0Ah shl 8) + (0 shl 24) ; class request to interface + SET_IDLE + no autorepeat |
stdcall USBControlTransferAsync, dword [eax+keyboard_data.configpipe-device_data.control], \ |
eax, edx, edx, keyboard_configured2; , <userdata>, <flags> |
; 3. Return. |
ret 20 |
|
; This function is called when SET_IDLE command for keyboard is done, |
; either successful or unsuccessful. |
keyboard_configured2: |
; Check the status of the transfer and go to the corresponding label |
; in the main handler. |
cmp dword [esp+8], 0 |
jnz keyboard_data_ready.error |
mov edx, [esp+20] |
push edx |
stdcall RegKeyboard, usbkbd_functions, edx |
pop edx |
mov [edx+keyboard_data.handle], eax |
jmp keyboard_data_ready.next |
|
; This function is called when another interrupt packet arrives, |
; processed either successfully or unsuccessfully. |
; It should parse the packet and initiate another transfer with |
; the same callback function. |
keyboard_data_ready: |
; 1. Check the status of the transfer. |
mov eax, [esp+8] |
test eax, eax |
jnz .error |
; Parse the packet, comparing with the previous packet. |
; For boot protocol, USB keyboard packet consists of the first byte |
; with status keys that are currently pressed. The second byte should |
; be ignored, and other 5 bytes denote keys that are currently pressed. |
push esi ebx ; save used registers to be stdcall |
; 2. Process control keys. |
; 2a. Initialize before loop for control keys. edx = mask for control bits |
; that were changed. |
mov ebx, [esp+20+8] |
movzx edx, byte [ebx+device_data.packet] ; get state of control keys |
xor dl, byte [ebx+keyboard_data.prevpacket] ; compare with previous state |
; 2b. If state of control keys has not changed, advance to 3. |
jz .nocontrol |
; 2c. Otherwise, loop over control keys; esi = bit number. |
xor esi, esi |
.controlloop: |
; 2d. Skip bits that have not changed. |
bt edx, esi |
jnc .controlnext |
push edx ; save register which is possibly modified by API |
; The state of the current control key has changed. |
; 2e. For extended control keys, send the prefix 0xE0. |
mov al, [control_keys+esi] |
test al, al |
jns @f |
push eax |
mov ecx, 0xE0 |
call SetKeyboardData |
pop eax |
and al, 0x7F |
@@: |
; 2f. If the current state of the control key is "pressed", send normal |
; scancode. Otherwise, the key is released, so set the high bit in scancode. |
movzx ecx, al |
bt dword [ebx+device_data.packet], esi |
jc @f |
or cl, 0x80 |
@@: |
call SetKeyboardData |
pop edx ; restore register which was possibly modified by API |
.controlnext: |
; 2g. We have 8 control keys. |
inc esi |
cmp esi, 8 |
jb .controlloop |
.nocontrol: |
; 3. Initialize before loop for normal keys. esi = index. |
push 2 |
pop esi |
.normalloop: |
; 4. Process one key which was pressed in the previous packet. |
; 4a. Get the next pressed key from the previous packet. |
movzx eax, byte [ebx+esi+keyboard_data.prevpacket] |
; 4b. Ignore special codes. |
cmp al, 3 |
jbe .normalnext1 |
; 4c. Ignore keys that are still pressed in the current packet. |
lea ecx, [ebx+device_data.packet] |
call haskey |
jz .normalnext1 |
; 4d. Say warning about keys with strange codes. |
cmp eax, normal_keys_number |
jae .badkey1 |
movzx ecx, [normal_keys+eax] |
jecxz .badkey1 |
; 4e. For extended keys, send the prefix 0xE0. |
push ecx ; save keycode |
test cl, cl |
jns @f |
push ecx |
mov ecx, 0xE0 |
call SetKeyboardData |
pop ecx |
@@: |
; 4f. Send the release event. |
or cl, 0x80 |
call SetKeyboardData |
; 4g. If this key is autorepeating, stop the timer. |
pop ecx ; restore keycode |
cmp cl, [ebx+keyboard_data.repeatkey] |
jnz .normalnext1 |
mov eax, [ebx+keyboard_data.timer] |
test eax, eax |
jz .normalnext1 |
stdcall CancelTimerHS, eax |
and [ebx+keyboard_data.timer], 0 |
jmp .normalnext1 |
.badkey1: |
DEBUGF 1,'K : unknown keycode: %x\n',al |
.normalnext1: |
; 5. Process one key which is pressed in the current packet. |
; 5a. Get the next pressed key from the current packet. |
movzx eax, byte [ebx+esi+device_data.packet] |
; 5b. Ignore special codes. |
cmp al, 3 |
jbe .normalnext2 |
; 5c. Ignore keys that were already pressed in the previous packet. |
lea ecx, [ebx+keyboard_data.prevpacket] |
call haskey |
jz .normalnext2 |
; 5d. Say warning about keys with strange codes. |
cmp eax, normal_keys_number |
jae .badkey2 |
movzx ecx, [normal_keys+eax] |
jecxz .badkey2 |
; 5e. For extended keys, send the prefix 0xE0. |
push ecx ; save keycode |
test cl, cl |
jns @f |
push ecx |
mov ecx, 0xE0 |
call SetKeyboardData |
pop ecx |
@@: |
; 5f. Send the press event. |
and cl, not 0x80 |
call SetKeyboardData |
; 5g. Stop the current auto-repeat timer, if present. |
mov eax, [ebx+keyboard_data.timer] |
test eax, eax |
jz @f |
stdcall CancelTimerHS, eax |
@@: |
; 5h. Start the auto-repeat timer. |
pop ecx ; restore keycode |
mov [ebx+keyboard_data.repeatkey], cl |
stdcall TimerHS, 25, 5, autorepeat_timer, ebx |
mov [ebx+keyboard_data.timer], eax |
jmp .normalnext2 |
.badkey2: |
DEBUGF 1,'K : unknown keycode: %x\n',al |
.normalnext2: |
; 6. Advance to next key. |
inc esi |
cmp esi, 8 |
jb .normalloop |
; 7. Save the packet data for future reference. |
mov eax, dword [ebx+device_data.packet] |
mov dword [ebx+keyboard_data.prevpacket], eax |
mov eax, dword [ebx+device_data.packet+4] |
mov dword [ebx+keyboard_data.prevpacket+4], eax |
pop ebx esi ; restore registers to be stdcall |
.next: |
; 8. Initiate transfer on the interrupt pipe. |
mov eax, [esp+20] |
push 1 ; flags for USBNormalTransferAsync |
push eax ; userdata for USBNormalTransferAsync |
add eax, device_data.packet |
stdcall USBNormalTransferAsync, dword [eax+device_data.intpipe-device_data.packet], \ |
eax, dword [eax+device_data.packetsize-device_data.packet], \ |
keyboard_data_ready;, <userdata>, <flags> |
; 9. Return. |
.nothing: |
ret 20 |
.error: |
; An error has occured. |
; 10. If an error is caused by the disconnect, do nothing, it is handled |
; in DeviceDisconnected. Otherwise, say a message. |
cmp eax, 16 |
jz @f |
push esi |
mov esi, errormsgkbd |
call SysMsgBoardStr |
pop esi |
@@: |
ret 20 |
|
; Auxiliary procedure for keyboard_data_ready. |
haskey: |
push 2 |
pop edx |
@@: |
cmp byte [ecx+edx], al |
jz @f |
inc edx |
cmp edx, 7 |
jbe @b |
@@: |
ret |
|
; Timer function for auto-repeat. |
autorepeat_timer: |
mov eax, [esp+4] |
movzx ecx, [eax+keyboard_data.repeatkey] |
test cl, cl |
jns @f |
push ecx |
mov ecx, 0xE0 |
call SetKeyboardData |
pop ecx |
and cl, not 0x80 |
@@: |
call SetKeyboardData |
ret 4 |
|
; This function is called to update LED state on the keyboard. |
SetKeyboardLights: |
mov eax, [esp+4] |
add eax, device_data.control |
mov dword [eax], 21h + (9 shl 8) + (2 shl 24) |
; class request to interface + SET_REPORT + Output zero report |
mov byte [eax+6], 1 |
mov edx, [esp+8] |
shr dl, 1 |
jnc @f |
or dl, 4 |
@@: |
lea ecx, [eax+keyboard_data.ledstate-device_data.control] |
mov [ecx], dl |
stdcall USBControlTransferAsync, dword [eax+keyboard_data.configpipe-device_data.control], \ |
eax, ecx, 1, keyboard_data_ready.nothing, 0, 0 |
ret 8 |
|
; This function is called when it is safe to free keyboard data. |
CloseKeyboard: |
mov eax, [esp+4] |
push ebx |
call Kfree |
pop ebx |
ret 4 |
|
; This function is called when SET_PROTOCOL command for mouse is done, |
; either successful or unsuccessful. |
mouse_configured: |
; Check the status of the transfer and go to the corresponding label |
; in the main handler. |
cmp dword [esp+8], 0 |
jnz mouse_data_ready.error |
mov eax, [esp+20] |
add eax, device_data.packet |
jmp mouse_data_ready.next |
|
; This function is called when another interrupt packet arrives, |
; processed either successfully or unsuccessfully. |
; It should parse the packet and initiate another transfer with |
; the same callback function. |
mouse_data_ready: |
; 1. Check the status of the transfer. |
mov eax, [esp+8] |
test eax, eax |
jnz .error |
mov edx, [esp+16] |
; 2. Parse the packet. |
; For boot protocol, USB mouse packet consists of at least 3 bytes. |
; The first byte is state of mouse buttons, the next two bytes are |
; x and y movements. |
; Normal mice do not distinguish between boot protocol and report protocol; |
; in this case, scroll data are also present. Advanced mice, however, |
; support two different protocols, boot protocol is used for compatibility |
; and does not contain extended buttons or scroll data. |
mov eax, [esp+12] ; buffer |
push eax |
xor ecx, ecx |
cmp edx, 4 |
jbe @f |
movsx ecx, byte [eax+4] |
@@: |
push ecx |
xor ecx, ecx |
cmp edx, 3 |
jbe @f |
movsx ecx, byte [eax+3] |
neg ecx |
@@: |
push ecx |
xor ecx, ecx |
cmp edx, 2 |
jbe @f |
movsx ecx, byte [eax+2] |
neg ecx |
@@: |
push ecx |
movsx ecx, byte [eax+1] |
push ecx |
movzx ecx, byte [eax] |
push ecx |
call SetMouseData |
pop eax |
.next: |
; 3. Initiate transfer on the interrupt pipe. |
stdcall USBNormalTransferAsync, dword [eax+device_data.intpipe-device_data.packet], \ |
eax, dword [eax+device_data.packetsize-device_data.packet], mouse_data_ready, eax, 1 |
; 4. Return. |
ret 20 |
.error: |
; An error has occured. |
; 5. If an error is caused by the disconnect, do nothing, it is handled |
; in DeviceDisconnected. Otherwise, say a message. |
cmp eax, 16 |
jz @f |
push esi |
mov esi, errormsgmouse |
call SysMsgBoardStr |
pop esi |
@@: |
ret 20 |
|
; This function is called when the device is disconnected. |
DeviceDisconnected: |
push ebx ; save used register to be stdcall |
; 1. Say a message. Use different messages for keyboards and mice. |
mov ebx, [esp+4+4] |
push esi |
mov esi, disconnectmsgk |
cmp byte [ebx+device_data.type], 1 |
jz @f |
mov esi, disconnectmsgm |
@@: |
stdcall SysMsgBoardStr |
pop esi |
; 2. If device is keyboard, then we must unregister it as a keyboard and |
; possibly stop the auto-repeat timer. |
cmp byte [ebx+device_data.type], 1 |
jnz .nokbd |
mov eax, [ebx+keyboard_data.timer] |
test eax, eax |
jz @f |
stdcall CancelTimerHS, eax |
@@: |
mov ecx, [ebx+keyboard_data.handle] |
jecxz .nokbd |
stdcall DelKeyboard, ecx |
; If keyboard is registered, then we should free data in CloseKeyboard, not here. |
jmp .nothing |
.nokbd: |
; 3. Free the device data. |
xchg eax, ebx |
call Kfree |
; 4. Return. |
.nothing: |
pop ebx ; restore used register to be stdcall |
ret 4 ; purge one dword argument to be stdcall |
|
; strings |
my_driver db 'usbhid',0 |
errormsgmouse db 'K : USB transfer error, disabling mouse',10,0 |
errormsgkbd db 'K : USB transfer error, disabling keyboard',10,0 |
disconnectmsgm db 'K : USB mouse disconnected',10,0 |
disconnectmsgk db 'K : USB keyboard disconnected',10,0 |
|
; data for keyboard: correspondence between HID usage keys and PS/2 scancodes. |
EX = 80h |
label control_keys byte |
db 1Dh, 2Ah, 38h, 5Bh+EX, 1Dh+EX, 36h, 38h+EX, 5Ch+EX |
label normal_keys byte |
db 00h, 00h, 00h, 00h, 1Eh, 30h, 2Eh, 20h, 12h, 21h, 22h, 23h, 17h, 24h, 25h, 26h ; 0x |
db 32h, 31h, 18h, 19h, 10h, 13h, 1Fh, 14h, 16h, 2Fh, 11h, 2Dh, 15h, 2Ch, 02h, 03h ; 1x |
db 04h, 05h, 06h, 07h, 08h, 09h, 0Ah, 0Bh, 1Ch, 01h, 0Eh, 0Fh, 39h, 0Ch, 0Dh, 1Ah ; 2x |
db 1Bh, 2Bh, 2Bh, 27h, 28h, 29h, 33h, 34h, 35h, 3Ah, 3Bh, 3Ch, 3Dh, 3Eh, 3Fh, 40h ; 3x |
db 41h, 42h, 43h, 44h, 57h, 58h,37h+EX,46h,0,52h+EX,47h+EX,49h+EX,53h+EX,4Fh+EX,51h+EX,4Dh+EX ; 4x |
db 4Bh+EX,50h+EX,48h+EX,45h,35h+EX,37h,4Ah,4Eh,1Ch+EX,4Fh,50h,51h,4Bh,4Ch,4Dh,47h ; 5x |
db 48h, 49h, 52h, 53h, 56h,5Dh+EX,5Eh+EX,59h,64h,65h,66h, 67h, 68h, 69h, 6Ah, 6Bh ; 6x |
db 6Ch, 6Dh, 6Eh, 76h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h ; 7x |
db 00h, 00h, 00h, 00h, 00h, 7Eh, 00h, 73h, 70h, 7Dh, 79h, 7Bh, 5Ch, 00h, 00h, 00h ; 8x |
db 0F2h,0F1h,78h, 77h, 76h |
normal_keys_number = $ - normal_keys |
|
; Exported variable: kernel API version. |
align 4 |
version dd 50005h |
; Structure with callback functions. |
usb_functions: |
dd 12 |
dd AddDevice |
dd DeviceDisconnected |
|
; Structure with callback functions for keyboards. |
usbkbd_functions: |
dd 12 |
dd CloseKeyboard |
dd SetKeyboardLights |
|
; for DEBUGF macro |
include_debug_strings |
|
; for uninitialized data |
section '.data' data readable writable align 16 |