xref: /openbsd-src/sys/dev/usb/uslcom.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: uslcom.c,v 1.31 2014/07/12 21:24:33 mpi 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/param.h>
20 #include <sys/systm.h>
21 #include <sys/kernel.h>
22 #include <sys/conf.h>
23 #include <sys/tty.h>
24 #include <sys/device.h>
25 
26 #include <dev/usb/usb.h>
27 #include <dev/usb/usbdi.h>
28 #include <dev/usb/usbdi_util.h>
29 #include <dev/usb/usbdevs.h>
30 
31 #include <dev/usb/usbdevs.h>
32 #include <dev/usb/ucomvar.h>
33 
34 #ifdef USLCOM_DEBUG
35 #define DPRINTFN(n, x)  do { if (uslcomdebug > (n)) printf x; } while (0)
36 int	uslcomdebug = 0;
37 #else
38 #define DPRINTFN(n, x)
39 #endif
40 #define DPRINTF(x) DPRINTFN(0, x)
41 
42 #define USLCOMBUFSZ		256
43 #define USLCOM_CONFIG_NO	0
44 #define USLCOM_IFACE_NO		0
45 
46 #define USLCOM_SET_DATA_BITS(x)	(x << 8)
47 
48 #define USLCOM_WRITE		0x41
49 #define USLCOM_READ		0xc1
50 
51 #define USLCOM_UART		0x00
52 #define USLCOM_SET_BAUD_DIV	0x01
53 #define USLCOM_DATA		0x03
54 #define USLCOM_BREAK		0x05
55 #define USLCOM_CTRL		0x07
56 #define USLCOM_SET_FLOW		0x13
57 #define USLCOM_SET_BAUD_RATE	0x1e
58 
59 #define USLCOM_UART_DISABLE	0x00
60 #define USLCOM_UART_ENABLE	0x01
61 
62 #define USLCOM_CTRL_DTR_ON	0x0001
63 #define USLCOM_CTRL_DTR_SET	0x0100
64 #define USLCOM_CTRL_RTS_ON	0x0002
65 #define USLCOM_CTRL_RTS_SET	0x0200
66 #define USLCOM_CTRL_CTS		0x0010
67 #define USLCOM_CTRL_DSR		0x0020
68 #define USLCOM_CTRL_DCD		0x0080
69 
70 #define USLCOM_BAUD_REF		3686400 /* 3.6864 MHz */
71 
72 #define USLCOM_STOP_BITS_1	0x00
73 #define USLCOM_STOP_BITS_2	0x02
74 
75 #define USLCOM_PARITY_NONE	0x00
76 #define USLCOM_PARITY_ODD	0x10
77 #define USLCOM_PARITY_EVEN	0x20
78 
79 #define USLCOM_BREAK_OFF	0x00
80 #define USLCOM_BREAK_ON		0x01
81 
82 /* USLCOM_SET_FLOW values - 1st word */
83 #define USLCOM_FLOW_DTR_ON	0x00000001 /* DTR static active */
84 #define USLCOM_FLOW_CTS_HS	0x00000008 /* CTS handshake */
85 /* USLCOM_SET_FLOW values - 2nd word */
86 #define USLCOM_FLOW_RTS_ON	0x00000040 /* RTS static active */
87 #define USLCOM_FLOW_RTS_HS	0x00000080 /* RTS handshake */
88 
89 struct uslcom_softc {
90 	struct device		 sc_dev;
91 	struct usbd_device	*sc_udev;
92 	struct usbd_interface	*sc_iface;
93 	struct device		*sc_subdev;
94 
95 	u_char			 sc_msr;
96 	u_char			 sc_lsr;
97 };
98 
99 void	uslcom_get_status(void *, int portno, u_char *lsr, u_char *msr);
100 void	uslcom_set(void *, int, int, int);
101 int	uslcom_param(void *, int, struct termios *);
102 int	uslcom_open(void *sc, int portno);
103 void	uslcom_close(void *, int);
104 void	uslcom_break(void *sc, int portno, int onoff);
105 
106 struct ucom_methods uslcom_methods = {
107 	uslcom_get_status,
108 	uslcom_set,
109 	uslcom_param,
110 	NULL,
111 	uslcom_open,
112 	uslcom_close,
113 	NULL,
114 	NULL,
115 };
116 
117 static const struct usb_devno uslcom_devs[] = {
118 	{ USB_VENDOR_BALTECH,		USB_PRODUCT_BALTECH_CARDREADER },
119 	{ USB_VENDOR_CLIPSAL,		USB_PRODUCT_CLIPSAL_5000CT2 },
120 	{ USB_VENDOR_CLIPSAL,		USB_PRODUCT_CLIPSAL_5500PACA },
121 	{ USB_VENDOR_CLIPSAL,		USB_PRODUCT_CLIPSAL_5500PCU },
122 	{ USB_VENDOR_CLIPSAL,		USB_PRODUCT_CLIPSAL_560884 },
123 	{ USB_VENDOR_CLIPSAL,		USB_PRODUCT_CLIPSAL_5800PC },
124 	{ USB_VENDOR_CLIPSAL,		USB_PRODUCT_CLIPSAL_C5000CT2 },
125 	{ USB_VENDOR_CLIPSAL,		USB_PRODUCT_CLIPSAL_L51xx },
126 	{ USB_VENDOR_DATAAPEX,		USB_PRODUCT_DATAAPEX_MULTICOM },
127 	{ USB_VENDOR_DELL,		USB_PRODUCT_DELL_DW700 },
128 	{ USB_VENDOR_DIGIANSWER,	USB_PRODUCT_DIGIANSWER_ZIGBEE802154 },
129 	{ USB_VENDOR_DYNASTREAM,	USB_PRODUCT_DYNASTREAM_ANT2USB },
130 	{ USB_VENDOR_DYNASTREAM,	USB_PRODUCT_DYNASTREAM_ANTDEVBOARD },
131 	{ USB_VENDOR_DYNASTREAM,	USB_PRODUCT_DYNASTREAM_ANTDEVBOARD2 },
132 	{ USB_VENDOR_ELV,		USB_PRODUCT_ELV_USBI2C },
133 	{ USB_VENDOR_FESTO,		USB_PRODUCT_FESTO_CMSP },
134 	{ USB_VENDOR_FESTO,		USB_PRODUCT_FESTO_CPX_USB },
135 	{ USB_VENDOR_FOXCONN,		USB_PRODUCT_FOXCONN_PIRELLI_DP_L10 },
136 	{ USB_VENDOR_FOXCONN,		USB_PRODUCT_FOXCONN_TCOM_TC_300 },
137 	{ USB_VENDOR_GEMPLUS,		USB_PRODUCT_GEMPLUS_PROXPU },
138 	{ USB_VENDOR_JABLOTRON,		USB_PRODUCT_JABLOTRON_PC60B },
139 	{ USB_VENDOR_KAMSTRUP,		USB_PRODUCT_KAMSTRUP_MBUS_250D },
140 	{ USB_VENDOR_KAMSTRUP,		USB_PRODUCT_KAMSTRUP_OPTICALEYE },
141 	{ USB_VENDOR_LINKINSTRUMENTS,	USB_PRODUCT_LINKINSTRUMENTS_MSO19 },
142 	{ USB_VENDOR_LINKINSTRUMENTS,	USB_PRODUCT_LINKINSTRUMENTS_MSO28 },
143 	{ USB_VENDOR_LINKINSTRUMENTS,	USB_PRODUCT_LINKINSTRUMENTS_MSO28_2 },
144 	{ USB_VENDOR_MEI,		USB_PRODUCT_MEI_CASHFLOW_SC },
145 	{ USB_VENDOR_MEI,		USB_PRODUCT_MEI_S2000 },
146 	{ USB_VENDOR_OWEN,		USB_PRODUCT_OWEN_AC4 },
147 	{ USB_VENDOR_PHILIPS,		USB_PRODUCT_PHILIPS_ACE1001 },
148 	{ USB_VENDOR_RENESAS,		USB_PRODUCT_RENESAS_RX610 },
149 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_AC_SERV_CAN },
150 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_AC_SERV_CIS },
151 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_AC_SERV_IBUS },
152 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_AC_SERV_OBD },
153 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_AEROCOMM },
154 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_AMBER_AMB2560 },
155 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_ARGUSISP },
156 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_ARKHAM_DS101_A },
157 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_ARKHAM_DS101_M },
158 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_ARYGON_MIFARE },
159 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_AVIT_USB_TTL },
160 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_BALLUFF_RFID },
161 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_BEI_VCP },
162 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_BSM7DUSB },
163 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_B_G_H3000 },
164 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_C2_EDGE_MODEM },
165 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_CP210X_1 },
166 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_CP210X_2 },
167 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_CP210X_3 },
168 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_CP210X_4 },
169 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_CRUMB128 },
170 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_CYGNAL },
171 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_CYGNAL_DEBUG },
172 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_CYGNAL_GPS },
173 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_DEGREECONT },
174 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_DEKTEK_DTAPLUS },
175 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_DESKTOPMOBILE },
176 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_EDG1228 },
177 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_EMS_C1007 },
178 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_HAMLINKUSB },
179 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_IMS_USB_RS422 },
180 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_INFINITY_MIC },
181 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_INSYS_MODEM },
182 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_IPLINK1220 },
183 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_IRZ_SG10 },
184 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_KYOCERA_GPS },
185 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_LIPOWSKY_HARP },
186 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_LIPOWSKY_JTAG },
187 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_LIPOWSKY_LIN },
188 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_MC35PU },
189 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_MJS_TOSLINK },
190 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_MSD_DASHHAWK },
191 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_MULTIPLEX_RC },
192 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_OPTRIS_MSPRO },
193 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_POLOLU },
194 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_PROCYON_AVS },
195 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_RIGBLASTER },
196 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_RIGTALK },
197 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_SB_PARAMOUNT_ME },
198 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_SUUNTO },
199 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_TAMSMASTER },
200 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_TELEGESIS_ETRX2 },
201 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_TRACIENT },
202 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_TRAQMATE },
203 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_USBCOUNT50 },
204 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_USBPULSE100 },
205 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_USBSCOPE50 },
206 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_USBWAVE12 },
207 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_VSTABI },
208 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_WAVIT },
209 	{ USB_VENDOR_SILABS,		USB_PRODUCT_SILABS_ZEPHYR_BIO },
210 	{ USB_VENDOR_SILABS2,		USB_PRODUCT_SILABS2_DCU11CLONE },
211 	{ USB_VENDOR_SILABS3,		USB_PRODUCT_SILABS3_GPRS_MODEM },
212 	{ USB_VENDOR_SILABS4,		USB_PRODUCT_SILABS4_100EU_MODEM },
213 	{ USB_VENDOR_SYNTECH,		USB_PRODUCT_SYNTECH_CIPHERLAB100 },
214 	{ USB_VENDOR_USI,		USB_PRODUCT_USI_MC60 },
215 	{ USB_VENDOR_VAISALA,		USB_PRODUCT_VAISALA_USBINSTCABLE },
216 	{ USB_VENDOR_WAGO,		USB_PRODUCT_WAGO_SERVICECABLE },
217 	{ USB_VENDOR_WAVESENSE,		USB_PRODUCT_WAVESENSE_JAZZ },
218 	{ USB_VENDOR_WIENERPLEINBAUS,	USB_PRODUCT_WIENERPLEINBAUS_CML },
219 	{ USB_VENDOR_WIENERPLEINBAUS,	USB_PRODUCT_WIENERPLEINBAUS_MPOD },
220 	{ USB_VENDOR_WIENERPLEINBAUS,	USB_PRODUCT_WIENERPLEINBAUS_PL512 },
221 	{ USB_VENDOR_WIENERPLEINBAUS,	USB_PRODUCT_WIENERPLEINBAUS_RCM },
222 };
223 
224 int uslcom_match(struct device *, void *, void *);
225 void uslcom_attach(struct device *, struct device *, void *);
226 int uslcom_detach(struct device *, int);
227 
228 struct cfdriver uslcom_cd = {
229 	NULL, "uslcom", DV_DULL
230 };
231 
232 const struct cfattach uslcom_ca = {
233 	sizeof(struct uslcom_softc), uslcom_match, uslcom_attach, uslcom_detach
234 };
235 
236 int
237 uslcom_match(struct device *parent, void *match, void *aux)
238 {
239 	struct usb_attach_arg *uaa = aux;
240 
241 	if (uaa->iface != NULL)
242 		return UMATCH_NONE;
243 
244 	return (usb_lookup(uslcom_devs, uaa->vendor, uaa->product) != NULL) ?
245 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
246 }
247 
248 void
249 uslcom_attach(struct device *parent, struct device *self, void *aux)
250 {
251 	struct uslcom_softc *sc = (struct uslcom_softc *)self;
252 	struct usb_attach_arg *uaa = aux;
253 	struct ucom_attach_args uca;
254 	usb_interface_descriptor_t *id;
255 	usb_endpoint_descriptor_t *ed;
256 	usbd_status error;
257 	int i;
258 
259 	bzero(&uca, sizeof(uca));
260 	sc->sc_udev = uaa->device;
261 
262 	if (usbd_set_config_index(sc->sc_udev, USLCOM_CONFIG_NO, 1) != 0) {
263 		printf("%s: could not set configuration no\n",
264 		    sc->sc_dev.dv_xname);
265 		usbd_deactivate(sc->sc_udev);
266 		return;
267 	}
268 
269 	/* get the first interface handle */
270 	error = usbd_device2interface_handle(sc->sc_udev, USLCOM_IFACE_NO,
271 	    &sc->sc_iface);
272 	if (error != 0) {
273 		printf("%s: could not get interface handle\n",
274 		    sc->sc_dev.dv_xname);
275 		usbd_deactivate(sc->sc_udev);
276 		return;
277 	}
278 
279 	id = usbd_get_interface_descriptor(sc->sc_iface);
280 
281 	uca.bulkin = uca.bulkout = -1;
282 	for (i = 0; i < id->bNumEndpoints; i++) {
283 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
284 		if (ed == NULL) {
285 			printf("%s: no endpoint descriptor found for %d\n",
286 			    sc->sc_dev.dv_xname, i);
287 			usbd_deactivate(sc->sc_udev);
288 			return;
289 		}
290 
291 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
292 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
293 			uca.bulkin = ed->bEndpointAddress;
294 		else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
295 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
296 			uca.bulkout = ed->bEndpointAddress;
297 	}
298 
299 	if (uca.bulkin == -1 || uca.bulkout == -1) {
300 		printf("%s: missing endpoint\n", sc->sc_dev.dv_xname);
301 		usbd_deactivate(sc->sc_udev);
302 		return;
303 	}
304 
305 	uca.ibufsize = USLCOMBUFSZ;
306 	uca.obufsize = USLCOMBUFSZ;
307 	uca.ibufsizepad = USLCOMBUFSZ;
308 	uca.opkthdrlen = 0;
309 	uca.device = sc->sc_udev;
310 	uca.iface = sc->sc_iface;
311 	uca.methods = &uslcom_methods;
312 	uca.arg = sc;
313 	uca.info = NULL;
314 
315 	sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
316 }
317 
318 int
319 uslcom_detach(struct device *self, int flags)
320 {
321 	struct uslcom_softc *sc = (struct uslcom_softc *)self;
322 	int rv = 0;
323 
324 	if (sc->sc_subdev != NULL) {
325 		rv = config_detach(sc->sc_subdev, flags);
326 		sc->sc_subdev = NULL;
327 	}
328 
329 	return (rv);
330 }
331 
332 int
333 uslcom_open(void *vsc, int portno)
334 {
335 	struct uslcom_softc *sc = vsc;
336 	usb_device_request_t req;
337 	usbd_status err;
338 
339 	if (usbd_is_dying(sc->sc_udev))
340 		return (EIO);
341 
342 	req.bmRequestType = USLCOM_WRITE;
343 	req.bRequest = USLCOM_UART;
344 	USETW(req.wValue, USLCOM_UART_ENABLE);
345 	USETW(req.wIndex, portno);
346 	USETW(req.wLength, 0);
347 	err = usbd_do_request(sc->sc_udev, &req, NULL);
348 	if (err)
349 		return (EIO);
350 
351 	return (0);
352 }
353 
354 void
355 uslcom_close(void *vsc, int portno)
356 {
357 	struct uslcom_softc *sc = vsc;
358 	usb_device_request_t req;
359 
360 	if (usbd_is_dying(sc->sc_udev))
361 		return;
362 
363 	req.bmRequestType = USLCOM_WRITE;
364 	req.bRequest = USLCOM_UART;
365 	USETW(req.wValue, USLCOM_UART_DISABLE);
366 	USETW(req.wIndex, portno);
367 	USETW(req.wLength, 0);
368 	usbd_do_request(sc->sc_udev, &req, NULL);
369 }
370 
371 void
372 uslcom_set(void *vsc, int portno, int reg, int onoff)
373 {
374 	struct uslcom_softc *sc = vsc;
375 	usb_device_request_t req;
376 	int ctl;
377 
378 	switch (reg) {
379 	case UCOM_SET_DTR:
380 		ctl = onoff ? USLCOM_CTRL_DTR_ON : 0;
381 		ctl |= USLCOM_CTRL_DTR_SET;
382 		break;
383 	case UCOM_SET_RTS:
384 		ctl = onoff ? USLCOM_CTRL_RTS_ON : 0;
385 		ctl |= USLCOM_CTRL_RTS_SET;
386 		break;
387 	case UCOM_SET_BREAK:
388 		uslcom_break(sc, portno, onoff);
389 		return;
390 	default:
391 		return;
392 	}
393 	req.bmRequestType = USLCOM_WRITE;
394 	req.bRequest = USLCOM_CTRL;
395 	USETW(req.wValue, ctl);
396 	USETW(req.wIndex, portno);
397 	USETW(req.wLength, 0);
398 	usbd_do_request(sc->sc_udev, &req, NULL);
399 }
400 
401 int
402 uslcom_param(void *vsc, int portno, struct termios *t)
403 {
404 	struct uslcom_softc *sc = (struct uslcom_softc *)vsc;
405 	usbd_status err;
406 	usb_device_request_t req;
407 	uint32_t baudrate, flowctrl[4];
408 	int data;
409 
410 	if (t->c_ospeed <= 0 || t->c_ospeed > 921600)
411 		return (EINVAL);
412 
413 	baudrate = t->c_ospeed;
414 	req.bmRequestType = USLCOM_WRITE;
415 	req.bRequest = USLCOM_SET_BAUD_RATE;
416 	USETW(req.wValue, 0);
417 	USETW(req.wIndex, portno);
418 	USETW(req.wLength, sizeof(baudrate));
419 	err = usbd_do_request(sc->sc_udev, &req, &baudrate);
420 	if (err)
421 		return (EIO);
422 
423 	if (ISSET(t->c_cflag, CSTOPB))
424 		data = USLCOM_STOP_BITS_2;
425 	else
426 		data = USLCOM_STOP_BITS_1;
427 	if (ISSET(t->c_cflag, PARENB)) {
428 		if (ISSET(t->c_cflag, PARODD))
429 			data |= USLCOM_PARITY_ODD;
430 		else
431 			data |= USLCOM_PARITY_EVEN;
432 	} else
433 		data |= USLCOM_PARITY_NONE;
434 	switch (ISSET(t->c_cflag, CSIZE)) {
435 	case CS5:
436 		data |= USLCOM_SET_DATA_BITS(5);
437 		break;
438 	case CS6:
439 		data |= USLCOM_SET_DATA_BITS(6);
440 		break;
441 	case CS7:
442 		data |= USLCOM_SET_DATA_BITS(7);
443 		break;
444 	case CS8:
445 		data |= USLCOM_SET_DATA_BITS(8);
446 		break;
447 	}
448 
449 	req.bmRequestType = USLCOM_WRITE;
450 	req.bRequest = USLCOM_DATA;
451 	USETW(req.wValue, data);
452 	USETW(req.wIndex, portno);
453 	USETW(req.wLength, 0);
454 	err = usbd_do_request(sc->sc_udev, &req, NULL);
455 	if (err)
456 		return (EIO);
457 
458 	if (ISSET(t->c_cflag, CRTSCTS)) {
459 		/*  rts/cts flow ctl */
460 		flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON | USLCOM_FLOW_CTS_HS);
461 		flowctrl[1] = htole32(USLCOM_FLOW_RTS_HS);
462 	} else {
463 		/* disable flow ctl */
464 		flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON);
465 		flowctrl[1] = htole32(USLCOM_FLOW_RTS_ON);
466 	}
467 	flowctrl[2] = 0;
468 	flowctrl[3] = 0;
469 
470 	req.bmRequestType = USLCOM_WRITE;
471 	req.bRequest = USLCOM_SET_FLOW;
472 	USETW(req.wValue, 0);
473 	USETW(req.wIndex, portno);
474 	USETW(req.wLength, sizeof(flowctrl));
475 	err = usbd_do_request(sc->sc_udev, &req, flowctrl);
476 	if (err)
477 		return (EIO);
478 
479 	return (0);
480 }
481 
482 void
483 uslcom_get_status(void *vsc, int portno, u_char *lsr, u_char *msr)
484 {
485 	struct uslcom_softc *sc = vsc;
486 
487 	if (msr != NULL)
488 		*msr = sc->sc_msr;
489 	if (lsr != NULL)
490 		*lsr = sc->sc_lsr;
491 }
492 
493 void
494 uslcom_break(void *vsc, int portno, int onoff)
495 {
496 	struct uslcom_softc *sc = vsc;
497 	usb_device_request_t req;
498 	int brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF;
499 
500 	req.bmRequestType = USLCOM_WRITE;
501 	req.bRequest = USLCOM_BREAK;
502 	USETW(req.wValue, brk);
503 	USETW(req.wIndex, portno);
504 	USETW(req.wLength, 0);
505 	usbd_do_request(sc->sc_udev, &req, NULL);
506 }
507