Added more documentation + USBSystem.md in docs\appdev

This commit is contained in:
rwusmm 2026-05-11 22:13:23 +03:00
parent 8e805938a1
commit 907824ebda
9 changed files with 552 additions and 26 deletions

2
.gitignore vendored
View file

@ -8,6 +8,8 @@ run.ps1
edk2-vars.fd
qemu-debug.log
boredos.dump
Makefile.unsafe
# makefile.unsafe isn't supposed to be committed
# Userland
src/userland/bin/

View file

@ -84,18 +84,13 @@ LIMINE_URL_BASE = https://github.com/limine-bootloader/limine/raw/v$(LIMINE_VERS
HOST_OS := $(shell uname -s 2>/dev/null || echo Windows)
.PHONY: all clean run run-hd limine-setup run-windows run-mac run-linux run-hd-mac run-hd-windows run-hd-linux unsafe
.PHONY: all clean run run-hd limine-setup run-windows run-mac run-linux run-hd-mac run-hd-windows run-hd-linux
all:
$(call PRINT_STEP,STARTING BOREDOS BUILD)
$(MAKE) $(ISO_IMAGE)
$(call PRINT_STEP,BUILD COMPLETE)
unsafe:
$(call PRINT_STEP,STARTING BOREDOS BUILD (UNSAFE MODE))
$(MAKE) $(ISO_IMAGE) LD=ld AR=ar CC=gcc TCC_CC=gcc TCC_AR=ar
$(call PRINT_STEP,BUILD COMPLETE)
$(BUILD_DIR):
$(call PRINT_STEP,CREATING BUILD DIRECTORY)
mkdir -p $(BUILD_DIR)

377
docs/appdev/USBsystem.md Normal file
View file

