Subversion Repositories Kolibri OS

Compare Revisions

No changes between revisions

Regard whitespace Rev 4286 → Rev 4287

/kernel/branches/Kolibri-acpi/drivers/usbhid.asm
File deleted
/kernel/branches/Kolibri-acpi/drivers/usb/usb.asm
File deleted
/kernel/branches/Kolibri-acpi/drivers/usb/urb.inc
File deleted
/kernel/branches/Kolibri-acpi/drivers/usb
Property changes:
Deleted: svn:ignore
-*.mnt
-lang.inc
-*.bat
-out.txt
-scin*
-*.obj
/kernel/branches/Kolibri-acpi/drivers/agp.asm
0,0 → 1,310
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; simple AGP driver for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
format MS COFF
 
DEBUG equ 1
FAST_WRITE equ 0 ; may cause problems with some motherboards
 
include 'proc32.inc'
include 'imports.inc'
 
struc IOCTL
{ .handle dd ?
.io_code dd ?
.input dd ?
.inp_size dd ?
.output dd ?
.out_size dd ?
}
 
virtual at 0
IOCTL IOCTL
end virtual
 
public START
public service_proc
public version
 
DRV_ENTRY equ 1
DRV_EXIT equ -1
 
SRV_GETVERSION equ 0
SRV_DETECT equ 1
 
API_VERSION equ 1
 
section '.flat' code readable align 16
 
proc START stdcall, state:dword
 
cmp [state], 1
jne .exit
.entry:
 
if DEBUG
mov esi, msgInit
call SysMsgBoardStr
end if
 
stdcall RegService, my_service, service_proc
ret
.fail:
.exit:
xor eax, eax
ret
endp
 
handle equ IOCTL.handle
io_code equ IOCTL.io_code
input equ IOCTL.input
inp_size equ IOCTL.inp_size
output equ IOCTL.output
out_size equ IOCTL.out_size
 
align 4
proc service_proc stdcall, ioctl:dword
 
mov ebx, [ioctl]
mov eax, [ebx+io_code]
cmp eax, SRV_GETVERSION
jne @F
 
mov eax, [ebx+output]
cmp [ebx+out_size], 4
jne .fail
mov [eax], dword API_VERSION
xor eax, eax
ret
@@:
mov ebx, [ioctl]
mov eax, [ebx+io_code]
cmp eax, SRV_DETECT
jne @F
call detect
@@:
.fail:
or eax, -1
ret
endp
 
restore handle
restore io_code
restore input
restore inp_size
restore output
restore out_size
 
align 4
proc detect
locals
last_bus dd ?
endl
 
mov esi, msgSearch
call SysMsgBoardStr
 
xor eax, eax
mov [bus], eax
inc eax
call PciApi ; get last bus
cmp eax, -1
je .error
mov [last_bus], eax
 
.next_bus:
and [devfn], 0
.next_dev:
stdcall PciRead16, [bus], [devfn], dword 0x0a ; read class/subclass
 
cmp ax, 0x0300 ; display controller - vga compatable controller
je .found
 
cmp ax, 0x0302 ; display controller - 3d controller
je .found
 
cmp ax, 0x0380 ; display controller - other display controller
je .found
 
.next:
inc [devfn]
cmp [devfn], 256
jb .next_dev
mov eax, [bus]
inc eax
mov [bus], eax
cmp eax, [last_bus]
jna .next_bus
 
.error:
mov esi, msgFail
call SysMsgBoardStr
 
xor eax, eax
inc eax
ret
 
.found:
stdcall PciRead8, [bus], [devfn], dword 0x06 ; read prog IF
test al, 1 shl 4 ; got capabilities list?
jnz .got_capabilities_list
 
; TODO: Do it the old way: detect device and check with a list of known capabilities
; stupid pre PCI 2.2 board....
 
jmp .next
 
.got_capabilities_list:
stdcall PciRead8, [bus], [devfn], dword 0x34 ; read capabilities offset
and eax, 11111100b ; always dword aligned
mov edi, eax
 
.read_capability:
stdcall PciRead32, [bus], [devfn], edi ; read capability
cmp al, 0x02 ; AGP
je .got_agp
movzx edi, ah ; pointer to next capability
test edi, edi
jnz .read_capability
jmp .next
 
.got_agp:
shr eax, 16
mov [revision], al ; high nibble = major revision
; low nibble = minor revision
add edi, 4
and al, 0xf0
cmp al, 0x30
je .agp_3
 
.agp_2:
mov esi, msgAGP2
call SysMsgBoardStr
 
stdcall PciRead32, [bus], [devfn], edi ; read AGP status
.agp_2_:
test al, 100b
jnz .100b
 
test al, 10b
jnz .010b
 
test al, 1b
jz .error
 
.001b:
mov [cmd], 001b
mov esi, msg1
call SysMsgBoardStr
jmp .agp_go
 
.010b:
mov [cmd], 010b
mov esi, msg2
call SysMsgBoardStr
jmp .agp_go
 
.100b:
mov [cmd], 100b
mov esi, msg4
call SysMsgBoardStr
jmp .agp_go
 
.agp_2m:
mov esi, msgAGP2m
call SysMsgBoardStr
jmp .agp_2_
 
.agp_3:
mov esi, msgAGP3
call SysMsgBoardStr
 
stdcall PciRead32, [bus], [devfn], edi ; read AGP status
test al, 1 shl 3
jz .agp_2m
 
test eax, 10b
jnz .8x
mov [cmd], 01b
mov esi, msg4
call SysMsgBoardStr
jmp .agp_go
 
.8x:
mov [cmd], 10b
mov esi, msg8
call SysMsgBoardStr
 
.agp_go:
 
if FAST_WRITE
test ax, 1 shl 4
jz @f
or [cmd], 1 shl 4
mov esi, msgfast
call SysMsgBoardStr
@@:
end if
 
test ax, 1 shl 9 ; Side band addressing
jz @f
or [cmd], 1 shl 9
mov esi, msgside
call SysMsgBoardStr
@@:
 
add edi, 4
mov eax, [cmd]
or eax, 1 shl 8 ; enable AGP
stdcall PciWrite32, [bus], [devfn], edi, eax ; write AGP cmd
 
mov esi, msgOK
call SysMsgBoardStr
 
ret
 
endp
 
 
; initialized data
 
align 4
version dd (5 shl 16) or (API_VERSION and 0xFFFF)
 
my_service db 'AGP', 0 ; max 16 chars include zero
 
msgInit db 'AGP driver loaded.', 13, 10, 0
msgSearch db 'Searching for AGP card...', 13, 10, 0
msgFail db 'device not found', 13, 10, 0
msgOK db 'AGP device enabled', 13, 10, 0
msgAGP2 db 'AGP2 device found', 13, 10, 0
msgAGP3 db 'AGP3 device found', 13, 10, 0
msgAGP2m db 'Running in AGP2 mode', 13, 10, 0
msg8 db '8x speed', 13, 10, 0
msg4 db '4x speed', 13, 10, 0
msg2 db '2x speed', 13, 10, 0
msg1 db '1x speed', 13, 10, 0
msgfast db 'Fast Write', 13, 10, 0
msgside db 'Side band addressing', 13, 10, 0
 
section '.data' data readable writable align 16
 
; uninitialized data
 
revision db ?
cmd dd ?
bus dd ?
devfn dd ?
 
/kernel/branches/Kolibri-acpi/drivers/apm.asm
0,0 → 1,350
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2009-2011. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
; 11.09.2009 staper@inbox.ru
; see kernel\docs\apm.txt
 
use32
 
org 0x0
 
db 'MENUET01'
dd 0x1
dd START
dd I_END
dd (I_END+100) and not 3
dd (I_END+100) and not 3
dd 0x0,0x0
 
include 'macros.inc'
 
START:
mcall 40,0x7
 
mcall 49,0x0001,0x0001,0x5308 ;CX = FFFFh APM v1.0
; mcall 49,0x0001,0x0001,0x530d
; mcall 49,0x0001,0x0001,0x530f
 
; mcall 49,0x0000,,0x5310 ;bl - number of batteries
redraw:
mcall 49,0x0000,,0x530c
dec cl
jz still
mcall 49,0x0001,0x0001,0x5308
mcall 49,0x01ff,,0x530c
test cl, cl
jz @f
mcall 49,0x0000,0x0001,0x530d
mcall 49,0x0000,0x0000,0x5307
mcall 49,0x0000,0x0001,0x5308
@@:
mcall 12,1
mcall 0,100*65536+235,100*65536+90,0x34ffffff,0x000000,title
mcall 49,0x0000,,0x5300
jnc @f
mcall 4,10*65536+3,0x80000000,text.4
bts [flags], 1
jmp .end
@@:
cmp al, 0
jne @f
mov edx, text.1
jmp .0
@@:
cmp al, 1
jne @f
mov edx, text.2
jmp .0
@@:
mov edx, text.3
.0:
push edx
mcall 4,169*65536+3,0x80dddddd,text.0
pop edx
add ebx, 47*65536
mcall
mcall 49,0x0001,,0x530a
jc .error
push si dx cx bx ;time of battery life, b. flag, b. status, AC line status
 
;AC line status
cmp bh, 0
jne @f
mov edx, text.01
jmp .1
@@:
cmp bh, 1
jne @f
mov edx, text.02
jmp .1
@@:
cmp bh, 2
jne @f
mov edx, text.03
jmp .1
@@:
mov edx, text.04
.1:
push edx
mcall 4,10*65536+10,0x80000000,text.00
pop edx
mcall ,100*65536+10,;0x80000000
 
;battery status
pop bx
cmp bl, 0
jne @f
mov edx, text.11
jmp .2
@@:
cmp bl, 1
jne @f
mov edx, text.12
jmp .2
@@:
cmp bl, 2
jne @f
mov edx, text.13
jmp .2
@@:
cmp bl, 3
jne @f
mov edx, text.14
jmp .2
@@:
mov edx, text.04
.2:
push edx
mcall 4,10*65536+20,0x80000000,text.10
pop edx
mcall ,100*65536+20,
 
;battery life, percentage and minutes/seconds
mcall ,10*65536+30,,text.20
pop cx
cmp cl, 0xff
jne @f
mcall ,100*65536+30,0x80000000,text.04
pop eax
jmp .end
@@:
shl ecx, 24
shr ecx, 24
mcall 47,0x80030000,,100*65536+30,0x347636
.3:
mcall 4,115*65536+30,0x80000000,text.15
mov dx, [esp]
shl edx, 17
shr edx, 17
mov ecx, edx
mcall 47,0x80030000,,140*65536+30
pop cx
mov edx, text.21
bt cx, 15
jc @f
mov edx, text.22
@@:
mcall 4,160*65536+30,0x80000000
pop si
.error:
.end:
;buttons
mcall 8,148*65536+16,45*65536+15,3,0x00677ab0
mcall ,166*65536+16,,4,
mcall ,184*65536+16,,5,
mcall ,202*65536+16,,6,
bt [flags], 1
jc @f
mcall ,65*65536+45,,2,
@@:
mcall 4,10*65536+50,0x80564242,text.30
mcall 12,2
 
still:
; mcall 10
mcall 23,12000
test eax, eax
jz redraw
 
dec al
jz redraw
dec al
jz key
dec al
jz button
jmp still
 
 
 
 
key:
mcall 2
jmp still
 
button:
mcall 17
cmp ah, 1
jne @f
mcall -1
 
@@:
cmp ah, 2
jne @f
mcall 5,50
mcall 49,0x0001,0x0001,0x5307
jmp redraw
 
@@:
cmp ah, 4
jg @f
mov edx, 0x01f7 ;primary chan.
call reserv_ports
jc redraw
sub bh, 3
.1:
call set_drive
btc [flags], 2
jnc .2
call device_reset
jmp .3
.2:
call standby_hdd
.3:
call free_ports
jmp redraw
 
@@:
cmp ah, 6
jg redraw
mov edx, 0x0177 ;secondary chan.
call reserv_ports
jc redraw
sub bh, 5
jmp .1
 
set_drive:
dec dx
in al, dx
test bh, bh
jnz @f
btr ax, 4
.1:
out dx, al
inc dx
ret
@@:
bts ax, 4
jmp .1
 
 
standby_hdd:
; 94h E0h nondata standby immediate
; 95h E1h nondata idle immediate
; 96h E2h nondata standby
; 97h E3h nondata idle
; 98h E5h nondata check power mode
; 99h E6h nondata set sleep mode
xor ecx, ecx
@@:
in al, dx
dec cx
jz @f
bt ax, 6
jnc @b
mov al, 0x96
out dx, al
mov al, 0xe2
out dx, al
@@:
ret
 
reserv_ports:
mov ecx, edx
dec ecx
push ax
mcall 46,0
test al, al
jnz @f
pop bx
clc
ret
@@:
pop bx
stc
ret
 
device_reset:
xor ecx, ecx
@@:
in al, dx
dec cx
jz @f
bt ax, 6
jnc @b
mov al, 0x10
out dx, al
@@:
ret
 
free_ports:
mov ecx, edx
dec ecx
mcall 46,1
ret
 
 
; ДАННЫЕ ПРОГРАММЫ
title db '',0
flags dw 0
 
text:
.0:
db 'APM v.1.',0
.1:
db '0',0
.2:
db '1',0
.3:
db '2',0
.4:
db 'APM not supported',0
 
.00:
db 'power status:',0
.01:
db 'off-line',0
.02:
db 'on-line',0
.03:
db 'on backup power',0
.04:
db 'unknown',0
 
.10:
db 'battery flag:',0
.11:
db 'high',0
.12:
db 'low',0
.13:
db 'critical',0
.14:
db 'charging',0
.15:
db ' % ,',0
 
.20:
db 'battery life:',0
.21:
db 'min',0
.22:
db 'sec',0
 
.30:
db 'STAND-BY: SYSTEM HDD: 0 1 2 3',0
 
I_END:
/kernel/branches/Kolibri-acpi/drivers/fdo.inc
0,0 → 1,453
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
_esp equ esp
 
;
; Formatted Debug Output (FDO)
; Copyright (c) 2005-2006, mike.dld
; Created: 2005-01-29, Changed: 2006-11-10
;
; For questions and bug reports, mail to mike.dld@gmail.com
;
; Available format specifiers are: %s, %d, %u, %x (with partial width support)
;
 
; to be defined:
; __DEBUG__ equ 1
; __DEBUG_LEVEL__ equ 5
 
; MOV Immediate.
; Useful for things like movi eax,10:
; shorter than regular mov, but slightly slower,
; do not use it in performance-critical places.
macro movi dst, imm
{
if imm >= -0x80 & imm <= 0x7F
push imm
pop dst
else
mov dst, imm
end if
}
 
macro debug_func name {
if used name
name@of@func equ name
}
 
macro debug_beginf {
align 4
name@of@func:
}
 
debug_endf fix end if
 
macro DEBUGS _sign,[_str] {
common
local tp
tp equ 0
match _arg:_num,_str \{
DEBUGS_N _sign,_num,_arg
tp equ 1
\}
match =0 _arg,tp _str \{
DEBUGS_N _sign,,_arg
\}
}
 
macro DEBUGS_N _sign,_num,[_str] {
common
pushf
pushad
local ..str,..label,is_str
is_str = 0
forward
if _str eqtype ''
is_str = 1
end if
common
if is_str = 1
jmp ..label
..str db _str,0
..label:
mov edx, ..str
else
esp equ esp+4*8+4
mov edx, _str
esp equ _esp
end if
if ~_num eq
if _num eqtype eax
if _num in <eax,ebx,ecx,edx,edi,ebp,esp>
mov esi, _num
else if ~_num eq esi
movzx esi, _num
end if
else if _num eqtype 0
mov esi, _num
else
local tp
tp equ 0
match [_arg],_num \{
mov esi, dword[_arg]
tp equ 1
\}
match =0 =dword[_arg],tp _num \{
mov esi, dword[_arg]
tp equ 1
\}
match =0 =word[_arg],tp _num \{
movzx esi, word[_arg]
tp equ 1
\}
match =0 =byte[_arg],tp _num \{
movzx esi, byte[_arg]
tp equ 1
\}
match =0,tp \{
'Error: specified string width is incorrect'
\}
end if
else
mov esi, 0x7FFFFFFF
end if
call fdo_debug_outstr
popad
popf
}
 
