SchOS/include/usb.h
Nexoniarz d0f7acf9eb Initial commit: Schunix as it stands before the SchOS rename
An x86-64 operating system built from xv6, now largely rewritten: SMP,
a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF
rasteriser, and a Linux-numbered syscall ABI.

No history before this point -- the tree was never under version control.
This commit exists so the rename that follows has something to undo to.
2026-08-09 13:47:01 +02:00

270 lines
9.6 KiB
C

#pragma once
#include "device.h"
// USB, the parts that are the same whatever host controller you have.
//
// Everything here comes from the USB 2.0 specification chapter 9, which every
// USB device implements regardless of speed, class or vendor. That is what
// makes enumeration possible at all: before you know anything about a device
// you can still ask it what it is, because the question is standardised.
// bmRequestType
#define USB_DIR_OUT 0x00
#define USB_DIR_IN 0x80
#define USB_TYPE_STANDARD 0x00
#define USB_TYPE_CLASS 0x20
#define USB_RECIP_DEVICE 0x00
#define USB_RECIP_INTERFACE 0x01
#define USB_RECIP_ENDPOINT 0x02
#define USB_RECIP_OTHER 0x03 // a hub's downstream port
// bRequest, standard requests
#define USB_REQ_GET_STATUS 0
#define USB_REQ_CLEAR_FEATURE 1
#define USB_REQ_SET_FEATURE 3
#define USB_REQ_SET_ADDRESS 5
#define USB_REQ_GET_DESCRIPTOR 6
#define USB_REQ_SET_DESCRIPTOR 7
#define USB_REQ_GET_CONFIG 8
#define USB_REQ_SET_CONFIG 9
#define USB_REQ_GET_INTERFACE 10
#define USB_REQ_SET_INTERFACE 11
// Descriptor types (the high byte of wValue in a GET_DESCRIPTOR)
#define USB_DT_DEVICE 1
#define USB_DT_CONFIG 2
#define USB_DT_STRING 3
#define USB_DT_INTERFACE 4
#define USB_DT_ENDPOINT 5
// Class codes we care about
#define USB_CLASS_HID 0x03
#define USB_CLASS_MASS_STORAGE 0x08
#define USB_CLASS_HUB 0x09
// HID subclass/protocol. Boot protocol is the one a BIOS uses, and the reason
// a USB keyboard works before any operating system has loaded: the report
// layout is fixed, so it can be read without parsing a report descriptor.
#define USB_HID_SUBCLASS_BOOT 0x01
#define USB_HID_PROTO_KEYBOARD 0x01
#define USB_HID_PROTO_MOUSE 0x02
// Bus speeds.
//
// These are xHCI's numbering, used as the common currency rather than
// translated at the boundary. That is a deliberate choice: there is one host
// controller, the values have to reach a slot context eventually, and a
// second enum whose only job is to be converted into this one would be
// ceremony. If a second controller ever appears, this is the thing to
// revisit -- but a class driver still has no business including xhci.h.
#define USB_SPEED_FULL 1 // 12 Mb/s
#define USB_SPEED_LOW 2 // 1.5 Mb/s
#define USB_SPEED_HIGH 3 // 480 Mb/s
#define USB_SPEED_SUPER 4 // 5 Gb/s
// An eight-byte SETUP packet, the header of every control transfer.
struct usb_setup {
uint8 bmRequestType;
uint8 bRequest;
uint16 wValue;
uint16 wIndex;
uint16 wLength;
} __attribute__((packed));
struct usb_device_desc {
uint8 bLength;
uint8 bDescriptorType;
uint16 bcdUSB;
uint8 bDeviceClass;
uint8 bDeviceSubClass;
uint8 bDeviceProtocol;
uint8 bMaxPacketSize0;
uint16 idVendor;
uint16 idProduct;
uint16 bcdDevice;
uint8 iManufacturer;
uint8 iProduct;
uint8 iSerialNumber;
uint8 bNumConfigurations;
} __attribute__((packed));
struct usb_config_desc {
uint8 bLength;
uint8 bDescriptorType;
uint16 wTotalLength;
uint8 bNumInterfaces;
uint8 bConfigurationValue;
uint8 iConfiguration;
uint8 bmAttributes;
uint8 bMaxPower;
} __attribute__((packed));
struct usb_interface_desc {
uint8 bLength;
uint8 bDescriptorType;
uint8 bInterfaceNumber;
uint8 bAlternateSetting;
uint8 bNumEndpoints;
uint8 bInterfaceClass;
uint8 bInterfaceSubClass;
uint8 bInterfaceProtocol;
uint8 iInterface;
} __attribute__((packed));
struct usb_endpoint_desc {
uint8 bLength;
uint8 bDescriptorType;
uint8 bEndpointAddress; // bit 7: 1 = IN
uint8 bmAttributes; // bits 1:0: 0 ctrl, 1 iso, 2 bulk, 3 interrupt
uint16 wMaxPacketSize;
uint8 bInterval;
} __attribute__((packed));
#define USB_EP_XFERTYPE(a) ((a) & 0x03)
#define USB_EP_BULK 2
#define USB_EP_INTERRUPT 3
#define USB_EP_IS_IN(a) (((a) & 0x80) != 0)
#define USB_EP_NUM(a) ((a) & 0x0F)
// Mass storage: SCSI transparent command set over bulk-only transport.
#define USB_MSC_SUBCLASS_SCSI 0x06
#define USB_MSC_PROTO_BOT 0x50
// ---------------------------------------------------------------------------
// Hubs
//
// A hub is a USB device like any other, and everything it does is done by
// sending it ordinary control requests. There is no separate bus protocol:
// "reset port 3" is a SET_FEATURE addressed to the hub.
// ---------------------------------------------------------------------------
#define USB_DT_HUB 0x29
// Port features, the wValue of SET_FEATURE / CLEAR_FEATURE on a port.
#define PORT_FEAT_ENABLE 1
#define PORT_FEAT_RESET 4
#define PORT_FEAT_POWER 8
#define PORT_FEAT_C_CONNECTION 16
#define PORT_FEAT_C_ENABLE 17
#define PORT_FEAT_C_RESET 20
// wPortStatus, the first half of a port's GET_STATUS reply.
#define PORTSTAT_CONNECTION (1 << 0)
#define PORTSTAT_ENABLE (1 << 1)
#define PORTSTAT_RESET (1 << 4)
#define PORTSTAT_POWER (1 << 8)
#define PORTSTAT_LOWSPEED (1 << 9)
#define PORTSTAT_HIGHSPEED (1 << 10)
struct usb_hub_desc {
uint8 bDescLength;
uint8 bDescriptorType;
uint8 bNbrPorts;
uint16 wHubCharacteristics;
uint8 bPwrOn2PwrGood; // in 2 ms units
uint8 bHubContrCurrent;
} __attribute__((packed));
// Bits 1:0 of wHubCharacteristics: 00 ganged power, 01 per-port.
#define HUB_CHAR_TT_MASK 0x0060
// HID class requests, on the interface rather than the device.
#define USB_HID_SET_IDLE 0x0A
#define USB_HID_SET_PROTOCOL 0x0B
#define USB_HID_PROTOCOL_BOOT 0
// ---------------------------------------------------------------------------
// The interface between the USB core and a host controller
//
// One host controller exists (xHCI) and one probably always will, but the
// core has no business knowing about TRBs and slot IDs, and the controller
// has no business knowing what a HID report is. This is where the line goes.
// ---------------------------------------------------------------------------
struct usb_dev;
struct usb_hc_ops {
// A control transfer on endpoint 0. `buf`, if any, must be DMA memory.
int (*control)(struct usb_dev *, struct usb_setup *, void *buf, int len);
// Make an endpoint usable. Returns 0, or -1 if the controller refused.
int (*open_ep)(struct usb_dev *, const struct usb_endpoint_desc *);
// Queue one IN transfer. Completion arrives via dev->complete.
int (*queue_in)(struct usb_dev *, int ep_addr, void *buf, int len);
// One bulk transfer, synchronously. Sleeps, so process context only.
// Returns bytes transferred, or -1.
int (*bulk)(struct usb_dev *, int ep_addr, void *buf, int len);
// Bring up a device found on a downstream port of `hub`. The controller
// works out the route string from the hub's own position.
void (*attach)(struct usb_dev *hub, int hub_port, int speed);
// Tell the controller this device is a hub with `nports` ports. It has to
// know: a hub is scheduled differently from anything else, and devices
// behind it are addressed relative to it.
int (*set_hub)(struct usb_dev *, int nports, int mtt);
};
#define USB_MAXCONF 256 // bytes of configuration descriptor we keep
struct usb_dev {
const struct usb_hc_ops *ops;
void *hc; // the controller's own handle for this device
int slot;
int speed;
int port; // ROOT hub port this whole branch hangs off
// Where the device sits in the tree.
//
// A USB device has no address of its own that describes its position -- the
// controller finds it by being told the path. `route` is xHCI's route
// string: five nibbles, one per tier, each holding the downstream port
// number taken at that tier. Tier 0 is a device on a root port, and its
// route is 0.
uint32 route;
int tier;
// Transaction translator, for a full or low speed device behind a
// high-speed hub. The controller has to know which hub is doing the
// speed conversion on its behalf, because it is the hub, not the device,
// that it actually schedules against.
int tt_slot, tt_port;
int in_use;
struct usb_device_desc desc;
uchar conf[USB_MAXCONF]; // the configuration descriptor and everything
int conflen; // that follows it, exactly as the device sent
// The first interface, which is all a boot-protocol or bulk-only device
// needs, and the endpoints belonging to it.
struct usb_interface_desc iface;
struct usb_endpoint_desc intr_in;
struct usb_endpoint_desc bulk_in;
struct usb_endpoint_desc bulk_out;
int has_intr_in, has_bulk_in, has_bulk_out;
// Filled in by whichever class driver claimed the device.
const char *driver;
void *drvdata;
void (*complete)(struct usb_dev *, void *buf, int len);
};
// One row per class driver. Matching is on the *interface* descriptor,
// because bDeviceClass is very often 0 ("look at the interfaces").
struct usb_driver {
const char *name;
uint8 class, subclass, protocol; // 0xFF matches anything
// What the rest of the kernel should consider this device to be, once this
// driver has claimed it. Distinct from `class` above, which is the USB
// interface class the descriptor advertises: the driver knows what it made
// of the device, the descriptor only says what the device claims to be.
enum dev_class devclass;
int (*probe)(struct usb_dev *);
};
// Called by a host controller once a device is addressed and its device
// descriptor has been read. The core reads the configuration, selects it, and
// offers the device to the class drivers.
void usb_attach(struct usb_dev *dev);