Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 4549 → Rev 5051

/drivers/usb/usbstor.asm
1,5 → 1,6
; standard driver stuff
format MS COFF
; standard driver stuff; version of driver model = 5
format PE DLL native 0.05
entry START
 
DEBUG = 1
DUMP_PACKETS = 0
8,13 → 9,8
__DEBUG__ = 1
__DEBUG_LEVEL__ = 1
 
include 'proc32.inc'
include 'imports.inc'
include 'fdo.inc'
include '../struct.inc'
 
public START
public version
 
; USB constants
DEVICE_DESCR_TYPE = 1
CONFIG_DESCR_TYPE = 2
29,43 → 25,37
INTERRUPT_PIPE = 3
 
; USB structures
virtual at 0
config_descr:
.bLength db ?
.bDescriptorType db ?
.wTotalLength dw ?
.bNumInterfaces db ?
.bConfigurationValue db ?
.iConfiguration db ?
.bmAttributes db ?
.bMaxPower db ?
.sizeof:
end virtual
struct config_descr
bLength db ?
bDescriptorType db ?
wTotalLength dw ?
bNumInterfaces db ?
bConfigurationValue db ?
iConfiguration db ?
bmAttributes db ?
bMaxPower db ?
ends
 
virtual at 0
interface_descr:
.bLength db ?
.bDescriptorType db ?
.bInterfaceNumber db ?
.bAlternateSetting db ?
.bNumEndpoints db ?
.bInterfaceClass db ?
.bInterfaceSubClass db ?
.bInterfaceProtocol db ?
.iInterface db ?
.sizeof:
end virtual
struct interface_descr
bLength db ?
bDescriptorType db ?
bInterfaceNumber db ?
bAlternateSetting db ?
bNumEndpoints db ?
bInterfaceClass db ?
bInterfaceSubClass db ?
bInterfaceProtocol db ?
iInterface db ?
ends
 
virtual at 0
endpoint_descr:
.bLength db ?
.bDescriptorType db ?
.bEndpointAddress db ?
.bmAttributes db ?
.wMaxPacketSize dw ?
.bInterval db ?
.sizeof:
end virtual
struct endpoint_descr
bLength db ?
bDescriptorType db ?
bEndpointAddress db ?
bmAttributes db ?
wMaxPacketSize dw ?
bInterval db ?
ends
 
; Mass storage protocol constants, USB layer
REQUEST_GETMAXLUN = 0xFE ; get max lun
73,38 → 63,28
 
; Mass storage protocol structures, USB layer
; Sent from host to device in the first stage of an operation.
struc command_block_wrapper
{
.Signature dd ? ; the constant 'USBC'
.Tag dd ? ; identifies response with request
.Length dd ? ; length of data-transport phase
.Flags db ? ; one of CBW_FLAG_*
struct command_block_wrapper
Signature dd ? ; the constant 'USBC'
Tag dd ? ; identifies response with request
Length dd ? ; length of data-transport phase
Flags db ? ; one of CBW_FLAG_*
CBW_FLAG_OUT = 0
CBW_FLAG_IN = 80h
.LUN db ? ; addressed unit
.CommandLength db ? ; the length of the following field
.Command rb 16
.sizeof:
}
virtual at 0
command_block_wrapper command_block_wrapper
end virtual
LUN db ? ; addressed unit
CommandLength db ? ; the length of the following field
Command rb 16
ends
 
; Sent from device to host in the last stage of an operation.
struc command_status_wrapper
{
.Signature dd ? ; the constant 'USBS'
.Tag dd ? ; identifies response with request
.LengthRest dd ? ; .Length - (size of data which were transferred)
.Status db ? ; one of CSW_STATUS_*
struct command_status_wrapper
Signature dd ? ; the constant 'USBS'
Tag dd ? ; identifies response with request
LengthRest dd ? ; .Length - (size of data which were transferred)
Status db ? ; one of CSW_STATUS_*
CSW_STATUS_OK = 0
CSW_STATUS_FAIL = 1
CSW_STATUS_FATAL = 2
.sizeof:
}
virtual at 0
command_status_wrapper command_status_wrapper
end virtual
ends
 
