1 | /* $NetBSD: ubsa_common.c,v 1.10 2016/04/23 10:15:32 skrll Exp $ */ |
2 | /*- |
3 | * Copyright (c) 2002, Alexander Kabaev <kan.FreeBSD.org>. |
4 | * All rights reserved. |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * |
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 | * SUCH DAMAGE. |
26 | */ |
27 | /* |
28 | * Copyright (c) 2001 The NetBSD Foundation, Inc. |
29 | * All rights reserved. |
30 | * |
31 | * This code is derived from software contributed to The NetBSD Foundation |
32 | * by Ichiro FUKUHARA (ichiro@ichiro.org). |
33 | * |
34 | * Redistribution and use in source and binary forms, with or without |
35 | * modification, are permitted provided that the following conditions |
36 | * are met: |
37 | * 1. Redistributions of source code must retain the above copyright |
38 | * notice, this list of conditions and the following disclaimer. |
39 | * 2. Redistributions in binary form must reproduce the above copyright |
40 | * notice, this list of conditions and the following disclaimer in the |
41 | * documentation and/or other materials provided with the distribution. |
42 | * |
43 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
44 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
45 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
46 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
47 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
48 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
49 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
50 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
51 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
52 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
53 | * POSSIBILITY OF SUCH DAMAGE. |
54 | */ |
55 | |
56 | #include <sys/cdefs.h> |
57 | __KERNEL_RCSID(0, "$NetBSD: ubsa_common.c,v 1.10 2016/04/23 10:15:32 skrll Exp $" ); |
58 | |
59 | #include <sys/param.h> |
60 | #include <sys/systm.h> |
61 | #include <sys/kernel.h> |
62 | #include <sys/kmem.h> |
63 | #include <sys/ioccom.h> |
64 | #include <sys/fcntl.h> |
65 | #include <sys/conf.h> |
66 | #include <sys/tty.h> |
67 | #include <sys/file.h> |
68 | #include <sys/select.h> |
69 | #include <sys/proc.h> |
70 | #include <sys/device.h> |
71 | #include <sys/poll.h> |
72 | #include <sys/sysctl.h> |
73 | #include <sys/bus.h> |
74 | |
75 | #include <dev/usb/usb.h> |
76 | #include <dev/usb/usbdi.h> |
77 | #include <dev/usb/usbdi_util.h> |
78 | #include <dev/usb/usbdivar.h> |
79 | |
80 | #include <dev/usb/usbcdc.h> |
81 | #include <dev/usb/usbdevs.h> |
82 | #include <dev/usb/usb_quirks.h> |
83 | #include <dev/usb/ucomvar.h> |
84 | #include <dev/usb/ubsavar.h> |
85 | |
86 | #ifdef UBSA_DEBUG |
87 | extern int ubsadebug; |
88 | #define DPRINTFN(n, x) do { \ |
89 | if (ubsadebug > (n)) \ |
90 | printf x; \ |
91 | } while (0) |
92 | #else |
93 | #define DPRINTFN(n, x) |
94 | #endif |
95 | #define DPRINTF(x) DPRINTFN(0, x) |
96 | |
97 | int |
98 | ubsa_request(struct ubsa_softc *sc, int portno, uint8_t request, uint16_t value) |
99 | { |
100 | usb_device_request_t req; |
101 | usbd_status err; |
102 | |
103 | if (sc->sc_quadumts) |
104 | req.bmRequestType = UT_WRITE_CLASS_INTERFACE; |
105 | else |
106 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE; |
107 | |
108 | if (portno >= UBSA_MAXCONN) { |
109 | printf("%s: ubsa_request: invalid port(%d)#\n" , |
110 | device_xname(sc->sc_dev), portno); |
111 | return USBD_INVAL; |
112 | } |
113 | |
114 | req.bRequest = request; |
115 | USETW(req.wValue, value); |
116 | USETW(req.wIndex, sc->sc_iface_number[portno]); |
117 | USETW(req.wLength, 0); |
118 | |
119 | err = usbd_do_request(sc->sc_udev, &req, 0); |
120 | if (err) |
121 | printf("%s: ubsa_request: %s\n" , |
122 | device_xname(sc->sc_dev), usbd_errstr(err)); |
123 | return err; |
124 | } |
125 | |
126 | void |
127 | ubsa_dtr(struct ubsa_softc *sc, int portno, int onoff) |
128 | { |
129 | |
130 | DPRINTF(("ubsa_dtr: onoff = %d\n" , onoff)); |
131 | |
132 | if (sc->sc_dtr == onoff) |
133 | return; |
134 | sc->sc_dtr = onoff; |
135 | |
136 | ubsa_request(sc, portno, UBSA_SET_DTR, onoff ? 1 : 0); |
137 | } |
138 | |
139 | void |
140 | ubsa_rts(struct ubsa_softc *sc, int portno, int onoff) |
141 | { |
142 | |
143 | DPRINTF(("ubsa_rts: onoff = %d\n" , onoff)); |
144 | |
145 | if (sc->sc_rts == onoff) |
146 | return; |
147 | sc->sc_rts = onoff; |
148 | |
149 | ubsa_request(sc, portno, UBSA_SET_RTS, onoff ? 1 : 0); |
150 | } |
151 | |
152 | void |
153 | ubsa_quadumts_dtr(struct ubsa_softc *sc, int portno, int onoff) |
154 | { |
155 | |
156 | DPRINTF(("ubsa_dtr: onoff = %d\n" , onoff)); |
157 | |
158 | if (sc->sc_dtr == onoff) |
159 | return; |
160 | sc->sc_dtr = onoff; |
161 | |
162 | ubsa_request(sc, portno, UBSA_QUADUMTS_SET_PIN, |
163 | (sc->sc_rts ? 2 : 0)+(sc->sc_dtr ? 1 : 0)); |
164 | } |
165 | |
166 | void |
167 | ubsa_quadumts_rts(struct ubsa_softc *sc, int portno, int onoff) |
168 | { |
169 | |
170 | DPRINTF(("ubsa_rts: onoff = %d\n" , onoff)); |
171 | |
172 | if (sc->sc_rts == onoff) |
173 | return; |
174 | sc->sc_rts = onoff; |
175 | |
176 | ubsa_request(sc, portno, UBSA_QUADUMTS_SET_PIN, |
177 | (sc->sc_rts ? 2 : 0)+(sc->sc_dtr ? 1 : 0)); |
178 | } |
179 | |
180 | void |
181 | ubsa_break(struct ubsa_softc *sc, int portno, int onoff) |
182 | { |
183 | DPRINTF(("ubsa_rts: onoff = %d\n" , onoff)); |
184 | |
185 | ubsa_request(sc, portno, UBSA_SET_BREAK, onoff ? 1 : 0); |
186 | } |
187 | |
188 | void |
189 | ubsa_set(void *addr, int portno, int reg, int onoff) |
190 | { |
191 | struct ubsa_softc *sc; |
192 | |
193 | sc = addr; |
194 | switch (reg) { |
195 | case UCOM_SET_DTR: |
196 | if (sc->sc_quadumts) |
197 | ubsa_quadumts_dtr(sc, portno, onoff); |
198 | else |
199 | ubsa_dtr(sc, portno, onoff); |
200 | break; |
201 | case UCOM_SET_RTS: |
202 | if (sc->sc_quadumts) |
203 | ubsa_quadumts_rts(sc, portno, onoff); |
204 | else |
205 | ubsa_rts(sc, portno, onoff); |
206 | break; |
207 | case UCOM_SET_BREAK: |
208 | if (!sc->sc_quadumts) |
209 | ubsa_break(sc, portno, onoff); |
210 | break; |
211 | default: |
212 | break; |
213 | } |
214 | } |
215 | |
216 | void |
217 | ubsa_baudrate(struct ubsa_softc *sc, int portno, speed_t speed) |
218 | { |
219 | uint16_t value = 0; |
220 | |
221 | DPRINTF(("ubsa_baudrate: speed = %d\n" , speed)); |
222 | |
223 | switch(speed) { |
224 | case B0: |
225 | break; |
226 | case B300: |
227 | case B600: |
228 | case B1200: |
229 | case B2400: |
230 | case B4800: |
231 | case B9600: |
232 | case B19200: |
233 | case B38400: |
234 | case B57600: |
235 | case B115200: |
236 | case B230400: |
237 | value = B230400 / speed; |
238 | break; |
239 | default: |
240 | printf("%s: ubsa_param: unsupported baudrate, " |
241 | "forcing default of 9600\n" , |
242 | device_xname(sc->sc_dev)); |
243 | value = B230400 / B9600; |
244 | break; |
245 | }; |
246 | |
247 | if (speed == B0) { |
248 | ubsa_flow(sc, portno, 0, 0); |
249 | ubsa_dtr(sc, portno, 0); |
250 | ubsa_rts(sc, portno, 0); |
251 | } else |
252 | ubsa_request(sc, portno, UBSA_SET_BAUDRATE, value); |
253 | } |
254 | |
255 | void |
256 | ubsa_parity(struct ubsa_softc *sc, int portno, tcflag_t cflag) |
257 | { |
258 | int value; |
259 | |
260 | DPRINTF(("ubsa_parity: cflag = 0x%x\n" , cflag)); |
261 | |
262 | if (cflag & PARENB) |
263 | value = (cflag & PARODD) ? UBSA_PARITY_ODD : UBSA_PARITY_EVEN; |
264 | else |
265 | value = UBSA_PARITY_NONE; |
266 | |
267 | ubsa_request(sc, portno, UBSA_SET_PARITY, value); |
268 | } |
269 | |
270 | void |
271 | ubsa_databits(struct ubsa_softc *sc, int portno, tcflag_t cflag) |
272 | { |
273 | int value; |
274 | |
275 | DPRINTF(("ubsa_databits: cflag = 0x%x\n" , cflag)); |
276 | |
277 | switch (cflag & CSIZE) { |
278 | case CS5: value = 0; break; |
279 | case CS6: value = 1; break; |
280 | case CS7: value = 2; break; |
281 | case CS8: value = 3; break; |
282 | default: |
283 | printf("%s: ubsa_param: unsupported databits requested, " |
284 | "forcing default of 8\n" , |
285 | device_xname(sc->sc_dev)); |
286 | value = 3; |
287 | } |
288 | |
289 | ubsa_request(sc, portno, UBSA_SET_DATA_BITS, value); |
290 | } |
291 | |
292 | void |
293 | ubsa_stopbits(struct ubsa_softc *sc, int portno, tcflag_t cflag) |
294 | { |
295 | int value; |
296 | |
297 | DPRINTF(("ubsa_stopbits: cflag = 0x%x\n" , cflag)); |
298 | |
299 | value = (cflag & CSTOPB) ? 1 : 0; |
300 | |
301 | ubsa_request(sc, portno, UBSA_SET_STOP_BITS, value); |
302 | } |
303 | |
304 | void |
305 | ubsa_flow(struct ubsa_softc *sc, int portno, tcflag_t cflag, tcflag_t iflag) |
306 | { |
307 | int value; |
308 | |
309 | DPRINTF(("ubsa_flow: cflag = 0x%x, iflag = 0x%x\n" , cflag, iflag)); |
310 | |
311 | value = 0; |
312 | if (cflag & CRTSCTS) |
313 | value |= UBSA_FLOW_OCTS | UBSA_FLOW_IRTS; |
314 | if (iflag & (IXON|IXOFF)) |
315 | value |= UBSA_FLOW_OXON | UBSA_FLOW_IXON; |
316 | |
317 | ubsa_request(sc, portno, UBSA_SET_FLOW_CTRL, value); |
318 | } |
319 | |
320 | int |
321 | ubsa_param(void *addr, int portno, struct termios *ti) |
322 | { |
323 | struct ubsa_softc *sc = addr; |
324 | |
325 | DPRINTF(("ubsa_param: sc = %p\n" , sc)); |
326 | |
327 | if (!sc->sc_quadumts) { |
328 | ubsa_baudrate(sc, portno, ti->c_ospeed); |
329 | ubsa_parity(sc, portno, ti->c_cflag); |
330 | ubsa_databits(sc, portno, ti->c_cflag); |
331 | ubsa_stopbits(sc, portno, ti->c_cflag); |
332 | ubsa_flow(sc, portno, ti->c_cflag, ti->c_iflag); |
333 | } |
334 | |
335 | return 0; |
336 | } |
337 | |
338 | int |
339 | ubsa_open(void *addr, int portno) |
340 | { |
341 | struct ubsa_softc *sc = addr; |
342 | int err; |
343 | |
344 | if (sc->sc_dying) |
345 | return ENXIO; |
346 | |
347 | if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) { |
348 | sc->sc_intr_buf = kmem_alloc(sc->sc_isize, KM_SLEEP); |
349 | /* XXX only iface# = 0 has intr line */ |
350 | /* XXX E220 specific? need to check */ |
351 | err = usbd_open_pipe_intr(sc->sc_iface[0], |
352 | sc->sc_intr_number, |
353 | USBD_SHORT_XFER_OK, |
354 | &sc->sc_intr_pipe, |
355 | sc, |
356 | sc->sc_intr_buf, |
357 | sc->sc_isize, |
358 | ubsa_intr, |
359 | UBSA_INTR_INTERVAL); |
360 | if (err) { |
361 | printf("%s: cannot open interrupt pipe (addr %d)\n" , |
362 | device_xname(sc->sc_dev), |
363 | sc->sc_intr_number); |
364 | return EIO; |
365 | } |
366 | } |
367 | |
368 | return 0; |
369 | } |
370 | |
371 | void |
372 | ubsa_close(void *addr, int portno) |
373 | { |
374 | struct ubsa_softc *sc = addr; |
375 | int err; |
376 | |
377 | if (sc->sc_dying) |
378 | return; |
379 | |
380 | DPRINTF(("ubsa_close: close\n" )); |
381 | |
382 | if (sc->sc_intr_pipe != NULL) { |
383 | err = usbd_abort_pipe(sc->sc_intr_pipe); |
384 | if (err) |
385 | printf("%s: abort interrupt pipe failed: %s\n" , |
386 | device_xname(sc->sc_dev), |
387 | usbd_errstr(err)); |
388 | err = usbd_close_pipe(sc->sc_intr_pipe); |
389 | if (err) |
390 | printf("%s: close interrupt pipe failed: %s\n" , |
391 | device_xname(sc->sc_dev), |
392 | usbd_errstr(err)); |
393 | kmem_free(sc->sc_intr_buf, sc->sc_isize); |
394 | sc->sc_intr_pipe = NULL; |
395 | } |
396 | } |
397 | |
398 | void |
399 | ubsa_intr(struct usbd_xfer *xfer, void *priv, |
400 | usbd_status status) |
401 | { |
402 | struct ubsa_softc *sc = priv; |
403 | u_char *buf; |
404 | int i; |
405 | |
406 | buf = sc->sc_intr_buf; |
407 | if (sc->sc_dying) |
408 | return; |
409 | |
410 | if (status != USBD_NORMAL_COMPLETION) { |
411 | if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) |
412 | return; |
413 | |
414 | DPRINTF(("%s: ubsa_intr: abnormal status: %s\n" , |
415 | device_xname(sc->sc_dev), usbd_errstr(status))); |
416 | usbd_clear_endpoint_stall_async(sc->sc_intr_pipe); |
417 | return; |
418 | } |
419 | |
420 | /* incidentally, Belkin adapter status bits match UART 16550 bits */ |
421 | sc->sc_lsr = buf[2]; |
422 | sc->sc_msr = buf[3]; |
423 | |
424 | DPRINTF(("%s: ubsa lsr = 0x%02x, msr = 0x%02x\n" , |
425 | device_xname(sc->sc_dev), sc->sc_lsr, sc->sc_msr)); |
426 | |
427 | for (i = 0; i < sc->sc_numif; i++) { |
428 | ucom_status_change(device_private(sc->sc_subdevs[i])); |
429 | } |
430 | } |
431 | |
432 | void |
433 | ubsa_get_status(void *addr, int portno, u_char *lsr, u_char *msr) |
434 | { |
435 | struct ubsa_softc *sc = addr; |
436 | |
437 | DPRINTF(("ubsa_get_status\n" )); |
438 | |
439 | if (lsr != NULL) |
440 | *lsr = sc->sc_lsr; |
441 | if (msr != NULL) |
442 | *msr = sc->sc_msr; |
443 | } |
444 | |
445 | |