Deleted Added
full compact
usb_serial.c (192502) usb_serial.c (192820)
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>
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: head/sys/dev/usb/serial/usb_serial.c 192502 2009-05-21 01:48:42Z thompsa $");
31__FBSDID("$FreeBSD: head/sys/dev/usb/serial/usb_serial.c 192820 2009-05-26 17:06:36Z thompsa $");
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 * 3. All advertising materials mentioning features or use of this software
50 * must display the following acknowledgement:
51 * This product includes software developed by the NetBSD
52 * Foundation, Inc. and its contributors.
53 * 4. Neither the name of The NetBSD Foundation nor the names of its
54 * contributors may be used to endorse or promote products derived
55 * from this software without specific prior written permission.
56 *
57 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67 * POSSIBILITY OF SUCH DAMAGE.
68 */
69
70#include <dev/usb/usb.h>
71#include <dev/usb/usb_mfunc.h>
72#include <dev/usb/usb_error.h>
73#include <dev/usb/usb_cdc.h>
74#include <dev/usb/usb_ioctl.h>
75
76#define USB_DEBUG_VAR usb2_com_debug
77
78#include <dev/usb/usb_core.h>
79#include <dev/usb/usb_debug.h>
80#include <dev/usb/usb_process.h>
81#include <dev/usb/usb_request.h>
82#include <dev/usb/usb_busdma.h>
83#include <dev/usb/usb_util.h>
84
85#include <dev/usb/serial/usb_serial.h>
86
87#if USB_DEBUG
88static int usb2_com_debug = 0;
89
90SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
91SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
92 &usb2_com_debug, 0, "ucom debug level");
93#endif
94
95static usb2_proc_callback_t usb2_com_cfg_start_transfers;
96static usb2_proc_callback_t usb2_com_cfg_open;
97static usb2_proc_callback_t usb2_com_cfg_close;
98static usb2_proc_callback_t usb2_com_cfg_line_state;
99static usb2_proc_callback_t usb2_com_cfg_status_change;
100static usb2_proc_callback_t usb2_com_cfg_param;
101
102static uint8_t usb2_com_units_alloc(uint32_t, uint32_t *);
103static void usb2_com_units_free(uint32_t, uint32_t);
104static int usb2_com_attach_tty(struct usb2_com_softc *, uint32_t);
105static void usb2_com_detach_tty(struct usb2_com_softc *);
106static void usb2_com_queue_command(struct usb2_com_softc *,
107 usb2_proc_callback_t *, struct termios *pt,
108 struct usb2_proc_msg *t0, struct usb2_proc_msg *t1);
109static void usb2_com_shutdown(struct usb2_com_softc *);
110static void usb2_com_break(struct usb2_com_softc *, uint8_t);
111static void usb2_com_dtr(struct usb2_com_softc *, uint8_t);
112static void usb2_com_rts(struct usb2_com_softc *, uint8_t);
113
114static tsw_open_t usb2_com_open;
115static tsw_close_t usb2_com_close;
116static tsw_ioctl_t usb2_com_ioctl;
117static tsw_modem_t usb2_com_modem;
118static tsw_param_t usb2_com_param;
119static tsw_outwakeup_t usb2_com_outwakeup;
120static tsw_free_t usb2_com_free;
121
122static struct ttydevsw usb2_com_class = {
123 .tsw_flags = TF_INITLOCK | TF_CALLOUT,
124 .tsw_open = usb2_com_open,
125 .tsw_close = usb2_com_close,
126 .tsw_outwakeup = usb2_com_outwakeup,
127 .tsw_ioctl = usb2_com_ioctl,
128 .tsw_param = usb2_com_param,
129 .tsw_modem = usb2_com_modem,
130 .tsw_free = usb2_com_free,
131};
132
133MODULE_DEPEND(ucom, usb, 1, 1, 1);
134MODULE_VERSION(ucom, 1);
135
136#define UCOM_UNIT_MAX 0x1000 /* exclusive */
137#define UCOM_SUB_UNIT_MAX 0x100 /* exclusive */
138
139static uint8_t usb2_com_bitmap[(UCOM_UNIT_MAX + 7) / 8];
140
141static uint8_t
142usb2_com_units_alloc(uint32_t sub_units, uint32_t *p_root_unit)
143{
144 uint32_t n;
145 uint32_t o;
146 uint32_t x;
147 uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units);
148 uint8_t error = 1;
149
150 mtx_lock(&Giant);
151
152 for (n = 0; n < max; n += sub_units) {
153
154 /* check for free consecutive bits */
155
156 for (o = 0; o < sub_units; o++) {
157
158 x = n + o;
159
160 if (usb2_com_bitmap[x / 8] & (1 << (x % 8))) {
161 goto skip;
162 }
163 }
164
165 /* allocate */
166
167 for (o = 0; o < sub_units; o++) {
168
169 x = n + o;
170
171 usb2_com_bitmap[x / 8] |= (1 << (x % 8));
172 }
173
174 error = 0;
175
176 break;
177
178skip: ;
179 }
180
181 mtx_unlock(&Giant);
182
183 /*
184 * Always set the variable pointed to by "p_root_unit" so that
185 * the compiler does not think that it is used uninitialised:
186 */
187 *p_root_unit = n;
188
189 return (error);
190}
191
192static void
193usb2_com_units_free(uint32_t root_unit, uint32_t sub_units)
194{
195 uint32_t x;
196
197 mtx_lock(&Giant);
198
199 while (sub_units--) {
200 x = root_unit + sub_units;
201 usb2_com_bitmap[x / 8] &= ~(1 << (x % 8));
202 }
203
204 mtx_unlock(&Giant);
205}
206
207/*
208 * "N" sub_units are setup at a time. All sub-units will
209 * be given sequential unit numbers. The number of
210 * sub-units can be used to differentiate among
211 * different types of devices.
212 *
213 * The mutex pointed to by "mtx" is applied before all
214 * callbacks are called back. Also "mtx" must be applied
215 * before calling into the ucom-layer!
216 */
217int
218usb2_com_attach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc,
219 uint32_t sub_units, void *parent,
220 const struct usb2_com_callback *callback, struct mtx *mtx)
221{
222 uint32_t n;
223 uint32_t root_unit;
224 int error = 0;
225
226 if ((sc == NULL) ||
227 (sub_units == 0) ||
228 (sub_units > UCOM_SUB_UNIT_MAX) ||
229 (callback == NULL)) {
230 return (EINVAL);
231 }
232
233 /* XXX unit management does not really belong here */
234 if (usb2_com_units_alloc(sub_units, &root_unit)) {
235 return (ENOMEM);
236 }
237
238 error = usb2_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
239 if (error) {
240 usb2_com_units_free(root_unit, sub_units);
241 return (error);
242 }
243
244 for (n = 0; n != sub_units; n++, sc++) {
245 sc->sc_unit = root_unit + n;
246 sc->sc_local_unit = n;
247 sc->sc_super = ssc;
248 sc->sc_mtx = mtx;
249 sc->sc_parent = parent;
250 sc->sc_callback = callback;
251
252 error = usb2_com_attach_tty(sc, sub_units);
253 if (error) {
254 usb2_com_detach(ssc, sc - n, n);
255 usb2_com_units_free(root_unit + n, sub_units - n);
256 return (error);
257 }
258 sc->sc_flag |= UCOM_FLAG_ATTACHED;
259 }
260 return (0);
261}
262
263/*
264 * NOTE: the following function will do nothing if
265 * the structure pointed to by "ssc" and "sc" is zero.
266 */
267void
268usb2_com_detach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc,
269 uint32_t sub_units)
270{
271 uint32_t n;
272
273 usb2_proc_drain(&ssc->sc_tq);
274
275 for (n = 0; n != sub_units; n++, sc++) {
276 if (sc->sc_flag & UCOM_FLAG_ATTACHED) {
277
278 usb2_com_detach_tty(sc);
279
280 usb2_com_units_free(sc->sc_unit, 1);
281
282 /* avoid duplicate detach: */
283 sc->sc_flag &= ~UCOM_FLAG_ATTACHED;
284 }
285 }
286 usb2_proc_free(&ssc->sc_tq);
287}
288
289static int
290usb2_com_attach_tty(struct usb2_com_softc *sc, uint32_t sub_units)
291{
292 struct tty *tp;
293 int error = 0;
294 char buf[32]; /* temporary TTY device name buffer */
295
296 tp = tty_alloc(&usb2_com_class, sc, sc->sc_mtx);
297 if (tp == NULL) {
298 error = ENOMEM;
299 goto done;
300 }
301 DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit);
302
303 buf[0] = 0; /* set some default value */
304
305 /* Check if the client has a custom TTY name */
306 if (sc->sc_callback->usb2_com_tty_name) {
307 sc->sc_callback->usb2_com_tty_name(sc, buf,
308 sizeof(buf), sc->sc_local_unit);
309 }
310 if (buf[0] == 0) {
311 /* Use default TTY name */
312 if (sub_units > 1) {
313 /* multiple modems in one */
314 if (snprintf(buf, sizeof(buf), "U%u.%u",
315 sc->sc_unit - sc->sc_local_unit,
316 sc->sc_local_unit)) {
317 /* ignore */
318 }
319 } else {
320 /* single modem */
321 if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) {
322 /* ignore */
323 }
324 }
325 }
326 tty_makedev(tp, NULL, "%s", buf);
327
328 sc->sc_tty = tp;
329
330 DPRINTF("ttycreate: %s\n", buf);
331 usb2_cv_init(&sc->sc_cv, "usb2_com");
332
333done:
334 return (error);
335}
336
337static void
338usb2_com_detach_tty(struct usb2_com_softc *sc)
339{
340 struct tty *tp = sc->sc_tty;
341
342 DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
343
344 /* the config thread has been stopped when we get here */
345
346 mtx_lock(sc->sc_mtx);
347 sc->sc_flag |= UCOM_FLAG_GONE;
348 sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
349 UCOM_FLAG_LL_READY);
350 mtx_unlock(sc->sc_mtx);
351 if (tp) {
352 tty_lock(tp);
353
354 usb2_com_close(tp); /* close, if any */
355
356 tty_rel_gone(tp);
357
358 mtx_lock(sc->sc_mtx);
359 /* Wait for the callback after the TTY is torn down */
360 while (sc->sc_ttyfreed == 0)
361 usb2_cv_wait(&sc->sc_cv, sc->sc_mtx);
362 /*
363 * make sure that read and write transfers are stopped
364 */
365 if (sc->sc_callback->usb2_com_stop_read) {
366 (sc->sc_callback->usb2_com_stop_read) (sc);
367 }
368 if (sc->sc_callback->usb2_com_stop_write) {
369 (sc->sc_callback->usb2_com_stop_write) (sc);
370 }
371 mtx_unlock(sc->sc_mtx);
372 }
373 usb2_cv_destroy(&sc->sc_cv);
374}
375
376static void
377usb2_com_queue_command(struct usb2_com_softc *sc,
378 usb2_proc_callback_t *fn, struct termios *pt,
379 struct usb2_proc_msg *t0, struct usb2_proc_msg *t1)
380{
381 struct usb2_com_super_softc *ssc = sc->sc_super;
382 struct usb2_com_param_task *task;
383
384 mtx_assert(sc->sc_mtx, MA_OWNED);
385
386 if (usb2_proc_is_gone(&ssc->sc_tq)) {
387 DPRINTF("proc is gone\n");
388 return; /* nothing to do */
389 }
390 /*
391 * NOTE: The task cannot get executed before we drop the
392 * "sc_mtx" mutex. It is safe to update fields in the message
393 * structure after that the message got queued.
394 */
395 task = (struct usb2_com_param_task *)
396 usb2_proc_msignal(&ssc->sc_tq, t0, t1);
397
398 /* Setup callback and softc pointers */
399 task->hdr.pm_callback = fn;
400 task->sc = sc;
401
402 /*
403 * Make a copy of the termios. This field is only present if
404 * the "pt" field is not NULL.
405 */
406 if (pt != NULL)
407 task->termios_copy = *pt;
408
409 /*
410 * Closing the device should be synchronous.
411 */
412 if (fn == usb2_com_cfg_close)
413 usb2_proc_mwait(&ssc->sc_tq, t0, t1);
414
415 /*
416 * In case of multiple configure requests,
417 * keep track of the last one!
418 */
419 if (fn == usb2_com_cfg_start_transfers)
420 sc->sc_last_start_xfer = &task->hdr;
421}
422
423static void
424usb2_com_shutdown(struct usb2_com_softc *sc)
425{
426 struct tty *tp = sc->sc_tty;
427
428 mtx_assert(sc->sc_mtx, MA_OWNED);
429
430 DPRINTF("\n");
431
432 /*
433 * Hang up if necessary:
434 */
435 if (tp->t_termios.c_cflag & HUPCL) {
436 usb2_com_modem(tp, 0, SER_DTR);
437 }
438}
439
440/*
441 * Return values:
442 * 0: normal
443 * else: taskqueue is draining or gone
444 */
445uint8_t
446usb2_com_cfg_is_gone(struct usb2_com_softc *sc)
447{
448 struct usb2_com_super_softc *ssc = sc->sc_super;
449
450 return (usb2_proc_is_gone(&ssc->sc_tq));
451}
452
453static void
454usb2_com_cfg_start_transfers(struct usb2_proc_msg *_task)
455{
456 struct usb2_com_cfg_task *task =
457 (struct usb2_com_cfg_task *)_task;
458 struct usb2_com_softc *sc = task->sc;
459
460 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
461 return;
462 }
463 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
464 /* TTY device closed */
465 return;
466 }
467
468 if (_task == sc->sc_last_start_xfer)
469 sc->sc_flag |= UCOM_FLAG_GP_DATA;
470
471 if (sc->sc_callback->usb2_com_start_read) {
472 (sc->sc_callback->usb2_com_start_read) (sc);
473 }
474 if (sc->sc_callback->usb2_com_start_write) {
475 (sc->sc_callback->usb2_com_start_write) (sc);
476 }
477}
478
479static void
480usb2_com_start_transfers(struct usb2_com_softc *sc)
481{
482 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
483 return;
484 }
485 /*
486 * Make sure that data transfers are started in both
487 * directions:
488 */
489 if (sc->sc_callback->usb2_com_start_read) {
490 (sc->sc_callback->usb2_com_start_read) (sc);
491 }
492 if (sc->sc_callback->usb2_com_start_write) {
493 (sc->sc_callback->usb2_com_start_write) (sc);
494 }
495}
496
497static void
498usb2_com_cfg_open(struct usb2_proc_msg *_task)
499{
500 struct usb2_com_cfg_task *task =
501 (struct usb2_com_cfg_task *)_task;
502 struct usb2_com_softc *sc = task->sc;
503
504 DPRINTF("\n");
505
506 if (sc->sc_flag & UCOM_FLAG_LL_READY) {
507
508 /* already opened */
509
510 } else {
511
512 sc->sc_flag |= UCOM_FLAG_LL_READY;
513
514 if (sc->sc_callback->usb2_com_cfg_open) {
515 (sc->sc_callback->usb2_com_cfg_open) (sc);
516
517 /* wait a little */
518 usb2_pause_mtx(sc->sc_mtx, hz / 10);
519 }
520 }
521}
522
523static int
524usb2_com_open(struct tty *tp)
525{
526 struct usb2_com_softc *sc = tty_softc(tp);
527 int error;
528
529 mtx_assert(sc->sc_mtx, MA_OWNED);
530
531 if (sc->sc_flag & UCOM_FLAG_GONE) {
532 return (ENXIO);
533 }
534 if (sc->sc_flag & UCOM_FLAG_HL_READY) {
535 /* already opened */
536 return (0);
537 }
538 DPRINTF("tp = %p\n", tp);
539
540 if (sc->sc_callback->usb2_com_pre_open) {
541 /*
542 * give the lower layer a chance to disallow TTY open, for
543 * example if the device is not present:
544 */
545 error = (sc->sc_callback->usb2_com_pre_open) (sc);
546 if (error) {
547 return (error);
548 }
549 }
550 sc->sc_flag |= UCOM_FLAG_HL_READY;
551
552 /* Disable transfers */
553 sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
554
555 sc->sc_lsr = 0;
556 sc->sc_msr = 0;
557 sc->sc_mcr = 0;
558
559 /* reset programmed line state */
560 sc->sc_pls_curr = 0;
561 sc->sc_pls_set = 0;
562 sc->sc_pls_clr = 0;
563
564 usb2_com_queue_command(sc, usb2_com_cfg_open, NULL,
565 &sc->sc_open_task[0].hdr,
566 &sc->sc_open_task[1].hdr);
567
568 /* Queue transfer enable command last */
569 usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL,
570 &sc->sc_start_task[0].hdr,
571 &sc->sc_start_task[1].hdr);
572
573 usb2_com_modem(tp, SER_DTR | SER_RTS, 0);
574
575 usb2_com_break(sc, 0);
576
577 usb2_com_status_change(sc);
578
579 return (0);
580}
581
582static void
583usb2_com_cfg_close(struct usb2_proc_msg *_task)
584{
585 struct usb2_com_cfg_task *task =
586 (struct usb2_com_cfg_task *)_task;
587 struct usb2_com_softc *sc = task->sc;
588
589 DPRINTF("\n");
590
591 if (sc->sc_flag & UCOM_FLAG_LL_READY) {
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 * 3. All advertising materials mentioning features or use of this software
50 * must display the following acknowledgement:
51 * This product includes software developed by the NetBSD
52 * Foundation, Inc. and its contributors.
53 * 4. Neither the name of The NetBSD Foundation nor the names of its
54 * contributors may be used to endorse or promote products derived
55 * from this software without specific prior written permission.
56 *
57 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
58 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
59 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
60 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
61 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
62 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
63 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
64 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
66 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
67 * POSSIBILITY OF SUCH DAMAGE.
68 */
69
70#include <dev/usb/usb.h>
71#include <dev/usb/usb_mfunc.h>
72#include <dev/usb/usb_error.h>
73#include <dev/usb/usb_cdc.h>
74#include <dev/usb/usb_ioctl.h>
75
76#define USB_DEBUG_VAR usb2_com_debug
77
78#include <dev/usb/usb_core.h>
79#include <dev/usb/usb_debug.h>
80#include <dev/usb/usb_process.h>
81#include <dev/usb/usb_request.h>
82#include <dev/usb/usb_busdma.h>
83#include <dev/usb/usb_util.h>
84
85#include <dev/usb/serial/usb_serial.h>
86
87#if USB_DEBUG
88static int usb2_com_debug = 0;
89
90SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
91SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
92 &usb2_com_debug, 0, "ucom debug level");
93#endif
94
95static usb2_proc_callback_t usb2_com_cfg_start_transfers;
96static usb2_proc_callback_t usb2_com_cfg_open;
97static usb2_proc_callback_t usb2_com_cfg_close;
98static usb2_proc_callback_t usb2_com_cfg_line_state;
99static usb2_proc_callback_t usb2_com_cfg_status_change;
100static usb2_proc_callback_t usb2_com_cfg_param;
101
102static uint8_t usb2_com_units_alloc(uint32_t, uint32_t *);
103static void usb2_com_units_free(uint32_t, uint32_t);
104static int usb2_com_attach_tty(struct usb2_com_softc *, uint32_t);
105static void usb2_com_detach_tty(struct usb2_com_softc *);
106static void usb2_com_queue_command(struct usb2_com_softc *,
107 usb2_proc_callback_t *, struct termios *pt,
108 struct usb2_proc_msg *t0, struct usb2_proc_msg *t1);
109static void usb2_com_shutdown(struct usb2_com_softc *);
110static void usb2_com_break(struct usb2_com_softc *, uint8_t);
111static void usb2_com_dtr(struct usb2_com_softc *, uint8_t);
112static void usb2_com_rts(struct usb2_com_softc *, uint8_t);
113
114static tsw_open_t usb2_com_open;
115static tsw_close_t usb2_com_close;
116static tsw_ioctl_t usb2_com_ioctl;
117static tsw_modem_t usb2_com_modem;
118static tsw_param_t usb2_com_param;
119static tsw_outwakeup_t usb2_com_outwakeup;
120static tsw_free_t usb2_com_free;
121
122static struct ttydevsw usb2_com_class = {
123 .tsw_flags = TF_INITLOCK | TF_CALLOUT,
124 .tsw_open = usb2_com_open,
125 .tsw_close = usb2_com_close,
126 .tsw_outwakeup = usb2_com_outwakeup,
127 .tsw_ioctl = usb2_com_ioctl,
128 .tsw_param = usb2_com_param,
129 .tsw_modem = usb2_com_modem,
130 .tsw_free = usb2_com_free,
131};
132
133MODULE_DEPEND(ucom, usb, 1, 1, 1);
134MODULE_VERSION(ucom, 1);
135
136#define UCOM_UNIT_MAX 0x1000 /* exclusive */
137#define UCOM_SUB_UNIT_MAX 0x100 /* exclusive */
138
139static uint8_t usb2_com_bitmap[(UCOM_UNIT_MAX + 7) / 8];
140
141static uint8_t
142usb2_com_units_alloc(uint32_t sub_units, uint32_t *p_root_unit)
143{
144 uint32_t n;
145 uint32_t o;
146 uint32_t x;
147 uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units);
148 uint8_t error = 1;
149
150 mtx_lock(&Giant);
151
152 for (n = 0; n < max; n += sub_units) {
153
154 /* check for free consecutive bits */
155
156 for (o = 0; o < sub_units; o++) {
157
158 x = n + o;
159
160 if (usb2_com_bitmap[x / 8] & (1 << (x % 8))) {
161 goto skip;
162 }
163 }
164
165 /* allocate */
166
167 for (o = 0; o < sub_units; o++) {
168
169 x = n + o;
170
171 usb2_com_bitmap[x / 8] |= (1 << (x % 8));
172 }
173
174 error = 0;
175
176 break;
177
178skip: ;
179 }
180
181 mtx_unlock(&Giant);
182
183 /*
184 * Always set the variable pointed to by "p_root_unit" so that
185 * the compiler does not think that it is used uninitialised:
186 */
187 *p_root_unit = n;
188
189 return (error);
190}
191
192static void
193usb2_com_units_free(uint32_t root_unit, uint32_t sub_units)
194{
195 uint32_t x;
196
197 mtx_lock(&Giant);
198
199 while (sub_units--) {
200 x = root_unit + sub_units;
201 usb2_com_bitmap[x / 8] &= ~(1 << (x % 8));
202 }
203
204 mtx_unlock(&Giant);
205}
206
207/*
208 * "N" sub_units are setup at a time. All sub-units will
209 * be given sequential unit numbers. The number of
210 * sub-units can be used to differentiate among
211 * different types of devices.
212 *
213 * The mutex pointed to by "mtx" is applied before all
214 * callbacks are called back. Also "mtx" must be applied
215 * before calling into the ucom-layer!
216 */
217int
218usb2_com_attach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc,
219 uint32_t sub_units, void *parent,
220 const struct usb2_com_callback *callback, struct mtx *mtx)
221{
222 uint32_t n;
223 uint32_t root_unit;
224 int error = 0;
225
226 if ((sc == NULL) ||
227 (sub_units == 0) ||
228 (sub_units > UCOM_SUB_UNIT_MAX) ||
229 (callback == NULL)) {
230 return (EINVAL);
231 }
232
233 /* XXX unit management does not really belong here */
234 if (usb2_com_units_alloc(sub_units, &root_unit)) {
235 return (ENOMEM);
236 }
237
238 error = usb2_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
239 if (error) {
240 usb2_com_units_free(root_unit, sub_units);
241 return (error);
242 }
243
244 for (n = 0; n != sub_units; n++, sc++) {
245 sc->sc_unit = root_unit + n;
246 sc->sc_local_unit = n;
247 sc->sc_super = ssc;
248 sc->sc_mtx = mtx;
249 sc->sc_parent = parent;
250 sc->sc_callback = callback;
251
252 error = usb2_com_attach_tty(sc, sub_units);
253 if (error) {
254 usb2_com_detach(ssc, sc - n, n);
255 usb2_com_units_free(root_unit + n, sub_units - n);
256 return (error);
257 }
258 sc->sc_flag |= UCOM_FLAG_ATTACHED;
259 }
260 return (0);
261}
262
263/*
264 * NOTE: the following function will do nothing if
265 * the structure pointed to by "ssc" and "sc" is zero.
266 */
267void
268usb2_com_detach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc,
269 uint32_t sub_units)
270{
271 uint32_t n;
272
273 usb2_proc_drain(&ssc->sc_tq);
274
275 for (n = 0; n != sub_units; n++, sc++) {
276 if (sc->sc_flag & UCOM_FLAG_ATTACHED) {
277
278 usb2_com_detach_tty(sc);
279
280 usb2_com_units_free(sc->sc_unit, 1);
281
282 /* avoid duplicate detach: */
283 sc->sc_flag &= ~UCOM_FLAG_ATTACHED;
284 }
285 }
286 usb2_proc_free(&ssc->sc_tq);
287}
288
289static int
290usb2_com_attach_tty(struct usb2_com_softc *sc, uint32_t sub_units)
291{
292 struct tty *tp;
293 int error = 0;
294 char buf[32]; /* temporary TTY device name buffer */
295
296 tp = tty_alloc(&usb2_com_class, sc, sc->sc_mtx);
297 if (tp == NULL) {
298 error = ENOMEM;
299 goto done;
300 }
301 DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit);
302
303 buf[0] = 0; /* set some default value */
304
305 /* Check if the client has a custom TTY name */
306 if (sc->sc_callback->usb2_com_tty_name) {
307 sc->sc_callback->usb2_com_tty_name(sc, buf,
308 sizeof(buf), sc->sc_local_unit);
309 }
310 if (buf[0] == 0) {
311 /* Use default TTY name */
312 if (sub_units > 1) {
313 /* multiple modems in one */
314 if (snprintf(buf, sizeof(buf), "U%u.%u",
315 sc->sc_unit - sc->sc_local_unit,
316 sc->sc_local_unit)) {
317 /* ignore */
318 }
319 } else {
320 /* single modem */
321 if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) {
322 /* ignore */
323 }
324 }
325 }
326 tty_makedev(tp, NULL, "%s", buf);
327
328 sc->sc_tty = tp;
329
330 DPRINTF("ttycreate: %s\n", buf);
331 usb2_cv_init(&sc->sc_cv, "usb2_com");
332
333done:
334 return (error);
335}
336
337static void
338usb2_com_detach_tty(struct usb2_com_softc *sc)
339{
340 struct tty *tp = sc->sc_tty;
341
342 DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
343
344 /* the config thread has been stopped when we get here */
345
346 mtx_lock(sc->sc_mtx);
347 sc->sc_flag |= UCOM_FLAG_GONE;
348 sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
349 UCOM_FLAG_LL_READY);
350 mtx_unlock(sc->sc_mtx);
351 if (tp) {
352 tty_lock(tp);
353
354 usb2_com_close(tp); /* close, if any */
355
356 tty_rel_gone(tp);
357
358 mtx_lock(sc->sc_mtx);
359 /* Wait for the callback after the TTY is torn down */
360 while (sc->sc_ttyfreed == 0)
361 usb2_cv_wait(&sc->sc_cv, sc->sc_mtx);
362 /*
363 * make sure that read and write transfers are stopped
364 */
365 if (sc->sc_callback->usb2_com_stop_read) {
366 (sc->sc_callback->usb2_com_stop_read) (sc);
367 }
368 if (sc->sc_callback->usb2_com_stop_write) {
369 (sc->sc_callback->usb2_com_stop_write) (sc);
370 }
371 mtx_unlock(sc->sc_mtx);
372 }
373 usb2_cv_destroy(&sc->sc_cv);
374}
375
376static void
377usb2_com_queue_command(struct usb2_com_softc *sc,
378 usb2_proc_callback_t *fn, struct termios *pt,
379 struct usb2_proc_msg *t0, struct usb2_proc_msg *t1)
380{
381 struct usb2_com_super_softc *ssc = sc->sc_super;
382 struct usb2_com_param_task *task;
383
384 mtx_assert(sc->sc_mtx, MA_OWNED);
385
386 if (usb2_proc_is_gone(&ssc->sc_tq)) {
387 DPRINTF("proc is gone\n");
388 return; /* nothing to do */
389 }
390 /*
391 * NOTE: The task cannot get executed before we drop the
392 * "sc_mtx" mutex. It is safe to update fields in the message
393 * structure after that the message got queued.
394 */
395 task = (struct usb2_com_param_task *)
396 usb2_proc_msignal(&ssc->sc_tq, t0, t1);
397
398 /* Setup callback and softc pointers */
399 task->hdr.pm_callback = fn;
400 task->sc = sc;
401
402 /*
403 * Make a copy of the termios. This field is only present if
404 * the "pt" field is not NULL.
405 */
406 if (pt != NULL)
407 task->termios_copy = *pt;
408
409 /*
410 * Closing the device should be synchronous.
411 */
412 if (fn == usb2_com_cfg_close)
413 usb2_proc_mwait(&ssc->sc_tq, t0, t1);
414
415 /*
416 * In case of multiple configure requests,
417 * keep track of the last one!
418 */
419 if (fn == usb2_com_cfg_start_transfers)
420 sc->sc_last_start_xfer = &task->hdr;
421}
422
423static void
424usb2_com_shutdown(struct usb2_com_softc *sc)
425{
426 struct tty *tp = sc->sc_tty;
427
428 mtx_assert(sc->sc_mtx, MA_OWNED);
429
430 DPRINTF("\n");
431
432 /*
433 * Hang up if necessary:
434 */
435 if (tp->t_termios.c_cflag & HUPCL) {
436 usb2_com_modem(tp, 0, SER_DTR);
437 }
438}
439
440/*
441 * Return values:
442 * 0: normal
443 * else: taskqueue is draining or gone
444 */
445uint8_t
446usb2_com_cfg_is_gone(struct usb2_com_softc *sc)
447{
448 struct usb2_com_super_softc *ssc = sc->sc_super;
449
450 return (usb2_proc_is_gone(&ssc->sc_tq));
451}
452
453static void
454usb2_com_cfg_start_transfers(struct usb2_proc_msg *_task)
455{
456 struct usb2_com_cfg_task *task =
457 (struct usb2_com_cfg_task *)_task;
458 struct usb2_com_softc *sc = task->sc;
459
460 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
461 return;
462 }
463 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
464 /* TTY device closed */
465 return;
466 }
467
468 if (_task == sc->sc_last_start_xfer)
469 sc->sc_flag |= UCOM_FLAG_GP_DATA;
470
471 if (sc->sc_callback->usb2_com_start_read) {
472 (sc->sc_callback->usb2_com_start_read) (sc);
473 }
474 if (sc->sc_callback->usb2_com_start_write) {
475 (sc->sc_callback->usb2_com_start_write) (sc);
476 }
477}
478
479static void
480usb2_com_start_transfers(struct usb2_com_softc *sc)
481{
482 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
483 return;
484 }
485 /*
486 * Make sure that data transfers are started in both
487 * directions:
488 */
489 if (sc->sc_callback->usb2_com_start_read) {
490 (sc->sc_callback->usb2_com_start_read) (sc);
491 }
492 if (sc->sc_callback->usb2_com_start_write) {
493 (sc->sc_callback->usb2_com_start_write) (sc);
494 }
495}
496
497static void
498usb2_com_cfg_open(struct usb2_proc_msg *_task)
499{
500 struct usb2_com_cfg_task *task =
501 (struct usb2_com_cfg_task *)_task;
502 struct usb2_com_softc *sc = task->sc;
503
504 DPRINTF("\n");
505
506 if (sc->sc_flag & UCOM_FLAG_LL_READY) {
507
508 /* already opened */
509
510 } else {
511
512 sc->sc_flag |= UCOM_FLAG_LL_READY;
513
514 if (sc->sc_callback->usb2_com_cfg_open) {
515 (sc->sc_callback->usb2_com_cfg_open) (sc);
516
517 /* wait a little */
518 usb2_pause_mtx(sc->sc_mtx, hz / 10);
519 }
520 }
521}
522
523static int
524usb2_com_open(struct tty *tp)
525{
526 struct usb2_com_softc *sc = tty_softc(tp);
527 int error;
528
529 mtx_assert(sc->sc_mtx, MA_OWNED);
530
531 if (sc->sc_flag & UCOM_FLAG_GONE) {
532 return (ENXIO);
533 }
534 if (sc->sc_flag & UCOM_FLAG_HL_READY) {
535 /* already opened */
536 return (0);
537 }
538 DPRINTF("tp = %p\n", tp);
539
540 if (sc->sc_callback->usb2_com_pre_open) {
541 /*
542 * give the lower layer a chance to disallow TTY open, for
543 * example if the device is not present:
544 */
545 error = (sc->sc_callback->usb2_com_pre_open) (sc);
546 if (error) {
547 return (error);
548 }
549 }
550 sc->sc_flag |= UCOM_FLAG_HL_READY;
551
552 /* Disable transfers */
553 sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
554
555 sc->sc_lsr = 0;
556 sc->sc_msr = 0;
557 sc->sc_mcr = 0;
558
559 /* reset programmed line state */
560 sc->sc_pls_curr = 0;
561 sc->sc_pls_set = 0;
562 sc->sc_pls_clr = 0;
563
564 usb2_com_queue_command(sc, usb2_com_cfg_open, NULL,
565 &sc->sc_open_task[0].hdr,
566 &sc->sc_open_task[1].hdr);
567
568 /* Queue transfer enable command last */
569 usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL,
570 &sc->sc_start_task[0].hdr,
571 &sc->sc_start_task[1].hdr);
572
573 usb2_com_modem(tp, SER_DTR | SER_RTS, 0);
574
575 usb2_com_break(sc, 0);
576
577 usb2_com_status_change(sc);
578
579 return (0);
580}
581
582static void
583usb2_com_cfg_close(struct usb2_proc_msg *_task)
584{
585 struct usb2_com_cfg_task *task =
586 (struct usb2_com_cfg_task *)_task;
587 struct usb2_com_softc *sc = task->sc;
588
589 DPRINTF("\n");
590
591 if (sc->sc_flag & UCOM_FLAG_LL_READY) {
592
593 sc->sc_flag &= ~(UCOM_FLAG_LL_READY |
594 UCOM_FLAG_GP_DATA);
595
596 if (sc->sc_callback->usb2_com_cfg_close) {
592 sc->sc_flag &= ~UCOM_FLAG_LL_READY;
593 if (sc->sc_callback->usb2_com_cfg_close)
597 (sc->sc_callback->usb2_com_cfg_close) (sc);
594 (sc->sc_callback->usb2_com_cfg_close) (sc);
598 }
599 } else {
600 /* already closed */
601 }
602}
603
604static void
605usb2_com_close(struct tty *tp)
606{
607 struct usb2_com_softc *sc = tty_softc(tp);
608
609 mtx_assert(sc->sc_mtx, MA_OWNED);
610
611 DPRINTF("tp=%p\n", tp);
612
613 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
614 DPRINTF("tp=%p already closed\n", tp);
615 return;
616 }
617 usb2_com_shutdown(sc);
618
619 usb2_com_queue_command(sc, usb2_com_cfg_close, NULL,
620 &sc->sc_close_task[0].hdr,
621 &sc->sc_close_task[1].hdr);
622
595 } else {
596 /* already closed */
597 }
598}
599
600static void
601usb2_com_close(struct tty *tp)
602{
603 struct usb2_com_softc *sc = tty_softc(tp);
604
605 mtx_assert(sc->sc_mtx, MA_OWNED);
606
607 DPRINTF("tp=%p\n", tp);
608
609 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
610 DPRINTF("tp=%p already closed\n", tp);
611 return;
612 }
613 usb2_com_shutdown(sc);
614
615 usb2_com_queue_command(sc, usb2_com_cfg_close, NULL,
616 &sc->sc_close_task[0].hdr,
617 &sc->sc_close_task[1].hdr);
618
623 sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
624 UCOM_FLAG_WR_START |
625 UCOM_FLAG_RTS_IFLOW);
619 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
626
627 if (sc->sc_callback->usb2_com_stop_read) {
628 (sc->sc_callback->usb2_com_stop_read) (sc);
629 }
620
621 if (sc->sc_callback->usb2_com_stop_read) {
622 (sc->sc_callback->usb2_com_stop_read) (sc);
623 }
630 if (sc->sc_callback->usb2_com_stop_write) {
631 (sc->sc_callback->usb2_com_stop_write) (sc);
632 }
633}
634
635static int
636usb2_com_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
637{
638 struct usb2_com_softc *sc = tty_softc(tp);
639 int error;
640
641 mtx_assert(sc->sc_mtx, MA_OWNED);
642
643 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
644 return (EIO);
645 }
646 DPRINTF("cmd = 0x%08lx\n", cmd);
647
648 switch (cmd) {
649 case TIOCSBRK:
650 usb2_com_break(sc, 1);
651 error = 0;
652 break;
653 case TIOCCBRK:
654 usb2_com_break(sc, 0);
655 error = 0;
656 break;
657 default:
658 if (sc->sc_callback->usb2_com_ioctl) {
659 error = (sc->sc_callback->usb2_com_ioctl)
660 (sc, cmd, data, 0, td);
661 } else {
662 error = ENOIOCTL;
663 }
664 break;
665 }
666 return (error);
667}
668
669static int
670usb2_com_modem(struct tty *tp, int sigon, int sigoff)
671{
672 struct usb2_com_softc *sc = tty_softc(tp);
673 uint8_t onoff;
674
675 mtx_assert(sc->sc_mtx, MA_OWNED);
676
677 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
678 return (0);
679 }
680 if ((sigon == 0) && (sigoff == 0)) {
681
682 if (sc->sc_mcr & SER_DTR) {
683 sigon |= SER_DTR;
684 }
685 if (sc->sc_mcr & SER_RTS) {
686 sigon |= SER_RTS;
687 }
688 if (sc->sc_msr & SER_CTS) {
689 sigon |= SER_CTS;
690 }
691 if (sc->sc_msr & SER_DCD) {
692 sigon |= SER_DCD;
693 }
694 if (sc->sc_msr & SER_DSR) {
695 sigon |= SER_DSR;
696 }
697 if (sc->sc_msr & SER_RI) {
698 sigon |= SER_RI;
699 }
700 return (sigon);
701 }
702 if (sigon & SER_DTR) {
703 sc->sc_mcr |= SER_DTR;
704 }
705 if (sigoff & SER_DTR) {
706 sc->sc_mcr &= ~SER_DTR;
707 }
708 if (sigon & SER_RTS) {
709 sc->sc_mcr |= SER_RTS;
710 }
711 if (sigoff & SER_RTS) {
712 sc->sc_mcr &= ~SER_RTS;
713 }
714 onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
715 usb2_com_dtr(sc, onoff);
716
717 onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
718 usb2_com_rts(sc, onoff);
719
720 return (0);
721}
722
723static void
724usb2_com_cfg_line_state(struct usb2_proc_msg *_task)
725{
726 struct usb2_com_cfg_task *task =
727 (struct usb2_com_cfg_task *)_task;
728 struct usb2_com_softc *sc = task->sc;
729 uint8_t notch_bits;
730 uint8_t any_bits;
731 uint8_t prev_value;
732 uint8_t last_value;
733 uint8_t mask;
734
735 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
736 return;
737 }
738
739 mask = 0;
740 /* compute callback mask */
741 if (sc->sc_callback->usb2_com_cfg_set_dtr)
742 mask |= UCOM_LS_DTR;
743 if (sc->sc_callback->usb2_com_cfg_set_rts)
744 mask |= UCOM_LS_RTS;
745 if (sc->sc_callback->usb2_com_cfg_set_break)
746 mask |= UCOM_LS_BREAK;
747
748 /* compute the bits we are to program */
749 notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
750 any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
751 prev_value = sc->sc_pls_curr ^ notch_bits;
752 last_value = sc->sc_pls_curr;
753
754 /* reset programmed line state */
755 sc->sc_pls_curr = 0;
756 sc->sc_pls_set = 0;
757 sc->sc_pls_clr = 0;
758
759 /* ensure that we don't loose any levels */
760 if (notch_bits & UCOM_LS_DTR)
761 sc->sc_callback->usb2_com_cfg_set_dtr(sc,
762 (prev_value & UCOM_LS_DTR) ? 1 : 0);
763 if (notch_bits & UCOM_LS_RTS)
764 sc->sc_callback->usb2_com_cfg_set_rts(sc,
765 (prev_value & UCOM_LS_RTS) ? 1 : 0);
766 if (notch_bits & UCOM_LS_BREAK)
767 sc->sc_callback->usb2_com_cfg_set_break(sc,
768 (prev_value & UCOM_LS_BREAK) ? 1 : 0);
769
770 /* set last value */
771 if (any_bits & UCOM_LS_DTR)
772 sc->sc_callback->usb2_com_cfg_set_dtr(sc,
773 (last_value & UCOM_LS_DTR) ? 1 : 0);
774 if (any_bits & UCOM_LS_RTS)
775 sc->sc_callback->usb2_com_cfg_set_rts(sc,
776 (last_value & UCOM_LS_RTS) ? 1 : 0);
777 if (any_bits & UCOM_LS_BREAK)
778 sc->sc_callback->usb2_com_cfg_set_break(sc,
779 (last_value & UCOM_LS_BREAK) ? 1 : 0);
780}
781
782static void
783usb2_com_line_state(struct usb2_com_softc *sc,
784 uint8_t set_bits, uint8_t clear_bits)
785{
786 mtx_assert(sc->sc_mtx, MA_OWNED);
787
788 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
789 return;
790 }
791
792 DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
793
794 /* update current programmed line state */
795 sc->sc_pls_curr |= set_bits;
796 sc->sc_pls_curr &= ~clear_bits;
797 sc->sc_pls_set |= set_bits;
798 sc->sc_pls_clr |= clear_bits;
799
800 /* defer driver programming */
801 usb2_com_queue_command(sc, usb2_com_cfg_line_state, NULL,
802 &sc->sc_line_state_task[0].hdr,
803 &sc->sc_line_state_task[1].hdr);
804}
805
806static void
807usb2_com_break(struct usb2_com_softc *sc, uint8_t onoff)
808{
809 DPRINTF("onoff = %d\n", onoff);
810
811 if (onoff)
812 usb2_com_line_state(sc, UCOM_LS_BREAK, 0);
813 else
814 usb2_com_line_state(sc, 0, UCOM_LS_BREAK);
815}
816
817static void
818usb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff)
819{
820 DPRINTF("onoff = %d\n", onoff);
821
822 if (onoff)
823 usb2_com_line_state(sc, UCOM_LS_DTR, 0);
824 else
825 usb2_com_line_state(sc, 0, UCOM_LS_DTR);
826}
827
828static void
829usb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff)
830{
831 DPRINTF("onoff = %d\n", onoff);
832
833 if (onoff)
834 usb2_com_line_state(sc, UCOM_LS_RTS, 0);
835 else
836 usb2_com_line_state(sc, 0, UCOM_LS_RTS);
837}
838
839static void
840usb2_com_cfg_status_change(struct usb2_proc_msg *_task)
841{
842 struct usb2_com_cfg_task *task =
843 (struct usb2_com_cfg_task *)_task;
844 struct usb2_com_softc *sc = task->sc;
845 struct tty *tp;
846 uint8_t new_msr;
847 uint8_t new_lsr;
848 uint8_t onoff;
849
850 tp = sc->sc_tty;
851
852 mtx_assert(sc->sc_mtx, MA_OWNED);
853
854 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
855 return;
856 }
857 if (sc->sc_callback->usb2_com_cfg_get_status == NULL) {
858 return;
859 }
860 /* get status */
861
862 new_msr = 0;
863 new_lsr = 0;
864
865 (sc->sc_callback->usb2_com_cfg_get_status) (sc, &new_lsr, &new_msr);
866
867 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
868 /* TTY device closed */
869 return;
870 }
871 onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
872
873 sc->sc_msr = new_msr;
874 sc->sc_lsr = new_lsr;
875
876 if (onoff) {
877
878 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
879
880 DPRINTF("DCD changed to %d\n", onoff);
881
882 ttydisc_modem(tp, onoff);
883 }
884}
885
886void
887usb2_com_status_change(struct usb2_com_softc *sc)
888{
889 mtx_assert(sc->sc_mtx, MA_OWNED);
890
891 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
892 return;
893 }
894 DPRINTF("\n");
895
896 usb2_com_queue_command(sc, usb2_com_cfg_status_change, NULL,
897 &sc->sc_status_task[0].hdr,
898 &sc->sc_status_task[1].hdr);
899}
900
901static void
902usb2_com_cfg_param(struct usb2_proc_msg *_task)
903{
904 struct usb2_com_param_task *task =
905 (struct usb2_com_param_task *)_task;
906 struct usb2_com_softc *sc = task->sc;
907
908 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
909 return;
910 }
911 if (sc->sc_callback->usb2_com_cfg_param == NULL) {
912 return;
913 }
914
915 (sc->sc_callback->usb2_com_cfg_param) (sc, &task->termios_copy);
916
917 /* wait a little */
918 usb2_pause_mtx(sc->sc_mtx, hz / 10);
919}
920
921static int
922usb2_com_param(struct tty *tp, struct termios *t)
923{
924 struct usb2_com_softc *sc = tty_softc(tp);
925 uint8_t opened;
926 int error;
927
928 mtx_assert(sc->sc_mtx, MA_OWNED);
929
930 opened = 0;
931 error = 0;
932
933 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
934
935 /* XXX the TTY layer should call "open()" first! */
936
937 error = usb2_com_open(tp);
938 if (error) {
939 goto done;
940 }
941 opened = 1;
942 }
943 DPRINTF("sc = %p\n", sc);
944
945 /* Check requested parameters. */
946 if (t->c_ospeed < 0) {
947 DPRINTF("negative ospeed\n");
948 error = EINVAL;
949 goto done;
950 }
951 if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
952 DPRINTF("mismatch ispeed and ospeed\n");
953 error = EINVAL;
954 goto done;
955 }
956 t->c_ispeed = t->c_ospeed;
957
958 if (sc->sc_callback->usb2_com_pre_param) {
959 /* Let the lower layer verify the parameters */
960 error = (sc->sc_callback->usb2_com_pre_param) (sc, t);
961 if (error) {
962 DPRINTF("callback error = %d\n", error);
963 goto done;
964 }
965 }
966
967 /* Disable transfers */
968 sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
969
970 /* Queue baud rate programming command first */
971 usb2_com_queue_command(sc, usb2_com_cfg_param, t,
972 &sc->sc_param_task[0].hdr,
973 &sc->sc_param_task[1].hdr);
974
975 /* Queue transfer enable command last */
976 usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL,
977 &sc->sc_start_task[0].hdr,
978 &sc->sc_start_task[1].hdr);
979
980 if (t->c_cflag & CRTS_IFLOW) {
981 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
982 } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
983 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
984 usb2_com_modem(tp, SER_RTS, 0);
985 }
986done:
987 if (error) {
988 if (opened) {
989 usb2_com_close(tp);
990 }
991 }
992 return (error);
993}
994
995static void
996usb2_com_outwakeup(struct tty *tp)
997{
998 struct usb2_com_softc *sc = tty_softc(tp);
999
1000 mtx_assert(sc->sc_mtx, MA_OWNED);
1001
1002 DPRINTF("sc = %p\n", sc);
1003
1004 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
1005 /* The higher layer is not ready */
1006 return;
1007 }
624}
625
626static int
627usb2_com_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
628{
629 struct usb2_com_softc *sc = tty_softc(tp);
630 int error;
631
632 mtx_assert(sc->sc_mtx, MA_OWNED);
633
634 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
635 return (EIO);
636 }
637 DPRINTF("cmd = 0x%08lx\n", cmd);
638
639 switch (cmd) {
640 case TIOCSBRK:
641 usb2_com_break(sc, 1);
642 error = 0;
643 break;
644 case TIOCCBRK:
645 usb2_com_break(sc, 0);
646 error = 0;
647 break;
648 default:
649 if (sc->sc_callback->usb2_com_ioctl) {
650 error = (sc->sc_callback->usb2_com_ioctl)
651 (sc, cmd, data, 0, td);
652 } else {
653 error = ENOIOCTL;
654 }
655 break;
656 }
657 return (error);
658}
659
660static int
661usb2_com_modem(struct tty *tp, int sigon, int sigoff)
662{
663 struct usb2_com_softc *sc = tty_softc(tp);
664 uint8_t onoff;
665
666 mtx_assert(sc->sc_mtx, MA_OWNED);
667
668 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
669 return (0);
670 }
671 if ((sigon == 0) && (sigoff == 0)) {
672
673 if (sc->sc_mcr & SER_DTR) {
674 sigon |= SER_DTR;
675 }
676 if (sc->sc_mcr & SER_RTS) {
677 sigon |= SER_RTS;
678 }
679 if (sc->sc_msr & SER_CTS) {
680 sigon |= SER_CTS;
681 }
682 if (sc->sc_msr & SER_DCD) {
683 sigon |= SER_DCD;
684 }
685 if (sc->sc_msr & SER_DSR) {
686 sigon |= SER_DSR;
687 }
688 if (sc->sc_msr & SER_RI) {
689 sigon |= SER_RI;
690 }
691 return (sigon);
692 }
693 if (sigon & SER_DTR) {
694 sc->sc_mcr |= SER_DTR;
695 }
696 if (sigoff & SER_DTR) {
697 sc->sc_mcr &= ~SER_DTR;
698 }
699 if (sigon & SER_RTS) {
700 sc->sc_mcr |= SER_RTS;
701 }
702 if (sigoff & SER_RTS) {
703 sc->sc_mcr &= ~SER_RTS;
704 }
705 onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
706 usb2_com_dtr(sc, onoff);
707
708 onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
709 usb2_com_rts(sc, onoff);
710
711 return (0);
712}
713
714static void
715usb2_com_cfg_line_state(struct usb2_proc_msg *_task)
716{
717 struct usb2_com_cfg_task *task =
718 (struct usb2_com_cfg_task *)_task;
719 struct usb2_com_softc *sc = task->sc;
720 uint8_t notch_bits;
721 uint8_t any_bits;
722 uint8_t prev_value;
723 uint8_t last_value;
724 uint8_t mask;
725
726 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
727 return;
728 }
729
730 mask = 0;
731 /* compute callback mask */
732 if (sc->sc_callback->usb2_com_cfg_set_dtr)
733 mask |= UCOM_LS_DTR;
734 if (sc->sc_callback->usb2_com_cfg_set_rts)
735 mask |= UCOM_LS_RTS;
736 if (sc->sc_callback->usb2_com_cfg_set_break)
737 mask |= UCOM_LS_BREAK;
738
739 /* compute the bits we are to program */
740 notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
741 any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
742 prev_value = sc->sc_pls_curr ^ notch_bits;
743 last_value = sc->sc_pls_curr;
744
745 /* reset programmed line state */
746 sc->sc_pls_curr = 0;
747 sc->sc_pls_set = 0;
748 sc->sc_pls_clr = 0;
749
750 /* ensure that we don't loose any levels */
751 if (notch_bits & UCOM_LS_DTR)
752 sc->sc_callback->usb2_com_cfg_set_dtr(sc,
753 (prev_value & UCOM_LS_DTR) ? 1 : 0);
754 if (notch_bits & UCOM_LS_RTS)
755 sc->sc_callback->usb2_com_cfg_set_rts(sc,
756 (prev_value & UCOM_LS_RTS) ? 1 : 0);
757 if (notch_bits & UCOM_LS_BREAK)
758 sc->sc_callback->usb2_com_cfg_set_break(sc,
759 (prev_value & UCOM_LS_BREAK) ? 1 : 0);
760
761 /* set last value */
762 if (any_bits & UCOM_LS_DTR)
763 sc->sc_callback->usb2_com_cfg_set_dtr(sc,
764 (last_value & UCOM_LS_DTR) ? 1 : 0);
765 if (any_bits & UCOM_LS_RTS)
766 sc->sc_callback->usb2_com_cfg_set_rts(sc,
767 (last_value & UCOM_LS_RTS) ? 1 : 0);
768 if (any_bits & UCOM_LS_BREAK)
769 sc->sc_callback->usb2_com_cfg_set_break(sc,
770 (last_value & UCOM_LS_BREAK) ? 1 : 0);
771}
772
773static void
774usb2_com_line_state(struct usb2_com_softc *sc,
775 uint8_t set_bits, uint8_t clear_bits)
776{
777 mtx_assert(sc->sc_mtx, MA_OWNED);
778
779 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
780 return;
781 }
782
783 DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
784
785 /* update current programmed line state */
786 sc->sc_pls_curr |= set_bits;
787 sc->sc_pls_curr &= ~clear_bits;
788 sc->sc_pls_set |= set_bits;
789 sc->sc_pls_clr |= clear_bits;
790
791 /* defer driver programming */
792 usb2_com_queue_command(sc, usb2_com_cfg_line_state, NULL,
793 &sc->sc_line_state_task[0].hdr,
794 &sc->sc_line_state_task[1].hdr);
795}
796
797static void
798usb2_com_break(struct usb2_com_softc *sc, uint8_t onoff)
799{
800 DPRINTF("onoff = %d\n", onoff);
801
802 if (onoff)
803 usb2_com_line_state(sc, UCOM_LS_BREAK, 0);
804 else
805 usb2_com_line_state(sc, 0, UCOM_LS_BREAK);
806}
807
808static void
809usb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff)
810{
811 DPRINTF("onoff = %d\n", onoff);
812
813 if (onoff)
814 usb2_com_line_state(sc, UCOM_LS_DTR, 0);
815 else
816 usb2_com_line_state(sc, 0, UCOM_LS_DTR);
817}
818
819static void
820usb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff)
821{
822 DPRINTF("onoff = %d\n", onoff);
823
824 if (onoff)
825 usb2_com_line_state(sc, UCOM_LS_RTS, 0);
826 else
827 usb2_com_line_state(sc, 0, UCOM_LS_RTS);
828}
829
830static void
831usb2_com_cfg_status_change(struct usb2_proc_msg *_task)
832{
833 struct usb2_com_cfg_task *task =
834 (struct usb2_com_cfg_task *)_task;
835 struct usb2_com_softc *sc = task->sc;
836 struct tty *tp;
837 uint8_t new_msr;
838 uint8_t new_lsr;
839 uint8_t onoff;
840
841 tp = sc->sc_tty;
842
843 mtx_assert(sc->sc_mtx, MA_OWNED);
844
845 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
846 return;
847 }
848 if (sc->sc_callback->usb2_com_cfg_get_status == NULL) {
849 return;
850 }
851 /* get status */
852
853 new_msr = 0;
854 new_lsr = 0;
855
856 (sc->sc_callback->usb2_com_cfg_get_status) (sc, &new_lsr, &new_msr);
857
858 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
859 /* TTY device closed */
860 return;
861 }
862 onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
863
864 sc->sc_msr = new_msr;
865 sc->sc_lsr = new_lsr;
866
867 if (onoff) {
868
869 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
870
871 DPRINTF("DCD changed to %d\n", onoff);
872
873 ttydisc_modem(tp, onoff);
874 }
875}
876
877void
878usb2_com_status_change(struct usb2_com_softc *sc)
879{
880 mtx_assert(sc->sc_mtx, MA_OWNED);
881
882 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
883 return;
884 }
885 DPRINTF("\n");
886
887 usb2_com_queue_command(sc, usb2_com_cfg_status_change, NULL,
888 &sc->sc_status_task[0].hdr,
889 &sc->sc_status_task[1].hdr);
890}
891
892static void
893usb2_com_cfg_param(struct usb2_proc_msg *_task)
894{
895 struct usb2_com_param_task *task =
896 (struct usb2_com_param_task *)_task;
897 struct usb2_com_softc *sc = task->sc;
898
899 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
900 return;
901 }
902 if (sc->sc_callback->usb2_com_cfg_param == NULL) {
903 return;
904 }
905
906 (sc->sc_callback->usb2_com_cfg_param) (sc, &task->termios_copy);
907
908 /* wait a little */
909 usb2_pause_mtx(sc->sc_mtx, hz / 10);
910}
911
912static int
913usb2_com_param(struct tty *tp, struct termios *t)
914{
915 struct usb2_com_softc *sc = tty_softc(tp);
916 uint8_t opened;
917 int error;
918
919 mtx_assert(sc->sc_mtx, MA_OWNED);
920
921 opened = 0;
922 error = 0;
923
924 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
925
926 /* XXX the TTY layer should call "open()" first! */
927
928 error = usb2_com_open(tp);
929 if (error) {
930 goto done;
931 }
932 opened = 1;
933 }
934 DPRINTF("sc = %p\n", sc);
935
936 /* Check requested parameters. */
937 if (t->c_ospeed < 0) {
938 DPRINTF("negative ospeed\n");
939 error = EINVAL;
940 goto done;
941 }
942 if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
943 DPRINTF("mismatch ispeed and ospeed\n");
944 error = EINVAL;
945 goto done;
946 }
947 t->c_ispeed = t->c_ospeed;
948
949 if (sc->sc_callback->usb2_com_pre_param) {
950 /* Let the lower layer verify the parameters */
951 error = (sc->sc_callback->usb2_com_pre_param) (sc, t);
952 if (error) {
953 DPRINTF("callback error = %d\n", error);
954 goto done;
955 }
956 }
957
958 /* Disable transfers */
959 sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
960
961 /* Queue baud rate programming command first */
962 usb2_com_queue_command(sc, usb2_com_cfg_param, t,
963 &sc->sc_param_task[0].hdr,
964 &sc->sc_param_task[1].hdr);
965
966 /* Queue transfer enable command last */
967 usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL,
968 &sc->sc_start_task[0].hdr,
969 &sc->sc_start_task[1].hdr);
970
971 if (t->c_cflag & CRTS_IFLOW) {
972 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
973 } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
974 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
975 usb2_com_modem(tp, SER_RTS, 0);
976 }
977done:
978 if (error) {
979 if (opened) {
980 usb2_com_close(tp);
981 }
982 }
983 return (error);
984}
985
986static void
987usb2_com_outwakeup(struct tty *tp)
988{
989 struct usb2_com_softc *sc = tty_softc(tp);
990
991 mtx_assert(sc->sc_mtx, MA_OWNED);
992
993 DPRINTF("sc = %p\n", sc);
994
995 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
996 /* The higher layer is not ready */
997 return;
998 }
1008 sc->sc_flag |= UCOM_FLAG_WR_START;
1009
1010 usb2_com_start_transfers(sc);
1011}
1012
1013/*------------------------------------------------------------------------*
1014 * usb2_com_get_data
1015 *
1016 * Return values:
1017 * 0: No data is available.
1018 * Else: Data is available.
1019 *------------------------------------------------------------------------*/
1020uint8_t
1021usb2_com_get_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
1022 uint32_t offset, uint32_t len, uint32_t *actlen)
1023{
1024 struct usb2_page_search res;
1025 struct tty *tp = sc->sc_tty;
1026 uint32_t cnt;
1027 uint32_t offset_orig;
1028
1029 mtx_assert(sc->sc_mtx, MA_OWNED);
1030
999 usb2_com_start_transfers(sc);
1000}
1001
1002/*------------------------------------------------------------------------*
1003 * usb2_com_get_data
1004 *
1005 * Return values:
1006 * 0: No data is available.
1007 * Else: Data is available.
1008 *------------------------------------------------------------------------*/
1009uint8_t
1010usb2_com_get_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
1011 uint32_t offset, uint32_t len, uint32_t *actlen)
1012{
1013 struct usb2_page_search res;
1014 struct tty *tp = sc->sc_tty;
1015 uint32_t cnt;
1016 uint32_t offset_orig;
1017
1018 mtx_assert(sc->sc_mtx, MA_OWNED);
1019
1031 if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
1032 (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) ||
1033 (!(sc->sc_flag & UCOM_FLAG_WR_START))) {
1020 if (tty_gone(tp) ||
1021 !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
1034 actlen[0] = 0;
1035 return (0); /* multiport device polling */
1036 }
1037 offset_orig = offset;
1038
1039 while (len != 0) {
1040
1041 usb2_get_page(pc, offset, &res);
1042
1043 if (res.length > len) {
1044 res.length = len;
1045 }
1046 /* copy data directly into USB buffer */
1047 cnt = ttydisc_getc(tp, res.buffer, res.length);
1048
1049 offset += cnt;
1050 len -= cnt;
1051
1052 if (cnt < res.length) {
1053 /* end of buffer */
1054 break;
1055 }
1056 }
1057
1058 actlen[0] = offset - offset_orig;
1059
1060 DPRINTF("cnt=%d\n", actlen[0]);
1061
1062 if (actlen[0] == 0) {
1063 return (0);
1064 }
1065 return (1);
1066}
1067
1068void
1069usb2_com_put_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
1070 uint32_t offset, uint32_t len)
1071{
1072 struct usb2_page_search res;
1073 struct tty *tp = sc->sc_tty;
1074 char *buf;
1075 uint32_t cnt;
1076
1077 mtx_assert(sc->sc_mtx, MA_OWNED);
1078
1022 actlen[0] = 0;
1023 return (0); /* multiport device polling */
1024 }
1025 offset_orig = offset;
1026
1027 while (len != 0) {
1028
1029 usb2_get_page(pc, offset, &res);
1030
1031 if (res.length > len) {
1032 res.length = len;
1033 }
1034 /* copy data directly into USB buffer */
1035 cnt = ttydisc_getc(tp, res.buffer, res.length);
1036
1037 offset += cnt;
1038 len -= cnt;
1039
1040 if (cnt < res.length) {
1041 /* end of buffer */
1042 break;
1043 }
1044 }
1045
1046 actlen[0] = offset - offset_orig;
1047
1048 DPRINTF("cnt=%d\n", actlen[0]);
1049
1050 if (actlen[0] == 0) {
1051 return (0);
1052 }
1053 return (1);
1054}
1055
1056void
1057usb2_com_put_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
1058 uint32_t offset, uint32_t len)
1059{
1060 struct usb2_page_search res;
1061 struct tty *tp = sc->sc_tty;
1062 char *buf;
1063 uint32_t cnt;
1064
1065 mtx_assert(sc->sc_mtx, MA_OWNED);
1066
1079 if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
1080 (!(sc->sc_flag & UCOM_FLAG_GP_DATA))) {
1067 if (tty_gone(tp))
1081 return; /* multiport device polling */
1068 return; /* multiport device polling */
1082 }
1069
1083 if (len == 0)
1084 return; /* no data */
1085
1086 /* set a flag to prevent recursation ? */
1087
1088 while (len > 0) {
1089
1090 usb2_get_page(pc, offset, &res);
1091
1092 if (res.length > len) {
1093 res.length = len;
1094 }
1095 len -= res.length;
1096 offset += res.length;
1097
1098 /* pass characters to tty layer */
1099
1100 buf = res.buffer;
1101 cnt = res.length;
1102
1103 /* first check if we can pass the buffer directly */
1104
1105 if (ttydisc_can_bypass(tp)) {
1106 if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1107 DPRINTF("tp=%p, data lost\n", tp);
1108 }
1109 continue;
1110 }
1111 /* need to loop */
1112
1113 for (cnt = 0; cnt != res.length; cnt++) {
1114 if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1115 /* XXX what should we do? */
1116
1117 DPRINTF("tp=%p, lost %d "
1118 "chars\n", tp, res.length - cnt);
1119 break;
1120 }
1121 }
1122 }
1123 ttydisc_rint_done(tp);
1124}
1125
1126static void
1127usb2_com_free(void *xsc)
1128{
1129 struct usb2_com_softc *sc = xsc;
1130
1131 mtx_lock(sc->sc_mtx);
1132 sc->sc_ttyfreed = 1;
1133 usb2_cv_signal(&sc->sc_cv);
1134 mtx_unlock(sc->sc_mtx);
1135}
1070 if (len == 0)
1071 return; /* no data */
1072
1073 /* set a flag to prevent recursation ? */
1074
1075 while (len > 0) {
1076
1077 usb2_get_page(pc, offset, &res);
1078
1079 if (res.length > len) {
1080 res.length = len;
1081 }
1082 len -= res.length;
1083 offset += res.length;
1084
1085 /* pass characters to tty layer */
1086
1087 buf = res.buffer;
1088 cnt = res.length;
1089
1090 /* first check if we can pass the buffer directly */
1091
1092 if (ttydisc_can_bypass(tp)) {
1093 if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
1094 DPRINTF("tp=%p, data lost\n", tp);
1095 }
1096 continue;
1097 }
1098 /* need to loop */
1099
1100 for (cnt = 0; cnt != res.length; cnt++) {
1101 if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
1102 /* XXX what should we do? */
1103
1104 DPRINTF("tp=%p, lost %d "
1105 "chars\n", tp, res.length - cnt);
1106 break;
1107 }
1108 }
1109 }
1110 ttydisc_rint_done(tp);
1111}
1112
1113static void
1114usb2_com_free(void *xsc)
1115{
1116 struct usb2_com_softc *sc = xsc;
1117
1118 mtx_lock(sc->sc_mtx);
1119 sc->sc_ttyfreed = 1;
1120 usb2_cv_signal(&sc->sc_cv);
1121 mtx_unlock(sc->sc_mtx);
1122}