; Constants of SCSI layer
SCSI_REQUEST_SENSE = 3
133,73 → 113,62
 
; Structures of SCSI layer
; Result of SCSI INQUIRY request.
struc inquiry_data
{
.PeripheralDevice db ? ; lower 5 bits are PeripheralDeviceType
struct inquiry_data
PeripheralDevice db ? ; lower 5 bits are PeripheralDeviceType
; upper 3 bits are PeripheralQualifier
.RemovableMedium db ? ; upper bit is RemovableMedium
RemovableMedium db ? ; upper bit is RemovableMedium
; other bits are for compatibility
.Version db ? ; lower 3 bits are ANSI-Approved version
Version db ? ; lower 3 bits are ANSI-Approved version
; next 3 bits are ECMA version
; upper 2 bits are ISO version
.ResponseDataFormat db ? ; lower 4 bits are ResponseDataFormat
ResponseDataFormat db ? ; lower 4 bits are ResponseDataFormat
; bit 6 is TrmIOP
; bit 7 is AENC
.AdditionalLength db ?
AdditionalLength db ?
dw ? ; reserved
.Flags db ?
.VendorID rb 8 ; vendor ID, big-endian
.ProductID rb 16 ; product ID, big-endian
.ProductRevBE dd ? ; product revision, big-endian
.sizeof:
}
virtual at 0
inquiry_data inquiry_data
end virtual
Flags db ?
VendorID rb 8 ; vendor ID, big-endian
ProductID rb 16 ; product ID, big-endian
ProductRevBE dd ? ; product revision, big-endian
ends
 
struc sense_data
{
.ErrorCode db ? ; lower 7 bits are error code:
struct sense_data
ErrorCode db ? ; lower 7 bits are error code:
; 70h = current error,
; 71h = deferred error
; upper bit is InformationValid
.SegmentNumber db ? ; number of segment descriptor
SegmentNumber db ? ; number of segment descriptor
; for commands COPY [+VERIFY], COMPARE
.SenseKey db ? ; bits 0-3 are one of SENSE_*
SenseKey db ? ; bits 0-3 are one of SENSE_*
; bit 4 is reserved
; bit 5 is IncorrectLengthIndicator
; bits 6 and 7 are used by
; sequential-access devices
.Information dd ? ; command-specific
.AdditionalLength db ? ; length of data starting here
.CommandInformation dd ? ; command-specific
.AdditionalSenseCode db ? ; \ more detailed error code
.AdditionalSenseQual db ? ; / standard has a large table of them
.FRUCode db ? ; which part of device has failed
Information dd ? ; command-specific
AdditionalLength db ? ; length of data starting here
CommandInformation dd ? ; command-specific
AdditionalSenseCode db ? ; \ more detailed error code
AdditionalSenseQual db ? ; / standard has a large table of them
FRUCode db ? ; which part of device has failed
; (device-specific, not regulated)
.SenseKeySpecific rb 3 ; depends on SenseKey
.sizeof:
}
virtual at 0
sense_data sense_data
end virtual
SenseKeySpecific rb 3 ; depends on SenseKey
ends
 