macro DEBUGD _sign,_dec {
local tp
tp equ 0
match _arg:_num,_dec \{
DEBUGD_N _sign,_num,_arg
tp equ 1
\}
match =0 _arg,tp _dec \{
DEBUGD_N _sign,,_arg
\}
}
 
macro DEBUGD_N _sign,_num,_dec {
pushf
pushad
if (~_num eq)
if (_dec eqtype eax | _dec eqtype 0)
'Error: precision allowed only for in-memory variables'
end if
if (~_num in <1,2,4>)
if _sign
'Error: 1, 2 and 4 are only allowed for precision in %d'
else
'Error: 1, 2 and 4 are only allowed for precision in %u'
end if
end if
end if
if _dec eqtype eax
if _dec in <ebx,ecx,edx,esi,edi,ebp,esp>
mov eax, _dec
else if ~_dec eq eax
if _sign = 1
movsx eax, _dec
else
movzx eax, _dec
end if
end if
else if _dec eqtype 0
mov eax, _dec
else
esp equ esp+4*8+4
if _num eq
mov eax, dword _dec
else if _num = 1
if _sign = 1
movsx eax, byte _dec
else
movzx eax, byte _dec
end if
else if _num = 2
if _sign = 1
movsx eax, word _dec
else
movzx eax, word _dec
end if
else
mov eax, dword _dec
end if
esp equ _esp
end if
mov cl, _sign
call fdo_debug_outdec
popad
popf
}
 
macro DEBUGH _sign,_hex {
local tp
tp equ 0
match _arg:_num,_hex \{
DEBUGH_N _sign,_num,_arg
tp equ 1
\}
match =0 _arg,tp _hex \{
DEBUGH_N _sign,,_arg
\}
}
 
macro DEBUGH_N _sign,_num,_hex {
pushf
pushad
if (~_num eq) & (~_num in <1,2,3,4,5,6,7,8>)
'Error: 1..8 are only allowed for precision in %x'
end if
if _hex eqtype eax
if _hex in <eax,ebx,ecx,edx,esi,edi,ebp,esp>
if ~_hex eq eax
mov eax, _hex
end if
mov edx, 8
else if _hex in <ax,bx,cx,dx,si,di,bp,sp>
if ~_hex eq ax
movzx eax, _hex
end if
if (_num eq)
mov edx, 4
end if
else if _hex in <al,ah,bl,bh,cl,ch,dl,dh>
if ~_hex eq al
movzx eax, _hex
end if
if (_num eq)
mov edx, 2
end if
end if
else if _hex eqtype 0
mov eax, _hex
else
esp equ esp+4*8+4
mov eax, dword _hex
esp equ _esp
end if
if ~_num eq
mov edx, _num
else
if ~_hex eqtype eax
mov edx, 8
end if
end if
call fdo_debug_outhex
popad
popf
}
 
;-----------------------------------------------------------------------------
 
debug_func fdo_debug_outchar
debug_beginf
pushad
movzx ecx, al
mov ebx, 1
; mov ecx,sys_msg_board
; call ecx ; sys_msg_board
stdcall SysMsgBoard
popad
ret
debug_endf
 
debug_func fdo_debug_outstr
debug_beginf
mov ebx, 1
.l1:
dec esi
js .l2
movzx ecx, byte[edx]
or cl, cl
jz .l2
; mov ecx,sys_msg_board
; call ecx ; sys_msg_board
stdcall SysMsgBoard
inc edx
jmp .l1
.l2:
ret
debug_endf
 
debug_func fdo_debug_outdec
debug_beginf
or cl, cl
jz @f
or eax, eax
jns @f
neg eax
push eax
mov al, '-'
call fdo_debug_outchar
pop eax
@@:
movi ecx, 10
push -'0'
.l1:
xor edx, edx
div ecx
push edx
test eax, eax
jnz .l1
.l2:
pop eax
add al, '0'
jz .l3
call fdo_debug_outchar
jmp .l2
.l3:
ret
debug_endf
 
debug_func fdo_debug_outhex
__fdo_hexdigits db '0123456789ABCDEF'
debug_beginf
mov cl, dl
neg cl
add cl, 8
shl cl, 2
rol eax, cl
.l1:
rol eax, 4
push eax
and eax, 0x0000000F
mov al, [__fdo_hexdigits+eax]
call fdo_debug_outchar
pop eax
dec edx
jnz .l1
ret
debug_endf
 
;-----------------------------------------------------------------------------
 
macro DEBUGF _level,_format,[_arg] {
common
if __DEBUG__ = 1 & _level >= __DEBUG_LEVEL__
local ..f1,f2,a1,a2,c1,c2,c3,..lbl
_debug_str_ equ __debug_str_ # a1
a1 = 0
c2 = 0
c3 = 0
f2 = 0
repeat ..lbl-..f1
virtual at 0
db _format,0,0
load c1 word from %-1
end virtual
if c1 = '%s'
virtual at 0
db _format,0,0
store word 0 at %-1
load c1 from f2-c2
end virtual
if c1 <> 0
DEBUGS 0,_debug_str_+f2-c2
end if
c2 = c2 + 1
f2 = %+1
DEBUGF_HELPER S,a1,0,_arg
else if c1 = '%x'
virtual at 0
db _format,0,0
store word 0 at %-1
load c1 from f2-c2
end virtual
if c1 <> 0
DEBUGS 0,_debug_str_+f2-c2
end if
c2 = c2 + 1
f2 = %+1
DEBUGF_HELPER H,a1,0,_arg
else if c1 = '%d' | c1 = '%u'
local c4
if c1 = '%d'
c4 = 1
else
c4 = 0
end if
virtual at 0
db _format,0,0
store word 0 at %-1
load c1 from f2-c2
end virtual
if c1 <> 0
DEBUGS 0,_debug_str_+f2-c2
end if
c2 = c2 + 1
f2 = %+1
DEBUGF_HELPER D,a1,c4,_arg
else if c1 = '\n'
c3 = c3 + 1
end if
end repeat
virtual at 0
db _format,0,0
load c1 from f2-c2
end virtual
if (c1<>0)&(f2<>..lbl-..f1-1)
DEBUGS 0,_debug_str_+f2-c2
end if
virtual at 0
..f1 db _format,0
..lbl:
__debug_strings equ __debug_strings,_debug_str_,<_format>,..lbl-..f1-1-c2-c3
end virtual
end if
}
 
macro __include_debug_strings dummy,[_id,_fmt,_len] {
common
local c1,a1,a2
forward
if defined _len & ~_len eq
_id:
a1 = 0
a2 = 0
repeat _len
virtual at 0
db _fmt,0,0
load c1 word from %+a2-1
end virtual
if (c1='%s')|(c1='%x')|(c1='%d')|(c1='%u')
db 0
a2 = a2 + 1
else if (c1='\n')
dw $0A0D
a1 = a1 + 1
a2 = a2 + 1
else
db c1 and 0x0FF
end if
end repeat
db 0
end if
}
 
macro DEBUGF_HELPER _letter,_num,_sign,[_arg] {
common
local num
num = 0
forward
if num = _num
DEBUG#_letter _sign,_arg
end if
num = num+1
common
_num = _num+1
}
 
macro include_debug_strings {
if __DEBUG__ = 1
match dbg_str,__debug_strings \{
__include_debug_strings dbg_str
\}
end if
}
/kernel/branches/Kolibri-acpi/drivers/usbhid/keyboard.inc
0,0 → 1,475
; HID keyboard driver, part of USBHID driver.
 
; Global constants.
; They are assembled in a macro to separate code and data;
; the code is located at the point of "include 'keyboard.inc'",
; the data are collected when workers_globals is instantiated.
macro workers_globals
{
; include global constants from previous workers
workers_globals
align 4
; Callbacks for HID layer.
keyboard_driver:
dd keyboard_driver_add_device
dd keyboard_driver_disconnect
dd keyboard_driver_begin_packet
dd keyboard_driver_array_overflow?
dd keyboard_driver_input_field
dd keyboard_driver_end_packet
; Callbacks for keyboard layer.
kbd_functions:
dd 12
dd CloseKeyboard
dd SetKeyboardLights
; Kernel keyboard layer takes input in form of PS/2 scancodes.
; data for keyboard: correspondence between HID usage keys and PS/2 scancodes.
EX = 80h ; if set, precede the scancode with special scancode 0xE0
label control_keys byte
; Usages 700E0h ... 700E7h: LCtrl, LShift, LAlt, LWin, RCtrl, RShift, RAlt, RWin
db 1Dh, 2Ah, 38h, 5Bh+EX, 1Dh+EX, 36h, 38h+EX, 5Ch+EX
; Usages 70004h ... 70004h + normal_keys_number - 1
label normal_keys byte
db 1Eh, 30h, 2Eh, 20h, 12h, 21h, 22h, 23h, 17h, 24h, 25h, 26h, 32h, 31h, 18h, 19h
db 10h, 13h, 1Fh, 14h, 16h, 2Fh, 11h, 2Dh, 15h, 2Ch, 02h, 03h, 04h, 05h, 06h, 07h
db 08h, 09h, 0Ah, 0Bh, 1Ch, 01h, 0Eh, 0Fh, 39h, 0Ch, 0Dh, 1Ah, 1Bh, 2Bh, 0, 27h
db 28h, 29h, 33h, 34h, 35h, 3Ah, 3Bh, 3Ch, 3Dh, 3Eh, 3Fh, 40h, 41h, 42h, 43h, 44h
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
db 35h+EX,37h,4Ah,4Eh,1Ch+EX,4Fh,50h, 51h, 4Bh, 4Ch, 4Dh, 47h, 48h, 49h, 52h, 53h
db 0,5Dh+EX,5Eh+EX
normal_keys_number = $ - normal_keys
}
 
; Data that are specific for one keyboard device.
struct keyboard_device_data
handle dd ? ; keyboard handle from RegKeyboard
timer dd ? ; auto-repeat timer handle
repeatkey db ? ; auto-repeat key code
rb 3 ; padding
usbdev dd ? ; pointer to device_data of USB and HID layers
modifiers dd ? ; state of LCtrl ... RWin
led_report dd ? ; output report for LEDs state
numlock_bit dd ? ; position of NumLock bit in LED output report
capslock_bit dd ?
scrolllock_bit dd ? ; guess what
ends
 
; This procedure is called when HID layer detects a new keyboard.
; in: ebx -> usb_device_data, edi -> collection
; out: eax = device-specific data or NULL on error
proc keyboard_driver_add_device
; 1. Allocate memory for keyboard_device_data. If failed, return NULL.
movi eax, sizeof.keyboard_device_data
call Kmalloc
test eax, eax
jz .nothing
; 2. Initialize keyboard_device_data: store pointer to USB layer data,
; zero some fields, initialize bit positions to -1.
mov [eax+keyboard_device_data.usbdev], ebx
xor ecx, ecx
mov [eax+keyboard_device_data.timer], ecx
mov [eax+keyboard_device_data.repeatkey], cl
mov [eax+keyboard_device_data.modifiers], ecx
mov [eax+keyboard_device_data.led_report], ecx
dec ecx
mov [eax+keyboard_device_data.numlock_bit], ecx
mov [eax+keyboard_device_data.capslock_bit], ecx
mov [eax+keyboard_device_data.scrolllock_bit], ecx
; 3. Look for LED report and bits corresponding to indicators.
; For now, assume that all LEDs are set by the same report.
; 3a. Save registers.
push ebx esi
; 3b. Prepare for loop over output reports: get the first output report.
; If there are no output records, skip step 3;
; default values of led_report and *_bit were set in step 2.
mov edx, [edi+collection.output.first_report]
test edx, edx
jz .led_report_set
.scan_led_report:
; Process one output report.
; 3c. Prepare for loop over field groups in the current report:
; get the first field group.
mov ecx, [edx+report.first_field]
.scan_led_field:
; Process one field group.
; 3d. If there are no more field groups, exit the loop over field groups.
test ecx, ecx
jz .next_led_report
; For now, assume that all LEDs are plain variable fields, not arrays.
; 3e. Ignore array field groups.
test byte [ecx+report_field_group.flags], HID_FIELD_VARIABLE
jz .next_led_field
; 3f. Loop over all fields in the current group.
push [ecx+report_field_group.count]
; esi = pointer to usage of the current field
lea esi, [ecx+report_field_group.common_sizeof]
; ebx = bit position of the current field
mov ebx, [ecx+report_field_group.offset]
; if report is numbered, add extra byte in the start of report
cmp [edx+report.id], 0
jz .scan_led_usage
add ebx, 8
.scan_led_usage:
; for USAGE_LED_*LOCK, store the current bit position in the corresponding field
; and store the current report as the LED report
cmp dword [esi], USAGE_LED_NUMLOCK
jz .numlock
cmp dword [esi], USAGE_LED_CAPSLOCK
jz .capslock
cmp dword [esi], USAGE_LED_SCROLLLOCK
jnz .next_field
.scrolllock:
mov [eax+keyboard_device_data.scrolllock_bit], ebx
jmp @f
.capslock:
mov [eax+keyboard_device_data.capslock_bit], ebx
jmp @f
.numlock:
mov [eax+keyboard_device_data.numlock_bit], ebx
@@:
mov [eax+keyboard_device_data.led_report], edx
.next_field:
add esi, 4
add ebx, [ecx+report_field_group.size]
dec dword [esp]
jnz .scan_led_usage
pop ebx
.next_led_field:
; 3g. Continue loop over field groups: get next field group.
mov ecx, [ecx+report_field_group.next]
jmp .scan_led_field
.next_led_report:
; 3h. If the LED report has been set, break from the loop over reports.
; Otherwise, get the next report and continue if the current report is not
; the last for this collection.
cmp [eax+keyboard_device_data.led_report], 0
jnz .led_report_set
cmp edx, [edi+collection.output.last_report]
mov edx, [edx+report.next]
jnz .scan_led_report
.led_report_set:
; 3i. Restore registers.
pop esi ebx
; 4. Register keyboard in the kernel.
; store pointer to keyboard_device_data in the stack
push eax
; call kernel API
stdcall RegKeyboard, kbd_functions, eax
; restore pointer to keyboard_device_data from the stack,
; putting keyboard handle from API to the stack
xchg eax, [esp]
; put keyboard handle from API from the stack to keyboard_device_data field
pop [eax+keyboard_device_data.handle]
; If failed, free keyboard_device_data and return NULL.
cmp [eax+keyboard_device_data.handle], 0
jz .fail_free
; 5. Return pointer to keyboard_device_data.
.nothing:
ret
.fail_free:
call Kfree
xor eax, eax
ret
endp
 
; This procedure is called when HID layer detects disconnect of a previously
; connected keyboard.
; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
proc keyboard_driver_disconnect
; 1. If an autorepeat timer is active, stop it.
cmp [edi+keyboard_device_data.timer], 0
jz @f
stdcall CancelTimerHS, [edi+keyboard_device_data.timer]
@@:
; 2. Unregister keyboard in the kernel.
stdcall DelKeyboard, [edi+keyboard_device_data.handle]
; We should free data in CloseKeyboard, not here.
ret
endp
 
; This procedure is called when HID layer starts processing a new input packet
; from a keyboard.
; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
proc keyboard_driver_begin_packet
; Nothing to do.
ret
endp
 
; This procedure is called when HID layer processes every non-empty array field group.
; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
; in: ecx = fields count (always nonzero), edx = pointer to fields values
; in: esi -> report_field_group
; out: CF set => group is ok, CF cleared => group should be ignored
proc keyboard_driver_array_overflow?
; The keyboard signals array overflow by filling the entire array with
; USAGE_KBD_ROLLOVER codes.
mov eax, [edx] ; eax = first field in the array
sub eax, USAGE_KBD_ROLLOVER ; eax = 0 if overflow, nonzero otherwise
neg eax ; CF cleared if eax was zero, CF set if eax was nonzero
ret
endp
 
