]> sigrok.org Git - libsigrok.git/blob - src/serial_hid_cp2110.c
serial_hid: add support for the SiLabs CP2110 chip (UT-D09, UT612)
[libsigrok.git] / src / serial_hid_cp2110.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2017 Carl-Fredrik Sundström <audio.cf@gmail.com>
5  * Copyright (C) 2017-2019 Gerhard Sittig <gerhard.sittig@gmx.net>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22 #include <glib.h>
23 #include <libsigrok/libsigrok.h>
24 #include "libsigrok-internal.h"
25 #include "serial_hid.h"
26 #include <string.h>
27
28 /** @cond PRIVATE */
29 #define LOG_PREFIX "serial-cp2110"
30 /** @endcond */
31
32 #ifdef HAVE_SERIAL_COMM
33 #ifdef HAVE_LIBHIDAPI
34
35 /**
36  * @file
37  *
38  * Support serial-over-HID, specifically the SiLabs CP2110 chip.
39  */
40
41 #define CP2110_MAX_BYTES_PER_REQUEST    63
42
43 static const struct vid_pid_item vid_pid_items_cp2110[] = {
44         { 0x10c4, 0xea80, },
45         VID_PID_TERM,
46 };
47
48 enum cp2110_report_id {
49         CP2110_UART_ENDIS = 0x41,
50         CP2110_UART_STATUS = 0x42,
51         CP2110_FIFO_PURGE = 0x43,
52         CP2110_UART_CONFIG = 0x50,
53 };
54
55 enum cp2110_uart_enable_param {
56         CP2110_UART_DISABLE = 0,
57         CP2110_UART_ENABLE = 1,
58 };
59
60 enum cp2110_fifo_purge_flag {
61         CP2110_FIFO_PURGE_TX = (1 << 0),
62         CP2110_FIFO_PURGE_RX = (1 << 1),
63 };
64
65 enum cp2110_uart_config_bitrate {
66         CP2110_BAUDRATE_MIN = 300,
67         CP2110_BAUDRATE_MAX = 1000000,
68 };
69
70 enum cp2110_uart_config_databits {
71         CP2110_DATABITS_MIN = 5,
72         CP2110_DATABITS_MAX = 8,
73 };
74
75 enum cp2110_uart_config_parity {
76         CP2110_PARITY_NONE = 0,
77         CP2110_PARITY_EVEN = 1,
78         CP2110_PARITY_ODD = 2,
79         CP2110_PARITY_MARK = 3,
80         CP2110_PARITY_SPACE = 4,
81 };
82
83 enum cp2110_uart_config_stopbits {
84         CP2110_STOPBITS_SHORT = 0,
85         CP2110_STOPBITS_LONG = 1,
86 };
87
88 /* Hardware flow control on CP2110 is RTS/CTS only. */
89 enum cp2110_uart_config_flowctrl {
90         CP2110_FLOWCTRL_NONE = 0,
91         CP2110_FLOWCTRL_HARD = 1,
92 };
93
94 static int cp2110_set_params(struct sr_serial_dev_inst *serial,
95         int baudrate, int bits, int parity, int stopbits,
96         int flowcontrol, int rts, int dtr)
97 {
98         uint8_t report[9];
99         int replen;
100         int rc;
101
102         /* Map serial API specs to CP2110 register values. Check ranges. */
103         if (baudrate < CP2110_BAUDRATE_MIN || baudrate > CP2110_BAUDRATE_MAX) {
104                 sr_err("CP2110: baudrate %d out of range", baudrate);
105                 return SR_ERR_ARG;
106         }
107         if (bits < CP2110_DATABITS_MIN || bits > CP2110_DATABITS_MAX) {
108                 sr_err("CP2110: %d databits out of range", bits);
109                 return SR_ERR_ARG;
110         }
111         bits -= CP2110_DATABITS_MIN;
112         switch (parity) {
113         case SP_PARITY_NONE:
114                 parity = CP2110_PARITY_NONE;
115                 break;
116         case SP_PARITY_ODD:
117                 parity = CP2110_PARITY_ODD;
118                 break;
119         case SP_PARITY_EVEN:
120                 parity = CP2110_PARITY_EVEN;
121                 break;
122         case SP_PARITY_MARK:
123                 parity = CP2110_PARITY_MARK;
124                 break;
125         case SP_PARITY_SPACE:
126                 parity = CP2110_PARITY_SPACE;
127                 break;
128         default:
129                 sr_err("CP2110: unknown parity spec %d", parity);
130                 return SR_ERR_ARG;
131         }
132         switch (stopbits) {
133         case 1:
134                 stopbits = CP2110_STOPBITS_SHORT;
135                 break;
136         case 2:
137                 stopbits = CP2110_STOPBITS_LONG;
138                 break;
139         default:
140                 sr_err("CP2110: unknown stop bits spec %d", stopbits);
141                 return SR_ERR_ARG;
142         }
143         switch (flowcontrol) {
144         case SP_FLOWCONTROL_NONE:
145                 flowcontrol = CP2110_FLOWCTRL_NONE;
146                 break;
147         case SP_FLOWCONTROL_XONXOFF:
148                 sr_err("CP2110: unsupported XON/XOFF flow control spec");
149                 return SR_ERR_ARG;
150         case SP_FLOWCONTROL_RTSCTS:
151                 flowcontrol = CP2110_FLOWCTRL_HARD;
152                 break;
153         default:
154                 sr_err("CP2110: unknown flow control spec %d", flowcontrol);
155                 return SR_ERR_ARG;
156         }
157
158         /*
159          * Enable the UART. Report layout:
160          * @0, length 1, enabled state (0: disable, 1: enable)
161          */
162         replen = 0;
163         report[replen++] = CP2110_UART_ENDIS;
164         report[replen++] = CP2110_UART_ENABLE;
165         rc = ser_hid_hidapi_set_report(serial, report, replen);
166         if (rc < 0)
167                 return SR_ERR;
168         if (rc != replen)
169                 return SR_ERR;
170
171         /*
172          * Setup bitrate and frame format. Report layout:
173          * (@-1, length 1, report number)
174          * @0, length 4, bitrate (big endian format)
175          * @4, length 1, parity
176          * @5, length 1, flow control
177          * @6, length 1, data bits (0: 5, 1: 6, 2: 7, 3: 8)
178          * @7, length 1, stop bits
179          */
180         replen = 0;
181         report[replen++] = CP2110_UART_CONFIG;
182         WB32(&report[replen], baudrate);
183         replen += sizeof(uint32_t);
184         report[replen++] = parity;
185         report[replen++] = flowcontrol;
186         report[replen++] = bits;
187         report[replen++] = stopbits;
188         rc = ser_hid_hidapi_set_report(serial, report, replen);
189         if (rc < 0)
190                 return SR_ERR;
191         if (rc != replen)
192                 return SR_ERR;
193
194         /*
195          * Currently not implemented: Control RTS and DTR state.
196          * TODO are these controlled via GPIO requests?
197          * GPIO.1 == RTS, can't find DTR in AN433 table 4.3
198          */
199         (void)rts;
200         (void)dtr;
201
202         return SR_OK;
203 }
204
205 static int cp2110_read_bytes(struct sr_serial_dev_inst *serial,
206         uint8_t *data, int space, unsigned int timeout)
207 {
208         uint8_t buffer[1 + CP2110_MAX_BYTES_PER_REQUEST];
209         int rc;
210         int count;
211
212         (void)timeout;
213
214         /*
215          * Check for available input data from the serial port.
216          * Packet layout:
217          * @0, length 1, number of bytes, range 0-63
218          * @1, length N, data bytes
219          */
220         memset(buffer, 0, sizeof(buffer));
221         rc = ser_hid_hidapi_get_data(serial, 0, buffer, sizeof(buffer), timeout);
222         if (rc == SR_ERR_TIMEOUT)
223                 return 0;
224         if (rc < 0)
225                 return SR_ERR;
226         if (rc == 0)
227                 return 0;
228         sr_dbg("DBG: %s() got report len %d, 0x%02x.", __func__, rc, buffer[0]);
229
230         /* Check the length spec, get the byte count. */
231         count = buffer[0];
232         if (!count)
233                 return 0;
234         if (count > CP2110_MAX_BYTES_PER_REQUEST)
235                 return SR_ERR;
236         sr_dbg("DBG: %s(), got %d UART RX bytes.", __func__, count);
237         if (count > space)
238                 return SR_ERR;
239
240         /* Pass received data bytes and their count to the caller. */
241         memcpy(data, &buffer[1], count);
242         return count;
243 }
244
245 static int cp2110_write_bytes(struct sr_serial_dev_inst *serial,
246         const uint8_t *data, int size)
247 {
248         uint8_t buffer[1 + CP2110_MAX_BYTES_PER_REQUEST];
249         int rc;
250
251         sr_dbg("DBG: %s() shall send UART TX data, len %d.", __func__, size);
252
253         if (size < 1)
254                 return 0;
255         if (size > CP2110_MAX_BYTES_PER_REQUEST) {
256                 size = CP2110_MAX_BYTES_PER_REQUEST;
257                 sr_dbg("DBG: %s() capping size to %d.", __func__, size);
258         }
259
260         /*
261          * Packet layout to send serial data to the USB HID chip:
262          * @0, length 1, number of bytes, range 0-63
263          * @1, length N, data bytes
264          */
265         buffer[0] = size;
266         memcpy(&buffer[1], data, size);
267         rc = ser_hid_hidapi_set_data(serial, 0, buffer, sizeof(buffer), 0);
268         if (rc < 0)
269                 return rc;
270         if (rc == 0)
271                 return 0;
272         return size;
273 }
274
275 static int cp2110_flush(struct sr_serial_dev_inst *serial)
276 {
277         uint8_t buffer[2];
278         int rc;
279
280         sr_dbg("DBG: %s() discarding RX and TX FIFO data.", __func__);
281
282         buffer[0] = CP2110_FIFO_PURGE;
283         buffer[1] = CP2110_FIFO_PURGE_TX | CP2110_FIFO_PURGE_RX;
284         rc = ser_hid_hidapi_set_data(serial, 0, buffer, sizeof(buffer), 0);
285         if (rc != 0)
286                 return SR_ERR;
287         return SR_OK;
288 }
289
290 static int cp2110_drain(struct sr_serial_dev_inst *serial)
291 {
292         uint8_t buffer[7];
293         int rc;
294         uint16_t tx_fill, rx_fill;
295
296         sr_dbg("DBG: %s() waiting for TX data to drain.", __func__);
297
298         /*
299          * Keep retrieving the UART status until the FIFO is found empty,
300          * or an error occured.
301          * Packet layout:
302          * @0, length 1, report ID
303          * @1, length 2, number of bytes in the TX FIFO (up to 480)
304          * @3, length 2, number of bytes in the RX FIFO (up to 480)
305          * @5, length 1, error status (parity and overrun error flags)
306          * @6, length 1, line break status
307          */
308         rx_fill = ~0;
309         do {
310                 memset(buffer, 0, sizeof(buffer));
311                 buffer[0] = CP2110_UART_STATUS;
312                 rc = ser_hid_hidapi_get_data(serial, 0, buffer, sizeof(buffer), 0);
313                 if (rc != sizeof(buffer)) {
314                         rc = SR_ERR_DATA;
315                         break;
316                 }
317                 if (buffer[0] != CP2110_UART_STATUS) {
318                         rc = SR_ERR_DATA;
319                         break;
320                 }
321                 rx_fill = RB16(&buffer[1]);
322                 tx_fill = RB16(&buffer[3]);
323                 if (!tx_fill) {
324                         rc = SR_OK;
325                         break;
326                 }
327                 g_usleep(2000);
328         } while (1);
329
330         sr_dbg("DBG: %s() TX drained, rc %d, RX fill %u, returning.",
331                 __func__, rc, (unsigned int)rx_fill);
332         return rc;
333 }
334
335 static struct ser_hid_chip_functions chip_cp2110 = {
336         .chipname = "cp2110",
337         .chipdesc = "SiLabs CP2110",
338         .vid_pid_items = vid_pid_items_cp2110,
339         .max_bytes_per_request = CP2110_MAX_BYTES_PER_REQUEST,
340         .set_params = cp2110_set_params,
341         .read_bytes = cp2110_read_bytes,
342         .write_bytes = cp2110_write_bytes,
343         .flush = cp2110_flush,
344         .drain = cp2110_drain,
345 };
346 SR_PRIV struct ser_hid_chip_functions *ser_hid_chip_funcs_cp2110 = &chip_cp2110;
347
348 #else
349
350 SR_PRIV struct ser_hid_chip_functions *ser_hid_chip_funcs_cp2110 = NULL;
351
352 #endif
353 #endif