πŸ“œ ⬆️ ⬇️

USB support in KolibriOS: what's inside? Part 5: logic level

Device connection processing, started at the host controller support level , has stopped, preparing the zero device endpoint for operation. The channel support level has provided methods for working with channels. It’s time to use them to continue the device initialization: the usb_new_device function from bus / usb / protocol.inc is usb_new_device .

Now the device can do little: it has not yet received an address on the bus, it has not yet been configured, it can use only 100 mA of bus power. In general, all that a device can do is to tell about itself in response to relevant questions. The story about the device itself is organized in the form of descriptors . A GET_DESCRIPTOR issue is the GET_DESCRIPTOR command sent to the null endpoint; the command must specify the type of descriptor , the sequence number of the descriptor among all with this type, the length of the data to be transmitted. Each command to the control endpoint takes 8 bytes and may or may not have additional data; in some commands some fields are not used. The command structure by byte and the fields used are described in Chapter 9 of the USB specification, here I will only describe the input and output data for the commands.

The logic device level has two tasks: first, configure the device; secondly, ask him, download the appropriate driver β€” or even drivers β€” and inform the driver about the new device.
')

The first steps


usb_new_device starts by creating a channel to the zero end point of the device. The host controllers support code before calling usb_new_device filled in the structure describing the device, all that remains is to specify the endpoint parameters: the number β€” zero β€” and the maximum packet size. The maximum packet size for a control endpoint can vary from 8 to 64 bytes and is recorded among the first 8 bytes of the device descriptor. However, to read the descriptor, you need an already open channel. Fortunately, you can get out of the dependency cycle: the maximum packet size is needed only to break one transfer into transactions; it is guaranteed that the maximum packet size for the control endpoint is at least 8 bytes; if during the initial stages of the configuration only data of length 8 bytes or less is transmitted, then the exact value of the maximum packet size is unimportant. Therefore, as the maximum value of the packet, you can set any number, not less than 8 bytes.

However, there is one problem: the EHCI emulation in VirtualBox is not entirely correct and requires 64 bytes to work with HighSpeed ​​devices. Since there are no other requirements, the channel to the zero endpoint begins to exist with a maximum packet size of 64 bytes.

Until the device receives an address on the bus, it responds to the zero address and prevents the reset of subsequent devices β€” if another device is reset, both devices will respond to packets with a zero address, which will lead to confusion on the bus. Therefore, the first thing that usb_new_device does after opening a channel is to select the first unallocated address and send the SET_ADDRESS command. At the entrance of the command SET_ADDRESS - address, there is no output.

After completing the SET_ADDRESS command, the usb_set_address_callback procedure receives usb_set_address_callback . If the device did not respond to the command or responded with an error, then the only option is to disconnect the device from the bus at the hub level. If the command is successful, usb_set_address_callback informs the support level of the host controllers that the address has changed; for the reasons I mentioned in one of the previous articles in the series , the change may take some time. In both cases, the zero address on the bus ceases to be occupied, so you can begin to reset subsequent devices, if any; the call to usb_test_pending_port is responsible for this.

When the host controller confirms the address change in the structure describing the device, the support code calls usb_after_set_address . Now the representations of the host controller and the device about the address on the bus are reconfigured, you can continue the configuration. The next command usb_after_set_address sends the question GET_DESCRIPTOR , requesting the first 8 bytes of the device descriptor β€” descriptor type 1, descriptor number 0. The last of these 8 bytes sets the maximum packet size for the zero end point β€” the last thing that was missing for full communication. The usb_get_descr8_callback response usb_get_descr8_callback pulls out the maximum packet size, reports it to the host controller, and waits for confirmation. After confirmation, call usb_after_set_endpoint_size , where the GET_DESCRIPTOR question for the device descriptor is repeated, now requesting data completely.

Device handle



The figure shows the hexadecimal dumps of the descriptors of two different devices: on the top β€” the virtual hub of RMH, on the bottom β€” of a mouse. The device handle consists of the following data in little-endian :Configurations are mutually exclusive modes of operation of the device, between which there is nothing in common. In theory, a USB device can have several different configurations, between which the software should choose. The USB specification, as a somewhat contrived example, leads a modem that can provide either one channel at 128 Kbps or two independent channels at 64 Kbps each. In practice, the vast majority of devices have exactly one configuration and use other methods for selecting the operating mode. However, this one configuration still needs to be explicitly turned on; before that, the device will remain non-functional and able only to give descriptors.

KolibriOS always selects the first device configuration. The usb_get_descr_callback function, called after reading the device descriptor, sends the GET_DESCRIPTOR command for the first configuration descriptor β€” type 2, number 0.

Together with the configuration descriptor, the device returns many other descriptors that specify the configuration details. The total amount of data associated with the configuration descriptor is not known in advance, but is contained in the configuration descriptor itself. Therefore, the request proceeds in two stages - at the first stage, the usb_get_descr_callback function requests 8 bytes, at the second stage, the usb_know_length_callback function pulls the total size from the read bytes and requests it already.

Configuration descriptor



One of the simplest configurations is the virtual hub of RMH, it is shown in the figure. Together with the configuration descriptor, the device returns descriptors of all interfaces of this configuration, for each interface descriptors of all its endpoints. Associated data includes other types of descriptors β€” for example, HID devices like mice and keyboards return a HID descriptor between an interface descriptor and an endpoint descriptor; parsing them is a matter of driver.

The configuration descriptor consists of the following data:
Interface Descriptor:
The endpoint descriptor specifies the parameters needed to open the channel:

The usb_set_config_callback function, after receiving the complete data associated with the configuration descriptor, SET_CONFIGURATION command with a parameter taken from the descriptor; no output from this command. After successful completion of the command, the device becomes fully functional. The last function of the logic unit level, usb_got_config_callback , parses the resulting configuration data: makes sure that the device does not try to cheat by replacing the total data size between the GET_DESCRIPTOR commands; passes through the list of descriptors, ignoring everything except interface descriptors; for zero-mode descriptors, determines the driver by device class, loads the driver and calls its AddDevice function, passing a pointer to the configuration data and a pointer to the desired interface descriptor inside them. For further work with the device meets the loaded driver. I will talk about existing KolibriOS drivers in subsequent articles.

All articles of the series


Part 1: general scheme
Part 2: Basics of working with host controllers
Part 3: Host Controller Support Code
Part 4: Channel Support Level
Part 5: logic level
Part 6: hub driver

Source: https://habr.com/ru/post/200172/


All Articles