-
Notifications
You must be signed in to change notification settings - Fork 1
/
driver_pl2303.c
394 lines (334 loc) · 11.4 KB
/
driver_pl2303.c
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
/*
* libusbserial
*
* Copyright (C) 2019-2022 Anton Prozorov <[email protected]>
* Copyright (c) 2014-2015 Felix Hädicke
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#include "io.h"
#include "driver.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <endian.h>
#define PROLIFIC_VENDOR_ID 0x067b
#define PROLIFIC_PRODUCT_ID_PL2303 0x2303 // PL2303HX, HXD, TA, ...
#define PROLIFIC_PRODUCT_ID_PL2303GC 0x23A3
#define PROLIFIC_PRODUCT_ID_PL2303GB 0x23B3
#define PROLIFIC_PRODUCT_ID_PL2303GT 0x23C3
#define PROLIFIC_PRODUCT_ID_PL2303GL 0x23D3
#define PROLIFIC_PRODUCT_ID_PL2303GE 0x23E3
#define PROLIFIC_PRODUCT_ID_PL2303GS 0x23F3
#define PL2303_REQTYPE_HOST2DEVICE_VENDOR 0x40
#define PL2303_REQTYPE_DEVICE2HOST_VENDOR 0xC0
#define PL2303_REQTYPE_HOST2DEVICE 0x21
#define PL2303_SET_LINE_CODING 0x20
#define PL2303_GET_LINE_CODING 0x21
#define PL2303_GET_VENDOR 0x01
#define PL2303_GET_N_VENDOR 0x81
#define PL2303_SET_VENDOR 0x01
#define PL2303_SET_N_VENDOR 0x80
#define PL2303_FLUSH_RX_VALUE 0x08
#define PL2303_FLUSH_TX_VALUE 0x09
#define PL2303_FLUSH_N_VALUE 0x07
#define PL2303_FLUSH_N_VALUE_RX 0x01
#define PL2303_FLUSH_N_VALUE_TX 0x02
#define PL2303_SET_CONTROL 0x22
#define PL2303_CONTROL_LINE_STATE_RTS 0x2
#define PL2303_CONTROL_LINE_STATE_DTR 0x1
enum pl2303_device_type
{
PL2303_TYPE_UNKNOWN = 0,
PL2303_TYPE_H,
PL2303_TYPE_TA,
PL2303_TYPE_TB,
PL2303_TYPE_HX,
PL2303_TYPE_HXD,
PL2303_TYPE_HXN
};
struct pl2303_data
{
enum pl2303_device_type type;
};
static const char *PROLIFIC_DEVICE_NAME_PL2303 = "PL2303";
static inline int pl2303_vendor_write(struct usbserial_port *port,
uint16_t value, uint16_t index)
{
struct pl2303_data *pdata = (struct pl2303_data*)port->driver_data;
return libusb_control_transfer(
port->usb_dev_hdl,
PL2303_REQTYPE_HOST2DEVICE_VENDOR,
pdata->type==PL2303_TYPE_HXN ? PL2303_SET_N_VENDOR : PL2303_SET_VENDOR,
value,
index,
NULL,
0,
DEFAULT_CONTROL_TIMEOUT_MILLIS);
}
static inline int pl2303_vendor_read(struct usbserial_port *port,
uint16_t value, unsigned char buf[1])
{
struct pl2303_data *pdata = (struct pl2303_data*)port->driver_data;
int ret = libusb_control_transfer(
port->usb_dev_hdl,
PL2303_REQTYPE_DEVICE2HOST_VENDOR,
pdata->type==PL2303_TYPE_HXN ? PL2303_GET_N_VENDOR : PL2303_GET_VENDOR,
value,
0,
buf,
1,
DEFAULT_CONTROL_TIMEOUT_MILLIS);
if (ret == 1)
return 0;
if (ret > 0)
return USBSERIAL_ERROR_CTRL_CMD_FAILED;
return ret;
}
static inline int pl2303_ctrl(struct usbserial_port *port,
uint16_t req, uint16_t value,
void *data, uint16_t size)
{
return libusb_control_transfer(
port->usb_dev_hdl,
PL2303_REQTYPE_HOST2DEVICE,
req,
value,
0,
data,
size,
DEFAULT_CONTROL_TIMEOUT_MILLIS);
}
static int pl2303_set_dtr_rts(struct usbserial_port *port, int dtr, int rts)
{
int control = 0;
if (dtr) control |= PL2303_CONTROL_LINE_STATE_DTR;
if (rts) control |= PL2303_CONTROL_LINE_STATE_RTS;
return pl2303_ctrl(port, PL2303_SET_CONTROL, control, NULL, 0);
}
static int pl2303_check_supported_by_vid_pid(uint16_t vid, uint16_t pid)
{
return ((PROLIFIC_VENDOR_ID == vid) &&
(PROLIFIC_PRODUCT_ID_PL2303 == pid ||
PROLIFIC_PRODUCT_ID_PL2303GC == pid ||
PROLIFIC_PRODUCT_ID_PL2303GB == pid ||
PROLIFIC_PRODUCT_ID_PL2303GT == pid ||
PROLIFIC_PRODUCT_ID_PL2303GL == pid ||
PROLIFIC_PRODUCT_ID_PL2303GE == pid ||
PROLIFIC_PRODUCT_ID_PL2303GS == pid));
}
static const char* pl2303_get_device_name(uint16_t vid, uint16_t pid, uint8_t classs, uint8_t subclass)
{
return PROLIFIC_DEVICE_NAME_PL2303;
}
static unsigned int pl2303_get_ports_count(uint16_t vid, uint16_t pid)
{
/* Are there any multiport CDC/ACM or Prolific devices out there? */
return 1;
}
static enum pl2303_device_type pl2303_detect_type(struct libusb_device_descriptor *desc)
{
/*
* Detection form Linux kernel driver:
* https://github.com/torvalds/linux/blob/master/drivers/usb/serial/pl2303.c
*/
if (desc->bDeviceClass == 0x02 || desc->bMaxPacketSize0 != 0x40)
return PL2303_TYPE_H;
uint16_t bcdDevice = le16toh(desc->bcdDevice);
uint16_t bcdUSB = le16toh(desc->bcdUSB);
switch (bcdUSB)
{
case 0x101:
/* USB 1.0.1? Let's assume they meant 1.1... */
break;
case 0x110:
switch (bcdDevice)
{
case 0x300:
return PL2303_TYPE_HX;
case 0x400:
return PL2303_TYPE_HXD;
default:
return PL2303_TYPE_HX;
}
break;
case 0x200:
switch (bcdDevice)
{
case 0x100: /* GC */
case 0x105:
return PL2303_TYPE_HXN;
case 0x300: /* GT / TA */
return PL2303_TYPE_TA;
case 0x305:
case 0x400: /* GL */
case 0x405:
return PL2303_TYPE_HXN;
case 0x500: /* GE / TB */
return PL2303_TYPE_TB;
case 0x505:
case 0x600: /* GS */
case 0x605:
case 0x700: /* GR */
case 0x705:
return PL2303_TYPE_HXN;
}
break;
}
return PL2303_TYPE_UNKNOWN;
}
static int pl2303_port_init(struct usbserial_port *port)
{
assert(port);
enum pl2303_device_type type = pl2303_detect_type(&port->usb_dev_desc);
if (type == PL2303_TYPE_UNKNOWN)
return -1;
unsigned char buf[1];
int ret = usbserial_io_get_endpoint(port, 0);
if (ret)
return ret;
if (type != PL2303_TYPE_HXN)
{
pl2303_vendor_read(port, 0x8484, buf);
pl2303_vendor_write(port, 0x0404, 0);
pl2303_vendor_read(port, 0x8484, buf);
pl2303_vendor_read(port, 0x8383, buf);
pl2303_vendor_read(port, 0x8484, buf);
pl2303_vendor_write(port, 0x0404, 1);
pl2303_vendor_read(port, 0x8484, buf);
pl2303_vendor_read(port, 0x8383, buf);
pl2303_vendor_write(port, 0, 1);
pl2303_vendor_write(port, 1, 0);
pl2303_vendor_write(port, 2, type==PL2303_TYPE_H ? 0x24 : 0x44);
pl2303_vendor_write(port, 3, 0);
pl2303_set_dtr_rts(port, 0, 0);
pl2303_vendor_write(port, 0x0505, 0x1311);
}
struct pl2303_data* pdata = (struct pl2303_data*) malloc(sizeof(struct pl2303_data));
if (!pdata)
{
ret = USBSERIAL_ERROR_RESOURCE_ALLOC_FAILED;
goto relase_if_and_return;
}
pdata->type = type;
return 0;
relase_if_and_return:
assert(ret != 0);
libusb_release_interface(port->usb_dev_hdl, port->port_idx);
return ret;
}
static int pl2303_port_deinit(struct usbserial_port *port)
{
assert(port);
return usbserial_io_free_endpoint(port);
}
static int pl2303_port_set_config(struct usbserial_port *port, const struct usbserial_config* config)
{
assert(port);
assert(config);
int ret;
unsigned char data[7];
unsigned char stop_bits_byte, parity_byte, data_bits_byte;
uint32_t baud = config->baud; /* we don't check the baudrate to correct value */
switch (config->stop_bits)
{
case USBSERIAL_STOPBITS_1: stop_bits_byte = 0; break;
case USBSERIAL_STOPBITS_1_5: stop_bits_byte = 1; break;
case USBSERIAL_STOPBITS_2: stop_bits_byte = 2; break;
default: return USBSERIAL_ERROR_INVALID_PARAMETER;
}
switch (config->parity)
{
case USBSERIAL_PARITY_NONE: parity_byte = 0; break;
case USBSERIAL_PARITY_ODD: parity_byte = 1; break;
case USBSERIAL_PARITY_EVEN: parity_byte = 2; break;
case USBSERIAL_PARITY_MARK: parity_byte = 3; break;
case USBSERIAL_PARITY_SPACE: parity_byte = 4; break;
default: return USBSERIAL_ERROR_INVALID_PARAMETER;
}
data_bits_byte = (unsigned char) config->data_bits;
baud = htole32(baud); /* to LE */
memcpy(data, &baud, 4);
data[4] = stop_bits_byte;
data[5] = parity_byte;
data[6] = data_bits_byte;
ret = pl2303_ctrl(port, PL2303_SET_LINE_CODING, 0, data, sizeof(data));
if (ret == sizeof(data))
return 0;
if (ret > 0)
return USBSERIAL_ERROR_CTRL_CMD_FAILED;
return ret;
}
static int pl2303_start_reader(struct usbserial_port *port)
{
assert(port);
assert(port->cb_read);
return usbserial_io_init_bulk_read_transfer(port);
}
static int pl2303_stop_reader(struct usbserial_port *port)
{
assert(port);
return usbserial_io_cancel_bulk_read_transfer(port);
}
static int pl2303_read(
struct usbserial_port *port,
void *data,
size_t size,
int timeout)
{
assert(port);
return usbserial_io_bulk_read(port, data, size, timeout);
}
static int pl2303_write(
struct usbserial_port *port,
const void *data,
size_t size)
{
assert(port);
return usbserial_io_bulk_write(port, data, size);
}
static int pl2303_purge(struct usbserial_port *port, int rx, int tx)
{
assert(port);
struct pl2303_data *pdata = (struct pl2303_data*)port->driver_data;
int p_rx_ret = 0, p_tx_ret = 0;
if (pdata->type == PL2303_TYPE_HXN)
{
int index = 0;
if (rx) index |= PL2303_FLUSH_N_VALUE_RX;
if (tx) index |= PL2303_FLUSH_N_VALUE_TX;
if (index)
p_rx_ret = pl2303_vendor_write(port, PL2303_FLUSH_N_VALUE, index);
} else
{
if (rx) p_rx_ret = pl2303_vendor_write(port, PL2303_FLUSH_RX_VALUE, 0);
if (tx) p_tx_ret = pl2303_vendor_write(port, PL2303_FLUSH_TX_VALUE, 0);
}
return p_rx_ret ? p_rx_ret : p_tx_ret;
}
const struct usbserial_driver driver_pl2303 =
{
.check_supported_by_vid_pid = pl2303_check_supported_by_vid_pid,
.check_supported_by_class = NULL,
.get_device_name = pl2303_get_device_name,
.get_ports_count = pl2303_get_ports_count,
.port_init = pl2303_port_init,
.port_deinit = pl2303_port_deinit,
.port_set_config = pl2303_port_set_config,
.start_reader = pl2303_start_reader,
.stop_reader = pl2303_stop_reader,
.read = pl2303_read,
.write = pl2303_write,
.purge = pl2303_purge,
.set_dtr_rts = pl2303_set_dtr_rts,
.read_data_process = NULL,
};
const struct usbserial_driver *usbserial_driver_pl2303 = &driver_pl2303;