Implement USB mass storage and batch OHCI bulk transfers
This commit is contained in:
1 parent
c0c56a9c84
commit
c28b9dd77f
7 files changed
+919
-146
No files matched your search
@@ -658,6 +658,7 @@ begin
|
||||
|
||||
{ Step 2: Create device at address 0 }
|
||||
dev := PUSBDevice(kalloc(sizeof(TUSBDevice)));
|
||||
if dev = nil then begin pop_trace; exit; end;
|
||||
dev^.Address := 0;
|
||||
dev^.Speed := speed;
|
||||
dev^.MaxPacket0 := 8; { Default, will be updated from descriptor }
|
||||
@@ -667,6 +668,8 @@ begin
|
||||
dev^.ParentPort := parentPort;
|
||||
dev^.NumEndpoints := 0;
|
||||
dev^.Configured := false;
|
||||
dev^.InterfaceNumber := 0;
|
||||
dev^.InterfaceCount := 0;
|
||||
dev^.fnDisconnect := nil;
|
||||
|
||||
{ Step 3: Get first 8 bytes of device descriptor to learn MaxPacketSize0 }
|
||||
@@ -730,7 +733,14 @@ begin
|
||||
end;
|
||||
|
||||
totalLen := cfgDesc.wTotalLength;
|
||||
if totalLen < sizeof(TUSBConfigDescriptor) then begin
|
||||
kfree(void(dev));
|
||||
pop_trace;
|
||||
exit;
|
||||
end;
|
||||
dev^.InterfaceCount := cfgDesc.bNumInterfaces;
|
||||
cfgBuf := Pointer(kalloc(totalLen));
|
||||
if cfgBuf = nil then begin kfree(void(dev)); pop_trace; exit; end;
|
||||
|
||||
status := usb_get_descriptor(dev, USB_DESC_CONFIGURATION, 0, cfgBuf, totalLen);
|
||||
if status <> tsSuccess then begin
|
||||
@@ -744,12 +754,14 @@ begin
|
||||
{ Step 7: Parse interface and endpoint descriptors }
|
||||
offset := sizeof(TUSBConfigDescriptor);
|
||||
dev^.NumEndpoints := 0;
|
||||
while offset < totalLen do begin
|
||||
while offset + 2 <= totalLen do begin
|
||||
{ Read descriptor length and type }
|
||||
if PUint8(uint32(cfgBuf) + offset)^ = 0 then break; { Safety: zero-length = corrupt }
|
||||
if PUint8(uint32(cfgBuf) + offset)^ < 2 then break;
|
||||
if offset + PUint8(uint32(cfgBuf) + offset)^ > totalLen then break;
|
||||
|
||||
if PUint8(uint32(cfgBuf) + offset + 1)^ = USB_DESC_ENDPOINT then begin
|
||||
if dev^.NumEndpoints < USB_MAX_ENDPOINTS then begin
|
||||
if (PUint8(uint32(cfgBuf) + offset)^ >= sizeof(TUSBEndpointDescriptor)) and
|
||||
(dev^.NumEndpoints < USB_MAX_ENDPOINTS) then begin
|
||||
epDesc := PUSBEndpointDescriptor(uint32(cfgBuf) + offset);
|
||||
dev^.Endpoints[dev^.NumEndpoints].Address := usb_ep_number(epDesc^.bEndpointAddress);
|
||||
dev^.Endpoints[dev^.NumEndpoints].Direction := usb_ep_direction(epDesc^.bEndpointAddress);
|
||||
@@ -780,16 +792,22 @@ begin
|
||||
{ Find the first interface descriptor for class/subclass/protocol }
|
||||
offset := sizeof(TUSBConfigDescriptor);
|
||||
ifDesc := nil;
|
||||
while offset < totalLen do begin
|
||||
if PUint8(uint32(cfgBuf) + offset)^ = 0 then break;
|
||||
if PUint8(uint32(cfgBuf) + offset + 1)^ = USB_DESC_INTERFACE then begin
|
||||
while offset + 2 <= totalLen do begin
|
||||
if PUint8(uint32(cfgBuf) + offset)^ < 2 then break;
|
||||
if offset + PUint8(uint32(cfgBuf) + offset)^ > totalLen then break;
|
||||
if (PUint8(uint32(cfgBuf) + offset + 1)^ = USB_DESC_INTERFACE) and
|
||||
(PUint8(uint32(cfgBuf) + offset)^ >= sizeof(TUSBInterfaceDescriptor)) then begin
|
||||
ifDesc := PUSBInterfaceDescriptor(uint32(cfgBuf) + offset);
|
||||
dev^.InterfaceNumber := ifDesc^.bInterfaceNumber;
|
||||
break;
|
||||
end;
|
||||
offset := offset + PUint8(uint32(cfgBuf) + offset)^;
|
||||
end;
|
||||
|
||||
DevID := PDeviceIdentifier(kalloc(sizeof(TDeviceIdentifier)));
|
||||
if DevID = nil then begin
|
||||
kfree(void(cfgBuf)); kfree(void(dev)); pop_trace; exit;
|
||||
end;
|
||||
DevID^.Bus := biUSB;
|
||||
DevID^.id0 := (uint32(devDesc.idVendor) SHL 16) OR devDesc.idProduct;
|
||||
DevID^.id1 := devDesc.bDeviceClass;
|
||||
|
||||
@@ -963,6 +963,8 @@ end;
|
||||
|
||||
{ ========================= Submit Transfer ========================= }
|
||||
|
||||
procedure ehci_free_qtd_chain(first : PEHCI_qTD); forward;
|
||||
|
||||
function ehci_submit_control(hc : PUSBHCDriver; transfer : PUSBTransfer) : boolean;
|
||||
var
|
||||
priv : PEHCI_PrivData;
|
||||
@@ -1004,7 +1006,7 @@ begin
|
||||
|
||||
{ SETUP qTD }
|
||||
qtd := ehci_alloc_qtd;
|
||||
if qtd = nil then begin pop_trace; exit; end;
|
||||
if qtd = nil then begin ehci_free_qtd_chain(firstqTD); pop_trace; exit; end;
|
||||
firstqTD := qtd;
|
||||
qtd^.Token := ehci_make_qtd_token(EHCI_QTD_PID_SETUP, false, 8, false, 3);
|
||||
ehci_fill_qtd_buffers(qtd, @transfer^.Setup, 8);
|
||||
@@ -1021,7 +1023,7 @@ begin
|
||||
|
||||
while remaining > 0 do begin
|
||||
qtd := ehci_alloc_qtd;
|
||||
if qtd = nil then begin pop_trace; exit; end;
|
||||
if qtd = nil then begin ehci_free_qtd_chain(firstqTD); pop_trace; exit; end;
|
||||
|
||||
if remaining > maxPkt then
|
||||
pktLen := maxPkt
|
||||
@@ -1034,6 +1036,7 @@ begin
|
||||
pid := EHCI_QTD_PID_OUT;
|
||||
|
||||
qtd^.Token := ehci_make_qtd_token(pid, toggle, pktLen, false, 3);
|
||||
qtd^.SWPad0 := pktLen; { Original data length, excluding SETUP/status }
|
||||
ehci_fill_qtd_buffers(qtd, Pointer(uint32(transfer^.Buffer) + offset), pktLen);
|
||||
qtd^.NextqTD := EHCI_QTD_T;
|
||||
qtd^.AltNextqTD := EHCI_QTD_T;
|
||||
@@ -1052,7 +1055,7 @@ begin
|
||||
|
||||
{ STATUS qTD (opposite direction, DATA1) }
|
||||
qtd := ehci_alloc_qtd;
|
||||
if qtd = nil then begin pop_trace; exit; end;
|
||||
if qtd = nil then begin ehci_free_qtd_chain(firstqTD); pop_trace; exit; end;
|
||||
|
||||
if (transfer^.Setup.bmRequestType AND $80) <> 0 then
|
||||
pid := EHCI_QTD_PID_OUT
|
||||
@@ -1070,7 +1073,7 @@ begin
|
||||
|
||||
{ Build QH }
|
||||
qh := ehci_alloc_qh;
|
||||
if qh = nil then begin pop_trace; exit; end;
|
||||
if qh = nil then begin ehci_free_qtd_chain(firstqTD); pop_trace; exit; end;
|
||||
|
||||
qh^.EPChars := ehci_make_qh_epchars(devAddr, 0, speedBits, maxPkt,
|
||||
true, false, isCtrlEP, 4);
|
||||
@@ -1152,7 +1155,7 @@ begin
|
||||
|
||||
repeat
|
||||
qtd := ehci_alloc_qtd;
|
||||
if qtd = nil then begin pop_trace; exit; end;
|
||||
if qtd = nil then begin ehci_free_qtd_chain(firstqTD); pop_trace; exit; end;
|
||||
|
||||
if firstqTD = nil then firstqTD := qtd;
|
||||
|
||||
@@ -1164,6 +1167,7 @@ begin
|
||||
pktLen := 0;
|
||||
|
||||
qtd^.Token := ehci_make_qtd_token(pid, toggle, pktLen, false, 3);
|
||||
qtd^.SWPad0 := pktLen; { Original data length, excluding SETUP/status }
|
||||
if pktLen > 0 then
|
||||
ehci_fill_qtd_buffers(qtd, Pointer(uint32(transfer^.Buffer) + offset), pktLen);
|
||||
qtd^.NextqTD := EHCI_QTD_T;
|
||||
@@ -1200,7 +1204,7 @@ begin
|
||||
|
||||
{ Build QH }
|
||||
qh := ehci_alloc_qh;
|
||||
if qh = nil then begin pop_trace; exit; end;
|
||||
if qh = nil then begin ehci_free_qtd_chain(firstqTD); pop_trace; exit; end;
|
||||
|
||||
qh^.EPChars := ehci_make_qh_epchars(devAddr, epNum, speedBits, maxPkt,
|
||||
true, false, false, 4);
|
||||
@@ -1297,6 +1301,11 @@ begin
|
||||
ehci_transfer_done := true;
|
||||
exit;
|
||||
end;
|
||||
if (qh^.SWTransfer <> nil) and
|
||||
(PUSBTransfer(qh^.SWTransfer)^.PipeType = ptBulk) and
|
||||
(((qtd^.Token SHR 16) AND $7FFF) <> 0) then begin
|
||||
ehci_transfer_done := true; exit;
|
||||
end;
|
||||
qtd := qtd^.SWNext;
|
||||
end;
|
||||
ehci_transfer_done := true;
|
||||
@@ -1385,6 +1394,8 @@ var
|
||||
transfer : PUSBTransfer;
|
||||
head : PEHCI_QH;
|
||||
listIdx : uint32;
|
||||
qtd : PEHCI_qTD;
|
||||
residual : uint32;
|
||||
begin
|
||||
if (hc = nil) or (hc^.PrivData = nil) then exit;
|
||||
priv := PEHCI_PrivData(hc^.PrivData);
|
||||
@@ -1422,7 +1433,15 @@ begin
|
||||
if transfer^.Status = tsInProgress then begin
|
||||
if ehci_transfer_done(qh) then begin
|
||||
transfer^.Status := ehci_transfer_status(qh);
|
||||
transfer^.ActualLen := transfer^.BufferLen;
|
||||
transfer^.ActualLen := 0;
|
||||
qtd := qh^.SWFirstqTD;
|
||||
while qtd <> nil do begin
|
||||
if (qtd^.Token AND EHCI_QTD_STS_ACTIVE) <> 0 then break;
|
||||
residual := (qtd^.Token SHR 16) AND $7FFF;
|
||||
if (qtd^.SWPad0 > 0) and (residual <= qtd^.SWPad0) then
|
||||
inc(transfer^.ActualLen, qtd^.SWPad0 - residual);
|
||||
qtd := qtd^.SWNext;
|
||||
end;
|
||||
{ Remove QH from schedule and free qTD chain }
|
||||
if listIdx = 0 then
|
||||
ehci_remove_async_qh(priv, qh)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -249,6 +249,8 @@ type
|
||||
Endpoints : array[0..USB_MAX_ENDPOINTS-1] of TUSBEndpoint;
|
||||
NumEndpoints : uint8;
|
||||
Configured : boolean;
|
||||
InterfaceNumber : uint8; { First interface, alternate setting zero }
|
||||
InterfaceCount : uint8; { Class drivers may reject composite devices }
|
||||
fnDisconnect : TUSBDisconnectCallback; { Called before device is freed on unplug }
|
||||
end;
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -37,6 +37,7 @@ uses
|
||||
driver.storage.metatest,
|
||||
driver.storage.perftest,
|
||||
driver.storage.ctl.ahci,
|
||||
driver.storage.ctl.usb,
|
||||
core.enc.fnv1a,
|
||||
core.enc.djb2,
|
||||
core.ds.bloom,
|
||||
@@ -76,6 +77,7 @@ begin
|
||||
driver.storage.metatest.UnitTest;
|
||||
driver.storage.perftest.UnitTest;
|
||||
driver.storage.ctl.ahci.UnitTest;
|
||||
driver.storage.ctl.usb.UnitTest;
|
||||
core.enc.fnv1a.UnitTest;
|
||||
core.enc.djb2.UnitTest;
|
||||
core.ds.bloom.UnitTest;
|
||||
|
||||
Reference in new issue
Block a user