Groundwork for usb-tablet, which is the fix for the guest cursor and the
host's never being in the same place: a PS/2 mouse reports how far it
moved, two pointers accumulating deltas independently drift apart, and no
amount of care in the compositor can make them agree. An absolute device
reports where it is, so they coincide by construction.
- the HID driver no longer matches the boot subclass only. usb-tablet
declares subclass 0 and was refused at the door; hid_probe decides
what an interface actually is, and still declines what it cannot make
sense of.
- a non-boot HID interface is taken to be an absolute pointer and is
not asked for boot protocol, which it does not have -- that request
failing is what stopped it binding.
- tablet_report decodes the fixed 6-byte layout (buttons, 16-bit X,
16-bit Y, wheel) and scales the logical range to pixels there,
because it is the only place that knows the range.
- input_mouse_abs reports EV_ABS/ABS_X/ABS_Y so a reader can tell an
absolute position from a delta, and mouse_set_abs places the pointer
rather than nudging it.
- Schcom treats EV_ABS as authoritative and EV_REL as a nudge.
The device binds and identifies correctly: "usb-hid: tablet (absolute) on
slot 1, endpoint 0x81 every 4 ms".
It is NOT finished, and QEMU_INPUT is empty by default because of it. No
reports have yet been observed arriving on the tablet's interrupt
endpoint, so making it the default pointer would trade a cursor that
drifts for one that does not move at all. Opt in with
make qemu QEMU_INPUT='-device qemu-xhci,id=xhci -device usb-tablet,bus=xhci.0'
Next step is to find out why nothing arrives: whether the first transfer
is queued but never completes, whether a completion needs re-arming for
this endpoint, or whether QEMU is not routing motion to the tablet at all
(`info mice`).
bootcheck 108 [ok], schcom-test passes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
275 lines
9.9 KiB
C
275 lines
9.9 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
|
|
// Not a USB value: bInterfaceProtocol has no code for "absolute pointer".
|
|
// A HID device that claims neither boot protocol is treated as a tablet, which
|
|
// is what a VM offers so its cursor and the guest's can agree. Kept out of the
|
|
// 0x00-0x02 range the specification defines so it cannot collide.
|
|
#define USB_HID_PROTO_TABLET 0x80
|
|
|
|
// 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);
|
|
|