xref: /openbsd-src/sys/dev/usb/ukspan.c (revision 7350f337b9e3eb4461d99580e625c7ef148d107c)
1 /*
2  * Copyright (c) 2019 Cody Cutler <ccutler@csail.mit.edu>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /*
18  * I don't know of any technical documentation for the Keyspan USA-19HS. I
19  * inspected the Linux driver (drivers/usb/serial/keyspan_usa90msg.h) to learn
20  * the device message format and the procedure for setting the baud rate.
21  */
22 
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/tty.h>
26 
27 #include <dev/usb/usb.h>
28 #include <dev/usb/usbdevs.h>
29 #include <dev/usb/usbdi.h>
30 #include <dev/usb/usbdi_util.h>
31 
32 #include <dev/usb/ucomvar.h>
33 
34 /*#define UKSPAN_DEBUG */
35 
36 #ifdef UKSPAN_DEBUG
37 	#define DPRINTF(x...)	do { printf(x); } while (0)
38 #else
39 	#define DPRINTF(x...)	do { ; } while (0)
40 #endif
41 
42 #define UKSPAN_PARITY_NONE	0x0
43 #define UKSPAN_PARITY_ODD	0x08
44 #define UKSPAN_PARITY_EVEN	0x18
45 
46 #define UKSPAN_DATA_5		0x0
47 #define UKSPAN_DATA_6		0x1
48 #define UKSPAN_DATA_7		0x2
49 #define UKSPAN_DATA_8		0x3
50 
51 #define UKSPAN_STOP_1		0x0
52 #define UKSPAN_STOP_2		0x4
53 
54 #define UKSPAN_MAGIC		0x2
55 
56 #define UKSPAN_CLOCK		14769231
57 
58 /*
59  * The following USB indexes and endpoint addresses may be specific to the
60  * Keyspan USA19HS device
61  */
62 #define UKSPAN_CONFIG_IDX	1
63 #define UKSPAN_IFACE_IDX	0
64 
65 #define UKSPAN_EA_BULKIN	(UE_DIR_IN  | 1)
66 #define UKSPAN_EA_BULKOUT	(UE_DIR_OUT | 1)
67 #define UKSPAN_EA_CONFIGIN	(UE_DIR_IN  | 2)
68 #define UKSPAN_EA_CONFIGOUT	(UE_DIR_OUT | 2)
69 
70 /* Sent to device on control out endpoint */
71 struct ukspan_cmsg {
72 	uint8_t	setclock;
73 	uint8_t	baudlo;
74 	uint8_t	baudhi;
75 	uint8_t	setlcr;
76 	uint8_t	lcr;
77 	uint8_t	setrxmode;
78 	uint8_t	rxmode;
79 	uint8_t	settxmode;
80 	uint8_t	txmode;
81 	uint8_t	settxflowcontrol;
82 	uint8_t	txflowcontrol;
83 	uint8_t	setrxflowcontrol;
84 	uint8_t	rxflowcontrol;
85 	uint8_t	sendxoff;
86 	uint8_t	sendxon;
87 	uint8_t	xonchar;
88 	uint8_t	xoffchar;
89 	uint8_t	sendchar;
90 	uint8_t	txchar;
91 	uint8_t	setrts;
92 	uint8_t	rts;
93 	uint8_t	setdtr;
94 	uint8_t	dtr;
95 
96 	uint8_t	rxforwardingchars;
97 	uint8_t	rxforwardingtimeoutms;
98 	uint8_t	txacksetting;
99 
100 	uint8_t	portenabled;
101 	uint8_t	txflush;
102 	uint8_t	txbreak;
103 	uint8_t	loopbackmode;
104 
105 	uint8_t	rxflush;
106 	uint8_t	rxforward;
107 	uint8_t	cancelrxoff;
108 	uint8_t	returnstatus;
109 } __packed;
110 
111 /* Received from device on control in endpoint */
112 struct ukspan_smsg {
113 	uint8_t msr;
114 	uint8_t	cts;
115 	uint8_t	dcd;
116 	uint8_t	dsr;
117 	uint8_t	ri;
118 	uint8_t	txxoff;
119 	uint8_t	rxbreak;
120 	uint8_t	rxoverrun;
121 	uint8_t	rxparity;
122 	uint8_t	rxframe;
123 	uint8_t	portstate;
124 	uint8_t	messageack;
125 	uint8_t	charack;
126 	uint8_t	controlresp;
127 } __packed;
128 
129 struct ukspan_softc {
130 	struct device sc_dev;
131 	struct usbd_device *udev;
132 	struct usbd_interface *iface;
133 	struct usbd_pipe *cout_pipe;
134 	struct usbd_pipe *cin_pipe;
135 	struct usbd_xfer *ixfer;
136 	struct usbd_xfer *oxfer;
137 	struct device *ucom_dev;
138 	struct ukspan_smsg smsg;
139 	struct ukspan_cmsg cmsg;
140 	u_char lsr;
141 	u_char msr;
142 };
143 
144 int  ukspan_match(struct device *, void *, void *);
145 void ukspan_attach(struct device *, struct device *, void *);
146 int  ukspan_detach(struct device *, int);
147 
148 void ukspan_close(void *, int);
149 int  ukspan_open(void *, int);
150 int  ukspan_param(void *, int, struct termios *);
151 void ukspan_set(void *, int, int, int);
152 void ukspan_get_status(void *, int, u_char *, u_char *);
153 
154 void ukspan_cmsg_init(bool, struct ukspan_cmsg *);
155 int  ukspan_cmsg_send(struct ukspan_softc *);
156 void ukspan_incb(struct usbd_xfer *, void *, usbd_status);
157 void ukspan_outcb(struct usbd_xfer *, void *, usbd_status);
158 void ukspan_destroy(struct ukspan_softc *);
159 
160 struct cfdriver ukspan_cd = {
161 	NULL, "ukspan", DV_DULL
162 };
163 
164 const struct cfattach ukspan_ca = {
165 	sizeof(struct ukspan_softc), ukspan_match, ukspan_attach,
166 	ukspan_detach
167 };
168 
169 static const struct usb_devno ukspan_devs[] = {
170 	{ USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19HS },
171 };
172 
173 static struct ucom_methods ukspan_methods = {
174 	.ucom_get_status = ukspan_get_status,
175 	.ucom_set = ukspan_set,
176 	.ucom_param = ukspan_param,
177 	.ucom_ioctl = NULL,
178 	.ucom_open = ukspan_open,
179 	.ucom_close = ukspan_close,
180 	.ucom_read = NULL,
181 	.ucom_write = NULL,
182 };
183 
184 int
185 ukspan_match(struct device *parent, void *match, void *aux)
186 {
187 	struct usb_attach_arg *uaa = aux;
188 
189 	if (uaa->iface != NULL)
190 		return UMATCH_NONE;
191 
192 	int found = usb_lookup(ukspan_devs, uaa->vendor, uaa->product) != NULL;
193 	return found ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
194 }
195 
196 void
197 ukspan_attach(struct device *parent, struct device *self, void *aux)
198 {
199 	struct ukspan_softc *sc = (struct ukspan_softc *)self;
200 	struct usb_attach_arg *uaa = aux;
201 	struct usbd_device *dev = uaa->device;
202 	struct ucom_attach_args uca = {0};
203 	usb_endpoint_descriptor_t *ed;
204 	const char *devname = sc->sc_dev.dv_xname;
205 	usbd_status err;
206 	int t1, t2, t3, t4;
207 
208 	DPRINTF("attach\n");
209 
210 	sc->udev = dev;
211 	sc->cin_pipe = sc->cout_pipe = NULL;
212 	sc->ixfer = sc->oxfer = NULL;
213 	sc->ucom_dev = NULL;
214 
215 	/*
216 	 * Switch to configuration 1 where the transfer mode of the input
217 	 * endpoints is bulk instead of interrupt, as ucom expects
218 	 */
219 	err = usbd_set_config_index(sc->udev, UKSPAN_CONFIG_IDX, 1);
220 	if (err) {
221 		printf("%s: set config failed\n", devname);
222 		goto fail;
223 	}
224 
225 	err = usbd_device2interface_handle(sc->udev, UKSPAN_IFACE_IDX,
226 	    &sc->iface);
227 	if (err) {
228 		printf("%s: get interface failed\n", devname);
229 		goto fail;
230 	}
231 
232 	ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_BULKIN);
233 	t1 = UE_GET_XFERTYPE(ed->bmAttributes);
234 	uca.ibufsize = UGETW(ed->wMaxPacketSize);
235 	uca.bulkin = UKSPAN_EA_BULKIN;
236 
237 	ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_BULKOUT);
238 	t2 = UE_GET_XFERTYPE(ed->bmAttributes);
239 	uca.obufsize = UGETW(ed->wMaxPacketSize);
240 	uca.bulkout = UKSPAN_EA_BULKOUT;
241 
242 	ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_CONFIGIN);
243 	t3 = UE_GET_XFERTYPE(ed->bmAttributes);
244 	if (UGETW(ed->wMaxPacketSize) < sizeof(struct ukspan_smsg)) {
245 		printf("%s: in config packet size too small\n", devname);
246 		goto fail;
247 	}
248 
249 	ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_CONFIGOUT);
250 	t4 = UE_GET_XFERTYPE(ed->bmAttributes);
251 	if (UGETW(ed->wMaxPacketSize) < sizeof(struct ukspan_cmsg)) {
252 		printf("%s: out config packet size too small\n", devname);
253 		goto fail;
254 	}
255 
256 	if (t1 != UE_BULK || t2 != UE_BULK || t3 != UE_BULK || t4 != UE_BULK) {
257 		printf("%s: unexpected xfertypes %x %x %x %x != %x\n", devname,
258 		    t1, t2, t3, t4, UE_BULK);
259 		goto fail;
260 	}
261 
262 	/* Resource acquisition starts here */
263 	err = usbd_open_pipe(sc->iface, UKSPAN_EA_CONFIGOUT, USBD_EXCLUSIVE_USE,
264 	    &sc->cout_pipe);
265 	if (err) {
266 		printf("%s: failed to create control out pipe\n", devname);
267 		goto fail;
268 	}
269 
270 	err = usbd_open_pipe(sc->iface, UKSPAN_EA_CONFIGIN, USBD_EXCLUSIVE_USE,
271 	    &sc->cin_pipe);
272 	if (err) {
273 		printf("%s: failed to create control out pipe\n", devname);
274 		goto fail;
275 	}
276 
277 	sc->ixfer = usbd_alloc_xfer(sc->udev);
278 	sc->oxfer = usbd_alloc_xfer(sc->udev);
279 	if (!sc->ixfer || !sc->oxfer) {
280 		printf("%s: failed to allocate xfers\n", devname);
281 		goto fail;
282 	}
283 
284 	usbd_setup_xfer(sc->ixfer, sc->cin_pipe, sc, &sc->smsg,
285 	    sizeof(sc->smsg), 0, USBD_NO_TIMEOUT, ukspan_incb);
286 	err = usbd_transfer(sc->ixfer);
287 	if (err && err != USBD_IN_PROGRESS) {
288 		printf("%s: failed to start ixfer\n", devname);
289 		goto fail;
290 	}
291 
292 	uca.portno = UCOM_UNK_PORTNO;
293 	uca.ibufsizepad = uca.ibufsize;
294 	uca.opkthdrlen = 0;
295 	uca.device = dev;
296 	uca.iface = sc->iface;
297 	uca.methods = &ukspan_methods;
298 	uca.arg = sc;
299 	uca.info = NULL;
300 
301 	sc->ucom_dev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
302 
303 	DPRINTF("attach done\n");
304 
305 	return;
306 fail:
307 	ukspan_destroy(sc);
308 	usbd_deactivate(sc->udev);
309 }
310 
311 int
312 ukspan_detach(struct device *self, int flags)
313 {
314 	struct ukspan_softc *sc = (struct ukspan_softc *)self;
315 	DPRINTF("detach\n");
316 
317 	ukspan_destroy(sc);
318 
319 	if (sc->ucom_dev) {
320 		config_detach(sc->ucom_dev, flags);
321 		sc->ucom_dev = NULL;
322 	}
323 	return 0;
324 }
325 
326 void
327 ukspan_outcb(struct usbd_xfer *xfer, void *priv, usbd_status status)
328 {
329 	struct ukspan_softc *sc = priv;
330 	const char *devname = sc->sc_dev.dv_xname;
331 
332 	DPRINTF("outcb\n");
333 
334 	if (usbd_is_dying(sc->udev)) {
335 		DPRINTF("usb dying\n");
336 		return;
337 	}
338 	if (status != USBD_NORMAL_COMPLETION) {
339 		printf("%s: oxfer failed\n", devname);
340 		return;
341 	}
342 }
343 
344 void
345 ukspan_incb(struct usbd_xfer *xfer, void *priv, usbd_status status)
346 {
347 	struct ukspan_softc *sc = priv;
348 	const char *devname = sc->sc_dev.dv_xname;
349 	const struct ukspan_smsg *smsg = &sc->smsg;
350 	usbd_status err;
351 	u_int32_t len;
352 
353 	DPRINTF("incb\n");
354 
355 	if (usbd_is_dying(sc->udev)) {
356 		printf("%s: usb dying\n", devname);
357 		return;
358 	}
359 	if (!sc->cin_pipe || !sc->ixfer) {
360 		printf("%s: no cin_pipe, but not dying?\n", devname);
361 		return;
362 	}
363 	if (status != USBD_NORMAL_COMPLETION) {
364 		if (status != USBD_NOT_STARTED && status != USBD_CANCELLED)
365 			printf("%s: ixfer failed\n", devname);
366 		return;
367 	}
368 
369 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
370 	if (len < sizeof(struct ukspan_smsg)) {
371 		printf("%s: short read\n", devname);
372 		return;
373 	}
374 
375 	/* The device provides the actual MSR register */
376 	sc->msr = smsg->msr;
377 	/* But not LSR... */
378 	sc->lsr = (smsg->rxoverrun ? ULSR_OE : 0) |
379 		  (smsg->rxparity  ? ULSR_PE : 0) |
380 		  (smsg->rxframe   ? ULSR_FE : 0) |
381 		  (smsg->rxbreak   ? ULSR_BI : 0);
382 	ucom_status_change((struct ucom_softc *)sc->ucom_dev);
383 
384 	usbd_setup_xfer(sc->ixfer, sc->cin_pipe, sc, &sc->smsg,
385 	    sizeof(sc->smsg), USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT,
386 	    ukspan_incb);
387 	err = usbd_transfer(sc->ixfer);
388 	if (err && err != USBD_IN_PROGRESS)
389 		printf("%s: usbd transfer failed\n", devname);
390 }
391 
392 void
393 ukspan_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
394 {
395 	struct ukspan_softc *sc = addr;
396 	DPRINTF("get status\n");
397 	if (lsr)
398 		*lsr = sc->lsr;
399 	if (msr)
400 		*msr = sc->msr;
401 }
402 
403 void
404 ukspan_cmsg_init(bool opening, struct ukspan_cmsg *omsg)
405 {
406 	bzero(omsg, sizeof(*omsg));
407 
408 	omsg->xonchar = 17;
409 	omsg->xoffchar = 19;
410 
411 	omsg->rxforwardingchars = 16;
412 	omsg->rxforwardingtimeoutms = 16;
413 	omsg->txacksetting = 0;
414 	omsg->txbreak = 0;
415 	if (opening) {
416 		omsg->portenabled = 1;
417 		omsg->rxflush = 1;
418 	}
419 }
420 
421 int
422 ukspan_cmsg_send(struct ukspan_softc *sc)
423 {
424 	const char *devname = sc->sc_dev.dv_xname;
425 	usbd_status err;
426 
427 	usbd_setup_xfer(sc->oxfer, sc->cout_pipe, sc, &sc->cmsg,
428 	    sizeof(sc->cmsg), USBD_SYNCHRONOUS, USBD_NO_TIMEOUT, ukspan_outcb);
429 	err = usbd_transfer(sc->oxfer);
430 	if (err != USBD_NORMAL_COMPLETION) {
431 		printf("%s: control xfer failed\n", devname);
432 		return EIO;
433 	}
434 	return 0;
435 }
436 
437 void
438 ukspan_set(void *addr, int portno, int reg, int onoff)
439 {
440 	struct ukspan_softc *sc = addr;
441 	const char *devname = sc->sc_dev.dv_xname;
442 	DPRINTF("set %#x = %#x\n", reg, onoff);
443 	int flag = !!onoff;
444 	switch (reg) {
445 	case UCOM_SET_DTR:
446 		sc->cmsg.setdtr = 1;
447 		sc->cmsg.dtr = flag;
448 		break;
449 	case UCOM_SET_RTS:
450 		sc->cmsg.setrts = 1;
451 		sc->cmsg.rts = flag;
452 		break;
453 	case UCOM_SET_BREAK:
454 		sc->cmsg.txbreak = flag;
455 		break;
456 	default:
457 		printf("%s: unhandled reg %#x\n", devname, reg);
458 		return;
459 	}
460 	ukspan_cmsg_send(sc);
461 }
462 
463 int
464 ukspan_param(void *addr, int portno, struct termios *ti)
465 {
466 	struct ukspan_softc *sc = addr;
467 	const char *devname = sc->sc_dev.dv_xname;
468 	struct ukspan_cmsg *cmsg = &sc->cmsg;
469 	speed_t baud;
470 	tcflag_t cflag;
471 	u_int32_t div;
472 	u_int8_t lcr;
473 
474 	DPRINTF("param: %#x %#x %#x\n", ti->c_ospeed, ti->c_cflag, ti->c_iflag);
475 
476 	/* Set baud */
477 	div = 1;
478 	baud = ti->c_ospeed;
479 	switch (baud) {
480 	case B300:
481 	case B600:
482 	case B1200:
483 	case B2400:
484 	case B4800:
485 	case B9600:
486 	case B19200:
487 	case B38400:
488 	case B57600:
489 	case B115200:
490 	case B230400:
491 		div = UKSPAN_CLOCK / (baud * 16);
492 		break;
493 	default:
494 		printf("%s: unexpected baud: %d\n", devname, baud);
495 		return EINVAL;
496 	}
497 
498 	cmsg->setclock = 1;
499 	cmsg->baudlo = div & 0xff;
500 	cmsg->baudhi = div >> 8;
501 
502 	cmsg->setrxmode = 1;
503 	cmsg->settxmode = 1;
504 	if (baud > 57600)
505 		cmsg->rxmode = cmsg->txmode = UKSPAN_MAGIC;
506 	else
507 		cmsg->rxmode = cmsg->txmode = 0;
508 
509 	/* Set parity, data, and stop bits */
510 	if ((cflag & CIGNORE) == 0) {
511 		cflag = ti->c_cflag;
512 		if (cflag & PARENB)
513 			lcr = (cflag & PARODD) ? UKSPAN_PARITY_ODD :
514 			    UKSPAN_PARITY_EVEN;
515 		else
516 			lcr = UKSPAN_PARITY_NONE;
517 		switch (cflag & CSIZE) {
518 		case CS5:
519 			lcr |= UKSPAN_DATA_5;
520 			break;
521 		case CS6:
522 			lcr |= UKSPAN_DATA_6;
523 			break;
524 		case CS7:
525 			lcr |= UKSPAN_DATA_7;
526 			break;
527 		case CS8:
528 			lcr |= UKSPAN_DATA_8;
529 			break;
530 		}
531 
532 		lcr |= (cflag & CSTOPB) ? UKSPAN_STOP_2 : UKSPAN_STOP_1;
533 
534 		cmsg->setlcr = 1;
535 		cmsg->lcr = lcr;
536 	}
537 
538 	/* XXX flow control? */
539 
540 	ukspan_cmsg_send(sc);
541 	return 0;
542 }
543 
544 int
545 ukspan_open(void *addr, int portno)
546 {
547 	struct ukspan_softc *sc = addr;
548 	int ret;
549 
550 	DPRINTF("open\n");
551 	if (usbd_is_dying(sc->udev)) {
552 		DPRINTF("usb dying\n");
553 		return ENXIO;
554 	}
555 
556 	ukspan_cmsg_init(true, &sc->cmsg);
557 	ret = ukspan_cmsg_send(sc);
558 	return ret;
559 }
560 
561 void
562 ukspan_close(void *addr, int portno)
563 {
564 	struct ukspan_softc *sc = addr;
565 	DPRINTF("close\n");
566 	if (usbd_is_dying(sc->udev)) {
567 		DPRINTF("usb dying\n");
568 		return;
569 	}
570 	ukspan_cmsg_init(false, &sc->cmsg);
571 	ukspan_cmsg_send(sc);
572 }
573 
574 void
575 ukspan_destroy(struct ukspan_softc *sc)
576 {
577 	DPRINTF("destroy\n");
578 	if (sc->cin_pipe) {
579 		usbd_close_pipe(sc->cin_pipe);
580 		sc->cin_pipe = NULL;
581 	}
582 	if (sc->cout_pipe) {
583 		usbd_close_pipe(sc->cout_pipe);
584 		sc->cout_pipe = NULL;
585 	}
586 	if (sc->oxfer) {
587 		usbd_free_xfer(sc->oxfer);
588 		sc->oxfer = NULL;
589 	}
590 	if (sc->ixfer) {
591 		usbd_free_xfer(sc->ixfer);
592 		sc->ixfer = NULL;
593 	}
594 }
595