CDC ACM GET LINE CODING request in Linux

Recently I was looking for a way to verify if CDC ACM GET LINE CODING request was working properly on a specific modem, since a customer was reporting an issue with it.

GET LINE CODING

In document “USB Communication Class Subclass Specification for PSTN Devices” GET LINE CODING is described as following (paragraph 6.2.2, Table 11):

“Requests current DTE rate, stop-bits, parity, and number-of-character bits.”

It is an optional request and, according to my experience, not very commonly used, since many of the values returned do not have much importance with pure USB-based modems.

Anyway, I had to verify this quickly so was hoping that something was already working and usable in Linux.

GET LINE CODING in Linux?

First step is to take a look at the cdc requests defined in include/uapi/linux/usb/cdc.h: good, USB_CDC_REQ_GET_LINE_CODING is properly defined, so things could be easier than expected.

However, looking for USB_CDC_REQ_GET_LINE_CODING in cdc-acm driver, no references are found, meaning that the request is not implemented.

Just for the sake of curiosity, I grepped through all the kernel code and the only driver using it is the acm gadget driver (drivers/usb/gadget/f_acm.c): no driver on the host side is using it, confirming the fact that it is not a very common request.

This forced me to write some code: since it was not meant for official inclusion I just wrote a dirty hack for testing.

Let’s take a look at what CDC GET LINE CODING returns (paragraph 6.3.11 of the standard):

dwDTERate: size 4, number, Data terminal rate in bits per second
bCharFormat: size 1, number, stop bits
bParityType: size 1, number, parity
bDataBits: size 1, number, data bits

looking again at cdc.h a struct definition for holding data is already available:

struct usb_cdc_line_coding {
 __le32 dwDTERate;
 __u8 bCharFormat;
#define USB_CDC_1_STOP_BITS 0
#define USB_CDC_1_5_STOP_BITS 1
#define USB_CDC_2_STOP_BITS 2

__u8 bParityType;
#define USB_CDC_NO_PARITY 0
#define USB_CDC_ODD_PARITY 1
#define USB_CDC_EVEN_PARITY 2
#define USB_CDC_MARK_PARITY 3
#define USB_CDC_SPACE_PARITY 4

__u8 bDataBits;
} __attribute__ ((packed));

Now we need to find a function for sending the request: cdc-acm has an example in acm_ctrl_msg where usb_control_msg is used. Function body can be found in drivers/usb/core/message.c: the important thing is that the function cannot be called in interrupt context, so we need to find a proper place for the call.

Since this is just an hack to quickly verify an hardware issue, I decided to put it before and after the tty termios setting in function acm_tty_set_termios: the added function is called acm_get_line_coding, used to send GET LINE CODING request and print the values returned. Anyway, all should be quite clear looking at the simple patch, based on v4.13-rc5

Note: this is only for testing, use it at your own risk!

An example of the kernel log output following a termios setting is:

[ 1413.986276] cdc_acm 3-8:1.0: ttyACM: dteRate: 115200, charFormat: 0, parityType: 0, dataBits: 8
[ 1413.988125] cdc_acm 3-8:1.0: ttyACM: dteRate: 57600, charFormat: 0, parityType: 0, dataBits: 8

To check the correctness of the request I used also an USB hardware sniffer to take the picture above, showing that all seems to be fine.