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.
270 lines
9.6 KiB
C
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);
|
|
|