Skip to content

Commit

Permalink
*** empty log message ***
Browse files Browse the repository at this point in the history
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11251 a95241bf-73f2-0310-859d-f6bbb57e9c96
  • Loading branch information
nielx committed Feb 4, 2005
1 parent 6c1ebb0 commit 6256385
Showing 1 changed file with 12 additions and 76 deletions.
88 changes: 12 additions & 76 deletions docs/develop/kernel/USB_stack_design
Original file line number Diff line number Diff line change
@@ -1,82 +1,18 @@
THE USB STACK
==========
The USB stack will follow the same design as the BeOS R5 stack. This implies that:
- There's a bus manager called the 'usb' manager - this file also contains the usb module
- This usb module will control other modules that implement the communication with the host controller
- These modules are the UHCI, EHCI and OHCI modules
- There will be support for RAW usb device handling in later stages of development
- The public API (for drivers) will be compatible with the API of BeOS R5
INSIDE THE USB STACK
==============

THE CORE OF THE MATTER: THE USB BUS MODULE
==============================
I'm a little confused whether or not to make the internals of the USB stack in C++, but eventually
I decided to do that, because it makes the whole thing clearer. This does mean that there still needs
to be some C-glue between the modules of the stack, because the OBOS kernel doesn't support
dynamic libraries. The core data of the USB stack is stored in a class called 'Stack'. On init of the
stack one global variable is created with the name 'data'.
The data struct is protected by one master lock called 'master'. This lock should be gained by
every thread that reads or writes in the 'data' struct.
The USB stack will support multiple host controllers. The USB specification says this is enough, however, there will possibly systems with more controllers. Between the usb core and the host controller driver (essentially another module), is a private api (that will be documented for future additions of host controllers).
For all of you who have been over my code and thought: "What is this guy doing?", I can firmly respond that I sometimes haven't a clue either. But when I'm actually reviewing my own code, I occassionally get the feeling that I do have a direction I'm persueing. In order to define this direction more, and share you in the fun, I have written this document. It's a rather superficial account (without actually quoting any code), but it should be enough to get you started.

THE INITIALISATION PHASE
As soon as a device driver calls the usb module, and the module isn't loaded before, a single instance
of the stack class is made. The constructor initialises the object, and it starts the search for host
controllers. All the host controller modules are called, and if there is no hardware present, or the
hardware corresponding to that host controller is malfunctioning, the controller returns B_ERROR. If
there is no controller that works, the USB module will fail to initialise.
If a controller succeeds in initialising the hardware, and the module initialisation succeeds, the
USB Stack class will create an instance of the BusManager class: the class that manages a bus. This
class will continue to control the host controller and it will do the hw_start() method that it provided
via de module struct. The host controller will do the proper initialisation.
First of all, it's always good to know that the USB stack is modularised. This means that there is a backend - a module - which connects to a whole range of modules that are in touch with the host controllers (the actual USB hardware). The combination of these modules is what we call the USB stack. The job of the backend (in CVS at /current/src/add-ons/kernel/bus_managers/usb) is to perform generic operations like managing device connects and disconnects, and pushing commands from drivers to the host-controller modules. The host controller modules are in direct contact with the hardware, meaning that they translate actual commands into something the host controller can work with and can transmit over the wires. Additionally, these host controller modules simulate the root hub: they provide a seamless interface for the backend module to communicate with the root hub.

BASIC OPERATION
After all host controllers have been registered and prepared to go, the BusManager will spawn
a thread that will keep exploring the device tree. The thread called 'usb_main_thread' will continually
pull all devices and hubs in order to see if there are any changes to the device tree, and, if so, will
communicate this to the device tree.
In the first experiments of my USB stack, there was a special 'C' interface for the host controller modules. This interface was clumsy, because all the objects (the backend is in C++) were constantly harvesting data all over the place because they couldn't simply send references of themselves. The second attempt of the USB stack introduces a libusb.a (the kernel doesn't support shared libraries), which contains the basic structure of the stack, and which is linked in by both the backend and the host controller modules. Instead of having a host_controller_module struct with function pointers, the host controller modules now simply returned an extended 'class Busmanager'. The downside is that whenever the libusb.a is changed, all the host controller modules have to be relinked in order to avoid disasters. (This is also something which prevents me from providing a nice C++ interface for drivers). However, this limitation is much easier to work around than the separate C-interface.

