📄 uhci.asm
字号:
mov dx, USB_COMMAND_REG ;DX = command reg
add dx, cx ;DX = command reg + USB HC base addr
in ax, dx
test ax, HOST_CONTROLLER_RUN
jz CheckActiveNo ;Br if HC is not running
CheckActiveYes:
popad
mov cl, 00000101b ;Indicate USB BIOS is in control and running
ret ; and HC has two root hub ports
CheckActiveNo:
popad
mov cl, 00000100b ;Indicate USB BIOS is not in control or not running
ret ; and HC has two root hub ports
UsbHcCheckActive endp
;---------------------------------------;
; GetUsbHcBusDevFunc ;
;---------------------------------------;--------------------------------------;
; This function returns the PCI bus, device, and function number of the USB ;
; host controller. ;
; ;
; Input: Nothing ;
; ;
; Output: BH = PCI Bus number ;
; BL = Device / Function number ;
; Bits 7-3: PCI device number ;
; Bits 2-0: Function number within the device ;
; CF = Clear if USB host controller was found, set if not found ;
; ;
; Destroys: AX, ECX, EDX ;
;------------------------------------------------------------------------------;
GetUsbHcBusDevFunc proc near
; Find the PCI bus/device/function number of the USB host controller by calling
; a chipset hook to get the Device/Vendor ID of the USB HC and then search for
; that HC on the PCI bus.
ifdef DOS_DEBUG
mov ecx, USB_HC_CLASS_CODE ;Defined by UHCI.EQU or OHCI.EQU
xor si, si
mov ax, 0B103h ;PCI Find Class function
int 1Ah ;Returns CF, BX=bus/dev/func of USB HC
else
call UsbGetHostControllerId ;Returns EDX = Vendor/Device ID
or edx, edx
clc
jz UsbFindDone ;Br if hook returned bus/dev/func #
call smi_pci_find_device ;Returns CF, BX=bus/dev/func of USB HC
endif
UsbFindDone:
ret
GetUsbHcBusDevFunc endp
;---------------------------------------;
; UsbHcInit ;
;---------------------------------------;--------------------------------------;
; This function initializes the host controller, its schedule, and all other ;
; associated data structures, and then starts the host controller. ;
; ;
; Input: DS = ES = Usb Data Area ;
; ;
; Output: Nothing ;
; ;
; Destroys: Nothing ;
;------------------------------------------------------------------------------;
_UsbHcInit proc near
pushad
ifdef DOS_DEBUG
push ds
push es
push DOS_DEBUG_DATA_AREA
push DOS_DEBUG_DATA_AREA
pop ds
pop es
pusha
xor di, di ;Start at offset 0
mov cx, 1000h ;Clear 4k words
xor ax, ax
rep stosw ;Clear 8k data area
popa
endif
; Find the PCI bus/device/function number of the USB host controller and
; store it for later use.
call GetUsbHcBusDevFunc ;Returns BX = b/d/f, destroys AX,ECX,EDX
jc UsbInitDone ;Br if USB HC not found
mov UsbHcBusDevFuncNum, bx ;Save bus/dev/func for later
; Get I/O base address of the HC and store it in the variable
; IO_Space_Base_Address in the USB data segment.
;mov bx, UsbHcBusDevFuncNum ;BX = bus/dev/func # of USB HC
mov di, USB_BASE_ADDRESS
mov si, PCI_REG_ADDRESS_WORD
call smi_pci_read_cfg ;Returns CX = I/O base of HC
and cl, 0FCh ;Mask out 2 reserved bits
mov IO_Space_Base_Address, cx
; First stop the host controller if it is at all active
mov dx, USB_COMMAND_REG
call ReadUsbIoRegByte
and al, NOT HOST_CONTROLLER_RUN
call WriteUsbIoRegByte
;
; Disable both ports for warm boot purposes
;
mov dx,USB_PORT1_CONTROL
call ReadUsbIoRegByte
and al,NOT PORT_ENABLE
call WriteUsbIoRegByte
mov dx,USB_PORT2_CONTROL
call ReadUsbIoRegByte
and al,NOT PORT_ENABLE
call WriteUsbIoRegByte
; Reset the host controller
mov dx, USB_COMMAND_REG
mov ax, GLOBAL_RESET
call WriteUsbIoRegByte
mov cx, 6660 ; 100 ms
call pm_fixed_delay
xor ax, ax ;Reset the usb command register
call WriteUsbIoRegByte
call UsbDataInit ;Initialize any data structures
; Program the frame list base address register
mov eax, HcdDataArea ;EAX = absolute addr of FrameList
add ax, offset FrameList
mov dx, USB_FRAME_LIST_BASE
call WriteUsbIoRegDword ;Write addr of frame list into HC
; Initialize the host controller's 1024 entry frame list to point to the
; first column of HID TDs.
mov di, offset FrameList
InitFrameListNextRepeat:
mov cx, ACTIVE_FRAMES ;CX = number of active frames
mov eax, HcdDataArea ;EAX = seg containing TD pool
add eax, offset TdHid ;EAX = addr of first HID TD
InitFrameListNextFrame:
stosd ;Set frame list entry = TD[cx]
add eax, size Transfer_Descriptor ;EAX = addr of next TD
mov bx, IDLE_FRAMES ;BX = # of idle frames between actives
InitFrameListNextIdle:
mov dword ptr [di], TERMINATE ;Set frame list entry to be idle
add di, 4 ;DI = ptr to next frame list entry
dec bx ;Dec idle frame counter
jnz InitFrameListNextIdle ;Br if more idle frames to insert
loop InitFrameListNextFrame ;Loop for all active frames
cmp di, offset FrameList + (FRAME_LIST_SIZE * 4)
jb InitFrameListNextRepeat ;Br if entire frame list is not done
; Now "hook" the first frame list entry and point it to the array of Hub
; TDs. Then link the root hub TD and the TDs in the TdHub array together
; in a linear linked list.
mov di, offset FrameList ;DI = ptr to first frame list entry
mov ebx, dword ptr [di] ;Save contents of first frame list entry
mov eax, HcdDataArea ;EAX = seg containing TD pool
add eax, offset TdRootHub ;EAX = addr of root hub TD
stosd ;Set frame list entry to point to root hub TD
mov di, offset TdRootHub ;DI = ptr to root hub TD
mov eax, HcdDataArea ;EAX = seg containing TDs
add eax, offset TdHub ;EAX = addr of first hub TD
mov (Transfer_Descriptor ptr [di]).TD_Link_Pointer, eax
mov di, offset TdHub ;DI = ptr to first TD in TdHub array
movzx eax, di
add eax, HcdDataArea
add eax, size Transfer_Descriptor ;EAX = ptr to next TD
mov cx, HUB_DEVICE_LIMIT - 1
InitHubListNext:
mov (Transfer_Descriptor ptr [di]).TD_Link_Pointer, eax
add eax, size Transfer_Descriptor
add di, size Transfer_Descriptor
loop InitHubListNext ;Fill all TdHub TDs except last
mov (Transfer_Descriptor ptr [di]).TD_Link_Pointer, ebx
;Link last hub TD to the saved contents
; of the first frame list entry
; Initialize the HID TDs into a two dimensional array. The
; array has ACTIVE_FRAMES number of rows (each row is headed by one frame list
; entry). The array has TDS_PER_FRAME number of columns. The last TD in each
; row has its link pointer set to point to QhControl (except for the first row
; which has its link pointer set to TdRepeat).
mov edx, HcdDataArea
add edx, offset QhControl
or edx, QUEUE_HEAD ;EDX = Link ptr to QhControl
mov cl, 0 ;CL will be row counter (0..ACTIVE_FRAMES-1)
InitTdArrayNextRow:
mov ch, 0 ;CH will be column counter (0..TDS_PER_FRAME-1)
InitTdArrayNextCol:
mov al, ACTIVE_FRAMES ;AL = number of rows
mul ch ;AL = index of TD in row 0 of cur column
add al, cl ;AL = index of TD in cur row of cur column
mov bl, size Transfer_Descriptor
mul bl ;AX = offset within TD pool of current TD
add ax, offset TdHid ;AX = offset within usbdseg of current TD
mov di, ax ;DI = offset within usbdseg of current TD
mov eax, HcdDataArea ;EDX = seg containing TDs
add eax, offset TdRepeat ;EAX = abs addr of TdRepeat
or cl, cl
jz @f ;Br if on row 0
mov eax, edx ;EAX = ptr to QhControl
@@:
cmp ch, TDS_PER_FRAME - 1
jae InitTdLastCol ;Br if cur TD is last col in its row
movzx eax, di ;EAX = offset within usbdseg of current TD
add eax, HcdDataArea
add eax, (size Transfer_Descriptor * ACTIVE_FRAMES)
;EAX = abs addr of next TD in row
InitTdLastCol:
mov (Transfer_Descriptor ptr [di]).TD_Link_Pointer, eax
inc ch
cmp ch, TDS_PER_FRAME
jb InitTdArrayNextCol ;Br if more cols to do
inc cl
cmp cl, ACTIVE_FRAMES
jb InitTdArrayNextRow ;Br if more rows to do
; Initialize the body of each HID TD and Hub TD to perform an
; interrupt transaction to an individual device address. All TDs
; in the both arrays will initially be disabled. As devices are found that
; need to be polled, the TD corresponding to the device's address will be
; made active.
mov cl, 0 ;CL will count TDs (0..MAX_DEVICES-1)
mov di, offset TdPool ;DI = offset of first TD in array
InitPoolNextTd:
mov (Transfer_Descriptor ptr [di]).TD_Control_Status, THREE_ERRORS
movzx eax, cl ;EAX = TD number
inc al ;EAX = TD's device address
shl ax, 8 ;Put device address in bits 14:8
or eax, IN_PACKET or ((DEFAULT_PACKET_LENGTH - 1) shl 21) ;Set PID=In, and MaxLen
mov (Transfer_Descriptor ptr [di]).TD_Token, eax
lea ax, (Transfer_Descriptor ptr [di]).DataArea ;AX = ptr to TD's data buffer
movzx eax, ax ;Clear upper half of EAX
add eax, HcdDataArea ;EAX = abs addr of TD's data buffer
mov (Transfer_Descriptor ptr [di]).TD_Buffer_Pointer, eax
mov (Transfer_Descriptor ptr [di]).CSReloadValue, THREE_ERRORS
mov (Transfer_Descriptor ptr [di]).pCallback, offset cgroup:PollingTdCallback
add (Transfer_Descriptor ptr [di]).pCallback, orgbase
mov (Transfer_Descriptor ptr [di]).ActiveFlag, FALSE
add di, size Transfer_Descriptor
inc cl ;Inc TD counter
cmp cl, MAX_DEVICES
jb InitPoolNextTd ;Br if more TDs to init
; Initialize the body TdRootHub. It will run a interrupt transaction to a
; nonexistant dummy device. This will have the effect of generating a periodic
; interrupt for the purpose of checking for attach/detach on the root
; hub's ports.
mov di, offset TdRootHub ;DI = ptr of TdRootHub
mov (Transfer_Descriptor ptr [di]).TD_Control_Status, INTERRUPT_ON_COMPLETE or ONE_ERROR or ACTIVE
mov (Transfer_Descriptor ptr [di]).TD_Token, IN_PACKET or (DUMMY_DEVICE_ADDR shl 8) or ((DEFAULT_PACKET_LENGTH - 1) shl 21) ;Set PID=In, Dev=Dummy, and MaxLen
lea ax, (Transfer_Descriptor ptr [di]).DataArea ;AX = ptr to TD's data buffer
movzx eax, ax ;Clear upper half of EAX
add eax, HcdDataArea ;EAX = abs addr of TD's data buffer
mov (Transfer_Descriptor ptr [di]).TD_Buffer_Pointer, eax
mov (Transfer_Descriptor ptr [di]).CSReloadValue, INTERRUPT_ON_COMPLETE or ONE_ERROR or ACTIVE
mov (Transfer_Descriptor ptr [di]).pCallback, offset cgroup:RootHubTdCallback
add (Transfer_Descriptor ptr [di]).pCallback, orgbase
mov (Transfer_Descriptor ptr [di]).ActiveFlag, TRUE
; Initialize the body of TdRepeat It will run a interrupt transaction to a
; nonexistant dummy device. This will have the effect of generating a periodic
; interrupt used to generate keyboard repeat. This TD is normally inactive, and
; is only activated when a key is pressed. TdRepeat will be set to timeout after
; two attempts. Since the TD is in the schedule at 16ms intervals, this will
; generate an interrupt at intervals of 32ms (when the TD is active). This 32ms
; periodic interrupt may then approximate the fastest keyboard repeat rate of
; 30 characters per second.
mov di, offset TdRepeat ;DI = ptr of TdRepeat
mov (Transfer_Descriptor ptr [di]).TD_Control_Status, TWO_ERRORS
mov (Transfer_Descriptor ptr [di]).TD_Token, IN_PACKET or (DUMMY_DEVICE_ADDR shl 8) or ((DEFAULT_PACKET_LENGTH - 1) shl 21) ;Set PID=In, Dev=Dummy, and MaxLen
lea ax, (Transfer_Descriptor ptr [di]).DataArea ;AX = ptr to TD's data buffer
movzx eax, ax ;Clear upper half of EAX
add eax, HcdDataArea ;EAX = abs addr of TD's data buffer
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -