1 /* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */
2
3 /*-
4 * Copyright (c) 2001-2003, 2005, 2008
5 * Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD: stable/9/sys/dev/usb/serial/usb_serial.c 268208 2014-07-03 08:07:37Z hselasky $");
32
33 /*-
34 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
35 * All rights reserved.
36 *
37 * This code is derived from software contributed to The NetBSD Foundation
38 * by Lennart Augustsson (lennart@augustsson.net) at
39 * Carlstedt Research & Technology.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 * notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 * notice, this list of conditions and the following disclaimer in the
48 * documentation and/or other materials provided with the distribution.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
51 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
52 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
53 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
54 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
57 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
58 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
59 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
60 * POSSIBILITY OF SUCH DAMAGE.
61 */
62
63 #include <sys/stdint.h>
64 #include <sys/stddef.h>
65 #include <sys/param.h>
66 #include <sys/queue.h>
67 #include <sys/types.h>
68 #include <sys/systm.h>
69 #include <sys/kernel.h>
70 #include <sys/bus.h>
71 #include <sys/module.h>
72 #include <sys/lock.h>
73 #include <sys/mutex.h>
74 #include <sys/condvar.h>
75 #include <sys/sysctl.h>
76 #include <sys/sx.h>
77 #include <sys/unistd.h>
78 #include <sys/callout.h>
79 #include <sys/malloc.h>
80 #include <sys/priv.h>
81 #include <sys/cons.h>
82 #include <sys/kdb.h>
83
84 #include <dev/usb/usb.h>
85 #include <dev/usb/usbdi.h>
86 #include <dev/usb/usbdi_util.h>
87
88 #define USB_DEBUG_VAR ucom_debug
89 #include <dev/usb/usb_debug.h>
90 #include <dev/usb/usb_busdma.h>
91 #include <dev/usb/usb_process.h>
92
93 #include <dev/usb/serial/usb_serial.h>
94
95 #include "opt_gdb.h"
96
97 static SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
98
99 #ifdef USB_DEBUG
100 static int ucom_debug = 0;
101
102 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
103 &ucom_debug, 0, "ucom debug level");
104 #endif
105
106 #define UCOM_CONS_BUFSIZE 1024
107
108 static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
109 static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
110
111 static unsigned int ucom_cons_rx_low = 0;
112 static unsigned int ucom_cons_rx_high = 0;
113
114 static unsigned int ucom_cons_tx_low = 0;
115 static unsigned int ucom_cons_tx_high = 0;
116
117 static int ucom_cons_unit = -1;
118 static int ucom_cons_subunit = 0;
119 static int ucom_cons_baud = 9600;
120 static struct ucom_softc *ucom_cons_softc = NULL;
121
122 TUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit);
123 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW | CTLFLAG_TUN,
124 &ucom_cons_unit, 0, "console unit number");
125 TUNABLE_INT("hw.usb.ucom.cons_subunit", &ucom_cons_subunit);
126 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RW | CTLFLAG_TUN,
127 &ucom_cons_subunit, 0, "console subunit number");
128 TUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud);
129 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW | CTLFLAG_TUN,
130 &ucom_cons_baud, 0, "console baud rate");
131
132 static usb_proc_callback_t ucom_cfg_start_transfers;
133 static usb_proc_callback_t ucom_cfg_open;
134 static usb_proc_callback_t ucom_cfg_close;
135 static usb_proc_callback_t ucom_cfg_line_state;
136 static usb_proc_callback_t ucom_cfg_status_change;
137 static usb_proc_callback_t ucom_cfg_param;
138
139 static int ucom_unit_alloc(void);
140 static void ucom_unit_free(int);
141 static int ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *);
142 static void ucom_detach_tty(struct ucom_super_softc *, struct ucom_softc *);
143 static void ucom_queue_command(struct ucom_softc *,
144 usb_proc_callback_t *, struct termios *pt,
145 struct usb_proc_msg *t0, struct usb_proc_msg *t1);
146 static void ucom_shutdown(struct ucom_softc *);
147 static void ucom_ring(struct ucom_softc *, uint8_t);
148 static void ucom_break(struct ucom_softc *, uint8_t);
149 static void ucom_dtr(struct ucom_softc *, uint8_t);
150 static void ucom_rts(struct ucom_softc *, uint8_t);
151
152 static tsw_open_t ucom_open;
153 static tsw_close_t ucom_close;
154 static tsw_ioctl_t ucom_ioctl;
155 static tsw_modem_t ucom_modem;
156 static tsw_param_t ucom_param;
157 static tsw_outwakeup_t ucom_outwakeup;
158 static tsw_inwakeup_t ucom_inwakeup;
159 static tsw_free_t ucom_free;
160
161 static struct ttydevsw ucom_class = {
162 .tsw_flags = TF_INITLOCK | TF_CALLOUT,
163 .tsw_open = ucom_open,
164 .tsw_close = ucom_close,
165 .tsw_outwakeup = ucom_outwakeup,
166 .tsw_inwakeup = ucom_inwakeup,
167 .tsw_ioctl = ucom_ioctl,
168 .tsw_param = ucom_param,
169 .tsw_modem = ucom_modem,
170 .tsw_free = ucom_free,
171 };
172
173 MODULE_DEPEND(ucom, usb, 1, 1, 1);
174 MODULE_VERSION(ucom, 1);
175
176 #define UCOM_UNIT_MAX 128 /* maximum number of units */
177 #define UCOM_TTY_PREFIX "U"
178
179 static struct unrhdr *ucom_unrhdr;
180 static struct mtx ucom_mtx;
181 static int ucom_close_refs;
182
183 static void
ucom_init(void * arg)184 ucom_init(void *arg)
185 {
186 DPRINTF("\n");
187 ucom_unrhdr = new_unrhdr(0, UCOM_UNIT_MAX - 1, NULL);
188 mtx_init(&ucom_mtx, "UCOM MTX", NULL, MTX_DEF);
189 }
190 SYSINIT(ucom_init, SI_SUB_KLD - 1, SI_ORDER_ANY, ucom_init, NULL);
191
192 static void
ucom_uninit(void * arg)193 ucom_uninit(void *arg)
194 {
195 struct unrhdr *hdr;
196 hdr = ucom_unrhdr;
197 ucom_unrhdr = NULL;
198
199 DPRINTF("\n");
200
201 if (hdr != NULL)
202 delete_unrhdr(hdr);
203
204 mtx_destroy(&ucom_mtx);
205 }
206 SYSUNINIT(ucom_uninit, SI_SUB_KLD - 3, SI_ORDER_ANY, ucom_uninit, NULL);
207
208 /*
209 * Mark a unit number (the X in cuaUX) as in use.
210 *
211 * Note that devices using a different naming scheme (see ucom_tty_name()
212 * callback) still use this unit allocation.
213 */
214 static int
ucom_unit_alloc(void)215 ucom_unit_alloc(void)
216 {
217 int unit;
218
219 /* sanity checks */
220 if (ucom_unrhdr == NULL) {
221 DPRINTF("ucom_unrhdr is NULL\n");
222 return (-1);
223 }
224 unit = alloc_unr(ucom_unrhdr);
225 DPRINTF("unit %d is allocated\n", unit);
226 return (unit);
227 }
228
229 /*
230 * Mark the unit number as not in use.
231 */
232 static void
ucom_unit_free(int unit)233 ucom_unit_free(int unit)
234 {
235 /* sanity checks */
236 if (unit < 0 || unit >= UCOM_UNIT_MAX || ucom_unrhdr == NULL) {
237 DPRINTF("cannot free unit number\n");
238 return;
239 }
240 DPRINTF("unit %d is freed\n", unit);
241 free_unr(ucom_unrhdr, unit);
242 }
243
244 /*
245 * Setup a group of one or more serial ports.
246 *
247 * The mutex pointed to by "mtx" is applied before all
248 * callbacks are called back. Also "mtx" must be applied
249 * before calling into the ucom-layer!
250 */
251 int
ucom_attach(struct ucom_super_softc * ssc,struct ucom_softc * sc,int subunits,void * parent,const struct ucom_callback * callback,struct mtx * mtx)252 ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
253 int subunits, void *parent,
254 const struct ucom_callback *callback, struct mtx *mtx)
255 {
256 int subunit;
257 int error = 0;
258
259 if ((sc == NULL) ||
260 (subunits <= 0) ||
261 (callback == NULL) ||
262 (mtx == NULL)) {
263 return (EINVAL);
264 }
265
266 /* allocate a uniq unit number */
267 ssc->sc_unit = ucom_unit_alloc();
268 if (ssc->sc_unit == -1)
269 return (ENOMEM);
270
271 /* generate TTY name string */
272 snprintf(ssc->sc_ttyname, sizeof(ssc->sc_ttyname),
273 UCOM_TTY_PREFIX "%d", ssc->sc_unit);
274
275 /* create USB request handling process */
276 error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
277 if (error) {
278 ucom_unit_free(ssc->sc_unit);
279 return (error);
280 }
281 ssc->sc_subunits = subunits;
282 ssc->sc_flag = UCOM_FLAG_ATTACHED |
283 UCOM_FLAG_FREE_UNIT;
284
285 if (callback->ucom_free == NULL)
286 ssc->sc_flag |= UCOM_FLAG_WAIT_REFS;
287
288 /* increment reference count */
289 ucom_ref(ssc);
290
291 for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
292 sc[subunit].sc_subunit = subunit;
293 sc[subunit].sc_super = ssc;
294 sc[subunit].sc_mtx = mtx;
295 sc[subunit].sc_parent = parent;
296 sc[subunit].sc_callback = callback;
297
298 error = ucom_attach_tty(ssc, &sc[subunit]);
299 if (error) {
300 ucom_detach(ssc, &sc[0]);
301 return (error);
302 }
303 /* increment reference count */
304 ucom_ref(ssc);
305
306 /* set subunit attached */
307 sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED;
308 }
309
310 DPRINTF("tp = %p, unit = %d, subunits = %d\n",
311 sc->sc_tty, ssc->sc_unit, ssc->sc_subunits);
312
313 return (0);
314 }
315
316 /*
317 * The following function will do nothing if the structure pointed to
318 * by "ssc" and "sc" is zero or has already been detached.
319 */
320 void
ucom_detach(struct ucom_super_softc * ssc,struct ucom_softc * sc)321 ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc)
322 {
323 int subunit;
324
325 if (!(ssc->sc_flag & UCOM_FLAG_ATTACHED))
326 return; /* not initialized */
327
328 if (ssc->sc_sysctl_ttyname != NULL) {
329 sysctl_remove_oid(ssc->sc_sysctl_ttyname, 1, 0);
330 ssc->sc_sysctl_ttyname = NULL;
331 }
332
333 if (ssc->sc_sysctl_ttyports != NULL) {
334 sysctl_remove_oid(ssc->sc_sysctl_ttyports, 1, 0);
335 ssc->sc_sysctl_ttyports = NULL;
336 }
337
338 usb_proc_drain(&ssc->sc_tq);
339
340 for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
341 if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) {
342
343 ucom_detach_tty(ssc, &sc[subunit]);
344
345 /* avoid duplicate detach */
346 sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED;
347 }
348 }
349 usb_proc_free(&ssc->sc_tq);
350
351 ucom_unref(ssc);
352
353 if (ssc->sc_flag & UCOM_FLAG_WAIT_REFS)
354 ucom_drain(ssc);
355
356 /* make sure we don't detach twice */
357 ssc->sc_flag &= ~UCOM_FLAG_ATTACHED;
358 }
359
360 void
ucom_drain(struct ucom_super_softc * ssc)361 ucom_drain(struct ucom_super_softc *ssc)
362 {
363 mtx_lock(&ucom_mtx);
364 while (ssc->sc_refs > 0) {
365 printf("ucom: Waiting for a TTY device to close.\n");
366 usb_pause_mtx(&ucom_mtx, hz);
367 }
368 mtx_unlock(&ucom_mtx);
369 }
370
371 void
ucom_drain_all(void * arg)372 ucom_drain_all(void *arg)
373 {
374 mtx_lock(&ucom_mtx);
375 while (ucom_close_refs > 0) {
376 printf("ucom: Waiting for all detached TTY "
377 "devices to have open fds closed.\n");
378 usb_pause_mtx(&ucom_mtx, hz);
379 }
380 mtx_unlock(&ucom_mtx);
381 }
382
383 static int
ucom_attach_tty(struct ucom_super_softc * ssc,struct ucom_softc * sc)384 ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
385 {
386 struct tty *tp;
387 char buf[32]; /* temporary TTY device name buffer */
388
389 tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx);
390 if (tp == NULL)
391 return (ENOMEM);
392
393 /* Check if the client has a custom TTY name */
394 buf[0] = '\0';
395 if (sc->sc_callback->ucom_tty_name) {
396 sc->sc_callback->ucom_tty_name(sc, buf,
397 sizeof(buf), ssc->sc_unit, sc->sc_subunit);
398 }
399 if (buf[0] == 0) {
400 /* Use default TTY name */
401 if (ssc->sc_subunits > 1) {
402 /* multiple modems in one */
403 snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u.%u",
404 ssc->sc_unit, sc->sc_subunit);
405 } else {
406 /* single modem */
407 snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u",
408 ssc->sc_unit);
409 }
410 }
411 tty_makedev(tp, NULL, "%s", buf);
412
413 sc->sc_tty = tp;
414
415 DPRINTF("ttycreate: %s\n", buf);
416
417 /* Check if this device should be a console */
418 if ((ucom_cons_softc == NULL) &&
419 (ssc->sc_unit == ucom_cons_unit) &&
420 (sc->sc_subunit == ucom_cons_subunit)) {
421
422 DPRINTF("unit %d subunit %d is console",
423 ssc->sc_unit, sc->sc_subunit);
424
425 ucom_cons_softc = sc;
426
427 tty_init_console(tp, ucom_cons_baud);
428
429 UCOM_MTX_LOCK(ucom_cons_softc);
430 ucom_cons_rx_low = 0;
431 ucom_cons_rx_high = 0;
432 ucom_cons_tx_low = 0;
433 ucom_cons_tx_high = 0;
434 sc->sc_flag |= UCOM_FLAG_CONSOLE;
435 ucom_open(ucom_cons_softc->sc_tty);
436 ucom_param(ucom_cons_softc->sc_tty, &tp->t_termios_init_in);
437 UCOM_MTX_UNLOCK(ucom_cons_softc);
438 }
439
440 return (0);
441 }
442
443 static void
ucom_detach_tty(struct ucom_super_softc * ssc,struct ucom_softc * sc)444 ucom_detach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
445 {
446 struct tty *tp = sc->sc_tty;
447
448 DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
449
450 if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
451 UCOM_MTX_LOCK(ucom_cons_softc);
452 ucom_close(ucom_cons_softc->sc_tty);
453 sc->sc_flag &= ~UCOM_FLAG_CONSOLE;
454 UCOM_MTX_UNLOCK(ucom_cons_softc);
455 ucom_cons_softc = NULL;
456 }
457
458 /* the config thread has been stopped when we get here */
459
460 UCOM_MTX_LOCK(sc);
461 sc->sc_flag |= UCOM_FLAG_GONE;
462 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
463 UCOM_MTX_UNLOCK(sc);
464
465 if (tp) {
466 mtx_lock(&ucom_mtx);
467 ucom_close_refs++;
468 mtx_unlock(&ucom_mtx);
469
470 tty_lock(tp);
471
472 ucom_close(tp); /* close, if any */
473
474 tty_rel_gone(tp);
475
476 UCOM_MTX_LOCK(sc);
477 /*
478 * make sure that read and write transfers are stopped
479 */
480 if (sc->sc_callback->ucom_stop_read)
481 (sc->sc_callback->ucom_stop_read) (sc);
482 if (sc->sc_callback->ucom_stop_write)
483 (sc->sc_callback->ucom_stop_write) (sc);
484 UCOM_MTX_UNLOCK(sc);
485 }
486 }
487
488 void
ucom_set_pnpinfo_usb(struct ucom_super_softc * ssc,device_t dev)489 ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev)
490 {
491 char buf[64];
492 uint8_t iface_index;
493 struct usb_attach_arg *uaa;
494
495 snprintf(buf, sizeof(buf), "ttyname=" UCOM_TTY_PREFIX
496 "%d ttyports=%d", ssc->sc_unit, ssc->sc_subunits);
497
498 /* Store the PNP info in the first interface for the device */
499 uaa = device_get_ivars(dev);
500 iface_index = uaa->info.bIfaceIndex;
501
502 if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0)
503 device_printf(dev, "Could not set PNP info\n");
504
505 /*
506 * The following information is also replicated in the PNP-info
507 * string which is registered above:
508 */
509 if (ssc->sc_sysctl_ttyname == NULL) {
510 ssc->sc_sysctl_ttyname = SYSCTL_ADD_STRING(NULL,
511 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
512 OID_AUTO, "ttyname", CTLFLAG_RD, ssc->sc_ttyname, 0,
513 "TTY device basename");
514 }
515 if (ssc->sc_sysctl_ttyports == NULL) {
516 ssc->sc_sysctl_ttyports = SYSCTL_ADD_INT(NULL,
517 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
518 OID_AUTO, "ttyports", CTLFLAG_RD,
519 NULL, ssc->sc_subunits, "Number of ports");
520 }
521 }
522
523 static void
ucom_queue_command(struct ucom_softc * sc,usb_proc_callback_t * fn,struct termios * pt,struct usb_proc_msg * t0,struct usb_proc_msg * t1)524 ucom_queue_command(struct ucom_softc *sc,
525 usb_proc_callback_t *fn, struct termios *pt,
526 struct usb_proc_msg *t0, struct usb_proc_msg *t1)
527 {
528 struct ucom_super_softc *ssc = sc->sc_super;
529 struct ucom_param_task *task;
530
531 UCOM_MTX_ASSERT(sc, MA_OWNED);
532
533 if (usb_proc_is_gone(&ssc->sc_tq)) {
534 DPRINTF("proc is gone\n");
535 return; /* nothing to do */
536 }
537 /*
538 * NOTE: The task cannot get executed before we drop the
539 * "sc_mtx" mutex. It is safe to update fields in the message
540 * structure after that the message got queued.
541 */
542 task = (struct ucom_param_task *)
543 usb_proc_msignal(&ssc->sc_tq, t0, t1);
544
545 /* Setup callback and softc pointers */
546 task->hdr.pm_callback = fn;
547 task->sc = sc;
548
549 /*
550 * Make a copy of the termios. This field is only present if
551 * the "pt" field is not NULL.
552 */
553 if (pt != NULL)
554 task->termios_copy = *pt;
555
556 /*
557 * Closing the device should be synchronous.
558 */
559 if (fn == ucom_cfg_close)
560 usb_proc_mwait(&ssc->sc_tq, t0, t1);
561
562 /*
563 * In case of multiple configure requests,
564 * keep track of the last one!
565 */
566 if (fn == ucom_cfg_start_transfers)
567 sc->sc_last_start_xfer = &task->hdr;
568 }
569
570 static void
ucom_shutdown(struct ucom_softc * sc)571 ucom_shutdown(struct ucom_softc *sc)
572 {
573 struct tty *tp = sc->sc_tty;
574
575 UCOM_MTX_ASSERT(sc, MA_OWNED);
576
577 DPRINTF("\n");
578
579 /*
580 * Hang up if necessary:
581 */
582 if (tp->t_termios.c_cflag & HUPCL) {
583 ucom_modem(tp, 0, SER_DTR);
584 }
585 }
586
587 /*
588 * Return values:
589 * 0: normal
590 * else: taskqueue is draining or gone
591 */
592 uint8_t
ucom_cfg_is_gone(struct ucom_softc * sc)593 ucom_cfg_is_gone(struct ucom_softc *sc)
594 {
595 struct ucom_super_softc *ssc = sc->sc_super;
596
597 return (usb_proc_is_gone(&ssc->sc_tq));
598 }
599
600 static void
ucom_cfg_start_transfers(struct usb_proc_msg * _task)601 ucom_cfg_start_transfers(struct usb_proc_msg *_task)
602 {
603 struct ucom_cfg_task *task =
604 (struct ucom_cfg_task *)_task;
605 struct ucom_softc *sc = task->sc;
606
607 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
608 return;
609 }
610 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
611 /* TTY device closed */
612 return;
613 }
614
615 if (_task == sc->sc_last_start_xfer)
616 sc->sc_flag |= UCOM_FLAG_GP_DATA;
617
618 if (sc->sc_callback->ucom_start_read) {
619 (sc->sc_callback->ucom_start_read) (sc);
620 }
621 if (sc->sc_callback->ucom_start_write) {
622 (sc->sc_callback->ucom_start_write) (sc);
623 }
624 }
625
626 static void
ucom_start_transfers(struct ucom_softc * sc)627 ucom_start_transfers(struct ucom_softc *sc)
628 {
629 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
630 return;
631 }
632 /*
633 * Make sure that data transfers are started in both
634 * directions:
635 */
636 if (sc->sc_callback->ucom_start_read) {
637 (sc->sc_callback->ucom_start_read) (sc);
638 }
639 if (sc->sc_callback->ucom_start_write) {
640 (sc->sc_callback->ucom_start_write) (sc);
641 }
642 }
643
644 static void
ucom_cfg_open(struct usb_proc_msg * _task)645 ucom_cfg_open(struct usb_proc_msg *_task)
646 {
647 struct ucom_cfg_task *task =
648 (struct ucom_cfg_task *)_task;
649 struct ucom_softc *sc = task->sc;
650
651 DPRINTF("\n");
652
653 if (sc->sc_flag & UCOM_FLAG_LL_READY) {
654
655 /* already opened */
656
657 } else {
658
659 sc->sc_flag |= UCOM_FLAG_LL_READY;
660
661 if (sc->sc_callback->ucom_cfg_open) {
662 (sc->sc_callback->ucom_cfg_open) (sc);
663
664 /* wait a little */
665 usb_pause_mtx(sc->sc_mtx, hz / 10);
666 }
667 }
668 }
669
670 static int
ucom_open(struct tty * tp)671 ucom_open(struct tty *tp)
672 {
673 struct ucom_softc *sc = tty_softc(tp);
674 int error;
675
676 UCOM_MTX_ASSERT(sc, MA_OWNED);
677
678 if (sc->sc_flag & UCOM_FLAG_GONE) {
679 return (ENXIO);
680 }
681 if (sc->sc_flag & UCOM_FLAG_HL_READY) {
682 /* already opened */
683 return (0);
684 }
685 DPRINTF("tp = %p\n", tp);
686
687 if (sc->sc_callback->ucom_pre_open) {
688 /*
689 * give the lower layer a chance to disallow TTY open, for
690 * example if the device is not present:
691 */
692 error = (sc->sc_callback->ucom_pre_open) (sc);
693 if (error) {
694 return (error);
695 }
696 }
697 sc->sc_flag |= UCOM_FLAG_HL_READY;
698
699 /* Disable transfers */
700 sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
701
702 sc->sc_lsr = 0;
703 sc->sc_msr = 0;
704 sc->sc_mcr = 0;
705
706 /* reset programmed line state */
707 sc->sc_pls_curr = 0;
708 sc->sc_pls_set = 0;
709 sc->sc_pls_clr = 0;
710
711 /* reset jitter buffer */
712 sc->sc_jitterbuf_in = 0;
713 sc->sc_jitterbuf_out = 0;
714
715 ucom_queue_command(sc, ucom_cfg_open, NULL,
716 &sc->sc_open_task[0].hdr,
717 &sc->sc_open_task[1].hdr);
718
719 /* Queue transfer enable command last */
720 ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
721 &sc->sc_start_task[0].hdr,
722 &sc->sc_start_task[1].hdr);
723
724 ucom_modem(tp, SER_DTR | SER_RTS, 0);
725
726 ucom_ring(sc, 0);
727
728 ucom_break(sc, 0);
729
730 ucom_status_change(sc);
731
732 return (0);
733 }
734
735 static void
ucom_cfg_close(struct usb_proc_msg * _task)736 ucom_cfg_close(struct usb_proc_msg *_task)
737 {
738 struct ucom_cfg_task *task =
739 (struct ucom_cfg_task *)_task;
740 struct ucom_softc *sc = task->sc;
741
742 DPRINTF("\n");
743
744 if (sc->sc_flag & UCOM_FLAG_LL_READY) {
745 sc->sc_flag &= ~UCOM_FLAG_LL_READY;
746 if (sc->sc_callback->ucom_cfg_close)
747 (sc->sc_callback->ucom_cfg_close) (sc);
748 } else {
749 /* already closed */
750 }
751 }
752
753 static void
ucom_close(struct tty * tp)754 ucom_close(struct tty *tp)
755 {
756 struct ucom_softc *sc = tty_softc(tp);
757
758 UCOM_MTX_ASSERT(sc, MA_OWNED);
759
760 DPRINTF("tp=%p\n", tp);
761
762 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
763 DPRINTF("tp=%p already closed\n", tp);
764 return;
765 }
766 ucom_shutdown(sc);
767
768 ucom_queue_command(sc, ucom_cfg_close, NULL,
769 &sc->sc_close_task[0].hdr,
770 &sc->sc_close_task[1].hdr);
771
772 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
773
774 if (sc->sc_callback->ucom_stop_read) {
775 (sc->sc_callback->ucom_stop_read) (sc);
776 }
777 }
778
779 static void
ucom_inwakeup(struct tty * tp)780 ucom_inwakeup(struct tty *tp)
781 {
782 struct ucom_softc *sc = tty_softc(tp);
783 uint16_t pos;
784
785 if (sc == NULL)
786 return;
787
788 UCOM_MTX_ASSERT(sc, MA_OWNED);
789
790 DPRINTF("tp=%p\n", tp);
791
792 if (ttydisc_can_bypass(tp) != 0 ||
793 (sc->sc_flag & UCOM_FLAG_HL_READY) == 0 ||
794 (sc->sc_flag & UCOM_FLAG_INWAKEUP) != 0) {
795 return;
796 }
797
798 /* prevent recursion */
799 sc->sc_flag |= UCOM_FLAG_INWAKEUP;
800
801 pos = sc->sc_jitterbuf_out;
802
803 while (sc->sc_jitterbuf_in != pos) {
804 int c;
805
806 c = (char)sc->sc_jitterbuf[pos];
807
808 if (ttydisc_rint(tp, c, 0) == -1)
809 break;
810 pos++;
811 if (pos >= UCOM_JITTERBUF_SIZE)
812 pos -= UCOM_JITTERBUF_SIZE;
813 }
814
815 sc->sc_jitterbuf_out = pos;
816
817 /* clear RTS in async fashion */
818 if ((sc->sc_jitterbuf_in == pos) &&
819 (sc->sc_flag & UCOM_FLAG_RTS_IFLOW))
820 ucom_rts(sc, 0);
821
822 sc->sc_flag &= ~UCOM_FLAG_INWAKEUP;
823 }
824
825 static int
ucom_ioctl(struct tty * tp,u_long cmd,caddr_t data,struct thread * td)826 ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
827 {
828 struct ucom_softc *sc = tty_softc(tp);
829 int error;
830
831 UCOM_MTX_ASSERT(sc, MA_OWNED);
832
833 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
834 return (EIO);
835 }
836 DPRINTF("cmd = 0x%08lx\n", cmd);
837
838 switch (cmd) {
839 #if 0
840 case TIOCSRING:
841 ucom_ring(sc, 1);
842 error = 0;
843 break;
844 case TIOCCRING:
845 ucom_ring(sc, 0);
846 error = 0;
847 break;
848 #endif
849 case TIOCSBRK:
850 ucom_break(sc, 1);
851 error = 0;
852 break;
853 case TIOCCBRK:
854 ucom_break(sc, 0);
855 error = 0;
856 break;
857 default:
858 if (sc->sc_callback->ucom_ioctl) {
859 error = (sc->sc_callback->ucom_ioctl)
860 (sc, cmd, data, 0, td);
861 } else {
862 error = ENOIOCTL;
863 }
864 break;
865 }
866 return (error);
867 }
868
869 static int
ucom_modem(struct tty * tp,int sigon,int sigoff)870 ucom_modem(struct tty *tp, int sigon, int sigoff)
871 {
872 struct ucom_softc *sc = tty_softc(tp);
873 uint8_t onoff;
874
875 UCOM_MTX_ASSERT(sc, MA_OWNED);
876
877 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
878 return (0);
879 }
880 if ((sigon == 0) && (sigoff == 0)) {
881
882 if (sc->sc_mcr & SER_DTR) {
883 sigon |= SER_DTR;
884 }
885 if (sc->sc_mcr & SER_RTS) {
886 sigon |= SER_RTS;
887 }
888 if (sc->sc_msr & SER_CTS) {
889 sigon |= SER_CTS;
890 }
891 if (sc->sc_msr & SER_DCD) {
892 sigon |= SER_DCD;
893 }
894 if (sc->sc_msr & SER_DSR) {
895 sigon |= SER_DSR;
896 }
897 if (sc->sc_msr & SER_RI) {
898 sigon |= SER_RI;
899 }
900 return (sigon);
901 }
902 if (sigon & SER_DTR) {
903 sc->sc_mcr |= SER_DTR;
904 }
905 if (sigoff & SER_DTR) {
906 sc->sc_mcr &= ~SER_DTR;
907 }
908 if (sigon & SER_RTS) {
909 sc->sc_mcr |= SER_RTS;
910 }
911 if (sigoff & SER_RTS) {
912 sc->sc_mcr &= ~SER_RTS;
913 }
914 onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
915 ucom_dtr(sc, onoff);
916
917 onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
918 ucom_rts(sc, onoff);
919
920 return (0);
921 }
922
923 static void
ucom_cfg_line_state(struct usb_proc_msg * _task)924 ucom_cfg_line_state(struct usb_proc_msg *_task)
925 {
926 struct ucom_cfg_task *task =
927 (struct ucom_cfg_task *)_task;
928 struct ucom_softc *sc = task->sc;
929 uint8_t notch_bits;
930 uint8_t any_bits;
931 uint8_t prev_value;
932 uint8_t last_value;
933 uint8_t mask;
934
935 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
936 return;
937 }
938
939 mask = 0;
940 /* compute callback mask */
941 if (sc->sc_callback->ucom_cfg_set_dtr)
942 mask |= UCOM_LS_DTR;
943 if (sc->sc_callback->ucom_cfg_set_rts)
944 mask |= UCOM_LS_RTS;
945 if (sc->sc_callback->ucom_cfg_set_break)
946 mask |= UCOM_LS_BREAK;
947 if (sc->sc_callback->ucom_cfg_set_ring)
948 mask |= UCOM_LS_RING;
949
950 /* compute the bits we are to program */
951 notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
952 any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
953 prev_value = sc->sc_pls_curr ^ notch_bits;
954 last_value = sc->sc_pls_curr;
955
956 /* reset programmed line state */
957 sc->sc_pls_curr = 0;
958 sc->sc_pls_set = 0;
959 sc->sc_pls_clr = 0;
960
961 /* ensure that we don't lose any levels */
962 if (notch_bits & UCOM_LS_DTR)
963 sc->sc_callback->ucom_cfg_set_dtr(sc,
964 (prev_value & UCOM_LS_DTR) ? 1 : 0);
965 if (notch_bits & UCOM_LS_RTS)
966 sc->sc_callback->ucom_cfg_set_rts(sc,
967 (prev_value & UCOM_LS_RTS) ? 1 : 0);
968 if (notch_bits & UCOM_LS_BREAK)
969 sc->sc_callback->ucom_cfg_set_break(sc,
970 (prev_value & UCOM_LS_BREAK) ? 1 : 0);
971 if (notch_bits & UCOM_LS_RING)
972 sc->sc_callback->ucom_cfg_set_ring(sc,
973 (prev_value & UCOM_LS_RING) ? 1 : 0);
974
975 /* set last value */
976 if (any_bits & UCOM_LS_DTR)
977 sc->sc_callback->ucom_cfg_set_dtr(sc,
978 (last_value & UCOM_LS_DTR) ? 1 : 0);
979 if (any_bits & UCOM_LS_RTS)
980 sc->sc_callback->ucom_cfg_set_rts(sc,
981 (last_value & UCOM_LS_RTS) ? 1 : 0);
982 if (any_bits & UCOM_LS_BREAK)
983 sc->sc_callback->ucom_cfg_set_break(sc,
984 (last_value & UCOM_LS_BREAK) ? 1 : 0);
985 if (any_bits & UCOM_LS_RING)
986 sc->sc_callback->ucom_cfg_set_ring(sc,
987 (last_value & UCOM_LS_RING) ? 1 : 0);
988 }
989
990 static void
ucom_line_state(struct ucom_softc * sc,uint8_t set_bits,uint8_t clear_bits)991 ucom_line_state(struct ucom_softc *sc,
992 uint8_t set_bits, uint8_t clear_bits)
993 {
994 UCOM_MTX_ASSERT(sc, MA_OWNED);
995
996 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
997 return;
998 }
999
1000 DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
1001
1002 /* update current programmed line state */
1003 sc->sc_pls_curr |= set_bits;
1004 sc->sc_pls_curr &= ~clear_bits;
1005 sc->sc_pls_set |= set_bits;
1006 sc->sc_pls_clr |= clear_bits;
1007
1008 /* defer driver programming */
1009 ucom_queue_command(sc, ucom_cfg_line_state, NULL,
1010 &sc->sc_line_state_task[0].hdr,
1011 &sc->sc_line_state_task[1].hdr);
1012 }
1013
1014 static void
ucom_ring(struct ucom_softc * sc,uint8_t onoff)1015 ucom_ring(struct ucom_softc *sc, uint8_t onoff)
1016 {
1017 DPRINTF("onoff = %d\n", onoff);
1018
1019 if (onoff)
1020 ucom_line_state(sc, UCOM_LS_RING, 0);
1021 else
1022 ucom_line_state(sc, 0, UCOM_LS_RING);
1023 }
1024
1025 static void
ucom_break(struct ucom_softc * sc,uint8_t onoff)1026 ucom_break(struct ucom_softc *sc, uint8_t onoff)
1027 {
1028 DPRINTF("onoff = %d\n", onoff);
1029
1030 if (onoff)
1031 ucom_line_state(sc, UCOM_LS_BREAK, 0);
1032 else
1033 ucom_line_state(sc, 0, UCOM_LS_BREAK);
1034 }
1035
1036 static void
ucom_dtr(struct ucom_softc * sc,uint8_t onoff)1037 ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
1038 {
1039 DPRINTF("onoff = %d\n", onoff);
1040
1041 if (onoff)
1042 ucom_line_state(sc, UCOM_LS_DTR, 0);
1043 else
1044 ucom_line_state(sc, 0, UCOM_LS_DTR);
1045 }
1046
1047 static void
ucom_rts(struct ucom_softc * sc,uint8_t onoff)1048 ucom_rts(struct ucom_softc *sc, uint8_t onoff)
1049 {
1050 DPRINTF("onoff = %d\n", onoff);
1051
1052 if (onoff)
1053 ucom_line_state(sc, UCOM_LS_RTS, 0);
1054 else
1055 ucom_line_state(sc, 0, UCOM_LS_RTS);
1056 }
1057
1058 static void
ucom_cfg_status_change(struct usb_proc_msg * _task)1059 ucom_cfg_status_change(struct usb_proc_msg *_task)
1060 {
1061 struct ucom_cfg_task *task =
1062 (struct ucom_cfg_task *)_task;
1063 struct ucom_softc *sc = task->sc;
1064 struct tty *tp;
1065 uint8_t new_msr;
1066 uint8_t new_lsr;
1067 uint8_t onoff;
1068 uint8_t lsr_delta;
1069
1070 tp = sc->sc_tty;
1071
1072 UCOM_MTX_ASSERT(sc, MA_OWNED);
1073
1074 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1075 return;
1076 }
1077 if (sc->sc_callback->ucom_cfg_get_status == NULL) {
1078 return;
1079 }
1080 /* get status */
1081
1082 new_msr = 0;
1083 new_lsr = 0;
1084
1085 (sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
1086
1087 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1088 /* TTY device closed */
1089 return;
1090 }
1091 onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
1092 lsr_delta = (sc->sc_lsr ^ new_lsr);
1093
1094 sc->sc_msr = new_msr;
1095 sc->sc_lsr = new_lsr;
1096
1097 if (onoff) {
1098
1099 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
1100
1101 DPRINTF("DCD changed to %d\n", onoff);
1102
1103 ttydisc_modem(tp, onoff);
1104 }
1105
1106 if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
1107
1108 DPRINTF("BREAK detected\n");
1109
1110 ttydisc_rint(tp, 0, TRE_BREAK);
1111 ttydisc_rint_done(tp);
1112 }
1113
1114 if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
1115
1116 DPRINTF("Frame error detected\n");
1117
1118 ttydisc_rint(tp, 0, TRE_FRAMING);
1119 ttydisc_rint_done(tp);
1120 }
1121
1122 if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
1123
1124 DPRINTF("Parity error detected\n");
1125
1126 ttydisc_rint(tp, 0, TRE_PARITY);
1127 ttydisc_rint_done(tp);
1128 }
1129 }
1130
1131 void
ucom_status_change(struct ucom_softc * sc)1132 ucom_status_change(struct ucom_softc *sc)
1133 {
1134 UCOM_MTX_ASSERT(sc, MA_OWNED);
1135
1136 if (sc->sc_flag & UCOM_FLAG_CONSOLE)
1137 return; /* not supported */
1138
1139 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1140 return;
1141 }
1142 DPRINTF("\n");
1143
1144 ucom_queue_command(sc, ucom_cfg_status_change, NULL,
1145 &sc->sc_status_task[0].hdr,
1146 &sc->sc_status_task[1].hdr);
1147 }
1148
1149 static void
ucom_cfg_param(struct usb_proc_msg * _task)1150 ucom_cfg_param(struct usb_proc_msg *_task)
1151 {
1152 struct ucom_param_task *task =
1153 (struct ucom_param_task *)_task;
1154 struct ucom_softc *sc = task->sc;
1155
1156 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
1157 return;
1158 }
1159 if (sc->sc_callback->ucom_cfg_param == NULL) {
1160 return;
1161 }
1162
1163 (sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
1164
1165 /* wait a little */
1166 usb_pause_mtx(sc->sc_mtx, hz / 10);
1167 }
1168
1169 static int
ucom_param(struct tty * tp,struct termios * t)1170 ucom_param(struct tty *tp, struct termios *t)
1171 {
1172 struct ucom_softc *sc = tty_softc(tp);
1173 uint8_t opened;
1174 int error;
1175
1176 UCOM_MTX_ASSERT(sc, MA_OWNED);
1177
1178 opened = 0;
1179 error = 0;
1180
1181 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1182
1183 /* XXX the TTY layer should call "open()" first! */
1184 /*
1185 * Not quite: Its ordering is partly backwards, but
1186 * some parameters must be set early in ttydev_open(),
1187 * possibly before calling ttydevsw_open().
1188 */
1189 error = ucom_open(tp);
1190 if (error)
1191 goto done;
1192
1193 opened = 1;
1194 }
1195 DPRINTF("sc = %p\n", sc);
1196
1197 /* Check requested parameters. */
1198 if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
1199 /* XXX c_ospeed == 0 is perfectly valid. */
1200 DPRINTF("mismatch ispeed and ospeed\n");
1201 error = EINVAL;
1202 goto done;
1203 }
1204 t->c_ispeed = t->c_ospeed;
1205
1206 if (sc->sc_callback->ucom_pre_param) {
1207 /* Let the lower layer verify the parameters */
1208 error = (sc->sc_callback->ucom_pre_param) (sc, t);
1209 if (error) {
1210 DPRINTF("callback error = %d\n", error);
1211 goto done;
1212 }
1213 }
1214
1215 /* Disable transfers */
1216 sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
1217
1218 /* Queue baud rate programming command first */
1219 ucom_queue_command(sc, ucom_cfg_param, t,
1220 &sc->sc_param_task[0].hdr,
1221 &sc->sc_param_task[1].hdr);
1222
1223 /* Queue transfer enable command last */
1224 ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
1225 &sc->sc_start_task[0].hdr,
1226 &sc->sc_start_task[1].hdr);
1227
1228 if (t->c_cflag & CRTS_IFLOW) {
1229 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
1230 } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
1231 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
1232 ucom_modem(tp, SER_RTS, 0);
1233 }
1234 done:
1235 if (error) {
1236 if (opened) {
1237 ucom_close(tp);
1238 }
1239 }
1240 return (error);
1241 }
1242
1243 static void
ucom_outwakeup(struct tty * tp)1244 ucom_outwakeup(struct tty *tp)
1245 {
1246 struct ucom_softc *sc = tty_softc(tp);
1247
1248 UCOM_MTX_ASSERT(sc, MA_OWNED);
1249
1250 DPRINTF("sc = %p\n", sc);
1251
1252 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1253 /* The higher layer is not ready */
1254 return;
1255 }
1256 ucom_start_transfers(sc);
1257 }
1258
1259 /*------------------------------------------------------------------------*
1260 * ucom_get_data
1261 *
1262 * Return values:
1263 * 0: No data is available.
1264 * Else: Data is available.
1265 *------------------------------------------------------------------------*/
1266 uint8_t
ucom_get_data(struct ucom_softc * sc,struct usb_page_cache * pc,uint32_t offset,uint32_t len,uint32_t * actlen)1267 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1268 uint32_t offset, uint32_t len, uint32_t *actlen)
1269 {
1270 struct usb_page_search res;
1271 struct tty *tp = sc->sc_tty;
1272 uint32_t cnt;
1273 uint32_t offset_orig;
1274
1275 UCOM_MTX_ASSERT(sc, MA_OWNED);
1276
1277 if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1278 unsigned int temp;
1279
1280 /* get total TX length */
1281
1282 temp = ucom_cons_tx_high - ucom_cons_tx_low;
1283 temp %= UCOM_CONS_BUFSIZE;
1284
1285 /* limit TX length */
1286
1287 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
1288 temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
1289
1290 if (temp > len)
1291 temp = len;
1292
1293 /* copy in data */
1294
1295 usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
1296
1297 /* update counters */
1298
1299 ucom_cons_tx_low += temp;
1300 ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
1301
1302 /* store actual length */
1303
1304 *actlen = temp;
1305
1306 return (temp ? 1 : 0);
1307 }
1308
1309 if (tty_gone(tp) ||
1310 !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1311 actlen[0] = 0;
1312 return (0); /* multiport device polling */
1313 }
1314 offset_orig = offset;
1315
1316 while (len != 0) {
1317
1318 usbd_get_page(pc, offset, &res);
1319
1320 if (res.length > len) {
1321 res.length = len;
1322 }
1323 /* copy data directly into USB buffer */
1324 cnt = ttydisc_getc(tp, res.buffer, res.length);
1325
1326 offset += cnt;
1327 len -= cnt;
1328
1329 if (cnt < res.length) {
1330 /* end of buffer */
1331 break;
1332 }
1333 }
1334
1335 actlen[0] = offset - offset_orig;
1336
1337 DPRINTF("cnt=%d\n", actlen[0]);
1338
1339 if (actlen[0] == 0) {
1340 return (0);
1341 }
1342 return (1);
1343 }
1344
1345 void
ucom_put_data(struct ucom_softc * sc,struct usb_page_cache * pc,uint32_t offset,uint32_t len)1346 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
1347 uint32_t offset, uint32_t len)
1348 {
1349 struct usb_page_search res;
1350 struct tty *tp = sc->sc_tty;
1351 char *buf;
1352 uint32_t cnt;
1353
1354 UCOM_MTX_ASSERT(sc, MA_OWNED);
1355
1356 if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
1357 unsigned int temp;
1358
1359 /* get maximum RX length */
1360
1361 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
1362 temp %= UCOM_CONS_BUFSIZE;
1363
1364 /* limit RX length */
1365
1366 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
1367 temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
1368
1369 if (temp > len)
1370 temp = len;
1371
1372 /* copy out data */
1373
1374 usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
1375
1376 /* update counters */
1377
1378 ucom_cons_rx_high += temp;
1379 ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
1380
1381 return;
1382 }
1383
1384 if (tty_gone(tp))
1385 return; /* multiport device polling */
1386
1387 if (len == 0)
1388 return; /* no data */
1389
1390 /* set a flag to prevent recursation ? */
1391
1392 while (len > 0) {
1393
1394 usbd_get_page(pc, offset, &res);
1395
1396 if (res.length > len) {
1397 res.length = len;
1398 }
1399 len -= res.length;
1400 offset += res.length;
1401
1402 /* pass characters to tty layer */
1403
1404 buf = res.buffer;
1405 cnt = res.length;
1406
1407 /* first check if we can pass the buffer directly */
1408
1409 if (ttydisc_can_bypass(tp)) {
1410
1411 /* clear any jitter buffer */
1412 sc->sc_jitterbuf_in = 0;
1413 sc->sc_jitterbuf_out = 0;
1414
1415 if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1416 DPRINTF("tp=%p, data lost\n", tp);
1417 }
1418 continue;
1419 }
1420 /* need to loop */
1421
1422 for (cnt = 0; cnt != res.length; cnt++) {
1423 if (sc->sc_jitterbuf_in != sc->sc_jitterbuf_out ||
1424 ttydisc_rint(tp, buf[cnt], 0) == -1) {
1425 uint16_t end;
1426 uint16_t pos;
1427
1428 pos = sc->sc_jitterbuf_in;
1429 end = sc->sc_jitterbuf_out +
1430 UCOM_JITTERBUF_SIZE - 1;
1431 if (end >= UCOM_JITTERBUF_SIZE)
1432 end -= UCOM_JITTERBUF_SIZE;
1433
1434 for (; cnt != res.length; cnt++) {
1435 if (pos == end)
1436 break;
1437 sc->sc_jitterbuf[pos] = buf[cnt];
1438 pos++;
1439 if (pos >= UCOM_JITTERBUF_SIZE)
1440 pos -= UCOM_JITTERBUF_SIZE;
1441 }
1442
1443 sc->sc_jitterbuf_in = pos;
1444
1445 /* set RTS in async fashion */
1446 if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW)
1447 ucom_rts(sc, 1);
1448
1449 DPRINTF("tp=%p, lost %d "
1450 "chars\n", tp, res.length - cnt);
1451 break;
1452 }
1453 }
1454 }
1455 ttydisc_rint_done(tp);
1456 }
1457
1458 static void
ucom_free(void * xsc)1459 ucom_free(void *xsc)
1460 {
1461 struct ucom_softc *sc = xsc;
1462
1463 if (sc->sc_callback->ucom_free != NULL)
1464 sc->sc_callback->ucom_free(sc);
1465 else
1466 ucom_unref(sc->sc_super);
1467
1468 mtx_lock(&ucom_mtx);
1469 ucom_close_refs--;
1470 mtx_unlock(&ucom_mtx);
1471 }
1472
1473 static cn_probe_t ucom_cnprobe;
1474 static cn_init_t ucom_cninit;
1475 static cn_term_t ucom_cnterm;
1476 static cn_getc_t ucom_cngetc;
1477 static cn_putc_t ucom_cnputc;
1478 static cn_grab_t ucom_cngrab;
1479 static cn_ungrab_t ucom_cnungrab;
1480
1481 CONSOLE_DRIVER(ucom);
1482
1483 static void
ucom_cnprobe(struct consdev * cp)1484 ucom_cnprobe(struct consdev *cp)
1485 {
1486 if (ucom_cons_unit != -1)
1487 cp->cn_pri = CN_NORMAL;
1488 else
1489 cp->cn_pri = CN_DEAD;
1490
1491 strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
1492 }
1493
1494 static void
ucom_cninit(struct consdev * cp)1495 ucom_cninit(struct consdev *cp)
1496 {
1497 }
1498
1499 static void
ucom_cnterm(struct consdev * cp)1500 ucom_cnterm(struct consdev *cp)
1501 {
1502 }
1503
1504 static void
ucom_cngrab(struct consdev * cp)1505 ucom_cngrab(struct consdev *cp)
1506 {
1507 }
1508
1509 static void
ucom_cnungrab(struct consdev * cp)1510 ucom_cnungrab(struct consdev *cp)
1511 {
1512 }
1513
1514 static int
ucom_cngetc(struct consdev * cd)1515 ucom_cngetc(struct consdev *cd)
1516 {
1517 struct ucom_softc *sc = ucom_cons_softc;
1518 int c;
1519
1520 if (sc == NULL)
1521 return (-1);
1522
1523 UCOM_MTX_LOCK(sc);
1524
1525 if (ucom_cons_rx_low != ucom_cons_rx_high) {
1526 c = ucom_cons_rx_buf[ucom_cons_rx_low];
1527 ucom_cons_rx_low ++;
1528 ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
1529 } else {
1530 c = -1;
1531 }
1532
1533 /* start USB transfers */
1534 ucom_outwakeup(sc->sc_tty);
1535
1536 UCOM_MTX_UNLOCK(sc);
1537
1538 /* poll if necessary */
1539 if (kdb_active && sc->sc_callback->ucom_poll)
1540 (sc->sc_callback->ucom_poll) (sc);
1541
1542 return (c);
1543 }
1544
1545 static void
ucom_cnputc(struct consdev * cd,int c)1546 ucom_cnputc(struct consdev *cd, int c)
1547 {
1548 struct ucom_softc *sc = ucom_cons_softc;
1549 unsigned int temp;
1550
1551 if (sc == NULL)
1552 return;
1553
1554 repeat:
1555
1556 UCOM_MTX_LOCK(sc);
1557
1558 /* compute maximum TX length */
1559
1560 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
1561 temp %= UCOM_CONS_BUFSIZE;
1562
1563 if (temp) {
1564 ucom_cons_tx_buf[ucom_cons_tx_high] = c;
1565 ucom_cons_tx_high ++;
1566 ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
1567 }
1568
1569 /* start USB transfers */
1570 ucom_outwakeup(sc->sc_tty);
1571
1572 UCOM_MTX_UNLOCK(sc);
1573
1574 /* poll if necessary */
1575 if (kdb_active && sc->sc_callback->ucom_poll) {
1576 (sc->sc_callback->ucom_poll) (sc);
1577 /* simple flow control */
1578 if (temp == 0)
1579 goto repeat;
1580 }
1581 }
1582
1583 /*------------------------------------------------------------------------*
1584 * ucom_ref
1585 *
1586 * This function will increment the super UCOM reference count.
1587 *------------------------------------------------------------------------*/
1588 void
ucom_ref(struct ucom_super_softc * ssc)1589 ucom_ref(struct ucom_super_softc *ssc)
1590 {
1591 mtx_lock(&ucom_mtx);
1592 ssc->sc_refs++;
1593 mtx_unlock(&ucom_mtx);
1594 }
1595
1596 /*------------------------------------------------------------------------*
1597 * ucom_free_unit
1598 *
1599 * This function will free the super UCOM's allocated unit
1600 * number. This function can be called on a zero-initialized
1601 * structure. This function can be called multiple times.
1602 *------------------------------------------------------------------------*/
1603 static void
ucom_free_unit(struct ucom_super_softc * ssc)1604 ucom_free_unit(struct ucom_super_softc *ssc)
1605 {
1606 if (!(ssc->sc_flag & UCOM_FLAG_FREE_UNIT))
1607 return;
1608
1609 ucom_unit_free(ssc->sc_unit);
1610
1611 ssc->sc_flag &= ~UCOM_FLAG_FREE_UNIT;
1612 }
1613
1614 /*------------------------------------------------------------------------*
1615 * ucom_unref
1616 *
1617 * This function will decrement the super UCOM reference count.
1618 *
1619 * Return values:
1620 * 0: UCOM structures are still referenced.
1621 * Else: UCOM structures are no longer referenced.
1622 *------------------------------------------------------------------------*/
1623 int
ucom_unref(struct ucom_super_softc * ssc)1624 ucom_unref(struct ucom_super_softc *ssc)
1625 {
1626 int retval;
1627
1628 mtx_lock(&ucom_mtx);
1629 retval = (ssc->sc_refs < 2);
1630 ssc->sc_refs--;
1631 mtx_unlock(&ucom_mtx);
1632
1633 if (retval)
1634 ucom_free_unit(ssc);
1635
1636 return (retval);
1637 }
1638
1639 #if defined(GDB)
1640
1641 #include <gdb/gdb.h>
1642
1643 static gdb_probe_f ucom_gdbprobe;
1644 static gdb_init_f ucom_gdbinit;
1645 static gdb_term_f ucom_gdbterm;
1646 static gdb_getc_f ucom_gdbgetc;
1647 static gdb_putc_f ucom_gdbputc;
1648
1649 GDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
1650
1651 static int
ucom_gdbprobe(void)1652 ucom_gdbprobe(void)
1653 {
1654 return ((ucom_cons_softc != NULL) ? 0 : -1);
1655 }
1656
1657 static void
ucom_gdbinit(void)1658 ucom_gdbinit(void)
1659 {
1660 }
1661
1662 static void
ucom_gdbterm(void)1663 ucom_gdbterm(void)
1664 {
1665 }
1666
1667 static void
ucom_gdbputc(int c)1668 ucom_gdbputc(int c)
1669 {
1670 ucom_cnputc(NULL, c);
1671 }
1672
1673 static int
ucom_gdbgetc(void)1674 ucom_gdbgetc(void)
1675 {
1676 return (ucom_cngetc(NULL));
1677 }
1678
1679 #endif
1680