DEVICES AND HOW THEY'RE MANAGED
The Busmanager keeps an internal tree of all the devices. A single device is represented by the
Device class. This class is inherited by the Hub class. Hubs are also devices,
but they can contain children and therefore are a special case. The root hub is represented as a
Hub too, but the host controller itself will decide on how to handle messages sent to it.
The basic structure of the stack (and of libusb.a) can be found in the usb_p.h file in the CVS location of the backend. You'll notice a few objects which I'll quickly go over. First of all, there's stack, which is basically a container for the individual Bus managers. The Busmanager class encapsulates a bus manager. The Device class represents a connected device, and the Hub class (inherits Device), manages the communication with the hubs. For the area of communication there are a few classes that are interesting. The Pipe class represents a data pipe. A 'pipe' is more an interface than something that is actually interesting during USB communication. A pipe encapsulates the properties regarding what kind of data is transmitted to where. Each Pipe type (corresponding to the four types of transfer), will be a subclass of Pipe. The objects of Pipe will all be associated with a device. Finally there's the Transfer class. The objects of this class are created by the Pipes, and contain the actual data for the transfers (including references to the callbacks).

THE UHCI CONTROLLER
==============
BASIC THEORY OF OPERATION
From: FreeBSD developers handbook
Written by Nick Hibma,. © 2000, 2001, 2002, 2003 by The FreeBSD Documentation Project
"The UHCI host controller maintains a framelist with 1024 pointers to per frame data structures.
It understands two different data types: transfer descriptors (TD) and queue heads (QH). Each TD
represents a packet to be communicated to or from a device endpoint. QHs are a means to
groupTDs (and QHs) together. Each transfer consists of one or more packets. The UHCI driver splits
large transfers into multiple packets. For every transfer, apart from isochronous transfers, a QH is
allocated. For every type of transfer these QHs are collected at a QH for that type. Isochronous
transfers have to be executed first because of the fixed latency requirement and are directly
referred to by the pointer in the framelist. The last isochronous TD refers to the QH for interrupt
transfers for that frame. All QHs for interrupt transfers point at the QH for control transfers, which in
turn points at the QH for bulk transfers.
This results in the following schedule being run in each frame. After fetching the pointer for the
current frame from the framelist the controller first executes the TDs for all the isochronous packets
in that frame. The last of these TDs refers to the QH for the interrupt transfers for thatframe. The
host controller will then descend from that QH to the QHs for the individual interrupt transfers. After
finishing that queue, the QH for the interrupt transfers will refer the controller to the QH for all control
transfers. It will execute all the subqueues scheduled there, followed by all the transfers queued at the
bulk QH. To facilitate the handling of finished or failed transfers different types of interrupts are
generated by the hardware at the end of each frame. In the last TD for a transfer the Interrupt-On
Completion bit is set by the HC driver to flag an interrupt when the transfer has completed. An error
interrupt is flagged if a TD reaches its maximum error count. If the short packet detect bit is set in a
TD and less than the set packet length is transferred this interrupt is flagged to notify the controller
driver of the completed transfer. It is the host controller driver's task to find out which transfer has
completed or produced an error. When called the interrupt service routine will locate all the finished
transfers and call their callbacks."
All these classes would be useless, if they weren't interacting with each other. The basic idea of the stack is the following. As soon as the USB bus_manager module is opened by a driver (for the first time), a Stack object is initialized. This object starts looking for host controller modules. Every host controller tries to find and initialize their cards, and if they succeed, they add a BusManager object to the stack. This bus manager object contains an object of the hub class, and the default pipes. The generic backend implementation starts a usb_explore thread, which will poll the status of the hubs and keep the internal administration up to date. As soon as a hub signals that a new device is connected, the Busmanager starts a procedure to connect the device. If it's a hub, a Hub is created, else a Device is created. This device object creates the default pipe, and contains all the data related to it.

New transfers are initialized by Pipes. The Transfer objects will be created by the Pipe subclasses only, and the ownership of the objects will go to the Busmanager that has to transmit them (that will take care of deletion). The SubmitTransfer function of the Busmanagers are the gate to the actual transfer. What happens is host controller dependent. The implementation of the host controller should take care of finishing a transfer.

As for closing, this design lacks some objects that correspond to USB objects. For example, there are no special Endpoint objects. At the moment, passing adresses is sufficient, but perhaps it should change in order to complete the design. Also, there is currently no idea how locking should work. Finally, the Stack class currently includes some memory management for usb, however, checking from the specs, only UHCI requires manual scheduling, OHCI and EHCI manage the scheduling and packaging itself. That should move. But at the moment, this design works.

INITIALISATION
- Reset the host controller (USBCMD)
- Turn on all interrupts( USBINTR)
- Start at frame 0 (FRBNUM)
- Set frame base pointer (FLBASEDDR)
- Start the host controller (USBCMD)
Also, several data structures need to be made:
- Frame list, one page (4096), contains 1024 frame list pointers.
- TODO: what else???
Finally, this document gives an idea of how it should work. For details: check the USB specs, host controller specs and the code. If anything stays unclear, please comment on my weblog, or e-mail me.

0 comments on commit 6256385

Please sign in to comment.