; This procedure is called from HID layer for every field.
; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
; in: ecx = field usage, edx = value, esi -> report_field_group
proc keyboard_driver_input_field
if HID_DUMP_UNCLAIMED
.unclaimed = default_driver_input_field
end if
; 1. Process normal keys:
; from USAGE_KBD_FIRST_KEY to USAGE_KBD_FIRST_KEY + normal_keys_number - 1,
; excluding zeroes in [normal_keys].
; 1a. Test whether usage is in the range.
lea eax, [ecx-USAGE_KBD_FIRST_KEY]
cmp eax, normal_keys_number
jae .not_normal_key
; 1b. If the corresponding entry in [normal_keys] is zero,
; pass this field to the default handler - if HID_DUMP_UNCLAIMED is enabled,
; default handler is default_driver_input_field, otherwise just ignore the field.
cmp [normal_keys + eax], 0
jz .unclaimed
; 1c. Get the scancode.
movzx ecx, [normal_keys + eax]
; 1d. Further actions are slightly different for key press and key release.
; Decide what to do.
test edx, edx
jz .normal_key_released
.normal_key_pressed:
; The key is pressed.
; 1e. Store the last pressed key for autorepeat.
mov [edi+keyboard_device_data.repeatkey], cl
; 1f. Copy bit 7 to CF and send scancode with bit 7 cleared.
btr ecx, 7
call .send_key
; 1g. Stop the previous autorepeat timer, if any.
mov eax, [edi+keyboard_device_data.timer]
test eax, eax
jz @f
stdcall CancelTimerHS, eax
@@:
; 1h. Start the new autorepeat timer with 250 ms initial delay
; and 50 ms subsequent delays.
stdcall TimerHS, 25, 5, autorepeat_timer, edi
mov [edi+keyboard_device_data.timer], eax
if ~HID_DUMP_UNCLAIMED
.unclaimed:
end if
ret
.normal_key_released:
; The key is released.
; 1i. Stop the autorepeat timer if it is autorepeating the released key.
cmp [edi+keyboard_device_data.repeatkey], cl
jnz .no_stop_timer
push ecx
mov [edi+keyboard_device_data.repeatkey], 0
mov eax, [edi+keyboard_device_data.timer]
test eax, eax
jz @f
stdcall CancelTimerHS, eax
mov [edi+keyboard_device_data.timer], 0
@@:
pop ecx
.no_stop_timer:
; 1j. Copy bit 7 to CF and send scancode with bit 7 set.
bts ecx, 7
call .send_key
ret
.not_normal_key:
; 2. USAGE_KBD_NOEVENT is simply a filler for free array fields,
; ignore it.
cmp ecx, USAGE_KBD_NOEVENT
jz .nothing
; 3. Process modifiers: 8 keys starting at USAGE_KBD_LCTRL.
; 3a. Test whether usage is in range.
; If not, we don't know what this field means, so pass it to the default handler.
lea eax, [ecx-USAGE_KBD_LCTRL]
cmp eax, 8
jae .unclaimed
; 3b. Further actions are slightly different for modifier press
; and modifier release. Decide what to do.
test edx, edx
jz .modifier_not_pressed
.modifier_pressed:
; The modifier is pressed.
; 3c. Set the corresponding status bit.
; If it was not set, send the corresponding scancode to the kernel
; with bit 7 cleared.
bts [edi+keyboard_device_data.modifiers], eax
jc @f
movzx ecx, [control_keys+eax]
btr ecx, 7
call .send_key
@@:
.nothing:
ret
.modifier_not_pressed:
; The modifier is not pressed.
; 3d. Clear the correspodning status bit.
; If it was set, send the corresponding scancode to the kernel
; with bit 7 set.
btr [edi+keyboard_device_data.modifiers], eax
jnc @f
movzx ecx, [control_keys+eax]
bts ecx, 7
call .send_key
@@:
ret
 
; Helper procedure. Sends scancode from cl to the kernel.
; If CF is set, precede it with special code 0xE0.
.send_key:
jnc @f
push ecx
mov ecx, 0xE0
call SetKeyboardData
pop ecx
@@:
call SetKeyboardData
ret
endp
 
; This procedure is called when HID layer ends processing a new input packet
; from a keyboard.
; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
proc keyboard_driver_end_packet
; Nothing to do.
ret
endp
 
; Timer callback for SetTimerHS.
proc autorepeat_timer
virtual at esp
dd ? ; return address
.data dd ?
end virtual
; Just resend the last pressed key.
mov eax, [.data]
movzx ecx, [eax+keyboard_device_data.repeatkey]
; Copy bit 7 to CF and send scancode with bit 7 cleared.
btr ecx, 7
call keyboard_driver_input_field.send_key
ret 4
endp
 
; This function is called from the keyboard layer
; when it is safe to free keyboard data.
proc CloseKeyboard
virtual at esp
dd ? ; return address
.device_data dd ?
end virtual
mov eax, [.device_data]
call Kfree
ret 4
endp
 
; This function is called from the keyboard layer
; to update LED state on the keyboard.
proc SetKeyboardLights stdcall uses ebx esi edi, device_data, led_state
locals
size dd ?
endl
; 1. Get the pointer to the LED report.
; If there is no LED report, exit from the function.
mov ebx, [device_data]
mov esi, [ebx+keyboard_device_data.led_report]
test esi, esi
jz .nothing
; 2. Get report size in bytes.
; report.size is size in bits without possible report ID;
; if an ID is assigned, the size is one byte greater.
mov eax, [esi+report.size]
add eax, 7
shr eax, 3
cmp [esi+report.id], 0
jz @f
inc eax
@@:
mov [size], eax
; 3. Allocate memory for report + 8 bytes for setup packet.
; Dword-align size for subsequent rep stosd and bts.
; If failed, exit from the function.
add eax, 8 + 3
and eax, not 3
push eax
call Kmalloc
pop ecx
test eax, eax
jz .nothing
; 4. Zero-initialize output report.
push eax
mov edi, eax
shr ecx, 2
xor eax, eax
rep stosd
pop edi
add edi, 8
; 5. Store report ID, if assigned. If not assigned, that would just write zero
; over zeroes.
mov edx, [esi+report.id]
mov [edi], edx
; 6. Set report bits corresponding to active indicators.
mov eax, [led_state]
test al, 1 ; PS/2 Scroll Lock
jz @f
mov ecx, [ebx+keyboard_device_data.scrolllock_bit]
test ecx, ecx
js @f
bts [edi], ecx
@@:
test al, 2 ; PS/2 Num Lock
jz @f
mov ecx, [ebx+keyboard_device_data.numlock_bit]
test ecx, ecx
js @f
bts [edi], ecx
@@:
test al, 4 ; PS/2 Caps Lock
jz @f
mov ecx, [ebx+keyboard_device_data.capslock_bit]
test ecx, ecx
js @f
bts [edi], ecx
@@:
; 7. Fill setup packet.
shl edx, 16 ; move Report ID to byte 2
or edx, 21h + \ ; Class-specific request to Interface
(9 shl 8) + \ ; SET_REPORT
(2 shl 24) ; Report Type = Output
lea eax, [edi-8]
mov ebx, [ebx+keyboard_device_data.usbdev]
mov dword [eax], edx
mov edx, [size]
shl edx, 16 ; move Size to last word
or edx, [ebx+usb_device_data.interface_number]
mov [eax+4], edx
; 8. Submit output control request.
stdcall USBControlTransferAsync, [ebx+usb_device_data.configpipe], \
eax, edi, [size], after_set_keyboard_lights, ebx, 0
; If failed, free the buffer now.
; If succeeded, the callback will free the buffer.
test eax, eax
jnz .nothing
lea eax, [edi-8]
call Kfree
.nothing:
ret
endp
 
; This procedure is called from the USB subsystem when the request initiated by
; SetKeyboardLights is completed, either successfully or unsuccessfully.
proc after_set_keyboard_lights
virtual at esp
dd ? ; return address
.pipe dd ?
.status dd ?
.buffer dd ?
.length dd ?
.calldata dd ?
end virtual
; Ignore status, just free the buffer allocated by SetKeyboardLights.
mov eax, [.buffer]
sub eax, 8
call Kfree
ret 20
endp
/kernel/branches/Kolibri-acpi/drivers/usbhid/mouse.inc
0,0 → 1,155
; HID mouse driver, part of USBHID driver.
 
; Global constants.
; They are assembled in a macro to separate code and data;
; the code is located at the point of "include 'mouse.inc'",
; the data are collected when workers_globals is instantiated.
macro workers_globals
{
; include global constants from previous workers
workers_globals
align 4
; Callbacks for HID layer.
mouse_driver:
dd mouse_driver_add_device
dd mouse_driver_disconnect
dd mouse_driver_begin_packet
dd mouse_driver_array_overflow?
dd mouse_driver_input_field
dd mouse_driver_end_packet
}
 
; Data that are specific for one mouse device.
struct mouse_device_data
buttons dd ? ; buttons that are currently pressed
dx dd ? ; current x moving
dy dd ? ; current y moving
wheel dd ? ; current wheel moving
hwheel dd ?
ends
 
; This procedure is called when HID layer detects a new mouse.
; in: ebx -> device_data from USB layer, edi -> collection
; out: eax = device-specific data or NULL on error
proc mouse_driver_add_device
; Just allocate memory; no initialization needed.
movi eax, sizeof.mouse_device_data
call Kmalloc
ret
endp
 
; This procedure is called when HID layer detects disconnect of a previously
; connected mouse.
; in: edi -> mouse_device_data (pointer returned from mouse_driver_add_device)
proc mouse_driver_disconnect
; Free the allocated memory.
mov eax, edi
call Kfree
ret
endp
 
; This procedure is called when HID layer starts processing a new input packet
; from a mouse.
; in: edi -> mouse_device_data (pointer returned from mouse_driver_add_device)
proc mouse_driver_begin_packet
; Zero all variables describing the current state.
mov [edi+mouse_device_data.buttons], 0
mov [edi+mouse_device_data.dx], 0
mov [edi+mouse_device_data.dy], 0
mov [edi+mouse_device_data.wheel], 0
mov [edi+mouse_device_data.hwheel], 0
ret
endp
 
; This procedure is called when HID layer processes every non-empty array field group.
; in: edi -> mouse_device_data (pointer returned from mouse_driver_add_device)
; in: ecx = fields count (always nonzero), edx = pointer to fields values
; in: esi -> report_field_group
; out: CF set => array is ok, CF cleared => array should be ignored
proc mouse_driver_array_overflow?
; no array fields, no overflows
stc
ret
endp
 
; This procedure is called from HID layer for every field.
; in: edi -> mouse_device_data (pointer returned from mouse_driver_add_device)
; in: ecx = field usage, edx = value, esi -> report_field_group
proc mouse_driver_input_field
; 1. Determine the handler. We process x/y moving, wheel and up to 32 buttons.
; Pass other fields to the default handler - default_driver_input_field if
; HID_DUMP_UNCLAIMED is enabled, just ignore otherwise.
cmp ecx, USAGE_GD_X
jz .x
cmp ecx, USAGE_GD_Y
jz .y
cmp ecx, USAGE_GD_WHEEL
jz .wheel
cmp ecx, 0xC0238
jz .hwheel
sub ecx, USAGE_BUTTON_PAGE + 1
jb .unclaimed
cmp ecx, 32
jae .unclaimed
; 2. This is a button.
; If a button is pressed, set the corresponding bit in the state.
; If a button is not pressed, do nothing.
test edx, edx
jz @f
bts [edi+mouse_device_data.buttons], ecx
@@:
if ~HID_DUMP_UNCLAIMED
.unclaimed:
end if
ret
if HID_DUMP_UNCLAIMED
.unclaimed:
add ecx, USAGE_BUTTON_PAGE + 1
jmp default_driver_input_field
end if
.x:
; 3. This is x moving. For relative fields, store the value in the state.
; Pass absolute field to the default handler.
test byte [esi+report_field_group.flags], HID_FIELD_RELATIVE
jz .unclaimed
mov [edi+mouse_device_data.dx], edx
ret
.y:
; 4. This is y moving. For relative fields, store the value in the state,
; changing the sign: HID uses "mathematics" scheme with Y axis increasing from
; bottom to top, the kernel expects "programming" PS/2-style with Y axis
; increasing from top to bottom.
; Pass absolute fields to the default handler.
test byte [esi+report_field_group.flags], HID_FIELD_RELATIVE
jz .unclaimed
neg edx
mov [edi+mouse_device_data.dy], edx
ret
.wheel:
; 5. This is wheel event. For relative fields, store the value in the state,
; changing the sign. Pass absolute fields to the default handler.
test byte [esi+report_field_group.flags], HID_FIELD_RELATIVE
jz .unclaimed
neg edx
mov [edi+mouse_device_data.wheel], edx
ret
.hwheel:
test byte [esi+report_field_group.flags], HID_FIELD_RELATIVE
jz .unclaimed
mov [edi+mouse_device_data.hwheel], edx
ret
endp
 
; This procedure is called when HID layer ends processing a new input packet
; from a mouse.
; in: edi -> mouse_device_data (pointer returned from mouse_driver_add_device)
proc mouse_driver_end_packet
; Call the kernel, passing collected state.
stdcall SetMouseData, \
[edi+mouse_device_data.buttons], \
[edi+mouse_device_data.dx], \
[edi+mouse_device_data.dy], \
[edi+mouse_device_data.wheel], \
[edi+mouse_device_data.hwheel]
ret
endp
/kernel/branches/Kolibri-acpi/drivers/usbhid/report.inc
0,0 → 1,1442
; Parser of HID structures: parse HID report descriptor,
; parse/generate input/output/feature reports.
 
; =============================================================================
; ================================= Constants =================================
; =============================================================================
; Usage codes from HID specification
; Generic Desktop usage page
USAGE_GD_POINTER = 10001h
USAGE_GD_MOUSE = 10002h
USAGE_GD_JOYSTICK = 10004h
USAGE_GD_GAMEPAD = 10005h
USAGE_GD_KEYBOARD = 10006h
USAGE_GD_KEYPAD = 10007h
 
USAGE_GD_X = 10030h
USAGE_GD_Y = 10031h
USAGE_GD_Z = 10032h
USAGE_GD_RX = 10033h
USAGE_GD_RY = 10034h
USAGE_GD_RZ = 10035h
USAGE_GD_SLIDER = 10036h
USAGE_GD_DIAL = 10037h
USAGE_GD_WHEEL = 10038h
 
; Keyboard/Keypad usage page
USAGE_KBD_NOEVENT = 70000h
USAGE_KBD_ROLLOVER = 70001h
USAGE_KBD_POSTFAIL = 70002h
USAGE_KBD_FIRST_KEY = 70004h ; this is 'A', actually
USAGE_KBD_LCTRL = 700E0h
USAGE_KBD_LSHIFT = 700E1h
USAGE_KBD_LALT = 700E2h
USAGE_KBD_LWIN = 700E3h
USAGE_KBD_RCTRL = 700E4h
USAGE_KBD_RSHIFT = 700E5h
USAGE_KBD_RALT = 700E6h
USAGE_KBD_RWIN = 700E7h
 
; LED usage page
USAGE_LED_NUMLOCK = 80001h
USAGE_LED_CAPSLOCK = 80002h
USAGE_LED_SCROLLLOCK = 80003h
 
; Button usage page
; First button is USAGE_BUTTON_PAGE+1, second - USAGE_BUTTON_PAGE+2 etc.
USAGE_BUTTON_PAGE = 90000h
 
