mirror of
https://github.com/KolibriOS/kolibrios.git
synced 2025-09-10 04:23:24 -04:00

This driver loading usb drivers for class and for vendor and product id device. Co-authored-by: Burer <burer@kolibrios.org> Reviewed-on: https://git.kolibrios.org/KolibriOS/kolibrios/pulls/253 Reviewed-by: Ivan B <dunkaist@noreply.localhost> Reviewed-by: Burer <burer@noreply.localhost>
524 lines
13 KiB
NASM
524 lines
13 KiB
NASM
;*****************************************************************************;
|
|
; Copyright (C) 2025, Mikhail Frolov aka Doczom . All rights reserved. ;
|
|
; Distributed under terms of the 3-Clause BSD License. ;
|
|
; ;
|
|
; usbother is a driver for loading USB drivers of a certain class and vendor. ;
|
|
; ;
|
|
; Version 0.1.1, 24 May 2025 ;
|
|
; ;
|
|
;*****************************************************************************;
|
|
format PE native 0.05
|
|
entry START
|
|
; const
|
|
|
|
DRV_VERSION = 0x0101 ; 0.1.1
|
|
|
|
|
|
; struct
|
|
include '../../struct.inc'
|
|
|
|
; USB device descriptor
|
|
struct DEVICE_DESCR
|
|
bLength db ?
|
|
bDescriptorType db ?
|
|
bcdUSB dw ?
|
|
bDeviceClass db ?
|
|
bDeviceSubClass db ?
|
|
bDeviceProtocol db ?
|
|
bMaxPacketSize0 db ?
|
|
idVendor dw ?
|
|
idProduct dw ?
|
|
bcdDevice dw ?
|
|
iManufacturer db ?
|
|
iProduct db ?
|
|
iSerialNumber db ?
|
|
bNumConfigurations 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 SRV
|
|
srv_name rb 16 ;ASCIIZ string
|
|
magic dd ? ;+0x10 ;'SRV '
|
|
size dd ? ;+0x14 ;size of structure SRV
|
|
fd dd ? ;+0x18 ;next SRV descriptor
|
|
bk dd ? ;+0x1C ;prev SRV descriptor
|
|
base dd ? ;+0x20 ;service base address
|
|
entry dd ? ;+0x24 ;service START function
|
|
srv_proc dd ? ;+0x28 ;user mode service handler
|
|
srv_proc_ex dd ? ;+0x2C ;kernel mode service handler
|
|
ends
|
|
|
|
struct USBSRV
|
|
srv SRV
|
|
usb_func dd ?
|
|
ends
|
|
|
|
struct USBFUNC
|
|
strucsize dd ?
|
|
add_device dd ?
|
|
device_disconnect dd ?
|
|
ends
|
|
|
|
USBDRV_TYPE_NOLOCK = 0 ; usb device not controlled (native driver
|
|
; not found and IOCTL not opened device)
|
|
USBDRV_TYPE_NATIVE = 1 ; native PE kernel driver for usb
|
|
USBDRV_TYPE_IOCTL = 2 ; usb device is controlled by IOCTL service
|
|
; (driver or userspace process/threads)
|
|
USBDRV_IOCTL_BLOCKED = 4 ; blocked IOCTL interface, device disconnected
|
|
|
|
|
|
|
|
struct DRV_CONTEXT
|
|
next dd ?
|
|
prev dd ?
|
|
drv_hand dd ?
|
|
drv_pdata dd ?
|
|
flags dd ?
|
|
|
|
config_descr dd ?
|
|
interface_descr dd ?
|
|
ep rd 64 ; 32 IN + 32 OUT endpoints' pipes
|
|
ends
|
|
DRV_CONTEXT.ep0 fix (DRV_CONTEXT.ep + 0)
|
|
|
|
|
|
section '.flat' code readable writable executable
|
|
|
|
include '../../proc32.inc'
|
|
include '../../peimport.inc'
|
|
include '../../macros.inc'
|
|
|
|
|
|
proc START c, state:dword, cmdline:dword
|
|
|
|
cmp [state], DRV_ENTRY
|
|
jne .end
|
|
; init
|
|
|
|
mov ecx, drv_list_lock
|
|
invoke MutexInit
|
|
|
|
mov ecx, interface_list_lock
|
|
invoke MutexInit
|
|
|
|
; load drv_list
|
|
stdcall load_drv_list, default_list
|
|
test eax, eax
|
|
jnz .end
|
|
|
|
; reg driver
|
|
invoke RegUSBDriver, drv_name, service_proc, usb_functions
|
|
ret
|
|
.end:
|
|
xor eax, eax
|
|
ret
|
|
endp
|
|
|
|
proc load_drv_list stdcall, .path:dword
|
|
|
|
push ebx
|
|
mov ecx, drv_list_lock
|
|
invoke MutexLock
|
|
; load file
|
|
invoke LoadFile, [.path]
|
|
test eax, eax
|
|
push eax
|
|
jnz @f
|
|
mov dword[esp], -1
|
|
jmp .exit
|
|
@@:
|
|
cmp [drv_list], 0
|
|
jz @f
|
|
|
|
invoke KernelFree, [drv_list]
|
|
@@:
|
|
mov eax,[esp]
|
|
mov [drv_list], eax
|
|
mov dword[esp], 0
|
|
.exit:
|
|
mov ecx, drv_list_lock
|
|
invoke MutexUnlock
|
|
pop eax
|
|
pop ebx
|
|
ret
|
|
endp
|
|
|
|
|
|
proc service_proc stdcall, .ioctl:dword
|
|
|
|
push esi
|
|
or eax, -1
|
|
mov esi, [.ioctl]
|
|
|
|
mov ecx, [esi + IOCTL.io_code]
|
|
cmp ecx, .count_ioctl_codes
|
|
jae .fail
|
|
|
|
jmp dword[.table_subfunction + ecx*4]
|
|
|
|
.table_subfunction:
|
|
dd .get_version
|
|
dd .update_list
|
|
dd .get_array_dev
|
|
dd .get_full_dev_data
|
|
dd .open_dev
|
|
dd .close_dev
|
|
dd .control_transfer
|
|
dd .bulk_transfer
|
|
dd .interrupt_transfer
|
|
;dd .control_transfer_async
|
|
;dd .bulk_transfer_async
|
|
;dd .interrupt_transfer_async
|
|
.count_ioctl_codes = ($ - .table_subfunction)/4
|
|
|
|
.get_version:
|
|
mov eax, [esi + IOCTL.output]
|
|
cmp [esi + IOCTL.out_size], 4
|
|
jne .fail
|
|
|
|
mov dword[eax], DRV_VERSION
|
|
xor eax, eax
|
|
jmp .exit
|
|
|
|
.update_list:
|
|
; update list
|
|
mov ecx, [esi + IOCTL.input]
|
|
cmp [esi + IOCTL.inp_size], 0
|
|
jnz @f
|
|
|
|
mov ecx, default_list
|
|
@@:
|
|
stdcall load_drv_list, ecx
|
|
|
|
.exit:
|
|
pop esi
|
|
ret
|
|
|
|
.get_array_dev:
|
|
.get_full_dev_data:
|
|
.open_dev:
|
|
.close_dev:
|
|
;
|
|
.control_transfer:
|
|
.bulk_transfer:
|
|
.interrupt_transfer:
|
|
|
|
.fail:
|
|
or eax, -1
|
|
jmp .exit
|
|
endp
|
|
|
|
|
|
proc AddDevice stdcall, .config_pipe:dword, \
|
|
.config_descr:dword,\
|
|
.interface:dword
|
|
|
|
push esi edi
|
|
mov eax, sizeof.DRV_CONTEXT
|
|
invoke Kmalloc
|
|
test eax, eax
|
|
jz .err_init
|
|
|
|
mov esi, eax
|
|
|
|
mov ecx, interface_list_lock
|
|
invoke MutexLock
|
|
|
|
mov edx, [usb_interface_list] ; next
|
|
mov [esi + DRV_CONTEXT.next], edx
|
|
mov [esi + DRV_CONTEXT.prev], usb_interface_list
|
|
mov [usb_interface_list], esi
|
|
mov [edx + DRV_CONTEXT.prev], esi
|
|
|
|
mov ecx, interface_list_lock
|
|
invoke MutexUnlock
|
|
|
|
and [esi + DRV_CONTEXT.drv_hand], 0
|
|
mov [esi + DRV_CONTEXT.flags], USBDRV_TYPE_NOLOCK
|
|
|
|
; lock mutex
|
|
mov ecx, drv_list_lock
|
|
invoke MutexLock
|
|
|
|
; save device context data
|
|
mov eax, [.config_pipe]
|
|
mov [esi + DRV_CONTEXT.ep0], eax
|
|
mov eax, [.config_descr]
|
|
mov [esi + DRV_CONTEXT.config_descr], eax
|
|
mov eax, [.interface]
|
|
mov [esi + DRV_CONTEXT.interface_descr], eax
|
|
|
|
; get pointer to list
|
|
mov edx, [drv_list]
|
|
test edx, edx
|
|
jz .err_exit
|
|
|
|
mov edi, edx
|
|
add edi, [edx]
|
|
|
|
; get in ecx VID:PID code
|
|
invoke USBGetParam, [.config_pipe], 0
|
|
mov ecx, dword[eax + DEVICE_DESCR.idVendor]
|
|
|
|
.loop_id_drv:
|
|
cmp dword[edi], 0
|
|
jz .end_loop_id_drv
|
|
|
|
mov edx, [drv_list]
|
|
add edx, [edi] ; ID_TABLE
|
|
.loop_id:
|
|
cmp dword[edx], 0
|
|
jz .end_loop_id
|
|
|
|
; check id
|
|
mov eax, ecx
|
|
test word[edx + 2], 0xffff
|
|
jne @f
|
|
; driver for all devices of VID
|
|
and eax, 0xffff
|
|
@@:
|
|
cmp [edx], eax ; check VID:PID
|
|
je @f
|
|
|
|
add edx, 4
|
|
jmp .loop_id
|
|
@@: ; found
|
|
call .load_drv
|
|
jnz .exit
|
|
|
|
add edx, 4
|
|
jmp .loop_id
|
|
.end_loop_id:
|
|
add edi, 8
|
|
jmp .loop_id_drv
|
|
|
|
.end_loop_id_drv:
|
|
|
|
|
|
push esi
|
|
mov esi, str_1
|
|
invoke SysMsgBoardStr
|
|
pop esi
|
|
|
|
|
|
; get in ecx class code
|
|
mov eax, [.interface]
|
|
mov ecx, dword[eax + INTERFACE_DESCR.bInterfaceClass] ; 24-31 bits
|
|
|
|
mov edi, [drv_list]
|
|
add edi, [edi + 4]
|
|
.loop_class:
|
|
cmp dword[edi], 0
|
|
jz .end_loop_class
|
|
|
|
; check class
|
|
movzx eax, byte[edi] ; length
|
|
and eax, 11b ; protect - max length = 3
|
|
lea eax, [eax*8] ; 1 = 8; 2 = 16; 3 = 24
|
|
xor edx, edx
|
|
bts edx, eax
|
|
dec edx ; bitmask
|
|
|
|
mov eax, [edi]
|
|
shr eax, 8
|
|
and eax, edx ; good class in list
|
|
|
|
and edx, ecx ; good class of device
|
|
|
|
cmp eax, edx
|
|
je @f
|
|
|
|
add edi, 8
|
|
jmp .loop_class
|
|
@@: ; found
|
|
call .load_drv
|
|
jnz .exit
|
|
|
|
add edi, 8
|
|
jmp .loop_class
|
|
|
|
; IN: edi - item list of driver
|
|
; esi - DRV_CONTEXT
|
|
; OUT: ZF - not found zF - found
|
|
; function save drv handl in DRV_CONTEXT.drv_hand
|
|
; and pdata in DRV_CONTEXT.drv_pdata
|
|
.load_drv:
|
|
push ecx edx
|
|
; load driver
|
|
push esi
|
|
mov esi, str_2
|
|
invoke SysMsgBoardStr
|
|
pop esi
|
|
|
|
mov ecx, [drv_list]
|
|
add ecx, [edi + 4]
|
|
|
|
pusha
|
|
mov esi, ecx
|
|
invoke SysMsgBoardStr
|
|
mov esi, str_newline
|
|
invoke SysMsgBoardStr
|
|
popa
|
|
|
|
invoke GetService, ecx
|
|
test eax, eax
|
|
jz @f
|
|
|
|
mov [esi + DRV_CONTEXT.drv_hand], eax
|
|
; get function list
|
|
mov ecx, [eax + USBSRV.usb_func]
|
|
|
|
; call AddDevice of driver
|
|
stdcall [ecx + USBFUNC.add_device], [.config_pipe], \
|
|
[.config_descr],\
|
|
[.interface]
|
|
mov [esi + DRV_CONTEXT.drv_pdata], eax
|
|
test eax, eax
|
|
jnz .load_drv.good
|
|
|
|
push esi
|
|
mov esi, str_4
|
|
invoke SysMsgBoardStr
|
|
pop esi
|
|
|
|
and [esi + DRV_CONTEXT.drv_hand], 0
|
|
@@:
|
|
pushf
|
|
push esi
|
|
mov esi, str_5
|
|
invoke SysMsgBoardStr
|
|
pop esi
|
|
popf
|
|
|
|
pop edx ecx
|
|
retn
|
|
.load_drv.good:
|
|
pushf
|
|
push esi
|
|
mov esi, str_3
|
|
invoke SysMsgBoardStr
|
|
pop esi
|
|
popf
|
|
|
|
mov [esi + DRV_CONTEXT.flags], USBDRV_TYPE_NATIVE
|
|
pop edx ecx
|
|
retn
|
|
|
|
|
|
.err_exit:
|
|
mov eax, esi
|
|
invoke Kfree
|
|
xor esi, esi
|
|
.end_loop_class:
|
|
.exit:
|
|
; driver not found - Added libusb driver
|
|
|
|
; unlock mutex
|
|
mov ecx, drv_list_lock
|
|
invoke MutexUnlock
|
|
|
|
mov eax, esi
|
|
pop edi esi
|
|
ret
|
|
.err_init:
|
|
xor eax, eax
|
|
pop edi esi
|
|
ret
|
|
endp
|
|
|
|
|
|
proc DeviceDisconnected stdcall, .pdata:dword
|
|
|
|
mov eax, [.pdata]
|
|
|
|
test [eax + DRV_CONTEXT.flags], USBDRV_TYPE_NATIVE
|
|
jz .no_native
|
|
|
|
cmp [eax + DRV_CONTEXT.drv_hand], 0
|
|
jz .free
|
|
|
|
; call device disconnected
|
|
mov ecx, [eax + DRV_CONTEXT.drv_hand]
|
|
|
|
mov edx, [ecx + USBSRV.usb_func]
|
|
cmp dword[edx], USBFUNC.device_disconnect
|
|
jbe .free ; TODO: check
|
|
|
|
stdcall [edx + USBFUNC.device_disconnect], [eax + DRV_CONTEXT.drv_pdata]
|
|
.free:
|
|
; clear list of DRV_CONTENT
|
|
mov ecx, interface_list_lock
|
|
invoke MutexLock
|
|
|
|
mov eax, [.pdata]
|
|
mov edx, [eax + DRV_CONTEXT.prev]
|
|
mov ecx, [eax + DRV_CONTEXT.next]
|
|
mov [edx + DRV_CONTEXT.next], ecx
|
|
mov [ecx + DRV_CONTEXT.prev], edx
|
|
|
|
mov ecx, interface_list_lock
|
|
invoke MutexUnlock
|
|
|
|
; free context
|
|
mov eax, [.pdata]
|
|
invoke Kfree
|
|
ret
|
|
.no_native:
|
|
test [eax + DRV_CONTEXT.flags], USBDRV_TYPE_IOCTL
|
|
jz .free
|
|
|
|
; set state for block user api and clear struct
|
|
or [eax + DRV_CONTEXT.flags], USBDRV_IOCTL_BLOCKED
|
|
xor ecx, ecx
|
|
;mov [eax + DRV_CONTEXT.drv_hand], ecx
|
|
;mov [eax + DRV_CONTEXT.drv_pdata], ecx
|
|
mov [eax + DRV_CONTEXT.config_descr], ecx
|
|
mov [eax + DRV_CONTEXT.interface_descr], ecx
|
|
mov [eax + DRV_CONTEXT.ep0], ecx
|
|
; TODO
|
|
jmp .free
|
|
endp
|
|
|
|
; data
|
|
drv_list_lock MUTEX
|
|
|
|
drv_list dd ?
|
|
|
|
interface_list_lock MUTEX
|
|
usb_interface_list:
|
|
dd usb_interface_list
|
|
dd usb_interface_list
|
|
|
|
|
|
usb_functions:
|
|
dd .end - usb_functions
|
|
dd AddDevice
|
|
dd DeviceDisconnected
|
|
.end:
|
|
|
|
drv_name db 'usbother', 0
|
|
|
|
default_list: db '/sys/settings/usbdrv.dat', 0
|
|
|
|
str_1: db 'USBOTHER: Driver for this ID not found', 13, 10, 0
|
|
str_2: db 'USBOTHER: Check found driver: ', 0, 13, 10, 0
|
|
str_3: db 'USBOTHER: Device driver is good', 13, 10, 0
|
|
str_4: db 'USBOTHER: Device driver fail prob', 13, 10, 0
|
|
str_5: db 'USBOTHER: Device driver load error', 13, 10, 0
|
|
str_newline: db 13,10,0
|
|
|
|
data fixups
|
|
end data
|