common.inc
kolibrios-nextgen_kolibrios-kernel/kernel/bus/usb/common.inc
; Constants and structures that are shared between different parts of
; USB subsystem and *HCI drivers.

; =============================================================================
; ================================= Constants =================================
; =============================================================================
; Version of all structures related to host controllers.
; Must be the same in kernel and *hci-drivers.
USBHC_VERSION = 2

; USB device must have at least 100ms of stable power before initializing can
; proceed; one timer tick is 10ms, so enforce delay in 10 ticks
USB_CONNECT_DELAY = 10
; USB requires at least 10 ms for reset signalling. Normally, this is one timer
; tick. However, it is possible that we start reset signalling in the end of
; interval between timer ticks and then we test time in the start of the next
; interval; in this case, the delta between [timer_ticks] is 1, but the real
; time passed is significantly less than 10 ms. To avoid this, we add an extra
; tick; this guarantees that at least 10 ms have passed.
USB_RESET_TIME = 2
; USB requires at least 10 ms of reset recovery, a delay between reset
; signalling and any commands to device. Add an extra tick for the same reasons
; as with the previous constant.
USB_RESET_RECOVERY_TIME = 2

; USB pipe types
CONTROL_PIPE = 0
ISOCHRONOUS_PIPE = 1
BULK_PIPE = 2
INTERRUPT_PIPE = 3

; Status codes for transfer callbacks.
; Taken from OHCI as most verbose controller in this sense.
USB_STATUS_OK           = 0     ; no error
USB_STATUS_CRC          = 1     ; CRC error
USB_STATUS_BITSTUFF     = 2     ; bit stuffing violation
USB_STATUS_TOGGLE       = 3     ; data toggle mismatch
USB_STATUS_STALL        = 4     ; device returned STALL
USB_STATUS_NORESPONSE   = 5     ; device not responding
USB_STATUS_PIDCHECK     = 6     ; invalid PID check bits
USB_STATUS_WRONGPID     = 7     ; unexpected PID value
USB_STATUS_OVERRUN      = 8     ; too many data from endpoint
USB_STATUS_UNDERRUN     = 9     ; too few data from endpoint
USB_STATUS_BUFOVERRUN   = 12    ; overflow of internal controller buffer
USB_STATUS_BUFUNDERRUN  = 13    ; underflow of internal controller buffer
USB_STATUS_CLOSED       = 16    ; pipe closed
                                ; either explicitly with USBClosePipe
                                ; or implicitly due to device disconnect
USB_STATUS_CANCELLED    = 17    ; transfer cancelled with USBAbortPipe

; Possible speeds of USB devices
USB_SPEED_FS = 0 ; full-speed
USB_SPEED_LS = 1 ; low-speed
USB_SPEED_HS = 2 ; high-speed

; flags for usb_pipe.Flags
USB_FLAG_CLOSED     = 1         ; pipe is closed, no new transfers
; pipe is closed, return error instead of submitting any new transfer
USB_FLAG_CAN_FREE   = 2
; pipe is closed via explicit call to USBClosePipe, so it can be freed without
; any driver notification; if this flag is not set, then the pipe is closed due
; to device disconnect, so it must remain valid until return from disconnect
; callback provided by the driver
USB_FLAG_EXTRA_WAIT = 4
; The pipe was in wait list, while another event occured;
; when the first wait will be done, reinsert the pipe to wait list
USB_FLAG_DISABLED   = 8
; The pipe is temporarily disabled so that it is not visible to hardware
; but still remains in software list. Used for usb_abort_pipe.
USB_FLAG_CLOSED_BIT = 0 ; USB_FLAG_CLOSED = 1 shl USB_FLAG_CLOSED_BIT

; =============================================================================
; ================================ Structures =================================
; =============================================================================

