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