USB learning: the difference between uhci ohci and ehci

USB learning: the difference between uhci ohci and ehci

Please indicate the source of the reprint Weilin blog: http://www.wangweilin.name/qrx_452.html

1. Uhci ohci ehci They are all host controller specifications.
OHCI is mainly USB chips on non-PC systems and PC motherboards with SiShe ALi chipsets.
UHCI is mostly USB controller chips on Intel and Via motherboards. The hardware circuit of UHCI is simpler than OHCI, the cost is lower, but the drive is more complicated. But they are all specified by USB1.1.
EHCI is developed by several manufacturers such as Intel and is compatible with OHCI. UHCI follows the USB2.0 specification.
USB specifications are stipulated from the register level, but each manufacturer may have several dedicated registers of its own.


2.
uhci (universal host controller interface): A hardware example of the usb 1.1 host controller used by Intel on its own chipset.
ehci (enhanced host controller interface): USB 2.0 main controller standard interface.
ohci (open host controller inferface): A host controller interface standard not only used by usb, which is subdivided into usb, 1394, or more (nothing else) Mainly follow the csr (configuration space register) standard (another standard, ha ha). It is the standard that other manufacturers follow when designing the usb host controller, such as via, nec, ali, including nvidia and so on.
Uhci and ohci are different in hardware implementation and access to the underlying software, but both completely (in fact, they are somewhat insufficient) support the requirements of the usb host controller in the usb 1.1 specification.
Similarly, ehci is a hardware design that meets the requirements of the usb host controller (high speed) in the usb 2.0 specification.

It should be that usb1.1 has been widely supported since win98, whether it is uhci or ohci. But ms really supports usb2.0 (or ehci) from win2k sp4 and winxp sp1. The real support mentioned here means that the system comes with ehci driver without the need for third-party drivers.

Apple’s arms are now awkward but not thighs, and it has begun to support usb1.1 and 2.0 interfaces on the mac. And the host controller above must be the ohci standard.

USB learning two: from the USB device plug in to the drive probe call process analysis

http://blog.csdn.net/aaronychen/archive/2008/03/17/2192147.aspx

This article will describe in detail how a USB device under 2.6.22 is plugged into the PC of the Linux system and is transferred step by step to the probe function of our USB device driver. We know that one of the parameters in the probe function of our USB driver is interface. Structure, so in general, any interface in a USB device should have a corresponding driver, of course there are exceptions (such as cdc-acm).

/*driver/usb/core/hub.c*/:usb_hub_init()-->hub_thread()-->hub_events()-->hub_port_connect_change()

We know that USB devices are connected to the system by plugging into a port of the upper HUB and then discovered by the system. When the USB device is inserted into a HUB, the status of that HUB port will change, so that the system will know the change , Hub_port_connect_change()/*driver/usb/core/hub.c*/will be called at this time

static void hub_connect_change(struct usb_hub *hub, int portl, u16 portstatus, u16 portchange)

{

....

usb_new_device(udev);

}

This function creates a usb_device object udev, initializes it, and then calls usb_new_device() to obtain various descriptors of the usb device and find the right driver for each interface.

int usb_new_device(struct usb_device *udev)

{

....

err = usb_get_configuration(udev);

....

device_add(&udev->dev);

}

This function first calls usb_get_configuration() to obtain various descriptors of the device (device descriptor, configuration descriptor, etc.), and then calls device_add() to add the USB device to the USB system, that is, the system Go back and find the corresponding driver for this device. In some early versions of 2.6, after analyzing the configuration descriptor, the interface was obtained while using the interface as a device to call device_add()

int device_add(struct device *dev)

{

....

if((error = bus_add_device(dev)))

bus_attach_device(dev);

}

This function is a general device management function, it will call bus_add_device for each device to add the device to the device list of the corresponding bus. Then call bus_attach_device() to match the corresponding driver, the first for USB devices The parameter dev when calling bus_attach_device() this time represents the entire usb device (the interface in the usb device will also call this function as a device in the future).

int bus_attach_device(struct device *dev)

{

ret = device_attach(dev);

}

This function is used to find the corresponding device driver for the device (implemented by calling device_attach()).

int device_attach(struct device *dev)

{

ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);

}

This function calls bus_for_each_drv() to find the matching driver from all the drivers registered on the bus.

