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