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