@ -0,0 +1,377 @@
# BoredOS USB System Documentation
## Overview
The BoredOS USB system provides a modular, extensible framework for USB device support in the kernel. The implementation follows a layered architecture with clear separation between host controller management, device enumeration, and driver management. The system currently supports UHCI (Universal Host Controller Interface) with infrastructure in place for OHCI, EHCI, and XHCI expansion.
## Architecture
### Core Components
The USB system consists of several key components that work together to provide USB functionality:
- **Host Controller Layer**: Manages USB host controllers (UHCI/OHCI/EHCI/XHCI)
- **Device Enumeration Layer**: Handles device discovery and configuration
- **Driver Management Layer**: Provides plug-and-play driver loading/unloading
- **Transfer Layer**: Implements USB transfer types (control, interrupt, bulk, isochronous)
### Directory Structure
```
src/userland/usb/
├── usb.c # Core USB stack implementation
├── usb.h # USB core data structures and constants
├── uhci.c # UHCI host controller driver
├── uhci.h # UHCI-specific definitions
├── driver.c # Driver management system
├── driver.h # Driver framework definitions
├── hid.h # HID class definitions
├── logitech_b110.c # Example device driver (Logitech B110 mouse)
├── logitech_b110.h # Logitech B110 driver definitions
└── syscall.h # System call interface definitions
```
## Host Controller Support
### UHCI Implementation
UHCI (Universal Host Controller Interface) is the primary supported host controller. The implementation includes:
- **Frame List Management**: 1024-entry frame list for transfer scheduling
- **Transfer Descriptors**: TD pools for efficient memory management
- **Queue Heads**: QH management for transfer chaining
- **Port Management**: Port reset, enable/disable, and status monitoring
Key UHCI features:
- Memory-mapped I/O access via PCI BAR0
- Interrupt and control transfer support
- Automatic port detection and device reset
- Frame-based scheduling (1ms frames)
### Controller Detection
The system automatically detects USB host controllers during initialization by scanning the PCI bus:
```c
// Class code 0x0C indicates USB controller
// Programming interface determines controller type:
// 0x00 = UHCI, 0x10 = OHCI, 0x20 = EHCI, 0x30 = XHCI
```
## Device Enumeration
### Enumeration Process
1. **Controller Initialization**: Initialize detected host controllers
2. **Port Scanning**: Check each controller's ports for connected devices
3. **Device Reset**: Reset newly detected devices
4. **Descriptor Reading**: Read device and configuration descriptors
5. **Address Assignment**: Assign unique USB addresses to devices
6. **Driver Loading**: Load appropriate drivers based on VID:PID
### Device Structure
Each USB device is represented by the `usb_device_t` structure:
```c
typedef struct {
uint16_t vendor_id; // Vendor identifier
uint16_t product_id; // Product identifier
uint8_t device_class; // USB device class
uint8_t device_subclass; // Device subclass
uint8_t device_protocol; // Device protocol
uint8_t config_value; // Configuration value
uint8_t interface_number; // Interface number
uint8_t alternate_setting; // Alternate setting
uint8_t endpoint_in; // IN endpoint address
uint8_t endpoint_out; // OUT endpoint address
uint16_t max_packet_size; // Maximum packet size
bool initialized; // Initialization status
} usb_device_t;
```
## Driver Management System
### Driver Registration
Drivers are registered with the driver manager using the `usb_driver_t` structure:
```c
typedef struct {
uint16_t vendor_id; // Target vendor ID
uint16_t product_id; // Target product ID
char name[64]; // Driver name
bool (*probe)(usb_device_t *dev); // Device probing function
int (*init)(usb_device_t *dev); // Initialization function
void (*deinit)(usb_device_t *dev); // Deinitialization function
int (*poll)(usb_device_t *dev); // Polling function
bool loaded; // Load status
} usb_driver_t;
```
### Hotplug Support
The system supports hotplug detection and automatic driver loading:
- **Device Detection**: Monitors for newly connected devices
- **Driver Matching**: Matches devices to registered drivers by VID:PID
- **Automatic Loading**: Loads appropriate drivers for new devices
- **Cleanup**: Unloads drivers when devices are disconnected
### Driver Lifecycle
1. **Registration**: Driver registers with the system
2. **Probing**: System probes driver for device compatibility
3. **Initialization**: Driver initializes compatible devices
4. **Operation**: Driver handles device I/O through polling
5. **Deinitialization**: Driver cleanup on device removal
## Transfer Types
### Control Transfers
Control transfers are used for device configuration and status requests:
```c
int usb_control_transfer(usb_device_t *dev, usb_setup_packet_t *setup, void *data);
```
The UHCI implementation handles control transfers through a three-stage TD chain:
- **Setup Stage**: Sends the 8-byte setup packet
- **Data Stage**: Optional data phase (IN or OUT)
- **Status Stage**: Transfer completion handshake
### Interrupt Transfers
Interrupt transfers are used for periodic data such as HID input:
```c
int usb_interrupt_in(usb_device_t *dev, uint8_t endpoint, void *data, uint16_t len);
```
Features:
- Periodic scheduling (typically 8ms for HID devices)
- Low-latency data delivery
- Automatic retry on errors
## Adding New USB Drivers
### Driver Template
To add a new USB driver, create a source file following this template:
```c
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in this file it has in it, as per the GPL license terms.
#include "driver.h"
#include "usb.h"
#include <stdio.h>
#include <string.h>
// Driver-specific constants
#define VENDOR_ID 0x1234
#define PRODUCT_ID 0x5678
// Driver state
static bool driver_initialized = false;
// Probe function - check if driver can handle this device
bool mydevice_probe(usb_device_t *dev) {
if (!dev) return false;
// Check VID:PID match
if (dev->vendor_id == VENDOR_ID && dev->product_id == PRODUCT_ID) {
return true;
}
return false;
}
// Initialize the device
int mydevice_init(usb_device_t *dev) {
if (!dev) return -1;
// Perform device-specific initialization
// Set up endpoints, configure device, etc.
driver_initialized = true;
printf("[MYDEVICE] Driver initialized\n");
return 0;
}
// Deinitialize the device
void mydevice_deinit(usb_device_t *dev) {
(void)dev;
driver_initialized = false;
printf("[MYDEVICE] Driver deinitialized\n");
}
// Poll the device for data/events
int mydevice_poll(usb_device_t *dev) {
if (!driver_initialized || !dev) return -1;
// Read data from device
uint8_t buffer[64];
int len = usb_interrupt_in(dev, dev->endpoint_in, buffer, sizeof(buffer));
if (len > 0) {
// Process received data
// Handle device-specific protocol
}
return len;
}
// Driver structure
static usb_driver_t mydevice_driver = {
.vendor_id = VENDOR_ID,
.product_id = PRODUCT_ID,
.name = "My USB Device",
.probe = mydevice_probe,
.init = mydevice_init,
.deinit = mydevice_deinit,
.poll = mydevice_poll,
.loaded = false
};
// Driver entry point
usb_driver_t* mydevice_get_driver(void) {
return &mydevice_driver;
}
```
### Integration Steps
1. **Create Driver Files**: Create `.c` and `.h` files for your driver
2. **Implement Required Functions**: Implement probe, init, deinit, and poll functions
3. **Register Driver**: Call `driver_register()` during system initialization
4. **Add to Build**: Include your driver files in the Makefile
5. **Test**: Test with actual hardware or USB passthrough
### Best Practices
- **Error Handling**: Always check return values and handle errors gracefully
- **Resource Management**: Clean up resources in deinit function
- **Thread Safety**: Use appropriate synchronization if accessing shared data
- **Performance**: Minimize polling frequency to reduce CPU usage
- **Standards Compliance**: Follow USB specification for your device class
## HID Support
The USB system includes basic HID (Human Interface Device) support:
### HID Classes Supported
- **HID Boot Mouse**: Basic mouse functionality
- **HID Boot Keyboard**: Basic keyboard functionality (planned)
### HID Report Processing
HID reports are parsed through the generic HID layer:
```c
// Parse mouse report
hid_mouse_report_t report;
hid_parse_mouse_report(buffer, len, &report);
// Handle mouse events
wm_handle_mouse(report.x, report.y, report.buttons, report.wheel);
```
## Limitations and Future Development
### Current Limitations
- **UHCI Only**: Only UHCI host controllers are fully supported
- **Basic HID**: Limited HID class support
- **No USB 3.0**: XHCI (USB 3.0) support not implemented
- **Limited Transfer Types**: Only control and interrupt transfers supported
- **No Power Management**: No USB power management features
### Planned Enhancements
- **OHCI/EHCI Support**: Add support for older host controllers
- **XHCI Support**: Implement USB 3.0 host controller support
- **Mass Storage**: Add USB mass storage class support
- **Audio Support**: Implement USB audio class
- **Hub Support**: Add USB hub enumeration and management
- **Power Management**: Implement USB power management features
## Debugging USB Issues
### Common Issues
1. **Device Not Detected**: Check PCI configuration and controller initialization
2. **Driver Not Loading**: Verify VID:PID matching and driver registration
3. **Transfer Failures**: Check endpoint configuration and TD setup
4. **Enumeration Failures**: Verify device reset and descriptor reading
### Debug Techniques
- **Serial Output**: Use serial_write() for debugging output
- **PCI Analysis**: Check PCI configuration space for controller detection
- **Transfer Monitoring**: Monitor transfer descriptor status fields
- **Driver Logging**: Add logging to driver functions for troubleshooting
## API Reference
### Core Functions
```c
// Initialize USB stack
void usb_init(void);
// Detect controller type
usb_hc_type_t usb_detect_controller(usb_hc_t *hc);
// Enumerate a device
bool usb_enumerate_device(usb_device_t *dev);
// Perform control transfer
int usb_control_transfer(usb_device_t *dev, usb_setup_packet_t *setup, void *data);
// Perform interrupt transfer
int usb_interrupt_in(usb_device_t *dev, uint8_t endpoint, void *data, uint16_t len);
// Enumerate all devices
void usb_enumerate_devices(void);
// Get device count
int usb_get_device_count(void);
// Get device by index
usb_device_t* usb_get_device(int index);
```
### Driver Management Functions
```c
// Initialize driver manager
void driver_manager_init(void);
// Register a driver
int driver_register(usb_driver_t *driver);
// Unregister a driver
int driver_unregister(usb_driver_t *driver);
// Load driver for device
int driver_load_for_device(usb_device_t *dev);
// Unload driver for device
void driver_unload_for_device(usb_device_t *dev);
// Check for hotplug events
void driver_check_hotplug(void);
// Poll active drivers
void driver_poll_active_instances(void);
```
## Conclusion
The BoredOS USB system provides a solid foundation for USB device support with room for expansion. The modular design allows for easy addition of new device drivers and host controller support. While currently focused on basic HID devices through UHCI, the architecture supports future expansion to include USB 3.0, mass storage, and other device classes.
For developers looking to contribute or add USB support for their devices, the driver framework provides a clean, well-defined interface that follows established USB development practices.