; Description of controller-specific data and functions.
struct usb_hardware_func
Version         dd      ?       ; must be USBHC_VERSION
ID              dd      ?       ; '*HCI'
DataSize        dd      ?       ; sizeof(*hci_controller)
BeforeInit      dd      ?
; Early initialization: take ownership from BIOS.
; in: [ebp-4] = (bus shl 8) + devfn
Init            dd      ?
; Initialize controller-specific part of controller data.
; in: eax -> *hci_controller to initialize, [ebp-4] = (bus shl 8) + devfn
; out: eax = 0 <=> failed, otherwise eax -> usb_controller
ProcessDeferred dd      ?
; Called regularly from the main loop of USB thread
; (either due to timeout from a previous call, or due to explicit wakeup).
; in: esi -> usb_controller
; out: eax = maximum timeout for next call (-1 = infinity)
SetDeviceAddress        dd      ?
; in: esi -> usb_controller, ebx -> usb_pipe, cl = address
GetDeviceAddress        dd      ?
; in: esi -> usb_controller, ebx -> usb_pipe
; out: eax = address
PortDisable             dd      ?
; Disable the given port in the root hub.
; in: esi -> usb_controller, ecx = port (zero-based)
InitiateReset           dd      ?
; Start reset signalling on the given port.
; in: esi -> usb_controller, ecx = port (zero-based)
SetEndpointPacketSize   dd      ?
; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size
AllocPipe               dd      ?
; out: eax = pointer to allocated usb_pipe
FreePipe                dd      ?
; void stdcall with one argument = pointer to previously allocated usb_pipe
InitPipe                dd      ?
; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe,
; esi -> usb_controller, eax -> usb_gtd for the first TD,
; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type
UnlinkPipe              dd      ?
; esi -> usb_controller, ebx -> usb_pipe
AllocTD                 dd      ?
; out: eax = pointer to allocated usb_gtd
FreeTD                  dd      ?
; void stdcall with one argument = pointer to previously allocated usb_gtd
AllocTransfer           dd      ?
; Allocate and initialize one stage of a transfer.
; ebx -> usb_pipe, other parameters are passed through the stack:
; buffer,size = data to transfer
; flags = same as in usb_open_pipe:
;   bit 0 = allow short transfer, other bits reserved
; td = pointer to the current end-of-queue descriptor
; direction =
;   0000b for normal transfers,
;   1000b for control SETUP transfer,
;   1101b for control OUT transfer,
;   1110b for control IN transfer
; returns eax = pointer to the new end-of-queue descriptor
; (not included in the queue itself) or 0 on error
InsertTransfer          dd      ?
; Activate previously initialized transfer (maybe with multiple stages).
; esi -> usb_controller, ebx -> usb_pipe,
; [esp+4] -> first usb_gtd for the transfer,
; ecx -> last descriptor for the transfer
NewDevice               dd      ?
; Initiate configuration of a new device (create pseudo-pipe describing that
; device and call usb_new_device).
; esi -> usb_controller, eax = speed (one of USB_SPEED_* constants).
DisablePipe             dd      ?
; This procedure temporarily removes the given pipe from hardware queue.
; esi -> usb_controller, ebx -> usb_pipe
EnablePipe              dd      ?
; This procedure reinserts the given pipe to hardware queue
; after DisablePipe, with clearing transfer queue.
; esi -> usb_controller, ebx -> usb_pipe
; edx -> current descriptor, eax -> new last descriptor
ends

; pointers to kernel API functions that are called from *HCI-drivers
struct usbhc_func
usb_process_gtd                 dd      ?
usb_init_static_endpoint        dd      ?
usb_wakeup_if_needed            dd      ?
usb_subscribe_control           dd      ?
usb_subscription_done           dd      ?
usb_allocate_common             dd      ?
usb_free_common                 dd      ?
usb_td_to_virt                  dd      ?
usb_init_transfer               dd      ?
usb_undo_tds                    dd      ?
usb_test_pending_port           dd      ?
usb_get_tt                      dd      ?
usb_get_tt_think_time           dd      ?
usb_new_device                  dd      ?
usb_disconnect_stage2           dd      ?
usb_process_wait_lists          dd      ?
usb_unlink_td                   dd      ?
usb_is_final_packet             dd      ?
usb_find_ehci_companion         dd      ?
ends