; Device data
; USB Mass storage device has one or more logical units, identified by LUN,
; logical unit number. The highest value of LUN, that is, number of units
; minus 1, can be obtained via control request Get Max LUN.
virtual at 0
usb_device_data:
.ConfigPipe dd ? ; configuration pipe
.OutPipe dd ? ; pipe for OUT bulk endpoint
.InPipe dd ? ; pipe for IN bulk endpoint
.MaxLUN dd ? ; maximum Logical Unit Number
.LogicalDevices dd ? ; pointer to array of usb_unit_data
struct usb_device_data
ConfigPipe dd ? ; configuration pipe
OutPipe dd ? ; pipe for OUT bulk endpoint
InPipe dd ? ; pipe for IN bulk endpoint
MaxLUN dd ? ; maximum Logical Unit Number
LogicalDevices dd ? ; pointer to array of usb_unit_data
; 1 for a connected USB device, 1 for each disk device
; the structure can be freed when .NumReferences decreases to zero
.NumReferences dd ? ; number of references
.ConfigRequest rb 8 ; buffer for configuration requests
.LengthRest dd ? ; Length - (size of data which were transferred)
NumReferences dd ? ; number of references
ConfigRequest rb 8 ; buffer for configuration requests
LengthRest dd ? ; Length - (size of data which were transferred)
; All requests to a given device are serialized,
; only one request to a given device can be processed at a time.
; The current request and all pending requests are organized in the following
208,46 → 177,42
; data stage is not tagged (unlike command_*_wrapper), so the only way to know
; what request the data are associated with is to guarantee that only one
; request is processing at the time.
.RequestsQueue rd 2
.QueueLock rd 3 ; protects .RequestsQueue
.InquiryData inquiry_data ; information about device
RequestsQueue rd 2
QueueLock rd 3 ; protects .RequestsQueue
InquiryData inquiry_data ; information about device
; data for the current request
.Command command_block_wrapper
.DeviceDisconnected db ?
.Status command_status_wrapper
.Sense sense_data
.sizeof:
end virtual
Command command_block_wrapper
DeviceDisconnected db ?
Status command_status_wrapper
Sense sense_data
ends
 
; Information about one logical device.
virtual at 0
usb_unit_data:
.Parent dd ? ; pointer to parent usb_device_data
.LUN db ? ; index in usb_device_data.LogicalDevices array
.DiskIndex db ? ; for name "usbhd<index>"
.MediaPresent db ?
struct usb_unit_data
Parent dd ? ; pointer to parent usb_device_data
LUN db ? ; index in usb_device_data.LogicalDevices array
DiskIndex db ? ; for name "usbhd<index>"
MediaPresent db ?
db ? ; alignment
.DiskDevice dd ? ; handle of disk device or NULL
.SectorSize dd ? ; sector size
DiskDevice dd ? ; handle of disk device or NULL
SectorSize dd ? ; sector size
; For some devices, the first request to the medium fails with 'unit not ready'.
; When the code sees this status, it retries the command several times.
; Two following variables track the retry count and total time for those;
; total time is currently used only for debug output.
.UnitReadyAttempts dd ?
.TimerTicks dd ?
.sizeof:
end virtual
UnitReadyAttempts dd ?
TimerTicks dd ?
ends
 
; This is the structure for items in the queue usb_device_data.RequestsQueue.
virtual at 0
request_queue_item:
.Next dd ? ; next item in the queue
.Prev dd ? ; prev item in the queue
.ReqBuilder dd ? ; procedure to fill command_block_wrapper
.Buffer dd ? ; input or output data
struct request_queue_item
Next dd ? ; next item in the queue
Prev dd ? ; prev item in the queue
ReqBuilder dd ? ; procedure to fill command_block_wrapper
Buffer dd ? ; input or output data
; (length is command_block_wrapper.Length)
.Callback dd ? ; procedure to call in the end of transfer
.UserData dd ? ; passed as-is to .Callback
Callback dd ? ; procedure to call in the end of transfer
UserData dd ? ; passed as-is to .Callback
; There are 3 possible stages of any request, one of them optional:
; command stage (host sends command_block_wrapper to device),
; optional data stage,
255,16 → 220,21
; Also, if a request fails, the code queues additional request
; SCSI_REQUEST_SENSE; sense_data from SCSI_REQUEST_SENSE
; contains some information about the error.
.Stage db ?
.sizeof:
end virtual
Stage db ?
ends
 
section '.flat' code readable align 16
section '.flat' code readable writable executable
include '../proc32.inc'
include '../peimport.inc'
include '../fdo.inc'
include '../macros.inc'
 
