xref: /openbsd/sys/dev/usb/umct.c (revision 4bdff4be)
1 /*	$OpenBSD: umct.c,v 1.50 2022/07/02 08:50:42 visa Exp $	*/
2 /*	$NetBSD: umct.c,v 1.10 2003/02/23 04:20:07 simonb Exp $	*/
3 /*
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Ichiro FUKUHARA (ichiro@ichiro.org).
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * MCT USB-RS232 Interface Controller
34  * http://www.mct.com.tw/prod/rs232.html
35  * http://www.dlink.com/products/usb/dsbs25
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/ioctl.h>
43 #include <sys/conf.h>
44 #include <sys/tty.h>
45 #include <sys/device.h>
46 
47 #include <dev/usb/usb.h>
48 #include <dev/usb/usbcdc.h>
49 
50 #include <dev/usb/usbdi.h>
51 #include <dev/usb/usbdi_util.h>
52 #include <dev/usb/usbdevs.h>
53 
54 #include <dev/usb/ucomvar.h>
55 
56 #include <dev/usb/umct.h>
57 
58 #ifdef UMCT_DEBUG
59 #define DPRINTFN(n, x)  do { if (umctdebug > (n)) printf x; } while (0)
60 int	umctdebug = 0;
61 #else
62 #define DPRINTFN(n, x)
63 #endif
64 #define DPRINTF(x) DPRINTFN(0, x)
65 
66 #define	UMCT_IFACE_INDEX	0
67 
68 struct	umct_softc {
69 	struct device		 sc_dev;	/* base device */
70 	struct usbd_device	*sc_udev;	/* USB device */
71 	struct usbd_interface	*sc_iface;	/* interface */
72 	int			 sc_iface_number;	/* interface number */
73 	u_int16_t		 sc_product;
74 
75 	int			 sc_intr_number;	/* interrupt number */
76 	struct usbd_pipe	*sc_intr_pipe;	/* interrupt pipe */
77 	u_char			*sc_intr_buf;	/* interrupt buffer */
78 	int			 sc_isize;
79 
80 	struct usb_cdc_line_state sc_line_state;	/* current line state */
81 	u_char			 sc_dtr;	/* current DTR state */
82 	u_char			 sc_rts;	/* current RTS state */
83 	u_char			 sc_break;	/* set break */
84 
85 	u_char			 sc_status;
86 
87 	struct device		*sc_subdev;	/* ucom device */
88 
89 	u_char			 sc_lsr;	/* Local status register */
90 	u_char			 sc_msr;	/* umct status register */
91 
92 	u_int			 last_lcr;	/* keep lcr register */
93 };
94 
95 /*
96  * These are the maximum number of bytes transferred per frame.
97  * The output buffer size cannot be increased due to the size encoding.
98  */
99 #define UMCTIBUFSIZE 256
100 #define UMCTOBUFSIZE 256
101 
102 void umct_init(struct umct_softc *);
103 void umct_set_baudrate(struct umct_softc *, u_int, int);
104 void umct_set_lcr(struct umct_softc *, u_int);
105 void umct_intr(struct usbd_xfer *, void *, usbd_status);
106 
107 void umct_set(void *, int, int, int);
108 void umct_dtr(struct umct_softc *, int);
109 void umct_rts(struct umct_softc *, int);
110 void umct_break(struct umct_softc *, int);
111 void umct_set_line_state(struct umct_softc *);
112 void umct_get_status(void *, int portno, u_char *lsr, u_char *msr);
113 int  umct_param(void *, int, struct termios *);
114 int  umct_open(void *, int);
115 void umct_close(void *, int);
116 
117 const struct ucom_methods umct_methods = {
118 	umct_get_status,
119 	umct_set,
120 	umct_param,
121 	NULL,
122 	umct_open,
123 	umct_close,
124 	NULL,
125 	NULL,
126 };
127 
128 static const struct usb_devno umct_devs[] = {
129 	/* MCT USB-232 Interface Products */
130 	{ USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232 },
131 	/* Sitecom USB-232 Products */
132 	{ USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232 },
133 	/* D-Link DU-H3SP USB BAY Hub Products */
134 	{ USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232 },
135 	/* BELKIN F5U109 */
136 	{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U109 },
137 	/* BELKIN F5U409 */
138 	{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U409 },
139 };
140 
141 int umct_match(struct device *, void *, void *);
142 void umct_attach(struct device *, struct device *, void *);
143 int umct_detach(struct device *, int);
144 
145 struct cfdriver umct_cd = {
146 	NULL, "umct", DV_DULL
147 };
148 
149 const struct cfattach umct_ca = {
150 	sizeof(struct umct_softc), umct_match, umct_attach, umct_detach
151 };
152 
153 int
154 umct_match(struct device *parent, void *match, void *aux)
155 {
156 	struct usb_attach_arg *uaa = aux;
157 
158 	if (uaa->iface == NULL)
159 		return (UMATCH_NONE);
160 
161 	return (usb_lookup(umct_devs, uaa->vendor, uaa->product) != NULL ?
162 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
163 }
164 
165 void
166 umct_attach(struct device *parent, struct device *self, void *aux)
167 {
168 	struct umct_softc *sc = (struct umct_softc *)self;
169 	struct usb_attach_arg *uaa = aux;
170 	struct usbd_device *dev = uaa->device;
171 	usb_config_descriptor_t *cdesc;
172 	usb_interface_descriptor_t *id;
173 	usb_endpoint_descriptor_t *ed;
174 
175 	char *devname = sc->sc_dev.dv_xname;
176 	usbd_status err;
177 	int i;
178 	struct ucom_attach_args uca;
179 
180         sc->sc_udev = dev;
181 	sc->sc_product = uaa->product;
182 
183 	DPRINTF(("\n\numct attach: sc=%p\n", sc));
184 
185 	/* initialize endpoints */
186 	uca.bulkin = uca.bulkout = -1;
187 	sc->sc_intr_number = -1;
188 	sc->sc_intr_pipe = NULL;
189 
190 	/* get the config descriptor */
191 	cdesc = usbd_get_config_descriptor(sc->sc_udev);
192 
193 	if (cdesc == NULL) {
194 		printf("%s: failed to get configuration descriptor\n",
195 			sc->sc_dev.dv_xname);
196 		usbd_deactivate(sc->sc_udev);
197 		return;
198 	}
199 
200 	/* get the interface */
201 	err = usbd_device2interface_handle(dev, UMCT_IFACE_INDEX,
202 							&sc->sc_iface);
203 	if (err) {
204 		printf("\n%s: failed to get interface, err=%s\n",
205 			devname, usbd_errstr(err));
206 		usbd_deactivate(sc->sc_udev);
207 		return;
208 	}
209 
210 	/* Find the bulk{in,out} and interrupt endpoints */
211 
212 	id = usbd_get_interface_descriptor(sc->sc_iface);
213 	sc->sc_iface_number = id->bInterfaceNumber;
214 
215 	for (i = 0; i < id->bNumEndpoints; i++) {
216 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
217 		if (ed == NULL) {
218 			printf("%s: no endpoint descriptor for %d\n",
219 				sc->sc_dev.dv_xname, i);
220 			usbd_deactivate(sc->sc_udev);
221 			return;
222 		}
223 
224 		/*
225 		 * The Bulkin endpoint is marked as an interrupt. Since
226 		 * we can't rely on the endpoint descriptor order, we'll
227 		 * check the wMaxPacketSize field to differentiate.
228 		 */
229 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
230 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT &&
231 		    UGETW(ed->wMaxPacketSize) != 0x2) {
232 			uca.bulkin = ed->bEndpointAddress;
233 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
234 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
235 			uca.bulkout = ed->bEndpointAddress;
236 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
237 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
238 			sc->sc_intr_number = ed->bEndpointAddress;
239 			sc->sc_isize = UGETW(ed->wMaxPacketSize);
240 		}
241 	}
242 
243 	if (uca.bulkin == -1) {
244 		printf("%s: Could not find data bulk in\n",
245 			sc->sc_dev.dv_xname);
246 		usbd_deactivate(sc->sc_udev);
247 		return;
248 	}
249 
250 	if (uca.bulkout == -1) {
251 		printf("%s: Could not find data bulk out\n",
252 			sc->sc_dev.dv_xname);
253 		usbd_deactivate(sc->sc_udev);
254 		return;
255 	}
256 
257 	if (sc->sc_intr_number== -1) {
258 		printf("%s: Could not find interrupt in\n",
259 			sc->sc_dev.dv_xname);
260 		usbd_deactivate(sc->sc_udev);
261 		return;
262 	}
263 
264 	sc->sc_dtr = sc->sc_rts = 0;
265 	uca.portno = UCOM_UNK_PORTNO;
266 	/* bulkin, bulkout set above */
267 	uca.ibufsize = UMCTIBUFSIZE;
268 	if (sc->sc_product == USB_PRODUCT_MCT_SITECOM_USB232)
269 		uca.obufsize = 16; /* device is broken */
270 	else
271 		uca.obufsize = UMCTOBUFSIZE;
272 	uca.ibufsizepad = UMCTIBUFSIZE;
273 	uca.opkthdrlen = 0;
274 	uca.device = dev;
275 	uca.iface = sc->sc_iface;
276 	uca.methods = &umct_methods;
277 	uca.arg = sc;
278 	uca.info = NULL;
279 
280 	umct_init(sc);
281 
282 	DPRINTF(("umct: in=0x%x out=0x%x intr=0x%x\n",
283 			uca.bulkin, uca.bulkout, sc->sc_intr_number ));
284 	sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
285 }
286 
287 int
288 umct_detach(struct device *self, int flags)
289 {
290 	struct umct_softc *sc = (struct umct_softc *)self;
291 	int rv = 0;
292 
293 	DPRINTF(("umct_detach: sc=%p flags=%d\n", sc, flags));
294 
295         if (sc->sc_intr_pipe != NULL) {
296                 usbd_close_pipe(sc->sc_intr_pipe);
297 		free(sc->sc_intr_buf, M_USBDEV, sc->sc_isize);
298                 sc->sc_intr_pipe = NULL;
299         }
300 
301 	if (sc->sc_subdev != NULL) {
302 		rv = config_detach(sc->sc_subdev, flags);
303 		sc->sc_subdev = NULL;
304 	}
305 
306 	return (rv);
307 }
308 
309 void
310 umct_set_line_state(struct umct_softc *sc)
311 {
312 	usb_device_request_t req;
313 	uByte ls;
314 
315 	ls = (sc->sc_dtr ? MCR_DTR : 0) |
316 	     (sc->sc_rts ? MCR_RTS : 0);
317 
318 	DPRINTF(("umct_set_line_state: DTR=%d,RTS=%d,ls=%02x\n",
319 			sc->sc_dtr, sc->sc_rts, ls));
320 
321 	req.bmRequestType = UMCT_SET_REQUEST;
322 	req.bRequest = REQ_SET_MCR;
323 	USETW(req.wValue, 0);
324 	USETW(req.wIndex, sc->sc_iface_number);
325 	USETW(req.wLength, LENGTH_SET_MCR);
326 
327 	(void)usbd_do_request(sc->sc_udev, &req, &ls);
328 }
329 
330 void
331 umct_set(void *addr, int portno, int reg, int onoff)
332 {
333 	struct umct_softc *sc = addr;
334 
335 	switch (reg) {
336 	case UCOM_SET_DTR:
337 		umct_dtr(sc, onoff);
338 		break;
339 	case UCOM_SET_RTS:
340 		umct_rts(sc, onoff);
341 		break;
342 	case UCOM_SET_BREAK:
343 		umct_break(sc, onoff);
344 		break;
345 	default:
346 		break;
347 	}
348 }
349 
350 void
351 umct_dtr(struct umct_softc *sc, int onoff)
352 {
353 
354 	DPRINTF(("umct_dtr: onoff=%d\n", onoff));
355 
356 	if (sc->sc_dtr == onoff)
357 		return;
358 	sc->sc_dtr = onoff;
359 
360 	umct_set_line_state(sc);
361 }
362 
363 void
364 umct_rts(struct umct_softc *sc, int onoff)
365 {
366 	DPRINTF(("umct_rts: onoff=%d\n", onoff));
367 
368 	if (sc->sc_rts == onoff)
369 		return;
370 	sc->sc_rts = onoff;
371 
372 	umct_set_line_state(sc);
373 }
374 
375 void
376 umct_break(struct umct_softc *sc, int onoff)
377 {
378 	DPRINTF(("umct_break: onoff=%d\n", onoff));
379 
380 	umct_set_lcr(sc, onoff ? sc->last_lcr | LCR_SET_BREAK :
381 		     sc->last_lcr);
382 }
383 
384 void
385 umct_set_lcr(struct umct_softc *sc, u_int data)
386 {
387 	usb_device_request_t req;
388 	uByte adata;
389 
390 	adata = data;
391 	req.bmRequestType = UMCT_SET_REQUEST;
392 	req.bRequest = REQ_SET_LCR;
393 	USETW(req.wValue, 0);
394 	USETW(req.wIndex, sc->sc_iface_number);
395 	USETW(req.wLength, LENGTH_SET_LCR);
396 
397 	/* XXX should check */
398 	(void)usbd_do_request(sc->sc_udev, &req, &adata);
399 }
400 
401 void
402 umct_set_baudrate(struct umct_softc *sc, u_int rate, int cts)
403 {
404         usb_device_request_t req;
405 	uDWord arate;
406 	u_int val;
407 
408 	if (sc->sc_product == USB_PRODUCT_MCT_SITECOM_USB232 ||
409 	    sc->sc_product == USB_PRODUCT_BELKIN_F5U109) {
410 		switch (rate) {
411 		case    300: val = 0x01; break;
412 		case    600: val = 0x02; break;
413 		case   1200: val = 0x03; break;
414 		case   2400: val = 0x04; break;
415 		case   4800: val = 0x06; break;
416 		case   9600: val = 0x08; break;
417 		case  19200: val = 0x09; break;
418 		case  38400: val = 0x0a; break;
419 		case  57600: val = 0x0b; break;
420 		case 115200: val = 0x0c; break;
421 		default:     val = -1; break;
422 		}
423 	} else {
424 		val = UMCT_BAUD_RATE(rate);
425 	}
426 	USETDW(arate, val);
427 
428         req.bmRequestType = UMCT_SET_REQUEST;
429         req.bRequest = REQ_SET_BAUD_RATE;
430         USETW(req.wValue, 0);
431         USETW(req.wIndex, sc->sc_iface_number);
432         USETW(req.wLength, LENGTH_BAUD_RATE);
433 
434 	/* XXX should check */
435         (void)usbd_do_request(sc->sc_udev, &req, arate);
436 
437 	/* unknown request, required after setting baud rate */
438 	USETDW(arate, 0);
439 	req.bmRequestType = UMCT_SET_REQUEST;
440 	req.bRequest = REQ_UNKNOWN1;
441 	USETW(req.wValue, 0);
442 	USETW(req.wIndex, sc->sc_iface_number);
443 	USETW(req.wLength, LENGTH_UNKNOWN1);
444 	(void)usbd_do_request(sc->sc_udev, &req, arate);
445 
446 	/* update CTS, also required after setting baud rate */
447 	USETDW(arate, cts);
448 	req.bmRequestType = UMCT_SET_REQUEST;
449 	req.bRequest = REQ_SET_CTS;
450 	USETW(req.wValue, 0);
451 	USETW(req.wIndex, sc->sc_iface_number);
452 	USETW(req.wLength, LENGTH_SET_CTS);
453 	(void)usbd_do_request(sc->sc_udev, &req, arate);
454 }
455 
456 void
457 umct_init(struct umct_softc *sc)
458 {
459 	umct_set_baudrate(sc, 9600, 0);
460 	umct_set_lcr(sc, LCR_DATA_BITS_8 | LCR_PARITY_NONE | LCR_STOP_BITS_1);
461 }
462 
463 int
464 umct_param(void *addr, int portno, struct termios *t)
465 {
466 	struct umct_softc *sc = addr;
467 	u_int data = 0;
468 
469 	DPRINTF(("umct_param: sc=%p\n", sc));
470 
471 	DPRINTF(("umct_param: BAUDRATE=%d\n", t->c_ospeed));
472 
473 	if (ISSET(t->c_cflag, CSTOPB))
474 		data |= LCR_STOP_BITS_2;
475 	else
476 		data |= LCR_STOP_BITS_1;
477 	if (ISSET(t->c_cflag, PARENB)) {
478 		if (ISSET(t->c_cflag, PARODD))
479 			data |= LCR_PARITY_ODD;
480 		else
481 			data |= LCR_PARITY_EVEN;
482 	} else
483 		data |= LCR_PARITY_NONE;
484 	switch (ISSET(t->c_cflag, CSIZE)) {
485 	case CS5:
486 		data |= LCR_DATA_BITS_5;
487 		break;
488 	case CS6:
489 		data |= LCR_DATA_BITS_6;
490 		break;
491 	case CS7:
492 		data |= LCR_DATA_BITS_7;
493 		break;
494 	case CS8:
495 		data |= LCR_DATA_BITS_8;
496 		break;
497 	}
498 
499 	umct_set_baudrate(sc, t->c_ospeed, ISSET(t->c_cflag, CRTSCTS));
500 
501 	sc->last_lcr = data;
502 	umct_set_lcr(sc, data);
503 
504 	return (0);
505 }
506 
507 int
508 umct_open(void *addr, int portno)
509 {
510 	struct umct_softc *sc = addr;
511 	int err, lcr_data;
512 
513 	if (usbd_is_dying(sc->sc_udev))
514 		return (EIO);
515 
516 	DPRINTF(("umct_open: sc=%p\n", sc));
517 
518 	/* initialize LCR */
519         lcr_data = LCR_DATA_BITS_8 | LCR_PARITY_NONE |
520 	    LCR_STOP_BITS_1;
521         umct_set_lcr(sc, lcr_data);
522 
523 	if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
524 		sc->sc_status = 0; /* clear status bit */
525 		sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
526 		err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_intr_number,
527 			USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc,
528 			sc->sc_intr_buf, sc->sc_isize,
529 			umct_intr, USBD_DEFAULT_INTERVAL);
530 		if (err) {
531 			DPRINTF(("%s: cannot open interrupt pipe (addr %d)\n",
532 				sc->sc_dev.dv_xname, sc->sc_intr_number));
533 					return (EIO);
534 		}
535 	}
536 
537 	return (0);
538 }
539 
540 void
541 umct_close(void *addr, int portno)
542 {
543 	struct umct_softc *sc = addr;
544 	int err;
545 
546 	if (usbd_is_dying(sc->sc_udev))
547 		return;
548 
549 	DPRINTF(("umct_close: close\n"));
550 
551 	if (sc->sc_intr_pipe != NULL) {
552 		err = usbd_close_pipe(sc->sc_intr_pipe);
553 		if (err)
554 			printf("%s: close interrupt pipe failed: %s\n",
555 				sc->sc_dev.dv_xname, usbd_errstr(err));
556 		free(sc->sc_intr_buf, M_USBDEV, sc->sc_isize);
557 		sc->sc_intr_pipe = NULL;
558 	}
559 }
560 
561 void
562 umct_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
563 {
564 	struct umct_softc *sc = priv;
565 	u_char *buf = sc->sc_intr_buf;
566 	u_char mstatus;
567 
568 	if (usbd_is_dying(sc->sc_udev))
569 		return;
570 
571 	if (status != USBD_NORMAL_COMPLETION) {
572 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
573 			return;
574 
575 		DPRINTF(("%s: abnormal status: %s\n", sc->sc_dev.dv_xname,
576 			usbd_errstr(status)));
577 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
578 		return;
579 	}
580 
581 	DPRINTF(("%s: umct status = MSR:%02x, LSR:%02x\n",
582 		 sc->sc_dev.dv_xname, buf[0],buf[1]));
583 
584 	sc->sc_lsr = sc->sc_msr = 0;
585 	mstatus = buf[0];
586 	if (ISSET(mstatus, MSR_DSR))
587 		sc->sc_msr |= UMSR_DSR;
588 	if (ISSET(mstatus, MSR_DCD))
589 		sc->sc_msr |= UMSR_DCD;
590 	ucom_status_change((struct ucom_softc *)sc->sc_subdev);
591 }
592 
593 void
594 umct_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
595 {
596 	struct umct_softc *sc = addr;
597 
598 	DPRINTF(("umct_get_status:\n"));
599 
600 	if (lsr != NULL)
601 		*lsr = sc->sc_lsr;
602 	if (msr != NULL)
603 		*msr = sc->sc_msr;
604 }
605