diff --git a/system/devices/usb-ids.zig b/system/devices/usb-ids.zig index d1d907d..c086aaf 100644 --- a/system/devices/usb-ids.zig +++ b/system/devices/usb-ids.zig @@ -103,6 +103,38 @@ pub fn className(class: u8) []const u8 { }; } +/// The USB speed class (as xHCI reports it in PORTSC/slot contexts) named. +pub fn speedName(speed: u32) []const u8 { + return switch (speed) { + 1 => "Full-speed", + 2 => "Low-speed", + 3 => "High-speed", + 4 => "SuperSpeed", + 5 => "SuperSpeedPlus", + else => "unknown-speed", + }; +} + +/// A USB3 Port Link State (xHCI PORTSC PLS field) named. +pub fn linkStateName(pls: u32) []const u8 { + return switch (pls) { + 0 => "U0", + 1 => "U1", + 2 => "U2", + 3 => "U3-suspended", + 4 => "Disabled", + 5 => "RxDetect", + 6 => "Inactive", + 7 => "Polling", + 8 => "Recovery", + 9 => "HotReset", + 10 => "Compliance", + 11 => "Test", + 15 => "Resume", + else => "reserved", + }; +} + /// A fuller name for a HID interface's (subclass, protocol): "HID boot keyboard" /// / "HID boot mouse", else just the class name. Used to make input devices /// recognizable in logs. diff --git a/system/drivers/usb-xhci-bus/usb-xhci-bus.zig b/system/drivers/usb-xhci-bus/usb-xhci-bus.zig index 767cb31..313a92c 100644 --- a/system/drivers/usb-xhci-bus/usb-xhci-bus.zig +++ b/system/drivers/usb-xhci-bus/usb-xhci-bus.zig @@ -230,9 +230,15 @@ fn bringUpPort(manager: runtime.ipc.Handle, engine: *library.Controller, port: u std.log.info("port {d} enumeration failed", .{port}); return; } - std.log.info("port {d} device: {s} vendor 0x{x:0>4} product 0x{x:0>4}, {d} interface(s)", .{ + var maker_buffer: [64]u8 = undefined; + var product_buffer: [64]u8 = undefined; + const maker = engine.readString(usb_device, @intFromEnum(usb_device.device_descriptor.manufacturer_index), &maker_buffer) orelse "?"; + const product = engine.readString(usb_device, @intFromEnum(usb_device.device_descriptor.product_index), &product_buffer) orelse "?"; + std.log.info("port {d} device: {s} \"{s} {s}\" (0x{x:0>4}:0x{x:0>4}), {d} interface(s)", .{ port, usb_ids.className(usb_device.device_descriptor.device_class), + maker, + product, usb_device.device_descriptor.vendor_id, usb_device.device_descriptor.product_id, usb_device.interface_count, @@ -280,9 +286,15 @@ fn bringUpBehindHub(manager: runtime.ipc.Handle, engine: *library.Controller, hu std.log.info("hub slot {d} port {d}: enumeration failed", .{ hub.slot_id, port }); return; } - std.log.info("hub slot {d} port {d} device: {s} vendor 0x{x:0>4} product 0x{x:0>4}, {d} interface(s)", .{ + var maker_buffer: [64]u8 = undefined; + var product_buffer: [64]u8 = undefined; + const maker = engine.readString(usb_device, @intFromEnum(usb_device.device_descriptor.manufacturer_index), &maker_buffer) orelse "?"; + const product = engine.readString(usb_device, @intFromEnum(usb_device.device_descriptor.product_index), &product_buffer) orelse "?"; + std.log.info("hub slot {d} port {d} device: {s} \"{s} {s}\" (0x{x:0>4}:0x{x:0>4}), {d} interface(s)", .{ hub.slot_id, port, usb_ids.className(usb_device.device_descriptor.device_class), + maker, + product, usb_device.device_descriptor.vendor_id, usb_device.device_descriptor.product_id, usb_device.interface_count, diff --git a/system/drivers/usb-xhci-bus/usb-xhci-library.zig b/system/drivers/usb-xhci-bus/usb-xhci-library.zig index 7b00547..feaef0b 100644 --- a/system/drivers/usb-xhci-bus/usb-xhci-library.zig +++ b/system/drivers/usb-xhci-bus/usb-xhci-library.zig @@ -22,6 +22,7 @@ const std = @import("std"); const runtime = @import("runtime"); const mmio = @import("mmio"); const usb_abi = @import("usb-abi"); +const usb_ids = @import("usb-ids"); const dma = runtime.dma; const system = runtime.system; @@ -511,12 +512,46 @@ pub const Controller = struct { var port: u32 = 1; while (port <= self.max_ports) : (port += 1) { const portsc = self.portStatus(port); - std.log.info("PORTSC[{d}] = 0x{x:0>8} ccs={d} ped={d} pls={d} pp={d} speed={d}", .{ - port, portsc, portsc & 1, (portsc >> 1) & 1, (portsc >> 5) & 0xF, (portsc >> 9) & 1, (portsc >> 10) & 0xF, + std.log.info("PORTSC[{d}] 0x{x:0>8}: {s}, {s}, link={s}, power={s}, {s}", .{ + port, + portsc, + if (portsc & 1 != 0) "connected" else "empty", + if (portsc & 2 != 0) "enabled" else "disabled", + usb_ids.linkStateName((portsc >> 5) & 0xF), + if (portsc & (1 << 9) != 0) "on" else "off", + usb_ids.speedName((portsc >> 10) & 0xF), }); } } + /// Read USB STRING descriptor `index` (English, langid 0x0409) into `out` as + /// ASCII, returning the slice — for logging manufacturer/product names. + /// Null for index 0 (no string) or a failed transfer. Non-ASCII code units + /// become '?'. + pub fn readString(self: *Controller, device: *Device, index: u8, out: []u8) ?[]const u8 { + if (index == 0) return null; + var raw: [256]u8 = undefined; + const request = usb_abi.Request{ + .request_type = .{ .recipient = .device, .kind = .standard, .direction = .device_to_host }, + .request_code = .get_descriptor, + .value = (@as(u16, 3) << 8) | index, // STRING descriptor + .index = 0x0409, // English (US) + .length = raw.len, + }; + if (!self.controlTransfer(device, request, raw[0..], true)) return null; + const length = raw[0]; // bLength; the UTF-16LE payload is bytes 2..length + if (length < 2) return null; + const chars = (@min(length, raw.len) - 2) / 2; + var n: usize = 0; + var i: usize = 0; + while (i < chars and n < out.len) : (i += 1) { + const unit = @as(u16, raw[2 + i * 2]) | (@as(u16, raw[2 + i * 2 + 1]) << 8); + out[n] = if (unit >= 0x20 and unit < 0x7F) @intCast(unit) else '?'; + n += 1; + } + return out[0..n]; + } + pub fn portStatus(self: *const Controller, port: u32) u32 { return read32(self.op_base + op_portsc_base + op_portsc_stride * (port - 1)); } diff --git a/test/qemu_test.py b/test/qemu_test.py index 025bd92..5fb6f9f 100644 --- a/test/qemu_test.py +++ b/test/qemu_test.py @@ -494,7 +494,7 @@ CASES = [ # B4a: the hub powers its ports. B4b: the keyboard behind it enumerates # (route string + TT) and binds usb-hid-keyboard. "expect": r"(?s)(?=.*hub slot \d+: \d+ downstream ports powered)" - r"(?=.*hub slot \d+ port \d+ device:.*vendor 0x0627)" + r"(?=.*hub slot \d+ port \d+ device:.*0x0627)" r"(?=.*usb-hid-keyboard: ok \(device 3)", "fail": r"DANOS-TEST-RESULT: FAIL"}, # USB mass storage end to end: the boot usb-storage device (the FAT32 image, @@ -676,7 +676,7 @@ CASES = [ "-device", "usb-hub,bus=xhci2.0,port=1", "-device", "usb-hub,bus=xhci2.0,port=1.1", "-device", "usb-kbd,bus=xhci2.0,port=1.1.1"], - "expect": r"(?s)(?=.*hub slot \d+ port \d+ device:.*vendor 0x0409)" + "expect": r"(?s)(?=.*hub slot \d+ port \d+ device:.*0x0409)" r"(?=.*usb-hid-keyboard: ok \(device 3)", "fail": r"DANOS-TEST-RESULT: FAIL"}, # Hub-downstream disconnect (B4c): device_del the keyboard behind the hub;