View file

@ -5,11 +5,16 @@
#include <string.h>
#include <stdio.h>
// Registry of available USB drivers
static usb_driver_t *driver_registry[MAX_DRIVERS];
static int driver_count = 0;
// Active driver instances (drivers loaded for specific devices)
static driver_instance_t active_instances[MAX_DRIVERS];
static int instance_count = 0;
// Initialize the driver manager
// Clears the driver registry and instance lists
void driver_manager_init(void) {
memset(driver_registry, 0, sizeof(driver_registry));
memset(active_instances, 0, sizeof(active_instances));
@ -17,9 +22,12 @@ void driver_manager_init(void) {
instance_count = 0;
}
// Register a USB driver in the driver registry
// Returns 0 on success, -1 on failure (duplicate or full registry)
int driver_register(usb_driver_t *driver) {
if (!driver || driver_count >= MAX_DRIVERS) return -1;
// Check for duplicate driver (same VID:PID)
for (int i = 0; i < driver_count; i++) {
if (driver_registry[i]->vendor_id == driver->vendor_id &&
driver_registry[i]->product_id == driver->product_id) {
@ -32,11 +40,15 @@ int driver_register(usb_driver_t *driver) {
return 0;
}
// Unregister a USB driver from the driver registry
// Returns 0 on success, -1 if driver not found
int driver_unregister(usb_driver_t *driver) {
if (!driver) return -1;
// Find and remove the driver from the registry
for (int i = 0; i < driver_count; i++) {
if (driver_registry[i] == driver) {
// Shift remaining drivers down
for (int j = i; j < driver_count - 1; j++) {
driver_registry[j] = driver_registry[j + 1];
}
@ -49,9 +61,13 @@ int driver_unregister(usb_driver_t *driver) {
return -1;
}
// Load and initialize a driver for a specific USB device
// Searches the registry for a matching driver and loads it
// Returns 0 on success, -1 if no matching driver found
int driver_load_for_device(usb_device_t *dev) {
if (!dev) return -1;
// Search for a driver that matches this device's VID:PID
for (int i = 0; i < driver_count; i++) {
usb_driver_t *driver = driver_registry[i];
@ -60,14 +76,17 @@ int driver_load_for_device(usb_device_t *dev) {
if (instance_count >= MAX_DRIVERS) return -1;
// Call the driver's probe function to check if it can handle this device
if (driver->probe && !driver->probe(dev)) {
continue;
}
// Initialize the driver
if (driver->init && driver->init(dev) != 0) {
continue;
}
// Create an active instance for this device
active_instances[instance_count].device = *dev;
active_instances[instance_count].driver = driver;
active_instances[instance_count].active = true;
@ -83,14 +102,18 @@ int driver_load_for_device(usb_device_t *dev) {
return -1;
}
// Unload a driver for a specific USB device
// Calls the driver's deinit function and removes the instance
void driver_unload_for_device(usb_device_t *dev) {
if (!dev) return;
// Find the active instance for this device
for (int i = 0; i < instance_count; i++) {
if (active_instances[i].active &&
active_instances[i].device.vendor_id == dev->vendor_id &&
active_instances[i].device.product_id == dev->product_id) {
// Deinitialize the driver
if (active_instances[i].driver->deinit) {
active_instances[i].driver->deinit(dev);
}
@ -98,6 +121,7 @@ void driver_unload_for_device(usb_device_t *dev) {
active_instances[i].driver->loaded = false;
active_instances[i].active = false;
// Shift remaining instances down
for (int j = i; j < instance_count - 1; j++) {
active_instances[j] = active_instances[j + 1];
}
@ -110,16 +134,20 @@ void driver_unload_for_device(usb_device_t *dev) {
}
}
void driver_hotplug_poll(void) {
// Check for newly connected or disconnected USB devices (hotplug)
// Loads drivers for new devices and unloads drivers for removed devices
void driver_check_hotplug(void) {
extern int usb_get_device_count(void);
extern usb_device_t* usb_get_device(int index);
int current_count = usb_get_device_count();
// Load drivers for new devices
for (int i = 0; i < current_count; i++) {
usb_device_t *dev = usb_get_device(i);
if (!dev) continue;
// Check if this device already has a driver loaded
bool found = false;
for (int j = 0; j < instance_count; j++) {
if (active_instances[j].active &&
@ -130,12 +158,14 @@ void driver_hotplug_poll(void) {
}
}
// If no driver loaded, try to load one
if (!found && !dev->initialized) {
driver_load_for_device(dev);
dev->initialized = true;
}
}
// Unload drivers for removed devices
for (int i = 0; i < instance_count; i++) {
bool still_present = false;
for (int j = 0; j < current_count; j++) {
@ -155,7 +185,9 @@ void driver_hotplug_poll(void) {
}
}
void driver_poll_all(void) {
// Poll all active driver instances
// Calls each driver's poll function to check for new data/events
void driver_poll_active_instances(void) {
for (int i = 0; i < instance_count; i++) {
if (active_instances[i].active && active_instances[i].driver->poll) {
active_instances[i].driver->poll(&active_instances[i].device);

View file

@ -31,7 +31,7 @@ int driver_register(usb_driver_t *driver);
int driver_unregister(usb_driver_t *driver);
int driver_load_for_device(usb_device_t *dev);
void driver_unload_for_device(usb_device_t *dev);
void driver_hotplug_poll(void);
void driver_poll_all(void);
void driver_check_hotplug(void);
void driver_poll_active_instances(void);
#endif

View file

@ -6,21 +6,26 @@
#include <string.h>
#include <stdio.h>
// Array of HID mouse devices that have been initialized
static usb_device_t hid_mouse_devices[8];
static int hid_mouse_count = 0;
// Parse a HID report descriptor for a mouse device
// Extracts information like button count, axis ranges, and wheel support
bool hid_parse_mouse_descriptor(const uint8_t *desc, int len, hid_mouse_desc_t *out) {
if (!desc || !out || len < 10) return false;
memset(out, 0, sizeof(hid_mouse_desc_t));
// Parse HID descriptor byte by byte
int i = 0;
while (i < len) {
uint8_t prefix = desc[i++];
uint8_t size = prefix & 0x03;
uint8_t type = (prefix >> 2) & 0x03;
uint8_t tag = prefix >> 4;
uint8_t size = prefix & 0x03; // Data size (0=0, 1=1, 2=2, 3=4 bytes)
uint8_t type = (prefix >> 2) & 0x03; // Item type (0=Main, 1=Global, 2=Local, 3=Reserved)
uint8_t tag = prefix >> 4; // Item tag
// Read the data value based on size
uint32_t value = 0;
if (size == 1) {
value = desc[i++];
@ -40,38 +45,45 @@ bool hid_parse_mouse_descriptor(const uint8_t *desc, int len, hid_mouse_desc_t *
i++;
}
// Global items describe usage pages and usage values
if (type == 1) {
if (tag == 0x04) out->usage_page = value;
if (tag == 0x08) out->usage = value;
if (tag == 0x04) out->usage_page = value; // Usage Page
if (tag == 0x08) out->usage = value; // Usage
}
// Local items describe specific capabilities
if (type == 2) {
if (tag == 0x01 && value == 0x30) out->max_x = 32767;
if (tag == 0x01 && value == 0x31) out->max_y = 32767;
if (tag == 0x09 && value == 0x38) out->has_wheel = true;
if (tag == 0x01 && value == 0x30) out->max_x = 32767; // X-axis usage
if (tag == 0x01 && value == 0x31) out->max_y = 32767; // Y-axis usage
if (tag == 0x09 && value == 0x38) out->has_wheel = true; // Wheel usage
}
}
// Set defaults for standard mouse
out->button_count = 3;
out->usage_page = 0x01;
out->usage = 0x02;
out->usage_page = 0x01; // Generic Desktop
out->usage = 0x02; // Mouse
return true;
}
// Initialize a HID mouse device
// Sets the mouse to boot protocol and configures it for operation
bool hid_init_mouse(usb_device_t *dev) {
if (hid_mouse_count >= 8) return false;
usb_setup_packet_t setup;
// Set the mouse to boot protocol (simplified HID mode)
setup.bmRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
setup.bRequest = USB_REQ_SET_PROTOCOL;
setup.wValue = 0;
setup.wValue = 0; // Boot protocol
setup.wIndex = dev->interface_number;
setup.wLength = 0;
usb_control_transfer(dev, &setup, NULL);
// Set idle rate to 0 (send reports immediately on change)
setup.bmRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
setup.bRequest = USB_REQ_SET_IDLE;
setup.wValue = 0;
@ -87,6 +99,8 @@ bool hid_init_mouse(usb_device_t *dev) {
return true;
}
// Get a mouse input report from the device
// Reads the interrupt endpoint and parses button/movement data
int hid_mouse_get_report(usb_device_t *dev, hid_mouse_report_t *report) {
if (!dev || !report) return -1;
@ -95,6 +109,11 @@ int hid_mouse_get_report(usb_device_t *dev, hid_mouse_report_t *report) {
if (len < 3) return -1;
// Parse standard mouse boot protocol report:
// Byte 0: button states
// Byte 1: X movement (signed)
// Byte 2: Y movement (signed)
// Byte 3: wheel movement (signed, optional)
report->buttons = buffer[0];
report->x = (int8_t)buffer[1];
report->y = (int8_t)buffer[2];
@ -103,17 +122,21 @@ int hid_mouse_get_report(usb_device_t *dev, hid_mouse_report_t *report) {
return len;
}
// Initialize the HID subsystem and scan for HID devices
// Currently only handles mouse devices
void usb_hid_init(void) {
printf("[HID] Initializing HID subsystem\n");
int device_count = usb_get_device_count();
// Scan all USB devices for HID class devices
for (int i = 0; i < device_count; i++) {
usb_device_t *dev = usb_get_device(i);
if (dev && dev->device_class == USB_CLASS_HID) {
printf("[HID] Found HID device\n");
// Check if this is a Logitech B110 mouse and initialize it
if (logitech_b110_probe(dev)) {
printf("[HID] Initializing Logitech B110\n");
logitech_b110_init(dev);

View file

@ -7,11 +7,15 @@
#include <string.h>
#include <stdio.h>
// Track whether the Logitech B110 driver has been initialized
static bool b110_initialized = false;
// Check if a USB device is a Logitech B110 mouse
// Returns true if the device VID:PID matches the B110
bool logitech_b110_probe(usb_device_t *dev) {
if (!dev) return false;
// Check vendor ID and product ID against known B110 values
if (dev->vendor_id == LOGITECH_VID && dev->product_id == LOGITECH_B110_PID) {
return true;
}
@ -19,9 +23,12 @@ bool logitech_b110_probe(usb_device_t *dev) {
return false;
}
// Initialize the Logitech B110 mouse driver
// Sets up the HID mouse for operation
int logitech_b110_init(usb_device_t *dev) {
if (!dev) return -1;
// Use the generic HID mouse initialization
b110_initialized = hid_init_mouse(dev);
if (b110_initialized) {
@ -31,28 +38,36 @@ int logitech_b110_init(usb_device_t *dev) {
return b110_initialized ? 0 : -1;
}
// Deinitialize the Logitech B110 mouse driver
// Marks the driver as unloaded
void logitech_b110_deinit(usb_device_t *dev) {
(void)dev;
b110_initialized = false;
printf("[B110] Mouse USB driver deinitialized\n");
}
// Poll the Logitech B110 mouse for input
// Reads mouse data and sends it to the window manager
int logitech_b110_poll(usb_device_t *dev) {
if (!b110_initialized || !dev) return -1;
uint8_t buffer[8];
// Read interrupt endpoint for mouse data
int len = usb_interrupt_in(dev, dev->endpoint_in, buffer, sizeof(buffer));
if (len > 0) {
hid_mouse_report_t report;
hid_parse_mouse_report(buffer, len, &report);
// Send mouse events to the window manager
wm_handle_mouse(report.x, report.y, report.buttons, report.wheel);
}
return len;
}
// Driver structure for the Logitech B110 mouse
// This is registered with the driver manager
static usb_driver_t logitech_b110_driver = {
.vendor_id = LOGITECH_VID,
.product_id = LOGITECH_B110_PID,
@ -64,6 +79,8 @@ static usb_driver_t logitech_b110_driver = {
.loaded = false
};
// Get a pointer to the Logitech B110 driver structure
// Used to register the driver with the driver manager
usb_driver_t* logitech_b110_get_driver(void) {
return &logitech_b110_driver;
}

View file

@ -7,8 +7,13 @@
#include <stdio.h>
#include <stdlib.h>
// UHCI controller state
static uhci_hc_t uhci_controller;
// Frame list pointer for UHCI scheduling (1024 entries)
static uint8_t *frame_list;
// Pools for queue heads and transfer descriptors (reused for transfers)
static uhci_qh_t *qh_pool;
static uhci_td_t *td_pool;
static int td_idx = 0;
@ -16,9 +21,12 @@ static int td_idx = 0;
#define FRAME_LIST_SIZE 1024
#define POOL_SIZE 64
// Initialize the UHCI host controller
// Sets up memory-mapped I/O, frame list, and enables the controller
bool uhci_init(usb_hc_t *hc) {
serial_write("[UHCI] Initializing UHCI controller\n");
// Map the PCI BAR to virtual address space
uint32_t bar = hc->bar0 & 0xFFFFFFF0;
if (bar == 0) {
serial_write("[UHCI] Invalid BAR0\n");
@ -28,23 +36,30 @@ bool uhci_init(usb_hc_t *hc) {
uhci_controller.base = (uint8_t *)p2v(bar);
uhci_controller.regs = (uhci_regs_t *)uhci_controller.base;
// Stop the controller by clearing the RUN bit
uint16_t cmd = inw((uint32_t)&uhci_controller.regs->cmd);
outw((uint32_t)&uhci_controller.regs->cmd, cmd & ~UHCI_CMD_RUN);
// Wait for controller to stop
for (volatile int i = 0; i < 10000; i++);
// Reset the controller
outw((uint32_t)&uhci_controller.regs->cmd, cmd | UHCI_CMD_HCRESET);
// Wait for reset to complete
for (volatile int i = 0; i < 10000; i++);
// Allocate the frame list (1024 * 4 bytes for physical addresses)
frame_list = (uint8_t *)malloc(FRAME_LIST_SIZE * 4);
if (!frame_list) {
printf("[UHCI] Failed to allocate frame list\n");
return false;
}
// Initialize frame list with terminate markers (0x01 = T-bit set)
memset(frame_list, 0, FRAME_LIST_SIZE * 4);
// Allocate pools for queue heads and transfer descriptors
qh_pool = (uhci_qh_t *)malloc(sizeof(uhci_qh_t) * POOL_SIZE);
td_pool = (uhci_td_t *)malloc(sizeof(uhci_td_t) * POOL_SIZE);
@ -56,6 +71,7 @@ bool uhci_init(usb_hc_t *hc) {
memset(qh_pool, 0, sizeof(uhci_qh_t) * POOL_SIZE);
memset(td_pool, 0, sizeof(uhci_td_t) * POOL_SIZE);
// Set all frame list entries to point to terminate (no transfers scheduled)
for (int i = 0; i < FRAME_LIST_SIZE; i++) {
frame_list[i * 4 + 0] = 0x01;
frame_list[i * 4 + 1] = 0x00;
@ -63,11 +79,14 @@ bool uhci_init(usb_hc_t *hc) {
frame_list[i * 4 + 3] = 0x00;
}
// Tell the controller where the frame list is in physical memory
uint32_t frame_list_phys = p2v((uint64_t)frame_list);
outl((uint32_t)&uhci_controller.regs->frame_base, frame_list_phys);
// Start the controller with max packet size (64 bytes)
outw((uint32_t)&uhci_controller.regs->cmd, UHCI_CMD_RUN | UHCI_CMD_MAXP);
// Check if devices are connected to ports and enable them
uint16_t port1 = inw((uint32_t)&uhci_controller.regs->port1);
if (port1 & UHCI_PORT_CCS) {
printf("[UHCI] Device detected on port 1\n");
@ -85,33 +104,44 @@ bool uhci_init(usb_hc_t *hc) {
return true;
}
// Reset a specific USB port on the UHCI controller
// Used when a device is misbehaving or needs re-initialization
void uhci_reset_port(usb_hc_t *hc, int port) {
(void)hc;
uhci_hc_t *controller = &uhci_controller;
uint16_t port_reg = (port == 1) ? (uint32_t)&controller->regs->port1 : (uint32_t)&controller->regs->port2;
// Assert reset signal
uint16_t status = inw(port_reg);
outw(port_reg, status | UHCI_PORT_PR);
// Wait for reset to take effect
for (volatile int i = 0; i < 10000; i++);
// De-assert reset signal
status = inw(port_reg);
outw(port_reg, status & ~UHCI_PORT_PR);
// Wait for device to recover
for (volatile int i = 0; i < 10000; i++);
// Enable the port
status = inw(port_reg);
outw(port_reg, status | UHCI_PORT_PED);
}
// Perform a USB control transfer using UHCI
// Sets up TDs (Transfer Descriptors) and QH (Queue Head) for the transfer
int uhci_control_transfer(usb_hc_t *hc, usb_setup_packet_t *setup, void *data, uint16_t len) {
(void)hc;
if (!uhci_controller.initialized) return -1;
// Allocate TDs from the pool (setup, optional data, and status)
uhci_td_t *td_setup = &td_pool[td_idx++ % POOL_SIZE];
uhci_td_t *td_data = (len > 0) ? &td_pool[td_idx++ % POOL_SIZE] : NULL;
uhci_td_t *td_status = &td_pool[td_idx++ % POOL_SIZE];
// Copy setup packet into a buffer
uint8_t *setup_buf = (uint8_t *)kmalloc(8);
if (!setup_buf) return -1;
@ -124,11 +154,13 @@ int uhci_control_transfer(usb_hc_t *hc, usb_setup_packet_t *setup, void *data, u
setup_buf[6] = setup->wLength & 0xFF;
setup_buf[7] = (setup->wLength >> 8) & 0xFF;
// Setup TD: sends the 8-byte setup packet
memset(td_setup, 0, sizeof(uhci_td_t));
td_setup->link = (td_data) ? (p2v((uint64_t)td_data) | 0x4) : (p2v((uint64_t)td_status) | 0x4);
td_setup->status = 0x00800000 | ((7 & 0x7FF) << 21);
td_setup->buffer = p2v((uint64_t)setup_buf);
// Data TD: optional data phase (if len > 0)
if (td_data && len > 0) {
memset(td_data, 0, sizeof(uhci_td_t));
td_data->link = p2v((uint64_t)td_status) | 0x4;
@ -136,32 +168,39 @@ int uhci_control_transfer(usb_hc_t *hc, usb_setup_packet_t *setup, void *data, u
td_data->buffer = p2v((uint64_t)data);
}
// Status TD: marks end of transfer
memset(td_status, 0, sizeof(uhci_td_t));
td_status->link = 0x00000001;
td_status->status = 0x00800000;
// Create Queue Head and link it to the first TD
uhci_qh_t *qh = &qh_pool[0];
memset(qh, 0, sizeof(uhci_qh_t));
qh->element_link = p2v((uint64_t)td_setup) | 0x4;
qh->link = 0x00000001;
// Insert QH into frame list at index 0
uint32_t qh_phys = p2v((uint64_t)qh);
frame_list[0] = qh_phys & 0xFF;
frame_list[1] = (qh_phys >> 8) & 0xFF;
frame_list[2] = (qh_phys >> 16) & 0xFF;
frame_list[3] = (qh_phys >> 24) & 0xFF;
// Trigger the transfer by setting max packet bit
outw((uint32_t)&uhci_controller.regs->cmd, inw((uint32_t)&uhci_controller.regs->cmd) | UHCI_CMD_MAXP);
// Wait for transfer to complete (polling)
for (volatile int i = 0; i < 100000; i++);
uint32_t status = td_status->status;
// Clear frame list entry
frame_list[0] = 0x01;
frame_list[1] = 0x00;
frame_list[2] = 0x00;
frame_list[3] = 0x00;
// Check if transfer completed successfully (bit 31 set = active)
if (status & 0x80000000) {
free(setup_buf);
return len;
@ -171,40 +210,50 @@ int uhci_control_transfer(usb_hc_t *hc, usb_setup_packet_t *setup, void *data, u
return -1;
}
// Perform an interrupt IN transfer using UHCI
// Used for periodic data like mouse movements or keyboard input
int uhci_interrupt_in(usb_hc_t *hc, uint8_t endpoint, void *data, uint16_t len) {
(void)hc;
(void)endpoint;
if (!uhci_controller.initialized) return -1;
// Allocate a single TD for the interrupt transfer
uhci_td_t *td = &td_pool[td_idx++ % POOL_SIZE];
// Configure TD for interrupt transfer
memset(td, 0, sizeof(uhci_td_t));
td->link = 0x00000001;
td->status = 0x00800000 | ((len & 0x7FF) << 21) | (0x69 << 16);
td->link = 0x00000001; // Terminate link
td->status = 0x00800000 | ((len & 0x7FF) << 21) | (0x69 << 16); // 0x69 = interrupt endpoint
td->buffer = p2v((uint64_t)data);
// Create QH for this transfer
uhci_qh_t *qh = &qh_pool[1];
memset(qh, 0, sizeof(uhci_qh_t));
qh->element_link = p2v((uint64_t)td) | 0x4;
qh->link = 0x00000001;
// Insert into frame list at index 0
uint32_t qh_phys = p2v((uint64_t)qh);
frame_list[0] = qh_phys & 0xFF;
frame_list[1] = (qh_phys >> 8) & 0xFF;
frame_list[2] = (qh_phys >> 16) & 0xFF;
frame_list[3] = (qh_phys >> 24) & 0xFF;
// Trigger the transfer
outw((uint32_t)&uhci_controller.regs->cmd, inw((uint32_t)&uhci_controller.regs->cmd) | UHCI_CMD_MAXP);
// Wait for completion (shorter timeout for interrupt transfers)
for (volatile int i = 0; i < 50000; i++);
uint32_t status = td->status;
// Clear frame list entry
frame_list[0] = 0x01;
frame_list[1] = 0x00;
frame_list[2] = 0x00;
frame_list[3] = 0x00;
// Check if transfer completed successfully
if (status & 0x80000000) {
return len;
}

View file

@ -8,16 +8,26 @@
#include <stdio.h>
#include <stdlib.h>
// Array of USB host controllers (max 8 supported)
static usb_hc_t usb_controllers[8];
static int usb_controller_count = 0;
// Array of detected USB devices
static usb_device_t usb_devices[USB_MAX_DEVICES];
static int usb_device_count = 0;
// Counter for assigning unique addresses to USB devices
static uint8_t device_address_counter = 1;
// Detect the type of USB host controller by reading PCI configuration space
// Returns the controller type (UHCI/OHCI/EHCI/XHCI) or UNKNOWN if not recognized
usb_hc_type_t usb_detect_controller(usb_hc_t *hc) {
// Read class code from PCI config register 0x08 (byte 3)
uint32_t class_code = pci_read_config(hc->bus, hc->device, hc->function, 0x08) >> 24;
// Read programming interface from PCI config register 0x09
uint32_t prog_if = pci_read_config(hc->bus, hc->device, hc->function, 0x09) & 0xFF;
// Class code 0x0C indicates a USB controller
if (class_code == 0x0C) {
if (prog_if == 0x00) return USB_HC_UHCI;
if (prog_if == 0x10) return USB_HC_OHCI;
@ -28,23 +38,29 @@ usb_hc_type_t usb_detect_controller(usb_hc_t *hc) {
return USB_HC_UNKNOWN;
}
// Initialize the USB stack by scanning PCI bus for USB host controllers
// Currently only UHCI is supported for initialization
void usb_init(void) {
printf("[USB] Initializing USB stack...\n");
int idx = 0;
// Scan entire PCI bus space for USB controllers
for (int bus = 0; bus < 256; bus++) {
for (int device = 0; device < 32; device++) {
for (int function = 0; function < 8; function++) {
// Check if device exists by reading vendor ID
uint32_t vendor_id = pci_read_config(bus, device, function, 0x00) & 0xFFFF;
if (vendor_id == 0xFFFF) continue;
uint32_t class_code = pci_read_config(bus, device, function, 0x08) >> 24;
// If this is a USB controller, save its info
if (class_code == 0x0C && idx < 8) {
usb_controllers[idx].bus = bus;
usb_controllers[idx].device = device;
usb_controllers[idx].function = function;
// Read base address registers (BARs) for memory-mapped I/O
usb_controllers[idx].bar0 = pci_read_config(bus, device, function, 0x10);
usb_controllers[idx].bar1 = pci_read_config(bus, device, function, 0x14);
usb_controllers[idx].bar2 = pci_read_config(bus, device, function, 0x18);
@ -60,6 +76,7 @@ void usb_init(void) {
printf("[USB] Found controller: %s\n", type_str);
// Initialize the controller if we support it
if (type == USB_HC_UHCI) {
if (uhci_init(&usb_controllers[idx])) {
printf("[USB] UHCI initialized\n");
@ -76,12 +93,15 @@ void usb_init(void) {
printf("[USB] Total controllers found: %d\n", usb_controller_count);
}
// Enumerate a USB device by reading its device descriptor and assigning it an address
// Returns true if enumeration succeeded, false otherwise
bool usb_enumerate_device(usb_device_t *dev) {
if (usb_device_count >= USB_MAX_DEVICES) return false;
usb_setup_packet_t setup;
uint8_t desc[18];
uint8_t desc[18]; // Device descriptor is 18 bytes
// Request the device descriptor to identify the device
setup.bmRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
setup.bRequest = USB_REQ_GET_DESCRIPTOR;
setup.wValue = (USB_DESC_DEVICE << 8) | 0;
@ -92,6 +112,7 @@ bool usb_enumerate_device(usb_device_t *dev) {
return false;
}
// Parse the device descriptor
dev->vendor_id = desc[8] | (desc[9] << 8);
dev->product_id = desc[10] | (desc[11] << 8);
dev->device_class = desc[4];
@ -99,6 +120,7 @@ bool usb_enumerate_device(usb_device_t *dev) {
dev->device_protocol = desc[6];
dev->max_packet_size = desc[7];
// Assign a unique USB address to the device
setup.bmRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
setup.bRequest = USB_REQ_SET_ADDRESS;
setup.wValue = device_address_counter;
@ -109,10 +131,11 @@ bool usb_enumerate_device(usb_device_t *dev) {
return false;
}
// Set default configuration values (simplified for basic devices)
dev->config_value = 1;
dev->interface_number = 0;
dev->endpoint_in = 0x81;
dev->endpoint_out = 0x01;
dev->endpoint_in = 0x81; // Interrupt IN endpoint
dev->endpoint_out = 0x01; // Interrupt OUT endpoint
usb_devices[usb_device_count++] = *dev;
device_address_counter++;
@ -120,6 +143,8 @@ bool usb_enumerate_device(usb_device_t *dev) {
return true;
}
// Perform a USB control transfer
// Currently assumes only one controller is present
int usb_control_transfer(usb_device_t *dev, usb_setup_packet_t *setup, void *data) {
(void)dev;
if (usb_controller_count == 0) return -1;
@ -130,6 +155,7 @@ int usb_control_transfer(usb_device_t *dev, usb_setup_packet_t *setup, void *dat
return uhci_control_transfer(hc, setup, data, len);
}
// Perform a USB interrupt IN transfer (used for things like mouse/keyboard data)
int usb_interrupt_in(usb_device_t *dev, uint8_t endpoint, void *data, uint16_t len) {
(void)dev;
if (usb_controller_count == 0) return -1;
@ -139,6 +165,8 @@ int usb_interrupt_in(usb_device_t *dev, uint8_t endpoint, void *data, uint16_t l
return uhci_interrupt_in(hc, endpoint, data, len);
}
// Enumerate devices on all USB controller ports
// Currently checks 2 ports per controller
void usb_enumerate_devices(void) {
printf("[USB] Starting device enumeration\n");
@ -147,6 +175,7 @@ void usb_enumerate_devices(void) {
return;
}
// Try to enumerate devices on ports 1 and 2
for (int port = 1; port <= 2; port++) {
usb_device_t dev;
memset(&dev, 0, sizeof(usb_device_t));
@ -157,10 +186,12 @@ void usb_enumerate_devices(void) {
}
}
// Get the total number of enumerated USB devices
int usb_get_device_count(void) {
return usb_device_count;
}
// Get a pointer to a USB device by its index
usb_device_t* usb_get_device(int index) {
if (index < 0 || index >= usb_device_count) return NULL;
return &usb_devices[index];