mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
Added more documentation + USBSystem.md in docs\appdev
This commit is contained in:
parent
8e805938a1
commit
907824ebda
9 changed files with 552 additions and 26 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -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/
|
||||
|
|
|
|||
7
Makefile
7
Makefile
|
|
@ -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
377
docs/appdev/USBsystem.md
Normal 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.
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
Loading…
Reference in a new issue