; The start procedure.
proc START
virtual at esp
dd ? ; return address
.reason dd ? ; DRV_ENTRY or DRV_EXIT
.cmdline dd ?
end virtual
; 1. Test whether the procedure is called with the argument DRV_ENTRY.
; If not, return 0.
273,14 → 243,14
jnz .nothing
; 2. Initialize: we have one global mutex.
mov ecx, free_numbers_lock
call MutexInit
invoke MutexInit
; 3. Register self as a USB driver.
; The name is my_driver = 'usbstor'; IOCTL interface is not supported;
; usb_functions is an offset of a structure with callback functions.
stdcall RegUSBDriver, my_driver, 0, usb_functions
invoke RegUSBDriver, my_driver, 0, usb_functions
; 4. Return the returned value of RegUSBDriver.
.nothing:
ret 4
ret
endp
 
; Helper procedures to work with requests queue.
298,12 → 268,12
.UserData dd ? ; request_queue_item.UserData
end virtual
; 1. Allocate the memory for the request description.
movi eax, request_queue_item.sizeof
call Kmalloc
movi eax, sizeof.request_queue_item
invoke Kmalloc
test eax, eax
jnz @f
mov esi, nomemory
call SysMsgBoardStr
invoke SysMsgBoardStr
pop esi ebx
ret 20
@@:
322,7 → 292,7
; 4. Lock the queue.
mov esi, [.device]
lea ecx, [esi+usb_device_data.QueueLock]
call MutexLock
invoke MutexLock
; 5. Insert the request to the tail of the queue.
add esi, usb_device_data.RequestsQueue
mov edx, [esi+request_queue_item.Prev]
340,7 → 310,7
call setup_request
jmp .nothing
.unlock:
call MutexUnlock
invoke MutexUnlock
; 9. Return.
.nothing:
pop esi ebx
369,7 → 339,7
stdcall [ebx+request_queue_item.Callback], esi, [ebx+request_queue_item.UserData]
; 4. Lock the queue.
lea ecx, [esi+usb_device_data.QueueLock]
call MutexLock
invoke MutexLock
; 5. Remove the request.
lea edx, [esi+usb_device_data.RequestsQueue]
mov eax, [ebx+request_queue_item.Next]
378,7 → 348,7
; 6. Free the request memory.
push eax edx
xchg eax, ebx
call Kfree
invoke Kfree
pop edx ebx
; 7. If there is a next request, start processing.
cmp ebx, edx
385,7 → 355,7
jnz setup_request
; 8. Unlock the queue and return.
lea ecx, [esi+usb_device_data.QueueLock]
call MutexUnlock
invoke MutexUnlock
ret
endp
 
410,7 → 380,7
lea edx, [esi+usb_device_data.ConfigRequest]
mov word [edx], (REQUEST_BORESET shl 8) + 21h ; class request
mov word [edx+6], ax ; length = 0
stdcall USBControlTransferAsync, [esi+usb_device_data.ConfigPipe], edx, eax, eax, recovery_callback1, esi, eax
invoke USBControlTransferAsync, [esi+usb_device_data.ConfigPipe], edx, eax, eax, recovery_callback1, esi, eax
; 2b. Fail here = fatal error.
test eax, eax
jz .fatal
417,7 → 387,7
; 2c. Otherwise, unlock the queue and return. recovery_callback1 will continue processing.
.unlock_return:
lea ecx, [esi+usb_device_data.QueueLock]
call MutexUnlock
invoke MutexUnlock
ret
.norecovery:
; 3. Send the command. Fail (no memory or device disconnected) = fatal error.
429,7 → 399,7
; 4. Fatal error. Set status = FATAL, unlock the queue, complete the request.
mov [esi+usb_device_data.Status.Status], CSW_STATUS_FATAL
lea ecx, [esi+usb_device_data.QueueLock]
call MutexUnlock
invoke MutexUnlock
jmp complete_request
endp
 
