a280ec01211120c2e00e07815d8a9d7c2b4ecd21
[dragonfly.git] / sys / bus / u4b / serial / uslcom.c
1 /*      $OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $ */
2
3 /*
4  * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <sys/stdint.h>
20 #include <sys/param.h>
21 #include <sys/queue.h>
22 #include <sys/types.h>
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/bus.h>
26 #include <sys/module.h>
27 #include <sys/lock.h>
28 #include <sys/condvar.h>
29 #include <sys/sysctl.h>
30 #include <sys/unistd.h>
31 #include <sys/callout.h>
32 #include <sys/malloc.h>
33 #include <sys/priv.h>
34 #include <sys/serial.h>
35
36 #include <bus/u4b/usb.h>
37 #include <bus/u4b/usbdi.h>
38 #include <bus/u4b/usbdi_util.h>
39 #include <bus/u4b/usb_ioctl.h>
40 #include "usbdevs.h"
41
42 #define USB_DEBUG_VAR uslcom_debug
43 #include <bus/u4b/usb_debug.h>
44 #include <bus/u4b/usb_process.h>
45
46 #include <bus/u4b/serial/usb_serial.h>
47
48 #ifdef USB_DEBUG
49 static int uslcom_debug = 0;
50
51 static SYSCTL_NODE(_hw_usb, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom");
52 SYSCTL_INT(_hw_usb_uslcom, OID_AUTO, debug, CTLFLAG_RW,
53     &uslcom_debug, 0, "Debug level");
54 #endif
55
56 #define USLCOM_BULK_BUF_SIZE            1024
57 #define USLCOM_CONFIG_INDEX     0
58 #define USLCOM_IFACE_INDEX      0
59
60 #define USLCOM_SET_DATA_BITS(x) ((x) << 8)
61
62 /* Request types */
63 #define USLCOM_WRITE            0x41
64 #define USLCOM_READ             0xc1
65
66 /* Request codes */
67 #define USLCOM_UART             0x00
68 #define USLCOM_BAUD_RATE        0x01    
69 #define USLCOM_DATA             0x03
70 #define USLCOM_BREAK            0x05
71 #define USLCOM_CTRL             0x07
72 #define USLCOM_RCTRL            0x08
73 #define USLCOM_SET_FLOWCTRL     0x13
74 #define USLCOM_VENDOR_SPECIFIC  0xff
75
76 /* USLCOM_UART values */
77 #define USLCOM_UART_DISABLE     0x00
78 #define USLCOM_UART_ENABLE      0x01
79
80 /* USLCOM_CTRL/USLCOM_RCTRL values */
81 #define USLCOM_CTRL_DTR_ON      0x0001  
82 #define USLCOM_CTRL_DTR_SET     0x0100
83 #define USLCOM_CTRL_RTS_ON      0x0002
84 #define USLCOM_CTRL_RTS_SET     0x0200
85 #define USLCOM_CTRL_CTS         0x0010
86 #define USLCOM_CTRL_DSR         0x0020
87 #define USLCOM_CTRL_RI          0x0040
88 #define USLCOM_CTRL_DCD         0x0080
89
90 /* USLCOM_BAUD_RATE values */
91 #define USLCOM_BAUD_REF         0x384000
92
93 /* USLCOM_DATA values */
94 #define USLCOM_STOP_BITS_1      0x00
95 #define USLCOM_STOP_BITS_2      0x02
96 #define USLCOM_PARITY_NONE      0x00
97 #define USLCOM_PARITY_ODD       0x10
98 #define USLCOM_PARITY_EVEN      0x20
99
100 #define USLCOM_PORT_NO          0x0000
101
102 /* USLCOM_BREAK values */
103 #define USLCOM_BREAK_OFF        0x00
104 #define USLCOM_BREAK_ON         0x01
105
106 /* USLCOM_SET_FLOWCTRL values - 1st word */
107 #define USLCOM_FLOW_DTR_ON      0x00000001 /* DTR static active */
108 #define USLCOM_FLOW_CTS_HS      0x00000008 /* CTS handshake */
109 /* USLCOM_SET_FLOWCTRL values - 2nd word */
110 #define USLCOM_FLOW_RTS_ON      0x00000040 /* RTS static active */
111 #define USLCOM_FLOW_RTS_HS      0x00000080 /* RTS handshake */
112
113 /* USLCOM_VENDOR_SPECIFIC values */
114 #define USLCOM_WRITE_LATCH      0x37E1
115 #define USLCOM_READ_LATCH       0x00C2
116
117 enum {
118         USLCOM_BULK_DT_WR,
119         USLCOM_BULK_DT_RD,
120         USLCOM_CTRL_DT_RD,
121         USLCOM_N_TRANSFER,
122 };
123
124 struct uslcom_softc {
125         struct ucom_super_softc sc_super_ucom;
126         struct ucom_softc sc_ucom;
127         struct usb_callout sc_watchdog;
128
129         struct usb_xfer *sc_xfer[USLCOM_N_TRANSFER];
130         struct usb_device *sc_udev;
131         struct lock sc_lock;
132
133         uint8_t          sc_msr;
134         uint8_t          sc_lsr;
135 };
136
137 static device_probe_t uslcom_probe;
138 static device_attach_t uslcom_attach;
139 static device_detach_t uslcom_detach;
140
141 static usb_callback_t uslcom_write_callback;
142 static usb_callback_t uslcom_read_callback;
143 static usb_callback_t uslcom_control_callback;
144
145 static void uslcom_open(struct ucom_softc *);
146 static void uslcom_close(struct ucom_softc *);
147 static void uslcom_set_dtr(struct ucom_softc *, uint8_t);
148 static void uslcom_set_rts(struct ucom_softc *, uint8_t);
149 static void uslcom_set_break(struct ucom_softc *, uint8_t);
150 static int uslcom_ioctl(struct ucom_softc *, uint32_t, caddr_t, int,
151                 struct thread *);
152 static int uslcom_pre_param(struct ucom_softc *, struct termios *);
153 static void uslcom_param(struct ucom_softc *, struct termios *);
154 static void uslcom_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
155 static void uslcom_start_read(struct ucom_softc *);
156 static void uslcom_stop_read(struct ucom_softc *);
157 static void uslcom_start_write(struct ucom_softc *);
158 static void uslcom_stop_write(struct ucom_softc *);
159 static void uslcom_poll(struct ucom_softc *ucom);
160
161 static const struct usb_config uslcom_config[USLCOM_N_TRANSFER] = {
162
163         [USLCOM_BULK_DT_WR] = {
164                 .type = UE_BULK,
165                 .endpoint = UE_ADDR_ANY,
166                 .direction = UE_DIR_OUT,
167                 .bufsize = USLCOM_BULK_BUF_SIZE,
168                .flags = {.pipe_bof = 1,},
169                 .callback = &uslcom_write_callback,
170         },
171
172         [USLCOM_BULK_DT_RD] = {
173                 .type = UE_BULK,
174                 .endpoint = UE_ADDR_ANY,
175                 .direction = UE_DIR_IN,
176                 .bufsize = USLCOM_BULK_BUF_SIZE,
177                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
178                 .callback = &uslcom_read_callback,
179         },
180         [USLCOM_CTRL_DT_RD] = {
181                 .type = UE_CONTROL,
182                 .endpoint = 0x00,
183                 .direction = UE_DIR_ANY,
184                 .bufsize = sizeof(struct usb_device_request) + 8,
185                 .flags = {.pipe_bof = 1,},
186                 .callback = &uslcom_control_callback,
187                 .timeout = 1000,        /* 1 second timeout */
188         },
189 };
190
191 static struct ucom_callback uslcom_callback = {
192         .ucom_cfg_open = &uslcom_open,
193         .ucom_cfg_close = &uslcom_close,
194         .ucom_cfg_get_status = &uslcom_get_status,
195         .ucom_cfg_set_dtr = &uslcom_set_dtr,
196         .ucom_cfg_set_rts = &uslcom_set_rts,
197         .ucom_cfg_set_break = &uslcom_set_break,
198         .ucom_ioctl = &uslcom_ioctl,
199         .ucom_cfg_param = &uslcom_param,
200         .ucom_pre_param = &uslcom_pre_param,
201         .ucom_start_read = &uslcom_start_read,
202         .ucom_stop_read = &uslcom_stop_read,
203         .ucom_start_write = &uslcom_start_write,
204         .ucom_stop_write = &uslcom_stop_write,
205         .ucom_poll = &uslcom_poll,
206 };
207
208 static const STRUCT_USB_HOST_ID uslcom_devs[] = {
209 #define USLCOM_DEV(v,p)  { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
210     USLCOM_DEV(BALTECH, CARDREADER),
211     USLCOM_DEV(CLIPSAL, 5500PCU),
212     USLCOM_DEV(DATAAPEX, MULTICOM),
213     USLCOM_DEV(DELL, DW700),
214     USLCOM_DEV(DIGIANSWER, ZIGBEE802154),
215     USLCOM_DEV(DYNASTREAM, ANTDEVBOARD),
216     USLCOM_DEV(DYNASTREAM, ANTDEVBOARD2),
217     USLCOM_DEV(DYNASTREAM, ANT2USB),
218     USLCOM_DEV(ELV, USBI2C),
219     USLCOM_DEV(FOXCONN, PIRELLI_DP_L10),
220     USLCOM_DEV(FOXCONN, TCOM_TC_300),
221     USLCOM_DEV(GEMALTO, PROXPU),
222     USLCOM_DEV(JABLOTRON, PC60B),
223     USLCOM_DEV(MEI, CASHFLOW_SC),
224     USLCOM_DEV(MEI, S2000),
225     USLCOM_DEV(JABLOTRON, PC60B),
226     USLCOM_DEV(OWEN, AC4),
227     USLCOM_DEV(PHILIPS, ACE1001),
228     USLCOM_DEV(PLX, CA42),
229     USLCOM_DEV(RENESAS, RX610),
230     USLCOM_DEV(SILABS, AEROCOMM),
231     USLCOM_DEV(SILABS, AMBER_AMB2560),
232     USLCOM_DEV(SILABS, ARGUSISP),
233     USLCOM_DEV(SILABS, ARKHAM_DS101_A),
234     USLCOM_DEV(SILABS, ARKHAM_DS101_M),
235     USLCOM_DEV(SILABS, ARYGON_MIFARE),
236     USLCOM_DEV(SILABS, AVIT_USB_TTL),
237     USLCOM_DEV(SILABS, B_G_H3000),
238     USLCOM_DEV(SILABS, BALLUFF_RFID),
239     USLCOM_DEV(SILABS, BEI_VCP),
240     USLCOM_DEV(SILABS, BSM7DUSB),
241     USLCOM_DEV(SILABS, BURNSIDE),
242     USLCOM_DEV(SILABS, C2_EDGE_MODEM),
243     USLCOM_DEV(SILABS, CP2102),
244     USLCOM_DEV(SILABS, CP210X_2),
245     USLCOM_DEV(SILABS, CRUMB128),
246     USLCOM_DEV(SILABS, CYGNAL),
247     USLCOM_DEV(SILABS, CYGNAL_DEBUG),
248     USLCOM_DEV(SILABS, CYGNAL_GPS),
249     USLCOM_DEV(SILABS, DEGREE),
250     USLCOM_DEV(SILABS, EMS_C1007),
251     USLCOM_DEV(SILABS, HELICOM),
252     USLCOM_DEV(SILABS, IMS_USB_RS422),
253     USLCOM_DEV(SILABS, INFINITY_MIC),
254     USLCOM_DEV(SILABS, INSYS_MODEM),
255     USLCOM_DEV(SILABS, KYOCERA_GPS),
256     USLCOM_DEV(SILABS, LIPOWSKY_HARP),
257     USLCOM_DEV(SILABS, LIPOWSKY_JTAG),
258     USLCOM_DEV(SILABS, LIPOWSKY_LIN),
259     USLCOM_DEV(SILABS, MC35PU),
260     USLCOM_DEV(SILABS, MJS_TOSLINK),
261     USLCOM_DEV(SILABS, MSD_DASHHAWK),
262     USLCOM_DEV(SILABS, POLOLU),
263     USLCOM_DEV(SILABS, PROCYON_AVS),
264     USLCOM_DEV(SILABS, SB_PARAMOUNT_ME),
265     USLCOM_DEV(SILABS, SUUNTO),
266     USLCOM_DEV(SILABS, TAMSMASTER),
267     USLCOM_DEV(SILABS, TELEGESIS_ETRX2),
268     USLCOM_DEV(SILABS, TRACIENT),
269     USLCOM_DEV(SILABS, TRAQMATE),
270     USLCOM_DEV(SILABS, USBCOUNT50),
271     USLCOM_DEV(SILABS, USBPULSE100),
272     USLCOM_DEV(SILABS, USBSCOPE50),
273     USLCOM_DEV(SILABS, USBWAVE12),
274     USLCOM_DEV(SILABS, VSTABI),
275     USLCOM_DEV(SILABS, WAVIT),
276     USLCOM_DEV(SILABS, WMRBATT),
277     USLCOM_DEV(SILABS, WMRRIGBLASTER),
278     USLCOM_DEV(SILABS, WMRRIGTALK),
279     USLCOM_DEV(SILABS, ZEPHYR_BIO),
280     USLCOM_DEV(SILABS2, DCU11CLONE),
281     USLCOM_DEV(SILABS3, GPRS_MODEM),
282     USLCOM_DEV(SILABS4, 100EU_MODEM),
283     USLCOM_DEV(SYNTECH, CYPHERLAB100),
284     USLCOM_DEV(USI, MC60),
285     USLCOM_DEV(VAISALA, CABLE),
286     USLCOM_DEV(WAGO, SERVICECABLE),
287     USLCOM_DEV(WAVESENSE, JAZZ),
288     USLCOM_DEV(WIENERPLEINBAUS, PL512),
289     USLCOM_DEV(WIENERPLEINBAUS, RCM),
290     USLCOM_DEV(WIENERPLEINBAUS, MPOD),
291     USLCOM_DEV(WIENERPLEINBAUS, CML),
292 #undef USLCOM_DEV
293 };
294
295 static device_method_t uslcom_methods[] = {
296         DEVMETHOD(device_probe, uslcom_probe),
297         DEVMETHOD(device_attach, uslcom_attach),
298         DEVMETHOD(device_detach, uslcom_detach),
299         DEVMETHOD_END
300 };
301
302 static devclass_t uslcom_devclass;
303
304 static driver_t uslcom_driver = {
305         .name = "uslcom",
306         .methods = uslcom_methods,
307         .size = sizeof(struct uslcom_softc),
308 };
309
310 DRIVER_MODULE(uslcom, uhub, uslcom_driver, uslcom_devclass, NULL, NULL);
311 MODULE_DEPEND(uslcom, ucom, 1, 1, 1);
312 MODULE_DEPEND(uslcom, usb, 1, 1, 1);
313 MODULE_VERSION(uslcom, 1);
314
315 static void
316 uslcom_watchdog(void *arg)
317 {
318         struct uslcom_softc *sc = arg;
319
320         KKASSERT(lockowned(&sc->sc_lock));
321
322         usbd_transfer_start(sc->sc_xfer[USLCOM_CTRL_DT_RD]);
323
324         usb_callout_reset(&sc->sc_watchdog,
325             hz / 4, &uslcom_watchdog, sc);
326 }
327
328 static int
329 uslcom_probe(device_t dev)
330 {
331         struct usb_attach_arg *uaa = device_get_ivars(dev);
332
333         DPRINTFN(11, "\n");
334
335         if (uaa->usb_mode != USB_MODE_HOST) {
336                 return (ENXIO);
337         }
338         if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) {
339                 return (ENXIO);
340         }
341         if (uaa->info.bIfaceIndex != USLCOM_IFACE_INDEX) {
342                 return (ENXIO);
343         }
344         return (usbd_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa));
345 }
346
347 static int
348 uslcom_attach(device_t dev)
349 {
350         struct usb_attach_arg *uaa = device_get_ivars(dev);
351         struct uslcom_softc *sc = device_get_softc(dev);
352         int error;
353
354         DPRINTFN(11, "\n");
355
356         device_set_usb_desc(dev);
357         lockinit(&sc->sc_lock, "uslcom", 0, LK_CANRECURSE);
358         usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_lock, 0);
359
360         sc->sc_udev = uaa->device;
361
362         error = usbd_transfer_setup(uaa->device,
363             &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config,
364             USLCOM_N_TRANSFER, sc, &sc->sc_lock);
365         if (error) {
366                 DPRINTF("one or more missing USB endpoints, "
367                     "error=%s\n", usbd_errstr(error));
368                 goto detach;
369         }
370         /* clear stall at first run */
371         lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
372         usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]);
373         usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]);
374         lockmgr(&sc->sc_lock, LK_RELEASE);
375
376         error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
377             &uslcom_callback, &sc->sc_lock);
378         if (error) {
379                 goto detach;
380         }
381         ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
382
383         return (0);
384
385 detach:
386         uslcom_detach(dev);
387         return (ENXIO);
388 }
389
390 static int
391 uslcom_detach(device_t dev)
392 {
393         struct uslcom_softc *sc = device_get_softc(dev);
394
395         DPRINTF("sc=%p\n", sc);
396
397         ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
398         usbd_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER);
399
400         usb_callout_drain(&sc->sc_watchdog);
401         lockuninit(&sc->sc_lock);
402
403         return (0);
404 }
405
406 static void
407 uslcom_open(struct ucom_softc *ucom)
408 {
409         struct uslcom_softc *sc = ucom->sc_parent;
410         struct usb_device_request req;
411
412         req.bmRequestType = USLCOM_WRITE;
413         req.bRequest = USLCOM_UART;
414         USETW(req.wValue, USLCOM_UART_ENABLE);
415         USETW(req.wIndex, USLCOM_PORT_NO);
416         USETW(req.wLength, 0);
417
418         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
419             &req, NULL, 0, 1000)) {
420                 DPRINTF("UART enable failed (ignored)\n");
421         }
422
423         /* start polling status */
424         uslcom_watchdog(sc);
425 }
426
427 static void
428 uslcom_close(struct ucom_softc *ucom)
429 {
430         struct uslcom_softc *sc = ucom->sc_parent;
431         struct usb_device_request req;
432
433         /* stop polling status */
434         usb_callout_stop(&sc->sc_watchdog);
435
436         req.bmRequestType = USLCOM_WRITE;
437         req.bRequest = USLCOM_UART;
438         USETW(req.wValue, USLCOM_UART_DISABLE);
439         USETW(req.wIndex, USLCOM_PORT_NO);
440         USETW(req.wLength, 0);
441
442         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
443             &req, NULL, 0, 1000)) {
444                 DPRINTF("UART disable failed (ignored)\n");
445         }
446 }
447
448 static void
449 uslcom_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
450 {
451         struct uslcom_softc *sc = ucom->sc_parent;
452         struct usb_device_request req;
453         uint16_t ctl;
454
455         DPRINTF("onoff = %d\n", onoff);
456
457         ctl = onoff ? USLCOM_CTRL_DTR_ON : 0;
458         ctl |= USLCOM_CTRL_DTR_SET;
459
460         req.bmRequestType = USLCOM_WRITE;
461         req.bRequest = USLCOM_CTRL;
462         USETW(req.wValue, ctl);
463         USETW(req.wIndex, USLCOM_PORT_NO);
464         USETW(req.wLength, 0);
465
466         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
467             &req, NULL, 0, 1000)) {
468                 DPRINTF("Setting DTR failed (ignored)\n");
469         }
470 }
471
472 static void
473 uslcom_set_rts(struct ucom_softc *ucom, uint8_t onoff)
474 {
475         struct uslcom_softc *sc = ucom->sc_parent;
476         struct usb_device_request req;
477         uint16_t ctl;
478
479         DPRINTF("onoff = %d\n", onoff);
480
481         ctl = onoff ? USLCOM_CTRL_RTS_ON : 0;
482         ctl |= USLCOM_CTRL_RTS_SET;
483
484         req.bmRequestType = USLCOM_WRITE;
485         req.bRequest = USLCOM_CTRL;
486         USETW(req.wValue, ctl);
487         USETW(req.wIndex, USLCOM_PORT_NO);
488         USETW(req.wLength, 0);
489
490         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
491             &req, NULL, 0, 1000)) {
492                 DPRINTF("Setting DTR failed (ignored)\n");
493         }
494 }
495
496 static int
497 uslcom_pre_param(struct ucom_softc *ucom, struct termios *t)
498 {
499         if (t->c_ospeed <= 0 || t->c_ospeed > 921600)
500                 return (EINVAL);
501         return (0);
502 }
503
504 static void
505 uslcom_param(struct ucom_softc *ucom, struct termios *t)
506 {
507         struct uslcom_softc *sc = ucom->sc_parent;
508         struct usb_device_request req;
509         uint32_t flowctrl[4];
510         uint16_t data;
511
512         DPRINTF("\n");
513
514         req.bmRequestType = USLCOM_WRITE;
515         req.bRequest = USLCOM_BAUD_RATE;
516         USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed);
517         USETW(req.wIndex, USLCOM_PORT_NO);
518         USETW(req.wLength, 0);
519
520         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
521             &req, NULL, 0, 1000)) {
522                 DPRINTF("Set baudrate failed (ignored)\n");
523         }
524
525         if (t->c_cflag & CSTOPB)
526                 data = USLCOM_STOP_BITS_2;
527         else
528                 data = USLCOM_STOP_BITS_1;
529         if (t->c_cflag & PARENB) {
530                 if (t->c_cflag & PARODD)
531                         data |= USLCOM_PARITY_ODD;
532                 else
533                         data |= USLCOM_PARITY_EVEN;
534         } else
535                 data |= USLCOM_PARITY_NONE;
536         switch (t->c_cflag & CSIZE) {
537         case CS5:
538                 data |= USLCOM_SET_DATA_BITS(5);
539                 break;
540         case CS6:
541                 data |= USLCOM_SET_DATA_BITS(6);
542                 break;
543         case CS7:
544                 data |= USLCOM_SET_DATA_BITS(7);
545                 break;
546         case CS8:
547                 data |= USLCOM_SET_DATA_BITS(8);
548                 break;
549         }
550
551         req.bmRequestType = USLCOM_WRITE;
552         req.bRequest = USLCOM_DATA;
553         USETW(req.wValue, data);
554         USETW(req.wIndex, USLCOM_PORT_NO);
555         USETW(req.wLength, 0);
556
557         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
558             &req, NULL, 0, 1000)) {
559                 DPRINTF("Set format failed (ignored)\n");
560         }
561        
562         if (t->c_cflag & CRTSCTS) {
563                 flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON | USLCOM_FLOW_CTS_HS);
564                 flowctrl[1] = htole32(USLCOM_FLOW_RTS_HS);
565                 flowctrl[2] = 0;
566                 flowctrl[3] = 0;
567         } else {
568                 flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON);
569                 flowctrl[1] = htole32(USLCOM_FLOW_RTS_ON);
570                 flowctrl[2] = 0;
571                 flowctrl[3] = 0;
572         }
573         req.bmRequestType = USLCOM_WRITE;
574         req.bRequest = USLCOM_SET_FLOWCTRL;
575         USETW(req.wValue, 0);
576         USETW(req.wIndex, USLCOM_PORT_NO);
577         USETW(req.wLength, sizeof(flowctrl));
578
579         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
580             &req, flowctrl, 0, 1000)) {
581                 DPRINTF("Set flowcontrol failed (ignored)\n");
582         }
583 }
584
585 static void
586 uslcom_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
587 {
588         struct uslcom_softc *sc = ucom->sc_parent;
589
590         DPRINTF("\n");
591
592         *lsr = sc->sc_lsr;
593         *msr = sc->sc_msr;
594 }
595
596 static void
597 uslcom_set_break(struct ucom_softc *ucom, uint8_t onoff)
598 {
599         struct uslcom_softc *sc = ucom->sc_parent;
600         struct usb_device_request req;
601         uint16_t brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF;
602
603         req.bmRequestType = USLCOM_WRITE;
604         req.bRequest = USLCOM_BREAK;
605         USETW(req.wValue, brk);
606         USETW(req.wIndex, USLCOM_PORT_NO);
607         USETW(req.wLength, 0);
608
609         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
610             &req, NULL, 0, 1000)) {
611                 DPRINTF("Set BREAK failed (ignored)\n");
612         }
613 }
614
615 static int
616 uslcom_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data,
617     int flag, struct thread *td)
618 {
619         struct uslcom_softc *sc = ucom->sc_parent;
620         struct usb_device_request req;
621         int error = 0;
622         uint8_t latch;
623
624         DPRINTF("cmd=0x%08x\n", cmd);
625
626         switch (cmd) {
627         case USB_GET_GPIO:
628                 req.bmRequestType = USLCOM_READ;
629                 req.bRequest = USLCOM_VENDOR_SPECIFIC;
630                 USETW(req.wValue, USLCOM_READ_LATCH);
631                 USETW(req.wIndex, 0);
632                 USETW(req.wLength, sizeof(latch));
633
634                 if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
635                     &req, &latch, 0, 1000)) {
636                         DPRINTF("Get LATCH failed\n");
637                         error = EIO;
638                 }
639                 *(int *)data = latch;
640                 break;
641
642         case USB_SET_GPIO:
643                 req.bmRequestType = USLCOM_WRITE;
644                 req.bRequest = USLCOM_VENDOR_SPECIFIC;
645                 USETW(req.wValue, USLCOM_WRITE_LATCH);
646                 USETW(req.wIndex, (*(int *)data));
647                 USETW(req.wLength, 0);
648                 
649                 if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
650                     &req, NULL, 0, 1000)) {
651                         DPRINTF("Set LATCH failed\n");
652                         error = EIO;
653                 }
654                 break;
655
656         default:
657                 DPRINTF("Unknown IOCTL\n");
658                 error = ENOIOCTL;
659                 break;
660         }
661         return (error);
662 }
663
664 static void
665 uslcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
666 {
667         struct uslcom_softc *sc = usbd_xfer_softc(xfer);
668         struct usb_page_cache *pc;
669         uint32_t actlen;
670
671         switch (USB_GET_STATE(xfer)) {
672         case USB_ST_SETUP:
673         case USB_ST_TRANSFERRED:
674 tr_setup:
675                 pc = usbd_xfer_get_frame(xfer, 0);
676                 if (ucom_get_data(&sc->sc_ucom, pc, 0,
677                     USLCOM_BULK_BUF_SIZE, &actlen)) {
678
679                         DPRINTF("actlen = %d\n", actlen);
680
681                         usbd_xfer_set_frame_len(xfer, 0, actlen);
682                         usbd_transfer_submit(xfer);
683                 }
684                 return;
685
686         default:                        /* Error */
687                 if (error != USB_ERR_CANCELLED) {
688                         /* try to clear stall first */
689                         usbd_xfer_set_stall(xfer);
690                         goto tr_setup;
691                 }
692                 return;
693         }
694 }
695
696 static void
697 uslcom_read_callback(struct usb_xfer *xfer, usb_error_t error)
698 {
699         struct uslcom_softc *sc = usbd_xfer_softc(xfer);
700         struct usb_page_cache *pc;
701         int actlen;
702
703         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
704
705         switch (USB_GET_STATE(xfer)) {
706         case USB_ST_TRANSFERRED:
707                 pc = usbd_xfer_get_frame(xfer, 0);
708                 ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
709
710         case USB_ST_SETUP:
711 tr_setup:
712                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
713                 usbd_transfer_submit(xfer);
714                 return;
715
716         default:                        /* Error */
717                 if (error != USB_ERR_CANCELLED) {
718                         /* try to clear stall first */
719                         usbd_xfer_set_stall(xfer);
720                         goto tr_setup;
721                 }
722                 return;
723         }
724 }
725
726 static void
727 uslcom_control_callback(struct usb_xfer *xfer, usb_error_t error)
728 {
729         struct uslcom_softc *sc = usbd_xfer_softc(xfer);
730         struct usb_page_cache *pc;
731         struct usb_device_request req;
732         uint8_t msr = 0;
733         uint8_t buf;
734
735         switch (USB_GET_STATE(xfer)) {
736         case USB_ST_TRANSFERRED:
737                 pc = usbd_xfer_get_frame(xfer, 1);
738                 usbd_copy_out(pc, 0, &buf, sizeof(buf));
739                 if (buf & USLCOM_CTRL_CTS)
740                         msr |= SER_CTS;
741                 if (buf & USLCOM_CTRL_DSR)
742                         msr |= SER_DSR;
743                 if (buf & USLCOM_CTRL_RI)
744                         msr |= SER_RI;
745                 if (buf & USLCOM_CTRL_DCD)
746                         msr |= SER_DCD;
747
748                 if (msr != sc->sc_msr) {
749                         DPRINTF("status change msr=0x%02x "
750                             "(was 0x%02x)\n", msr, sc->sc_msr);
751                         sc->sc_msr = msr;
752                         ucom_status_change(&sc->sc_ucom);
753                 }
754                 break;
755
756         case USB_ST_SETUP:
757                 req.bmRequestType = USLCOM_READ;
758                 req.bRequest = USLCOM_RCTRL;
759                 USETW(req.wValue, 0);
760                 USETW(req.wIndex, USLCOM_PORT_NO);
761                 USETW(req.wLength, sizeof(buf));
762                
763                 usbd_xfer_set_frames(xfer, 2);
764                 usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
765                 usbd_xfer_set_frame_len(xfer, 1, sizeof(buf));
766
767                 pc = usbd_xfer_get_frame(xfer, 0);
768                 usbd_copy_in(pc, 0, &req, sizeof(req));
769                 usbd_transfer_submit(xfer);
770                 break;
771
772         default:                /* error */
773                 if (error != USB_ERR_CANCELLED)
774                         DPRINTF("error=%s\n", usbd_errstr(error));
775                 break;
776         }
777 }
778
779 static void
780 uslcom_start_read(struct ucom_softc *ucom)
781 {
782         struct uslcom_softc *sc = ucom->sc_parent;
783
784         /* start read endpoint */
785         usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]);
786 }
787
788 static void
789 uslcom_stop_read(struct ucom_softc *ucom)
790 {
791         struct uslcom_softc *sc = ucom->sc_parent;
792
793         /* stop read endpoint */
794         usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]);
795 }
796
797 static void
798 uslcom_start_write(struct ucom_softc *ucom)
799 {
800         struct uslcom_softc *sc = ucom->sc_parent;
801
802         usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]);
803 }
804
805 static void
806 uslcom_stop_write(struct ucom_softc *ucom)
807 {
808         struct uslcom_softc *sc = ucom->sc_parent;
809
810         usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]);
811 }
812
813 static void
814 uslcom_poll(struct ucom_softc *ucom)
815 {
816         struct uslcom_softc *sc = ucom->sc_parent;
817         usbd_transfer_poll(sc->sc_xfer, USLCOM_N_TRANSFER);
818 }