; Controller descriptor.
; This structure represents the common (controller-independent) part
; of a controller for the USB code. The corresponding controller-dependent
; part *hci_controller is located immediately before usb_controller.
struct usb_controller
; Two following fields organize all controllers in the global linked list.
Next            dd      ?
Prev            dd      ?
HardwareFunc    dd      ?
; Pointer to usb_hardware_func structure with controller-specific functions.
NumPorts        dd      ?
; Number of ports in the root hub.
PCICoordinates  dd      ?
; Device:function and bus number from PCI.
;
; The hardware is allowed to cache some data from hardware structures.
; Regular operations are designed considering this,
; but sometimes it is required to wait for synchronization of hardware cache
; with modified structures in memory.
; The code keeps two queues of pipes waiting for synchronization,
; one for asynchronous (bulk/control) pipes, one for periodic pipes, hardware
; cache is invalidated under different conditions for those types.
; Both queues are organized in the same way, as single-linked lists.
; There are three special positions: the head of list (new pipes are added
; here), the first pipe to be synchronized at the current iteration,
; the tail of list (all pipes starting from here are synchronized).
WaitPipeListAsync       dd      ?
WaitPipeListPeriodic    dd      ?
; List heads.
WaitPipeRequestAsync    dd      ?
WaitPipeRequestPeriodic dd      ?
; Pending request to hardware to refresh cache for items from WaitPipeList*.
; (Pointers to some items in WaitPipeList* or NULLs).
ReadyPipeHeadAsync      dd      ?
ReadyPipeHeadPeriodic   dd      ?
; Items of RemovingList* which were released by hardware and are ready
; for further processing.
; (Pointers to some items in WaitPipeList* or NULLs).
NewConnected    dd      ?
; bit mask of recently connected ports of the root hub,
; bit set = a device was recently connected to the corresponding port;
; after USB_CONNECT_DELAY ticks of stable status these ports are moved to
; PendingPorts
NewDisconnected dd      ?
; bit mask of disconnected ports of the root hub,
; bit set = a device in the corresponding port was disconnected,
; disconnect processing is required.
PendingPorts    dd      ?
; bit mask of ports which are ready to be initialized
ControlLock     MUTEX   ?
; mutex which guards all operations with control queue
BulkLock        MUTEX   ?
; mutex which guards all operations with bulk queue
PeriodicLock    MUTEX   ?
; mutex which guards all operations with periodic queues
WaitSpinlock:
; spinlock guarding WaitPipeRequest/ReadyPipeHead (but not WaitPipeList)
StartWaitFrame  dd      ?
; USB frame number when WaitPipeRequest* was registered.
ResettingHub    dd      ?
; Pointer to usb_hub responsible for the currently resetting port, if any.
; NULL for the root hub.
ResettingPort   db      ?
; Port that is currently resetting, 0-based.
ResettingSpeed  db      ?
; Speed of currently resetting device.
ResettingStatus db      ?
; Status of port reset. 0 = no port is resetting, -1 = reset failed,
; 1 = reset in progress, 2 = reset recovery in progress.
                rb      1       ; alignment
ResetTime       dd      ?
; Time when reset signalling or reset recovery has been started.
SetAddressBuffer        rb      8
; Buffer for USB control command SET_ADDRESS.
ExistingAddresses       rd      128/32
; Bitmask for 128 bits; bit i is cleared <=> address i is free for allocating
; for new devices. Bit 0 is always set.
ConnectedTime   rd      16
; Time, in timer ticks, when the port i has signalled the connect event.
; Valid only if bit i in NewConnected is set.
DevicesByPort   rd      16
; Pointer to usb_pipe for zero endpoint (which serves as device handle)
; for each port.
ends

