uslcom.c revision 188413
1/*	$OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $	*/
2
3#include <sys/cdefs.h>
4__FBSDID("$FreeBSD: head/sys/dev/usb2/serial/uslcom2.c 188413 2009-02-09 22:05:25Z thompsa $");
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 <dev/usb2/include/usb2_devid.h>
23#include <dev/usb2/include/usb2_standard.h>
24#include <dev/usb2/include/usb2_mfunc.h>
25#include <dev/usb2/include/usb2_error.h>
26
27#define	USB_DEBUG_VAR uslcom_debug
28
29#include <dev/usb2/core/usb2_core.h>
30#include <dev/usb2/core/usb2_debug.h>
31#include <dev/usb2/core/usb2_process.h>
32#include <dev/usb2/core/usb2_request.h>
33#include <dev/usb2/core/usb2_lookup.h>
34#include <dev/usb2/core/usb2_util.h>
35#include <dev/usb2/core/usb2_busdma.h>
36
37#include <dev/usb2/serial/usb2_serial.h>
38
39#if USB_DEBUG
40static int uslcom_debug = 0;
41
42SYSCTL_NODE(_hw_usb2, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom");
43SYSCTL_INT(_hw_usb2_uplcom, 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
87enum {
88	USLCOM_BULK_DT_WR,
89	USLCOM_BULK_DT_RD,
90	USLCOM_N_TRANSFER,
91};
92
93struct 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
104static device_probe_t uslcom_probe;
105static device_attach_t uslcom_attach;
106static device_detach_t uslcom_detach;
107
108static usb2_callback_t uslcom_write_callback;
109static usb2_callback_t uslcom_read_callback;
110
111static void uslcom_open(struct usb2_com_softc *);
112static void uslcom_close(struct usb2_com_softc *);
113static void uslcom_set_dtr(struct usb2_com_softc *, uint8_t);
114static void uslcom_set_rts(struct usb2_com_softc *, uint8_t);
115static void uslcom_set_break(struct usb2_com_softc *, uint8_t);
116static int uslcom_pre_param(struct usb2_com_softc *, struct termios *);
117static void uslcom_param(struct usb2_com_softc *, struct termios *);
118static void uslcom_get_status(struct usb2_com_softc *, uint8_t *, uint8_t *);
119static void uslcom_start_read(struct usb2_com_softc *);
120static void uslcom_stop_read(struct usb2_com_softc *);
121static void uslcom_start_write(struct usb2_com_softc *);
122static void uslcom_stop_write(struct usb2_com_softc *);
123
124static 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
145struct 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
160static 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
181static 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
188static devclass_t uslcom_devclass;
189
190static driver_t uslcom_driver = {
191	.name = "uplcom",
192	.methods = uslcom_methods,
193	.size = sizeof(struct uslcom_softc),
194};
195
196DRIVER_MODULE(uplcom, ushub, uslcom_driver, uslcom_devclass, NULL, 0);
197MODULE_DEPEND(uplcom, usb2_serial, 1, 1, 1);
198MODULE_DEPEND(uplcom, usb2_core, 1, 1, 1);
199MODULE_VERSION(uplcom, 1);
200
201static int
202uslcom_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
220static int
221uslcom_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
252detach:
253	uslcom_detach(dev);
254	return (ENXIO);
255}
256
257static int
258uslcom_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
271static void
272uslcom_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
289static void
290uslcom_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
307static void
308uslcom_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
331static void
332uslcom_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
355static int
356uslcom_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
363static void
364uslcom_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
422static void
423uslcom_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
433static void
434uslcom_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
452static void
453uslcom_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:
461tr_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
482static void
483uslcom_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:
492tr_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
507static void
508uslcom_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
516static void
517uslcom_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
525static void
526uslcom_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
533static void
534uslcom_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