453,11 → 423,11
if DUMP_PACKETS
DEBUGF 1,'K : USBSTOR out:'
mov eax, edx
mov ecx, command_block_wrapper.sizeof
mov ecx, sizeof.command_block_wrapper
call debug_dump
DEBUGF 1,'\n'
end if
stdcall USBNormalTransferAsync, [esi+usb_device_data.OutPipe], edx, command_block_wrapper.sizeof, request_callback1, esi, 0
invoke USBNormalTransferAsync, [esi+usb_device_data.OutPipe], edx, sizeof.command_block_wrapper, request_callback1, esi, 0
test eax, eax
jz .nothing
; 5. If the next stage is data stage in the same direction, enqueue it here.
473,7 → 443,7
call debug_dump
DEBUGF 1,'\n'
end if
stdcall USBNormalTransferAsync, [esi+usb_device_data.OutPipe], [edx+request_queue_item.Buffer], [esi+usb_device_data.Command.Length], request_callback2, esi, 0
invoke USBNormalTransferAsync, [esi+usb_device_data.OutPipe], [edx+request_queue_item.Buffer], [esi+usb_device_data.Command.Length], request_callback2, esi, 0
.nothing:
ret
endp
551,7 → 521,7
cmp [ecx+usb_device_data.Command.Flags], 0
jns .nothing
; 5. Initiate USB transfer. If this fails, go to the error handler.
stdcall USBNormalTransferAsync, [ecx+usb_device_data.InPipe], [edx+request_queue_item.Buffer], [ecx+usb_device_data.Command.Length], request_callback2, ecx, 0
invoke USBNormalTransferAsync, [ecx+usb_device_data.InPipe], [edx+request_queue_item.Buffer], [ecx+usb_device_data.Command.Length], request_callback2, ecx, 0
test eax, eax
jz .error
; 6. The status stage goes to the same direction, enqueue it now.
625,7 → 595,7
; 4. Initiate USB transfer. If this fails, go to the error handler.
..enqueue_status:
lea edx, [ecx+usb_device_data.Status]
stdcall USBNormalTransferAsync, [ecx+usb_device_data.InPipe], edx, command_status_wrapper.sizeof, request_callback3, ecx, 0
invoke USBNormalTransferAsync, [ecx+usb_device_data.InPipe], edx, sizeof.command_status_wrapper, request_callback3, ecx, 0
test eax, eax
jz .error
.nothing:
698,7 → 668,7
; 6. Invalid status block. Say error, set status to fatal and complete request.
push esi
mov esi, invresponse
call SysMsgBoardStr
invoke SysMsgBoardStr
pop esi
mov [esi+usb_device_data.Status.Status], CSW_STATUS_FATAL
jmp .complete
728,10 → 698,10
; edx = first argument = pointer to usb_device_data.Command,
; second argument = custom data given to queue_request (ignored).
proc request_sense_req
mov [edx+command_block_wrapper.Length], sense_data.sizeof
mov [edx+command_block_wrapper.Length], sizeof.sense_data
mov [edx+command_block_wrapper.Flags], CBW_FLAG_IN
mov byte [edx+command_block_wrapper.Command+0], SCSI_REQUEST_SENSE
mov byte [edx+command_block_wrapper.Command+4], sense_data.sizeof
mov byte [edx+command_block_wrapper.Command+4], sizeof.sense_data
ret 8
endp
 
