uslcom.c revision 193045
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/usb/serial/uslcom.c 193045 2009-05-29 18:46:57Z 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 "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
40static int uslcom_debug = 0;
41
42SYSCTL_NODE(_hw_usb, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom");
43SYSCTL_INT(_hw_usb_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
87enum {
88	USLCOM_BULK_DT_WR,
89	USLCOM_BULK_DT_RD,
90	USLCOM_N_TRANSFER,
91};
92
93struct uslcom_softc {
94	struct ucom_super_softc sc_super_ucom;
95	struct ucom_softc sc_ucom;
96
97	struct usb_xfer *sc_xfer[USLCOM_N_TRANSFER];
98	struct usb_device *sc_udev;
99	struct mtx sc_mtx;
100
101	uint8_t		 sc_msr;
102	uint8_t		 sc_lsr;
103};
104
105static device_probe_t uslcom_probe;
106static device_attach_t uslcom_attach;
107static device_detach_t uslcom_detach;
108
109static usb_callback_t uslcom_write_callback;
110static usb_callback_t uslcom_read_callback;
111
112static void uslcom_open(struct ucom_softc *);
113static void uslcom_close(struct ucom_softc *);
114static void uslcom_set_dtr(struct ucom_softc *, uint8_t);
115static void uslcom_set_rts(struct ucom_softc *, uint8_t);
116static void uslcom_set_break(struct ucom_softc *, uint8_t);
117static int uslcom_pre_param(struct ucom_softc *, struct termios *);
118static void uslcom_param(struct ucom_softc *, struct termios *);
119static void uslcom_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
120static void uslcom_start_read(struct ucom_softc *);
121static void uslcom_stop_read(struct ucom_softc *);
122static void uslcom_start_write(struct ucom_softc *);
123static void uslcom_stop_write(struct ucom_softc *);
124
125static const struct usb_config uslcom_config[USLCOM_N_TRANSFER] = {
126
127	[USLCOM_BULK_DT_WR] = {
128		.type = UE_BULK,
129		.endpoint = UE_ADDR_ANY,
130		.direction = UE_DIR_OUT,
131		.bufsize = USLCOM_BULK_BUF_SIZE,
132		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
133		.callback = &uslcom_write_callback,
134	},
135
136	[USLCOM_BULK_DT_RD] = {
137		.type = UE_BULK,
138		.endpoint = UE_ADDR_ANY,
139		.direction = UE_DIR_IN,
140		.bufsize = USLCOM_BULK_BUF_SIZE,
141		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
142		.callback = &uslcom_read_callback,
143	},
144};
145
146struct ucom_callback uslcom_callback = {
147	.usb2_com_cfg_open = &uslcom_open,
148	.usb2_com_cfg_close = &uslcom_close,
149	.usb2_com_cfg_get_status = &uslcom_get_status,
150	.usb2_com_cfg_set_dtr = &uslcom_set_dtr,
151	.usb2_com_cfg_set_rts = &uslcom_set_rts,
152	.usb2_com_cfg_set_break = &uslcom_set_break,
153	.usb2_com_cfg_param = &uslcom_param,
154	.usb2_com_pre_param = &uslcom_pre_param,
155	.usb2_com_start_read = &uslcom_start_read,
156	.usb2_com_stop_read = &uslcom_stop_read,
157	.usb2_com_start_write = &uslcom_start_write,
158	.usb2_com_stop_write = &uslcom_stop_write,
159};
160
161static const struct usb_device_id uslcom_devs[] = {
162    { USB_VPI(USB_VENDOR_BALTECH,	USB_PRODUCT_BALTECH_CARDREADER, 0) },
163    { USB_VPI(USB_VENDOR_DYNASTREAM,	USB_PRODUCT_DYNASTREAM_ANTDEVBOARD, 0) },
164    { USB_VPI(USB_VENDOR_JABLOTRON,	USB_PRODUCT_JABLOTRON_PC60B, 0) },
165    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_ARGUSISP, 0) },
166    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_CRUMB128, 0) },
167    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_DEGREE, 0) },
168    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_BURNSIDE, 0) },
169    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_HELICOM, 0) },
170    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_LIPOWSKY_HARP, 0) },
171    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_LIPOWSKY_JTAG, 0) },
172    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_LIPOWSKY_LIN, 0) },
173    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_POLOLU, 0) },
174    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_CP2102, 0) },
175    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_CP210X_2, 0) },
176    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_SUUNTO, 0) },
177    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_TRAQMATE, 0) },
178    { USB_VPI(USB_VENDOR_SILABS2,	USB_PRODUCT_SILABS2_DCU11CLONE, 0) },
179    { USB_VPI(USB_VENDOR_USI,		USB_PRODUCT_USI_MC60, 0) },
180};
181
182static device_method_t uslcom_methods[] = {
183	DEVMETHOD(device_probe, uslcom_probe),
184	DEVMETHOD(device_attach, uslcom_attach),
185	DEVMETHOD(device_detach, uslcom_detach),
186	{0, 0}
187};
188
189static devclass_t uslcom_devclass;
190
191static driver_t uslcom_driver = {
192	.name = "uslcom",
193	.methods = uslcom_methods,
194	.size = sizeof(struct uslcom_softc),
195};
196
197DRIVER_MODULE(uslcom, uhub, uslcom_driver, uslcom_devclass, NULL, 0);
198MODULE_DEPEND(uslcom, ucom, 1, 1, 1);
199MODULE_DEPEND(uslcom, usb, 1, 1, 1);
200MODULE_VERSION(uslcom, 1);
201
202static int
203uslcom_probe(device_t dev)
204{
205	struct usb_attach_arg *uaa = device_get_ivars(dev);
206
207	DPRINTFN(11, "\n");
208
209	if (uaa->usb_mode != USB_MODE_HOST) {
210		return (ENXIO);
211	}
212	if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) {
213		return (ENXIO);
214	}
215	if (uaa->info.bIfaceIndex != USLCOM_IFACE_INDEX) {
216		return (ENXIO);
217	}
218	return (usb2_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa));
219}
220
221static int
222uslcom_attach(device_t dev)
223{
224	struct usb_attach_arg *uaa = device_get_ivars(dev);
225	struct uslcom_softc *sc = device_get_softc(dev);
226	int error;
227
228	DPRINTFN(11, "\n");
229
230	device_set_usb2_desc(dev);
231	mtx_init(&sc->sc_mtx, "uslcom", NULL, MTX_DEF);
232
233	sc->sc_udev = uaa->device;
234
235	error = usb2_transfer_setup(uaa->device,
236	    &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config,
237	    USLCOM_N_TRANSFER, sc, &sc->sc_mtx);
238	if (error) {
239		DPRINTF("one or more missing USB endpoints, "
240		    "error=%s\n", usb2_errstr(error));
241		goto detach;
242	}
243	/* clear stall at first run */
244	mtx_lock(&sc->sc_mtx);
245	usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]);
246	usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]);
247	mtx_unlock(&sc->sc_mtx);
248
249	error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
250	    &uslcom_callback, &sc->sc_mtx);
251	if (error) {
252		goto detach;
253	}
254	return (0);
255
256detach:
257	uslcom_detach(dev);
258	return (ENXIO);
259}
260
261static int
262uslcom_detach(device_t dev)
263{
264	struct uslcom_softc *sc = device_get_softc(dev);
265
266	DPRINTF("sc=%p\n", sc);
267
268	usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
269	usb2_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER);
270	mtx_destroy(&sc->sc_mtx);
271
272	return (0);
273}
274
275static void
276uslcom_open(struct ucom_softc *ucom)
277{
278	struct uslcom_softc *sc = ucom->sc_parent;
279	struct usb_device_request req;
280
281	req.bmRequestType = USLCOM_WRITE;
282	req.bRequest = USLCOM_UART;
283	USETW(req.wValue, USLCOM_UART_ENABLE);
284	USETW(req.wIndex, USLCOM_PORT_NO);
285	USETW(req.wLength, 0);
286
287        if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
288	    &req, NULL, 0, 1000)) {
289		DPRINTF("UART enable failed (ignored)\n");
290	}
291}
292
293static void
294uslcom_close(struct ucom_softc *ucom)
295{
296	struct uslcom_softc *sc = ucom->sc_parent;
297	struct usb_device_request req;
298
299	req.bmRequestType = USLCOM_WRITE;
300	req.bRequest = USLCOM_UART;
301	USETW(req.wValue, USLCOM_UART_DISABLE);
302	USETW(req.wIndex, USLCOM_PORT_NO);
303	USETW(req.wLength, 0);
304
305        if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
306	    &req, NULL, 0, 1000)) {
307		DPRINTF("UART disable failed (ignored)\n");
308	}
309}
310
311static void
312uslcom_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
313{
314        struct uslcom_softc *sc = ucom->sc_parent;
315	struct usb_device_request req;
316	uint16_t ctl;
317
318        DPRINTF("onoff = %d\n", onoff);
319
320	ctl = onoff ? USLCOM_CTRL_DTR_ON : 0;
321	ctl |= USLCOM_CTRL_DTR_SET;
322
323	req.bmRequestType = USLCOM_WRITE;
324	req.bRequest = USLCOM_CTRL;
325	USETW(req.wValue, ctl);
326	USETW(req.wIndex, USLCOM_PORT_NO);
327	USETW(req.wLength, 0);
328
329        if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
330	    &req, NULL, 0, 1000)) {
331		DPRINTF("Setting DTR failed (ignored)\n");
332	}
333}
334
335static void
336uslcom_set_rts(struct ucom_softc *ucom, uint8_t onoff)
337{
338        struct uslcom_softc *sc = ucom->sc_parent;
339	struct usb_device_request req;
340	uint16_t ctl;
341
342        DPRINTF("onoff = %d\n", onoff);
343
344	ctl = onoff ? USLCOM_CTRL_RTS_ON : 0;
345	ctl |= USLCOM_CTRL_RTS_SET;
346
347	req.bmRequestType = USLCOM_WRITE;
348	req.bRequest = USLCOM_CTRL;
349	USETW(req.wValue, ctl);
350	USETW(req.wIndex, USLCOM_PORT_NO);
351	USETW(req.wLength, 0);
352
353        if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
354	    &req, NULL, 0, 1000)) {
355		DPRINTF("Setting DTR failed (ignored)\n");
356	}
357}
358
359static int
360uslcom_pre_param(struct ucom_softc *ucom, struct termios *t)
361{
362	if (t->c_ospeed <= 0 || t->c_ospeed > 921600)
363		return (EINVAL);
364	return (0);
365}
366
367static void
368uslcom_param(struct ucom_softc *ucom, struct termios *t)
369{
370	struct uslcom_softc *sc = ucom->sc_parent;
371	struct usb_device_request req;
372	uint16_t data;
373
374	DPRINTF("\n");
375
376	req.bmRequestType = USLCOM_WRITE;
377	req.bRequest = USLCOM_BAUD_RATE;
378	USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed);
379	USETW(req.wIndex, USLCOM_PORT_NO);
380	USETW(req.wLength, 0);
381
382        if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
383	    &req, NULL, 0, 1000)) {
384		DPRINTF("Set baudrate failed (ignored)\n");
385	}
386
387	if (t->c_cflag & CSTOPB)
388		data = USLCOM_STOP_BITS_2;
389	else
390		data = USLCOM_STOP_BITS_1;
391	if (t->c_cflag & PARENB) {
392		if (t->c_cflag & PARODD)
393			data |= USLCOM_PARITY_ODD;
394		else
395			data |= USLCOM_PARITY_EVEN;
396	} else
397		data |= USLCOM_PARITY_NONE;
398	switch (t->c_cflag & CSIZE) {
399	case CS5:
400		data |= USLCOM_SET_DATA_BITS(5);
401		break;
402	case CS6:
403		data |= USLCOM_SET_DATA_BITS(6);
404		break;
405	case CS7:
406		data |= USLCOM_SET_DATA_BITS(7);
407		break;
408	case CS8:
409		data |= USLCOM_SET_DATA_BITS(8);
410		break;
411	}
412
413	req.bmRequestType = USLCOM_WRITE;
414	req.bRequest = USLCOM_DATA;
415	USETW(req.wValue, data);
416	USETW(req.wIndex, USLCOM_PORT_NO);
417	USETW(req.wLength, 0);
418
419        if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
420	    &req, NULL, 0, 1000)) {
421		DPRINTF("Set format failed (ignored)\n");
422	}
423	return;
424}
425
426static void
427uslcom_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
428{
429	struct uslcom_softc *sc = ucom->sc_parent;
430
431	DPRINTF("\n");
432
433	*lsr = sc->sc_lsr;
434	*msr = sc->sc_msr;
435}
436
437static void
438uslcom_set_break(struct ucom_softc *ucom, uint8_t onoff)
439{
440        struct uslcom_softc *sc = ucom->sc_parent;
441	struct usb_device_request req;
442	uint16_t brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF;
443
444	req.bmRequestType = USLCOM_WRITE;
445	req.bRequest = USLCOM_BREAK;
446	USETW(req.wValue, brk);
447	USETW(req.wIndex, USLCOM_PORT_NO);
448	USETW(req.wLength, 0);
449
450        if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
451	    &req, NULL, 0, 1000)) {
452		DPRINTF("Set BREAK failed (ignored)\n");
453	}
454}
455
456static void
457uslcom_write_callback(struct usb_xfer *xfer)
458{
459	struct uslcom_softc *sc = xfer->priv_sc;
460	uint32_t actlen;
461
462	switch (USB_GET_STATE(xfer)) {
463	case USB_ST_SETUP:
464	case USB_ST_TRANSFERRED:
465tr_setup:
466		if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
467		    USLCOM_BULK_BUF_SIZE, &actlen)) {
468
469			DPRINTF("actlen = %d\n", actlen);
470
471			xfer->frlengths[0] = actlen;
472			usb2_start_hardware(xfer);
473		}
474		return;
475
476	default:			/* Error */
477		if (xfer->error != USB_ERR_CANCELLED) {
478			/* try to clear stall first */
479			xfer->flags.stall_pipe = 1;
480			goto tr_setup;
481		}
482		return;
483	}
484}
485
486static void
487uslcom_read_callback(struct usb_xfer *xfer)
488{
489	struct uslcom_softc *sc = xfer->priv_sc;
490
491	switch (USB_GET_STATE(xfer)) {
492	case USB_ST_TRANSFERRED:
493		usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
494
495	case USB_ST_SETUP:
496tr_setup:
497		xfer->frlengths[0] = xfer->max_data_length;
498		usb2_start_hardware(xfer);
499		return;
500
501	default:			/* Error */
502		if (xfer->error != USB_ERR_CANCELLED) {
503			/* try to clear stall first */
504			xfer->flags.stall_pipe = 1;
505			goto tr_setup;
506		}
507		return;
508	}
509}
510
511static void
512uslcom_start_read(struct ucom_softc *ucom)
513{
514	struct uslcom_softc *sc = ucom->sc_parent;
515
516	/* start read endpoint */
517	usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]);
518}
519
520static void
521uslcom_stop_read(struct ucom_softc *ucom)
522{
523	struct uslcom_softc *sc = ucom->sc_parent;
524
525	/* stop read endpoint */
526	usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]);
527}
528
529static void
530uslcom_start_write(struct ucom_softc *ucom)
531{
532	struct uslcom_softc *sc = ucom->sc_parent;
533
534	usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]);
535}
536
537static void
538uslcom_stop_write(struct ucom_softc *ucom)
539{
540	struct uslcom_softc *sc = ucom->sc_parent;
541
542	usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]);
543}
544