Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Home/Net/Drivers/Run.ZC
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ U0 NetDriverInit()
net_driver = net_driver->next;
}

//No supported PCI NIC: fall back to a USB CDC-ECM adapter if one is bound.
//No PCI NIC: fall back to a bound USB CDC-ECM adapter.
if (!found && UsbEcmPresent)
{
found = TRUE;
Expand Down
41 changes: 16 additions & 25 deletions src/Home/Net/Drivers/UsbEcm.ZC
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
/* USB CDC-ECM network driver (Zeal net stack side).

Bridges the kernel USB CDC-ECM class driver (src/Kernel/Usb/KUsbEcm.ZC)
into the net stack's driver contract. The kernel side owns the xHCI bulk
transfers and a receive frame ring; this file just implements the
Ethernet + NetDriver entry points the stack calls, and runs a task that
drains received frames into the NetQueue (the USB stack is polled - there
is no NIC interrupt to hook).

Selected by NetDriverInit (Run.ZC) when a USB NIC is bound and no PCI NIC
matched. Works for QEMU `-device usb-net` (its CDC-ECM config) and the
RTL8153 USB-C adapter.
/* USB CDC-ECM network driver.

Net stack side of the kernel CDC-ECM class driver (Kernel/Usb/KUsbEcm.ZC),
which owns the bulk transfers and the receive ring. Selected by
NetDriverInit when no PCI NIC matched. RX is drained by a task; the USB
stack is polled, so there is no NIC interrupt.
*/

#define USB_ECM_TX_BUFFERS 8 //power of two; TX frames in flight
#define USB_ECM_TX_BUFFERS 8 //power of two

//The kernel USB ECM interface (UsbEcmPresent/MacAddr/Send/Recv) is provided by
//public AOT funcs in $LK,"KUsbEcm.ZC",A="FF:::/Kernel/Usb/KUsbEcm.ZC"$; they are
//already in the global symbol table, so call them directly (no extern - an
//extern decl creates an unresolved JIT import instead of binding the public).
//UsbEcm* are public kernel AOT funcs, already in the global symbol table.
//Do not extern them: that makes an unresolved JIT import instead of binding.

class CUsbEcmNet
{
Expand All @@ -35,17 +27,17 @@ U8 *EthernetMACGet()
}

I64 NetDriverPacketBufferGet(I64 de_index)
{//Start of the built TX frame for this descriptor.
{//Built TX frame for this descriptor.
return usb_ecm_net.tx_buffers + de_index * ETHERNET_FRAME_SIZE;
}

I64 NetDriverPacketLengthGet(I64 de_index)
{//Total frame length (header + payload) stored at allocate time.
{//Frame length recorded at allocate time.
return usb_ecm_net.tx_lengths[de_index];
}

U0 NetDriverTransmitPacketFinish(I64 de_index)
{//Hand the finished frame to the kernel for a bulk-OUT transfer.
{//Hand the frame to the kernel for a bulk-OUT transfer.
U8 *frame = usb_ecm_net.tx_buffers + de_index * ETHERNET_FRAME_SIZE;
I64 len = usb_ecm_net.tx_lengths[de_index];

Expand All @@ -54,9 +46,8 @@ U0 NetDriverTransmitPacketFinish(I64 de_index)

I64 EthernetFrameAllocate(U8 **packet_buffer_out, U8 *source_address,
U8 *destination_address, U16 ethertype, I64 packet_length)
{/* Build an Ethernet frame header into the next TX buffer and hand back the
payload pointer + descriptor index. Mirrors the PCNet driver: pad the
payload up to the minimum, cap the whole frame to the buffer size. */
{/* Build a frame header in the next TX buffer, return the payload pointer
and descriptor index. Pads the payload to the minimum, caps to buffer. */

U8 *frame;
I64 de_index, total;
Expand Down Expand Up @@ -84,7 +75,7 @@ I64 EthernetFrameAllocate(U8 **packet_buffer_out, U8 *source_address,
}

U0 NetStop()
{//Pause RX delivery (the kernel keeps draining USB into its ring regardless).
{//Pause RX delivery; the kernel keeps filling its ring.
usb_ecm_net.running = FALSE;
}

Expand All @@ -94,7 +85,7 @@ U0 NetStart()
}

U0 UsbEcmNetRxTask(I64 dummy=0)
{//Drain received frames from the kernel ECM ring into the NetQueue.
{//Drain the kernel ECM ring into the NetQueue.
U8 *buf;
I64 len;

Expand Down
82 changes: 40 additions & 42 deletions src/Kernel/Usb/KUsb.HH
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#define USB_DEVS_MAX 16
#define USB_EPS_MAX 8
#define USB_IFS_MAX 6
#define USB_HCS_MAX 4 //xHCI controllers driven at once

//USB descriptor types
#define USB_DESC_DEV 1
Expand Down Expand Up @@ -62,7 +63,7 @@
#define USB_MSD_RQ_RESET 0xFF
#define USB_MSD_RQ_GET_MAX_LUN 0xFE

//CDC / Ethernet Networking Control Model (ECM)
//CDC / Ethernet Networking Control Model
#define USB_CLASS_COMM 0x02 //CDC communications (control interface)
#define USB_CLASS_CDC_DATA 0x0A //CDC data interface (bulk endpoints)
#define USB_CDC_SUBCLASS_ECM 0x06 //Ethernet Networking Control Model
Expand All @@ -78,8 +79,7 @@
//Hub class
#define USB_DESC_HUB 0x29 //hub descriptor (USB2)
#define USB_DESC_SSHUB 0x2A //SuperSpeed hub descriptor
//SuperSpeed hubs must be told their tier before they can route packets
//(USB 3.x 10.14.2.7). Without it, downstream Address Device fails.
//SuperSpeed hubs must be told their tier before they can route (USB 3.x 10.14.2.7).
#define USB_HUB_RQ_SET_DEPTH 12
//Hub class feature selectors (port)
#define USB_HUB_F_PORT_CONNECTION 0
Expand Down Expand Up @@ -253,14 +253,13 @@ class CUsbEPDesc
#define XHCI_RING_TRBS 256 //4096 / 16
#define XHCI_EVT_RING_TRBS 256

//Interrupt/bulk-IN auto-requeue report handler kinds (see CUsbEP.auto_kind).
//Auto-requeue handler kinds (CUsbEP.auto_kind).
#define USB_AUTO_NONE 0
#define USB_AUTO_KBD 1
#define USB_AUTO_MOUSE 2
#define USB_AUTO_ECM 3 //CDC-ECM bulk-IN: receive ethernet frame

//Forward decls: report handlers live in UsbHid.ZC / KUsbEcm.ZC (compiled after
//KXhci.ZC), but XhciEventPoll dispatches to them. Single-pass compiler needs these.
//XhciEventPoll dispatches to handlers compiled after it.
extern U0 UsbKbdCB(U8 *dev, U8 *ep);
extern U0 UsbMouseCB(U8 *dev, U8 *ep);
extern U0 UsbEcmRxCB(U8 *dev, U8 *ep);
Expand Down Expand Up @@ -312,9 +311,35 @@ class CUsbIf
ep_count;
};

class CXhciGlobals
{
Bool present, running;
U8 *mmio, //capability base (uncached alias)
*op, //operational base
*rt; //runtime base
U32 *db; //doorbell array
I64 max_slots, max_ports, ctx_size,
scratch_count;
U64 *dcbaa,
*scratch_arr,
*erst;
CXhciRing cmd_ring;
CXhciTRB *evt_ring;
I64 evt_deq, evt_cycle;
I64 lock, //ring enqueue lock
poll_lock; //event ring consumer lock (boot inline poll vs USB task)
//Command completion, set by event poll
Bool cmd_done;
I64 cmd_code, cmd_slot;
CXhciTRB *cmd_trb; //pending command TRB
Bool port_change; //port status change event seen
I64 pci_bus, pci_dev, pci_fun;
};

class CUsbDev
{
Bool present, configured;
CXhciGlobals *hc; //owning controller; slot/port are unique only within it
I64 slot, //xHCI slot id
port, //root port index (0-based)
speed; //USB_SPEED_*
Expand All @@ -328,8 +353,7 @@ class CUsbDev
I64 vendor_id, product_id,
dev_class, dev_subclass, dev_proto,
config_val;
//Topology: root devices have route 0, depth 0, no parent. Devices behind hubs
//carry an xHCI route string + the parent hub's slot/port (for TT of LS/FS).
//Topology. Root devices: route 0, depth 0, no parent.
I64 route, //xHCI route string (20 bits)
depth, //hub tier (0 = attached to a root port)
parent_slot, //parent hub xHCI slot id (0 = root)
Expand All @@ -342,39 +366,15 @@ class CUsbDev
U8 desc_name[64]; //product string, ASCII
};

class CXhciGlobals
{
Bool present, running;
U8 *mmio, //capability base (uncached alias)
*op, //operational base
*rt; //runtime base
U32 *db; //doorbell array
I64 max_slots, max_ports, ctx_size,
scratch_count;
U64 *dcbaa,
*scratch_arr,
*erst;
CXhciRing cmd_ring;
CXhciTRB *evt_ring;
I64 evt_deq, evt_cycle;
I64 lock, //ring enqueue lock
poll_lock; //event ring consumer lock (boot inline poll vs USB task)
//Command completion, set by event poll
Bool cmd_done;
I64 cmd_code, cmd_slot;
CXhciTRB *cmd_trb; //pending command TRB
Bool port_change; //port status change event seen
I64 pci_bus, pci_dev, pci_fun;
};

//Hub driver (KUsbHub.ZC) enumerates downstream devices then binds them via
//UsbDrvBind (KUsbBoot.ZC, compiled later): forward decls for the single-pass
//compiler. Declared here (after CUsbDev/CUsbIf) so the struct types are known.
//KUsbHub.ZC calls UsbDrvBind from KUsbBoot.ZC, compiled later. Declared after
//CUsbDev/CUsbIf so the types are known.
extern U0 UsbDrvBind(CUsbDev *dev);
extern U0 UsbHubAttach(CUsbDev *dev, CUsbIf *uif);

//Globals defined in $LK,"KXhci.ZC",A="FF:::/Kernel/Usb/KXhci.ZC,usb_xhci"$.
extern CXhciGlobals usb_xhci;
extern CXhciGlobals usb_hcs[USB_HCS_MAX];
extern I64 usb_hc_count;
extern CXhciGlobals *usb_xhci; //active controller
extern CUsbDev usb_devs[USB_DEVS_MAX];
extern CTask *usb_task;
extern U8 usb_boot_iso_drv_let; //Drive letter of mounted USB install media, else 0
Expand All @@ -383,12 +383,10 @@ public extern U0 UsbRep();
public extern I64 UsbBootInit();
public extern U0 UsbBootDriveFallback();
public extern U0 UsbTaskMain(I64 dummy=0);
public extern Bool XhciInit(I64 start_n=0, Bool any=FALSE);
public extern U0 XhciSwitch();
public extern U0 XhciFindNic(I64 max_tries=8);
public extern I64 XhciInitAll();

//CDC-ECM USB NIC interface, consumed by the /System net driver
//$LK,"UsbEcm.ZC",A="FF:::/Home/Net/Drivers/UsbEcm.ZC"$. Operates on the first NIC.
//CDC-ECM NIC interface for $LK,"UsbEcm.ZC",A="FF:::/Home/Net/Drivers/UsbEcm.ZC"$.
//Operates on the first bound NIC.
public extern U0 UsbEcmRep();
public extern Bool UsbEcmPresent();
public extern U0 UsbEcmMacAddr(U8 *out6);
Expand Down
Loading
Loading