; Pipe descriptor.
; * An USB pipe is described by two structures, for hardware and for software.
; * This is the software part. The hardware part is defined in a driver
;   of the corresponding controller.
; * The hardware part is located immediately before usb_pipe,
;   both are allocated at once by controller-specific code
;   (it knows the total length, which depends on the hardware part).
struct usb_pipe
Controller      dd      ?
; Pointer to usb_controller structure corresponding to this pipe.
; Must be the first dword after hardware part, see *hci_new_device.
;
; Every endpoint is included into one of processing lists:
; * Bulk list contains all Bulk endpoints.
; * Control list contains all Control endpoints.
; * Several Periodic lists serve Interrupt endpoints with different interval.
;   - There are N=2^n "leaf" periodic lists for N ms interval, one is processed
;     in the frames 0,N,2N,..., another is processed in the frames
;     1,1+N,1+2N,... and so on. The hardware starts processing of periodic
;     endpoints in every frame from the list identified by lower n bits of the
;     frame number; the addresses of these N lists are written to the
;     controller data area during the initialization.
;   - We assume that n=5, N=32 to simplify the code and compact the data.
;     OHCI works in this way. UHCI and EHCI actually have n=10, N=1024,
;     but this is an overkill for interrupt endpoints; the large value of N is
;     useful only for isochronous transfers in UHCI and EHCI. UHCI/EHCI code
;     initializes "leaf" lists k,k+32,k+64,...,k+(1024-32) to the same value,
;     giving essentially N=32.
;     This restriction means that the actual maximum interval of polling any
;     interrupt endpoint is 32ms, which seems to be a reasonable value.
;   - Similarly, there are 16 lists for 16-ms interval, 8 lists for 8-ms
;     interval and so on. Finally, there is one list for 1ms interval. Their
;     addresses are not directly known to the controller.
;   - The hardware serves endpoints following a physical link from the hardware
;     part.
;   - The hardware links are organized as follows. If the list item is not the
;     last, it's hardware link points to the next item. The hardware link of
;     the last item points to the first item of the "next" list.
;   - The "next" list for k-th and (k+M)-th periodic lists for interval 2M ms
;     is the k-th periodic list for interval M ms, M >= 1. In this scheme,
;     if two "previous" lists are served in the frames k,k+2M,k+4M,...
;     and k+M,k+3M,k+5M,... correspondingly, the "next" list is served in
;     the frames k,k+M,k+2M,k+3M,k+4M,k+5M,..., which is exactly what we want.
;   - The links between Periodic, Control, Bulk lists and the processing of
;     Isochronous endpoints are controller-specific.
; * The head of every processing list is a static entry which does not
;   correspond to any real pipe. It is described by usb_static_ep
;   structure, not usb_pipe. For OHCI and UHCI, sizeof.usb_static_ep plus
;   sizeof hardware part is 20h, the total number of lists is
;   32+16+8+4+2+1+1+1 = 65, so all these structures fit in one page,
;   leaving space for other data. This is another reason for 32ms limit.
; * Static endpoint descriptors are kept in *hci_controller structure.
; * All items in every processing list, including the static head, are
;   organized in a double-linked list using .NextVirt and .PrevVirt fields.
; * [[item.NextVirt].PrevVirt] = [[item.PrevVirt].NextVirt] for all items.
NextVirt        dd      ?
; Next endpoint in the processing list.
; See also PrevVirt field and the description before NextVirt field.
PrevVirt        dd      ?
; Previous endpoint in the processing list.
; See also NextVirt field and the description before NextVirt field.
BaseList                dd      ?
; Pointer to head of the processing list.
;
; Every pipe has the associated transfer queue, that is, the double-linked
; list of Transfer Descriptors aka TD. For Control, Bulk and Interrupt
; endpoints this list consists of usb_gtd structures
; (GTD = General Transfer Descriptors), for Isochronous endpoints
; this list consists of usb_itd structures, which are not developed yet.
; The pipe needs to know only the last TD; the first TD can be
; obtained as [[pipe.LastTD].NextVirt].
LastTD          dd      ?
; Last TD in the transfer queue.
;
; All opened pipes corresponding to the same physical device are organized in
; the double-linked list using .NextSibling and .PrevSibling fields.
; The head of this list is kept in usb_device_data structure (OpenedPipeList).
; This list is used when the device is disconnected and all pipes for the
; device should be closed.
; Also, all pipes closed due to disconnect must remain valid at least until
; driver-provided disconnect function returns; all should-be-freed-but-not-now
; pipes for one device are organized in another double-linked list with
; the head in usb_device_data.ClosedPipeList; this list uses the same link
; fields, one pipe can never be in both lists.
NextSibling     dd      ?
; Next pipe for the physical device.
PrevSibling     dd      ?
; Previous pipe for the physical device.
;
; When hardware part of pipe is changed, some time is needed before further
; actions so that hardware reacts on this change. During that time,
; all changed pipes are organized in single-linked list with the head
; usb_controller.WaitPipeList* and link field NextWait.
; Currently there are two possible reasons to change:
; change of address/packet size in initial configuration,
; close of the pipe. They are distinguished by USB_FLAG_CLOSED.
NextWait        dd      ?
Lock            MUTEX
; Mutex that guards operations with transfer queue for this pipe.
Type            db      ?
; Type of pipe, one of {CONTROL,ISOCHRONOUS,BULK,INTERRUPT}_PIPE.
Flags           db      ?
; Combination of flags, USB_FLAG_*.
                rb      2       ; dword alignment
