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.
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.