xref: /openbsd/sys/dev/usb/uxrcom.c (revision 09467b48)
1 /*	$OpenBSD: uxrcom.c,v 1.2 2020/07/31 10:49:33 mglocker Exp $	*/
2 
3 /*
4  * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
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 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/kernel.h>
22 #include <sys/tty.h>
23 #include <sys/device.h>
24 
25 #include <dev/usb/usb.h>
26 #include <dev/usb/usbcdc.h>
27 #include <dev/usb/usbdi.h>
28 #include <dev/usb/usbdi_util.h>
29 #include <dev/usb/usbdevs.h>
30 
31 #include <dev/usb/ucomvar.h>
32 
33 #ifdef UXRCOM_DEBUG
34 #define DPRINTFN(n, x)  do { if (uxrcomdebug > (n)) printf x; } while (0)
35 int	uxrcomdebug = 0;
36 #else
37 #define DPRINTFN(n, x)
38 #endif
39 #define DPRINTF(x) DPRINTFN(0, x)
40 
41 #define UXRCOMBUFSZ		64
42 #define UXRCOM_INTR_IFACE_NO	0
43 #define UXRCOM_DATA_IFACE_NO	1
44 
45 #define XR_SET_REG		0
46 #define XR_GET_REGN		1
47 
48 #define XR_FLOW_CONTROL		0x000c
49 #define  XR_FLOW_CONTROL_ON	1
50 #define  XR_FLOW_CONTROL_OFF	0
51 #define XR_TX_BREAK		0x0014
52 #define  XR_TX_BREAK_ON		1
53 #define  XR_TX_BREAK_OFF	0
54 #define XR_GPIO_SET		0x001d
55 #define XR_GPIO_CLEAR		0x001e
56 #define  XR_GPIO3		(1 << 3)
57 #define  XR_GPIO5		(1 << 5)
58 
59 struct uxrcom_softc {
60 	struct device		sc_dev;
61 	struct usbd_device	*sc_udev;
62 	struct usbd_interface	*sc_intr_iface;
63 	struct usbd_interface	*sc_data_iface;
64 	struct device		*sc_subdev;
65 
66 	struct usb_cdc_line_state sc_line_state;
67 
68 	int			sc_intr_number;
69 	struct usbd_pipe	*sc_intr_pipe;
70 	struct usb_cdc_notification sc_intr_buf;
71 	u_char			sc_msr;
72 	u_char			sc_lsr;
73 };
74 
75 void	uxrcom_get_status(void *, int, u_char *, u_char *);
76 void	uxrcom_set(void *, int, int, int);
77 int	uxrcom_param(void *, int, struct termios *);
78 int	uxrcom_open(void *, int);
79 void	uxrcom_close(void *, int);
80 void	uxrcom_break(void *, int, int);
81 void	uxrcom_intr(struct usbd_xfer *, void *, usbd_status);
82 
83 struct ucom_methods uxrcom_methods = {
84 	uxrcom_get_status,
85 	uxrcom_set,
86 	uxrcom_param,
87 	NULL,
88 	uxrcom_open,
89 	uxrcom_close,
90 	NULL,
91 	NULL,
92 };
93 
94 static const struct usb_devno uxrcom_devs[] = {
95 	{ USB_VENDOR_EXAR,		USB_PRODUCT_EXAR_XR21V1410 },
96 };
97 
98 int uxrcom_match(struct device *, void *, void *);
99 void uxrcom_attach(struct device *, struct device *, void *);
100 int uxrcom_detach(struct device *, int);
101 
102 struct cfdriver uxrcom_cd = {
103 	NULL, "uxrcom", DV_DULL
104 };
105 
106 const struct cfattach uxrcom_ca = {
107 	sizeof(struct uxrcom_softc), uxrcom_match, uxrcom_attach, uxrcom_detach
108 };
109 
110 int
111 uxrcom_match(struct device *parent, void *match, void *aux)
112 {
113 	struct usb_attach_arg *uaa = aux;
114 
115 	if (uaa->iface == NULL)
116 		return UMATCH_NONE;
117 	if (uaa->ifaceno != UXRCOM_INTR_IFACE_NO)
118 		return UMATCH_NONE;
119 
120 	return (usb_lookup(uxrcom_devs, uaa->vendor, uaa->product) != NULL) ?
121 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
122 }
123 
124 void
125 uxrcom_attach(struct device *parent, struct device *self, void *aux)
126 {
127 	struct uxrcom_softc *sc = (struct uxrcom_softc *)self;
128 	struct usb_attach_arg *uaa = aux;
129 	struct ucom_attach_args uca;
130 	usb_interface_descriptor_t *id;
131 	usb_endpoint_descriptor_t *ed;
132 	usbd_status error;
133 	int i;
134 
135 	memset(&uca, 0, sizeof(uca));
136 	sc->sc_udev = uaa->device;
137 	sc->sc_intr_iface = uaa->iface;
138 	sc->sc_intr_number = -1;
139 
140 	id = usbd_get_interface_descriptor(sc->sc_intr_iface);
141 	for (i = 0; i < id->bNumEndpoints; i++) {
142 		ed = usbd_interface2endpoint_descriptor(sc->sc_intr_iface, i);
143 		if (ed == NULL) {
144 			printf("%s: no endpoint descriptor found for %d\n",
145 			    sc->sc_dev.dv_xname, i);
146 			usbd_deactivate(sc->sc_udev);
147 			return;
148 		}
149 
150 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
151 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT)
152 			sc->sc_intr_number = ed->bEndpointAddress;
153 	}
154 
155 	error = usbd_device2interface_handle(sc->sc_udev, UXRCOM_DATA_IFACE_NO,
156 	    &sc->sc_data_iface);
157 	if (error != 0) {
158 		printf("%s: could not get data interface handle\n",
159 		    sc->sc_dev.dv_xname);
160 		usbd_deactivate(sc->sc_udev);
161 		return;
162 	}
163 
164 	id = usbd_get_interface_descriptor(sc->sc_data_iface);
165 	uca.bulkin = uca.bulkout = -1;
166 	for (i = 0; i < id->bNumEndpoints; i++) {
167 		ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i);
168 		if (ed == NULL) {
169 			printf("%s: no endpoint descriptor found for %d\n",
170 			    sc->sc_dev.dv_xname, i);
171 			usbd_deactivate(sc->sc_udev);
172 			return;
173 		}
174 
175 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
176 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
177 			uca.bulkin = ed->bEndpointAddress;
178 		else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
179 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
180 			uca.bulkout = ed->bEndpointAddress;
181 	}
182 
183 	if (uca.bulkin == -1 || uca.bulkout == -1) {
184 		printf("%s: missing endpoint\n", sc->sc_dev.dv_xname);
185 		usbd_deactivate(sc->sc_udev);
186 		return;
187 	}
188 
189 	usbd_claim_iface(sc->sc_udev, UXRCOM_DATA_IFACE_NO);
190 
191 	uca.ibufsize = UXRCOMBUFSZ;
192 	uca.obufsize = UXRCOMBUFSZ;
193 	uca.ibufsizepad = UXRCOMBUFSZ;
194 	uca.opkthdrlen = 0;
195 	uca.device = sc->sc_udev;
196 	uca.iface = sc->sc_data_iface;
197 	uca.methods = &uxrcom_methods;
198 	uca.arg = sc;
199 	uca.info = NULL;
200 
201 	sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
202 }
203 
204 int
205 uxrcom_detach(struct device *self, int flags)
206 {
207 	struct uxrcom_softc *sc = (struct uxrcom_softc *)self;
208 	int rv = 0;
209 
210 	if (sc->sc_intr_pipe != NULL) {
211 		usbd_close_pipe(sc->sc_intr_pipe);
212 		sc->sc_intr_pipe = NULL;
213 	}
214 
215 	if (sc->sc_subdev != NULL)
216 		rv = config_detach(sc->sc_subdev, flags);
217 
218 	return rv;
219 }
220 
221 int
222 uxrcom_open(void *vsc, int portno)
223 {
224 	struct uxrcom_softc *sc = vsc;
225 	int err;
226 
227 	if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
228 		err = usbd_open_pipe_intr(sc->sc_intr_iface,
229 		    sc->sc_intr_number, USBD_SHORT_XFER_OK, &sc->sc_intr_pipe,
230 		    sc, &sc->sc_intr_buf, sizeof(sc->sc_intr_buf),
231 		    uxrcom_intr, USBD_DEFAULT_INTERVAL);
232 		if (err)
233 			return EIO;
234 	}
235 
236 	return 0;
237 }
238 
239 void
240 uxrcom_close(void *vsc, int portno)
241 {
242 	struct uxrcom_softc *sc = vsc;
243 	int err;
244 
245 	if (usbd_is_dying(sc->sc_udev))
246 		return;
247 
248 	if (sc->sc_intr_pipe != NULL) {
249 		err = usbd_close_pipe(sc->sc_intr_pipe);
250 		if (err)
251 			printf("%s: close intr pipe failed: %s\n",
252 			    sc->sc_dev.dv_xname, usbd_errstr(err));
253 		sc->sc_intr_pipe = NULL;
254 	}
255 }
256 
257 void
258 uxrcom_set(void *vsc, int portno, int reg, int onoff)
259 {
260 	struct uxrcom_softc *sc = vsc;
261 	usb_device_request_t req;
262 	uint16_t index;
263 	uint8_t value;
264 
265 	index = onoff ? XR_GPIO_SET : XR_GPIO_CLEAR;
266 
267 	switch (reg) {
268 	case UCOM_SET_DTR:
269 		value = XR_GPIO3;
270 		break;
271 	case UCOM_SET_RTS:
272 		value = XR_GPIO5;
273 		break;
274 	case UCOM_SET_BREAK:
275 		uxrcom_break(sc, portno, onoff);
276 		return;
277 	default:
278 		return;
279 	}
280 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
281 	req.bRequest = XR_SET_REG;
282 	USETW(req.wValue, value);
283 	USETW(req.wIndex, index);
284 	USETW(req.wLength, 0);
285 	usbd_do_request(sc->sc_udev, &req, NULL);
286 }
287 
288 usbd_status
289 uxrcom_set_line_coding(struct uxrcom_softc *sc,
290     struct usb_cdc_line_state *state)
291 {
292 	usb_device_request_t req;
293 	usbd_status err;
294 
295 	if (memcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0)
296 		return USBD_NORMAL_COMPLETION;
297 
298 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
299 	req.bRequest = UCDC_SET_LINE_CODING;
300 	USETW(req.wValue, 0);
301 	USETW(req.wIndex, UXRCOM_INTR_IFACE_NO);
302 	USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
303 
304 	err = usbd_do_request(sc->sc_udev, &req, state);
305 	if (err)
306 		return err;
307 
308 	sc->sc_line_state = *state;
309 
310 	return USBD_NORMAL_COMPLETION;
311 }
312 
313 int
314 uxrcom_param(void *vsc, int portno, struct termios *t)
315 {
316 	struct uxrcom_softc *sc = (struct uxrcom_softc *)vsc;
317 	usb_device_request_t req;
318 	usbd_status err;
319 	struct usb_cdc_line_state ls;
320 	uint8_t flowctrl;
321 
322 	if (t->c_ospeed <= 0 || t->c_ospeed > 48000000)
323 		return (EINVAL);
324 
325 	USETDW(ls.dwDTERate, t->c_ospeed);
326 	if (ISSET(t->c_cflag, CSTOPB))
327 		ls.bCharFormat = UCDC_STOP_BIT_2;
328 	else
329 		ls.bCharFormat = UCDC_STOP_BIT_1;
330 	if (ISSET(t->c_cflag, PARENB)) {
331 		if (ISSET(t->c_cflag, PARODD))
332 			ls.bParityType = UCDC_PARITY_ODD;
333 		else
334 			ls.bParityType = UCDC_PARITY_EVEN;
335 	} else
336 		ls.bParityType = UCDC_PARITY_NONE;
337 	switch (ISSET(t->c_cflag, CSIZE)) {
338 	case CS5:
339 		ls.bDataBits = 5;
340 		break;
341 	case CS6:
342 		ls.bDataBits = 6;
343 		break;
344 	case CS7:
345 		ls.bDataBits = 7;
346 		break;
347 	case CS8:
348 		ls.bDataBits = 8;
349 		break;
350 	}
351 
352 	err = uxrcom_set_line_coding(sc, &ls);
353 	if (err)
354 		return (EIO);
355 
356 	if (ISSET(t->c_cflag, CRTSCTS)) {
357 		/*  rts/cts flow ctl */
358 		flowctrl = XR_FLOW_CONTROL_ON;
359 	} else {
360 		/* disable flow ctl */
361 		flowctrl = XR_FLOW_CONTROL_OFF;
362 	}
363 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
364 	req.bRequest = XR_SET_REG;
365 	USETW(req.wValue, flowctrl);
366 	USETW(req.wIndex, XR_FLOW_CONTROL);
367 	USETW(req.wLength, 0);
368 	usbd_do_request(sc->sc_udev, &req, NULL);
369 
370 	return (0);
371 }
372 
373 void
374 uxrcom_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
375 {
376 	struct uxrcom_softc *sc = priv;
377 	uint8_t mstatus;
378 
379 	if (usbd_is_dying(sc->sc_udev))
380 		return;
381 
382 	if (status != USBD_NORMAL_COMPLETION) {
383 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
384 			return;
385 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
386 		return;
387 	}
388 
389 	if (sc->sc_intr_buf.bmRequestType != UCDC_NOTIFICATION)
390 		return;
391 
392 	switch (sc->sc_intr_buf.bNotification) {
393 	case UCDC_N_SERIAL_STATE:
394 		sc->sc_lsr = sc->sc_msr = 0;
395 		mstatus = sc->sc_intr_buf.data[0];
396 
397 		if (ISSET(mstatus, UCDC_N_SERIAL_RI))
398 			sc->sc_msr |= UMSR_RI;
399 		if (ISSET(mstatus, UCDC_N_SERIAL_DSR))
400 			sc->sc_msr |= UMSR_DSR;
401 		if (ISSET(mstatus, UCDC_N_SERIAL_DCD))
402 			sc->sc_msr |= UMSR_DCD;
403 		ucom_status_change((struct ucom_softc *)sc->sc_subdev);
404 		break;
405 	default:
406 		break;
407 	}
408 }
409 
410 void
411 uxrcom_get_status(void *vsc, int portno, u_char *lsr, u_char *msr)
412 {
413 	struct uxrcom_softc *sc = vsc;
414 
415 	if (msr != NULL)
416 		*msr = sc->sc_msr;
417 	if (lsr != NULL)
418 		*lsr = sc->sc_lsr;
419 }
420 
421 void
422 uxrcom_break(void *vsc, int portno, int onoff)
423 {
424 	struct uxrcom_softc *sc = vsc;
425 	usb_device_request_t req;
426 	uint8_t brk = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
427 
428 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
429 	req.bRequest = XR_SET_REG;
430 	USETW(req.wValue, brk);
431 	USETW(req.wIndex, XR_TX_BREAK);
432 	USETW(req.wLength, 0);
433 	usbd_do_request(sc->sc_udev, &req, NULL);
434 }
435