]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/usb/serial/uvscom.c
Add support for SIMCom SIM7600E.
[FreeBSD/FreeBSD.git] / sys / dev / usb / serial / uvscom.c
1 /*      $NetBSD: usb/uvscom.c,v 1.1 2002/03/19 15:08:42 augustss Exp $  */
2
3 #include <sys/cdefs.h>
4 __FBSDID("$FreeBSD$");
5
6 /*-
7  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
8  *
9  * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  */
34
35 /*
36  * uvscom: SUNTAC Slipper U VS-10U driver.
37  * Slipper U is a PC Card to USB converter for data communication card
38  * adapter.  It supports DDI Pocket's Air H" C@rd, C@rd H" 64, NTT's P-in,
39  * P-in m@ater and various data communication card adapters.
40  */
41
42 #include <sys/stdint.h>
43 #include <sys/stddef.h>
44 #include <sys/param.h>
45 #include <sys/queue.h>
46 #include <sys/types.h>
47 #include <sys/systm.h>
48 #include <sys/kernel.h>
49 #include <sys/bus.h>
50 #include <sys/module.h>
51 #include <sys/lock.h>
52 #include <sys/mutex.h>
53 #include <sys/condvar.h>
54 #include <sys/sysctl.h>
55 #include <sys/sx.h>
56 #include <sys/unistd.h>
57 #include <sys/callout.h>
58 #include <sys/malloc.h>
59 #include <sys/priv.h>
60
61 #include <dev/usb/usb.h>
62 #include <dev/usb/usbdi.h>
63 #include <dev/usb/usbdi_util.h>
64 #include "usbdevs.h"
65
66 #define USB_DEBUG_VAR uvscom_debug
67 #include <dev/usb/usb_debug.h>
68 #include <dev/usb/usb_process.h>
69
70 #include <dev/usb/serial/usb_serial.h>
71
72 #ifdef USB_DEBUG
73 static int uvscom_debug = 0;
74
75 static SYSCTL_NODE(_hw_usb, OID_AUTO, uvscom, CTLFLAG_RW, 0, "USB uvscom");
76 SYSCTL_INT(_hw_usb_uvscom, OID_AUTO, debug, CTLFLAG_RWTUN,
77     &uvscom_debug, 0, "Debug level");
78 #endif
79
80 #define UVSCOM_MODVER           1       /* module version */
81
82 #define UVSCOM_CONFIG_INDEX     0
83 #define UVSCOM_IFACE_INDEX      0
84
85 /* Request */
86 #define UVSCOM_SET_SPEED        0x10
87 #define UVSCOM_LINE_CTL         0x11
88 #define UVSCOM_SET_PARAM        0x12
89 #define UVSCOM_READ_STATUS      0xd0
90 #define UVSCOM_SHUTDOWN         0xe0
91
92 /* UVSCOM_SET_SPEED parameters */
93 #define UVSCOM_SPEED_150BPS     0x00
94 #define UVSCOM_SPEED_300BPS     0x01
95 #define UVSCOM_SPEED_600BPS     0x02
96 #define UVSCOM_SPEED_1200BPS    0x03
97 #define UVSCOM_SPEED_2400BPS    0x04
98 #define UVSCOM_SPEED_4800BPS    0x05
99 #define UVSCOM_SPEED_9600BPS    0x06
100 #define UVSCOM_SPEED_19200BPS   0x07
101 #define UVSCOM_SPEED_38400BPS   0x08
102 #define UVSCOM_SPEED_57600BPS   0x09
103 #define UVSCOM_SPEED_115200BPS  0x0a
104
105 /* UVSCOM_LINE_CTL parameters */
106 #define UVSCOM_BREAK            0x40
107 #define UVSCOM_RTS              0x02
108 #define UVSCOM_DTR              0x01
109 #define UVSCOM_LINE_INIT        0x08
110
111 /* UVSCOM_SET_PARAM parameters */
112 #define UVSCOM_DATA_MASK        0x03
113 #define UVSCOM_DATA_BIT_8       0x03
114 #define UVSCOM_DATA_BIT_7       0x02
115 #define UVSCOM_DATA_BIT_6       0x01
116 #define UVSCOM_DATA_BIT_5       0x00
117
118 #define UVSCOM_STOP_MASK        0x04
119 #define UVSCOM_STOP_BIT_2       0x04
120 #define UVSCOM_STOP_BIT_1       0x00
121
122 #define UVSCOM_PARITY_MASK      0x18
123 #define UVSCOM_PARITY_EVEN      0x18
124 #define UVSCOM_PARITY_ODD       0x08
125 #define UVSCOM_PARITY_NONE      0x00
126
127 /* Status bits */
128 #define UVSCOM_TXRDY            0x04
129 #define UVSCOM_RXRDY            0x01
130
131 #define UVSCOM_DCD              0x08
132 #define UVSCOM_NOCARD           0x04
133 #define UVSCOM_DSR              0x02
134 #define UVSCOM_CTS              0x01
135 #define UVSCOM_USTAT_MASK       (UVSCOM_NOCARD | UVSCOM_DSR | UVSCOM_CTS)
136
137 #define UVSCOM_BULK_BUF_SIZE    1024    /* bytes */
138
139 enum {
140         UVSCOM_BULK_DT_WR,
141         UVSCOM_BULK_DT_RD,
142         UVSCOM_INTR_DT_RD,
143         UVSCOM_N_TRANSFER,
144 };
145
146 struct uvscom_softc {
147         struct ucom_super_softc sc_super_ucom;
148         struct ucom_softc sc_ucom;
149
150         struct usb_xfer *sc_xfer[UVSCOM_N_TRANSFER];
151         struct usb_device *sc_udev;
152         struct mtx sc_mtx;
153
154         uint16_t sc_line;               /* line control register */
155
156         uint8_t sc_iface_no;            /* interface number */
157         uint8_t sc_iface_index;         /* interface index */
158         uint8_t sc_lsr;                 /* local status register */
159         uint8_t sc_msr;                 /* uvscom status register */
160         uint8_t sc_unit_status;         /* unit status */
161 };
162
163 /* prototypes */
164
165 static device_probe_t uvscom_probe;
166 static device_attach_t uvscom_attach;
167 static device_detach_t uvscom_detach;
168 static void uvscom_free_softc(struct uvscom_softc *);
169
170 static usb_callback_t uvscom_write_callback;
171 static usb_callback_t uvscom_read_callback;
172 static usb_callback_t uvscom_intr_callback;
173
174 static void     uvscom_free(struct ucom_softc *);
175 static void     uvscom_cfg_set_dtr(struct ucom_softc *, uint8_t);
176 static void     uvscom_cfg_set_rts(struct ucom_softc *, uint8_t);
177 static void     uvscom_cfg_set_break(struct ucom_softc *, uint8_t);
178 static int      uvscom_pre_param(struct ucom_softc *, struct termios *);
179 static void     uvscom_cfg_param(struct ucom_softc *, struct termios *);
180 static int      uvscom_pre_open(struct ucom_softc *);
181 static void     uvscom_cfg_open(struct ucom_softc *);
182 static void     uvscom_cfg_close(struct ucom_softc *);
183 static void     uvscom_start_read(struct ucom_softc *);
184 static void     uvscom_stop_read(struct ucom_softc *);
185 static void     uvscom_start_write(struct ucom_softc *);
186 static void     uvscom_stop_write(struct ucom_softc *);
187 static void     uvscom_cfg_get_status(struct ucom_softc *, uint8_t *,
188                     uint8_t *);
189 static void     uvscom_cfg_write(struct uvscom_softc *, uint8_t, uint16_t);
190 static uint16_t uvscom_cfg_read_status(struct uvscom_softc *);
191 static void     uvscom_poll(struct ucom_softc *ucom);
192
193 static const struct usb_config uvscom_config[UVSCOM_N_TRANSFER] = {
194
195         [UVSCOM_BULK_DT_WR] = {
196                 .type = UE_BULK,
197                 .endpoint = UE_ADDR_ANY,
198                 .direction = UE_DIR_OUT,
199                 .bufsize = UVSCOM_BULK_BUF_SIZE,
200                 .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
201                 .callback = &uvscom_write_callback,
202         },
203
204         [UVSCOM_BULK_DT_RD] = {
205                 .type = UE_BULK,
206                 .endpoint = UE_ADDR_ANY,
207                 .direction = UE_DIR_IN,
208                 .bufsize = UVSCOM_BULK_BUF_SIZE,
209                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
210                 .callback = &uvscom_read_callback,
211         },
212
213         [UVSCOM_INTR_DT_RD] = {
214                 .type = UE_INTERRUPT,
215                 .endpoint = UE_ADDR_ANY,
216                 .direction = UE_DIR_IN,
217                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
218                 .bufsize = 0,   /* use wMaxPacketSize */
219                 .callback = &uvscom_intr_callback,
220         },
221 };
222
223 static const struct ucom_callback uvscom_callback = {
224         .ucom_cfg_get_status = &uvscom_cfg_get_status,
225         .ucom_cfg_set_dtr = &uvscom_cfg_set_dtr,
226         .ucom_cfg_set_rts = &uvscom_cfg_set_rts,
227         .ucom_cfg_set_break = &uvscom_cfg_set_break,
228         .ucom_cfg_param = &uvscom_cfg_param,
229         .ucom_cfg_open = &uvscom_cfg_open,
230         .ucom_cfg_close = &uvscom_cfg_close,
231         .ucom_pre_open = &uvscom_pre_open,
232         .ucom_pre_param = &uvscom_pre_param,
233         .ucom_start_read = &uvscom_start_read,
234         .ucom_stop_read = &uvscom_stop_read,
235         .ucom_start_write = &uvscom_start_write,
236         .ucom_stop_write = &uvscom_stop_write,
237         .ucom_poll = &uvscom_poll,
238         .ucom_free = &uvscom_free,
239 };
240
241 static const STRUCT_USB_HOST_ID uvscom_devs[] = {
242         /* SUNTAC U-Cable type A4 */
243         {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS144L4, 0)},
244         /* SUNTAC U-Cable type D2 */
245         {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_DS96L, 0)},
246         /* SUNTAC Ir-Trinity */
247         {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_IS96U, 0)},
248         /* SUNTAC U-Cable type P1 */
249         {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_PS64P1, 0)},
250         /* SUNTAC Slipper U */
251         {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_VS10U, 0)},
252 };
253
254 static device_method_t uvscom_methods[] = {
255         DEVMETHOD(device_probe, uvscom_probe),
256         DEVMETHOD(device_attach, uvscom_attach),
257         DEVMETHOD(device_detach, uvscom_detach),
258         DEVMETHOD_END
259 };
260
261 static devclass_t uvscom_devclass;
262
263 static driver_t uvscom_driver = {
264         .name = "uvscom",
265         .methods = uvscom_methods,
266         .size = sizeof(struct uvscom_softc),
267 };
268
269 DRIVER_MODULE(uvscom, uhub, uvscom_driver, uvscom_devclass, NULL, 0);
270 MODULE_DEPEND(uvscom, ucom, 1, 1, 1);
271 MODULE_DEPEND(uvscom, usb, 1, 1, 1);
272 MODULE_VERSION(uvscom, UVSCOM_MODVER);
273 USB_PNP_HOST_INFO(uvscom_devs);
274
275 static int
276 uvscom_probe(device_t dev)
277 {
278         struct usb_attach_arg *uaa = device_get_ivars(dev);
279
280         if (uaa->usb_mode != USB_MODE_HOST) {
281                 return (ENXIO);
282         }
283         if (uaa->info.bConfigIndex != UVSCOM_CONFIG_INDEX) {
284                 return (ENXIO);
285         }
286         if (uaa->info.bIfaceIndex != UVSCOM_IFACE_INDEX) {
287                 return (ENXIO);
288         }
289         return (usbd_lookup_id_by_uaa(uvscom_devs, sizeof(uvscom_devs), uaa));
290 }
291
292 static int
293 uvscom_attach(device_t dev)
294 {
295         struct usb_attach_arg *uaa = device_get_ivars(dev);
296         struct uvscom_softc *sc = device_get_softc(dev);
297         int error;
298
299         device_set_usb_desc(dev);
300         mtx_init(&sc->sc_mtx, "uvscom", NULL, MTX_DEF);
301         ucom_ref(&sc->sc_super_ucom);
302
303         sc->sc_udev = uaa->device;
304
305         DPRINTF("sc=%p\n", sc);
306
307         sc->sc_iface_no = uaa->info.bIfaceNum;
308         sc->sc_iface_index = UVSCOM_IFACE_INDEX;
309
310         error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index,
311             sc->sc_xfer, uvscom_config, UVSCOM_N_TRANSFER, sc, &sc->sc_mtx);
312
313         if (error) {
314                 DPRINTF("could not allocate all USB transfers!\n");
315                 goto detach;
316         }
317         sc->sc_line = UVSCOM_LINE_INIT;
318
319         /* clear stall at first run */
320         mtx_lock(&sc->sc_mtx);
321         usbd_xfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
322         usbd_xfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_RD]);
323         mtx_unlock(&sc->sc_mtx);
324
325         error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
326             &uvscom_callback, &sc->sc_mtx);
327         if (error) {
328                 goto detach;
329         }
330         ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
331
332         /* start interrupt pipe */
333         mtx_lock(&sc->sc_mtx);
334         usbd_transfer_start(sc->sc_xfer[UVSCOM_INTR_DT_RD]);
335         mtx_unlock(&sc->sc_mtx);
336
337         return (0);
338
339 detach:
340         uvscom_detach(dev);
341         return (ENXIO);
342 }
343
344 static int
345 uvscom_detach(device_t dev)
346 {
347         struct uvscom_softc *sc = device_get_softc(dev);
348
349         DPRINTF("sc=%p\n", sc);
350
351         /* stop interrupt pipe */
352
353         if (sc->sc_xfer[UVSCOM_INTR_DT_RD])
354                 usbd_transfer_stop(sc->sc_xfer[UVSCOM_INTR_DT_RD]);
355
356         ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
357         usbd_transfer_unsetup(sc->sc_xfer, UVSCOM_N_TRANSFER);
358
359         device_claim_softc(dev);
360
361         uvscom_free_softc(sc);
362
363         return (0);
364 }
365
366 UCOM_UNLOAD_DRAIN(uvscom);
367
368 static void
369 uvscom_free_softc(struct uvscom_softc *sc)
370 {
371         if (ucom_unref(&sc->sc_super_ucom)) {
372                 mtx_destroy(&sc->sc_mtx);
373                 device_free_softc(sc);
374         }
375 }
376
377 static void
378 uvscom_free(struct ucom_softc *ucom)
379 {
380         uvscom_free_softc(ucom->sc_parent);
381 }
382
383 static void
384 uvscom_write_callback(struct usb_xfer *xfer, usb_error_t error)
385 {
386         struct uvscom_softc *sc = usbd_xfer_softc(xfer);
387         struct usb_page_cache *pc;
388         uint32_t actlen;
389
390         switch (USB_GET_STATE(xfer)) {
391         case USB_ST_SETUP:
392         case USB_ST_TRANSFERRED:
393 tr_setup:
394                 pc = usbd_xfer_get_frame(xfer, 0);
395                 if (ucom_get_data(&sc->sc_ucom, pc, 0,
396                     UVSCOM_BULK_BUF_SIZE, &actlen)) {
397
398                         usbd_xfer_set_frame_len(xfer, 0, actlen);
399                         usbd_transfer_submit(xfer);
400                 }
401                 return;
402
403         default:                        /* Error */
404                 if (error != USB_ERR_CANCELLED) {
405                         /* try to clear stall first */
406                         usbd_xfer_set_stall(xfer);
407                         goto tr_setup;
408                 }
409                 return;
410         }
411 }
412
413 static void
414 uvscom_read_callback(struct usb_xfer *xfer, usb_error_t error)
415 {
416         struct uvscom_softc *sc = usbd_xfer_softc(xfer);
417         struct usb_page_cache *pc;
418         int actlen;
419
420         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
421
422         switch (USB_GET_STATE(xfer)) {
423         case USB_ST_TRANSFERRED:
424                 pc = usbd_xfer_get_frame(xfer, 0);
425                 ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
426
427         case USB_ST_SETUP:
428 tr_setup:
429                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
430                 usbd_transfer_submit(xfer);
431                 return;
432
433         default:                        /* Error */
434                 if (error != USB_ERR_CANCELLED) {
435                         /* try to clear stall first */
436                         usbd_xfer_set_stall(xfer);
437                         goto tr_setup;
438                 }
439                 return;
440         }
441 }
442
443 static void
444 uvscom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
445 {
446         struct uvscom_softc *sc = usbd_xfer_softc(xfer);
447         struct usb_page_cache *pc;
448         uint8_t buf[2];
449         int actlen;
450
451         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
452
453         switch (USB_GET_STATE(xfer)) {
454         case USB_ST_TRANSFERRED:
455                 if (actlen >= 2) {
456
457                         pc = usbd_xfer_get_frame(xfer, 0);
458                         usbd_copy_out(pc, 0, buf, sizeof(buf));
459
460                         sc->sc_lsr = 0;
461                         sc->sc_msr = 0;
462                         sc->sc_unit_status = buf[1];
463
464                         if (buf[0] & UVSCOM_TXRDY) {
465                                 sc->sc_lsr |= ULSR_TXRDY;
466                         }
467                         if (buf[0] & UVSCOM_RXRDY) {
468                                 sc->sc_lsr |= ULSR_RXRDY;
469                         }
470                         if (buf[1] & UVSCOM_CTS) {
471                                 sc->sc_msr |= SER_CTS;
472                         }
473                         if (buf[1] & UVSCOM_DSR) {
474                                 sc->sc_msr |= SER_DSR;
475                         }
476                         if (buf[1] & UVSCOM_DCD) {
477                                 sc->sc_msr |= SER_DCD;
478                         }
479                         /*
480                          * the UCOM layer will ignore this call if the TTY
481                          * device is closed!
482                          */
483                         ucom_status_change(&sc->sc_ucom);
484                 }
485         case USB_ST_SETUP:
486 tr_setup:
487                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
488                 usbd_transfer_submit(xfer);
489                 return;
490
491         default:                        /* Error */
492                 if (error != USB_ERR_CANCELLED) {
493                         /* try to clear stall first */
494                         usbd_xfer_set_stall(xfer);
495                         goto tr_setup;
496                 }
497                 return;
498         }
499 }
500
501 static void
502 uvscom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
503 {
504         struct uvscom_softc *sc = ucom->sc_parent;
505
506         DPRINTF("onoff = %d\n", onoff);
507
508         if (onoff)
509                 sc->sc_line |= UVSCOM_DTR;
510         else
511                 sc->sc_line &= ~UVSCOM_DTR;
512
513         uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line);
514 }
515
516 static void
517 uvscom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
518 {
519         struct uvscom_softc *sc = ucom->sc_parent;
520
521         DPRINTF("onoff = %d\n", onoff);
522
523         if (onoff)
524                 sc->sc_line |= UVSCOM_RTS;
525         else
526                 sc->sc_line &= ~UVSCOM_RTS;
527
528         uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line);
529 }
530
531 static void
532 uvscom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
533 {
534         struct uvscom_softc *sc = ucom->sc_parent;
535
536         DPRINTF("onoff = %d\n", onoff);
537
538         if (onoff)
539                 sc->sc_line |= UVSCOM_BREAK;
540         else
541                 sc->sc_line &= ~UVSCOM_BREAK;
542
543         uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line);
544 }
545
546 static int
547 uvscom_pre_param(struct ucom_softc *ucom, struct termios *t)
548 {
549         switch (t->c_ospeed) {
550                 case B150:
551                 case B300:
552                 case B600:
553                 case B1200:
554                 case B2400:
555                 case B4800:
556                 case B9600:
557                 case B19200:
558                 case B38400:
559                 case B57600:
560                 case B115200:
561                 default:
562                 return (EINVAL);
563         }
564         return (0);
565 }
566
567 static void
568 uvscom_cfg_param(struct ucom_softc *ucom, struct termios *t)
569 {
570         struct uvscom_softc *sc = ucom->sc_parent;
571         uint16_t value;
572
573         DPRINTF("\n");
574
575         switch (t->c_ospeed) {
576         case B150:
577                 value = UVSCOM_SPEED_150BPS;
578                 break;
579         case B300:
580                 value = UVSCOM_SPEED_300BPS;
581                 break;
582         case B600:
583                 value = UVSCOM_SPEED_600BPS;
584                 break;
585         case B1200:
586                 value = UVSCOM_SPEED_1200BPS;
587                 break;
588         case B2400:
589                 value = UVSCOM_SPEED_2400BPS;
590                 break;
591         case B4800:
592                 value = UVSCOM_SPEED_4800BPS;
593                 break;
594         case B9600:
595                 value = UVSCOM_SPEED_9600BPS;
596                 break;
597         case B19200:
598                 value = UVSCOM_SPEED_19200BPS;
599                 break;
600         case B38400:
601                 value = UVSCOM_SPEED_38400BPS;
602                 break;
603         case B57600:
604                 value = UVSCOM_SPEED_57600BPS;
605                 break;
606         case B115200:
607                 value = UVSCOM_SPEED_115200BPS;
608                 break;
609         default:
610                 return;
611         }
612
613         uvscom_cfg_write(sc, UVSCOM_SET_SPEED, value);
614
615         value = 0;
616
617         if (t->c_cflag & CSTOPB) {
618                 value |= UVSCOM_STOP_BIT_2;
619         }
620         if (t->c_cflag & PARENB) {
621                 if (t->c_cflag & PARODD) {
622                         value |= UVSCOM_PARITY_ODD;
623                 } else {
624                         value |= UVSCOM_PARITY_EVEN;
625                 }
626         } else {
627                 value |= UVSCOM_PARITY_NONE;
628         }
629
630         switch (t->c_cflag & CSIZE) {
631         case CS5:
632                 value |= UVSCOM_DATA_BIT_5;
633                 break;
634         case CS6:
635                 value |= UVSCOM_DATA_BIT_6;
636                 break;
637         case CS7:
638                 value |= UVSCOM_DATA_BIT_7;
639                 break;
640         default:
641         case CS8:
642                 value |= UVSCOM_DATA_BIT_8;
643                 break;
644         }
645
646         uvscom_cfg_write(sc, UVSCOM_SET_PARAM, value);
647 }
648
649 static int
650 uvscom_pre_open(struct ucom_softc *ucom)
651 {
652         struct uvscom_softc *sc = ucom->sc_parent;
653
654         DPRINTF("sc = %p\n", sc);
655
656         /* check if PC card was inserted */
657
658         if (sc->sc_unit_status & UVSCOM_NOCARD) {
659                 DPRINTF("no PC card!\n");
660                 return (ENXIO);
661         }
662         return (0);
663 }
664
665 static void
666 uvscom_cfg_open(struct ucom_softc *ucom)
667 {
668         struct uvscom_softc *sc = ucom->sc_parent;
669
670         DPRINTF("sc = %p\n", sc);
671
672         uvscom_cfg_read_status(sc);
673 }
674
675 static void
676 uvscom_cfg_close(struct ucom_softc *ucom)
677 {
678         struct uvscom_softc *sc = ucom->sc_parent;
679
680         DPRINTF("sc=%p\n", sc);
681
682         uvscom_cfg_write(sc, UVSCOM_SHUTDOWN, 0);
683 }
684
685 static void
686 uvscom_start_read(struct ucom_softc *ucom)
687 {
688         struct uvscom_softc *sc = ucom->sc_parent;
689
690         usbd_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_RD]);
691 }
692
693 static void
694 uvscom_stop_read(struct ucom_softc *ucom)
695 {
696         struct uvscom_softc *sc = ucom->sc_parent;
697
698         usbd_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_RD]);
699 }
700
701 static void
702 uvscom_start_write(struct ucom_softc *ucom)
703 {
704         struct uvscom_softc *sc = ucom->sc_parent;
705
706         usbd_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
707 }
708
709 static void
710 uvscom_stop_write(struct ucom_softc *ucom)
711 {
712         struct uvscom_softc *sc = ucom->sc_parent;
713
714         usbd_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
715 }
716
717 static void
718 uvscom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
719 {
720         struct uvscom_softc *sc = ucom->sc_parent;
721
722         *lsr = sc->sc_lsr;
723         *msr = sc->sc_msr;
724 }
725
726 static void
727 uvscom_cfg_write(struct uvscom_softc *sc, uint8_t index, uint16_t value)
728 {
729         struct usb_device_request req;
730         usb_error_t err;
731
732         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
733         req.bRequest = index;
734         USETW(req.wValue, value);
735         USETW(req.wIndex, 0);
736         USETW(req.wLength, 0);
737
738         err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
739             &req, NULL, 0, 1000);
740         if (err) {
741                 DPRINTFN(0, "device request failed, err=%s "
742                     "(ignored)\n", usbd_errstr(err));
743         }
744 }
745
746 static uint16_t
747 uvscom_cfg_read_status(struct uvscom_softc *sc)
748 {
749         struct usb_device_request req;
750         usb_error_t err;
751         uint8_t data[2];
752
753         req.bmRequestType = UT_READ_VENDOR_DEVICE;
754         req.bRequest = UVSCOM_READ_STATUS;
755         USETW(req.wValue, 0);
756         USETW(req.wIndex, 0);
757         USETW(req.wLength, 2);
758
759         err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
760             &req, data, 0, 1000);
761         if (err) {
762                 DPRINTFN(0, "device request failed, err=%s "
763                     "(ignored)\n", usbd_errstr(err));
764         }
765         return (data[0] | (data[1] << 8));
766 }
767
768 static void
769 uvscom_poll(struct ucom_softc *ucom)
770 {
771         struct uvscom_softc *sc = ucom->sc_parent;
772         usbd_transfer_poll(sc->sc_xfer, UVSCOM_N_TRANSFER);
773 }