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