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