📄 usbmain.asm
字号:
call no_data_control ; handshake with host
ret ; for interface 0 then we are done
; Set the endpoint stall feature for the selected endpoint.
SetEndpointStall:
mov A, [wValue] ; load wValue
cmp A, endpoint_stall ; test for valid feature
jnz SendStall ; stall unsupported features
mov A,[wIndexHi]
cmp A,0
jnz SendStall
mov A,[wIndex]
cmp A,0 ; if stalling endpoint 0
jz SendStall
cmp A,80h ; in's or out, ack it but do nothing
jz SendStall ; about it
mov A, [configuration_status] ; if not configured
cmp A, UNCONFIGURED ; anything else is illegal
jz SendStall
mov A,[wIndex]
cmp A,81h
jnz .se2
mov A,1
mov [ep1_stall_status], A ; stalled
mov A, STALL ; stall endpoint one
iowr EP_A1_Mode
jmp .done
.se2:
cmp A,082h
jnz SendStall
mov A,1
mov [ep2_stall_status], A ; stalled
mov A, STALL ; stall endpoint two
iowr EP_A2_Mode
.done:
call no_data_control ; handshake with host
ret ; return
; The device status is a 16-bit value (two bytes) with only D[1:0]
; defined. D0=0 specifies bus-powered, which never changes. D1
; reflects the status of the device_remote_wakeup feature. This
; feature can either be disabled (D1=0) or enabled (D1=1).
GetDeviceStatus:
mov A, 2 ; send two bytes
mov [data_count], A
mov A,[remote_wakeup_status]
jmp Send
; There are five descriptor types. The descriptor type will be in
; the high byte of wValue. The descriptor index will be in the low
; byte of wValue. The standard request to a device supports three
; of these types: device, configuration, and string. The standard
; request does not support interface or endpoint descriptor types.
GetDescriptor:
mov X,report_id_tbl_len ;init X to size of descriptor type/index table
.loop:
dec X
jc SendStall ;if X is neg, table has been completely traversed
dec X
mov A,X
index report_id_tbl ;else get 1st byte of table entry
cmp A,[wValueHi] ;compare with descriptor type
jnz .loop ;no match, contine
mov A,X ;else get 2nd byte of table entry
index report_id_tbl+1 ;compare with decrptor index
cmp A,[wValue] ;
jnz .loop ;no match, continue
.found: ;X points to table entry
mov A,X
asr A ;make it a unary index
mov X,A ;and save it
mov A,[wValueHi] ;if we're getting a HID descriptor
cmp A,HID
jz .adjust
cmp A,report ;or a report descriptor,
jz .adjust ;adjust it
jmp .continue
.adjust:
mov A,[wIndexHi]
cmp A,0
jnz SendStall
mov A,[wIndex] ;wIndex indicates which of 2
cmp A,2
jnc SendStall
mov A,X
dec A ;adjust the index in X by this value
add A,[wIndex]
mov X,A
.continue:
mov A,X ;A now contains index of exact descriptor we require
jmp SendRom ;
XPAGEOFF
report_id_tbl:
db device,00,configuration,00,string,00
db string,01,string,02,string,03,string,04,string,05,string,06
db HID,00,HID,00,report,00,report,00
report_id_tbl_end:
XPAGEON
report_id_tbl_len: EQU (report_id_tbl_end - report_id_tbl)
; Return the current device configuration to the host. The possible
; values are zero (unconfigured) and one (configured).
GetConfiguration:
mov A, 1 ; send one byte
mov [data_count], A
mov A,[configuration_status]
jmp Send
; The interface status is a 16-bit value (two bytes) that is always
; zero for both bytes.
GetInterfaceStatus:
mov A,2
mov [data_count],A
mov A,0
jmp Send
; The interface status is an 8bit value that is always zero since
; there are no alternate interface settings defined
GetInterface:
call Interface0
jz .get
call Interface1
jnz SendStall
.get:
mov A,1
mov [data_count],A
mov A,0
jmp Send
; The endpoint status is a 16-bit value (two bytes) with only one
; bit (D0) defined. If D0=0, then the selected endpoint is not
; stalled. If D0=1, then the selected endpoint is stalled.
GetEndpointStatus:
mov A, 2 ; send two bytes
mov [data_count], A
mov A,[wIndexHi]
cmp A,0
jnz SendStall
mov A,[wIndex]
cmp A,0
jz Send ;endpoint 0 status, always not stalled
cmp A,80h
mov A,0
jz Send ;endpoint 0 status again
mov A, [configuration_status]
cmp A, UNCONFIGURED ;if unconfigured, nothing else to do
jz SendStall
mov A,[wIndex] ;EP1 status?
cmp A,081h
jnz .ge2
mov A, [ep1_stall_status] ;send status of EP1
jmp Send
.ge2:
cmp A,082h ;EP2 status?
jnz SendStall
mov A,[ep2_stall_status]
jmp Send ;send status of EP2
;------------------------------------------------------------------------
; Set Report is used to control the LEDs in the keyboard. An OUT packet
; will be received that will have a bit set for every LED that should be
; turned on.
SetReport:
call Interface0
jnz SendStall
mov A,[wValueHi]
cmp A,02
jnz SendStall
mov A, [configuration_status]
cmp A, UNCONFIGURED ;ignore if unconfigures
jz SendStall
mov A, ACKOUTSTATUSIN ; accept OUT, enable TX0 IN
mov [EP0_Next_Mode],A
ret
;table to translate usb-command enumeration of leds into our own bits
XPAGEOFF
usb_led_tbl:
db 0
db NUM_LOCK_LED
db CAPS_LOCK_LED
db NUM_LOCK_LED + CAPS_LOCK_LED
db SCROLL_LOCK_LED
db SCROLL_LOCK_LED + NUM_LOCK_LED
db SCROLL_LOCK_LED + CAPS_LOCK_LED
db SCROLL_LOCK_LED + NUM_LOCK_LED + CAPS_LOCK_LED
XPAGEON
; Set Idle silences a particular report on the interrupt pipe until a new
; event occurs or the specified amount of time (wValueHi) passes.
; The resolution of the specified duration is 4ms (i.e the weight of the
; LSB is 4ms)
SetIdle:
call Interface0
jz .SetIfc0Idle
call Interface1
jz .SetIfc1Idle
jmp SendStall ;else it's a bogus request
.SetIfc0Idle: ;
mov A,kbd_idle_period ;point A at base of keyboard idle table
call AdjustIdle
jmp .done_SetIdle
.SetIfc1Idle: ;interface 1 supports both consumer and power keys
mov A,[wValue]
cmp A,0 ;if setting all idles,
jnz .setifc01
mov X,id_tbl_len ;init X to length of report ID table
.all_loop:
dec X
jc .done_SetIdle ;done, get out
mov A,X
index idle_tbl
call AdjustIdle ;adjust idle for this report
jmp .all_loop ;and get out
.setifc01: ;else if setting only one
mov X,id_tbl_len ;init X to size of report id table
.loop:
dec X
jc SendStall ;if X is neg, table has been completely traversed
mov A,X
index id_tbl ;else get byte of table entry
cmp A,[wValue] ;compare with report ID
jnz .loop ;no match, contine
.found: ;X points to table index
mov A,X
index idle_tbl ;get address of idle setting variable
call AdjustIdle ;adjust idle for this index
.done_SetIdle:
call no_data_control ; handshake with host
ret
AdjustIdle:
;entered with A = address of idle variable
push X
mov X,A
mov A,[X+0] ; get current idle period
cmp A,0 ; if zero,
jnz .set1
mov A,[wValueHi] ; institute new idle period immediately
mov [X+0],A
mov [X+1],A
jmp .exit
.set1:
mov A,[X+1] ;else get time left in current idle period
cmp A,2
jc .start_new_idle_period ;if less than 4 ms,
mov A,[wValueHi] ; write next idle period only
mov [X+0],A ;and leave current idle period alone
jmp .exit
.start_new_idle_period:
mov A,[wValueHi] ;compare new period with old
cmp A,[X+0] ;if new period is shorter than the old,
mov [X+0],A
jnc .set2
mov A,1 ;set next report period and
mov [X+1],A ;and force a new report immediately
jmp .exit
.set2: ;else new period is longer than old,
sub A,[X+1] ;so adjust current period to expire sooner
mov [X+1],A
.exit:
pop X
ret
; Set Protocol switches between the boot protocol and the report protocol.
; For boot protocol, wValue=0. For report protocol, wValue=1.
SetProtocol:
call Interface0
jz .setprot0
call Interface1
jnz SendStall
mov A,[wValue]
cmp A,1
jnz SendStall
jmp .done
.setprot0:
mov A, [wValue] ; load wValue
cmp A, REPORT_PROTOCOL+1 ; only valid protocols are BOOT & REPORT
jnc SendStall
mov [protocol_status], A ; write new protocol value
.done:
call no_data_control ; handshake with host
ret ; return
; Get Report returns a report over the control pipe
GetReport:
mov A, [configuration_status] ;skip if unconfigured
cmp A, UNCONFIGURED
jz SendStall
call Interface0 ;check which interface requested
jz .getifc0
call Interface1
jnz SendStall
jmp .getifc1
.getifc0: ;interface 0!
mov A,[wValueHi]
cmp A,2
jz .getleds ;wValueHi = 2, get led report
cmp A,1
jnz SendStall
mov A,8 ;wValueHi = 1, get keyboard report
mov [data_count],A
mov A,[usb_current_state]
cmp A,USB_REPORT_ERROR
jnz .keys
mov A,0 ;if we need to send an error report
index key_error_offset
mov [page],A ;point at ROM with all 1's
mov A,0
index key_error_offset + 1
mov [data_start],A
jmp SendAll
.keys: ; else we need to send the key buffer
mov A, usb_report_buffer ; set the start address to report_buffer
jmp SendRam ; send report (from RAM) to host
.getifc1: ;interface 1!
cmp A,1
jnz SendStall ;anything other than interface 1 not supported
mov X,id_tbl_len ;init X to length of report ID table
.loop:
dec X
jc SendStall ;if X is neg, table has been completely traversed
mov A,X
index id_tbl ;else get id
cmp A,[wValue] ;compare with requested report ID
jnz .loop ;no match, contine
.found: ;X now indexes to report ID
mov A,X
index report_len_tbl ;use it to get the report length
mov [data_count],A ;store this in data count
mov A,X
index report_addr_tbl ;now get starting address in RAM of report
jmp SendRam ;and go send it
.getleds:
mov A,1
mov [data_count],A
mov A,[usb_leds]
jmp Send
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -