Add uticom(4) driver for Texas Instruments TUSB3410 USB to serial chips
[dragonfly.git] / sys / dev / usbmisc / uticom / uticom.c
1 /*
2  * Copyright (c) 2006-2007 Dmitry Komissaroff <dxi@mail.ru>.
3  * Copyright (c) 2007 Hasso Tepper <hasso@estpak.ee>.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $DragonFly: src/sys/dev/usbmisc/uticom/uticom.c,v 1.1 2007/11/07 08:31:08 hasso Exp $
27  */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/bus.h>
34 #include <sys/ioccom.h>
35 #include <sys/fcntl.h>
36 #include <sys/conf.h>
37 #include <sys/tty.h>
38 #include <sys/file.h>
39 #include <sys/select.h>
40 #include <sys/proc.h>
41 #include <sys/poll.h>
42 #include <sys/sysctl.h>
43 #include <sys/taskqueue.h>
44
45 #include <bus/usb/usb.h>
46 #include <bus/usb/usbcdc.h>
47 #include <bus/usb/usbdi.h>
48 #include <bus/usb/usbdi_util.h>
49 #include <bus/usb/usbdivar.h>
50 #include <bus/usb/usb_quirks.h>
51
52 #include "../ucom/ucomvar.h"
53
54 #include "uticom_fw3410.h"
55
56 SYSCTL_NODE(_hw_usb, OID_AUTO, uticom, CTLFLAG_RW, 0, "USB uticom");
57
58 #ifdef USB_DEBUG
59 static int      uticomdebug = 0;
60 SYSCTL_INT(_hw_usb_uticom, OID_AUTO, debug, CTLFLAG_RW, &uticomdebug, 0,
61            "uticom debug level");
62
63 #define DPRINTFN(n, x)  do { if (uticomdebug > (n)) kprintf x; } while (0)
64 #else
65 #define DPRINTFN(n, x)
66 #endif
67
68 #define DPRINTF(x) DPRINTFN(0, x)
69
70 #define UTICOM_CONFIG_INDEX     1
71 #define UTICOM_ACTIVE_INDEX     2
72
73 #define UTICOM_IFACE_INDEX      0
74
75 /*
76  * These are the maximum number of bytes transferred per frame.
77  * The output buffer size cannot be increased due to the size encoding.
78  */
79 #define UTICOM_IBUFSZ           64
80 #define UTICOM_OBUFSZ           64
81
82 #define UTICOM_FW_BUFSZ         16284
83
84 #define UTICOM_INTR_INTERVAL    100     /* ms */
85
86 #define UTICOM_RQ_LINE          0
87 /* Used to sync data0/1-toggle on reopen bulk pipe. */
88 #define UTICOM_RQ_SOF           1
89 #define UTICOM_RQ_SON           2
90
91 #define UTICOM_RQ_BAUD          3
92 #define UTICOM_RQ_LCR           4
93 #define UTICOM_RQ_FCR           5
94 #define UTICOM_RQ_RTS           6
95 #define UTICOM_RQ_DTR           7
96 #define UTICOM_RQ_BREAK         8
97 #define UTICOM_RQ_CRTSCTS       9
98
99 #define UTICOM_BRATE_REF        923077
100
101 #define UTICOM_SET_DATA_BITS(x) (x - 5)
102
103 #define UTICOM_STOP_BITS_1      0x00
104 #define UTICOM_STOP_BITS_2      0x40
105
106 #define UTICOM_PARITY_NONE      0x00
107 #define UTICOM_PARITY_ODD       0x08
108 #define UTICOM_PARITY_EVEN      0x18
109
110 #define UTICOM_LCR_OVR          0x1
111 #define UTICOM_LCR_PTE          0x2
112 #define UTICOM_LCR_FRE          0x4
113 #define UTICOM_LCR_BRK          0x8
114
115 #define UTICOM_MCR_CTS          0x1
116 #define UTICOM_MCR_DSR          0x2
117 #define UTICOM_MCR_CD           0x4
118 #define UTICOM_MCR_RI           0x8
119
120 /* Structures */
121 struct uticom_fw_header {
122         uint16_t      length;
123         uint8_t       checkSum;
124 } __attribute__((packed));
125
126 struct uticom_buf {
127         unsigned int            buf_size;
128         char                    *buf_buf;
129         char                    *buf_get;
130         char                    *buf_put;
131 };
132
133 struct  uticom_softc {
134         struct ucom_softc       sc_ucom;
135
136         int                     sc_iface_number; /* interface number */
137
138         usbd_interface_handle   sc_intr_iface;  /* interrupt interface */
139         int                     sc_intr_number; /* interrupt number */
140         usbd_pipe_handle        sc_intr_pipe;   /* interrupt pipe */
141         u_char                  *sc_intr_buf;   /* interrupt buffer */
142         int                     sc_isize;
143
144         u_char                  sc_dtr;         /* current DTR state */
145         u_char                  sc_rts;         /* current RTS state */
146         u_char                  sc_status;
147
148         u_char                  sc_lsr;         /* Local status register */
149         u_char                  sc_msr;         /* uticom status register */
150 };
151
152 static  usbd_status uticom_reset(struct uticom_softc *);
153 static  usbd_status uticom_set_crtscts(struct uticom_softc *);
154 static  void uticom_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
155
156 static  void uticom_set(void *, int, int, int);
157 static  void uticom_dtr(struct uticom_softc *, int);
158 static  void uticom_rts(struct uticom_softc *, int);
159 static  void uticom_break(struct uticom_softc *, int);
160 static  void uticom_get_status(void *, int, u_char *, u_char *);
161 #if 0 /* TODO */
162 static  int  uticom_ioctl(void *, int, u_long, caddr_t, int, usb_proc_ptr);
163 #endif
164 static  int  uticom_param(void *, int, struct termios *);
165 static  int  uticom_open(void *, int);
166 static  void uticom_close(void *, int);
167
168 static int uticom_download_fw(struct uticom_softc *sc, unsigned int pipeno,
169                               usbd_device_handle dev, unsigned char *firmware,
170                               unsigned int firmware_size);
171
172 struct ucom_callback uticom_callback = {
173         uticom_get_status,
174         uticom_set,
175         uticom_param,
176         NULL, /* uticom_ioctl, TODO */
177         uticom_open,
178         uticom_close,
179         NULL,
180         NULL
181 };
182
183 static const struct usb_devno uticom_devs [] = {
184         { USB_DEVICE(0x0451, 0x3410) }  /* TI TUSB3410 chip */
185 };
186
187 static device_probe_t uticom_match;
188 static device_attach_t uticom_attach;
189 static device_detach_t uticom_detach;
190
191 static device_method_t uticom_methods[] = {
192         DEVMETHOD(device_probe, uticom_match),
193         DEVMETHOD(device_attach, uticom_attach),
194         DEVMETHOD(device_detach, uticom_detach),
195         { 0, 0 }
196 };
197
198 static driver_t uticom_driver = {
199         "ucom",
200         uticom_methods,
201         sizeof (struct uticom_softc)
202 };
203
204 DRIVER_MODULE(uticom, uhub, uticom_driver, ucom_devclass, usbd_driver_load, 0);
205 MODULE_DEPEND(uticom, usb, 1, 1, 1);
206 MODULE_DEPEND(uticom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
207 MODULE_VERSION(uticom, 1);
208
209 /* Sticky DSR level sysctl handling. */
210 static int uticomstickdsr = 0;
211 static int
212 sysctl_hw_usb_uticom_stickdsr(SYSCTL_HANDLER_ARGS)
213 {
214         int err, val;
215
216         val = uticomstickdsr;
217         err = sysctl_handle_int(oidp, &val, sizeof(val), req);
218         if (err != 0 || req->newptr == NULL)
219                 return (err);
220         if (val == 0 || val == 1)
221                 uticomstickdsr = val;
222         else
223                 err = EINVAL;
224
225         return (err);
226 }
227 SYSCTL_PROC(_hw_usb_uticom, OID_AUTO, stickdsr, CTLTYPE_INT | CTLFLAG_RW,
228             0, sizeof(int), sysctl_hw_usb_uticom_stickdsr,
229             "I", "uticom sticky dsr level");
230
231 static int
232 uticom_match(device_t self)
233 {
234         struct usb_attach_arg *uaa = device_get_ivars(self);
235
236         if (uaa->iface != NULL)
237                 return (UMATCH_NONE);
238
239         return (usb_lookup(uticom_devs, uaa->vendor, uaa->product) != NULL ?
240                 UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
241 }
242
243 static int
244 uticom_attach(device_t self)
245 {
246         struct uticom_softc *sc = device_get_softc(self);
247         struct usb_attach_arg *uaa = device_get_ivars(self);
248
249         usbd_device_handle dev = uaa->device;
250         struct ucom_softc *ucom;
251         usb_config_descriptor_t *cdesc;
252         usb_interface_descriptor_t *id;
253         usb_endpoint_descriptor_t *ed;
254         usbd_status err;
255         int status, i;
256         usb_device_descriptor_t *dd;
257
258         ucom = &sc->sc_ucom;
259         bzero(sc, sizeof (struct uticom_softc));
260         ucom->sc_dev = self;
261         ucom->sc_udev = dev;
262         ucom->sc_iface = uaa->iface;
263
264         /* Initialize endpoints. */
265         ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
266         sc->sc_intr_number = -1;
267         sc->sc_intr_pipe = NULL;
268
269         dd = usbd_get_device_descriptor(dev);
270         DPRINTF(("%s: uticom_attach: num of configurations %d\n",
271                  device_get_nameunit(self), dd->bNumConfigurations));
272
273         /* The device without firmware has single configuration with single
274          * bulk out interface. */
275         if (dd->bNumConfigurations > 1)
276                 goto fwload_done;
277                 
278         /* Loading firmware. */
279         DPRINTF(("%s: uticom_attach: starting loading firmware\n",
280                  device_get_nameunit(self)));
281
282         err = usbd_set_config_index(dev, UTICOM_CONFIG_INDEX, 1);
283         if (err) {
284                 device_printf(self, "failed to set configuration: %s\n",
285                               usbd_errstr(err));
286                 ucom->sc_dying = 1;
287                 return ENXIO;
288         }
289
290         /* Get the config descriptor. */
291         cdesc = usbd_get_config_descriptor(ucom->sc_udev);
292
293         if (cdesc == NULL) {
294                 device_printf(self, "failed to get configuration descriptor\n");
295                 ucom->sc_dying = 1;
296                 return ENXIO;
297         }
298
299         err = usbd_device2interface_handle(dev, UTICOM_IFACE_INDEX,
300                                            &ucom->sc_iface);
301         if (err) {
302                 device_printf(self, "failed to get interface: %s\n",
303                               usbd_errstr(err));
304                 ucom->sc_dying = 1;
305                 return ENXIO;
306         }
307
308         /* Find the bulk out interface used to upload firmware. */
309         id = usbd_get_interface_descriptor(ucom->sc_iface);
310         sc->sc_iface_number = id->bInterfaceNumber;
311
312         for (i = 0; i < id->bNumEndpoints; i++) {
313                 ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
314                 if (ed == NULL) {
315                         device_printf(self,
316                                       "no endpoint descriptor for %d\n", i);
317                         ucom->sc_dying = 1;
318                         return ENXIO;
319                 }
320
321                 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
322                     UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
323                         ucom->sc_bulkout_no = ed->bEndpointAddress;
324                         DPRINTF(("%s: uticom_attach: data bulk out num: %d\n",
325                                  device_get_nameunit(self),
326                                  ed->bEndpointAddress));
327                 }
328
329                 if (ucom->sc_bulkout_no == -1) {
330                         device_printf(self, "could not find data bulk out\n");
331                         ucom->sc_dying = 1;
332                         return ENXIO;
333                 }
334         }
335
336         status = uticom_download_fw(sc, ucom->sc_bulkout_no, dev,
337                                     uticom_fw_3410, sizeof(uticom_fw_3410));
338
339         if (status) {
340                 device_printf(self, "firmware download failed\n");
341                 ucom->sc_dying = 1;
342                 return ENXIO;
343         } else {
344                 device_printf(self, "firmware download succeeded\n");
345         }
346                 
347         status = usbd_reload_device_desc(dev);
348         if (status) {
349                 device_printf(self, "error reloading device descriptor\n");
350                 ucom->sc_dying = 1;
351                 return ENXIO;
352         }
353
354 fwload_done:
355         dd = usbd_get_device_descriptor(dev);
356         DPRINTF(("%s: uticom_attach: num of configurations %d\n",
357                  device_get_nameunit(self), dd->bNumConfigurations));
358
359         err = usbd_set_config_index(dev, UTICOM_ACTIVE_INDEX, 1);
360         if (err) {
361                 device_printf(self, "failed to set configuration: %s\n",
362                               usbd_errstr(err));
363                 ucom->sc_dying = 1;
364                 return ENXIO;
365         }
366
367         /* Get the config descriptor. */
368         cdesc = usbd_get_config_descriptor(ucom->sc_udev);
369         if (cdesc == NULL) {
370                 device_printf(self, "failed to get configuration descriptor\n");
371                 ucom->sc_dying = 1;
372                 return ENXIO;
373         }
374
375         /* Get the interface (XXX: multiport chips are not supported yet). */
376         err = usbd_device2interface_handle(dev, UTICOM_IFACE_INDEX,
377                                            &ucom->sc_iface);
378         if (err) {
379                 device_printf(self, "failed to get interface: %s\n",
380                               usbd_errstr(err));
381                 ucom->sc_dying = 1;
382                 return ENXIO;
383         }
384
385         /* Find the interrupt endpoints. */
386         id = usbd_get_interface_descriptor(ucom->sc_iface);
387         sc->sc_iface_number = id->bInterfaceNumber;
388
389         for (i = 0; i < id->bNumEndpoints; i++) {
390                 ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
391                 if (ed == NULL) {
392                         device_printf(self,
393                                       "no endpoint descriptor for %d\n", i);
394                         ucom->sc_dying = 1;
395                         return ENXIO;
396                 }
397
398                 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
399                     UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
400                         sc->sc_intr_number = ed->bEndpointAddress;
401                         sc->sc_isize = UGETW(ed->wMaxPacketSize);
402                 }
403         }
404
405         if (sc->sc_intr_number == -1) {
406                 device_printf(self, "could not find interrupt in\n");
407                 ucom->sc_dying = 1;
408                 return ENXIO;
409         }
410
411         /* Keep interface for interrupt. */
412         sc->sc_intr_iface = ucom->sc_iface;
413
414         /* Find the bulk{in,out} endpoints. */
415         id = usbd_get_interface_descriptor(ucom->sc_iface);
416         sc->sc_iface_number = id->bInterfaceNumber;
417
418         for (i = 0; i < id->bNumEndpoints; i++) {
419                 ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
420                 if (ed == NULL) {
421                         device_printf(self,
422                                       "no endpoint descriptor for %d\n", i);
423                         ucom->sc_dying = 1;
424                         return ENXIO;
425                 }
426
427                 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
428                     UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
429                         ucom->sc_bulkin_no = ed->bEndpointAddress;
430                 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
431                     UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
432                         ucom->sc_bulkout_no = ed->bEndpointAddress;
433                 }
434         }
435
436         if (ucom->sc_bulkin_no == -1) {
437                 device_printf(self, "could not find data bulk in\n");
438                 ucom->sc_dying = 1;
439                 return ENXIO;
440         }
441
442         if (ucom->sc_bulkout_no == -1) {
443                 device_printf(self, "could not find data bulk out\n");
444                 ucom->sc_dying = 1;
445                 return ENXIO;
446         }
447
448         sc->sc_dtr = sc->sc_rts = -1;
449         ucom->sc_parent = sc;
450         ucom->sc_portno = UCOM_UNK_PORTNO;
451         ucom->sc_ibufsize = UTICOM_IBUFSZ;
452         ucom->sc_obufsize = UTICOM_OBUFSZ;
453         ucom->sc_ibufsizepad = UTICOM_IBUFSZ;
454         ucom->sc_opkthdrlen = 0;
455         ucom->sc_callback = &uticom_callback;
456
457         err = uticom_reset(sc);
458         if (err) {
459                 device_printf(self, "reset failed: %s\n", usbd_errstr(err));
460                 ucom->sc_dying = 1;
461                 return ENXIO;
462         }
463
464         DPRINTF(("%s: uticom_attach: in = 0x%x, out = 0x%x, intr = 0x%x\n",
465                  device_get_nameunit(self), ucom->sc_bulkin_no,
466                  ucom->sc_bulkout_no, sc->sc_intr_number));
467
468         ucom_attach(&sc->sc_ucom);
469         return 0;
470 }
471
472 static int
473 uticom_detach(device_t self)
474 {
475         struct uticom_softc *sc = device_get_softc(self);
476
477         DPRINTF(("%s: uticom_detach: sc = %p\n",
478                  device_get_nameunit(self), sc));
479
480         if (sc->sc_intr_pipe != NULL) {
481                 usbd_abort_pipe(sc->sc_intr_pipe);
482                 usbd_close_pipe(sc->sc_intr_pipe);
483                 kfree(sc->sc_intr_buf, M_USBDEV);
484                 sc->sc_intr_pipe = NULL;
485         }
486
487         sc->sc_ucom.sc_dying = 1;
488         return (ucom_detach(&sc->sc_ucom));
489 }
490
491 static usbd_status
492 uticom_reset(struct uticom_softc *sc)
493 {
494         usb_device_request_t req;
495         usbd_status err;
496         device_t dev = sc->sc_ucom.sc_dev;
497
498         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
499         req.bRequest = UTICOM_RQ_SON;
500         USETW(req.wValue, 0);
501         USETW(req.wIndex, 0);
502         USETW(req.wLength, 0);
503
504         err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
505         if (err){
506                 device_printf(dev, "uticom_reset: %s\n", usbd_errstr(err));
507                 return (EIO);
508         }
509
510         DPRINTF(("%s: uticom_reset: done\n", device_get_nameunit(dev)));
511         return (0);
512 }
513
514 static void
515 uticom_set(void *addr, int portno, int reg, int onoff)
516 {
517         struct uticom_softc *sc = addr;
518
519         switch (reg) {
520         case UCOM_SET_DTR:
521                 uticom_dtr(sc, onoff);
522                 break;
523         case UCOM_SET_RTS:
524                 uticom_rts(sc, onoff);
525                 break;
526         case UCOM_SET_BREAK:
527                 uticom_break(sc, onoff);
528                 break;
529         default:
530                 break;
531         }
532 }
533
534 static void
535 uticom_dtr(struct uticom_softc *sc, int onoff)
536 {
537         usb_device_request_t req;
538         usbd_status err;
539         device_t dev = sc->sc_ucom.sc_dev;
540
541         DPRINTF(("%s: uticom_dtr: onoff = %d\n", device_get_nameunit(dev),
542                  onoff));
543
544         if (sc->sc_dtr == onoff)
545                 return;
546         sc->sc_dtr = onoff;
547
548         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
549         req.bRequest = UTICOM_RQ_DTR;
550         USETW(req.wValue, sc->sc_dtr ? UCDC_LINE_DTR : 0);
551         USETW(req.wIndex, 0);
552         USETW(req.wLength, 0);
553
554         err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
555         if (err)
556                 device_printf(dev, "uticom_dtr: %s\n", usbd_errstr(err));
557 }
558
559 static void
560 uticom_rts(struct uticom_softc *sc, int onoff)
561 {
562         usb_device_request_t req;
563         usbd_status err;
564         device_t dev = sc->sc_ucom.sc_dev;
565
566         DPRINTF(("%s: uticom_rts: onoff = %d\n", device_get_nameunit(dev),
567                  onoff));
568
569         if (sc->sc_rts == onoff) 
570                 return;
571         sc->sc_rts = onoff;
572         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
573         req.bRequest = UTICOM_RQ_RTS;
574         USETW(req.wValue, sc->sc_rts ? UCDC_LINE_RTS : 0);
575         USETW(req.wIndex, 0);
576         USETW(req.wLength, 0);
577
578         err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
579         if (err)
580                 device_printf(dev, "uticom_rts: %s\n", usbd_errstr(err));
581 }
582
583 static void
584 uticom_break(struct uticom_softc *sc, int onoff)
585 {
586         usb_device_request_t req;
587         usbd_status err;
588         device_t dev = sc->sc_ucom.sc_dev;
589
590         DPRINTF(("%s: uticom_break: onoff = %d\n", device_get_nameunit(dev),
591                  onoff));
592
593         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
594         req.bRequest = UTICOM_RQ_BREAK;
595         USETW(req.wValue, onoff ? 1 : 0);
596         USETW(req.wIndex, 0);
597         USETW(req.wLength, 0);
598
599         err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
600         if (err)
601                 device_printf(dev, "uticom_break: %s\n", usbd_errstr(err));
602 }
603
604 static usbd_status
605 uticom_set_crtscts(struct uticom_softc *sc)
606 {
607         usb_device_request_t req;
608         usbd_status err;
609         device_t dev = sc->sc_ucom.sc_dev;
610
611         DPRINTF(("%s: uticom_set_crtscts: on\n", device_get_nameunit(dev)));
612
613         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
614         req.bRequest = UTICOM_RQ_CRTSCTS;
615         USETW(req.wValue, 1);
616         USETW(req.wIndex, 0);
617         USETW(req.wLength, 0);
618
619         err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
620         if (err) {
621                 device_printf(dev, "uticom_set_crtscts: %s\n",
622                               usbd_errstr(err));
623                 return (err);
624         }
625
626         return (USBD_NORMAL_COMPLETION);
627 }
628
629 static int
630 uticom_param(void *vsc, int portno, struct termios *t)
631 {
632         struct uticom_softc *sc = (struct uticom_softc *)vsc;
633         device_t dev = sc->sc_ucom.sc_dev;
634         usb_device_request_t req;
635         usbd_status err;
636         uint8_t data;
637
638         DPRINTF(("%s: uticom_param\n", device_get_nameunit(dev)));
639
640         switch (t->c_ospeed) {
641         case 1200:
642         case 2400:
643         case 4800:
644         case 7200:
645         case 9600:
646         case 14400:
647         case 19200:
648         case 38400:
649         case 57600:
650         case 115200:
651         case 230400:
652         case 460800:
653         case 921600:
654                 req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
655                 req.bRequest = UTICOM_RQ_BAUD;
656                 USETW(req.wValue, (UTICOM_BRATE_REF / t->c_ospeed));
657                 USETW(req.wIndex, 0);
658                 USETW(req.wLength, 0);
659
660                 err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
661                 if (err) {
662                         device_printf(dev, "uticom_param: %s\n",
663                                       usbd_errstr(err));
664                         return (EIO);
665                 }
666                 break;
667         default:
668                 device_printf(dev, "uticom_param: unsupported baud rate %d\n",
669                               t->c_ospeed);
670                 return (EINVAL);
671         }
672
673         switch (ISSET(t->c_cflag, CSIZE)) {
674         case CS5:
675                 data = UTICOM_SET_DATA_BITS(5);
676                 break;
677         case CS6:
678                 data = UTICOM_SET_DATA_BITS(6);
679                 break;
680         case CS7:
681                 data = UTICOM_SET_DATA_BITS(7);
682                 break;
683         case CS8:
684                 data = UTICOM_SET_DATA_BITS(8);
685                 break;
686         default:
687                 return (EIO);
688         }
689
690         if (ISSET(t->c_cflag, CSTOPB))
691                 data |= UTICOM_STOP_BITS_2;
692         else
693                 data |= UTICOM_STOP_BITS_1;
694
695         if (ISSET(t->c_cflag, PARENB)) {
696                 if (ISSET(t->c_cflag, PARODD))
697                         data |= UTICOM_PARITY_ODD;
698                 else
699                         data |= UTICOM_PARITY_EVEN;
700         } else
701                 data |= UTICOM_PARITY_NONE;
702
703         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
704         req.bRequest = UTICOM_RQ_LCR;
705         USETW(req.wIndex, 0);
706         USETW(req.wLength, 0);
707         USETW(req.wValue, data);
708
709         err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
710         if (err) {
711                 device_printf(dev, "uticom_param: %s\n", usbd_errstr(err));
712                 return (err);
713         }
714
715         if (ISSET(t->c_cflag, CRTSCTS)) {
716                 err = uticom_set_crtscts(sc);
717                 if (err)
718                         return (EIO);
719         } 
720
721         return (0);
722 }
723
724 static int
725 uticom_open(void *addr, int portno)
726 {
727         struct uticom_softc *sc = addr;
728         device_t dev = sc->sc_ucom.sc_dev;
729         usbd_status err;
730
731         if (sc->sc_ucom.sc_dying)
732                 return (ENXIO);
733
734         DPRINTF(("%s: uticom_open\n", device_get_nameunit(dev)));
735
736         sc->sc_status = 0; /* clear status bit */
737
738         if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
739                 sc->sc_intr_buf = kmalloc(sc->sc_isize, M_USBDEV, M_WAITOK);
740                 err = usbd_open_pipe_intr(sc->sc_intr_iface, sc->sc_intr_number,
741                                           USBD_SHORT_XFER_OK, &sc->sc_intr_pipe,
742                                           sc, sc->sc_intr_buf, sc->sc_isize,
743                                           uticom_intr, UTICOM_INTR_INTERVAL);
744                 if (err) {
745                         device_printf(dev, "cannot open interrupt pipe "
746                                       "(addr %d)\n", sc->sc_intr_number);
747                         return (EIO);
748                 }
749         }
750
751         DPRINTF(("%s: uticom_open: port opened\n", device_get_nameunit(dev)));
752         return (0);
753 }
754
755 static void
756 uticom_close(void *addr, int portno)
757 {
758         struct uticom_softc *sc = addr;
759         device_t dev = sc->sc_ucom.sc_dev;
760         usb_device_request_t req;
761         usbd_status err;
762
763         if (sc->sc_ucom.sc_dying)
764                 return;
765
766         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
767         req.bRequest = UTICOM_RQ_SON;
768         USETW(req.wValue, 0);
769         USETW(req.wIndex, 0);
770         USETW(req.wLength, 0);
771
772         /* Try to reset UART part of chip. */
773         err = usbd_do_request(sc->sc_ucom.sc_udev, &req, NULL);
774         if (err) {
775                 device_printf(dev, "uticom_close: %s\n", usbd_errstr(err));
776                 return;
777         }
778
779         DPRINTF(("%s: uticom_close: close\n",
780                  device_get_nameunit(sc->sc_ucom.sc_dev)));
781
782         if (sc->sc_intr_pipe != NULL) {
783                 err = usbd_abort_pipe(sc->sc_intr_pipe);
784                 if (err)
785                         device_printf(dev, "abort interrupt pipe failed: %s\n",
786                                       usbd_errstr(err));
787                 err = usbd_close_pipe(sc->sc_intr_pipe);
788                 if (err)
789                         device_printf(dev, "close interrupt pipe failed: %s\n",
790                                       usbd_errstr(err));
791                 kfree(sc->sc_intr_buf, M_USBDEV);
792                 sc->sc_intr_pipe = NULL;
793         }
794 }
795
796 static void
797 uticom_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
798 {
799         struct uticom_softc *sc = priv;
800         u_char *buf = sc->sc_intr_buf;
801
802         if (sc->sc_ucom.sc_dying)
803                 return;
804
805         if (status != USBD_NORMAL_COMPLETION) {
806                 if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
807                         DPRINTF(("%s: uticom_intr: int status: %s\n",
808                                  device_get_nameunit(sc->sc_ucom.sc_dev),
809                                  usbd_errstr(status)));
810                         return;
811                 }
812
813                 DPRINTF(("%s: uticom_intr: abnormal status: %s\n",
814                         device_get_nameunit(sc->sc_ucom.sc_dev),
815                         usbd_errstr(status)));
816                 usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
817                 return;
818         }
819
820         if (!xfer->actlen)
821                 return;
822
823         DPRINTF(("%s: xfer_length = %d\n",
824                  device_get_nameunit(sc->sc_ucom.sc_dev), xfer->actlen));
825
826         sc->sc_lsr = sc->sc_msr = 0;
827
828         if (buf[0] == 0) {
829                 /* msr registers */
830                 if (buf[1] & UTICOM_MCR_CTS)
831                         sc->sc_msr |= UMSR_CTS;
832                 if (buf[1] & UTICOM_MCR_DSR)
833                         sc->sc_msr |= UMSR_DSR;
834                 if (buf[1] & UTICOM_MCR_CD)
835                         sc->sc_msr |= UMSR_DCD;         
836                 if (buf[1] & UTICOM_MCR_RI)
837                         sc->sc_msr |= UMSR_RI;          
838         } else {
839                 /* lsr registers */
840                 if (buf[0] & UTICOM_LCR_OVR)
841                         sc->sc_lsr |= ULSR_OE;
842                 if (buf[0] & UTICOM_LCR_PTE)
843                         sc->sc_lsr |= ULSR_PE;
844                 if (buf[0] & UTICOM_LCR_FRE)
845                         sc->sc_lsr |= ULSR_FE;
846                 if (buf[0] & UTICOM_LCR_BRK)
847                         sc->sc_lsr |= ULSR_BI;  
848         }
849   
850         if (uticomstickdsr)
851                 sc->sc_msr |= UMSR_DSR;
852
853         ucom_status_change(&sc->sc_ucom);
854 }
855
856 static void
857 uticom_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
858 {
859 #if 0 /* TODO */
860         struct uticom_softc *sc = addr;
861
862         DPRINTF(("uticom_get_status:\n"));
863
864         if (lsr != NULL)
865                 *lsr = sc->sc_lsr;
866         if (msr != NULL)
867                 *msr = sc->sc_msr;
868 #endif
869         return;
870 }
871
872 #if 0 /* TODO */
873 static int
874 uticom_ioctl(void *addr, int portno, u_long cmd, caddr_t data, int flag,
875              usb_proc_ptr p)
876 {
877         struct uticom_softc *sc = addr;
878         int error = 0;
879
880         if (sc->sc_ucom.sc_dying)
881                 return (EIO);
882
883         DPRINTF(("uticom_ioctl: cmd = 0x%08lx\n", cmd));
884
885         switch (cmd) {
886         case TIOCNOTTY:
887         case TIOCMGET:
888         case TIOCMSET:
889         case USB_GET_CM_OVER_DATA:
890         case USB_SET_CM_OVER_DATA:
891                 break;
892
893         default:
894                 DPRINTF(("uticom_ioctl: unknown\n"));
895                 error = ENOTTY;
896                 break;
897         }
898
899         return (error);
900 }
901 #endif
902
903 static int uticom_download_fw(struct uticom_softc *sc, unsigned int pipeno,
904                               usbd_device_handle dev, unsigned char *firmware,
905                               unsigned int firmware_size)
906 {
907         int buffer_size;
908         int pos;
909         uint8_t cs = 0;
910         uint8_t *buffer;
911         usbd_status err;
912         struct uticom_fw_header *header;
913
914         buffer_size = UTICOM_FW_BUFSZ + sizeof(struct uticom_fw_header);
915         buffer = kmalloc(buffer_size, M_USBDEV, M_WAITOK);
916
917         if (!buffer) {
918                 device_printf(sc->sc_ucom.sc_dev,
919                               "uticom_download_fw: out of memory\n");
920                 return ENOMEM;
921         }
922
923         memcpy(buffer, firmware, firmware_size);
924         memset(buffer + firmware_size, 0xff, buffer_size - firmware_size);
925
926         for (pos = sizeof(struct uticom_fw_header); pos < buffer_size; pos++)
927                 cs = (uint8_t)(cs + buffer[pos]);
928
929         header = (struct uticom_fw_header*)buffer;
930         header->length = (uint16_t)(buffer_size -
931                                      sizeof(struct uticom_fw_header));
932         header->checkSum = cs;
933
934         DPRINTF(("%s: downloading firmware ...\n",
935                  device_get_nameunit(sc->sc_ucom.sc_dev)));
936
937         usbd_xfer_handle oxfer = 0;
938         u_char *obuf;
939         usbd_status error = 0;
940         usbd_pipe_handle pipe;
941
942         err = usbd_open_pipe(sc->sc_ucom.sc_iface, pipeno, USBD_EXCLUSIVE_USE,
943                              &pipe);
944         if (err) {
945                 device_printf(sc->sc_ucom.sc_dev, "open bulk out error "
946                               "(addr %d): %s\n", pipeno, usbd_errstr(err));
947                 error = EIO;
948                 goto finish;
949         }
950
951         oxfer = usbd_alloc_xfer(dev);
952         if (oxfer == NULL) {
953                 error = ENOMEM;
954                 goto finish;
955         }
956
957         obuf = usbd_alloc_buffer(oxfer, buffer_size);
958         if (obuf == NULL) {
959                 error = ENOMEM;
960                 goto finish;
961         }
962
963         memcpy(obuf, buffer, buffer_size);
964
965         usbd_setup_xfer(oxfer, pipe, (usbd_private_handle)sc, obuf, buffer_size,
966                         USBD_NO_COPY || USBD_SYNCHRONOUS, USBD_NO_TIMEOUT, 0);
967         err = usbd_sync_transfer(oxfer);
968
969         if (err != USBD_NORMAL_COMPLETION)
970                 device_printf(sc->sc_ucom.sc_dev, "uticom_download_fw: "
971                               "error: %s\n", usbd_errstr(err));
972
973 finish:
974         usbd_free_buffer(oxfer);
975         usbd_free_xfer(oxfer);
976         oxfer = NULL;
977         usbd_abort_pipe(pipe);
978         usbd_close_pipe(pipe);
979         kfree(buffer, M_USBDEV);
980
981         return err;     
982 }