Compare commits
34
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
738e5828f6 | ||
|
|
9fa1c859ac | ||
|
|
d01b534a3d | ||
|
|
491b0ea5c3 | ||
|
|
de6fde05cc | ||
|
|
fb675a2299 | ||
|
|
aa374b6816 | ||
|
|
f427b7bcc7 | ||
|
|
4ba3c5b524 | ||
|
|
7180ad39d8 | ||
|
|
6c7bcc1911 | ||
|
|
e09c9b5595 | ||
|
|
9476bb0c8a | ||
|
|
8f7db8507b | ||
|
|
188b1aebb0 | ||
|
|
452203b7b4 | ||
|
|
76c419336e | ||
|
|
2ad0ae8420 | ||
|
|
c0c199bfe6 | ||
|
|
7ec3bcb9b9 | ||
|
|
4bd0a2dfbf | ||
|
|
431e5bc8f4 | ||
|
|
8169d93a9c | ||
|
|
547251dbd0 | ||
|
|
7bbcc44c5e | ||
|
|
25475a88a7 | ||
|
|
db49c29627 | ||
|
|
c237553508 | ||
|
|
0c96d32754 | ||
|
|
3f83f279d7 | ||
|
|
82fe9fa6df | ||
|
|
983d9a428d | ||
|
|
2fa77d8295 | ||
|
|
f53afc1d2d |
No files matched your search
+8
-1
@@ -1 +1,8 @@
|
|||||||
* text=auto eol=lf
|
* text=auto eol=lf
|
||||||
|
lvglh/** linguist-vendored
|
||||||
|
lvgl/** linguist-vendored
|
||||||
|
.vscode/* linguist-vendored
|
||||||
|
lib/* linguist-generated
|
||||||
|
release/* linguist-generated
|
||||||
|
iso/* linguist-generated
|
||||||
|
*.asm linguist-detectable
|
||||||
@@ -15,3 +15,4 @@ localenv.json
|
|||||||
dockerout.txt
|
dockerout.txt
|
||||||
AGENTS.md
|
AGENTS.md
|
||||||
*.log
|
*.log
|
||||||
|
/doc/*.md
|
||||||
+22
-8
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# compile_lvgl.sh — Download LVGL v9.2 source and compile into lib/liblvgl.a
|
# compile_lvgl.sh — Download LVGL v9.2 source and compile into lib/liblvgl.a
|
||||||
# Clone & compile in /tmp (fast container-local fs).
|
# Clone & compile in /tmp (fast container-local fs).
|
||||||
# Copy source + objects to /lvgl (host mount) for debugging.
|
# Cache liblvgl.a on host mount (/code/lvgl/) to skip rebuild.
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
LVGL_VERSION="v9.2.2"
|
LVGL_VERSION="v9.2.2"
|
||||||
@@ -10,6 +10,7 @@ LVGL_DIR="/tmp/lvgl"
|
|||||||
OBJ_DIR="/tmp/lvgl_obj"
|
OBJ_DIR="/tmp/lvgl_obj"
|
||||||
CONF_DIR="$(pwd)/lvglh"
|
CONF_DIR="$(pwd)/lvglh"
|
||||||
OUT_DIR="$(pwd)/lib"
|
OUT_DIR="$(pwd)/lib"
|
||||||
|
CACHE_DIR="/code/lvgl"
|
||||||
|
|
||||||
CC="gcc"
|
CC="gcc"
|
||||||
CFLAGS="-m32 -march=i686 -ffreestanding \
|
CFLAGS="-m32 -march=i686 -ffreestanding \
|
||||||
@@ -26,6 +27,17 @@ echo " "
|
|||||||
echo "Compiling LVGL..."
|
echo "Compiling LVGL..."
|
||||||
echo " "
|
echo " "
|
||||||
|
|
||||||
|
# If cached liblvgl.a exists on host mount, just copy it and skip everything
|
||||||
|
if [ -f "${CACHE_DIR}/liblvgl.a" ]; then
|
||||||
|
echo "Found cached liblvgl.a in ${CACHE_DIR}, copying to ${OUT_DIR}..."
|
||||||
|
cp "${CACHE_DIR}/liblvgl.a" "${OUT_DIR}/liblvgl.a"
|
||||||
|
echo "(Delete lvgl/liblvgl.a to force a full rebuild.)"
|
||||||
|
echo " "
|
||||||
|
echo "LVGL compilation complete (cached)."
|
||||||
|
echo " "
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# Clone LVGL into /tmp (container-local)
|
# Clone LVGL into /tmp (container-local)
|
||||||
rm -rf "$LVGL_DIR"
|
rm -rf "$LVGL_DIR"
|
||||||
echo "Downloading LVGL ${LVGL_VERSION}..."
|
echo "Downloading LVGL ${LVGL_VERSION}..."
|
||||||
@@ -105,14 +117,16 @@ else
|
|||||||
echo "No custom LVGL files in lvglh/."
|
echo "No custom LVGL files in lvglh/."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Copy source + objects to /lvgl host mount for debugging
|
# Cache liblvgl.a and source to host mount for next build
|
||||||
echo "Copying LVGL source and objects to host mount..."
|
echo "Caching LVGL to host mount..."
|
||||||
mkdir /code/lvgl 2>/dev/null || true
|
mkdir -p "$CACHE_DIR"
|
||||||
rm -rf /code/lvgl/* /code/lvgl/.[!.]* /code/lvgl/..?* 2>/dev/null || true
|
rm -rf ${CACHE_DIR}/* ${CACHE_DIR}/.[!.]* ${CACHE_DIR}/..?* 2>/dev/null || true
|
||||||
cp -a "$LVGL_DIR/src" /code/lvgl/src
|
cp "${OUT_DIR}/liblvgl.a" "${CACHE_DIR}/liblvgl.a"
|
||||||
cp -a "$OBJ_DIR" /code/lvgl/obj
|
cp -a "$LVGL_DIR/src" "${CACHE_DIR}/src"
|
||||||
|
cp -a "$LVGL_DIR"/*.h "${CACHE_DIR}/" 2>/dev/null || true
|
||||||
|
cp -a "$OBJ_DIR" "${CACHE_DIR}/obj"
|
||||||
echo "Done."
|
echo "Done."
|
||||||
|
|
||||||
echo " "
|
echo " "
|
||||||
echo "LVGL compilation complete."
|
echo "LVGL compilation complete."
|
||||||
echo " "
|
echo " "
|
||||||
+1
-1
@@ -4,4 +4,4 @@ echo "======================="
|
|||||||
echo " "
|
echo " "
|
||||||
echo "Compiling FPC Sources..."
|
echo "Compiling FPC Sources..."
|
||||||
echo " "
|
echo " "
|
||||||
fpc -Aelf -gw -g -gl -n -v0ew -O3 -OpPENTIUM3 -Si -Sc -Sg -Xd -CX -XXs -CfSSE -CfSSE2 -Rintel -Pi386 -Tlinux -FElib/ -Fusrc/* -Fusrc/driver/* -Fusrc/driver/net/* -Fusrc/driver/bus/* -Fusrc/driver/bus/usb/* -Fusrc/driver/hid/* src/kernel.pas
|
fpc -Aelf -gw -g -gl -n -v0ew -O3 -OpPENTIUM3 -Si -Sc -Sg -Xd -CX -XXs -CfSSE -CfSSE2 -Rintel -Pi386 -Tlinux -FElib/ -Fusrc/* -Fusrc/include/* -Fusrc/driver/* -Fusrc/driver/net/* -Fusrc/driver/bus/* -Fusrc/driver/bus/usb/* -Fusrc/driver/hid/* src/kernel.pas
|
||||||
Whitespace-only changes.
+108
-5
@@ -12,15 +12,118 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
{
|
{
|
||||||
ContextSwitcher - Switch Process Contexts when preempted.
|
ContextSwitcher - Custom IRQ0 handler for preemptive context switching.
|
||||||
|
|
||||||
@author(Kieron Morris <[email protected]>)
|
Replaces the default ISR_32 with a naked assembly stub that saves the
|
||||||
|
interrupted context (PUSHAD + segment regs), calls a Pascal helper to
|
||||||
|
dispatch timer hooks, send EOI, and run the scheduler, then restores
|
||||||
|
the next process context (or idles) via the returned ESP.
|
||||||
|
|
||||||
|
@author(Kieron Morris <[email protected]>)
|
||||||
}
|
}
|
||||||
unit contextswitcher;
|
unit contextswitcher;
|
||||||
|
|
||||||
|
{$ASMMODE intel}
|
||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
implementation;
|
uses
|
||||||
|
idt, isrmanager, processmanager, proctypes, util, tracer;
|
||||||
|
|
||||||
|
{ Initialise: overwrite IDT gate 32 with our custom ISR. }
|
||||||
|
procedure init;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{ -----------------------------------------------------------------------
|
||||||
|
do_context_switch_work
|
||||||
|
Pascal helper called from the assembly ISR. Receives the current ESP
|
||||||
|
(pointing to the GS..EFLAGS save area), dispatches timer hooks, sends
|
||||||
|
EOI, runs the scheduler and returns the ESP for the next context.
|
||||||
|
Uses register calling convention: saved_esp in EAX, result in EAX.
|
||||||
|
----------------------------------------------------------------------- }
|
||||||
|
function do_context_switch_work(saved_esp : uint32) : uint32;
|
||||||
|
var
|
||||||
|
next : PProcessContext;
|
||||||
|
begin
|
||||||
|
{ 1. Save ESP into the current process }
|
||||||
|
processmanager.CurrentProcess^.SavedESP := saved_esp;
|
||||||
|
|
||||||
|
{ 2. Dispatch all hooks registered on interrupt 32 (timer hooks).
|
||||||
|
This fires TMR_0_ISR.Main which in turn fires BDA tick,
|
||||||
|
graphics refresh, USB hotplug, etc. }
|
||||||
|
isrmanager.dispatchHooks(32);
|
||||||
|
|
||||||
|
{ 3. Send End-Of-Interrupt to the master PIC }
|
||||||
|
outb($20, $20);
|
||||||
|
|
||||||
|
{ 4. Pick the next process to run (always returns non-nil; idle as fallback) }
|
||||||
|
next := processmanager.scheduler_pick_next;
|
||||||
|
processmanager.CurrentProcess := next;
|
||||||
|
|
||||||
|
{ 5. Return the selected process ESP }
|
||||||
|
do_context_switch_work := next^.SavedESP;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ -----------------------------------------------------------------------
|
||||||
|
context_switch_isr
|
||||||
|
Naked assembly ISR that replaces ISR_32 (IRQ0 / PIT timer).
|
||||||
|
|
||||||
|
Stack on entry (pushed by CPU):
|
||||||
|
[ESP+8] EFLAGS
|
||||||
|
[ESP+4] CS
|
||||||
|
[ESP+0] EIP
|
||||||
|
|
||||||
|
We push general-purpose and segment registers, call the Pascal helper,
|
||||||
|
switch ESP to the returned value, pop registers and IRETD.
|
||||||
|
----------------------------------------------------------------------- }
|
||||||
|
procedure context_switch_isr; assembler; nostackframe;
|
||||||
|
asm
|
||||||
|
{ Save all general-purpose registers (EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI) }
|
||||||
|
pushad
|
||||||
|
|
||||||
|
{ Save segment registers }
|
||||||
|
push ds
|
||||||
|
push es
|
||||||
|
push fs
|
||||||
|
push gs
|
||||||
|
|
||||||
|
{ Load kernel data segments }
|
||||||
|
mov ax, $10
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov fs, ax
|
||||||
|
mov gs, ax
|
||||||
|
|
||||||
|
{ Call Pascal helper: EAX = current ESP (register calling convention).
|
||||||
|
Returns new ESP in EAX. }
|
||||||
|
mov eax, esp
|
||||||
|
call do_context_switch_work
|
||||||
|
mov esp, eax
|
||||||
|
|
||||||
|
{ Restore segment registers }
|
||||||
|
pop gs
|
||||||
|
pop fs
|
||||||
|
pop es
|
||||||
|
pop ds
|
||||||
|
|
||||||
|
{ Restore general-purpose registers }
|
||||||
|
popad
|
||||||
|
|
||||||
|
{ Return from interrupt }
|
||||||
|
iretd
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ -----------------------------------------------------------------------
|
||||||
|
init
|
||||||
|
Override IDT gate 32 with our custom ISR. Must be called AFTER
|
||||||
|
isrmanager.init so that the default gate has been set up first.
|
||||||
|
----------------------------------------------------------------------- }
|
||||||
|
procedure init;
|
||||||
|
begin
|
||||||
|
idt.set_gate(32, uint32(@context_switch_isr), $08, ISR_RING_0);
|
||||||
|
tracer.push_trace('contextswitcher.init');
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
+2
-5
@@ -22,7 +22,7 @@ unit cpu;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
util, RTC;
|
util, RTC, stdio;
|
||||||
|
|
||||||
type
|
type
|
||||||
PCapabilities_Old = ^TCapabilities_Old;
|
PCapabilities_Old = ^TCapabilities_Old;
|
||||||
@@ -113,12 +113,10 @@ var
|
|||||||
CAP_OLD, CAP_NEW : uint32;
|
CAP_OLD, CAP_NEW : uint32;
|
||||||
|
|
||||||
procedure init();
|
procedure init();
|
||||||
|
procedure Terminal_Command_CPU(Params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
|
||||||
stdio;
|
|
||||||
|
|
||||||
procedure getCPUIdentifier;
|
procedure getCPUIdentifier;
|
||||||
var
|
var
|
||||||
id0, id1, id2 : uint32;
|
id0, id1, id2 : uint32;
|
||||||
@@ -325,7 +323,6 @@ end;
|
|||||||
|
|
||||||
procedure init();
|
procedure init();
|
||||||
begin
|
begin
|
||||||
stdio.registerCommand('CPU', @Terminal_Command_CPU, 'CPU Info.');
|
|
||||||
CPUID.Capabilities0:= PCapabilities_Old(@CAP_OLD);
|
CPUID.Capabilities0:= PCapabilities_Old(@CAP_OLD);
|
||||||
CPUID.Capabilities1:= PCapabilities_New(@CAP_NEW);
|
CPUID.Capabilities1:= PCapabilities_New(@CAP_NEW);
|
||||||
getCPUIdentifier;
|
getCPUIdentifier;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
@author(Aaron Hance <[email protected]>)
|
@author(Aaron Hance <[email protected]>)
|
||||||
@author(Kieron Morris <[email protected]>)
|
@author(Kieron Morris <[email protected]>)
|
||||||
}
|
}
|
||||||
unit PCI;
|
unit pci;
|
||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
|
|||||||
+1769
-40
File diff suppressed because it is too large.
Load diff
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
@author(Kieron Morris <[email protected]>)
|
@author(Kieron Morris <[email protected]>)
|
||||||
}
|
}
|
||||||
unit OHCI;
|
unit ohci;
|
||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
@@ -23,6 +23,7 @@ uses
|
|||||||
drivermanagement,
|
drivermanagement,
|
||||||
usbtypes,
|
usbtypes,
|
||||||
usbcore,
|
usbcore,
|
||||||
|
isrmanager,
|
||||||
lists,
|
lists,
|
||||||
strings;
|
strings;
|
||||||
|
|
||||||
@@ -234,6 +235,7 @@ type
|
|||||||
EDBulk : POHCI_ED; { Head of bulk ED list }
|
EDBulk : POHCI_ED; { Head of bulk ED list }
|
||||||
EDInterrupt : POHCI_ED; { Dummy head for interrupt ED list }
|
EDInterrupt : POHCI_ED; { Dummy head for interrupt ED list }
|
||||||
NumPorts : uint8; { Number of downstream ports }
|
NumPorts : uint8; { Number of downstream ports }
|
||||||
|
PollBusy : boolean; { Re-entrancy guard for poll }
|
||||||
PCIBus : uint8; { PCI address }
|
PCIBus : uint8; { PCI address }
|
||||||
PCISlot : uint8;
|
PCISlot : uint8;
|
||||||
PCIFunc : uint8;
|
PCIFunc : uint8;
|
||||||
@@ -1022,6 +1024,60 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ ========================= Interrupt-Driven Completion ========================= }
|
||||||
|
|
||||||
|
const
|
||||||
|
OHCI_MAX_INSTANCES = 4;
|
||||||
|
|
||||||
|
var
|
||||||
|
OHCIInstances : array[0..OHCI_MAX_INSTANCES-1] of PUSBHCDriver;
|
||||||
|
OHCIInstanceCount : uint32;
|
||||||
|
|
||||||
|
{ ISR handler — registered on the PCI interrupt line. }
|
||||||
|
procedure ohci_isr;
|
||||||
|
var
|
||||||
|
i : uint32;
|
||||||
|
hc : PUSBHCDriver;
|
||||||
|
priv : POHCI_PrivData;
|
||||||
|
intSts : uint32;
|
||||||
|
begin
|
||||||
|
for i := 0 to OHCIInstanceCount - 1 do begin
|
||||||
|
hc := OHCIInstances[i];
|
||||||
|
if hc = nil then continue;
|
||||||
|
priv := POHCI_PrivData(hc^.PrivData);
|
||||||
|
if priv = nil then continue;
|
||||||
|
|
||||||
|
{ Check if this controller has pending interrupt status }
|
||||||
|
intSts := ohci_read(priv^.MMIOBase, OHCI_REG_INTSTATUS);
|
||||||
|
if intSts = 0 then continue;
|
||||||
|
|
||||||
|
{ Acknowledge interrupt status NOW to de-assert level-triggered PCI line.
|
||||||
|
Must happen before ohci_poll, because PollBusy guard may skip the
|
||||||
|
acknowledge inside poll — leaving the line asserted = interrupt storm. }
|
||||||
|
ohci_write(priv^.MMIOBase, OHCI_REG_INTSTATUS, intSts);
|
||||||
|
|
||||||
|
{ Process completions (walks ED lists) }
|
||||||
|
ohci_poll(hc);
|
||||||
|
|
||||||
|
{ Hotplug: if RootHubStatusChange, flag for deferred processing }
|
||||||
|
if ((intSts AND OHCI_INT_RHSC) <> 0) and hc^.HotplugArmed then
|
||||||
|
hc^.PortChangePending := true;
|
||||||
|
end;
|
||||||
|
usbcore.fire_completion_hooks;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Enable hardware interrupts: WritebackDoneHead, RootHubStatusChange, UnrecoverableError + MIE. }
|
||||||
|
procedure ohci_enable_interrupts(hc : PUSBHCDriver);
|
||||||
|
var
|
||||||
|
priv : POHCI_PrivData;
|
||||||
|
begin
|
||||||
|
if (hc = nil) or (hc^.PrivData = nil) then exit;
|
||||||
|
priv := POHCI_PrivData(hc^.PrivData);
|
||||||
|
ohci_write(priv^.MMIOBase, OHCI_REG_INTENABLE,
|
||||||
|
OHCI_INT_WDH OR OHCI_INT_RHSC OR OHCI_INT_UE OR OHCI_INT_MIE);
|
||||||
|
syslog.logln('OHCI', 'Hardware interrupts enabled.');
|
||||||
|
end;
|
||||||
|
|
||||||
procedure ohci_poll(hc : PUSBHCDriver);
|
procedure ohci_poll(hc : PUSBHCDriver);
|
||||||
var
|
var
|
||||||
priv : POHCI_PrivData;
|
priv : POHCI_PrivData;
|
||||||
@@ -1038,6 +1094,11 @@ var
|
|||||||
begin
|
begin
|
||||||
if (hc = nil) or (hc^.PrivData = nil) then exit;
|
if (hc = nil) or (hc^.PrivData = nil) then exit;
|
||||||
priv := POHCI_PrivData(hc^.PrivData);
|
priv := POHCI_PrivData(hc^.PrivData);
|
||||||
|
|
||||||
|
{ Re-entrancy guard }
|
||||||
|
if priv^.PollBusy then exit;
|
||||||
|
priv^.PollBusy := true;
|
||||||
|
|
||||||
base := priv^.MMIOBase;
|
base := priv^.MMIOBase;
|
||||||
|
|
||||||
{ Read and acknowledge interrupt status }
|
{ Read and acknowledge interrupt status }
|
||||||
@@ -1107,6 +1168,7 @@ begin
|
|||||||
ed := nextED;
|
ed := nextED;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
priv^.PollBusy := false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ ========================= Load / Init ========================= }
|
{ ========================= Load / Init ========================= }
|
||||||
@@ -1124,6 +1186,7 @@ var
|
|||||||
begin
|
begin
|
||||||
push_trace('OHCI.load');
|
push_trace('OHCI.load');
|
||||||
load := false;
|
load := false;
|
||||||
|
OHCIInstanceCount := 0;
|
||||||
|
|
||||||
devices := PCI.getDeviceInfo($0C, $03, $10, count);
|
devices := PCI.getDeviceInfo($0C, $03, $10, count);
|
||||||
syslog.log('OHCI', 'Found ');
|
syslog.log('OHCI', 'Found ');
|
||||||
@@ -1220,9 +1283,24 @@ begin
|
|||||||
hcEntry := usbcore.register_hc(@hc);
|
hcEntry := usbcore.register_hc(@hc);
|
||||||
|
|
||||||
{ Scan ports for connected devices (use the stable pointer, not stack-local @hc) }
|
{ Scan ports for connected devices (use the stable pointer, not stack-local @hc) }
|
||||||
if hcEntry <> nil then
|
if hcEntry <> nil then begin
|
||||||
usbcore.scan_ports(hcEntry)
|
{ Track instance for ISR dispatch }
|
||||||
else
|
if OHCIInstanceCount < OHCI_MAX_INSTANCES then begin
|
||||||
|
OHCIInstances[OHCIInstanceCount] := hcEntry;
|
||||||
|
inc(OHCIInstanceCount);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Register ISR on PCI interrupt line }
|
||||||
|
syslog.log('OHCI', 'Registering ISR on IRQ ');
|
||||||
|
syslog.writeintln(devices[i].interrupt_line);
|
||||||
|
isrmanager.registerISR(32 + devices[i].interrupt_line, @ohci_isr);
|
||||||
|
|
||||||
|
{ Enable hardware interrupts }
|
||||||
|
ohci_enable_interrupts(hcEntry);
|
||||||
|
|
||||||
|
{ Scan for connected devices }
|
||||||
|
usbcore.scan_ports(hcEntry);
|
||||||
|
end else
|
||||||
syslog.logln('OHCI', 'Failed to register HC with USB core.');
|
syslog.logln('OHCI', 'Failed to register HC with USB core.');
|
||||||
|
|
||||||
syslog.logln('OHCI', 'Controller initialized and registered.');
|
syslog.logln('OHCI', 'Controller initialized and registered.');
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
@author(Kieron Morris <[email protected]>)
|
@author(Kieron Morris <[email protected]>)
|
||||||
}
|
}
|
||||||
unit UHCI;
|
unit uhci;
|
||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
@@ -22,6 +22,7 @@ uses
|
|||||||
drivermanagement,
|
drivermanagement,
|
||||||
usbtypes,
|
usbtypes,
|
||||||
usbcore,
|
usbcore,
|
||||||
|
isrmanager,
|
||||||
lists,
|
lists,
|
||||||
strings;
|
strings;
|
||||||
|
|
||||||
@@ -154,6 +155,7 @@ type
|
|||||||
QHControl : PUHCI_QH; { Queue Head for control transfers }
|
QHControl : PUHCI_QH; { Queue Head for control transfers }
|
||||||
QHBulk : PUHCI_QH; { Queue Head for bulk transfers }
|
QHBulk : PUHCI_QH; { Queue Head for bulk transfers }
|
||||||
QHInterrupt : PUHCI_QH; { Queue Head for interrupt transfers }
|
QHInterrupt : PUHCI_QH; { Queue Head for interrupt transfers }
|
||||||
|
PollBusy : boolean; { Re-entrancy guard for poll }
|
||||||
PCIBus : uint8; { PCI bus/slot/func for bus mastering }
|
PCIBus : uint8; { PCI bus/slot/func for bus mastering }
|
||||||
PCISlot : uint8;
|
PCISlot : uint8;
|
||||||
PCIFunc : uint8;
|
PCIFunc : uint8;
|
||||||
@@ -750,6 +752,56 @@ begin
|
|||||||
pop_trace;
|
pop_trace;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ ========================= Interrupt-Driven Completion ========================= }
|
||||||
|
|
||||||
|
const
|
||||||
|
UHCI_MAX_INSTANCES = 4;
|
||||||
|
|
||||||
|
var
|
||||||
|
UHCIInstances : array[0..UHCI_MAX_INSTANCES-1] of PUSBHCDriver;
|
||||||
|
UHCIInstanceCount : uint32;
|
||||||
|
|
||||||
|
{ ISR handler — registered on the PCI interrupt line. }
|
||||||
|
procedure uhci_isr;
|
||||||
|
var
|
||||||
|
i : uint32;
|
||||||
|
hc : PUSBHCDriver;
|
||||||
|
priv : PUHCI_PrivData;
|
||||||
|
status : uint16;
|
||||||
|
begin
|
||||||
|
for i := 0 to UHCIInstanceCount - 1 do begin
|
||||||
|
hc := UHCIInstances[i];
|
||||||
|
if hc = nil then continue;
|
||||||
|
priv := PUHCI_PrivData(hc^.PrivData);
|
||||||
|
if priv = nil then continue;
|
||||||
|
|
||||||
|
{ Check if this controller has pending interrupt status }
|
||||||
|
status := uhci_read16(priv^.IOBase, UHCI_REG_USBSTS);
|
||||||
|
if (status AND (UHCI_STS_USBINT OR UHCI_STS_ERROR)) = 0 then continue;
|
||||||
|
|
||||||
|
{ Acknowledge interrupt status NOW to de-assert level-triggered PCI line.
|
||||||
|
Must happen before uhci_poll, because PollBusy guard may skip the
|
||||||
|
acknowledge inside poll — leaving the line asserted = interrupt storm. }
|
||||||
|
uhci_write16(priv^.IOBase, UHCI_REG_USBSTS, status AND (UHCI_STS_USBINT OR UHCI_STS_ERROR));
|
||||||
|
|
||||||
|
{ Process completions (walks QH/TD chains) }
|
||||||
|
uhci_poll(hc);
|
||||||
|
end;
|
||||||
|
usbcore.fire_completion_hooks;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Enable hardware interrupts: Interrupt-on-Complete, Short Packet, Timeout/CRC. }
|
||||||
|
procedure uhci_enable_interrupts(hc : PUSBHCDriver);
|
||||||
|
var
|
||||||
|
priv : PUHCI_PrivData;
|
||||||
|
begin
|
||||||
|
if (hc = nil) or (hc^.PrivData = nil) then exit;
|
||||||
|
priv := PUHCI_PrivData(hc^.PrivData);
|
||||||
|
uhci_write16(priv^.IOBase, UHCI_REG_USBINTR,
|
||||||
|
UHCI_INTR_IOC OR UHCI_INTR_SP OR UHCI_INTR_TIMEOUT);
|
||||||
|
syslog.logln('UHCI', 'Hardware interrupts enabled.');
|
||||||
|
end;
|
||||||
|
|
||||||
{ ========================= Poll / Completion ========================= }
|
{ ========================= Poll / Completion ========================= }
|
||||||
|
|
||||||
procedure uhci_poll(hc : PUSBHCDriver);
|
procedure uhci_poll(hc : PUSBHCDriver);
|
||||||
@@ -768,6 +820,10 @@ begin
|
|||||||
if (hc = nil) or (hc^.PrivData = nil) then exit;
|
if (hc = nil) or (hc^.PrivData = nil) then exit;
|
||||||
priv := PUHCI_PrivData(hc^.PrivData);
|
priv := PUHCI_PrivData(hc^.PrivData);
|
||||||
|
|
||||||
|
{ Re-entrancy guard }
|
||||||
|
if priv^.PollBusy then exit;
|
||||||
|
priv^.PollBusy := true;
|
||||||
|
|
||||||
{ Read and clear status register }
|
{ Read and clear status register }
|
||||||
status := uhci_read16(priv^.IOBase, UHCI_REG_USBSTS);
|
status := uhci_read16(priv^.IOBase, UHCI_REG_USBSTS);
|
||||||
if (status AND (UHCI_STS_USBINT OR UHCI_STS_ERROR)) <> 0 then
|
if (status AND (UHCI_STS_USBINT OR UHCI_STS_ERROR)) <> 0 then
|
||||||
@@ -841,6 +897,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
qh := qh^.SWNext;
|
qh := qh^.SWNext;
|
||||||
end;
|
end;
|
||||||
|
priv^.PollBusy := false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ ========================= Load / Init ========================= }
|
{ ========================= Load / Init ========================= }
|
||||||
@@ -857,6 +914,7 @@ var
|
|||||||
begin
|
begin
|
||||||
push_trace('UHCI.load');
|
push_trace('UHCI.load');
|
||||||
load := false;
|
load := false;
|
||||||
|
UHCIInstanceCount := 0;
|
||||||
|
|
||||||
devices := PCI.getDeviceInfo($0C, $03, $00, count);
|
devices := PCI.getDeviceInfo($0C, $03, $00, count);
|
||||||
syslog.log('UHCI', 'Found ');
|
syslog.log('UHCI', 'Found ');
|
||||||
@@ -945,9 +1003,24 @@ begin
|
|||||||
hcEntry := usbcore.register_hc(@hc);
|
hcEntry := usbcore.register_hc(@hc);
|
||||||
|
|
||||||
{ Scan ports for connected devices (use the stable pointer, not stack-local @hc) }
|
{ Scan ports for connected devices (use the stable pointer, not stack-local @hc) }
|
||||||
if hcEntry <> nil then
|
if hcEntry <> nil then begin
|
||||||
usbcore.scan_ports(hcEntry)
|
{ Track instance for ISR dispatch }
|
||||||
else
|
if UHCIInstanceCount < UHCI_MAX_INSTANCES then begin
|
||||||
|
UHCIInstances[UHCIInstanceCount] := hcEntry;
|
||||||
|
inc(UHCIInstanceCount);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Register ISR on PCI interrupt line }
|
||||||
|
syslog.log('UHCI', 'Registering ISR on IRQ ');
|
||||||
|
syslog.writeintln(devices[i].interrupt_line);
|
||||||
|
isrmanager.registerISR(32 + devices[i].interrupt_line, @uhci_isr);
|
||||||
|
|
||||||
|
{ Enable hardware interrupts }
|
||||||
|
uhci_enable_interrupts(hcEntry);
|
||||||
|
|
||||||
|
{ Scan for connected devices }
|
||||||
|
usbcore.scan_ports(hcEntry);
|
||||||
|
end else
|
||||||
syslog.logln('UHCI', 'Failed to register HC with USB core.');
|
syslog.logln('UHCI', 'Failed to register HC with USB core.');
|
||||||
|
|
||||||
syslog.logln('UHCI', 'Controller initialized and registered.');
|
syslog.logln('UHCI', 'Controller initialized and registered.');
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
@author(Kieron Morris <[email protected]>)
|
@author(Kieron Morris <[email protected]>)
|
||||||
}
|
}
|
||||||
unit USB;
|
unit usb;
|
||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
@@ -35,6 +35,7 @@ uses
|
|||||||
usbhub,
|
usbhub,
|
||||||
usb_keyboard,
|
usb_keyboard,
|
||||||
usb_mouse,
|
usb_mouse,
|
||||||
|
usb_storage,
|
||||||
OHCI, UHCI, EHCI, XHCI;
|
OHCI, UHCI, EHCI, XHCI;
|
||||||
|
|
||||||
procedure init;
|
procedure init;
|
||||||
@@ -89,6 +90,9 @@ begin
|
|||||||
{ Initialize HID class drivers }
|
{ Initialize HID class drivers }
|
||||||
usb_keyboard.init;
|
usb_keyboard.init;
|
||||||
usb_mouse.init;
|
usb_mouse.init;
|
||||||
|
|
||||||
|
{ Initialize storage class driver }
|
||||||
|
usb_storage.init;
|
||||||
|
|
||||||
UHCI_ID.Bus:= biPCI;
|
UHCI_ID.Bus:= biPCI;
|
||||||
UHCI_ID.id0:= idANY;
|
UHCI_ID.id0:= idANY;
|
||||||
|
|||||||
+2362
-40
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -217,6 +217,9 @@ type
|
|||||||
PUSBHCDriver = ^TUSBHCDriver;
|
PUSBHCDriver = ^TUSBHCDriver;
|
||||||
PUSBTransfer = ^TUSBTransfer;
|
PUSBTransfer = ^TUSBTransfer;
|
||||||
|
|
||||||
|
{ ---- Disconnect Callback ---- }
|
||||||
|
TUSBDisconnectCallback = procedure(dev : PUSBDevice);
|
||||||
|
|
||||||
{ ---- Transfer Request ---- }
|
{ ---- Transfer Request ---- }
|
||||||
|
|
||||||
TUSBTransfer = record
|
TUSBTransfer = record
|
||||||
@@ -246,6 +249,7 @@ type
|
|||||||
Endpoints : array[0..USB_MAX_ENDPOINTS-1] of TUSBEndpoint;
|
Endpoints : array[0..USB_MAX_ENDPOINTS-1] of TUSBEndpoint;
|
||||||
NumEndpoints : uint8;
|
NumEndpoints : uint8;
|
||||||
Configured : boolean;
|
Configured : boolean;
|
||||||
|
fnDisconnect : TUSBDisconnectCallback; { Called before device is freed on unplug }
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ ---- HC Driver Abstraction (Function Pointer Types) ---- }
|
{ ---- HC Driver Abstraction (Function Pointer Types) ---- }
|
||||||
@@ -269,6 +273,8 @@ type
|
|||||||
PrivData : Pointer; { HC-specific state }
|
PrivData : Pointer; { HC-specific state }
|
||||||
Devices : PLinkedListBase; { List of PUSBDevice attached }
|
Devices : PLinkedListBase; { List of PUSBDevice attached }
|
||||||
NextAddress : uint8; { Next available USB address (1-127) }
|
NextAddress : uint8; { Next available USB address (1-127) }
|
||||||
|
PortChangePending : boolean; { Set by ISR when port status changes; processed in deferred context }
|
||||||
|
HotplugArmed : boolean; { False during boot; set true after initial scan_ports to ignore spurious RHSC }
|
||||||
{ Operations }
|
{ Operations }
|
||||||
fnReset : TUSBHCReset;
|
fnReset : TUSBHCReset;
|
||||||
fnStart : TUSBHCStart;
|
fnStart : TUSBHCStart;
|
||||||
|
|||||||
@@ -32,32 +32,41 @@ uses
|
|||||||
|
|
||||||
procedure callback(scan_code : void);
|
procedure callback(scan_code : void);
|
||||||
var
|
var
|
||||||
info : TKeyInfo;
|
info : TKeyInfo;
|
||||||
|
raw : uint8;
|
||||||
|
baseCode : uint8;
|
||||||
|
isBreak : boolean;
|
||||||
begin
|
begin
|
||||||
info.SHIFT_DOWN := keyboard.is_shift;
|
raw := uint8(scan_code);
|
||||||
info.CTRL_DOWN := keyboard.is_ctrl;
|
|
||||||
info.ALT_DOWN := keyboard.is_alt;
|
{ In PS/2 scancode set 1, bit 7 set = key release (break code) }
|
||||||
info.is_down_code := true;
|
isBreak := (raw AND $80) <> 0;
|
||||||
|
baseCode := raw AND $7F;
|
||||||
|
|
||||||
|
{ Update modifier state (use raw codes so both make/break are handled) }
|
||||||
|
if raw = 42 then keyboard.is_shift := true;
|
||||||
|
if raw = 170 then keyboard.is_shift := false;
|
||||||
|
if raw = 29 then keyboard.is_ctrl := true;
|
||||||
|
if raw = 157 then keyboard.is_ctrl := false;
|
||||||
|
if raw = 56 then keyboard.is_alt := true;
|
||||||
|
if raw = 184 then keyboard.is_alt := false;
|
||||||
|
|
||||||
|
info.SHIFT_DOWN := keyboard.is_shift;
|
||||||
|
info.CTRL_DOWN := keyboard.is_ctrl;
|
||||||
|
info.ALT_DOWN := keyboard.is_alt;
|
||||||
|
info.is_down_code := not isBreak;
|
||||||
|
|
||||||
if keyboard.is_shift then begin
|
if keyboard.is_shift then begin
|
||||||
if key_matrix_shift[uint8(scan_code)].key_code <> 0 then begin
|
if key_matrix_shift[baseCode].key_code <> 0 then begin
|
||||||
info.key_code := key_matrix_shift[uint8(scan_code)].key_code;
|
info.key_code := key_matrix_shift[baseCode].key_code;
|
||||||
keyboard.reportKeyEvent(info);
|
keyboard.reportKeyEvent(info);
|
||||||
end;
|
end;
|
||||||
end else begin
|
end else begin
|
||||||
if key_matrix[uint8(scan_code)].key_code <> 0 then begin
|
if key_matrix[baseCode].key_code <> 0 then begin
|
||||||
info.key_code := key_matrix[uint8(scan_code)].key_code;
|
info.key_code := key_matrix[baseCode].key_code;
|
||||||
keyboard.reportKeyEvent(info);
|
keyboard.reportKeyEvent(info);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Update modifier state }
|
|
||||||
if uint8(scan_code) = 42 then keyboard.is_shift := true;
|
|
||||||
if uint8(scan_code) = 170 then keyboard.is_shift := false;
|
|
||||||
if uint8(scan_code) = 29 then keyboard.is_ctrl := true;
|
|
||||||
if uint8(scan_code) = 157 then keyboard.is_ctrl := false;
|
|
||||||
if uint8(scan_code) = 56 then keyboard.is_alt := true;
|
|
||||||
if uint8(scan_code) = 184 then keyboard.is_alt := false;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function load(ptr : void) : boolean;
|
function load(ptr : void) : boolean;
|
||||||
|
|||||||
@@ -119,8 +119,12 @@ var
|
|||||||
x32, y32 : sint32;
|
x32, y32 : sint32;
|
||||||
r : pchar;
|
r : pchar;
|
||||||
begin
|
begin
|
||||||
|
{ mouse_wait(0) checks OBF with a short timeout (100 iterations).
|
||||||
|
When it returns true, data is ready — read it directly with inb($60).
|
||||||
|
Do NOT call mouse_read here: it uses mouse_wait_long (100k iterations)
|
||||||
|
and would spin-block the ISR if the next packet byte hasn't arrived yet. }
|
||||||
while mouse_wait(0) do begin
|
while mouse_wait(0) do begin
|
||||||
b := mouse_read;
|
b := inb($60);
|
||||||
if Cycle = 0 then begin
|
if Cycle = 0 then begin
|
||||||
if (b AND $08) = $08 then begin
|
if (b AND $08) = $08 then begin
|
||||||
Mouse_Byte[Cycle] := b;
|
Mouse_Byte[Cycle] := b;
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Loaded 20 of 65 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user