USB serial drivers, Part 3

[Part 1] Part 2]

Here I discuss some aspects of DSDI, the Device Specific Driver Interface - the interface between GSD and DSD. All DSDI definitions are in usbser_dsdi.h header file. There are plenty of comments there, so I'll skip the least interesting parts.

Versioning

DSDI provides simple versioning via ds_version. DSD should always set it to DS_OPS_VERSION, which is then resolved to the right value during compilation:

enum {
        DS_OPS_VERSION_V0       = 0,
        DS_OPS_VERSION          = DS_OPS_VERSION_V0
};

Version number is passed to the GSD with the ds_ops structure as an argument to ds_attach(), which is the very first DSD->GSD call. The GSD will be able to provide DSD with the right version of interfaces or fail attach if it doesn't support this version.

Initial configuration

ds_attach() is called during driver attach(9E) phase:

        int     (\*ds_attach)(ds_attach_info_t \*aip);

The only argument is a pointer to the structure:

typedef struct ds_attach_info {
        /\*
         \* passed to DSD:
         \*/
        dev_info_t      \*ai_dip;        /\* devinfo \*/
        /\*
         \* these event callbacks should be registered by DSD
         \* using usb_register_event_cbs()
         \*/
        usb_event_t     \*ai_usb_events;
        /\*
         \* returned by DSD:
         \*/
        ds_hdl_t        \*ai_hdl; /\* handle to be used by GSD in other calls \*/
        uint_t          \*ai_port_cnt;   /\* number of ports \*/
} ds_attach_info_t;

Pretty self-explanatory, ai_dip and ai_usb_events are input parameters, ai_hdl and ai_port_cnt are output parameters. Attach is the right place to allocate and initialize per-device and per-port resources, download firmware, reset the device into a known state. Some drivers may also open USB pipes at attach time, although doing it at port open time is more preferable.

After attach, the driver would typically register callbacks using ds_register_cb():

typedef struct ds_cb {
        void            (\*cb_tx)(caddr_t);      /\* transmit callback \*/
        void            (\*cb_rx)(caddr_t);      /\* receive callback \*/
        void            (\*cb_status)(caddr_t);  /\* status change callback \*/
        caddr_t         cb_arg;                 /\* callback argument \*/
} ds_cb_t;

        int     (\*ds_register_cb)(ds_hdl_t, uint_t port_num, ds_cb_t \*cb);

Note that callback registration is per port. Typically the function pointers will be the same, but cb_arg is different to uniquely identify a port.

Working with ports

Before GSD uses a port, it opens it using ds_open_port(). This is usually done when application uses open(2) system call on the serial device.

        int     (\*ds_open_port)(ds_hdl_t, uint_t port_num);

Port open initializes the port, opens per-port USB pipes. It is also a good idea to ensure clean software and hardware state - do not assume that the preceding close performed all necessary cleanup.

Right after opening, port settings can be in an unknown state, so GSD sets port parameters, such as baud rate and parity, using ds_set_port_params():

	int     (\*ds_set_port_params)(ds_hdl_t, uint_t port_num, 
			ds_port_params_t \*tp);

The ds_port_params_t structure contains a variable-length array of parameters. This function can be called at any time.

In order to transmit one or more characters, the GSD calls ds_tx():

	int     (\*ds_tx)(ds_hdl_t, uint_t port_num, mblk_t \*mp);

The data is passed in a STREAMS message block. Currently this operation must always succeed: if DSD cannot transmit immediately, it should buffer the data. When data transfer is completed, DSD should notify GSD by calling the previously registered cb_tx() callback.

Data receipt is backwards: when data arrives, DSD buffers it, calls cb_rx() callback. GSD then calls ds_rx(), which returns all available data:

	mblk_t  \*(\*ds_rx)(ds_hdl_t, uint_t port_num);

The returned mblk_t can be a linked list of blocks (through the b_cont field). See also Part 2 for description how error bytes are represented.

Other operations are pretty self-explanatory and/or described in the header file.

In the fourth and the last part I will provide C code that can be used as a starting point for writing a new DSD.


Tags:

Comments:

Post a Comment:
Comments are closed for this entry.
About

artem

Search

Top Tags
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today