Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c7619bc2c | ||
|
|
d318eca262 | ||
|
|
c28b9dd77f | ||
|
|
c0c56a9c84 | ||
|
|
720ba46630 | ||
|
|
8e0ab3da8b | ||
|
|
3d4f846a8f | ||
|
|
d8d51ba5df | ||
|
|
d1ecd3fc41 | ||
|
|
7e99bc2db1 | ||
|
|
096db148d0 | ||
|
|
663bfbb0e4 | ||
|
|
aadc59f831 | ||
|
|
5d7aacc28f | ||
|
|
4810617922 | ||
|
|
666a011a9f | ||
|
|
a6e81bf5d3 |
No files matched your search
@@ -15,6 +15,7 @@ localenv.json
|
||||
dockerout.txt
|
||||
AGENTS.md
|
||||
lessons_learnt.md
|
||||
/plan.md
|
||||
*.log
|
||||
wasuro
|
||||
src/core/core.version.pas
|
||||
|
||||
@@ -1,49 +1,68 @@
|
||||
# driver.storage.ctl.usb
|
||||
# USB mass storage
|
||||
|
||||
USB Mass Storage Class driver stub.
|
||||
`driver.storage.ctl.usb` implements SCSI transparent commands over USB Mass
|
||||
Storage Bulk-Only Transport (interface class/subclass/protocol `08/06/50`).
|
||||
|
||||
## Overview
|
||||
It probes INQUIRY, TEST UNIT READY and READ CAPACITY(10), registers LUN 0 with
|
||||
the storage manager, and exposes READ(10), WRITE(10), REQUEST SENSE and
|
||||
SYNCHRONIZE CACHE(10). Existing partition discovery and filesystem mounting
|
||||
apply to the resulting USB disk. Writes complete only after cache synchronization.
|
||||
|
||||
This unit registers a USB class driver with the driver manager (`driver.mgr`) to claim USB Mass Storage Class interfaces (class `$08`, subclass `$06` SCSI, protocol `$50` Bulk-Only). When the USB stack enumerates a matching interface, the `load` callback is invoked. Currently the callback only logs a detection message; no I/O infrastructure is established.
|
||||
## Execution and ownership
|
||||
|
||||
Full Bulk-Only Transport (BOT) support — including Max LUN query, endpoint setup, CBW/CSW command exchange, and `driver.storage.mgr` device registration — is planned but not yet implemented.
|
||||
An attachment task probes and registers each disk after scheduling starts; boot
|
||||
initialization never waits on a worker that cannot run yet.
|
||||
|
||||
## Boot Registration
|
||||
Storage dispatch only publishes a request. One persistent `usb.storage` worker
|
||||
performs I/O in task context, with one command active per device and a reusable,
|
||||
page-aligned 4 KiB bounce buffer. Caller buffers are never submitted for DMA.
|
||||
Commands are split at the bounce-buffer boundary; all LBA/count and buffer-end
|
||||
arithmetic is checked before submission. CBW/CSW wire sizes, tags, status,
|
||||
residue, and actual transfer lengths are validated.
|
||||
|
||||
Registered with `boot.mgr` as `driver.storage.ctl.usb` at the `device` barrier. The barrier order places `device` after `bus`, ensuring `driver.bus.usb.core.init` has run before the storage class driver registers with the driver manager.
|
||||
OHCI batches bulk data into spans of at most 4 KiB per transfer descriptor,
|
||||
using the endpoint's hardware toggle carry across packets. A 4 KiB full-speed
|
||||
read uses one data descriptor instead of 64. Short packets still halt the chain
|
||||
and report the actual byte count; interrupt endpoint packet sizing is unchanged.
|
||||
|
||||
## Dependencies
|
||||
The worker performs reset recovery (class reset, clear bulk-IN halt, clear
|
||||
bulk-OUT halt) after malformed status or phase errors. Failed SCSI commands
|
||||
request sense and return storage errors, including write protection.
|
||||
|
||||
- `boot.mgr`
|
||||
On disconnect the disk is marked removed immediately. Accepted requests are
|
||||
completed with errors, and idle volumes are invalidated/unmounted by the worker;
|
||||
busy volumes are retried. Storage-manager records remain stable tombstones.
|
||||
Class-driver buffers and context are freed after pending hardware work drains.
|
||||
|
||||
- `driver.mgr`
|
||||
- `memory.heap`
|
||||
- `driver.storage.types`
|
||||
- `io.syslog`
|
||||
- `debug.tracer`
|
||||
- `driver.bus.usb.core`
|
||||
- `driver.bus.usb.types`
|
||||
- `core.util`, `arch.x86.util`
|
||||
Host controllers currently have no cancellation API. A timed-out transfer is
|
||||
therefore quarantined with its private device snapshot and DMA memory. No further
|
||||
commands are issued to that device. If hardware never completes, those allocations
|
||||
remain retained until reboot; freeing them would permit DMA into reused memory.
|
||||
The timeout uses the PIT's actual frequency and a bounded polling fallback.
|
||||
|
||||
## Constants
|
||||
## Supported scope
|
||||
|
||||
| Constant | Value | Description |
|
||||
|---|---|---|
|
||||
| `USB_CLASS_MASS_STORAGE` | `$08` | USB interface class for mass storage |
|
||||
| `USB_SC_SCSI` | `$06` | SCSI transparent command set subclass |
|
||||
| `USB_PROTO_BBB` | `$50` | Bulk-Only (BOT) transport protocol |
|
||||
- LUN 0 on a single-interface device with one bulk-IN and one bulk-OUT endpoint.
|
||||
- Direct-access disks with power-of-two sectors from 512 through 4096 bytes.
|
||||
- Capacity representable by the existing 32-bit storage-sector API; the
|
||||
READ CAPACITY(16) sentinel is rejected.
|
||||
- OHCI, EHCI and xHCI bulk transfers. OHCI/EHCI report actual data lengths and
|
||||
terminate bulk reads on short packets.
|
||||
|
||||
## Functions and Procedures
|
||||
UAS, additional LUNs, composite/alternate-setting interfaces, UHCI storage,
|
||||
optical devices, and media replacement without USB reconnect are not supported.
|
||||
The current xHCI driver lacks endpoint-ring reset support: a halted bulk endpoint
|
||||
fails the device closed rather than attempting an unsafe ring reuse.
|
||||
|
||||
### init
|
||||
## Validation
|
||||
|
||||
```pascal
|
||||
procedure init;
|
||||
```
|
||||
`UnitTest` is called by the boot test suite and is available as `USBMSCTEST`.
|
||||
Tests use a memory-only mock host controller: wire layout, byte order, capacity
|
||||
limits, reads, writes, sense errors, short transfers, status stalls, reset ordering,
|
||||
and request bounds. They perform no disk I/O.
|
||||
|
||||
Constructs a `TDeviceIdentifier` matching any USB device with `bInterfaceClass = $08`, `bInterfaceSubClass = $06`, `bInterfaceProtocol = $50`, and registers the `load` callback with `driver.mgr`.
|
||||
For hardware/VM checks, boot with a disposable USB disk, confirm `USBMSC` discovery
|
||||
and filesystem mounting, test file read/write/read-back, then unplug while idle
|
||||
and during I/O. Do not run destructive disk tests against a disk containing data.
|
||||
|
||||
## Notes
|
||||
|
||||
- The `load` callback currently returns true unconditionally after logging a detection message.
|
||||
- Once full BOT support is implemented, `load` should query the Max LUN, allocate endpoint structures, and call `driver.storage.mgr.register_device` for each logical unit.
|
||||
Protocol: [USB-IF Bulk-Only Transport 1.0](https://www.usb.org/sites/default/files/usbmassbulk_10.pdf).
|
||||
@@ -14,7 +14,7 @@ The I/O model operates as follows:
|
||||
- `storage_read` / `storage_write` are convenience wrappers that build a stack-local `TIORequest` and call `submit_io_wait`.
|
||||
- `storage_read_async` / `storage_write_async` build and submit a request with a caller-supplied callback, returning immediately.
|
||||
|
||||
Legacy `storage_read_legacy` / `storage_write_legacy` procedure wrappers are retained for callers not yet migrated to the function-returning API.
|
||||
Blocking callers use `storage_read` / `storage_write` and check the returned `TError`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -205,17 +205,42 @@ function storage_write_async(device : PStorage_Device; addr : uint32; sectors :
|
||||
|
||||
Non-blocking write. Analogous to `storage_read_async`.
|
||||
|
||||
### storage_read_legacy / storage_write_legacy
|
||||
|
||||
```pascal
|
||||
procedure storage_read_legacy(device : PStorage_Device; addr : uint32; sectors : uint32; buffer : puint32);
|
||||
procedure storage_write_legacy(device : PStorage_Device; addr : uint32; sectors : uint32; buffer : puint32);
|
||||
```
|
||||
|
||||
Drop-in procedure replacements for callers using the old void-returning API. Forward to `storage_read` / `storage_write` and discard the error code. Marked for removal once all callers are migrated.
|
||||
|
||||
## Notes
|
||||
|
||||
- `dispatch_next` is an internal procedure called with interrupts disabled. It dequeues the head of the CFIFO, allocates a heap copy via `ioreq_copy` (for ISR safety), and invokes the appropriate `dispatchRead` or `dispatchWrite` on the device. If neither dispatch pointer is set, the request is failed with `eDeviceNotFound`.
|
||||
- The blocking path relies on pointer-dereferenced spin (`puint32(@wait.Done)^`) to prevent FPC from caching the `Done` flag in a register across the `hlt` instruction.
|
||||
- Devices without `dispatchRead`/`dispatchWrite` set (i.e., drivers not yet migrated to Phase 2) will have `submit_io` return `eDeviceNotFound`. Legacy drivers must be migrated before they can use the new I/O path.
|
||||
|
||||
|
||||
## Bounded performance path
|
||||
|
||||
The manager merges up to 32 consecutive queued requests when operation, device,
|
||||
LBA range, and virtual buffer range are contiguous. It never searches past the
|
||||
queue head or delays a lone request to create a merge. Drivers opt in through
|
||||
`mergeMaxSectors`; SATA AHCI advertises 65,536 sectors, while other drivers default
|
||||
to zero (disabled). A merged command delivers one completion to each original
|
||||
request, in FIFO order. A command error fails every member conservatively.
|
||||
|
||||
`storage_get_stats` takes an interrupt-protected snapshot of the device counters.
|
||||
`DISK info <index>` displays accepted/completed/failed logical requests, hardware
|
||||
commands, merged requests, queue-full rejections, queue and active high-water
|
||||
marks, successful read/write sectors, request-size buckets, queue/service ticks,
|
||||
and FAT metadata cache hits/misses. Queue ticks are accumulated per logical
|
||||
request; service ticks are accumulated per hardware command. Tick values use
|
||||
`Counters.c32`, not milliseconds. Counters are unsigned 32-bit values, start at
|
||||
device registration, and wrap; use snapshot differences over bounded benchmark
|
||||
intervals. Size buckets are <=8, 9..256, and >256 sectors.
|
||||
|
||||
FAT32 blocking and async reads share a pooled transfer engine with batches of up
|
||||
to four outstanding reads, capped by device queue depth. Destination slices are
|
||||
disjoint. On any error, the engine drains every accepted request before releasing
|
||||
its context and reports only the contiguous successful byte prefix. Unaligned
|
||||
edges and odd DMA addresses use the context's private scratch sector. Writes
|
||||
remain sequential. Run maps store cumulative file-cluster offsets; an open-file
|
||||
cursor handles sequential locality and binary search handles random seeks.
|
||||
|
||||
`STORPERF` runs memory-backed regression checks (also included in boot unit tests)
|
||||
for reverse completion order, failures, unaligned reads, run lookup, queue
|
||||
rejection, merging limits, and callback accounting. It does not access real disks
|
||||
or measure hardware throughput. Compare counters alongside `STORBENCH` and file
|
||||
benchmarks on contiguous and fragmented files for actual performance validation.
|
||||
@@ -58,3 +58,111 @@ Registers the `STORTEST` and `DISKTEST` shell commands with `io.stdio`. Must be
|
||||
- `run_tests` exercises `makeAbsolutePathFrom`, `resolvePathFrom`, `changeDirectoryFrom`, `GetDirectoryListingFrom`, `newVirtualDirectory`, and `PathValid` including `.` and `..` traversal.
|
||||
- `run_disk_tests` is destructive and will irreversibly wipe the partition table of disk 0. It is never called automatically.
|
||||
- The `Assert` helper used in both procedures increments `passed` or `failed` counters and logs the test name on failure; passing tests produce no output to avoid noise.
|
||||
|
||||
|
||||
## Raw read benchmark
|
||||
|
||||
`STORBENCH` is implemented and registered by `driver.storage.bench`. It performs
|
||||
read-only raw-device I/O; it does not invoke the destructive `DISKTEST` path.
|
||||
|
||||
```text
|
||||
STORBENCH [disk] [MiB]
|
||||
STORBENCH list
|
||||
STORBENCH run [disk] [MiB] [KiB/request] [depth] [seq|random] [start_lba]
|
||||
STORBENCH compare [disk] [MiB]
|
||||
STORBENCH help
|
||||
```
|
||||
|
||||
Manual (`run`) defaults are disk 0, 128 MiB, 1024 KiB/request, sequential reads, and a depth of
|
||||
`min(4, hardware depth)` (at least one). Examples:
|
||||
|
||||
```text
|
||||
STORBENCH 0 128 64 4
|
||||
STORBENCH 0 128 4 16 random
|
||||
STORBENCH 0 256 1024 1 seq 2048
|
||||
STORBENCH compare 0 128
|
||||
```
|
||||
|
||||
The comparison runs sequential reads at 4, 64, and 1024 KiB/request, each at depths
|
||||
1, 4, 16, and 32. The MiB argument applies to each of the twelve cases. Queue depth
|
||||
may exceed hardware depth to expose software queuing and request merging.
|
||||
|
||||
Limits are 1..16384 MiB, 1..1024 KiB/request, and depth 1..32. Request size must be
|
||||
an exact number of device sectors. Buffers are allocated once per case, with a
|
||||
separate slice for every outstanding request and a maximum total size of 32 MiB.
|
||||
The transfer range is clamped to the device. Without an explicit LBA, reads start
|
||||
at sector 2048 when the full requested range fits there; otherwise they start at
|
||||
zero. Random mode uses a fixed xorshift seed and samples that range with replacement,
|
||||
so total bytes read are bounded but unique coverage is not guaranteed.
|
||||
|
||||
Reports include successful bytes, elapsed microseconds, MiB/s, IOPS, accepted and
|
||||
completed request counts, peak outstanding requests, average/p95/max completion
|
||||
latency, and per-run storage-counter deltas. Latencies are measured from submission
|
||||
to callback. The p95 is an upper bound from logarithmic microsecond buckets, not an exact
|
||||
microsecond percentile. Runs shorter than 10 ms are excluded from suite speed comparisons.
|
||||
No console output occurs inside the measured loop. Any failure stops new work;
|
||||
already accepted DMA is drained before buffers are freed, and failed requests
|
||||
contribute no bytes or successful IOPS.
|
||||
|
||||
Timing prefers the Hyper-V-compatible VM reference counter (100 ns ticks), detected
|
||||
through its Hv#1 interface and reference-counter privilege. It needs no calibration.
|
||||
Other platforms use an invariant TSC calibrated against two complete RTC seconds,
|
||||
with up to three attempts to tolerate transient RTC updates or scheduling delays.
|
||||
Calibration requires agreement within 1%; failures include the specific reason.
|
||||
Unlike delivered timer interrupts, this clock advances while interrupts are masked.
|
||||
Queue submission restores interrupts between requests. Filesystem caches are
|
||||
bypassed, but drive and virtual-machine/host caches may still affect results.
|
||||
Virtual or sparse disks can report throughput above physical-media speed.
|
||||
Device counter deltas may include concurrent I/O from other processes;
|
||||
benchmark-owned byte, request, and latency counters do not.
|
||||
|
||||
`driver.storage.bench.UnitTest` runs memory-only parser, arithmetic, range,
|
||||
queue-depth, completion-order, and error-accounting tests at boot. It never reads
|
||||
or writes a real disk. The clock tests also verify that an advertised VM reference
|
||||
counter advances with interrupts masked; they never calibrate the RTC at boot.
|
||||
|
||||
|
||||
### Automatic suite and table output
|
||||
|
||||
`STORBENCH` without arguments, or `STORBENCH auto [disk] [MiB]`, runs six
|
||||
read-only profiles. Auto defaults to disk 0 and **32 MiB per profile** (up to
|
||||
192 MiB total before device-size clamping). One or two numeric arguments select the auto suite: `STORBENCH 2 512` tests
|
||||
disk 2 with 512 MiB per profile. `STORBENCH list` lists disk indices, capacities,
|
||||
controllers, sector sizes, and hardware queue depths. Three or more numeric
|
||||
arguments retain the legacy manual syntax; `run` selects manual mode explicitly.
|
||||
|
||||
| Profile | Request size | Queue depth |
|
||||
| --- | --- | --- |
|
||||
| Seq large | 1024 KiB | 1 |
|
||||
| Seq queued | 1024 KiB | queued |
|
||||
| Seq medium | 64 KiB | queued |
|
||||
| Random small | 4 KiB | 1 |
|
||||
| Random queued | 4 KiB | queued |
|
||||
| Random medium | 64 KiB | queued |
|
||||
|
||||
Queued depth is the device hardware depth clamped to 4..16, allowing software
|
||||
queue testing even on serial devices. A request smaller than a device sector is
|
||||
increased to one sector. Each profile samples the same byte-span size, with the
|
||||
existing fixed random seed and range clamping rules.
|
||||
|
||||
Auto and compare modes print an aligned ASCII table with request KiB, queue
|
||||
depth, MiB/s, IOPS, average latency, p95 latency upper bound, and status. Rows are
|
||||
printed outside the measured loop. Successful runs shorter than 10 ms show `-` metrics and
|
||||
`SHORT`, and are excluded from best-result and queue-gain summaries. Errors show
|
||||
`FAILED`, stop the suite after accepted DMA has drained, and leave remaining
|
||||
profiles unrun. The summary includes successful profiles, bytes actually read,
|
||||
measured time, best sequential throughput, peak random IOPS, and (in auto mode)
|
||||
queued/QD1 throughput ratios. These ratios describe the measured cases, including
|
||||
any controller-cache effects; they are not a hardware rating.
|
||||
|
||||
```text
|
||||
STORBENCH
|
||||
STORBENCH list
|
||||
STORBENCH 2 512
|
||||
STORBENCH auto 0 64
|
||||
STORBENCH compare 0 32
|
||||
```
|
||||
|
||||
Profiles reuse the same raw device range. Host/drive caches are not flushed, so
|
||||
later profiles may read warm data. These results are observed raw read throughput,
|
||||
not a claim of uncached physical-media performance.
|
||||
@@ -150,6 +150,7 @@ var
|
||||
device : PStorage_Device;
|
||||
volCount : uint32;
|
||||
freeSec : uint32;
|
||||
stats : TStorageStats;
|
||||
begin
|
||||
if paramCount(params) < 2 then begin
|
||||
println('Usage: DISK info <disk>');
|
||||
@@ -191,6 +192,16 @@ begin
|
||||
else
|
||||
volCount := 0;
|
||||
print('Partitions: '); printint(volCount); println('');
|
||||
storage_get_stats(device,stats);
|
||||
print('I/O submitted/completed/failed: '); printint(stats.Submitted); print(' / '); printint(stats.Completed); print(' / '); printint(stats.Failed); println('');
|
||||
print('Commands / merged requests: '); printint(stats.Commands); print(' / '); printint(stats.Merged); println('');
|
||||
print('Queue peak / active peak / queue full: '); printint(stats.QueuePeak); print(' / '); printint(stats.ActivePeak); print(' / '); printint(stats.QueueFull); println('');
|
||||
print('Read / written sectors: '); printint(stats.ReadSectors); print(' / '); printint(stats.WriteSectors); println('');
|
||||
print('Requests <=8 / <=256 / >256 sectors: '); printint(stats.SmallRequests); print(' / '); printint(stats.MediumRequests); print(' / '); printint(stats.LargeRequests); println('');
|
||||
print('Queue ticks total / max: '); printint(stats.QueueTicks); print(' / '); printint(stats.MaxQueueTicks); println('');
|
||||
print('Service ticks total / max: '); printint(stats.ServiceTicks); print(' / '); printint(stats.MaxServiceTicks); println('');
|
||||
print('FAT cache hits / misses: '); printint(stats.FatCacheHits); print(' / '); printint(stats.FatCacheMisses); println('');
|
||||
|
||||
end;
|
||||
|
||||
{ ---------- DISK WIPE ---------- }
|
||||
|
||||
+641
-166
File diff suppressed because it is too large.
Load diff
@@ -195,6 +195,7 @@ procedure do_refresh_load(state: PFileBrowserState); forward;
|
||||
procedure do_refresh_build(state: PFileBrowserState); forward;
|
||||
procedure schedule_refresh(state: PFileBrowserState); forward;
|
||||
procedure fb_refresh_poll_cb(tmr: Plv_timer); cdecl; forward;
|
||||
function fb_error_name(err : TError) : pchar; forward;
|
||||
procedure fb_collect_cb(key: pchar; data: void; ud: void); forward;
|
||||
procedure do_refresh(state: PFileBrowserState); forward;
|
||||
procedure navigate_to(state: PFileBrowserState; newDir: pchar); forward;
|
||||
@@ -582,6 +583,51 @@ begin
|
||||
puint32(@state^.refresh_pending)^ := 1;
|
||||
end;
|
||||
|
||||
function fb_error_name(err : TError) : pchar;
|
||||
begin
|
||||
case err of
|
||||
eNone: fb_error_name := 'eNone';
|
||||
eUnknown: fb_error_name := 'eUnknown';
|
||||
eNotSupported: fb_error_name := 'eNotSupported';
|
||||
eOutOfMemory: fb_error_name := 'eOutOfMemory';
|
||||
eInvalidArgument: fb_error_name := 'eInvalidArgument';
|
||||
eFileInUse: fb_error_name := 'eFileInUse';
|
||||
eFileDoesNotExist: fb_error_name := 'eFileDoesNotExist';
|
||||
eInvalidFileName: fb_error_name := 'eInvalidFileName';
|
||||
eInvalidFileExtension: fb_error_name := 'eInvalidFileExtension';
|
||||
eFilenameTooLong: fb_error_name := 'eFilenameTooLong';
|
||||
eDirectoryDoesNotExist: fb_error_name := 'eDirectoryDoesNotExist';
|
||||
eDirectoryAlreadyExists: fb_error_name := 'eDirectoryAlreadyExists';
|
||||
eDirectoryNotEmpty: fb_error_name := 'eDirectoryNotEmpty';
|
||||
eDirectoryFull: fb_error_name := 'eDirectoryFull';
|
||||
eNotADirectory: fb_error_name := 'eNotADirectory';
|
||||
eWriteOnly: fb_error_name := 'eWriteOnly';
|
||||
eReadOnly: fb_error_name := 'eReadOnly';
|
||||
ePermissionDenied: fb_error_name := 'ePermissionDenied';
|
||||
eInvalidPath: fb_error_name := 'eInvalidPath';
|
||||
eTooManyOpenFiles: fb_error_name := 'eTooManyOpenFiles';
|
||||
eInvalidHandle: fb_error_name := 'eInvalidHandle';
|
||||
eFileNotLoaded: fb_error_name := 'eFileNotLoaded';
|
||||
eAlreadyExists: fb_error_name := 'eAlreadyExists';
|
||||
eDiskFull: fb_error_name := 'eDiskFull';
|
||||
eIOError: fb_error_name := 'eIOError';
|
||||
eIOTimeout: fb_error_name := 'eIOTimeout';
|
||||
eIOCancelled: fb_error_name := 'eIOCancelled';
|
||||
eDeviceNotReady: fb_error_name := 'eDeviceNotReady';
|
||||
eDeviceRemoved: fb_error_name := 'eDeviceRemoved';
|
||||
eDeviceNotFound: fb_error_name := 'eDeviceNotFound';
|
||||
eQueueFull: fb_error_name := 'eQueueFull';
|
||||
eNoFreeSlot: fb_error_name := 'eNoFreeSlot';
|
||||
eCorruptFilesystem: fb_error_name := 'eCorruptFilesystem';
|
||||
eBadSector: fb_error_name := 'eBadSector';
|
||||
eAlreadyMounted: fb_error_name := 'eAlreadyMounted';
|
||||
eNotMounted: fb_error_name := 'eNotMounted';
|
||||
eUnsupportedFilesystem: fb_error_name := 'eUnsupportedFilesystem';
|
||||
eInvalidPartitionTable: fb_error_name := 'eInvalidPartitionTable';
|
||||
eVolumeNotFound: fb_error_name := 'eVolumeNotFound';
|
||||
end;
|
||||
end;
|
||||
|
||||
{ fb_refresh_poll_cb — 50ms LVGL poll timer; when rd_ready=1, build UI }
|
||||
procedure fb_refresh_poll_cb(tmr: Plv_timer); cdecl;
|
||||
var
|
||||
@@ -604,9 +650,12 @@ begin
|
||||
case state^.wop_kind of
|
||||
1: lv_msgbox_add_text(mbox, 'Could not create folder.');
|
||||
2: lv_msgbox_add_text(mbox, 'Could not rename item.');
|
||||
3: lv_msgbox_add_text(mbox, 'Could not create file.');
|
||||
4: lv_msgbox_add_text(mbox, 'Could not delete item.');
|
||||
else
|
||||
lv_msgbox_add_text(mbox, 'Operation failed.');
|
||||
end;
|
||||
lv_msgbox_add_text(mbox, fb_error_name(state^.wop_result));
|
||||
ok_b := lv_msgbox_add_footer_button(mbox, 'OK');
|
||||
lv_obj_add_event_cb(ok_b, @fb_mbox_close_cb, LV_EVENT_CLICKED, mbox);
|
||||
end;
|
||||
@@ -619,8 +668,8 @@ begin
|
||||
kfree(void(state^.wop_path2));
|
||||
state^.wop_path2 := nil;
|
||||
end;
|
||||
{ For rename, clear selection }
|
||||
if state^.wop_kind = 2 then
|
||||
{ For rename/delete, clear selection }
|
||||
if (state^.wop_kind = 2) or (state^.wop_kind = 4) then
|
||||
clear_selection(state);
|
||||
schedule_refresh(state);
|
||||
end;
|
||||
@@ -1591,15 +1640,16 @@ begin
|
||||
@fb_delete_done, state);
|
||||
end;
|
||||
|
||||
{ Async completion callback — runs in ISR/worker context, defer to LVGL }
|
||||
{ Async completion callback — runs outside the LVGL thread, so only set flags. }
|
||||
procedure fb_delete_done(error: TError; userdata: pointer);
|
||||
var
|
||||
state: PFileBrowserState;
|
||||
begin
|
||||
state := PFileBrowserState(userdata);
|
||||
if state = nil then exit;
|
||||
state^.op_err := error;
|
||||
lv_timer_create(@fb_delete_done_timer, 1, state);
|
||||
state^.wop_kind := 4;
|
||||
state^.wop_result := error;
|
||||
puint32(@state^.wop_done)^ := 1;
|
||||
end;
|
||||
|
||||
{ Deferred UI update after delete }
|
||||
@@ -1860,15 +1910,20 @@ begin
|
||||
if (error = eNone) and (nfctx^.handle <> 0) then begin
|
||||
{ WriteFileAsync needs a non-nil buffer; Length=0 so it is never read }
|
||||
driver.storage.vfs.WriteFileAsync(nfctx^.handle, 0,
|
||||
puint8(@nfctx^.err), 0, @fb_newfile_written, nfctx);
|
||||
puint8(@nfctx^.err), 0, nil, @fb_newfile_written, nfctx);
|
||||
end else begin
|
||||
if nfctx^.handle <> 0 then
|
||||
driver.storage.vfs.CloseFile(nfctx^.handle);
|
||||
lv_timer_create(@fb_newfile_done_timer, 1, nfctx);
|
||||
if nfctx^.state <> nil then begin
|
||||
nfctx^.state^.wop_kind := 3;
|
||||
nfctx^.state^.wop_result := nfctx^.err;
|
||||
puint32(@nfctx^.state^.wop_done)^ := 1;
|
||||
end;
|
||||
kfree(void(nfctx));
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Async step 2: write complete — close handle and schedule UI refresh }
|
||||
{ Async step 2: write complete — close handle and defer UI work via poll flags }
|
||||
procedure fb_newfile_written(error: TError; userdata: pointer);
|
||||
var
|
||||
nfctx : PNewFileCtx;
|
||||
@@ -1878,7 +1933,12 @@ begin
|
||||
if error <> eNone then nfctx^.err := error;
|
||||
if nfctx^.handle <> 0 then
|
||||
driver.storage.vfs.CloseFile(nfctx^.handle);
|
||||
lv_timer_create(@fb_newfile_done_timer, 1, nfctx);
|
||||
if nfctx^.state <> nil then begin
|
||||
nfctx^.state^.wop_kind := 3;
|
||||
nfctx^.state^.wop_result := nfctx^.err;
|
||||
puint32(@nfctx^.state^.wop_done)^ := 1;
|
||||
end;
|
||||
kfree(void(nfctx));
|
||||
end;
|
||||
|
||||
procedure fb_newfile_done_timer(tmr: Plv_timer); cdecl;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+2
-1
@@ -26,7 +26,7 @@ uses
|
||||
boot.mgr,
|
||||
debug.tracer, io.stdio, proc.mgr,
|
||||
//progs
|
||||
app.base64, app.md5sum, app.dhclient, app.vbeinfo, app.testcmd, app.ping, app.meminfo, app.setres,
|
||||
app.base64, app.md5sum, app.dhclient, app.vbeinfo, app.testcmd, app.ping, app.meminfo, app.setres, app.iotest,
|
||||
//drivers
|
||||
driver.storage.ctl.ram, driver.mgr,
|
||||
//network
|
||||
@@ -92,6 +92,7 @@ begin
|
||||
app.bsod.init();
|
||||
app.vterminal.init;
|
||||
app.filebrowser.init();
|
||||
app.iotest.init();
|
||||
end;
|
||||
|
||||
Initialization
|
||||
|
||||
+17
-8
@@ -288,7 +288,9 @@ begin
|
||||
if bootrecord <> nil then begin
|
||||
if bootrecord^.boot_sector <> $AA55 then begin
|
||||
println('No valid mbr found, initialising disk...');
|
||||
driver.storage.vol.mgr.init_disk(device);
|
||||
if driver.storage.vol.mgr.init_disk(device) <> eNone then begin
|
||||
println('Error: disk initialization failed.'); exit;
|
||||
end;
|
||||
end;
|
||||
kfree(puint32(bootrecord));
|
||||
end;
|
||||
@@ -335,7 +337,9 @@ begin
|
||||
|
||||
driver.storage.vol.mbr.setup_partition(@part, lba, sectors);
|
||||
part.system_id := 0;
|
||||
driver.storage.vol.mgr.add_partition(device, slot, part);
|
||||
if driver.storage.vol.mgr.add_partition(device, slot, part) <> eNone then begin
|
||||
println('Error: partition creation failed.'); exit;
|
||||
end;
|
||||
|
||||
print('Partition added in slot ');
|
||||
printint(slot);
|
||||
@@ -407,7 +411,9 @@ begin
|
||||
exit;
|
||||
end;
|
||||
|
||||
driver.storage.vol.mgr.remove_partition(device, slot);
|
||||
if driver.storage.vol.mgr.remove_partition(device, slot) <> eNone then begin
|
||||
println('Error: partition removal failed.'); exit;
|
||||
end;
|
||||
|
||||
print('Partition slot ');
|
||||
printint(slot);
|
||||
@@ -523,11 +529,14 @@ begin
|
||||
exit;
|
||||
end;
|
||||
|
||||
vol^.filesystem := fs;
|
||||
fs^.createCallback(vol, vol^.sectorCount, vol^.sectorStart, nil);
|
||||
|
||||
{ Re-probe filesystem so identify picks up the new signature }
|
||||
driver.storage.fs.mgr.probe_volume(vol);
|
||||
i := 0;
|
||||
while i < driver.storage.vol.mgr.get_volume_count() do begin
|
||||
if driver.storage.vol.mgr.get_volume(i) = vol then break;
|
||||
Inc(i);
|
||||
end;
|
||||
if not driver.storage.vol.mgr.format_volume(device, i, fsName, nil) then begin
|
||||
println('Error: format failed; the volume was not reported ready.'); exit;
|
||||
end;
|
||||
|
||||
print('Partition ');
|
||||
printint(slot);
|
||||
|
||||
@@ -44,6 +44,7 @@ function inl(port : uint16) : uint32;
|
||||
procedure io_wait;
|
||||
|
||||
procedure __SSE_128_memcpy(source : uint32; dest : uint32);
|
||||
procedure __REP_MOVSB_memcpy(source : uint32; dest : uint32; size : uint32);
|
||||
|
||||
procedure halt_and_catch_fire();
|
||||
procedure halt_and_dont_catch_fire();
|
||||
@@ -113,6 +114,19 @@ asm
|
||||
MOVAPS [EAX], XMM1
|
||||
end;
|
||||
|
||||
procedure __REP_MOVSB_memcpy(source : uint32; dest : uint32; size : uint32); assembler;
|
||||
asm
|
||||
push esi
|
||||
push edi
|
||||
cld
|
||||
mov esi, source
|
||||
mov edi, dest
|
||||
mov ecx, size
|
||||
rep movsb
|
||||
pop edi
|
||||
pop esi
|
||||
end;
|
||||
|
||||
function getESP : uint32;
|
||||
var
|
||||
tmp: uint32;
|
||||
|
||||
@@ -27,6 +27,10 @@ uses
|
||||
arch.x86.isr.mgr,
|
||||
arch.x86.idt;
|
||||
|
||||
const
|
||||
PIT_INPUT_HZ = 1193182;
|
||||
PIT_DIVISOR = 149;
|
||||
|
||||
procedure register();
|
||||
procedure hook(hook_method : uint32);
|
||||
procedure unhook(hook_method : uint32);
|
||||
@@ -54,7 +58,7 @@ procedure register();
|
||||
begin
|
||||
if not registered then begin
|
||||
asm
|
||||
mov ax, 149
|
||||
mov ax, PIT_DIVISOR
|
||||
out $40, al
|
||||
mov al, ah
|
||||
out $40, al
|
||||
|
||||
@@ -271,6 +271,7 @@ procedure fpc_shortstr_assign(len: longint; sstr, dstr: Pointer); compilerproc;
|
||||
any int64/uint64 arithmetic occurs (e.g. sint32 * uint32 promotion).
|
||||
Must live in the system unit so the compiler can resolve it. }
|
||||
function fpc_mul_int64(f1, f2: int64): int64; compilerproc;
|
||||
function fpc_mul_dword_to_qword(f1, f2: uint32): qword; compilerproc;
|
||||
function fpc_div_int64(d, n: int64): int64; compilerproc;
|
||||
function fpc_mod_int64(d, n: int64): int64; compilerproc;
|
||||
function fpc_mod_qword(d, n: qword): qword; compilerproc;
|
||||
@@ -523,6 +524,18 @@ begin
|
||||
fpc_mul_int64 := res;
|
||||
end;
|
||||
|
||||
function fpc_mul_dword_to_qword(f1, f2: uint32): qword; [public, alias: 'FPC_MUL_DWORD_TO_QWORD']; compilerproc;
|
||||
var res: qword;
|
||||
begin
|
||||
asm
|
||||
MOV EAX, f1
|
||||
MUL f2
|
||||
MOV DWORD [res], EAX
|
||||
MOV DWORD [res+4], EDX
|
||||
end;
|
||||
fpc_mul_dword_to_qword := res;
|
||||
end;
|
||||
|
||||
{ ---------- 64-bit division / modulo ---------- }
|
||||
|
||||
{ Helper: unsigned 64÷64→64 using shift-subtract (binary long division).
|
||||
|
||||
@@ -96,6 +96,7 @@ uses
|
||||
driver.net.dev.e1000,
|
||||
driver.bus.usb.ehci,
|
||||
driver.storage.fs.fat32,
|
||||
driver.storage.fs.ext2,
|
||||
arch.x86.fault,
|
||||
core.ds.fifo,
|
||||
driver.storage.fs.mgr,
|
||||
|
||||
+28
-9
@@ -42,6 +42,26 @@ function HexCharToDecimal(hex : char) : uint8;
|
||||
function abs(x : sint32) : uint32;
|
||||
|
||||
implementation
|
||||
{$ifdef CPU386}
|
||||
uses
|
||||
arch.x86.util;
|
||||
|
||||
const
|
||||
MEMCPY_X86_SMALL_COPY_THRESHOLD = 16;
|
||||
{$endif}
|
||||
|
||||
procedure scalar_memcpy(source : uint32; dest : uint32; size : uint32);
|
||||
var
|
||||
src, dst : puint8;
|
||||
i : uint32;
|
||||
begin
|
||||
if size = 0 then exit;
|
||||
for i := 0 to size - 1 do begin
|
||||
src := puint8(source + i);
|
||||
dst := puint8(dest + i);
|
||||
dst^ := src^;
|
||||
end;
|
||||
end;
|
||||
|
||||
function abs(x : sint32) : uint32;
|
||||
var
|
||||
@@ -123,17 +143,16 @@ begin
|
||||
end;
|
||||
|
||||
procedure memcpy(source : uint32; dest : uint32; size : uint32);
|
||||
var
|
||||
src, dst : puint8;
|
||||
i : uint32;
|
||||
|
||||
begin
|
||||
if size = 0 then exit;
|
||||
for i:=0 to size-1 do begin
|
||||
src:= puint8(source + i);
|
||||
dst:= puint8(dest + i);
|
||||
dst^:= src^;
|
||||
end;
|
||||
{$ifdef CPU386}
|
||||
if size < MEMCPY_X86_SMALL_COPY_THRESHOLD then
|
||||
scalar_memcpy(source, dest, size)
|
||||
else
|
||||
arch.x86.util.__REP_MOVSB_memcpy(source, dest, size);
|
||||
{$else}
|
||||
scalar_memcpy(source, dest, size);
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
function getWord(i : uint32; hi : boolean) : uint16;
|
||||
|
||||
@@ -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
Loaded 20 of 54 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user