780,7 → 750,7
jz .known
; 1c. If the device is unknown, print a message and go to 11c.
mov esi, unkdevice
call SysMsgBoardStr
invoke SysMsgBoardStr
jmp .nothing
; 1d. If the device uses known command set, print a message and continue
; configuring.
787,18 → 757,18
.known:
push esi
mov esi, okdevice
call SysMsgBoardStr
invoke SysMsgBoardStr
pop esi
; 2. Allocate memory for internal device data.
; 2a. Call the kernel.
mov eax, usb_device_data.sizeof
call Kmalloc
mov eax, sizeof.usb_device_data
invoke Kmalloc
; 2b. Check return value.
test eax, eax
jnz @f
; 2c. If failed, say a message and go to 11c.
mov esi, nomemory
call SysMsgBoardStr
invoke SysMsgBoardStr
jmp .nothing
@@:
; 2d. If succeeded, zero the contents and continue configuring.
829,7 → 799,7
mov [eax+request_queue_item.Next], eax
mov [eax+request_queue_item.Prev], eax
lea ecx, [ebx+usb_device_data.QueueLock]
call MutexInit
invoke MutexInit
; Bulk-only mass storage devices use one OUT bulk endpoint for sending
; command/data and one IN bulk endpoint for receiving data/status.
; Look for those endpoints.
862,9 → 832,9
jnz .lookep
; 5. Check that the descriptor contains all required data and all data are
; readable. The opposite is an error.
cmp byte [esi+endpoint_descr.bLength], endpoint_descr.sizeof
cmp byte [esi+endpoint_descr.bLength], sizeof.endpoint_descr
jb .errorep
lea ecx, [esi+endpoint_descr.sizeof]
lea ecx, [esi+sizeof.endpoint_descr]
cmp ecx, edx
ja .errorep
; 6. Check that the endpoint is bulk endpoint. The opposite is an error.
887,7 → 857,7
movzx edx, [esi+endpoint_descr.wMaxPacketSize]
movzx eax, [esi+endpoint_descr.bInterval] ; not used for USB1, may be important for USB2
; 9c. Call the kernel.
stdcall USBOpenPipe, [ebx+usb_device_data.ConfigPipe], ecx, edx, BULK_PIPE, eax
invoke USBOpenPipe, [ebx+usb_device_data.ConfigPipe], ecx, edx, BULK_PIPE, eax
; 9d. Restore registers.
pop edx ecx
; 9e. Check result. If failed, go to 11b.
907,7 → 877,7
.free:
; 11b. Free the allocated usb_device_data.
xchg eax, ebx
call Kfree
invoke Kfree
.nothing:
; 11c. Return an error.
xor eax, eax
922,7 → 892,7
if DUMP_PACKETS
DEBUGF 1,'K : GETMAXLUN: %x %x %x %x %x %x %x %x\n',[eax]:2,[eax+1]:2,[eax+2]:2,[eax+3]:2,[eax+4]:2,[eax+5]:2,[eax+6]:2,[eax+7]:2
end if
stdcall USBControlTransferAsync, [ebx+usb_device_data.ConfigPipe], eax, ecx, 1, known_lun_callback, ebx, 0
invoke USBControlTransferAsync, [ebx+usb_device_data.ConfigPipe], eax, ecx, 1, known_lun_callback, ebx, 0
; 13. Return with pointer to device data as returned value.
xchg eax, ebx
.return:
955,15 → 925,15
mov eax, [ebx+usb_device_data.MaxLUN]
inc eax
DEBUGF 1,'K : %d logical unit(s)\n',eax
imul eax, usb_unit_data.sizeof
imul eax, sizeof.usb_unit_data
push ebx
call Kmalloc
invoke Kmalloc
pop ebx
; If failed, print a message and do nothing.
test eax, eax
jnz @f
mov esi, nomemory
call SysMsgBoardStr
invoke SysMsgBoardStr
pop esi ebx
ret 20
@@:
980,12 → 950,12
mov [esi+usb_unit_data.SectorSize], eax
mov [esi+usb_unit_data.UnitReadyAttempts], eax
push ecx
call GetTimerTicks
invoke GetTimerTicks
mov [esi+usb_unit_data.TimerTicks], eax
stdcall queue_request, ebx, test_unit_ready_req, 0, test_unit_ready_callback, esi
pop ecx
inc ecx
add esi, usb_unit_data.sizeof
add esi, sizeof.usb_unit_data
cmp ecx, [ebx+usb_device_data.MaxLUN]
jbe .looplun
; 4. Return.
999,11 → 969,11
proc inquiry_req
mov eax, [esp+8]
mov al, [eax+usb_unit_data.LUN]
mov [edx+command_block_wrapper.Length], inquiry_data.sizeof
mov [edx+command_block_wrapper.Length], sizeof.inquiry_data
mov [edx+command_block_wrapper.Flags], CBW_FLAG_IN
mov [edx+command_block_wrapper.LUN], al
mov byte [edx+command_block_wrapper.Command+0], SCSI_INQUIRY
mov byte [edx+command_block_wrapper.Command+4], inquiry_data.sizeof
mov byte [edx+command_block_wrapper.Command+4], sizeof.inquiry_data
ret 8
endp
 