; Flags for input/output/feature fields
HID_FIELD_CONSTANT = 1 ; if not, then Data field
HID_FIELD_VARIABLE = 2 ; if not, then Array field
HID_FIELD_RELATIVE = 4 ; if not, then Absolute field
HID_FIELD_WRAP = 8
HID_FIELD_NONLINEAR = 10h
HID_FIELD_NOPREFERRED= 20h ; no preferred state
HID_FIELD_HASNULL = 40h ; has null state
HID_FIELD_VOLATILE = 80h ; for output/feature fields
HID_FIELD_BUFBYTES = 100h; buffered bytes
 
; Report descriptor can easily describe gigabytes of (meaningless) data.
; Keep report size reasonable to avoid excessive memory allocations and
; calculation overflows; 1 Kb is more than enough (typical size is 3-10 bytes).
MAX_REPORT_BYTES = 1024
 
; =============================================================================
; ================================ Structures =================================
; =============================================================================
; Every meaningful report field group has one or more associated usages.
; Usages can be individual or joined into continuous ranges.
; This structure describes one range or one individual usage in a large array;
; individual usage is equivalent to a range of length 1.
struct usage_range
offset dd ?
; Sum of range sizes over all previous array items.
; Size of range a equals
; [a + sizeof.usage_range + usage_range.offset] - [a + usage_range.offset].
; The total sum over all array items immediately follows the array,
; this field must be the first so that the formula above works for the last item.
first_usage dd ?
; Usage code for first item in the range.
ends
 
; This structure describes one group of report fields with identical properties.
struct report_field_group
next dd ?
; All field groups in one report are organized in a single-linked list.
; This is the next group in the report or 0 for the last group.
size dd ?
; Size in bits of one field. Cannot be zero or greater than 32.
count dd ? ; field count, cannot be zero
offset dd ? ; offset from report start, in bits
; Following fields are decoded from report descriptor, see HID spec for details.
flags dd ?
logical_minimum dd ?
logical_maximum dd ?
physical_minimum dd ?
physical_maximum dd ?
unit_exponent dd ?
unit dd ?
; Following fields are used to speedup extract_field_value.
mask dd ?
; Bitmask for all data bits except sign bit:
; (1 shl .size) - 1 for unsigned fields, (1 shl (.size-1)) - 1 for signed fields
sign_mask dd ?
; Zero for unsigned fields. Bitmask with sign bit set for signed fields.
common_sizeof rd 0
; Variable and Array field groups differ significantly.
; Variable field groups are simple. There are .count fields, each field has
; predefined Usage, the content of a field is its value. Each field is
; always present in the report. For Variable field groups, we just keep
; additional .count dwords with usages for individual fields.
; Array field groups are complicated. There are .count uniform fields.
; The content of a field determines Usage; Usages which are currently presented
; in the report have value = 1, other Usages have value = 0. The number of
; possible Usages is limited only by field .size; 32-bit field could encode any
; Usage, so it is unreasonable to keep all Usages in the plain array, as with
; Variable fields. However, many unrelated Usages in one group are meaningless,
; so usually possible values are grouped in sequential ranges; number of ranges
; is limited by report descriptor size (max 0xFFFF bytes should contain all
; information, including usage ranges and field descriptions).
; Also, for Array variables we pass changes in state to drivers, not the state
; itself, because sending information about all possible Usages is inpractical;
; so we should remember the previous state in addition to the current state.
; Thus, for Array variables keep the following information, in this order:
; * some members listed below; note that they do NOT exist for Variable groups;
; * array of usage ranges in form of usage_range structures, including
; an additional dword after array described in usage_range structure;
; * allocated memory for current values of the report;
; * values of the previous report.
num_values_prev dd ? ; number of values in the previous report
num_usage_ranges dd ? ; number of usage_range, always nonzero
usages rd 0
ends
 
; This structure describes one report.
; All reports of one type are organized into a single-linked list.
struct report
next dd ? ; pointer to next report of the same type, if any
size dd ? ; total size in bits
first_field dd ? ; pointer to first report_field_group for this report
last_field dd ?
; pointer to last report_field_group for this report, if any;
; address of .first_field, if .first_field is 0
id dd ?
; Report ID, if assigned. Zero otherwise.
top_level_collection dd ? ; top-level collection for this report
ends
 
; This structure describes a set of reports of the same type;
; there are 3 sets (possibly empty), input, output and feature.
struct report_set
data dd ?
; If .numbered is zero, this is zero for the empty set and
; a pointer to the (only) report structure otherwise.
; If .numbered is nonzero, this is a pointer to 256-dword array of pointers
; to reports organized by report ID.
first_report dd ?
; Pointer to the first report or 0 for the empty set.
numbered db ?
; If zero, report IDs are not used, there can be at most one report in the set.
; If nonzero, first byte of the report is report ID.
rb 3 ; padding
ends
 
; This structure describes a range of reports of one type that belong to
; some collection.
struct collection_report_set
first_report dd ?
first_field dd ?
last_report dd ?
last_field dd ?
ends
 
; This structure defines driver callbacks which are used while
; device is active; i.e. all callbacks except add_device.
struct hid_driver_active_callbacks
disconnect dd ?
; Called when an existing HID device is disconnected.
;
; Four following functions are called when a new input packet arrives
; in the following order: .begin_packet, then .input_field several times
; for each input field, interleaved with .array_overflow? for array groups,
; then .end_packet.
begin_packet dd ?
; edi -> driver data
array_overflow? dd ?
; edi -> driver data
; out: CF cleared <=> ignore this array
input_field dd ?
; edi -> driver data, ecx = usage, edx = value
end_packet dd ?
; edi -> driver data
ends
 
; This structure describes one collection.
struct collection
next dd ? ; pointer to the next collection in the same level
; must be the first field
parent dd ? ; pointer to nesting collection
first_child dd ? ; pointer to the first nested collection
last_child dd ? ; pointer to the last nested collection
; or to .first_child, if .first_child is zero
type dd ? ; Application, Physical etc
usage dd ? ; associated Usage code
; Next fields are filled only for top-level collections.
callbacks hid_driver_active_callbacks
driver_data dd ? ; value to be passed as is to driver callbacks
input collection_report_set
output collection_report_set
feature collection_report_set
ends
 
; This structure keeps all data used by the HID layer for one device.
struct hid_data
input report_set
output report_set
feature report_set
first_collection dd ?
ends
 
; This structure defines callbacks required from the driver.
struct hid_driver_callbacks
add_device dd ?
; Called when a new HID device is connected.
active hid_driver_active_callbacks
ends
 
; Two following structures describe temporary data;
; the corresponding objects cease to exist when HID parser completes
; state of Global items
struct global_items
next dd ?
usage_page dd ?
logical_minimum dd ?
logical_maximum dd ?
physical_minimum dd ?
physical_maximum dd ?
unit_exponent dd ?
unit dd ?
report_size dd ?
report_id dd ?
report_count dd ?
ends
 
; one range of Usages
struct usage_list_item
next dd ?
first_usage dd ?
num_usages dd ?
ends
 
; =============================================================================
; =================================== Code ====================================
; =============================================================================
 
macro workers_globals
{
workers_globals
; Jump tables for switch'ing in the code.
align 4
; jump table for two lower bits which encode size of item data
parse_descr_label.fetch_jumps:
dd parse_descr_label.fetch_none ; x0, x4, x8, xC
dd parse_descr_label.fetch_byte ; x1, x5, x9, xD
dd parse_descr_label.fetch_word ; x2, x6, xA, xE
dd parse_descr_label.fetch_dword ; x3, x7, xB, xF
; jump table for two next bits which encode item type
parse_descr_label.type_jumps:
dd parse_descr_label.parse_main
dd parse_descr_label.parse_global
dd parse_descr_label.parse_local
dd parse_descr_label.parse_reserved
; jump table for 4 upper bits in the case of Main item
parse_descr_label.main_jumps:
dd parse_descr_label.input ; 80...83
dd parse_descr_label.output ; 90...93
dd parse_descr_label.collection ; A0...A3
dd parse_descr_label.feature ; B0...B3
dd parse_descr_label.end_collection ; C0...C3
parse_descr_label.num_main_items = ($ - parse_descr_label.main_jumps) / 4
; jump table for 4 upper bits in the case of Global item
parse_descr_label.global_jumps:
dd parse_descr_label.usage_page ; 04...07
dd parse_descr_label.logical_minimum ; 14...17
dd parse_descr_label.logical_maximum ; 24...27
dd parse_descr_label.physical_minimum ; 34...37
dd parse_descr_label.physical_maximum ; 44...47
dd parse_descr_label.unit_exponent ; 54...57
dd parse_descr_label.unit ; 64...67
dd parse_descr_label.report_size ; 74...77
dd parse_descr_label.report_id ; 84...87
dd parse_descr_label.report_count ; 94...97
dd parse_descr_label.push ; A4...A7
dd parse_descr_label.pop ; B4...B7
parse_descr_label.num_global_items = ($ - parse_descr_label.global_jumps) / 4
; jump table for 4 upper bits in the case of Local item
parse_descr_label.local_jumps:
dd parse_descr_label.usage ; 08...0B
dd parse_descr_label.usage_minimum ; 18...1B
dd parse_descr_label.usage_maximum ; 28...2B
dd parse_descr_label.item_parsed ; 38...3B = designator item; ignore
dd parse_descr_label.item_parsed ; 48...4B = designator minimum; ignore
dd parse_descr_label.item_parsed ; 58...5B = designator maximum; ignore
dd parse_descr_label.item_parsed ; 68...6B not assigned
dd parse_descr_label.item_parsed ; 78...7B = string index; ignore
dd parse_descr_label.item_parsed ; 88...8B = string minimum; ignore
dd parse_descr_label.item_parsed ; 98...9B = string maximum; ignore
dd parse_descr_label.delimiter ; A8...AB
parse_descr_label.num_local_items = ($ - parse_descr_label.local_jumps) / 4
}
 
; Local variables for parse_descr.
macro parse_descr_locals
{
cur_item_size dd ? ; encoded size of data for current item
report_ok db ? ; 0 on error, 1 if everything is ok
field_type db ? ; 0/1/2 for input/output/feature fields
rb 2 ; alignment
field_data dd ? ; data for current item when it describes a field group
last_reports rd 3 ; pointers to last input/output/feature records
usage_minimum dd ? ; current value of Usage Minimum
usage_list dd ? ; list head of usage_list_item
usage_tail dd ? ; list tail of usage_list_item
num_usage_ranges dd ? ; number of usage ranges, size of usage_list
delimiter_depth dd ? ; normally 0; 1 inside of Delimiter();
; nested Delimiter()s are not allowed
usage_variant dd ? ; 0 outside of Delimiter()s and for first Usage inside Delimiter(),
; incremented with each new Usage inside Delimiter()
cur_collection dd ? ; current collection
last_collection dd ? ; last top-level collection
}
 
