GSoC xHCI: Final Submission

by ramateja.g | August 25, 2017

Links:

Link to my work: https://svn.reactos.org/svn/reactos/branches/GSoC_2017/usbxhci/reactos/drivers/usb/usbxhci/

Link to check out using svn: https://svn.reactos.org/reactos/branches/GSoC_2017/usbxhci/reactos/

All the commits log: https://svn.reactos.org/svn/reactos/branches/GSoC_2017/usbxhci/?view=log

https://code.reactos.org/changelog/~br=GSoC_2017/reactos/branches/GSoC_2017/usbxhci?max=30&view=fe

Introduction:

My aim is to develop driver for xHCI which is compatible with ReactOS. For the development I’ve used Windows 2003 server edition running on Vmware Workstation. The first link points to the folder in which all main code and header files are present. To view any file, click on it and in the log; against any revision, click on view/download to see the code. To get and compile my work please follow the instructions given in https://reactos.org/wiki/Building_ReactOS. Check out usbxhci branch from the link given above. To build the whole operating system use the commands given in the above link. To build only the xHCI driver use the command “ninja usbxhci” in the output folder(similar to “ninja livecd” example in the wiki).

What is xHCI:

Universal Serial Bus(USB) has 5 different modes of data transfer which are segregated based on the transfer speed, starting at low speed (USB 1.0) up to SuperSpeed+ (USB 3.1). xHCI stands for extensible host controller interface. Host Controller(HC) is the hardware that connects network devices or storage devices to the computer. A host controller interface (HCI) is a register-level interface that enables a HC for USB hardware to communicate with a HC driver in software. xHCI is a computer interface specification which is capable of interfacing with USB 1.x, 2.0 and 3.x compatible devices. The xHCI architecture was designed to support all USB speeds, including SuperSpeed (5 Gbit/s) and future speeds, under a single driver stack.

Major Components:

eXtenstible Host Controller Interface specification can be found on Intel website.

Image

The above figure shows xHCI general architecture. xHCI gives the register interface by which we can communicate with the hardware.

In figure there are 3 blocks PCI config space MMIO space and Memory Address Space. In general hardware is connected to PCI which is standardised form of processor bus; it connects to processor and memory. So from software point of view our first contact with the HC will be through PCI bus. Communication with PCI is done by its respective driver.  PCI driver provides the base address as can be seen in the image.

MMIO space is the register space of the controller. Register space in xHCI is divided into 5 segments. Capability registers are at direct offset from the base address. Operational registers are at offsets from operational base which can be calculated from the information given in the documentation. Similarly other bases like runtime base, doorbell base and extended capabilities bases can be calculated. All of these are the hardware component accessible by the software.

Memory address space is the space allocated for the xHCI driver in the Memory (RAM). When the driver is loaded it is allocated some memory which can be used as required. In the memory address space crucial components are Event ring, Command ring, Device contexts and Transfer rings.  Other components are there as support to these i.e., event ring segment table is used to store the locations of the event ring for the hardware to find them. Similarly device context base address array has the addresses to different device contexts.

Rings are circular data structures used by the hardware and software to communicate with each other. Event rings are used by the hardware to notify about different events like device attach, command completion etc., Command ring is used by the driver to send commands to the Controller.  Transfer rings are used to transfer data between computer and the connected devices.

Work Done:

Link to blogs:  https://reactos.org/blog/49141

USB stack

Above diagram shows the USB driver stack in Windows. After deliberation we’ve decided to take the existing usbport driver in ReactOS and implement xHCI driver below it. Due to this all the functions needed to communicate with usbport remained same as in ehci.

After stable empty driver was created then I started working on the functions. Starting the controller was the first objective. This included many steps like initializing registers with required values. Starting the controller broke down into two parts, initializing hardware and initializing resources. Resources are the data structures in the Memory address space.

In the next phase I worked on generating interrupts. Interrupt mechanism is very different for xHCI compared to older versions, this is detailed in one of my blog posts. It supports MSI interrupt mechanism but the PCI driver present in ReactOS didn’t support that. So I had to go with the traditional interrupt generation mechanism. After some initial implementation interrupts were being generated but after a single interrupt the driver stopped accepting further interrupts.  

To get the driver to accept multiple interrupts many other functions were needed. First to test the interrupt mechanism I’ve sent a Command to check if the controller was processing it and generating interrupt. Here I was stuck for some time as the code seemed to be right but it wasn’t working. Upon debugging we’ve found a typecast issue in the runtime register base address calculation that caused the problem. Once that was cleared up I worked on various functions like GetPortStatus which are required for interrupt processing.

Next major hurdle was continuous call of some functions which we initially thought to be some bug. Later it turned out to be a regular polling mechanism. After that got cleared up and some code optimisation/debugging it started generating interrupts on every connect/ disconnect of the pendrive. In windows 2003 server edition we can see unknown device attached when we connect a pendrive.

Image

In the pre-final week I’ve looked into USB device initialization. This required sending various commands to the controller. Rudimentary implementation of the command and event ring was present from the start. It needed to be refined. So I’ve worked on functions to send commands and receive events and also account for looping of the ring. I’ve tested the rings till they looped and everything was functioning normally.

As a part of final submission I’ve cleaned up the code. Removed commented out code and edited the code to follow reactOS coding style also added licence headers. Upon merging with the trunk there were a few issues that were handled. I’ve modified the stop controller so that the controller can be safely disabled and enabled in the device manager.

Summary:

Driver is now able to successfully start the controller. It generates Port status change events for every disconnect or connect and also generates events on command completion. It generates interrupt if there is any event. It handles any spurious interrupts. It can send commands to the controller and successful completion of valid commands is tested. Both command ring and event ring are capable of handling ring full situations and also ring loop back scenario. Controller can be disabled which successfully stops the controller and re-enabled which essentially resets the driver.  

Future work:

Next logical step will be to complete USB device initialization. This involves assigning device slot, getting the address, setting up control endpoint etc., Data structures required to do assign device slot and functions to get the address are ready. Once the device slot is assigned the first operation that driver performs on the USB device is to set an address. This is done using the command ring. After that it moves forward to Device Configuration phase for which we need control endpoint. Hence first QueryEndpointRequirements function should be dealt with. After that OpenEndpoint function should be written.  

As the control transfer is done using ring mechanism and code for command/event ring is working we can just port it to make transfer ring function. After all the required functions are ready we can initialise the USB device and get its description using control transfer.  There are other minor tasks that should be worked on later like all the ring structures can actually be expanded as per the specification but for now we’ve decided to make them static. That can be altered later without affecting the main code as the structures are set up for it.  All of these are short term future work.

In the long run a lot more needs to be implemented. All the transfer types i.e., Control transfer, Bulk Transfer, Isoch transfer etc., Also functions to handle slot management and to handle older usb types should be worked on.