1034,7 → 1004,7
movi ebx, 1
mov ecx, new_disk_thread
; edx = parameter
call CreateThread
invoke CreateThread
pop edi esi ecx ebx
cmp eax, -1
jnz .nothing
1046,7 → 1016,7
; 4. The command has failed. Print a message and do nothing.
push esi
mov esi, inquiry_fail
call SysMsgBoardStr
invoke SysMsgBoardStr
pop esi
ret 8
endp
1079,7 → 1049,7
; possibly after some repetitions. Print a debug message showing
; number and time of those. Remember that media is ready and go to 4.
DEBUGF 1,'K : media is ready\n'
call GetTimerTicks
invoke GetTimerTicks
sub eax, [edx+usb_unit_data.TimerTicks]
DEBUGF 1,'K : %d attempts, %d ticks\n',[edx+usb_unit_data.UnitReadyAttempts],eax
inc [edx+usb_unit_data.MediaPresent]
1094,7 → 1064,7
jz @f
push ecx edx esi
movi esi, 10
call Sleep
invoke Sleep
pop esi edx ecx
stdcall queue_request, ecx, test_unit_ready_req, 0, test_unit_ready_callback, edx
ret 8
1120,7 → 1090,7
; 1. Generate name.
; 1a. Find a free index.
mov ecx, free_numbers_lock
call MutexLock
invoke MutexLock
xor eax, eax
@@:
bsf edx, [free_numbers+eax]
1128,10 → 1098,10
add eax, 4
cmp eax, 4*4
jnz @b
call MutexUnlock
invoke MutexUnlock
push esi
mov esi, noindex
call SysMsgBoardStr
invoke SysMsgBoardStr
pop esi
jmp .drop_reference
@@:
1139,7 → 1109,7
btr [free_numbers+eax], edx
lea eax, [eax*8+edx]
push eax
call MutexUnlock
invoke MutexUnlock
pop eax
; 1c. Generate a name of the form "usbhd<index>" in the stack.
mov dword [esp], 'usbh'
1166,41 → 1136,41
; 4. Notify the kernel about a new disk.
; 4a. Add a disk.
; stdcall queue_request, ecx, read_capacity_req, eax, read_capacity_callback, eax
stdcall DiskAdd, disk_functions, edx, esi, 0
invoke DiskAdd, disk_functions, edx, esi, 0
mov ebx, eax
; 4b. If it failed, release the index and do nothing.
test eax, eax
jz .free_index
; 4c. Notify the kernel that a media is present.
stdcall DiskMediaChanged, eax, 1
invoke DiskMediaChanged, eax, 1
; 5. Lock the requests queue, check that device is not disconnected,
; store the disk handle, unlock the requests queue.
mov ecx, [esi+usb_unit_data.Parent]
add ecx, usb_device_data.QueueLock
call MutexLock
invoke MutexLock
cmp byte [ecx+usb_device_data.DeviceDisconnected-usb_device_data.QueueLock], 0
jnz .disconnected
mov [esi+usb_unit_data.DiskDevice], ebx
call MutexUnlock
invoke MutexUnlock
jmp .exit
.disconnected:
call MutexUnlock
invoke MutexUnlock
stdcall disk_close, ebx
jmp .exit
.free_index:
mov ecx, free_numbers_lock
call MutexLock
invoke MutexLock
movzx eax, [esi+usb_unit_data.DiskIndex]
bts [free_numbers], eax
call MutexUnlock
invoke MutexUnlock
.drop_reference:
mov esi, [esi+usb_unit_data.Parent]
lock dec [esi+usb_device_data.NumReferences]
jnz .exit
mov eax, [esi+usb_device_data.LogicalDevices]
call Kfree
invoke Kfree
xchg eax, esi
call Kfree
invoke Kfree
.exit:
or eax, -1
int 0x40
1216,7 → 1186,7
end virtual
; 1. Say a message.
mov esi, disconnectmsg
call SysMsgBoardStr
invoke SysMsgBoardStr
; 2. Lock the requests queue, set .DeviceDisconnected to 1,
; unlock the requests queue.
; Locking is required for synchronization with queue_request:
1229,9 → 1199,9
; then queue_request tries to use them.
mov esi, [.device]
lea ecx, [esi+usb_device_data.QueueLock]
call MutexLock
invoke MutexLock
mov [esi+usb_device_data.DeviceDisconnected], 1
call MutexUnlock
invoke MutexUnlock
; 3. Drop one reference to the structure and check whether
; that was the last reference.
lock dec [esi+usb_device_data.NumReferences]
1247,9 → 1217,9
mov eax, [esi+usb_unit_data.DiskDevice]
test eax, eax
jz @f
stdcall DiskDel, eax
invoke DiskDel, eax
@@:
add esi, usb_unit_data.sizeof
add esi, sizeof.usb_unit_data
dec ebx
jnz .diskdel
; In this case, some operations with those disks are still possible,
1262,10 → 1232,10
mov eax, [esi+usb_device_data.LogicalDevices]
test eax, eax
jz @f
call Kfree
invoke Kfree
@@:
xchg eax, esi
call Kfree
invoke Kfree
jmp .return
endp
 
