1 /*
2 * Copyright (c) 2024, sakumisu
3 * Copyright (c) 2024, Derek Konigsberg
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7 #include "usbh_core.h"
8 #include "usbh_pl2303.h"
9
10 #undef USB_DBG_TAG
11 #define USB_DBG_TAG "usbh_pl2303"
12 #include "usb_log.h"
13
14 #define DEV_FORMAT "/dev/ttyUSB%d"
15
16 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_pl2303_buf[USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)];
17
18 #define CONFIG_USBHOST_MAX_PL2303_CLASS 1
19
20 #define UT_WRITE_VENDOR_DEVICE (USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE)
21 #define UT_READ_VENDOR_DEVICE (USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE)
22
23 static struct usbh_pl2303 g_pl2303_class[CONFIG_USBHOST_MAX_PL2303_CLASS];
24 static uint32_t g_devinuse = 0;
25
usbh_pl2303_class_alloc(void)26 static struct usbh_pl2303 *usbh_pl2303_class_alloc(void)
27 {
28 uint8_t devno;
29
30 for (devno = 0; devno < CONFIG_USBHOST_MAX_PL2303_CLASS; devno++) {
31 if ((g_devinuse & (1U << devno)) == 0) {
32 g_devinuse |= (1U << devno);
33 memset(&g_pl2303_class[devno], 0, sizeof(struct usbh_pl2303));
34 g_pl2303_class[devno].minor = devno;
35 return &g_pl2303_class[devno];
36 }
37 }
38 return NULL;
39 }
40
usbh_pl2303_class_free(struct usbh_pl2303 * pl2303_class)41 static void usbh_pl2303_class_free(struct usbh_pl2303 *pl2303_class)
42 {
43 uint8_t devno = pl2303_class->minor;
44
45 if (devno < 32) {
46 g_devinuse &= ~(1U << devno);
47 }
48 memset(pl2303_class, 0, sizeof(struct usbh_pl2303));
49 }
50
usbh_pl2303_get_chiptype(struct usbh_pl2303 * pl2303_class)51 static int usbh_pl2303_get_chiptype(struct usbh_pl2303 *pl2303_class)
52 {
53 int ret = 0;
54
55 switch (pl2303_class->hport->device_desc.bcdDevice) {
56 case 0x0300:
57 pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HX;
58 /* or TA, that is HX with external crystal */
59 break;
60 case 0x0400:
61 pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXD;
62 /* or EA, that is HXD with ESD protection */
63 /* or RA, that has internal voltage level converter that works only up to 1Mbaud (!) */
64 break;
65 case 0x0500:
66 pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXD;
67 /* in fact it's TB, that is HXD with external crystal */
68 break;
69 default:
70 /* NOTE: I have no info about the bcdDevice for the base PL2303 (up to 1.2Mbaud,
71 only fixed rates) and for PL2303SA (8-pin chip, up to 115200 baud */
72 /* Determine the chip type. This algorithm is taken from Linux. */
73 if (pl2303_class->hport->device_desc.bDeviceClass == 0x02) {
74 pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303;
75 } else if (pl2303_class->hport->device_desc.bMaxPacketSize0 == 0x40) {
76 pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HX;
77 } else {
78 pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303;
79 }
80 break;
81 }
82
83 /*
84 * The new chip revision PL2303HXN is only compatible with the new
85 * PLCOM_SET_REQUEST_PL2303HXN command. Issuing the old command
86 * PLCOM_SET_REQUEST to the new chip raises an error. Thus, PL2303HX
87 * and PL2303HXN can be distinguished by issuing an old-style request
88 * (on a status register) to the new chip and checking the error.
89 */
90 if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HX) {
91 struct usb_setup_packet *setup = pl2303_class->hport->setup;
92
93 setup->bmRequestType = UT_READ_VENDOR_DEVICE;
94 setup->bRequest = PL2303_SET_REQUEST;
95 setup->wValue = PL2303_STATUS_REG_PL2303HX;
96 setup->wIndex = 0;
97 setup->wLength = 1;
98
99 ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
100 if (ret == -USB_ERR_STALL) {
101 pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXN;
102 ret = 0;
103 } else if (ret < 0) {
104 USB_LOG_WRN("Error checking chip type: %d\r\n", ret);
105 return ret;
106 }
107 }
108
109 switch (pl2303_class->chiptype) {
110 case USBH_PL2303_TYPE_PL2303:
111 USB_LOG_INFO("chiptype = 2303\r\n");
112 break;
113 case USBH_PL2303_TYPE_PL2303HX:
114 USB_LOG_INFO("chiptype = 2303HX/TA\r\n");
115 break;
116 case USBH_PL2303_TYPE_PL2303HXN:
117 USB_LOG_INFO("chiptype = 2303HXN\r\n");
118 break;
119 case USBH_PL2303_TYPE_PL2303HXD:
120 USB_LOG_INFO("chiptype = 2303HXD/TB/RA/EA\r\n");
121 break;
122 default:
123 USB_LOG_INFO("chiptype = [%d]\r\n", pl2303_class->chiptype);
124 break;
125 }
126
127 return ret;
128 }
129
usbh_pl2303_do(struct usbh_pl2303 * pl2303_class,uint8_t req_type,uint8_t request,uint16_t value,uint16_t index,uint16_t length)130 static int usbh_pl2303_do(struct usbh_pl2303 *pl2303_class,
131 uint8_t req_type, uint8_t request, uint16_t value, uint16_t index,
132 uint16_t length)
133 {
134 struct usb_setup_packet *setup;
135
136 if (!pl2303_class || !pl2303_class->hport) {
137 return -USB_ERR_INVAL;
138 }
139 setup = pl2303_class->hport->setup;
140
141 setup->bmRequestType = req_type;
142 setup->bRequest = request;
143 setup->wValue = value;
144 setup->wIndex = index;
145 setup->wLength = length;
146
147 return usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
148 }
149
usbh_pl2303_set_line_coding(struct usbh_pl2303 * pl2303_class,struct cdc_line_coding * line_coding)150 int usbh_pl2303_set_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding)
151 {
152 struct usb_setup_packet *setup;
153
154 if (!pl2303_class || !pl2303_class->hport) {
155 return -USB_ERR_INVAL;
156 }
157 setup = pl2303_class->hport->setup;
158
159 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
160 setup->bRequest = CDC_REQUEST_SET_LINE_CODING;
161 setup->wValue = 0;
162 setup->wIndex = pl2303_class->intf;
163 setup->wLength = 7;
164
165 memcpy(g_pl2303_buf, line_coding, sizeof(struct cdc_line_coding));
166
167 return usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
168 }
169
usbh_pl2303_get_line_coding(struct usbh_pl2303 * pl2303_class,struct cdc_line_coding * line_coding)170 int usbh_pl2303_get_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding)
171 {
172 struct usb_setup_packet *setup;
173 int ret;
174
175 if (!pl2303_class || !pl2303_class->hport) {
176 return -USB_ERR_INVAL;
177 }
178 setup = pl2303_class->hport->setup;
179
180 setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
181 setup->bRequest = CDC_REQUEST_GET_LINE_CODING;
182 setup->wValue = 0;
183 setup->wIndex = pl2303_class->intf;
184 setup->wLength = 7;
185
186 ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
187 if (ret < 0) {
188 return ret;
189 }
190 memcpy(line_coding, g_pl2303_buf, sizeof(struct cdc_line_coding));
191 return ret;
192 }
193
usbh_pl2303_set_line_state(struct usbh_pl2303 * pl2303_class,bool dtr,bool rts)194 int usbh_pl2303_set_line_state(struct usbh_pl2303 *pl2303_class, bool dtr, bool rts)
195 {
196 struct usb_setup_packet *setup;
197
198 if (!pl2303_class || !pl2303_class->hport) {
199 return -USB_ERR_INVAL;
200 }
201 setup = pl2303_class->hport->setup;
202
203 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
204 setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE;
205 setup->wValue = (dtr << 0) | (rts << 1);
206 setup->wIndex = pl2303_class->intf;
207 setup->wLength = 0;
208
209 return usbh_control_transfer(pl2303_class->hport, setup, NULL);
210 }
211
usbh_pl2303_connect(struct usbh_hubport * hport,uint8_t intf)212 static int usbh_pl2303_connect(struct usbh_hubport *hport, uint8_t intf)
213 {
214 struct usb_endpoint_descriptor *ep_desc;
215 int ret = 0;
216
217 struct usbh_pl2303 *pl2303_class = usbh_pl2303_class_alloc();
218 if (pl2303_class == NULL) {
219 USB_LOG_ERR("Fail to alloc pl2303_class\r\n");
220 return -USB_ERR_NOMEM;
221 }
222
223 pl2303_class->hport = hport;
224 pl2303_class->intf = intf;
225
226 hport->config.intf[intf].priv = pl2303_class;
227
228 do {
229 ret = usbh_pl2303_get_chiptype(pl2303_class);
230 if (ret < 0) {
231 break;
232 }
233
234 /* Startup reset sequence, if necessary for the chip type */
235 if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303HXN) {
236 struct usb_setup_packet *setup = pl2303_class->hport->setup;
237
238 setup->bmRequestType = UT_WRITE_VENDOR_DEVICE;
239 setup->bRequest = PL2303_SET_REQUEST;
240 setup->wValue = 0;
241 setup->wIndex = pl2303_class->intf;
242 setup->wLength = 0;
243
244 ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
245 if (ret < 0) {
246 USB_LOG_WRN("Initialization reset failed: %d\r\n", ret);
247 break;
248 }
249 }
250
251 if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303) {
252 /* HX variants seem to lock up after a clear stall request. */
253 /*
254 * The FreeBSD code sets the stall flags on the in and out pipes
255 * here. Have no idea exactly how to do this, or if it is necessary.
256 * May just leave this code unwritten until test hardware is available.
257 */
258 } else if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HX || pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HXD) {
259 /* Reset upstream data pipes */
260 ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 8, 0, 0);
261 if (ret < 0) {
262 USB_LOG_WRN("Could not reset upstream data pipes (8,0): %d\r\n", ret);
263 break;
264 }
265 ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 9, 0, 0);
266 if (ret < 0) {
267 USB_LOG_WRN("Could not reset upstream data pipes (9,0): %d\r\n", ret);
268 break;
269 }
270 } else if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HXN) {
271 /* Reset upstream data pipes */
272 ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST_PL2303HXN, 0x07, 0x03, 0);
273 if (ret < 0) {
274 USB_LOG_WRN("Could not reset upstream data pipes (7,3): %d\r\n", ret);
275 break;
276 }
277 }
278
279 /* Final device initialization, if necessary for the chip type */
280 if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303HXN) {
281 if (usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
282 usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x0404, 0, 0) < 0 ||
283 usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
284 usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8383, 0, 1) < 0 ||
285 usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
286 usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x0404, 1, 0) < 0 ||
287 usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
288 usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8383, 0, 1) < 0 ||
289 usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0, 1, 0) < 0 ||
290 usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 1, 0, 0) < 0) {
291 USB_LOG_WRN("Could not complete init sequence\r\n");
292 ret = -USB_ERR_INVAL;
293 break;
294 }
295
296 if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303) {
297 ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 2, 0x44, 0);
298 } else {
299 ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 2, 0x24, 0);
300 }
301 if (ret < 0) {
302 USB_LOG_WRN("Could not complete final init request: %d\r\n", ret);
303 break;
304 }
305 }
306 } while (0);
307
308 if (ret < 0) {
309 USB_LOG_ERR("Failed to initialize PL2303 device: %d\r\n", ret);
310 return ret;
311 }
312
313 for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
314 ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
315 if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
316 continue;
317 } else {
318 if (ep_desc->bEndpointAddress & 0x80) {
319 USBH_EP_INIT(pl2303_class->bulkin, ep_desc);
320 } else {
321 USBH_EP_INIT(pl2303_class->bulkout, ep_desc);
322 }
323 }
324 }
325
326 snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, pl2303_class->minor);
327
328 USB_LOG_INFO("Register PL2303 Class:%s\r\n", hport->config.intf[intf].devname);
329
330 #if 0
331 USB_LOG_INFO("Test pl2303 rx and tx and rx for 5 times, baudrate is 115200\r\n");
332
333 struct cdc_line_coding linecoding;
334 uint8_t count = 5;
335
336 linecoding.dwDTERate = 115200;
337 linecoding.bDataBits = 8;
338 linecoding.bParityType = 0;
339 linecoding.bCharFormat = 0;
340 usbh_pl2303_set_line_coding(pl2303_class, &linecoding);
341 usbh_pl2303_set_line_state(pl2303_class, true, false);
342
343 memset(g_pl2303_buf, 'a', sizeof(g_pl2303_buf));
344 ret = usbh_pl2303_bulk_out_transfer(pl2303_class, g_pl2303_buf, sizeof(g_pl2303_buf), 0xfffffff);
345 USB_LOG_RAW("out ret:%d\r\n", ret);
346 while (count--) {
347 ret = usbh_pl2303_bulk_in_transfer(pl2303_class, g_pl2303_buf, sizeof(g_pl2303_buf), 0xfffffff);
348 USB_LOG_RAW("in ret:%d\r\n", ret);
349 if (ret > 0) {
350 for (uint32_t i = 0; i < ret; i++) {
351 USB_LOG_RAW("%02x ", g_pl2303_buf[i]);
352 }
353 }
354 USB_LOG_RAW("\r\n");
355 }
356 #endif
357
358 usbh_pl2303_run(pl2303_class);
359 return ret;
360 }
361
usbh_pl2303_disconnect(struct usbh_hubport * hport,uint8_t intf)362 static int usbh_pl2303_disconnect(struct usbh_hubport *hport, uint8_t intf)
363 {
364 int ret = 0;
365
366 struct usbh_pl2303 *pl2303_class = (struct usbh_pl2303 *)hport->config.intf[intf].priv;
367
368 if (pl2303_class) {
369 if (pl2303_class->bulkin) {
370 usbh_kill_urb(&pl2303_class->bulkin_urb);
371 }
372
373 if (pl2303_class->bulkout) {
374 usbh_kill_urb(&pl2303_class->bulkout_urb);
375 }
376
377 if (hport->config.intf[intf].devname[0] != '\0') {
378 usb_osal_thread_schedule_other();
379 USB_LOG_INFO("Unregister PL2303 Class:%s\r\n", hport->config.intf[intf].devname);
380 usbh_pl2303_stop(pl2303_class);
381 }
382
383 usbh_pl2303_class_free(pl2303_class);
384 }
385
386 return ret;
387 }
388
usbh_pl2303_bulk_in_transfer(struct usbh_pl2303 * pl2303_class,uint8_t * buffer,uint32_t buflen,uint32_t timeout)389 int usbh_pl2303_bulk_in_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
390 {
391 int ret;
392 struct usbh_urb *urb = &pl2303_class->bulkin_urb;
393
394 usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkin, buffer, buflen, timeout, NULL, NULL);
395 ret = usbh_submit_urb(urb);
396 if (ret == 0) {
397 ret = urb->actual_length;
398 }
399 return ret;
400 }
401
usbh_pl2303_bulk_out_transfer(struct usbh_pl2303 * pl2303_class,uint8_t * buffer,uint32_t buflen,uint32_t timeout)402 int usbh_pl2303_bulk_out_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
403 {
404 int ret;
405 struct usbh_urb *urb = &pl2303_class->bulkout_urb;
406
407 usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkout, buffer, buflen, timeout, NULL, NULL);
408 ret = usbh_submit_urb(urb);
409 if (ret == 0) {
410 ret = urb->actual_length;
411 }
412 return ret;
413 }
414
usbh_pl2303_run(struct usbh_pl2303 * pl2303_class)415 __WEAK void usbh_pl2303_run(struct usbh_pl2303 *pl2303_class)
416 {
417 (void)pl2303_class;
418 }
419
usbh_pl2303_stop(struct usbh_pl2303 * pl2303_class)420 __WEAK void usbh_pl2303_stop(struct usbh_pl2303 *pl2303_class)
421 {
422 (void)pl2303_class;
423 }
424
425 static const uint16_t pl2303_id_table[][2] = {
426 { 0x067B, 0x2303 }, // PL2303 Serial (ATEN/IOGEAR UC232A)
427 { 0x067B, 0x23A3 }, // PL2303HXN Serial, type GC
428 { 0x067B, 0x23B3 }, // PL2303HXN Serial, type GB
429 { 0x067B, 0x23C3 }, // PL2303HXN Serial, type GT
430 { 0x067B, 0x23D3 }, // PL2303HXN Serial, type GL
431 { 0x067B, 0x23E3 }, // PL2303HXN Serial, type GE
432 { 0x067B, 0x23F3 }, // PL2303HXN Serial, type GS
433 { 0, 0 },
434 };
435
436 const struct usbh_class_driver pl2303_class_driver = {
437 .driver_name = "pl2303",
438 .connect = usbh_pl2303_connect,
439 .disconnect = usbh_pl2303_disconnect
440 };
441
442 CLASS_INFO_DEFINE const struct usbh_class_info pl2303_class_info = {
443 .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
444 .bInterfaceClass = 0xff,
445 .bInterfaceSubClass = 0x00,
446 .bInterfaceProtocol = 0x00,
447 .id_table = pl2303_id_table,
448 .class_driver = &pl2303_class_driver
449 };