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