int bus_for_each_drv(struct bus_type *bus,

struct device_driver *start,

void *data,

int (*fn)(struct device_driver *, void *))

{

       ....

while((drv = next_driver(&i)) && !error)

error = fn(drv, data);//return 0 to continue the search, return an error value to stop the search.

}

This function traverses all drivers on the bus and calls fn() for each driver to see if it matches. The fn here is __device_attach.

static int __device_attach(struct device_driver *drv, void *data)

{

struct device *dev = data;

return driver_probe_device(drv, dev);

}

int driver_probe_device(struct device *drv, struct device *dev)

{

   …

   if(drv->bus->match && !drv->bus_match(dev, drv))

   …

 

   ret = really_probe(dev, drv);

}

   For usb drivers, we register our driver through usb_registe()r, this function will specify the bus in our driver object (usb_driver) as usb_bus_type:
//bus_register(&usb_bus_type)---drivers/usb/core/usb.c
//usb_bus_type----drivers/usb/core/driver.c

   Struct bus_type usb_bus_type = {

   …

   .match = usb_device_match,

   ....

}

Therefore, for the usb driver, usb_device_match() will be called first.

static int usb_device_match(struct device *dev, struct device_driver *drv)

{

     if(is_usb_device(dev)) {/*dev represents the entire usb device*/

          ....

}

else/*dev represents a usb device interface*/

{

   …

   usb_match_id();

   …

   usb_match_dynamic_id();

   …

}

}

This function just does some rough matching, if the match is successful, it returns 1, and then real_probe will do further matching, if the match fails, it returns 0, and really_probe will not be executed. The call of this function guarantees that dev, drv or Both are device-level (i.e. dev stands for usb device, drv stands for usb device driver), or both are interface level (i.e. dev stands for an interface of the usb device, drv stands for USB interface driver).

static int really_probe(struct device *dev, struct device_driver *drv)

{

   …

   dev->driver = drv;//Assign value first, it will be used in the probe process later

   else if(drv->probe)

          ret = drv->probe(dev);

probe_failed:

   dev->drvier = NULL;//probe failed, reset it

   …

}

For usb, the call of this function has two branches, 1: dev, drv represents the device level, 2 dev, drv represents the interface level. Other combinations are filtered out in usb_device_match,

Branch 1: dev, drv represents the device level:

At this time, the drv must be usb_generic_driver.Because in the current usb system, only this driver represents the driver of the entire device, it is registered in usb_init, and the usb driver we usually write represents an interface.

struct usb_device_driver usb_generic_driver = {

   …

   .probe = generic_probe,

   …

}

Therefore, at this time drv->probe will call generic_probe().

static int generic_probe(struct usb_device *udev)

{

   …

   c = choose_configuration(dev);

   if(c >= 0) {

   err = usb_set_configuration(udev, c);//Set the configuration and register the interface.

   …

}

}

This function selects a suitable configuration for this usb device and registers the interface under this configuration.

int usb_set_configuration(struct usb_device *dev, int configuration)

{

   …

   for(I = 0; I < nintf; i++) {

   struct usb_interface *intf = cp->interface[i];

   …

   device_add(&intf->dev);

   …

}

   …

}

This function is more important, but we only care about the probe process and therefore save a lot of things. It calls the device_add() function for each interface under the current configuration. According to the previous analysis, this process will go to the next step. We will analyze Branch 2.

Branch 2: dev, drv represents the interface level:

At this time, dev represents an interface, and drv represents our own usb driver. But we should see that drv is a device_driver type, and the type of usb driver we write is generally usb_driver, so the probe here is written by ourselves The probe is obviously not the same. In fact, the drv here is a sub-object embedded in our driver object (because all drivers under Linux must be represented by device_driver). Then where is the probe function of this sub-object What about the assignment? It depends on the usb_register function.

Tracing this function we can see that the probe function here is actually usb_probe_interface/*usb/core/driver.c*/(all usb interface drivers are the same).

static int usb_probe_interface(struct device *dev)

{

struct driver = to_usb_driver(dev->driver);//dev->driver is set in really_probe.

error = driver->probe(intf, id);//This is the probe function we wrote ourselves.

}

driver->probe(intf, id); This is called into our own code,


Reference : https://blog.csdn.net/qq160816/article/details/6454595