xref: /freebsd/sys/dev/usb/serial/uslcom.c (revision 39beb93c)
1 /*	$OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $	*/
2 
3 #include <sys/cdefs.h>
4 __FBSDID("$FreeBSD$");
5 
6 /*
7  * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 #include "usbdevs.h"
23 #include <dev/usb/usb.h>
24 #include <dev/usb/usb_mfunc.h>
25 #include <dev/usb/usb_error.h>
26 
27 #define	USB_DEBUG_VAR uslcom_debug
28 
29 #include <dev/usb/usb_core.h>
30 #include <dev/usb/usb_debug.h>
31 #include <dev/usb/usb_process.h>
32 #include <dev/usb/usb_request.h>
33 #include <dev/usb/usb_lookup.h>
34 #include <dev/usb/usb_util.h>
35 #include <dev/usb/usb_busdma.h>
36 
37 #include <dev/usb/serial/usb_serial.h>
38 
39 #if USB_DEBUG
40 static int uslcom_debug = 0;
41 
42 SYSCTL_NODE(_hw_usb2, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom");
43 SYSCTL_INT(_hw_usb2_uslcom, OID_AUTO, debug, CTLFLAG_RW,
44     &uslcom_debug, 0, "Debug level");
45 #endif
46 
47 #define	USLCOM_BULK_BUF_SIZE		1024
48 #define	USLCOM_CONFIG_INDEX	0
49 #define	USLCOM_IFACE_INDEX	0
50 
51 #define	USLCOM_SET_DATA_BITS(x)	((x) << 8)
52 
53 #define	USLCOM_WRITE		0x41
54 #define	USLCOM_READ		0xc1
55 
56 #define	USLCOM_UART		0x00
57 #define	USLCOM_BAUD_RATE	0x01
58 #define	USLCOM_DATA		0x03
59 #define	USLCOM_BREAK		0x05
60 #define	USLCOM_CTRL		0x07
61 
62 #define	USLCOM_UART_DISABLE	0x00
63 #define	USLCOM_UART_ENABLE	0x01
64 
65 #define	USLCOM_CTRL_DTR_ON	0x0001
66 #define	USLCOM_CTRL_DTR_SET	0x0100
67 #define	USLCOM_CTRL_RTS_ON	0x0002
68 #define	USLCOM_CTRL_RTS_SET	0x0200
69 #define	USLCOM_CTRL_CTS		0x0010
70 #define	USLCOM_CTRL_DSR		0x0020
71 #define	USLCOM_CTRL_DCD		0x0080
72 
73 #define	USLCOM_BAUD_REF		0x384000
74 
75 #define	USLCOM_STOP_BITS_1	0x00
76 #define	USLCOM_STOP_BITS_2	0x02
77 
78 #define	USLCOM_PARITY_NONE	0x00
79 #define	USLCOM_PARITY_ODD	0x10
80 #define	USLCOM_PARITY_EVEN	0x20
81 
82 #define	USLCOM_PORT_NO		0xFFFF /* XXX think this should be 0 --hps */
83 
84 #define	USLCOM_BREAK_OFF	0x00
85 #define	USLCOM_BREAK_ON		0x01
86 
87 enum {
88 	USLCOM_BULK_DT_WR,
89 	USLCOM_BULK_DT_RD,
90 	USLCOM_N_TRANSFER,
91 };
92 
93 struct uslcom_softc {
94 	struct usb2_com_super_softc sc_super_ucom;
95 	struct usb2_com_softc sc_ucom;
96 
97 	struct usb2_xfer *sc_xfer[USLCOM_N_TRANSFER];
98 	struct usb2_device *sc_udev;
99 
100 	uint8_t		 sc_msr;
101 	uint8_t		 sc_lsr;
102 };
103 
104 static device_probe_t uslcom_probe;
105 static device_attach_t uslcom_attach;
106 static device_detach_t uslcom_detach;
107 
108 static usb2_callback_t uslcom_write_callback;
109 static usb2_callback_t uslcom_read_callback;
110 
111 static void uslcom_open(struct usb2_com_softc *);
112 static void uslcom_close(struct usb2_com_softc *);
113 static void uslcom_set_dtr(struct usb2_com_softc *, uint8_t);
114 static void uslcom_set_rts(struct usb2_com_softc *, uint8_t);
115 static void uslcom_set_break(struct usb2_com_softc *, uint8_t);
116 static int uslcom_pre_param(struct usb2_com_softc *, struct termios *);
117 static void uslcom_param(struct usb2_com_softc *, struct termios *);
118 static void uslcom_get_status(struct usb2_com_softc *, uint8_t *, uint8_t *);
119 static void uslcom_start_read(struct usb2_com_softc *);
120 static void uslcom_stop_read(struct usb2_com_softc *);
121 static void uslcom_start_write(struct usb2_com_softc *);
122 static void uslcom_stop_write(struct usb2_com_softc *);
123 
124 static const struct usb2_config uslcom_config[USLCOM_N_TRANSFER] = {
125 
126 	[USLCOM_BULK_DT_WR] = {
127 		.type = UE_BULK,
128 		.endpoint = UE_ADDR_ANY,
129 		.direction = UE_DIR_OUT,
130 		.mh.bufsize = USLCOM_BULK_BUF_SIZE,
131 		.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
132 		.mh.callback = &uslcom_write_callback,
133 	},
134 
135 	[USLCOM_BULK_DT_RD] = {
136 		.type = UE_BULK,
137 		.endpoint = UE_ADDR_ANY,
138 		.direction = UE_DIR_IN,
139 		.mh.bufsize = USLCOM_BULK_BUF_SIZE,
140 		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
141 		.mh.callback = &uslcom_read_callback,
142 	},
143 };
144 
145 struct usb2_com_callback uslcom_callback = {
146 	.usb2_com_cfg_open = &uslcom_open,
147 	.usb2_com_cfg_close = &uslcom_close,
148 	.usb2_com_cfg_get_status = &uslcom_get_status,
149 	.usb2_com_cfg_set_dtr = &uslcom_set_dtr,
150 	.usb2_com_cfg_set_rts = &uslcom_set_rts,
151 	.usb2_com_cfg_set_break = &uslcom_set_break,
152 	.usb2_com_cfg_param = &uslcom_param,
153 	.usb2_com_pre_param = &uslcom_pre_param,
154 	.usb2_com_start_read = &uslcom_start_read,
155 	.usb2_com_stop_read = &uslcom_stop_read,
156 	.usb2_com_start_write = &uslcom_start_write,
157 	.usb2_com_stop_write = &uslcom_stop_write,
158 };
159 
160 static const struct usb2_device_id uslcom_devs[] = {
161     { USB_VPI(USB_VENDOR_BALTECH,	USB_PRODUCT_BALTECH_CARDREADER, 0) },
162     { USB_VPI(USB_VENDOR_DYNASTREAM,	USB_PRODUCT_DYNASTREAM_ANTDEVBOARD, 0) },
163     { USB_VPI(USB_VENDOR_JABLOTRON,	USB_PRODUCT_JABLOTRON_PC60B, 0) },
164     { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_ARGUSISP, 0) },
165     { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_CRUMB128, 0) },
166     { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_DEGREE, 0) },
167     { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_BURNSIDE, 0) },
168     { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_HELICOM, 0) },
169     { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_LIPOWSKY_HARP, 0) },
170     { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_LIPOWSKY_JTAG, 0) },
171     { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_LIPOWSKY_LIN, 0) },
172     { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_POLOLU, 0) },
173     { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_CP2102, 0) },
174     { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_CP210X_2, 0) },
175     { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_SUUNTO, 0) },
176     { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_TRAQMATE, 0) },
177     { USB_VPI(USB_VENDOR_SILABS2,	USB_PRODUCT_SILABS2_DCU11CLONE, 0) },
178     { USB_VPI(USB_VENDOR_USI,		USB_PRODUCT_USI_MC60, 0) },
179 };
180 
181 static device_method_t uslcom_methods[] = {
182 	DEVMETHOD(device_probe, uslcom_probe),
183 	DEVMETHOD(device_attach, uslcom_attach),
184 	DEVMETHOD(device_detach, uslcom_detach),
185 	{0, 0}
186 };
187 
188 static devclass_t uslcom_devclass;
189 
190 static driver_t uslcom_driver = {
191 	.name = "uslcom",
192 	.methods = uslcom_methods,
193 	.size = sizeof(struct uslcom_softc),
194 };
195 
196 DRIVER_MODULE(uslcom, ushub, uslcom_driver, uslcom_devclass, NULL, 0);
197 MODULE_DEPEND(uslcom, ucom, 1, 1, 1);
198 MODULE_DEPEND(uslcom, usb, 1, 1, 1);
199 MODULE_VERSION(uslcom, 1);
200 
201 static int
202 uslcom_probe(device_t dev)
203 {
204 	struct usb2_attach_arg *uaa = device_get_ivars(dev);
205 
206 	DPRINTFN(11, "\n");
207 
208 	if (uaa->usb2_mode != USB_MODE_HOST) {
209 		return (ENXIO);
210 	}
211 	if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) {
212 		return (ENXIO);
213 	}
214 	if (uaa->info.bIfaceIndex != USLCOM_IFACE_INDEX) {
215 		return (ENXIO);
216 	}
217 	return (usb2_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa));
218 }
219 
220 static int
221 uslcom_attach(device_t dev)
222 {
223 	struct usb2_attach_arg *uaa = device_get_ivars(dev);
224 	struct uslcom_softc *sc = device_get_softc(dev);
225 	int error;
226 
227 	DPRINTFN(11, "\n");
228 
229 	device_set_usb2_desc(dev);
230 
231 	sc->sc_udev = uaa->device;
232 
233 	error = usb2_transfer_setup(uaa->device,
234 	    &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config,
235 	    USLCOM_N_TRANSFER, sc, &Giant);
236 	if (error) {
237 		DPRINTF("one or more missing USB endpoints, "
238 		    "error=%s\n", usb2_errstr(error));
239 		goto detach;
240 	}
241 	/* clear stall at first run */
242 	usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]);
243 	usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]);
244 
245 	error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
246 	    &uslcom_callback, &Giant);
247 	if (error) {
248 		goto detach;
249 	}
250 	return (0);
251 
252 detach:
253 	uslcom_detach(dev);
254 	return (ENXIO);
255 }
256 
257 static int
258 uslcom_detach(device_t dev)
259 {
260 	struct uslcom_softc *sc = device_get_softc(dev);
261 
262 	DPRINTF("sc=%p\n", sc);
263 
264 	usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
265 
266 	usb2_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER);
267 
268 	return (0);
269 }
270 
271 static void
272 uslcom_open(struct usb2_com_softc *ucom)
273 {
274 	struct uslcom_softc *sc = ucom->sc_parent;
275 	struct usb2_device_request req;
276 
277 	req.bmRequestType = USLCOM_WRITE;
278 	req.bRequest = USLCOM_UART;
279 	USETW(req.wValue, USLCOM_UART_ENABLE);
280 	USETW(req.wIndex, USLCOM_PORT_NO);
281 	USETW(req.wLength, 0);
282 
283         if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
284 	    &req, NULL, 0, 1000)) {
285 		DPRINTF("UART enable failed (ignored)\n");
286 	}
287 }
288 
289 static void
290 uslcom_close(struct usb2_com_softc *ucom)
291 {
292 	struct uslcom_softc *sc = ucom->sc_parent;
293 	struct usb2_device_request req;
294 
295 	req.bmRequestType = USLCOM_WRITE;
296 	req.bRequest = USLCOM_UART;
297 	USETW(req.wValue, USLCOM_UART_DISABLE);
298 	USETW(req.wIndex, USLCOM_PORT_NO);
299 	USETW(req.wLength, 0);
300 
301         if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
302 	    &req, NULL, 0, 1000)) {
303 		DPRINTF("UART disable failed (ignored)\n");
304 	}
305 }
306 
307 static void
308 uslcom_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
309 {
310         struct uslcom_softc *sc = ucom->sc_parent;
311 	struct usb2_device_request req;
312 	uint16_t ctl;
313 
314         DPRINTF("onoff = %d\n", onoff);
315 
316 	ctl = onoff ? USLCOM_CTRL_DTR_ON : 0;
317 	ctl |= USLCOM_CTRL_DTR_SET;
318 
319 	req.bmRequestType = USLCOM_WRITE;
320 	req.bRequest = USLCOM_CTRL;
321 	USETW(req.wValue, ctl);
322 	USETW(req.wIndex, USLCOM_PORT_NO);
323 	USETW(req.wLength, 0);
324 
325         if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
326 	    &req, NULL, 0, 1000)) {
327 		DPRINTF("Setting DTR failed (ignored)\n");
328 	}
329 }
330 
331 static void
332 uslcom_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
333 {
334         struct uslcom_softc *sc = ucom->sc_parent;
335 	struct usb2_device_request req;
336 	uint16_t ctl;
337 
338         DPRINTF("onoff = %d\n", onoff);
339 
340 	ctl = onoff ? USLCOM_CTRL_RTS_ON : 0;
341 	ctl |= USLCOM_CTRL_RTS_SET;
342 
343 	req.bmRequestType = USLCOM_WRITE;
344 	req.bRequest = USLCOM_CTRL;
345 	USETW(req.wValue, ctl);
346 	USETW(req.wIndex, USLCOM_PORT_NO);
347 	USETW(req.wLength, 0);
348 
349         if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
350 	    &req, NULL, 0, 1000)) {
351 		DPRINTF("Setting DTR failed (ignored)\n");
352 	}
353 }
354 
355 static int
356 uslcom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
357 {
358 	if (t->c_ospeed <= 0 || t->c_ospeed > 921600)
359 		return (EINVAL);
360 	return (0);
361 }
362 
363 static void
364 uslcom_param(struct usb2_com_softc *ucom, struct termios *t)
365 {
366 	struct uslcom_softc *sc = ucom->sc_parent;
367 	struct usb2_device_request req;
368 	uint16_t data;
369 
370 	DPRINTF("\n");
371 
372 	req.bmRequestType = USLCOM_WRITE;
373 	req.bRequest = USLCOM_BAUD_RATE;
374 	USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed);
375 	USETW(req.wIndex, USLCOM_PORT_NO);
376 	USETW(req.wLength, 0);
377 
378         if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
379 	    &req, NULL, 0, 1000)) {
380 		DPRINTF("Set baudrate failed (ignored)\n");
381 	}
382 
383 	if (t->c_cflag & CSTOPB)
384 		data = USLCOM_STOP_BITS_2;
385 	else
386 		data = USLCOM_STOP_BITS_1;
387 	if (t->c_cflag & PARENB) {
388 		if (t->c_cflag & PARODD)
389 			data |= USLCOM_PARITY_ODD;
390 		else
391 			data |= USLCOM_PARITY_EVEN;
392 	} else
393 		data |= USLCOM_PARITY_NONE;
394 	switch (t->c_cflag & CSIZE) {
395 	case CS5:
396 		data |= USLCOM_SET_DATA_BITS(5);
397 		break;
398 	case CS6:
399 		data |= USLCOM_SET_DATA_BITS(6);
400 		break;
401 	case CS7:
402 		data |= USLCOM_SET_DATA_BITS(7);
403 		break;
404 	case CS8:
405 		data |= USLCOM_SET_DATA_BITS(8);
406 		break;
407 	}
408 
409 	req.bmRequestType = USLCOM_WRITE;
410 	req.bRequest = USLCOM_DATA;
411 	USETW(req.wValue, data);
412 	USETW(req.wIndex, USLCOM_PORT_NO);
413 	USETW(req.wLength, 0);
414 
415         if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
416 	    &req, NULL, 0, 1000)) {
417 		DPRINTF("Set format failed (ignored)\n");
418 	}
419 	return;
420 }
421 
422 static void
423 uslcom_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
424 {
425 	struct uslcom_softc *sc = ucom->sc_parent;
426 
427 	DPRINTF("\n");
428 
429 	*lsr = sc->sc_lsr;
430 	*msr = sc->sc_msr;
431 }
432 
433 static void
434 uslcom_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
435 {
436         struct uslcom_softc *sc = ucom->sc_parent;
437 	struct usb2_device_request req;
438 	uint16_t brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF;
439 
440 	req.bmRequestType = USLCOM_WRITE;
441 	req.bRequest = USLCOM_BREAK;
442 	USETW(req.wValue, brk);
443 	USETW(req.wIndex, USLCOM_PORT_NO);
444 	USETW(req.wLength, 0);
445 
446         if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
447 	    &req, NULL, 0, 1000)) {
448 		DPRINTF("Set BREAK failed (ignored)\n");
449 	}
450 }
451 
452 static void
453 uslcom_write_callback(struct usb2_xfer *xfer)
454 {
455 	struct uslcom_softc *sc = xfer->priv_sc;
456 	uint32_t actlen;
457 
458 	switch (USB_GET_STATE(xfer)) {
459 	case USB_ST_SETUP:
460 	case USB_ST_TRANSFERRED:
461 tr_setup:
462 		if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
463 		    USLCOM_BULK_BUF_SIZE, &actlen)) {
464 
465 			DPRINTF("actlen = %d\n", actlen);
466 
467 			xfer->frlengths[0] = actlen;
468 			usb2_start_hardware(xfer);
469 		}
470 		return;
471 
472 	default:			/* Error */
473 		if (xfer->error != USB_ERR_CANCELLED) {
474 			/* try to clear stall first */
475 			xfer->flags.stall_pipe = 1;
476 			goto tr_setup;
477 		}
478 		return;
479 	}
480 }
481 
482 static void
483 uslcom_read_callback(struct usb2_xfer *xfer)
484 {
485 	struct uslcom_softc *sc = xfer->priv_sc;
486 
487 	switch (USB_GET_STATE(xfer)) {
488 	case USB_ST_TRANSFERRED:
489 		usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
490 
491 	case USB_ST_SETUP:
492 tr_setup:
493 		xfer->frlengths[0] = xfer->max_data_length;
494 		usb2_start_hardware(xfer);
495 		return;
496 
497 	default:			/* Error */
498 		if (xfer->error != USB_ERR_CANCELLED) {
499 			/* try to clear stall first */
500 			xfer->flags.stall_pipe = 1;
501 			goto tr_setup;
502 		}
503 		return;
504 	}
505 }
506 
507 static void
508 uslcom_start_read(struct usb2_com_softc *ucom)
509 {
510 	struct uslcom_softc *sc = ucom->sc_parent;
511 
512 	/* start read endpoint */
513 	usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]);
514 }
515 
516 static void
517 uslcom_stop_read(struct usb2_com_softc *ucom)
518 {
519 	struct uslcom_softc *sc = ucom->sc_parent;
520 
521 	/* stop read endpoint */
522 	usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]);
523 }
524 
525 static void
526 uslcom_start_write(struct usb2_com_softc *ucom)
527 {
528 	struct uslcom_softc *sc = ucom->sc_parent;
529 
530 	usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]);
531 }
532 
533 static void
534 uslcom_stop_write(struct usb2_com_softc *ucom)
535 {
536 	struct uslcom_softc *sc = ucom->sc_parent;
537 
538 	usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]);
539 }
540