1286,17 → 1256,17
end virtual
mov esi, [.userdata]
mov ecx, free_numbers_lock
call MutexLock
invoke MutexLock
movzx eax, [esi+usb_unit_data.DiskIndex]
bts [free_numbers], eax
call MutexUnlock
invoke MutexUnlock
mov esi, [esi+usb_unit_data.Parent]
lock dec [esi+usb_device_data.NumReferences]
jnz .nothing
mov eax, [esi+usb_device_data.LogicalDevices]
call Kfree
invoke Kfree
xchg eax, esi
call Kfree
invoke Kfree
.nothing:
pop esi ebx
ret 4
1308,7 → 1278,7
; 1. Create event for waiting.
xor esi, esi
xor ecx, ecx
call CreateEvent
invoke CreateEvent
test eax, eax
jz .generic_fail
push eax
1340,7 → 1310,7
; 3. Wait for event. This destroys it.
mov eax, [.event]
mov ebx, [.event_code]
call WaitEvent
invoke WaitEvent
; 4. Get the status and results.
pop ecx
bswap ecx ; .LastLBA
1403,7 → 1373,7
mov ebx, [ecx+disk_querymedia.event_code-disk_querymedia.locals]
xor edx, edx
xor esi, esi
call RaiseEvent
invoke RaiseEvent
pop edi esi ebx
ret 8
endp
1443,7 → 1413,7
; 4. Create event for waiting.
xor esi, esi
xor ecx, ecx
call CreateEvent
invoke CreateEvent
test eax, eax
jz .generic_fail
push eax ; .event
1476,7 → 1446,7
; 6. Wait for event. This destroys it.
mov eax, [.event]
mov ebx, [.event_code]
call WaitEvent
invoke WaitEvent
; 7. Get the status. If the operation has failed, abort.
pop eax ; .status
pop ecx ecx ; cleanup .event_code, .event
1579,7 → 1549,7
mov ebx, [esi+disk_read_write.event_code-disk_read_write.locals]
xor edx, edx
xor esi, esi
call RaiseEvent
invoke RaiseEvent
; 6. Return.
pop edi esi ebx
ret 8
1599,9 → 1569,7
;read_fail db 'K : READ command failed',13,10,0
noindex db 'K : failed to generate disk name',13,10,0
 
; Exported variable: kernel API version.
align 4
version dd 50005h
; Structure with callback functions.
usb_functions:
dd usb_functions_end - usb_functions
1620,6 → 1588,9
dd 0 ; adjust_cache_size: use default cache
disk_functions_end:
 
data fixups
end data
 
free_numbers_lock rd 3
; 128 devices should be enough for everybody
free_numbers dd -1, -1, -1, -1
1626,6 → 1597,3
 
; for DEBUGF macro
include_debug_strings
 
; for uninitialized data
section '.data' data readable writable align 16