DeviceData      dd      ?
; Pointer to usb_device_data, common for all pipes for one device.
ends

; This structure describes the static head of every list of pipes.
struct usb_static_ep
; software fields
Bandwidth       dd      ?
; valid only for interrupt/isochronous USB1 lists
; The offsets of the following two fields must be the same in this structure
; and in usb_pipe.
NextVirt        dd      ?
PrevVirt        dd      ?
ends

; This structure represents one transfer descriptor
; ('g' stands for "general" as opposed to isochronous usb_itd).
; Note that one transfer can have several descriptors:
; a control transfer has three stages.
; Additionally, every controller has a limit on transfer length with
; one descriptor (packet size for UHCI, 1K for OHCI, 4K for EHCI),
; large transfers must be split into individual packets according to that limit.
struct usb_gtd
Callback        dd      ?
; Zero for intermediate descriptors, pointer to callback function
; for final descriptor. See the docs for description of the callback.
UserData        dd      ?
; Dword which is passed to Callback as is, not used by USB code itself.
; Two following fields organize all descriptors for one pipe in
; the linked list.
NextVirt        dd      ?
PrevVirt        dd      ?
Pipe            dd      ?
; Pointer to the parent usb_pipe.
Buffer          dd      ?
; Pointer to data for this descriptor.
Length          dd      ?
; Length of data for this descriptor.
ends

; Interface-specific data. Several interfaces of one device can operate
; independently, each is controlled by some driver and is identified by
; some driver-specific data passed as is to the driver.
struct usb_interface_data
DriverData      dd      ?
; Passed as is to the driver.
DriverFunc      dd      ?
; Pointer to USBSRV structure for the driver.
ends

; Device-specific data.
struct usb_device_data
PipeListLock    MUTEX
; Lock guarding OpenedPipeList. Must be the first item of the structure,
; the code passes pointer to usb_device_data as is to mutex_lock/unlock.
OpenedPipeList  rd      2
; List of all opened pipes for the device.
; Used when the device is disconnected, so all pipes should be closed.
ClosedPipeList  rd      2
; List of all closed, but still valid pipes for the device.
; A pipe closed with USBClosePipe is just deallocated,
; but a pipe closed due to disconnect must remain valid until driver-provided
; disconnect handler returns; this list links all such pipes to deallocate them
; after disconnect processing.
NumPipes        dd      ?
; Number of not-yet-closed pipes.
Hub             dd      ?
; NULL if connected to the root hub, pointer to usb_hub otherwise.
TTHub           dd      ?
; Pointer to usb_hub for (the) hub with Transaction Translator for the device,
; NULL if the device operates in the same speed as the controller.
Port            db      ?
; Port on the hub, zero-based.
TTPort          db      ?
; Port on the TTHub, zero-based.
DeviceDescrSize db      ?
; Size of device descriptor.
Speed           db      ?
; Device speed, one of USB_SPEED_*.
Timer           dd      ?
; Handle of timer that handles request timeout.
NumInterfaces   dd      ?
; Number of interfaces.
ConfigDataSize  dd      ?
; Total size of data associated with the configuration descriptor
; (including the configuration descriptor itself).
Interfaces      dd      ?
; Offset from the beginning of this structure to Interfaces field.
; Variable-length fields:
; DeviceDescriptor:
;  device descriptor starts here
; ConfigDescriptor = DeviceDescriptor + DeviceDescrSize
;  configuration descriptor with all associated data
; Interfaces = ALIGN_UP(ConfigDescriptor + ConfigDataSize, 4)
;  array of NumInterfaces elements of type usb_interface_data
ends

usb_device_data.DeviceDescriptor = sizeof.usb_device_data
