xref: /dragonfly/sys/bus/u4b/serial/uplcom.c (revision 2b3f93ea6d1f70880f3e87f3c2cbe0dc0bfc9332)
1 /*        $NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $    */
2 
3 /*-
4  * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /*-
30  * Copyright (c) 2001 The NetBSD Foundation, Inc.
31  * All rights reserved.
32  *
33  * This code is derived from software contributed to The NetBSD Foundation
34  * by Ichiro FUKUHARA (ichiro@ichiro.org).
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  *
45  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
46  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
47  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
49  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
50  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
51  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
52  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
53  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
54  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
55  * POSSIBILITY OF SUCH DAMAGE.
56  */
57 
58 /*
59  * This driver supports several USB-to-RS232 serial adapters driven by
60  * Prolific PL-2303, PL-2303X and probably PL-2303HX USB-to-RS232
61  * bridge chip.  The adapters are sold under many different brand
62  * names.
63  *
64  * Datasheets are available at Prolific www site at
65  * http://www.prolific.com.tw.  The datasheets don't contain full
66  * programming information for the chip.
67  *
68  * PL-2303HX is probably programmed the same as PL-2303X.
69  *
70  * There are several differences between PL-2303 and PL-2303(H)X.
71  * PL-2303(H)X can do higher bitrate in bulk mode, has _probably_
72  * different command for controlling CRTSCTS and needs special
73  * sequence of commands for initialization which aren't also
74  * documented in the datasheet.
75  */
76 
77 #include <sys/stdint.h>
78 #include <sys/param.h>
79 #include <sys/queue.h>
80 #include <sys/types.h>
81 #include <sys/systm.h>
82 #include <sys/kernel.h>
83 #include <sys/bus.h>
84 #include <sys/module.h>
85 #include <sys/lock.h>
86 #include <sys/condvar.h>
87 #include <sys/sysctl.h>
88 #include <sys/unistd.h>
89 #include <sys/callout.h>
90 #include <sys/malloc.h>
91 #include <sys/caps.h>
92 #include <sys/serial.h>
93 
94 #include <bus/u4b/usb.h>
95 #include <bus/u4b/usbdi.h>
96 #include <bus/u4b/usbdi_util.h>
97 #include <bus/u4b/usb_cdc.h>
98 #include "usbdevs.h"
99 
100 #define   USB_DEBUG_VAR uplcom_debug
101 #include <bus/u4b/usb_debug.h>
102 #include <bus/u4b/usb_process.h>
103 
104 #include <bus/u4b/serial/usb_serial.h>
105 
106 #ifdef USB_DEBUG
107 static int uplcom_debug = 0;
108 
109 static SYSCTL_NODE(_hw_usb, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom");
110 SYSCTL_INT(_hw_usb_uplcom, OID_AUTO, debug, CTLFLAG_RW,
111     &uplcom_debug, 0, "Debug level");
112 #endif
113 
114 #define   UPLCOM_MODVER                           1         /* module version */
115 
116 #define   UPLCOM_CONFIG_INDEX           0
117 #define   UPLCOM_IFACE_INDEX            0
118 #define   UPLCOM_SECOND_IFACE_INDEX     1
119 
120 #ifndef UPLCOM_INTR_INTERVAL
121 #define   UPLCOM_INTR_INTERVAL                    0         /* default */
122 #endif
123 
124 #define   UPLCOM_BULK_BUF_SIZE 1024     /* bytes */
125 
126 #define   UPLCOM_SET_REQUEST            0x01
127 #define   UPLCOM_SET_CRTSCTS            0x41
128 #define   UPLCOM_SET_CRTSCTS_PL2303X    0x61
129 #define   RSAQ_STATUS_CTS                         0x80
130 #define   RSAQ_STATUS_DSR                         0x02
131 #define   RSAQ_STATUS_DCD                         0x01
132 
133 #define   TYPE_PL2303                             0
134 #define   TYPE_PL2303HX                           1
135 
136 enum {
137           UPLCOM_BULK_DT_WR,
138           UPLCOM_BULK_DT_RD,
139           UPLCOM_INTR_DT_RD,
140           UPLCOM_N_TRANSFER,
141 };
142 
143 struct uplcom_softc {
144           struct ucom_super_softc sc_super_ucom;
145           struct ucom_softc sc_ucom;
146 
147           struct usb_xfer *sc_xfer[UPLCOM_N_TRANSFER];
148           struct usb_device *sc_udev;
149           struct lock sc_lock;
150 
151           uint16_t sc_line;
152 
153           uint8_t   sc_lsr;                       /* local status register */
154           uint8_t   sc_msr;                       /* uplcom status register */
155           uint8_t   sc_chiptype;                  /* type of chip */
156           uint8_t   sc_ctrl_iface_no;
157           uint8_t   sc_data_iface_no;
158           uint8_t   sc_iface_index[2];
159 };
160 
161 /* prototypes */
162 
163 static usb_error_t uplcom_reset(struct uplcom_softc *, struct usb_device *);
164 static usb_error_t uplcom_pl2303_do(struct usb_device *, int8_t, uint8_t,
165                               uint16_t, uint16_t, uint16_t);
166 static int          uplcom_pl2303_init(struct usb_device *, uint8_t);
167 static void         uplcom_cfg_set_dtr(struct ucom_softc *, uint8_t);
168 static void         uplcom_cfg_set_rts(struct ucom_softc *, uint8_t);
169 static void         uplcom_cfg_set_break(struct ucom_softc *, uint8_t);
170 static int          uplcom_pre_param(struct ucom_softc *, struct termios *);
171 static void         uplcom_cfg_param(struct ucom_softc *, struct termios *);
172 static void         uplcom_start_read(struct ucom_softc *);
173 static void         uplcom_stop_read(struct ucom_softc *);
174 static void         uplcom_start_write(struct ucom_softc *);
175 static void         uplcom_stop_write(struct ucom_softc *);
176 static void         uplcom_cfg_get_status(struct ucom_softc *, uint8_t *,
177                         uint8_t *);
178 static void         uplcom_poll(struct ucom_softc *ucom);
179 
180 static device_probe_t uplcom_probe;
181 static device_attach_t uplcom_attach;
182 static device_detach_t uplcom_detach;
183 
184 static usb_callback_t uplcom_intr_callback;
185 static usb_callback_t uplcom_write_callback;
186 static usb_callback_t uplcom_read_callback;
187 
188 static const struct usb_config uplcom_config_data[UPLCOM_N_TRANSFER] = {
189 
190           [UPLCOM_BULK_DT_WR] = {
191                     .type = UE_BULK,
192                     .endpoint = UE_ADDR_ANY,
193                     .direction = UE_DIR_OUT,
194                     .bufsize = UPLCOM_BULK_BUF_SIZE,
195                     .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
196                     .callback = &uplcom_write_callback,
197                     .if_index = 0,
198           },
199 
200           [UPLCOM_BULK_DT_RD] = {
201                     .type = UE_BULK,
202                     .endpoint = UE_ADDR_ANY,
203                     .direction = UE_DIR_IN,
204                     .bufsize = UPLCOM_BULK_BUF_SIZE,
205                     .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
206                     .callback = &uplcom_read_callback,
207                     .if_index = 0,
208           },
209 
210           [UPLCOM_INTR_DT_RD] = {
211                     .type = UE_INTERRUPT,
212                     .endpoint = UE_ADDR_ANY,
213                     .direction = UE_DIR_IN,
214                     .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
215                     .bufsize = 0,       /* use wMaxPacketSize */
216                     .callback = &uplcom_intr_callback,
217                     .if_index = 1,
218           },
219 };
220 
221 static struct ucom_callback uplcom_callback = {
222           .ucom_cfg_get_status = &uplcom_cfg_get_status,
223           .ucom_cfg_set_dtr = &uplcom_cfg_set_dtr,
224           .ucom_cfg_set_rts = &uplcom_cfg_set_rts,
225           .ucom_cfg_set_break = &uplcom_cfg_set_break,
226           .ucom_cfg_param = &uplcom_cfg_param,
227           .ucom_pre_param = &uplcom_pre_param,
228           .ucom_start_read = &uplcom_start_read,
229           .ucom_stop_read = &uplcom_stop_read,
230           .ucom_start_write = &uplcom_start_write,
231           .ucom_stop_write = &uplcom_stop_write,
232           .ucom_poll = &uplcom_poll,
233 };
234 
235 #define   UPLCOM_DEV(v,p)                                   \
236   { USB_VENDOR(USB_VENDOR_##v), USB_PRODUCT(USB_PRODUCT_##v##_##p) }
237 
238 static const STRUCT_USB_HOST_ID uplcom_devs[] = {
239           UPLCOM_DEV(ACERP, S81),                           /* BenQ S81 phone */
240           UPLCOM_DEV(ADLINK, ND6530),             /* ADLINK ND-6530 USB-Serial */
241           UPLCOM_DEV(ALCATEL, OT535),             /* Alcatel One Touch 535/735 */
242           UPLCOM_DEV(ALCOR, AU9720),              /* Alcor AU9720 USB 2.0-RS232 */
243           UPLCOM_DEV(ANCHOR, SERIAL),             /* Anchor Serial adapter */
244           UPLCOM_DEV(ATEN, UC232A),               /* PLANEX USB-RS232 URS-03 */
245           UPLCOM_DEV(BELKIN, F5U257),             /* Belkin F5U257 USB to Serial */
246           UPLCOM_DEV(COREGA, CGUSBRS232R),        /* Corega CG-USBRS232R */
247           UPLCOM_DEV(EPSON, CRESSI_EDY),                    /* Cressi Edy diving computer */
248           UPLCOM_DEV(EPSON, N2ITION3),            /* Zeagle N2iTion3 diving computer */
249           UPLCOM_DEV(ELECOM, UCSGT),              /* ELECOM UC-SGT Serial Adapter */
250           UPLCOM_DEV(ELECOM, UCSGT0),             /* ELECOM UC-SGT Serial Adapter */
251           UPLCOM_DEV(HAL, IMR001),                /* HAL Corporation Crossam2+USB */
252           UPLCOM_DEV(HP, LD220),                            /* HP LD220 POS Display */
253           UPLCOM_DEV(IODATA, USBRSAQ),            /* I/O DATA USB-RSAQ */
254           UPLCOM_DEV(IODATA, USBRSAQ5),           /* I/O DATA USB-RSAQ5 */
255           UPLCOM_DEV(ITEGNO, WM1080A),            /* iTegno WM1080A GSM/GFPRS modem */
256           UPLCOM_DEV(ITEGNO, WM2080A),            /* iTegno WM2080A CDMA modem */
257           UPLCOM_DEV(LEADTEK, 9531),              /* Leadtek 9531 GPS */
258           UPLCOM_DEV(MICROSOFT, 700WX),           /* Microsoft Palm 700WX */
259           UPLCOM_DEV(MOBILEACTION, MA620),        /* Mobile Action MA-620 Infrared Adapter */
260           UPLCOM_DEV(NETINDEX, WS002IN),                    /* Willcom W-S002IN */
261           UPLCOM_DEV(NOKIA2, CA42),               /* Nokia CA-42 cable */
262           UPLCOM_DEV(OTI, DKU5),                            /* OTI DKU-5 cable */
263           UPLCOM_DEV(PANASONIC, TYTP50P6S),       /* Panasonic TY-TP50P6-S flat screen */
264           UPLCOM_DEV(PLX, CA42),                            /* PLX CA-42 clone cable */
265           UPLCOM_DEV(PROLIFIC, ALLTRONIX_GPRS),   /* Alltronix ACM003U00 modem */
266           UPLCOM_DEV(PROLIFIC, ALDIGA_AL11U),     /* AlDiga AL-11U modem */
267           UPLCOM_DEV(PROLIFIC, DCU11),            /* DCU-11 Phone Cable */
268           UPLCOM_DEV(PROLIFIC, HCR331),           /* HCR331 Card Reader */
269           UPLCOM_DEV(PROLIFIC, MICROMAX_610U),    /* Micromax 610U modem */
270           UPLCOM_DEV(PROLIFIC, PHAROS),           /* Prolific Pharos */
271           UPLCOM_DEV(PROLIFIC, PL2303),           /* Generic adapter */
272           UPLCOM_DEV(PROLIFIC, RSAQ2),            /* I/O DATA USB-RSAQ2 */
273           UPLCOM_DEV(PROLIFIC, RSAQ3),            /* I/O DATA USB-RSAQ3 */
274           UPLCOM_DEV(PROLIFIC, UIC_MSR206),       /* UIC MSR206 Card Reader */
275           UPLCOM_DEV(PROLIFIC2, PL2303),                    /* Prolific adapter */
276           UPLCOM_DEV(RADIOSHACK, USBCABLE),       /* Radio Shack USB Adapter */
277           UPLCOM_DEV(RATOC, REXUSB60),            /* RATOC REX-USB60 */
278           UPLCOM_DEV(SAGEM, USBSERIAL),           /* Sagem USB-Serial Controller */
279           UPLCOM_DEV(SAMSUNG, I330),              /* Samsung I330 phone cradle */
280           UPLCOM_DEV(SANWA, KB_USB2),             /* Sanwa KB-USB2 Multimeter cable */
281           UPLCOM_DEV(SIEMENS3, EF81),             /* Siemens EF81 */
282           UPLCOM_DEV(SIEMENS3, SX1),              /* Siemens SX1 */
283           UPLCOM_DEV(SIEMENS3, X65),              /* Siemens X65 */
284           UPLCOM_DEV(SIEMENS3, X75),              /* Siemens X75 */
285           UPLCOM_DEV(SITECOM, SERIAL),            /* Sitecom USB to Serial */
286           UPLCOM_DEV(SMART, PL2303),              /* SMART Technologies USB to Serial */
287           UPLCOM_DEV(SONY, QN3),                            /* Sony QN3 phone cable */
288           UPLCOM_DEV(SONYERICSSON, DATAPILOT),    /* Sony Ericsson Datapilot */
289           UPLCOM_DEV(SONYERICSSON, DCU10),        /* Sony Ericsson DCU-10 Cable */
290           UPLCOM_DEV(SOURCENEXT, KEIKAI8),        /* SOURCENEXT KeikaiDenwa 8 */
291           UPLCOM_DEV(SOURCENEXT, KEIKAI8_CHG),    /* SOURCENEXT KeikaiDenwa 8 with charger */
292           UPLCOM_DEV(SPEEDDRAGON, MS3303H),       /* Speed Dragon USB-Serial */
293           UPLCOM_DEV(SYNTECH, CPT8001C),                    /* Syntech CPT-8001C Barcode scanner */
294           UPLCOM_DEV(TDK, UHA6400),               /* TDK USB-PHS Adapter UHA6400 */
295           UPLCOM_DEV(TDK, UPA9664),               /* TDK USB-PHS Adapter UPA9664 */
296           UPLCOM_DEV(TRIPPLITE, U209),            /* Tripp-Lite U209-000-R USB to Serial */
297           UPLCOM_DEV(YCCABLE, PL2303),            /* YC Cable USB-Serial */
298 };
299 #undef UPLCOM_DEV
300 
301 static device_method_t uplcom_methods[] = {
302           DEVMETHOD(device_probe, uplcom_probe),
303           DEVMETHOD(device_attach, uplcom_attach),
304           DEVMETHOD(device_detach, uplcom_detach),
305           DEVMETHOD_END
306 };
307 
308 static devclass_t uplcom_devclass;
309 
310 static driver_t uplcom_driver = {
311           .name = "uplcom",
312           .methods = uplcom_methods,
313           .size = sizeof(struct uplcom_softc),
314 };
315 
316 DRIVER_MODULE(uplcom, uhub, uplcom_driver, uplcom_devclass, NULL, NULL);
317 MODULE_DEPEND(uplcom, ucom, 1, 1, 1);
318 MODULE_DEPEND(uplcom, usb, 1, 1, 1);
319 MODULE_VERSION(uplcom, UPLCOM_MODVER);
320 
321 static int
uplcom_probe(device_t dev)322 uplcom_probe(device_t dev)
323 {
324           struct usb_attach_arg *uaa = device_get_ivars(dev);
325 
326           DPRINTFN(11, "\n");
327 
328           if (uaa->usb_mode != USB_MODE_HOST) {
329                     return (ENXIO);
330           }
331           if (uaa->info.bConfigIndex != UPLCOM_CONFIG_INDEX) {
332                     return (ENXIO);
333           }
334           if (uaa->info.bIfaceIndex != UPLCOM_IFACE_INDEX) {
335                     return (ENXIO);
336           }
337           return (usbd_lookup_id_by_uaa(uplcom_devs, sizeof(uplcom_devs), uaa));
338 }
339 
340 static int
uplcom_attach(device_t dev)341 uplcom_attach(device_t dev)
342 {
343           struct usb_attach_arg *uaa = device_get_ivars(dev);
344           struct uplcom_softc *sc = device_get_softc(dev);
345           struct usb_interface *iface;
346           struct usb_interface_descriptor *id;
347           struct usb_device_descriptor *dd;
348           int error;
349 
350           DPRINTFN(11, "\n");
351 
352           device_set_usb_desc(dev);
353           lockinit(&sc->sc_lock, "uplcom", 0, LK_CANRECURSE);
354 
355           DPRINTF("sc = %p\n", sc);
356 
357           sc->sc_udev = uaa->device;
358 
359           /* Determine the chip type.  This algorithm is taken from Linux. */
360           dd = usbd_get_device_descriptor(sc->sc_udev);
361           if (dd->bDeviceClass == 0x02)
362                     sc->sc_chiptype = TYPE_PL2303;
363           else if (dd->bMaxPacketSize == 0x40)
364                     sc->sc_chiptype = TYPE_PL2303HX;
365           else
366                     sc->sc_chiptype = TYPE_PL2303;
367 
368           DPRINTF("chiptype: %s\n",
369               (sc->sc_chiptype == TYPE_PL2303HX) ?
370               "2303X" : "2303");
371 
372           /*
373            * USB-RSAQ1 has two interface
374            *
375            *  USB-RSAQ1       | USB-RSAQ2
376            * -----------------+-----------------
377            * Interface 0      |Interface 0
378            *  Interrupt(0x81) | Interrupt(0x81)
379            * -----------------+ BulkIN(0x02)
380            * Interface 1          | BulkOUT(0x83)
381            *   BulkIN(0x02)   |
382            *   BulkOUT(0x83)  |
383            */
384 
385           sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
386           sc->sc_iface_index[1] = UPLCOM_IFACE_INDEX;
387 
388           iface = usbd_get_iface(uaa->device, UPLCOM_SECOND_IFACE_INDEX);
389           if (iface) {
390                     id = usbd_get_interface_descriptor(iface);
391                     if (id == NULL) {
392                               device_printf(dev, "no interface descriptor (2)\n");
393                               goto detach;
394                     }
395                     sc->sc_data_iface_no = id->bInterfaceNumber;
396                     sc->sc_iface_index[0] = UPLCOM_SECOND_IFACE_INDEX;
397                     usbd_set_parent_iface(uaa->device,
398                         UPLCOM_SECOND_IFACE_INDEX, uaa->info.bIfaceIndex);
399           } else {
400                     sc->sc_data_iface_no = sc->sc_ctrl_iface_no;
401                     sc->sc_iface_index[0] = UPLCOM_IFACE_INDEX;
402           }
403 
404           error = usbd_transfer_setup(uaa->device,
405               sc->sc_iface_index, sc->sc_xfer, uplcom_config_data,
406               UPLCOM_N_TRANSFER, sc, &sc->sc_lock);
407           if (error) {
408                     DPRINTF("one or more missing USB endpoints, "
409                         "error=%s\n", usbd_errstr(error));
410                     goto detach;
411           }
412           error = uplcom_reset(sc, uaa->device);
413           if (error) {
414                     device_printf(dev, "reset failed, error=%s\n",
415                         usbd_errstr(error));
416                     goto detach;
417           }
418           /* clear stall at first run */
419           lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
420           usbd_xfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
421           usbd_xfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
422           lockmgr(&sc->sc_lock, LK_RELEASE);
423 
424           error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
425               &uplcom_callback, &sc->sc_lock);
426           if (error) {
427                     goto detach;
428           }
429           /*
430            * do the initialization during attach so that the system does not
431            * sleep during open:
432            */
433           if (uplcom_pl2303_init(uaa->device, sc->sc_chiptype)) {
434                     device_printf(dev, "init failed\n");
435                     goto detach;
436           }
437           ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
438 
439           return (0);
440 
441 detach:
442           uplcom_detach(dev);
443           return (ENXIO);
444 }
445 
446 static int
uplcom_detach(device_t dev)447 uplcom_detach(device_t dev)
448 {
449           struct uplcom_softc *sc = device_get_softc(dev);
450 
451           DPRINTF("sc=%p\n", sc);
452 
453           ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
454           usbd_transfer_unsetup(sc->sc_xfer, UPLCOM_N_TRANSFER);
455           lockuninit(&sc->sc_lock);
456 
457           return (0);
458 }
459 
460 static usb_error_t
uplcom_reset(struct uplcom_softc * sc,struct usb_device * udev)461 uplcom_reset(struct uplcom_softc *sc, struct usb_device *udev)
462 {
463           struct usb_device_request req;
464 
465           req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
466           req.bRequest = UPLCOM_SET_REQUEST;
467           USETW(req.wValue, 0);
468           req.wIndex[0] = sc->sc_data_iface_no;
469           req.wIndex[1] = 0;
470           USETW(req.wLength, 0);
471 
472           return (usbd_do_request(udev, NULL, &req, NULL));
473 }
474 
475 static usb_error_t
uplcom_pl2303_do(struct usb_device * udev,int8_t req_type,uint8_t request,uint16_t value,uint16_t index,uint16_t length)476 uplcom_pl2303_do(struct usb_device *udev, int8_t req_type, uint8_t request,
477     uint16_t value, uint16_t index, uint16_t length)
478 {
479           struct usb_device_request req;
480           usb_error_t err;
481           uint8_t buf[4];
482 
483           req.bmRequestType = req_type;
484           req.bRequest = request;
485           USETW(req.wValue, value);
486           USETW(req.wIndex, index);
487           USETW(req.wLength, length);
488 
489           err = usbd_do_request(udev, NULL, &req, buf);
490           if (err) {
491                     DPRINTF("error=%s\n", usbd_errstr(err));
492                     return (1);
493           }
494           return (0);
495 }
496 
497 static int
uplcom_pl2303_init(struct usb_device * udev,uint8_t chiptype)498 uplcom_pl2303_init(struct usb_device *udev, uint8_t chiptype)
499 {
500           int err;
501 
502           if (uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1)
503               || uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0, 0)
504               || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1)
505               || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1)
506               || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1)
507               || uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1, 0)
508               || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1)
509               || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1)
510               || uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0, 1, 0)
511               || uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 1, 0, 0))
512                     return (EIO);
513 
514           if (chiptype == TYPE_PL2303HX)
515                     err = uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x44, 0);
516           else
517                     err = uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x24, 0);
518           if (err)
519                     return (EIO);
520 
521           if (uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 8, 0, 0)
522               || uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 9, 0, 0))
523                     return (EIO);
524           return (0);
525 }
526 
527 static void
uplcom_cfg_set_dtr(struct ucom_softc * ucom,uint8_t onoff)528 uplcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
529 {
530           struct uplcom_softc *sc = ucom->sc_parent;
531           struct usb_device_request req;
532 
533           DPRINTF("onoff = %d\n", onoff);
534 
535           if (onoff)
536                     sc->sc_line |= UCDC_LINE_DTR;
537           else
538                     sc->sc_line &= ~UCDC_LINE_DTR;
539 
540           req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
541           req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
542           USETW(req.wValue, sc->sc_line);
543           req.wIndex[0] = sc->sc_data_iface_no;
544           req.wIndex[1] = 0;
545           USETW(req.wLength, 0);
546 
547           ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
548               &req, NULL, 0, 1000);
549 }
550 
551 static void
uplcom_cfg_set_rts(struct ucom_softc * ucom,uint8_t onoff)552 uplcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
553 {
554           struct uplcom_softc *sc = ucom->sc_parent;
555           struct usb_device_request req;
556 
557           DPRINTF("onoff = %d\n", onoff);
558 
559           if (onoff)
560                     sc->sc_line |= UCDC_LINE_RTS;
561           else
562                     sc->sc_line &= ~UCDC_LINE_RTS;
563 
564           req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
565           req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
566           USETW(req.wValue, sc->sc_line);
567           req.wIndex[0] = sc->sc_data_iface_no;
568           req.wIndex[1] = 0;
569           USETW(req.wLength, 0);
570 
571           ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
572               &req, NULL, 0, 1000);
573 }
574 
575 static void
uplcom_cfg_set_break(struct ucom_softc * ucom,uint8_t onoff)576 uplcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
577 {
578           struct uplcom_softc *sc = ucom->sc_parent;
579           struct usb_device_request req;
580           uint16_t temp;
581 
582           DPRINTF("onoff = %d\n", onoff);
583 
584           temp = (onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
585 
586           req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
587           req.bRequest = UCDC_SEND_BREAK;
588           USETW(req.wValue, temp);
589           req.wIndex[0] = sc->sc_data_iface_no;
590           req.wIndex[1] = 0;
591           USETW(req.wLength, 0);
592 
593           ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
594               &req, NULL, 0, 1000);
595 }
596 
597 static const int32_t uplcom_rates[] = {
598           75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400,
599           19200, 28800, 38400, 57600, 115200,
600           /*
601            * Higher speeds are probably possible. PL2303X supports up to
602            * 6Mb and can set any rate
603            */
604           230400, 460800, 614400, 921600, 1228800
605 };
606 
607 static int
uplcom_pre_param(struct ucom_softc * ucom,struct termios * t)608 uplcom_pre_param(struct ucom_softc *ucom, struct termios *t)
609 {
610           struct uplcom_softc *sc = ucom->sc_parent;
611           uint8_t i;
612 
613           DPRINTF("\n");
614 
615           /**
616            * Check requested baud rate.
617            *
618            * The PL2303 can only set specific baud rates, up to 1228800 baud.
619            * The PL2303X can set any baud rate up to 6Mb.
620            * The PL2303HX rev. D can set any baud rate up to 12Mb.
621            *
622            * XXX: We currently cannot identify the PL2303HX rev. D, so treat
623            *      it the same as the PL2303X.
624            */
625           if (sc->sc_chiptype != TYPE_PL2303HX) {
626                     for (i = 0; i < NELEM(uplcom_rates); i++) {
627                               if (uplcom_rates[i] == t->c_ospeed)
628                                         return (0);
629                     }
630           } else {
631                     if (t->c_ospeed <= 6000000)
632                               return (0);
633           }
634 
635           DPRINTF("uplcom_param: bad baud rate (%d)\n", t->c_ospeed);
636           return (EIO);
637 }
638 
639 static void
uplcom_cfg_param(struct ucom_softc * ucom,struct termios * t)640 uplcom_cfg_param(struct ucom_softc *ucom, struct termios *t)
641 {
642           struct uplcom_softc *sc = ucom->sc_parent;
643           struct usb_cdc_line_state ls;
644           struct usb_device_request req;
645 
646           DPRINTF("sc = %p\n", sc);
647 
648           memset(&ls, 0, sizeof(ls));
649 
650           USETDW(ls.dwDTERate, t->c_ospeed);
651 
652           if (t->c_cflag & CSTOPB) {
653                     ls.bCharFormat = UCDC_STOP_BIT_2;
654           } else {
655                     ls.bCharFormat = UCDC_STOP_BIT_1;
656           }
657 
658           if (t->c_cflag & PARENB) {
659                     if (t->c_cflag & PARODD) {
660                               ls.bParityType = UCDC_PARITY_ODD;
661                     } else {
662                               ls.bParityType = UCDC_PARITY_EVEN;
663                     }
664           } else {
665                     ls.bParityType = UCDC_PARITY_NONE;
666           }
667 
668           switch (t->c_cflag & CSIZE) {
669           case CS5:
670                     ls.bDataBits = 5;
671                     break;
672           case CS6:
673                     ls.bDataBits = 6;
674                     break;
675           case CS7:
676                     ls.bDataBits = 7;
677                     break;
678           case CS8:
679                     ls.bDataBits = 8;
680                     break;
681           }
682 
683           DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
684               UGETDW(ls.dwDTERate), ls.bCharFormat,
685               ls.bParityType, ls.bDataBits);
686 
687           req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
688           req.bRequest = UCDC_SET_LINE_CODING;
689           USETW(req.wValue, 0);
690           req.wIndex[0] = sc->sc_data_iface_no;
691           req.wIndex[1] = 0;
692           USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
693 
694           ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
695               &req, &ls, 0, 1000);
696 
697           if (t->c_cflag & CRTSCTS) {
698 
699                     DPRINTF("crtscts = on\n");
700 
701                     req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
702                     req.bRequest = UPLCOM_SET_REQUEST;
703                     USETW(req.wValue, 0);
704                     if (sc->sc_chiptype == TYPE_PL2303HX)
705                               USETW(req.wIndex, UPLCOM_SET_CRTSCTS_PL2303X);
706                     else
707                               USETW(req.wIndex, UPLCOM_SET_CRTSCTS);
708                     USETW(req.wLength, 0);
709 
710                     ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
711                         &req, NULL, 0, 1000);
712           } else {
713                     req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
714                     req.bRequest = UPLCOM_SET_REQUEST;
715                     USETW(req.wValue, 0);
716                     USETW(req.wIndex, 0);
717                     USETW(req.wLength, 0);
718                     ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
719                         &req, NULL, 0, 1000);
720           }
721 }
722 
723 static void
uplcom_start_read(struct ucom_softc * ucom)724 uplcom_start_read(struct ucom_softc *ucom)
725 {
726           struct uplcom_softc *sc = ucom->sc_parent;
727 
728           /* start interrupt endpoint */
729           usbd_transfer_start(sc->sc_xfer[UPLCOM_INTR_DT_RD]);
730 
731           /* start read endpoint */
732           usbd_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
733 }
734 
735 static void
uplcom_stop_read(struct ucom_softc * ucom)736 uplcom_stop_read(struct ucom_softc *ucom)
737 {
738           struct uplcom_softc *sc = ucom->sc_parent;
739 
740           /* stop interrupt endpoint */
741           usbd_transfer_stop(sc->sc_xfer[UPLCOM_INTR_DT_RD]);
742 
743           /* stop read endpoint */
744           usbd_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
745 }
746 
747 static void
uplcom_start_write(struct ucom_softc * ucom)748 uplcom_start_write(struct ucom_softc *ucom)
749 {
750           struct uplcom_softc *sc = ucom->sc_parent;
751 
752           usbd_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
753 }
754 
755 static void
uplcom_stop_write(struct ucom_softc * ucom)756 uplcom_stop_write(struct ucom_softc *ucom)
757 {
758           struct uplcom_softc *sc = ucom->sc_parent;
759 
760           usbd_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
761 }
762 
763 static void
uplcom_cfg_get_status(struct ucom_softc * ucom,uint8_t * lsr,uint8_t * msr)764 uplcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
765 {
766           struct uplcom_softc *sc = ucom->sc_parent;
767 
768           DPRINTF("\n");
769 
770           *lsr = sc->sc_lsr;
771           *msr = sc->sc_msr;
772 }
773 
774 static void
uplcom_intr_callback(struct usb_xfer * xfer,usb_error_t error)775 uplcom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
776 {
777           struct uplcom_softc *sc = usbd_xfer_softc(xfer);
778           struct usb_page_cache *pc;
779           uint8_t buf[9];
780           int actlen;
781 
782           usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
783 
784           switch (USB_GET_STATE(xfer)) {
785           case USB_ST_TRANSFERRED:
786 
787                     DPRINTF("actlen = %u\n", actlen);
788 
789                     if (actlen >= 9) {
790 
791                               pc = usbd_xfer_get_frame(xfer, 0);
792                               usbd_copy_out(pc, 0, buf, sizeof(buf));
793 
794                               DPRINTF("status = 0x%02x\n", buf[8]);
795 
796                               sc->sc_lsr = 0;
797                               sc->sc_msr = 0;
798 
799                               if (buf[8] & RSAQ_STATUS_CTS) {
800                                         sc->sc_msr |= SER_CTS;
801                               }
802                               if (buf[8] & RSAQ_STATUS_DSR) {
803                                         sc->sc_msr |= SER_DSR;
804                               }
805                               if (buf[8] & RSAQ_STATUS_DCD) {
806                                         sc->sc_msr |= SER_DCD;
807                               }
808                               ucom_status_change(&sc->sc_ucom);
809                     }
810           case USB_ST_SETUP:
811 tr_setup:
812                     usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
813                     usbd_transfer_submit(xfer);
814                     return;
815 
816           default:                      /* Error */
817                     if (error != USB_ERR_CANCELLED) {
818                               /* try to clear stall first */
819                               usbd_xfer_set_stall(xfer);
820                               goto tr_setup;
821                     }
822                     return;
823           }
824 }
825 
826 static void
uplcom_write_callback(struct usb_xfer * xfer,usb_error_t error)827 uplcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
828 {
829           struct uplcom_softc *sc = usbd_xfer_softc(xfer);
830           struct usb_page_cache *pc;
831           uint32_t actlen;
832 
833           switch (USB_GET_STATE(xfer)) {
834           case USB_ST_SETUP:
835           case USB_ST_TRANSFERRED:
836 tr_setup:
837                     pc = usbd_xfer_get_frame(xfer, 0);
838                     if (ucom_get_data(&sc->sc_ucom, pc, 0,
839                         UPLCOM_BULK_BUF_SIZE, &actlen)) {
840 
841                               DPRINTF("actlen = %d\n", actlen);
842 
843                               usbd_xfer_set_frame_len(xfer, 0, actlen);
844                               usbd_transfer_submit(xfer);
845                     }
846                     return;
847 
848           default:                      /* Error */
849                     if (error != USB_ERR_CANCELLED) {
850                               /* try to clear stall first */
851                               usbd_xfer_set_stall(xfer);
852                               goto tr_setup;
853                     }
854                     return;
855           }
856 }
857 
858 static void
uplcom_read_callback(struct usb_xfer * xfer,usb_error_t error)859 uplcom_read_callback(struct usb_xfer *xfer, usb_error_t error)
860 {
861           struct uplcom_softc *sc = usbd_xfer_softc(xfer);
862           struct usb_page_cache *pc;
863           int actlen;
864 
865           usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
866 
867           switch (USB_GET_STATE(xfer)) {
868           case USB_ST_TRANSFERRED:
869                     pc = usbd_xfer_get_frame(xfer, 0);
870                     ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
871 
872           case USB_ST_SETUP:
873 tr_setup:
874                     usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
875                     usbd_transfer_submit(xfer);
876                     return;
877 
878           default:                      /* Error */
879                     if (error != USB_ERR_CANCELLED) {
880                               /* try to clear stall first */
881                               usbd_xfer_set_stall(xfer);
882                               goto tr_setup;
883                     }
884                     return;
885           }
886 }
887 
888 static void
uplcom_poll(struct ucom_softc * ucom)889 uplcom_poll(struct ucom_softc *ucom)
890 {
891           struct uplcom_softc *sc = ucom->sc_parent;
892           usbd_transfer_poll(sc->sc_xfer, UPLCOM_N_TRANSFER);
893 }
894