; Parse report descriptor. The caller should provide local variables
; [buffer] = pointer to report descriptor, [length] = length of report descriptor,
; [calldata] = pointer to hid_data (possibly wrapped in a large structure).
macro parse_descr
{
parse_descr_label:
; 1. Initialize.
; 1a. Set some variables to initial values.
xor edi, edi
mov dword [report_ok], edi
mov [usage_list], edi
mov [cur_collection], edi
mov eax, [calldata]
add eax, hid_data.input.first_report
mov [last_reports+0*4], eax
add eax, hid_data.output.first_report - hid_data.input.first_report
mov [last_reports+1*4], eax
add eax, hid_data.feature.first_report - hid_data.output.first_report
mov [last_reports+2*4], eax
add eax, hid_data.first_collection - hid_data.feature.first_report
mov [last_collection], eax
; 1b. Allocate state of global items.
movi eax, sizeof.global_items
call Kmalloc
test eax, eax
jz .memory_error
; 1c. Zero-initialize it and move pointer to edi.
push eax
xchg eax, edi
movi ecx, sizeof.global_items / 4
rep stosd
pop edi
; 1d. Load pointer to data into esi and make [length] point to end of data.
mov esi, [buffer]
add [length], esi
; 2. Clear all local items.
; This is needed in the beginning and after processing any Main item.
.zero_local_items:
mov eax, [usage_list]
@@:
test eax, eax
jz @f
push [eax+usage_list_item.next]
call Kfree
pop eax
jmp @b
@@:
lea ecx, [usage_list]
mov [usage_tail], ecx
mov [ecx], eax
mov [delimiter_depth], eax
mov [usage_variant], eax
mov [usage_minimum], eax
mov [num_usage_ranges], eax
; 3. Parse items until end of data found.
cmp esi, [length]
jae .parse_end
.fetch_next_item:
; --------------------------------- Parse item --------------------------------
; 4. Parse one item.
; 4a. Get item data. eax = first item byte = code+type+size (4+2+2 bits),
; ebx = item data interpreted as unsigned,
; ecx = item data interpreted as signed.
movzx eax, byte [esi]
mov ecx, eax
and ecx, 3
mov [cur_item_size], ecx
jmp dword [.fetch_jumps+ecx*4]
.invalid_report:
mov esi, invalid_report_msg
jmp .end_str
.fetch_none:
xor ebx, ebx
xor ecx, ecx
inc esi
jmp .fetched
.fetch_byte:
add esi, 2
cmp esi, [length]
ja .invalid_report
movzx ebx, byte [esi-1]
movsx ecx, bl
jmp .fetched
.fetch_word:
add esi, 3
cmp esi, [length]
ja .invalid_report
movzx ebx, word [esi-2]
movsx ecx, bx
jmp .fetched
.fetch_dword:
add esi, 5
cmp esi, [length]
ja .invalid_report
mov ebx, dword [esi-4]
mov ecx, ebx
.fetched:
; 4b. Select the branch according to item type.
; For every type, select the concrete handler and go there.
mov edx, eax
shr edx, 2
and edx, 3
shr eax, 4
jmp dword [.type_jumps+edx*4]
; -------------------------------- Main items ---------------------------------
.parse_main:
sub eax, 8
cmp eax, .num_main_items
jae .item_parsed
jmp dword [.main_jumps+eax*4]
; There are 5 Main items.
; Input/Output/Feature items create new field groups in the corresponding report;
; Collection item opens a new collection (possibly nested),
; End Collection item closes the most nested collection.
.output:
mov [field_type], 1
jmp .new_field
.feature:
mov [field_type], 2
jmp .new_field
.input:
mov [field_type], 0
.new_field:
; Create a new field group.
mov [field_data], ebx
movzx ebx, [field_type]
if sizeof.report_set = 12
lea ebx, [ebx*3]
shl ebx, 2
else
err Change the code
end if
add ebx, [calldata]
; 5. Sanity checks: field size and fields count must be nonzero,
; field size cannot be more than 32 bits,
; if field count is more than MAX_REPORT_SIZE * 8, the report would be more than
; MAX_REPORT_SIZE bytes, so it is invalid too.
; More precise check for size occurs later; this check only guarantees that
; there will be no overflows during subsequent calculations.
cmp [edi+global_items.report_size], 0
jz .invalid_report
cmp [edi+global_items.report_size], 32
ja .invalid_report
; There are devices with Report Count(0) + Input(Constant Variable),
; zero-length padding. Thus, do not consider descriptors with Report Count(0)
; as invalid; instead, just ignore fields with Report Count(0).
cmp [edi+global_items.report_count], 0
jz .zero_local_items
cmp [edi+global_items.report_count], MAX_REPORT_BYTES * 8
ja .invalid_report
; 6. Get the pointer to the place for the corresponding report in ebx.
; 6a. If report ID is not assigned, ebx already points to report_set.data,
; so go to 7.
cmp [edi+global_items.report_id], 0
jz .report_ptr_found
; 6b. If table for reports was already allocated,
; go to 6d skipping the next substep.
cmp [ebx+report_set.numbered], 0
jnz .report_set_allocated
; 6c. This is the first report with ID;
; allocate and zero-initialize table for reports.
; Note: it is incorrect but theoretically possible that some fields were
; already allocated in report without ID; if so, abort processing with error.
cmp [ebx+report_set.data], 0
jnz .invalid_report
mov eax, 256*4
call Kmalloc
test eax, eax
jz .memory_error
mov [ebx+report_set.data], eax
inc [ebx+report_set.numbered]
push edi
mov edi, eax
mov ecx, 256
xor eax, eax
rep stosd
pop edi
; 6d. Report ID is assigned, report table is allocated,
; get the pointer to the corresponding item in the report table.
.report_set_allocated:
mov ebx, [ebx+report_set.data]
mov ecx, [edi+global_items.report_id]
lea ebx, [ebx+ecx*4]
; 7. If the field group is the first one in the report,
; allocate and initialize report without fields.
.report_ptr_found:
; 7a. Check whether the report has been allocated.
cmp dword [ebx], 0
jnz .report_allocated
; 7b. Allocate.
movi eax, sizeof.report
call Kmalloc
test eax, eax
jz .memory_error
; 7c. Initialize.
xor edx, edx
lea ecx, [eax+report.first_field]
mov [ebx], eax
mov [eax+report.next], edx
mov [eax+report.size], edx
mov [ecx], edx
mov [eax+report.last_field], ecx
mov [eax+report.top_level_collection], edx
mov ecx, [edi+global_items.report_id]
mov [eax+report.id], ecx
; 7d. Append to the overall list of reports.
movzx edx, [field_type]
lea edx, [last_reports+edx*4]
mov ecx, [edx]
mov [edx], eax
mov [ecx], eax
.report_allocated:
mov ebx, [ebx]
; ebx points to an already existing report; add new field.
; 8. Calculate total size of the group and
; check that the new group would not overflow the report.
mov eax, [edi+global_items.report_size]
mul [edi+global_items.report_count]
mov ecx, [ebx+report.size]
add ecx, eax
cmp ecx, MAX_REPORT_BYTES * 8
ja .invalid_report
; 9. If there are no usages for this group, this is padding;
; add it's size to total report size and stop processing.
cmp [num_usage_ranges], 0
jz .padding
; 10. Allocate memory for the group: this includes field group structure
; and additional fields depending on field type.
; See comments in report_field_group structure.
push eax
mov edx, [edi+global_items.report_count]
lea eax, [report_field_group.common_sizeof+edx*4]
test byte [field_data], HID_FIELD_VARIABLE
jnz @f
lea eax, [eax+edx*4]
mov edx, [num_usage_ranges]
lea eax, [eax+edx*sizeof.usage_range+4]
@@:
call Kmalloc
pop edx
test eax, eax
jz .memory_error
; 11. Update report data.
; Field offset is the current report size;
; get the current report size and update report size.
; Also store the pointer to new field in the previous last field
; and update the last field.
mov ecx, [ebx+report.last_field]
xadd [ebx+report.size], edx
mov [ebx+report.last_field], eax
mov [ecx], eax
; 12. Initialize field data: offset was calculated in the previous step,
; copy other characteristics from global_items data,
; calculate .mask and .sign_mask.
mov [eax+report_field_group.offset], edx
xor edx, edx
mov [eax+report_field_group.next], edx
mov [eax+report_field_group.sign_mask], edx
inc edx
mov ecx, [edi+global_items.report_size]
mov [eax+report_field_group.size], ecx
shl edx, cl
cmp [edi+global_items.logical_minimum], 0
jge .unsigned
shr edx, 1
mov [eax+report_field_group.sign_mask], edx
.unsigned:
dec edx
mov [eax+report_field_group.mask], edx
mov ecx, [edi+global_items.report_count]
mov [eax+report_field_group.count], ecx
mov ecx, [field_data]
mov [eax+report_field_group.flags], ecx
irps field, logical_minimum logical_maximum physical_minimum physical_maximum unit_exponent unit
\{
mov ecx, [edi+global_items.\#field]
mov [eax+report_field_group.\#field], ecx
\}
; 13. Update the current collection; nesting collections will be updated by
; end-of-collection handler.
movzx edx, [field_type]
if sizeof.collection_report_set = 16
shl edx, 4
else
err Change the code
end if
mov ecx, [cur_collection]
test ecx, ecx
jz .no_collection
lea ecx, [ecx+collection.input+edx]
mov [ecx+collection_report_set.last_report], ebx
mov [ecx+collection_report_set.last_field], eax
cmp [ecx+collection_report_set.first_field], 0
jnz .no_collection
mov [ecx+collection_report_set.first_report], ebx
mov [ecx+collection_report_set.first_field], eax
.no_collection:
; 14. Transform usage ranges. The target format depends on field type.
test byte [eax+report_field_group.flags], HID_FIELD_VARIABLE
jz .transform_usages_for_array
; For Variable field groups, expand all ranges to array with .count Usages.
; If total number of Usages in all ranges is too large, ignore excessive.
; If total number of Usages in all ranges is too small, duplicate the last
; Usage up to .count Usages (e.g. group of several indicators can have one usage
; "Generic Indicator" assigned to all fields).
mov ecx, [eax+report_field_group.count]
mov ebx, [usage_list]
.next_usage_range_for_variable:
mov edx, [ebx+usage_list_item.first_usage]
push [ebx+usage_list_item.num_usages]
.next_usage_for_variable:
mov [eax+report_field_group.common_sizeof], edx
dec ecx
jz @f
add eax, 4
inc edx
dec dword [esp]
jnz .next_usage_for_variable
dec edx
inc dword [esp]
cmp [ebx+usage_list_item.next], 0
jz .next_usage_for_variable
pop edx
mov ebx, [ebx+usage_list_item.next]
jmp .next_usage_range_for_variable
@@:
pop ebx
jmp .zero_local_items
.transform_usages_for_array:
; For Array field groups, leave ranges unexpanded, but recode in the form
; more convenient to value lookup, see comments in report_field_group structure.
mov ecx, [num_usage_ranges]
mov [eax+report_field_group.num_usage_ranges], ecx
and [eax+report_field_group.num_values_prev], 0
mov ecx, [usage_list]
xor ebx, ebx
@@:
mov edx, [ecx+usage_list_item.first_usage]
mov [eax+report_field_group.usages+usage_range.offset], ebx
add ebx, [ecx+usage_list_item.num_usages]
jc .invalid_report
mov [eax+report_field_group.usages+usage_range.first_usage], edx
add eax, sizeof.usage_range
mov ecx, [ecx+usage_list_item.next]
test ecx, ecx
jnz @b
mov [eax+report_field_group.usages], ebx
; New field is initialized.
jmp .zero_local_items
.padding:
mov [ebx+report.size], ecx
jmp .zero_local_items
; Create a new collection, nested in the current one.
.collection:
; Actions are quite straightforward:
; allocate, zero-initialize, update parent, if there is one,
; make it current.
movi eax, sizeof.collection
call Kmalloc
test eax, eax
jz .memory_error
push eax edi
movi ecx, sizeof.collection / 4
xchg edi, eax
xor eax, eax
rep stosd
pop edi eax
mov edx, [cur_collection]
mov [eax+collection.parent], edx
lea ecx, [last_collection]
test edx, edx
jz .no_parent
lea ecx, [edx+collection.last_child]
.no_parent:
mov edx, [ecx]
mov [ecx], eax
mov [edx], eax
lea ecx, [eax+collection.first_child]
; In theory, there must be at least one usage.
; In practice, some nested collections don't have any. Use zero in this case.
mov edx, [usage_list]
test edx, edx
jz @f
mov edx, [edx+usage_list_item.first_usage]
@@:
mov [eax+collection.last_child], ecx
mov [eax+collection.type], ebx
mov [eax+collection.usage], edx
mov [cur_collection], eax
jmp .zero_local_items
; Close the current collection.
.end_collection:
; There must be an opened collection.
mov eax, [cur_collection]
test eax, eax
jz .invalid_report
; Make parent collection the current one.
mov edx, [eax+collection.parent]
mov [cur_collection], edx
; Add field range of the closing collection to field range for nesting collection,
; if there is one.
test edx, edx
jz .zero_local_items
push 3 ; for each type: input, output, feature
.update_ranges:
mov ecx, [eax+collection.input.last_report]
test ecx, ecx
jz .no_fields
mov [edx+collection.input.last_report], ecx
mov ecx, [eax+collection.input.last_field]
mov [edx+collection.input.last_field], ecx
cmp [edx+collection.input.first_report], 0
jnz .no_fields
mov ecx, [eax+collection.input.first_report]
mov [edx+collection.input.first_report], ecx
mov ecx, [eax+collection.input.first_field]
mov [edx+collection.input.first_field], ecx
.no_fields:
add eax, sizeof.collection_report_set
add edx, sizeof.collection_report_set
dec dword [esp]
jnz .update_ranges
pop eax
jmp .zero_local_items
; ------------------------------- Global items --------------------------------
.parse_global:
cmp eax, .num_global_items
jae .item_parsed
jmp dword [.global_jumps+eax*4]
; For most global items, just store the value in the current global_items structure.
; Note 1: Usage Page will be used for upper word of Usage[| Minimum|Maximum], so
; shift it in advance.
; Note 2: the HID specification allows both signed and unsigned values for
; logical and physical minimum/maximum, but does not give a method to distinguish.
; Thus, hope that minimum comes first, parse the minimum as signed value always,
; if it is less than zero, assume signed values, otherwise assume unsigned values.
; This covers both common cases Minimum(0)/Maximum(FF) and Minimum(-7F)/Maximum(7F).
; Note 3: zero value for Report ID is forbidden by the HID specification.
; It is quite convenient, we use report_id == 0 for reports without ID.
.usage_page:
shl ebx, 16
mov [edi+global_items.usage_page], ebx
jmp .item_parsed
.logical_minimum:
mov [edi+global_items.logical_minimum], ecx
jmp .item_parsed
.logical_maximum:
cmp [edi+global_items.logical_minimum], 0
jge @f
mov ebx, ecx
@@:
mov [edi+global_items.logical_maximum], ebx
jmp .item_parsed
.physical_minimum:
mov [edi+global_items.physical_minimum], ecx
jmp .item_parsed
.physical_maximum:
cmp [edi+global_items.physical_maximum], 0
jge @f
mov ebx, ecx
@@:
mov [edi+global_items.physical_maximum], ebx
jmp .item_parsed
.unit_exponent:
mov [edi+global_items.unit_exponent], ecx
jmp .item_parsed
.unit:
mov [edi+global_items.unit], ebx
jmp .item_parsed
.report_size:
mov [edi+global_items.report_size], ebx
jmp .item_parsed
.report_id:
test ebx, ebx
jz .invalid_report
cmp ebx, 0x100
jae .invalid_report
mov [edi+global_items.report_id], ebx
jmp .item_parsed
.report_count:
mov [edi+global_items.report_count], ebx
jmp .item_parsed
; Two special global items: Push/Pop.
.push:
; For Push, allocate new global_items structure,
; initialize from the current one and make it current.
movi eax, sizeof.global_items
call Kmalloc
test eax, eax
jz .memory_error
push esi eax
movi ecx, sizeof.global_items / 4
mov esi, edi
xchg eax, edi
rep movsd
pop edi esi
mov [edi+global_items.next], eax
jmp .item_parsed
.pop:
; For Pop, restore the last global_items structure and free the current one.
mov eax, [edi+global_items.next]
test eax, eax
jz .invalid_report
push eax
xchg eax, edi
call Kfree
pop edi
jmp .item_parsed
; -------------------------------- Local items --------------------------------
.parse_local:
cmp eax, .num_local_items
jae .item_parsed
jmp dword [.local_jumps+eax*4]
.usage:
; Usage tag.
; If length is 0, 1, 2 bytes, append the global item Usage Page.
cmp [cur_item_size], 2
ja @f
or ebx, [edi+global_items.usage_page]
@@:
; If inside Delimiter(), ignore everything except the first tag.
cmp [delimiter_depth], 0
jz .usage.write
inc [usage_variant]
cmp [usage_variant], 1
jnz .item_parsed
.usage.write:
; Add new range with start = item data and length = 1.
mov [usage_minimum], ebx
push 1
.new_usage:
movi eax, sizeof.usage_list_item
call Kmalloc
pop edx
test eax, eax
jz .memory_error
inc [num_usage_ranges]
mov ecx, [usage_minimum]
and [eax+usage_list_item.next], 0
mov [eax+usage_list_item.first_usage], ecx
mov [eax+usage_list_item.num_usages], edx
mov ecx, [usage_tail]
mov [usage_tail], eax
mov [ecx], eax
jmp .item_parsed
.usage_minimum:
; Usage Minimum tag. Just store in the local var.
; If length is 0, 1, 2 bytes, append the global item Usage Page.
cmp [cur_item_size], 2
ja @f
or ebx, [edi+global_items.usage_page]
@@:
mov [usage_minimum], ebx
jmp .item_parsed
.usage_maximum:
; Usage Maximum tag.
; If length is 0, 1, 2 bytes, append the global item Usage Page.
cmp [cur_item_size], 2
ja @f
or ebx, [edi+global_items.usage_page]
@@:
; Meaningless inside Delimiter().
cmp [delimiter_depth], 0
jnz .invalid_report
; Add new range with start = saved Usage Minimum and
; length = Usage Maximum - Usage Minimum + 1.
sub ebx, [usage_minimum]
inc ebx
push ebx
jmp .new_usage
.delimiter:
; Delimiter tag.
test ebx, ebx
jz .delimiter.close
; Delimiter(Opened).
; Store that we are inside Delimiter(),
; say a warning that only preferred Usage will be used.
cmp [delimiter_depth], 0
jnz .invalid_report
inc [delimiter_depth]
push esi
mov esi, delimiter_note
call SysMsgBoardStr
pop esi
jmp .item_parsed
.delimiter.close:
; Delimiter(Closed).
; Store that we are not inside Delimiter() anymore.
dec [delimiter_depth]
js .invalid_report
and [usage_variant], 0
jmp .item_parsed
.parse_reserved:
; Ignore reserved items, except that tag 0xFE means long item
; with first data byte = length of additional data,
; second data byte = long item tag. No long items are defined yet,
; so just skip them.
cmp eax, 0xF
jnz .item_parsed
cmp [cur_item_size], 2
jnz .item_parsed
movzx ecx, bl
add esi, ecx
cmp esi, [length]
ja .invalid_report
.item_parsed:
cmp esi, [length]
jb .fetch_next_item
.parse_end:
;-------------------------------- End of parsing ------------------------------
; If there are opened collections, it is invalid report.
cmp [cur_collection], 0
jnz .invalid_report
; There must be at least one input field.
mov eax, [calldata]
add eax, hid_data.input.first_report
cmp [last_reports+0*4], eax
jz .invalid_report
; Everything is ok.
inc [report_ok]
jmp .end
.memory_error:
mov esi, nomemory_msg
.end_str:
call SysMsgBoardStr
.end:
; Free all global_items structures.
test edi, edi
jz @f
push [edi+global_items.next]
xchg eax, edi
call Kfree
pop edi
jmp .end
@@:
; Free the last Usage list, if any.
mov eax, [usage_list]
@@:
test eax, eax
jz @f
push [eax+usage_list_item.next]
call Kfree
pop eax
jmp @b
@@:
}
 
; Assign drivers to top-level HID collections.
; The caller should provide ebx = pointer to hid_data and a local variable
; [has_driver], it will be initialized with 0 if no driver is present.
macro postprocess_descr
{
postprocess_report_label:
; Assign drivers to top-level collections.
; Use mouse driver for Usage(GenericDesktop:Mouse),
; use keyboard driver for Usage(GenericDesktop:Keyboard)
; and Usage(GenericDesktop:Keypad)
; 1. Prepare for the loop: get the pointer to the first collection,
; store that no drivers were assigned yet.
mov edi, [ebx+hid_data.first_collection]
if ~HID_DUMP_UNCLAIMED
mov [has_driver], 0
end if
.next_collection:
; 2. Test whether there is a collection to test; if no, break from the loop.
test edi, edi
jz .postprocess_done
; 3. Get pointer to driver callbacks depending on [collection.usage].
; If [collection.usage] is unknown, use default driver if HID_DUMP_UNCLAIMED
; and do not assign a driver otherwise.
mov esi, mouse_driver
cmp [edi+collection.usage], USAGE_GD_MOUSE
jz .has_driver
mov esi, keyboard_driver
cmp [edi+collection.usage], USAGE_GD_KEYBOARD
jz .has_driver
cmp [edi+collection.usage], USAGE_GD_KEYPAD
jz .has_driver
if HID_DUMP_UNCLAIMED
mov esi, default_driver
else
xor esi, esi
end if
; 4. If no driver is assigned (possible only if not HID_DUMP_UNCLAIMED),
; go to 7 with driver data = 0;
; other code uses this as a sign that driver callbacks should not be called.
.has_driver:
xor eax, eax
if ~HID_DUMP_UNCLAIMED
test esi, esi
jz .set_driver
end if
; 5. Notify the driver about new device.
call [esi+hid_driver_callbacks.add_device]
; 6. If the driver has returned non-zero driver data,
; store that is an assigned driver.
; Otherwise, if HID_DUMP_UNCLAIMED, try to assign the default driver.
if HID_DUMP_UNCLAIMED
test eax, eax
jnz .set_driver
mov esi, default_driver
call [esi+hid_driver_callbacks.add_device]
else
test eax, eax
jz @f
mov [has_driver], 1
jmp .set_driver
@@:
xor esi, esi
end if
.set_driver:
; 7. Store driver data. If a driver is assigned, copy driver callbacks.
mov [edi+collection.driver_data], eax
test esi, esi
jz @f
push edi
lodsd ; skip hid_driver_callbacks.add_device
add edi, collection.callbacks
repeat sizeof.hid_driver_active_callbacks / 4
movsd
end repeat
pop edi
@@:
; 8. Store pointer to the collection in all input reports belonging to it.
; Note that the HID spec requires that reports should not cross top-level collections.
mov eax, [edi+collection.input.first_report]
test eax, eax
jz .reports_processed
.next_report:
mov [eax+report.top_level_collection], edi
cmp eax, [edi+collection.input.last_report]
mov eax, [eax+report.next]
jnz .next_report
.reports_processed:
mov edi, [edi+collection.next]
jmp .next_collection
.postprocess_done:
}
 
; Cleanup all resources allocated during parse_descr and postprocess_descr.
; Called when the corresponding device is disconnected
; with ebx = pointer to hid_data.
macro hid_cleanup
{
; 1. Notify all assigned drivers about disconnect.
; Loop over all top-level collections and call callbacks.disconnect,
; if a driver is assigned.
mov esi, [ebx+hid_data.first_collection]
.notify_drivers:
test esi, esi
jz .notify_drivers_done
mov edi, [esi+collection.driver_data]
test edi, edi
jz @f
call [esi+collection.callbacks.disconnect]
@@:
mov esi, [esi+collection.next]
jmp .notify_drivers
.notify_drivers_done:
; 2. Free all collections.
mov esi, [ebx+hid_data.first_collection]
.free_collections:
test esi, esi
jz .collections_done
; If a collection has childen, make it forget about them,
; kill all children; after last child is killed, return to
; the collection as a parent; this time, it will appear
; as childless, so it will be killed after children.
mov eax, [esi+collection.first_child]
test eax, eax
jz .no_children
and [esi+collection.first_child], 0
xchg esi, eax
jmp .free_collections
.no_children:
; If a collection has no children (maybe there were no children at all,
; maybe all children were already killed), kill it and proceed either to
; next sibling (if any) or to the parent.
mov eax, [esi+collection.next]
test eax, eax
jnz @f
mov eax, [esi+collection.parent]
@@:
xchg eax, esi
call Kfree
jmp .free_collections
.collections_done:
; 3. Free all three report sets.
push 3
lea esi, [ebx+hid_data.input]
; For every report set, loop over all reports,
; for every report free all field groups, then free report itself.
; When all reports in one set have been freed, free also report list table,
; if there is one (reports are numbered).
.report_set_loop:
mov edi, [esi+report_set.first_report]
.report_loop:
test edi, edi
jz .report_done
mov eax, [edi+report.first_field]
.field_loop:
test eax, eax
jz .field_done
push [eax+report_field_group.next]
call Kfree
pop eax
jmp .field_loop
.field_done:
mov eax, [edi+report.next]
xchg eax, edi
call Kfree
jmp .report_loop
.report_done:
cmp [esi+report_set.numbered], 0
jz @f
mov eax, [esi+report_set.data]
call Kfree
@@:
add esi, sizeof.report_set
dec dword [esp]
jnz .report_set_loop
pop eax
}
 
; Helper for parse_input. Extracts value of one field.
; in: esi -> report_field_group
; in: eax = offset in bits from report start
; in: report -> report data
; out: edx = value
; Note: it can read one dword past report data.
macro extract_field_value report
{
mov ecx, eax
shr eax, 5
shl eax, 2
add eax, report
and ecx, 31
mov edx, [eax]
mov eax, [eax+4]
shrd edx, eax, cl
mov ecx, [esi+report_field_group.sign_mask]
and ecx, edx
and edx, [esi+report_field_group.mask]
sub edx, ecx
}
 
; Local variables for parse_input.
macro parse_input_locals
{
count_inside_group dd ?
; Number of fields left in the current field.
field_offset dd ?
; Offset of the current field from report start, in bits.
field_range_size dd ?
; Size of range with valid values, Logical Maximum - Logical Minimum + 1.
cur_usage dd ?
; Pointer to current usage for Variable field groups.
num_values dd ?
; Number of values in the current instantiation of Array field group.
values_base dd ?
; Pointer to memory allocated for array with current values.
values_prev dd ?
; Pointer to memory allocated for array with previous values.
values_cur_ptr dd ?
; Pointer to the next value in [values_base] array.
values_end dd ?
; End of data in array with current values.
values_prev_ptr dd ?
; Pointer to the next value in [values_prev_ptr] array.
values_prev_end dd ?
; End of data in array with previous values.
}
 
; Parse input report. The caller should provide esi = pointer to report,
; local variables parse_input_locals and [buffer] = report data.
macro parse_input
{
; 1. Ignore the report if there is no driver for it.
mov ebx, [esi+report.top_level_collection]
mov edi, [ebx+collection.driver_data]
test edi, edi
jz .done
; 2. Notify the driver that a new packet arrived.
call [ebx+collection.callbacks.begin_packet]
; Loop over all field groups.
; Report without fields is meaningless, but theoretically possible:
; parse_descr does not create reports of zero size, but
; a report can consist of "padding" fields without usages and have
; no real fields.
mov esi, [esi+report.first_field]
test esi, esi
jz .packet_processed
.field_loop:
; 3. Prepare for group handling: initialize field offset, fields count
; and size of range for valid values.
mov eax, [esi+report_field_group.offset]
mov [field_offset], eax
mov ecx, [esi+report_field_group.count]
mov [count_inside_group], ecx
mov eax, [esi+report_field_group.logical_maximum]
inc eax
sub eax, [esi+report_field_group.logical_minimum]
mov [field_range_size], eax
; 4. Select handler. Variable and Array groups are handled entirely differently;
; for Variable groups, advance to 5, for Array groups, go to 6.
test byte [esi+report_field_group.flags], HID_FIELD_VARIABLE
jz .array_field
; 5. Variable groups. They are simple. Loop over all .count fields,
; for every field extract the value and get the next usage,
; if the value is within valid range, call the driver.
lea eax, [esi+report_field_group.common_sizeof]
mov [cur_usage], eax
.variable_data_loop:
mov eax, [field_offset]
extract_field_value [buffer] ; -> edx
mov ecx, [cur_usage]
mov ecx, [ecx]
call [ebx+collection.callbacks.input_field]
add [cur_usage], 4
mov eax, [esi+report_field_group.size]
add [field_offset], eax
dec [count_inside_group]
jnz .variable_data_loop
; Variable group is processed; go to 12.
jmp .field_done
.array_field:
; Array groups. They are complicated.
; 6. Array group: extract all values in one array.
; memory was allocated during group creation, use it
; 6a. Prepare: get data pointer, initialize num_values with zero.
mov eax, [esi+report_field_group.num_usage_ranges]
lea edx, [esi+report_field_group.usages+eax*sizeof.usage_range+4]
mov eax, [esi+report_field_group.count]
mov [values_cur_ptr], edx
mov [values_base], edx
lea edx, [edx+ecx*4]
mov [values_prev], edx
mov [values_prev_ptr], edx
mov [num_values], 0
; 6b. Start loop for every field. Note that there must be at least one field,
; parse_descr does not allow .count == 0.
.array_getval_loop:
; 6c. Extract the value of the current field.
mov eax, [field_offset]
extract_field_value [buffer] ; -> edx
; 6d. Transform the value to the usage with binary search in array of
; usage_ranges. started at [esi+report_field_group.usages]
; having [esi+report_field_group.num_usage_ranges] items.
; Ignore items outside of valid range.
sub edx, [esi+report_field_group.logical_minimum]
cmp edx, [field_range_size]
jae .array_skip_item
; If there are too few usages, use last of them.
mov ecx, [esi+report_field_group.num_usage_ranges] ; upper bound
xor eax, eax ; lower bound
cmp edx, [esi+report_field_group.usages+ecx*sizeof.usage_range+usage_range.offset]
jae .array_last_usage
; loop invariant: usages[eax].offset <= edx < usages[ecx].offset
.array_find_usage:
lea edi, [eax+ecx]
shr edi, 1
cmp edi, eax
jz .array_found_usage_range
cmp edx, [esi+report_field_group.usages+edi*sizeof.usage_range+usage_range.offset]
jae .update_low
mov ecx, edi
jmp .array_find_usage
.update_low:
mov eax, edi
jmp .array_find_usage
.array_last_usage:
lea eax, [ecx-1]
mov edx, [esi+report_field_group.usages+ecx*sizeof.usage_range+usage_range.offset]
dec edx
.array_found_usage_range:
sub edx, [esi+report_field_group.usages+eax*sizeof.usage_range+usage_range.offset]
add edx, [esi+report_field_group.usages+eax*sizeof.usage_range+usage_range.first_usage]
; 6e. Store the usage, advance data pointer, continue loop started at 6b.
mov eax, [values_cur_ptr]
mov [eax], edx
add [values_cur_ptr], 4
inc [num_values]
.array_skip_item:
mov eax, [esi+report_field_group.size]
add [field_offset], eax
dec [count_inside_group]
jnz .array_getval_loop
; 7. Array group: ask driver about array overflow.
; If driver says that the array is invalid, stop processing this group
; (in particular, do not update previous values).
mov ecx, [num_values]
test ecx, ecx
jz .duplicates_removed
mov edx, [values_base]
mov edi, [ebx+collection.driver_data]
call [ebx+collection.callbacks.array_overflow?]
jnc .field_done
; 8. Array group: sort the array with current values.
push esi
mov ecx, [num_values]
mov edx, [values_base]
call sort
pop esi
; 9. Array group: remove duplicates.
cmp [num_values], 1
jbe .duplicates_removed
mov eax, [values_base]
mov edx, [eax]
add eax, 4
mov ecx, eax
.duplicates_loop:
cmp edx, [eax]
jz @f
mov edx, [eax]
mov [ecx], edx
add ecx, 4
@@:
add eax, 4
cmp eax, [values_cur_ptr]
jb .duplicates_loop
mov [values_cur_ptr], ecx
sub ecx, [values_base]
shr ecx, 2
mov [num_values], ecx
.duplicates_removed:
; 10. Array group: compare current and previous values,
; call driver for differences.
mov edi, [ebx+collection.driver_data]
mov eax, [values_cur_ptr]
mov [values_end], eax
mov eax, [values_base]
mov [values_cur_ptr], eax
mov eax, [esi+report_field_group.num_values_prev]
shl eax, 2
add eax, [values_prev]
mov [values_prev_end], eax
.find_common:
mov eax, [values_cur_ptr]
cmp eax, [values_end]
jae .cur_done
mov ecx, [eax]
mov eax, [values_prev_ptr]
cmp eax, [values_prev_end]
jae .prev_done
mov edx, [eax]
cmp ecx, edx
jb .advance_cur
ja .advance_prev
; common item in both arrays; ignore
add [values_cur_ptr], 4
add [values_prev_ptr], 4
jmp .find_common
.advance_cur:
; item is present in current array but not in previous;
; call the driver with value = 1
add [values_cur_ptr], 4
mov edx, 1
call [ebx+collection.callbacks.input_field]
jmp .find_common
.advance_prev:
; item is present in previous array but not in current;
; call the driver with value = 0
add [values_prev_ptr], 4
mov ecx, edx
xor edx, edx
call [ebx+collection.callbacks.input_field]
jmp .find_common
.prev_done:
; for all items which are left in current array
; call the driver with value = 1
mov eax, [values_cur_ptr]
@@:
add [values_cur_ptr], 4
mov ecx, [eax]
mov edx, 1
call [ebx+collection.callbacks.input_field]
mov eax, [values_cur_ptr]
cmp eax, [values_end]
jb @b
jmp .copy_array
.cur_done:
; for all items which are left in previous array
; call the driver with value = 0
mov eax, [values_prev_ptr]
add [values_prev_ptr], 4
cmp eax, [values_prev_end]
jae @f
mov ecx, [eax]
xor edx, edx
call [ebx+collection.callbacks.input_field]
jmp .cur_done
@@:
.copy_array:
; 11. Array group: copy current values to previous values.
push esi edi
mov ecx, [num_values]
mov [esi+report_field_group.num_values_prev], ecx
mov esi, [values_base]
mov edi, [values_prev]
rep movsd
pop edi esi
; 12. Field group is processed. Repeat with the next group, if any.
.field_done:
mov esi, [esi+report_field_group.next]
test esi, esi
jnz .field_loop
.packet_processed:
; 13. Packet is processed, notify the driver.
call [ebx+collection.callbacks.end_packet]
}
/kernel/branches/Kolibri-acpi/drivers/usbhid/sort.inc
0,0 → 1,60
; Sort array of unsigned dwords in non-decreasing order.
; ecx = array size, edx = array pointer.
; Destroys eax, ecx, esi, edi.
sort:
test ecx, ecx
jz .done
mov eax, ecx
@@:
push eax
call .restore
pop eax
dec eax
jnz @b
@@:
cmp ecx, 1
jz .done
mov esi, 1
mov edi, ecx
call .exchange
dec ecx
mov eax, 1
call .restore
jmp @b
.done:
ret
 
.exchange:
push eax ecx
mov eax, [edx+esi*4-4]
mov ecx, [edx+edi*4-4]
mov [edx+esi*4-4], ecx
mov [edx+edi*4-4], eax
pop ecx eax
ret
 
.restore:
lea esi, [eax+eax]
cmp esi, ecx
ja .doner
mov edi, [edx+eax*4-4]
cmp [edx+esi*4-4], edi
ja .need_xchg
cmp esi, ecx
jae .doner
mov edi, [edx+eax*4-4]
cmp [edx+esi*4], edi
jbe .doner
.need_xchg:
cmp esi, ecx
jz .do_xchg
mov edi, [edx+esi*4-4]
cmp [edx+esi*4], edi
sbb esi, -1
.do_xchg:
mov edi, eax
call .exchange
mov eax, esi
jmp .restore
.doner:
ret
/kernel/branches/Kolibri-acpi/drivers/usbhid/unclaimed.inc
0,0 → 1,60
; HID default driver, part of USBHID driver.
; Present only if compile-time setting HID_DUMP_UNCLAIMED is on.
; Active for those devices when we do not have a specialized driver.
; Just dumps everything to the debug board.
 
if HID_DUMP_UNCLAIMED
; Global constants.
; They are assembled in a macro to separate code and data;
; the code is located at the point of "include 'unclaimed.inc'",
; the data are collected when workers_globals is instantiated.
macro workers_globals
{
; include global constants from previous workers
workers_globals
align 4
; Callbacks for HID layer.
default_driver:
dd default_driver_add_device
dd default_driver_disconnect
dd default_driver_begin_packet
dd default_driver_array_overflow?
dd default_driver_input_field
dd default_driver_end_packet
}
; This procedure is called when HID layer detects a new driverless device.
; in: ebx -> usb_device_data, edi -> collection
; out: eax = device-specific data or NULL on error
default_driver_add_device:
; just return something nonzero, no matter what
xor eax, eax
inc eax
ret
; This procedure is called when HID layer processes every non-empty array field group.
; in: edi -> keyboard_device_data (pointer returned from keyboard_driver_add_device)
; in: ecx = fields count (always nonzero), edx = pointer to fields values
; in: esi -> report_field_group
; out: CF set => group is ok, CF cleared => group should be ignored
default_driver_array_overflow?:
; parse everything
stc
ret
; This procedure is called from HID layer for every field.
; in: ecx = field usage, edx = value, esi -> report_field_group
default_driver_input_field:
; Do not dump zero values in Variable fields,
; they are present even if the corresponding control is inactive.
test edx, edx
jnz @f
test byte [esi+report_field_group.flags], HID_FIELD_VARIABLE
jnz .nodump
@@:
DEBUGF 1,'K : unclaimed HID input: usage=%x, value=%x\n',ecx,edx
.nodump:
; pass through
; Three nothing-to-do procedures.
default_driver_disconnect:
default_driver_begin_packet:
default_driver_end_packet:
ret
end if
/kernel/branches/Kolibri-acpi/drivers/usbhid/usbhid.asm
0,0 → 1,553
; 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'
include '../../struct.inc'
 
public START
public version
 
; Compile-time settings.
; If set, the code will dump all descriptors as they are read to the debug board.
USB_DUMP_DESCRIPTORS = 1
; If set, the code will dump any unclaimed input to the debug board.
HID_DUMP_UNCLAIMED = 1
 
; 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 HID constants
HID_DESCR_TYPE = 21h
REPORT_DESCR_TYPE = 22h
PHYSICAL_DESCR_TYPE = 23h
 
; USB structures
struct config_descr
bLength db ?
bDescriptorType db ?
wTotalLength dw ?
bNumInterfaces db ?
bConfigurationValue db ?
iConfiguration db ?
bmAttributes db ?
bMaxPower db ?
ends
 
struct interface_descr
bLength db ?
bDescriptorType db ?
bInterfaceNumber db ?
bAlternateSetting db ?
bNumEndpoints db ?
bInterfaceClass db ?
bInterfaceSubClass db ?
bInterfaceProtocol db ?
iInterface db ?
ends
 
struct endpoint_descr
bLength db ?
bDescriptorType db ?
bEndpointAddress db ?
bmAttributes db ?
wMaxPacketSize dw ?
bInterval db ?
ends
 
; USB HID structures
struct hid_descr
bLength db ?
bDescriptorType db ?
bcdHID dw ?
bCountryCode db ?
bNumDescriptors db ?
base_sizeof rb 0
; now two fields are repeated .bNumDescriptors times:
subDescriptorType db ?
subDescriptorLength dw ?
ends
 
; Include macro for parsing report descriptors/data.
macro workers_globals
{}
include 'report.inc'
 
; Driver data for all devices
struct usb_device_data
hid hid_data ; data of HID layer
epdescr dd ? ; endpoint descriptor
hiddescr dd ? ; HID descriptor
interface_number dd ? ; copy of interface_descr.bInterfaceNumber
configpipe dd ? ; config pipe handle
intpipe dd ? ; interrupt pipe handle
input_transfer_size dd ? ; input transfer size
input_buffer dd ? ; buffer for input transfers
control rb 8 ; control packet to device
ends
 
section '.flat' code readable align 16
; The start procedure.
proc START
virtual at esp
dd ? ; return address
.reason dd ?
end virtual
; 1. Test whether the procedure is called with the argument DRV_ENTRY.
; If not, return 0.
xor eax, eax ; initialize return value
cmp [.reason], 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
endp
 
; This procedure is called when new HID device is detected.
; It initializes the device.
proc AddDevice
push ebx esi edi ; save used registers to be stdcall
virtual at esp
rd 3 ; saved registers
dd ? ; return address
.config_pipe dd ?
.config_descr dd ?
.interface dd ?
end virtual
DEBUGF 1,'K : USB HID device detected\n'
; 1. Allocate memory for device data.
movi eax, sizeof.usb_device_data
call Kmalloc
test eax, eax
jnz @f
mov esi, nomemory_msg
call SysMsgBoardStr
jmp .return0
@@:
; zero-initialize it
mov edi, eax
xchg eax, ebx
xor eax, eax
movi ecx, sizeof.usb_device_data / 4
rep stosd
mov edx, [.interface]
; 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.
; Also, look for the HID descriptor; according to HID spec, it must be
; located before endpoint descriptors.
; 2. Get the upper bound of all descriptors' data.
mov eax, [.config_descr]
movzx ecx, [eax+config_descr.wTotalLength]
add eax, ecx
; 3. 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 for IN endpoint is found
; (HID descriptor must be located before the endpoint descriptor).
; 3a. Loop start: edx points to the interface descriptor.
.lookep:
; 3b. Get next descriptor.
movzx ecx, byte [edx] ; the first byte of all descriptors is length
test ecx, ecx
jz .cfgerror
add edx, ecx
; 3c. Check that at least two bytes are readable. The opposite is an error.
inc edx
cmp edx, eax
jae .cfgerror
dec edx
; 3d. Check that this descriptor is not interface descriptor. The opposite is
; an error.
cmp [edx+endpoint_descr.bDescriptorType], INTERFACE_DESCR_TYPE
jz .cfgerror
; 3e. For HID descriptor, proceed to 4.
; For endpoint descriptor, go to 5.
; For other descriptors, continue the loop.
; Note: bDescriptorType is in the same place in all descriptors.
cmp [edx+endpoint_descr.bDescriptorType], ENDPOINT_DESCR_TYPE
jz .foundep
cmp [edx+endpoint_descr.bDescriptorType], HID_DESCR_TYPE
jnz .lookep
; 4a. Check that the descriptor contains all required data and all data are
; readable. The opposite is an error.
movzx ecx, [edx+hid_descr.bLength]
cmp ecx, hid_descr.base_sizeof + 3
jb .cfgerror
add ecx, edx
cmp ecx, eax
ja .cfgerror
; 4b. Store the pointer in usb_device_data structure for further references.
mov [ebx+usb_device_data.hiddescr], edx
; 4c. Continue the loop.
jmp .lookep
.foundep:
; 5a. Check that the descriptor contains all required data and all data are
; readable. The opposite is an error.
cmp byte [edx+endpoint_descr.bLength], sizeof.endpoint_descr
jb .cfgerror
lea ecx, [edx+sizeof.endpoint_descr]
cmp ecx, eax
jbe @f
; 6. An error occured during processing endpoint descriptor.
.cfgerror:
; 6a. Print a message.
mov esi, invalid_config_descr_msg
call SysMsgBoardStr
; 6b. Free memory allocated for device data.
.free:
xchg eax, ebx
call Kfree
.return0:
; 6c. Return an error.
xor eax, eax
.nothing:
pop edi esi ebx ; restore used registers to be stdcall
ret 12
@@:
; 5b. If this is not IN interrupt endpoint, ignore it and continue the loop.
test [edx+endpoint_descr.bEndpointAddress], 80h
jz .lookep
mov cl, [edx+endpoint_descr.bmAttributes]
and cl, 3
cmp cl, INTERRUPT_PIPE
jnz .lookep
; 5c. Store the pointer in usb_device_data structure for futher references.
mov [ebx+usb_device_data.epdescr], edx
; 5d. Check that HID descriptor was found. If not, go to 6.
cmp [ebx+usb_device_data.hiddescr], 0
jz .cfgerror
.descriptors_found:
; 6. Configuration descriptor seems to be ok.
; Send SET_IDLE command disabling auto-repeat feature (it is quite useless)
; and continue configuring in SET_IDLE callback.
lea edx, [ebx+usb_device_data.control]
mov eax, [.interface]
mov dword [edx], 21h + \ ; Class-specific request to Interface
(0Ah shl 8) + \ ; SET_IDLE
(0 shl 16) + \ ; apply to all input reports
(0 shl 24) ; disable auto-repeat
movzx eax, [eax+interface_descr.bInterfaceNumber]
mov [ebx+usb_device_data.interface_number], eax
mov [edx+4], eax ; set interface number, zero length
mov eax, [.config_pipe]
mov [ebx+usb_device_data.configpipe], eax
xor ecx, ecx
stdcall USBControlTransferAsync, eax, edx, ecx, ecx, idle_set, ebx, ecx
; 7. Return pointer to usb_device_data.
xchg eax, ebx
jmp .nothing
endp
 
; This procedure is called by USB stack when SET_IDLE request initiated by
; AddDevice is completed, either successfully or unsuccessfully.
proc idle_set
push ebx esi ; save used registers to be stdcall
virtual at esp
rd 2 ; saved registers
dd ? ; return address
.pipe dd ?
.status dd ?
.buffer dd ?
.length dd ?
.calldata dd ?
end virtual
; Ignore status. Support for SET_IDLE is optional, so the device is free to
; STALL the request; config pipe should remain functional without explicit cleanup.
mov ebx, [.calldata]
; 1. HID descriptor contains length of Report descriptor. Parse it.
mov esi, [ebx+usb_device_data.hiddescr]
movzx ecx, [esi+hid_descr.bNumDescriptors]
lea eax, [hid_descr.base_sizeof+ecx*3]
cmp eax, 100h
jae .cfgerror
cmp al, [esi+hid_descr.bLength]
jb .cfgerror
.look_report:
dec ecx
js .cfgerror
cmp [esi+hid_descr.subDescriptorType], REPORT_DESCR_TYPE
jz .found_report
add esi, 3
jmp .look_report
.cfgerror:
mov esi, invalid_config_descr_msg
.abort_with_msg:
call SysMsgBoardStr
jmp .nothing
.found_report:
; 2. Send request for the Report descriptor.
; 2a. Allocate memory.
movzx eax, [esi+hid_descr.subDescriptorLength]
test eax, eax
jz .cfgerror
push eax
call Kmalloc
pop ecx
; If failed, say a message and stop initialization.
mov esi, nomemory_msg
test eax, eax
jz .abort_with_msg
; 2b. Submit the request.
xchg eax, esi
lea edx, [ebx+usb_device_data.control]
mov eax, [ebx+usb_device_data.interface_number]
mov dword [edx], 81h + \ ; Standard request to Interface
(6 shl 8) + \ ; GET_DESCRIPTOR
(0 shl 16) + \ ; descriptor index: there is only one report descriptor
(REPORT_DESCR_TYPE shl 24); descriptor type
mov [edx+4], ax ; Interface number
mov [edx+6], cx ; descriptor length
stdcall USBControlTransferAsync, [ebx+usb_device_data.configpipe], \
edx, esi, ecx, got_report, ebx, 0
; 2c. If failed, free the buffer and stop initialization.
test eax, eax
jnz .nothing
xchg eax, esi
call Kfree
.nothing:
pop esi ebx ; restore used registers to be stdcall
ret 20
endp
 
; This procedure is called by USB stack when the report descriptor queried
; by idle_set is completed, either successfully or unsuccessfully.
proc got_report stdcall uses ebx esi edi, pipe, status, buffer, length, calldata
locals
parse_descr_locals
if ~HID_DUMP_UNCLAIMED
has_driver db ?
rb 3
end if
endl
; 1. Check the status; if the request has failed, say something to the debug board
; and stop initialization.
cmp [status], 0
jnz .generic_fail
; 2. Subtract size of setup packet from the total length;
; the rest is length of the descriptor, and it must be nonzero.
sub [length], 8
ja .has_something
.generic_fail:
push esi
mov esi, reportfail
call SysMsgBoardStr
pop esi
jmp .exit
.has_something:
; 3. Process descriptor.
; 3a. Dump it to the debug board, if enabled in compile-time setting.
if USB_DUMP_DESCRIPTORS
mov eax, [buffer]
mov ecx, [length]
DEBUGF 1,'K : report descriptor:'
@@:
DEBUGF 1,' %x',[eax]:2
inc eax
dec ecx
jnz @b
DEBUGF 1,'\n'
end if
; 3b. Call the HID layer.
parse_descr
cmp [report_ok], 0
jz got_report.exit
mov ebx, [calldata]
postprocess_descr
; 4. Stop initialization if no driver is assigned.
if ~HID_DUMP_UNCLAIMED
cmp [has_driver], 0
jz got_report.exit
end if
; 5. Open interrupt IN pipe. If failed, stop initialization.
mov edx, [ebx+usb_device_data.epdescr]
movzx ecx, [edx+endpoint_descr.bEndpointAddress]
movzx eax, [edx+endpoint_descr.bInterval]
movzx edx, [edx+endpoint_descr.wMaxPacketSize]
stdcall USBOpenPipe, [ebx+usb_device_data.configpipe], ecx, edx, INTERRUPT_PIPE, eax
test eax, eax
jz got_report.exit
mov [ebx+usb_device_data.intpipe], eax
; 6. Initialize buffer for input packet.
; 6a. Find the length of input packet.
; This is the maximal length of all input reports.
mov edx, [ebx+usb_device_data.hid.input.first_report]
xor eax, eax
.find_input_size:
test edx, edx
jz .found_input_size
cmp eax, [edx+report.size]
jae @f
mov eax, [edx+report.size]
@@:
mov edx, [edx+report.next]
jmp .find_input_size
.found_input_size:
; report.size is in bits, transform it to bytes
add eax, 7
shr eax, 3
; if reports are numbered, the first byte is report ID, include it
cmp [ebx+usb_device_data.hid.input.numbered], 0
jz @f
inc eax
@@:
mov [ebx+usb_device_data.input_transfer_size], eax
; 6b. Allocate memory for input packet: dword-align and add additional dword
; for extract_field_value.
add eax, 4+3
and eax, not 3
call Kmalloc
test eax, eax
jnz @f
mov esi, nomemory_msg
call SysMsgBoardStr
jmp got_report.exit
@@:
mov [ebx+usb_device_data.input_buffer], eax
; 7. Submit a request for input packet and wait for input.
call ask_for_input
got_report.exit:
mov eax, [buffer]
call Kfree
ret
endp
 
; Helper procedure for got_report and got_input.
; Submits a request for the next input packet.
proc ask_for_input
; just call USBNormalTransferAsync with correct parameters,
; allow short packets
stdcall USBNormalTransferAsync, \
[ebx+usb_device_data.intpipe], \
[ebx+usb_device_data.input_buffer], \
[ebx+usb_device_data.input_transfer_size], \
got_input, ebx, \
1
ret
endp
 
; This procedure is called by USB stack when a HID device responds with input
; data packet.
proc got_input stdcall uses ebx esi edi, pipe, status, buffer, length, calldata
locals
parse_input_locals
endl
; 1. Validate parameters: fail on error, ignore zero-length transfers.
mov ebx, [calldata]
cmp [status], 0
jnz .fail
cmp [length], 0
jz .done
; 2. Get pointer to report in esi.
; 2a. If there are no report IDs, use hid.input.data.
mov eax, [buffer]
mov esi, [ebx+usb_device_data.hid.input.data]
cmp [ebx+usb_device_data.hid.input.numbered], 0
jz .report_found
; 2b. Otherwise, the first byte of report is report ID;
; locate the report by its ID, advance buffer+length to one byte.
movzx eax, byte [eax]
mov esi, [esi+eax*4]
inc [buffer]
dec [length]
.report_found:
; 3. Validate: ignore transfers with unregistered report IDs
; and transfers which are too short for the corresponding report.
test esi, esi
jz .done
mov eax, [esi+report.size]
add eax, 7
shr eax, 3
cmp eax, [length]
ja .done
; 4. Pass everything to HID layer.
parse_input
.done:
; 5. Query the next input.
mov ebx, [calldata]
call ask_for_input
.nothing:
ret
.fail:
mov esi, transfer_error_msg
call SysMsgBoardStr
jmp .nothing
endp
 
; This function is called by the USB subsystem when a device is disconnected.
proc DeviceDisconnected
push ebx esi edi ; save used registers to be stdcall
virtual at esp
rd 3 ; saved registers
dd ? ; return address
.device_data dd ?
end virtual
; 1. Say a message.
mov ebx, [.device_data]
mov esi, disconnectmsg
stdcall SysMsgBoardStr
; 2. Ask HID layer to release all HID-related resources.
hid_cleanup
; 3. Free the device data.
xchg eax, ebx
call Kfree
; 4. Return.
.nothing:
pop edi esi ebx ; restore used registers to be stdcall
ret 4 ; purge one dword argument to be stdcall
endp
 
include 'sort.inc'
include 'unclaimed.inc'
include 'mouse.inc'
include 'keyboard.inc'
 
; strings
my_driver db 'usbhid',0
nomemory_msg db 'K : no memory',13,10,0
invalid_config_descr_msg db 'K : invalid config descriptor',13,10,0
reportfail db 'K : failed to read report descriptor',13,10,0
transfer_error_msg db 'K : USB transfer error, disabling HID device',13,10,0
disconnectmsg db 'K : USB HID device disconnected',13,10,0
invalid_report_msg db 'K : report descriptor is invalid',13,10,0
delimiter_note db 'K : note: alternate usage ignored',13,10,0
 
; Exported variable: kernel API version.
align 4
version dd 50005h
; Structure with callback functions.
usb_functions:
dd 12
dd AddDevice
dd DeviceDisconnected
 
; for DEBUGF macro
include_debug_strings
 
; Workers data
workers_globals
 
; for uninitialized data
;section '.data' data readable writable align 16
/kernel/branches/Kolibri-acpi/drivers/usbhid
Property changes:
Added: tsvn:logminsize
+5
\ No newline at end of property
/kernel/branches/Kolibri-acpi/drivers/vidrdc.asm
0,0 → 1,438
; Stub of videodriver for RDC Semiconductor Co. M2010/M2012 videocards (controller names: R3306/R3308).
; It is used in SoC produced by DMP Electronics Inc.:
; Vortex86MX (contains RDC M2010 graphics card, appears in eBox-3300MX)
; Vortex86MX+ (contains RDC M2012 graphics card, appears in eBox-3310MX)
; Link to manufacturers websites -
; RDC Semiconductor Co.: http://www.rdc.com.tw
; DM&P Electronics Inc.: http://www.dmp.com.tw and http://www.compactpc.com.tw
; Code stolen from vidintel.asm driver (c) by CleverMouse and adapted for RDC.
 
; When the start procedure gets control,
; it tries to detect preferred resolution,
; sets the detected resolution assuming 32-bpp VESA mode and exits
; (without registering a service).
; Detection can be overloaded with compile-time settings
; use_predefined_mode/predefined_width/predefined_height.
 
; set predefined resolution here
use_predefined_mode = 0;1
predefined_width = 0;1366
predefined_height = 0;768
 
; standard driver stuff
format MS COFF
 
DEBUG = 1
 
include 'proc32.inc'
include 'imports.inc'
 
public START
public version
 
section '.flat' code readable align 16
; the start procedure (see the description above)
START:
; 1. Detect device. Abort if not found.
push esi
call DetectDevice
test esi, esi
jz .return0
 
;{START}yogev_ezra: temporary exit after detection
pusha
mov esi, exitmsg
call SysMsgBoardStr
popa
jmp .return0
;{END}yogev_ezra: temporary exit after detection
; 2. Detect optimal mode unless the mode is given explicitly. Abort if failed.
if use_predefined_mode = 0
call DetectMode
end if
cmp [width], 0
jz .return0_cleanup
; 3. Set the detected mode.
call SetMode
; 4. Cleanup and return.
.return0_cleanup:
stdcall FreeKernelSpace, esi
.return0:
pop esi
xor eax, eax
ret 4
 
; check that there is RDC videocard
; if so, map MMIO registers and set internal variables
; esi points to MMIO block; NULL means no device
DetectDevice:
; 1. Sanity check: check that we are dealing with RDC videocard.
; Integrated video device for RDC is always at PCI:0:13:0 (bus:dev:fn=0:0d:0)
xor esi, esi ; initialize return value to NULL
; 1a. Get PCI VendorID and DeviceID.
push esi ; in: reg=0 (register) -> register 00 means return DeviceID (bits 16-31) + VendorID (bits 0-15)
push 68h ; in: devfn=13:0 | device:5bit (0Dh = 1101) + func:3bit (0 = 000) -> total:1byte (1101000b = 68h)
push esi ; in: bus=0
call PciRead32
; 1b. loword(eax) = ax = VendorID, hiword(eax) = DeviceID.
; Test whether we have RDC Semiconductor Co. chipset.
cmp ax, 17F3h ;VendorID 0x17F3, 'RDC Semiconductor Co.'
jnz .return
; 1c. Say hi including DeviceID.
shr eax, 10h ; now, ax = HIWORD(eax) = PCI DeviceID
push edi
pusha
mov edi, pciid_text ; edi='0000'
call WriteWord
mov esi, hellomsg
call SysMsgBoardStr
popa
; 1d. Test whether we know this DeviceID.
; If this is the case, remember the position of the device in line of RDC cards;
; this knowledge will be useful later.
; Tested on devices with id: 17F3:2010, 17F3:2012.
mov ecx, pciids_num
mov edi, pciids
repnz scasw
pop edi
jnz .return_unknown_pciid
sub ecx, pciids_num - 1
neg ecx
mov [deviceType], ecx
; 1e. Continue saying hi with positive intonation.
pusha
mov esi, knownmsg
call SysMsgBoardStr
popa
; 2. Prepare MMIO region to control the card.
; 2a. Read MMIO physical address from PCI config space.
; According to RDC M2010/M2012 registers manual, their memory-mapped I/O space is located at Base address #1
push 14h ; in: reg=14h (register) -> register 14h means Base address #1 (BAR1) in PCI configuration space
push 68h ; in: devfn=13:0 | device:5bit (0Dh = 1101) + func:3bit (0 = 000) -> total:1byte (1101000b = 68h)
push esi ; in: bus=0
call PciRead32
; 2b. Mask out PCI region type, lower 4 bits.
and al, not 0xF
; 2c. Create virtual mapping of the physical memory.
push 1Bh
push 100000h
push eax
call MapIoMem
; 3. Return.
xchg esi, eax
.return:
ret
; 1f. If we do not know DeviceID, continue saying hi with negative intonation.
.return_unknown_pciid:
pusha
mov esi, unknownmsg
call SysMsgBoardStr
popa
ret
 
; Convert word in ax to hexadecimal text in edi, advance edi.
WriteWord:
; 1. Convert high byte.
push eax
mov al, ah
call WriteByte
pop eax
; 2. Convert low byte.
; Fall through to WriteByte; ret from WriteByte is ret from WriteWord too.
 
; Convert byte in al to hexadecimal text in edi, advance edi.
WriteByte:
; 1. Convert high nibble.
push eax
shr al, 4
call WriteNibble
pop eax
; 2. Convert low nibble.
and al, 0xF
; Fall through to WriteNibble; ret from WriteNibble is ret from WriteByte too.
 
; Convert nibble in al to hexadecimal text in edi, advance edi.
WriteNibble:
; Obvious, isn't it?
cmp al, 10
sbb al, 69h
das
stosb ; This instruction uses EDI implicitly
ret
 
if use_predefined_mode = 0
; detect resolution of the flat panel
DetectMode:
push esi edi
; 1. Get the location of block of GMBUS* registers.
; Starting with Ironlake, GMBUS* registers were moved.
add esi, 5100h
cmp [deviceType], pciids_num ;ironlake_start
jb @f
add esi, 0xC0000
@@:
; 2. Initialize GMBUS engine.
mov edi, edid
mov ecx, 0x10000
@@:
test byte [esi+8+1], 80h
loopnz @b
jnz .fail
mov dword [esi], 3
test byte [esi+8+1], 4
jz .noreset
call ResetGMBus
jnz .fail
.noreset:
; 3. Send read command.
and dword [esi+20h], 0
mov dword [esi+4], 4E8000A1h
; 4. Wait for data, writing to the buffer as data arrive.
.getdata:
mov ecx, 0x10000
@@:
test byte [esi+8+1], 8
loopz @b
test byte [esi+8+1], 4
jz .dataok
call ResetGMBus
jmp .fail
.dataok:
mov eax, [esi+0Ch]
stosd
cmp edi, edid+80h
jb .getdata
; 5. Wait for bus idle.
mov ecx, 0x10000
@@:
test byte [esi+8+1], 2
loopnz @b
; 6. We got EDID; dump it if DEBUG.
if DEBUG
pusha
xor ecx, ecx
mov esi, edid
mov edi, edid_text
.dumploop:
lodsb
call WriteByte
mov al, ' '
stosb
inc cl
test cl, 15
jnz @f
mov byte [edi-1], 13
mov al, 10
stosb
@@:
test cl, cl
jns .dumploop
mov esi, edidmsg
call SysMsgBoardStr
popa
end if
; 7. Test whether EDID is good.
; 7a. Signature: 00 FF FF FF FF FF FF 00.
mov esi, edid
cmp dword [esi], 0xFFFFFF00
jnz .fail
cmp dword [esi+4], 0x00FFFFFF
jnz .fail
; 7b. Checksum must be zero.
xor edx, edx
mov ecx, 80h
@@:
lodsb
add dl, al
loop @b
jnz .fail
; 8. Get width and height from EDID.
xor eax, eax
mov ah, [esi-80h+3Ah]
shr ah, 4
mov al, [esi-80h+38h]
mov [width], eax
mov ah, [esi-80h+3Dh]
shr ah, 4
mov al, [esi-80h+3Bh]
mov [height], eax
; 9. Return.
.fail:
pop edi esi
ret
 
; reset bus, clear all errors
ResetGMBus:
; look into the PRM
mov dword [esi+4], 80000000h
mov dword [esi+4], 0
mov ecx, 0x10000
@@:
test byte [esi+8+1], 2
loopnz @b
ret
end if
 
; set resolution [width]*[height]
SetMode:
; 1. Program the registers of videocard.
; look into the PRM
cli
; or byte [esi+7000Ah], 0Ch ; PIPEACONF: disable Display+Cursor Planes
; or byte [esi+7100Ah], 0Ch ; PIPEBCONF: disable Display+Cursor Planes
xor eax, eax
xor edx, edx
cmp [deviceType], pciids_num ;i965_start
jb @f
mov dl, 9Ch - 84h
@@:
; or byte [esi+71403h], 80h ; VGACNTRL: VGA Display Disable
and byte [esi+70080h], not 27h ; CURACNTR: disable cursor A
mov dword [esi+70084h], eax ; CURABASE: force write to CURA* regs
and byte [esi+700C0h], not 27h ; CURBCNTR: disable cursor B
mov dword [esi+700C4h], eax ; CURBBASE: force write to CURB* regs
and byte [esi+70183h], not 80h ; DSPACNTR: disable Primary A Plane
mov dword [esi+edx+70184h], eax ; DSPALINOFF/DSPASURF: force write to DSPA* regs
and byte [esi+71183h], not 80h ; DSPBCNTR: disable Primary B Plane
mov dword [esi+edx+71184h], eax ; DSPBLINOFF/DSPBSURF: force write to DSPB* regs
if 1
cmp [deviceType], pciids_num ;ironlake_start
jae .disable_pipes
mov edx, 10000h
or byte [esi+70024h], 2 ; PIPEASTAT: clear VBLANK status
or byte [esi+71024h], 2 ; PIPEBSTAT: clear VBLANK status
.wait_vblank_preironlake1:
mov ecx, 1000h
loop $
test byte [esi+7000Bh], 80h ; PIPEACONF: pipe A active?
jz @f
test byte [esi+70024h], 2 ; PIPEASTAT: got VBLANK?
jz .wait_vblank_preironlake2
@@:
test byte [esi+7100Bh], 80h ; PIPEBCONF: pipe B active?
jz .disable_pipes
test byte [esi+71024h], 2 ; PIPEBSTAT: got VBLANK?
jnz .disable_pipes
.wait_vblank_preironlake2:
dec edx
jnz .wait_vblank_preironlake1
jmp .not_disabled
.disable_pipes:
end if
and byte [esi+7000Bh], not 80h ; PIPEACONF: disable pipe
and byte [esi+7100Bh], not 80h ; PIPEBCONF: disable pipe
cmp [deviceType], pciids_num ;gen4_start
jb .wait_watching_scanline
; g45 and later: use special flag from PIPE*CONF
mov edx, 10000h
@@:
mov ecx, 1000h
loop $
test byte [esi+7000Bh], 40h ; PIPEACONF: wait until pipe disabled
jz @f
dec edx
jnz @b
jmp .not_disabled
@@:
test byte [esi+7100Bh], 40h ; PIPEBCONF: wait until pipe disabled
jz .disabled
mov ecx, 1000h
loop $
dec edx
jnz @b
jmp .not_disabled
; pineview and before: wait while scanline still changes
.wait_watching_scanline:
mov edx, 1000h
.dis1:
push dword [esi+71000h]
push dword [esi+70000h]
mov ecx, 10000h
loop $
pop eax
xor eax, [esi+70000h]
and eax, 1FFFh
pop eax
jnz .notdis1
xor eax, [esi+71000h]
and eax, 1FFFh
jz .disabled
.notdis1:
dec edx
jnz .dis1
.not_disabled:
sti
jmp .return
.disabled:
lea eax, [esi+61183h]
cmp [deviceType], pciids_num ;ironlake_start
jb @f
add eax, 0xE0000 - 0x60000
@@:
lea edx, [esi+60000h]
test byte [eax], 40h
jz @f
add edx, 1000h
@@:
mov eax, [width]
dec eax
shl eax, 16
mov ax, word [height]
dec eax
mov dword [edx+1Ch], eax ; PIPEASRC: set source image size
ror eax, 16
mov dword [edx+10190h], eax ; for old cards
mov ecx, [width]
add ecx, 15
and ecx, not 15
shl ecx, 2
mov dword [edx+10188h], ecx ; DSPASTRIDE: set scanline length
mov dword [edx+10184h], 0 ; DSPALINOFF: force write to DSPA* registers
and byte [esi+61233h], not 80h ; PFIT_CONTROL: disable panel fitting
or byte [edx+1000Bh], 80h ; PIPEACONF: enable pipe
; and byte [edx+1000Ah], not 0Ch ; PIPEACONF: enable Display+Cursor Planes
or byte [edx+10183h], 80h ; DSPACNTR: enable Display Plane A
sti
; 2. Notify the kernel that resolution has changed.
call GetDisplay
mov edx, [width]
mov dword [eax+8], edx
mov edx, [height]
mov dword [eax+0Ch], edx
mov [eax+18h], ecx
mov eax, [width]
dec eax
dec edx
call SetScreen
.return:
ret
 
align 4
hellomsg db 'RDC videocard detected, PciId=17F3:' ;VendorID 0x17F3, 'RDC Semiconductor Co.'
pciid_text db '0000'
db ', which is ', 0
knownmsg db 'known',13,10,0
unknownmsg db 'unknown',13,10,0
exitmsg db 'Card detected successfully, exiting driver...',13,10,0
 
if DEBUG
edidmsg db 'EDID successfully read:',13,10
edid_text rb 8*(16*3+1)
db 0
end if
 
version:
dd 0x50005
 
width dd predefined_width
height dd predefined_height
 
pciids:
dw 0x2010 ; M2010 - appears in eBox-3300MX (Vortex86MX SoC)
dw 0x2012 ; M2012 - appears in eBox-3310MX (Vortex86MX+ SoC)
pciids_num = ($ - pciids) / 2
 
align 4
deviceType dd ?
edid rb 0x80