Merge commit '37e84ab74e939caf52150fc3352081786ecc0c29' into merges
[unleashed.git] / usr / src / uts / common / io / usb / clients / usbser / usbser.c
blob0fe258b713c6c2b548dc1b5ba1d6dfc2ae3d0657
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
29 * USB generic serial driver (GSD)
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/stream.h>
35 #include <sys/stropts.h>
36 #include <sys/errno.h>
37 #include <sys/cred.h>
38 #include <sys/conf.h>
39 #include <sys/stat.h>
40 #include <sys/modctl.h>
41 #include <sys/ddi.h>
42 #include <sys/sunddi.h>
43 #include <sys/sunndi.h>
44 #include <sys/termio.h>
45 #include <sys/termiox.h>
46 #include <sys/stropts.h>
47 #include <sys/stream.h>
48 #include <sys/strsubr.h>
49 #include <sys/strsun.h>
50 #include <sys/strtty.h>
51 #include <sys/policy.h>
52 #include <sys/consdev.h>
54 #include <sys/usb/usba.h>
55 #include <sys/usb/clients/usbser/usbser_var.h>
56 #include <sys/usb/clients/usbser/usbser_dsdi.h>
57 #include <sys/usb/clients/usbser/usbser_rseq.h>
58 #include <sys/usb/usba/genconsole.h>
60 /* autoconfiguration subroutines */
61 static int usbser_rseq_do_cb(rseq_t *, int, uintptr_t);
62 static int usbser_free_soft_state(usbser_state_t *);
63 static int usbser_init_soft_state(usbser_state_t *);
64 static int usbser_fini_soft_state(usbser_state_t *);
65 static int usbser_attach_dev(usbser_state_t *);
66 static void usbser_detach_dev(usbser_state_t *);
67 static int usbser_attach_ports(usbser_state_t *);
68 static int usbser_create_port_minor_nodes(usbser_state_t *, int);
69 static void usbser_detach_ports(usbser_state_t *);
70 static int usbser_create_taskq(usbser_state_t *);
71 static void usbser_destroy_taskq(usbser_state_t *);
72 static void usbser_set_dev_state_init(usbser_state_t *);
74 /* hotplugging and power management */
75 static int usbser_disconnect_cb(dev_info_t *);
76 static int usbser_reconnect_cb(dev_info_t *);
77 static void usbser_disconnect_ports(usbser_state_t *);
78 static int usbser_cpr_suspend(dev_info_t *);
79 static int usbser_suspend_ports(usbser_state_t *);
80 static void usbser_cpr_resume(dev_info_t *);
81 static int usbser_restore_device_state(usbser_state_t *);
82 static void usbser_restore_ports_state(usbser_state_t *);
84 /* STREAMS subroutines */
85 static int usbser_open_setup(queue_t *, usbser_port_t *, int, int,
86 cred_t *);
87 static int usbser_open_init(usbser_port_t *, int);
88 static void usbser_check_port_props(usbser_port_t *);
89 static void usbser_open_fini(usbser_port_t *);
90 static int usbser_open_line_setup(usbser_port_t *, int, int);
91 static int usbser_open_carrier_check(usbser_port_t *, int, int);
92 static void usbser_open_queues_init(usbser_port_t *, queue_t *);
93 static void usbser_open_queues_fini(usbser_port_t *);
94 static void usbser_close_drain(usbser_port_t *);
95 static void usbser_close_cancel_break(usbser_port_t *);
96 static void usbser_close_hangup(usbser_port_t *);
97 static void usbser_close_cleanup(usbser_port_t *);
99 /* threads */
100 static void usbser_thr_dispatch(usbser_thread_t *);
101 static void usbser_thr_cancel(usbser_thread_t *);
102 static void usbser_thr_wake(usbser_thread_t *);
103 static void usbser_wq_thread(void *);
104 static void usbser_rq_thread(void *);
106 /* DSD callbacks */
107 static void usbser_tx_cb(caddr_t);
108 static void usbser_rx_cb(caddr_t);
109 static void usbser_rx_massage_data(usbser_port_t *, mblk_t *);
110 static void usbser_rx_massage_mbreak(usbser_port_t *, mblk_t *);
111 static void usbser_rx_cb_put(usbser_port_t *, queue_t *, queue_t *,
112 mblk_t *);
113 static void usbser_status_cb(caddr_t);
114 static void usbser_status_proc_cb(usbser_port_t *);
116 /* serial support */
117 static void usbser_wmsg(usbser_port_t *);
118 static int usbser_data(usbser_port_t *, mblk_t *);
119 static int usbser_ioctl(usbser_port_t *, mblk_t *);
120 static void usbser_iocdata(usbser_port_t *, mblk_t *);
121 static void usbser_stop(usbser_port_t *, mblk_t *);
122 static void usbser_start(usbser_port_t *, mblk_t *);
123 static void usbser_stopi(usbser_port_t *, mblk_t *);
124 static void usbser_starti(usbser_port_t *, mblk_t *);
125 static void usbser_flush(usbser_port_t *, mblk_t *);
126 static void usbser_break(usbser_port_t *, mblk_t *);
127 static void usbser_delay(usbser_port_t *, mblk_t *);
128 static void usbser_restart(void *);
129 static int usbser_port_program(usbser_port_t *);
130 static void usbser_inbound_flow_ctl(usbser_port_t *);
132 /* misc */
133 static int usbser_dev_is_online(usbser_state_t *);
134 static void usbser_serialize_port_act(usbser_port_t *, int);
135 static void usbser_release_port_act(usbser_port_t *, int);
136 #ifdef DEBUG
137 static char *usbser_msgtype2str(int);
138 static char *usbser_ioctl2str(int);
139 #endif
141 /* USBA events */
142 usb_event_t usbser_usb_events = {
143 usbser_disconnect_cb, /* disconnect */
144 usbser_reconnect_cb, /* reconnect */
145 NULL, /* pre-suspend */
146 NULL, /* pre-resume */
149 /* debug support */
150 uint_t usbser_errlevel = USB_LOG_L4;
151 uint_t usbser_errmask = DPRINT_MASK_ALL;
152 uint_t usbser_instance_debug = (uint_t)-1;
154 /* usb serial console */
155 static struct usbser_state *usbser_list;
156 static kmutex_t usbser_lock;
157 static int usbser_console_abort;
158 static usb_console_info_t console_input, console_output;
159 static uchar_t *console_input_buf;
160 static uchar_t *console_input_start, *console_input_end;
162 _NOTE(SCHEME_PROTECTS_DATA("unshared", usbser_console_abort))
163 _NOTE(SCHEME_PROTECTS_DATA("unshared", console_input))
164 _NOTE(SCHEME_PROTECTS_DATA("unshared", console_output))
165 _NOTE(SCHEME_PROTECTS_DATA("unshared", console_input_start))
166 _NOTE(SCHEME_PROTECTS_DATA("unshared", console_input_end))
168 static void usbser_putchar(cons_polledio_arg_t, uchar_t);
169 static int usbser_getchar(cons_polledio_arg_t);
170 static boolean_t usbser_ischar(cons_polledio_arg_t);
171 static void usbser_polledio_enter(cons_polledio_arg_t);
172 static void usbser_polledio_exit(cons_polledio_arg_t);
173 static int usbser_polledio_init(usbser_port_t *);
174 static void usbser_polledio_fini(usbser_port_t *);
176 static struct cons_polledio usbser_polledio = {
177 CONSPOLLEDIO_V1,
178 NULL, /* to be set later */
179 usbser_putchar,
180 usbser_getchar,
181 usbser_ischar,
182 usbser_polledio_enter,
183 usbser_polledio_exit
186 /* various statistics. TODO: replace with kstats */
187 static int usbser_st_tx_data_loss = 0;
188 static int usbser_st_rx_data_loss = 0;
189 static int usbser_st_put_stopi = 0;
190 static int usbser_st_mstop = 0;
191 static int usbser_st_mstart = 0;
192 static int usbser_st_mstopi = 0;
193 static int usbser_st_mstarti = 0;
194 static int usbser_st_rsrv = 0;
195 _NOTE(SCHEME_PROTECTS_DATA("monotonic stats", usbser_st_{
196 tx_data_loss rx_data_loss put_stopi mstop mstart mstopi mstarti rsrv}))
197 _NOTE(SCHEME_PROTECTS_DATA("unshared", usb_bulk_req_t))
198 _NOTE(SCHEME_PROTECTS_DATA("unshared", usb_intr_req_t))
200 /* taskq parameter */
201 extern pri_t minclsyspri;
204 * modload support
206 extern struct mod_ops mod_miscops;
208 static struct modlmisc modlmisc = {
209 &mod_miscops, /* Type of module */
210 "USB generic serial module"
213 static struct modlinkage modlinkage = {
214 MODREV_1, (void *)&modlmisc, NULL
218 #define RSEQ(f1, f2) RSEQE(f1, usbser_rseq_do_cb, f2, NULL)
222 * loadable module entry points
223 * ----------------------------
227 _init(void)
229 int err;
231 mutex_init(&usbser_lock, NULL, MUTEX_DRIVER, NULL);
233 if ((err = mod_install(&modlinkage)) != 0)
234 mutex_destroy(&usbser_lock);
236 return (err);
241 _fini(void)
243 int err;
245 if ((err = mod_remove(&modlinkage)) != 0)
246 return (err);
248 mutex_destroy(&usbser_lock);
250 return (0);
255 _info(struct modinfo *modinfop)
257 return (mod_info(&modlinkage, modinfop));
262 * soft state size
265 usbser_soft_state_size()
267 return (sizeof (usbser_state_t));
272 * autoconfiguration entry points
273 * ------------------------------
276 /*ARGSUSED*/
278 usbser_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
279 void **result, void *statep)
281 int instance;
282 int ret = DDI_FAILURE;
283 usbser_state_t *usbserp;
285 instance = USBSER_MINOR2INST(getminor((dev_t)arg));
287 switch (infocmd) {
288 case DDI_INFO_DEVT2DEVINFO:
289 *result = NULL;
290 usbserp = ddi_get_soft_state(statep, instance);
291 if (usbserp != NULL) {
292 *result = usbserp->us_dip;
293 if (*result != NULL) {
294 ret = DDI_SUCCESS;
298 break;
299 case DDI_INFO_DEVT2INSTANCE:
300 *result = (void *)(uintptr_t)instance;
301 ret = DDI_SUCCESS;
303 break;
304 default:
305 break;
308 return (ret);
312 * device attach
314 static rseq_t rseq_att[] = {
315 RSEQ(NULL, usbser_free_soft_state),
316 RSEQ(usbser_init_soft_state, usbser_fini_soft_state),
317 RSEQ(usbser_attach_dev, usbser_detach_dev),
318 RSEQ(usbser_attach_ports, usbser_detach_ports),
319 RSEQ(usbser_create_taskq, usbser_destroy_taskq),
320 RSEQ(NULL, usbser_set_dev_state_init)
323 static void
324 usbser_insert(struct usbser_state *usp)
326 struct usbser_state *tmp;
328 mutex_enter(&usbser_lock);
329 tmp = usbser_list;
330 if (tmp == NULL)
331 usbser_list = usp;
332 else {
333 while (tmp->us_next)
334 tmp = tmp->us_next;
335 tmp->us_next = usp;
337 mutex_exit(&usbser_lock);
340 static void
341 usbser_remove(struct usbser_state *usp)
343 struct usbser_state *tmp, *prev = NULL;
345 mutex_enter(&usbser_lock);
346 tmp = usbser_list;
347 while (tmp != usp) {
348 prev = tmp;
349 tmp = tmp->us_next;
351 ASSERT(tmp == usp); /* must exist, else attach/detach wrong */
352 if (prev)
353 prev->us_next = usp->us_next;
354 else
355 usbser_list = usp->us_next;
356 usp->us_next = NULL;
357 mutex_exit(&usbser_lock);
361 * Return the first serial device, with dip held. This is called
362 * from the console subsystem to place console on usb serial device.
364 dev_info_t *
365 usbser_first_device(void)
367 dev_info_t *dip = NULL;
369 mutex_enter(&usbser_lock);
370 if (usbser_list) {
371 dip = usbser_list->us_dip;
372 ndi_hold_devi(dip);
374 mutex_exit(&usbser_lock);
376 return (dip);
380 usbser_attach(dev_info_t *dip, ddi_attach_cmd_t cmd,
381 void *statep, ds_ops_t *ds_ops)
383 int instance;
384 usbser_state_t *usp;
386 instance = ddi_get_instance(dip);
388 switch (cmd) {
389 case DDI_ATTACH:
391 break;
392 case DDI_RESUME:
393 usbser_cpr_resume(dip);
395 return (DDI_SUCCESS);
396 default:
398 return (DDI_FAILURE);
401 /* allocate and get soft state */
402 if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS) {
404 return (DDI_FAILURE);
406 if ((usp = ddi_get_soft_state(statep, instance)) == NULL) {
407 ddi_soft_state_free(statep, instance);
409 return (DDI_FAILURE);
412 usp->us_statep = statep;
413 usp->us_dip = dip;
414 usp->us_instance = instance;
415 usp->us_ds_ops = ds_ops;
417 if (rseq_do(rseq_att, NELEM(rseq_att), (uintptr_t)usp, 0) == RSEQ_OK) {
418 ddi_report_dev(dip);
419 usbser_insert(usp);
421 return (DDI_SUCCESS);
422 } else {
424 return (DDI_FAILURE);
429 * device detach
432 usbser_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, void *statep)
434 int instance = ddi_get_instance(dip);
435 usbser_state_t *usp;
436 int rval;
438 usp = ddi_get_soft_state(statep, instance);
440 switch (cmd) {
441 case DDI_DETACH:
442 USB_DPRINTF_L4(DPRINT_DETACH, usp->us_lh, "usbser_detach");
443 usbser_remove(usp);
444 (void) rseq_undo(rseq_att, NELEM(rseq_att), (uintptr_t)usp, 0);
445 USB_DPRINTF_L4(DPRINT_DETACH, NULL,
446 "usbser_detach.%d: end", instance);
448 return (DDI_SUCCESS);
449 case DDI_SUSPEND:
450 rval = usbser_cpr_suspend(dip);
452 return ((rval == USB_SUCCESS)? DDI_SUCCESS : DDI_FAILURE);
453 default:
455 return (DDI_FAILURE);
460 * STREAMS entry points
461 * --------------------
464 * port open
466 /*ARGSUSED*/
468 usbser_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr,
469 void *statep)
471 usbser_state_t *usp;
472 usbser_port_t *pp;
473 int minor = getminor(*dev);
474 int instance;
475 uint_t port_num;
476 int rval;
478 instance = USBSER_MINOR2INST(minor);
479 if (instance < 0) {
481 return (ENXIO);
484 usp = ddi_get_soft_state(statep, instance);
485 if (usp == NULL) {
487 return (ENXIO);
490 /* don't allow to open disconnected device */
491 mutex_enter(&usp->us_mutex);
492 if (usp->us_dev_state == USB_DEV_DISCONNECTED) {
493 mutex_exit(&usp->us_mutex);
495 return (ENXIO);
497 mutex_exit(&usp->us_mutex);
499 /* get port soft state */
500 port_num = USBSER_MINOR2PORT(minor);
501 if (port_num >= usp->us_port_cnt) {
503 return (ENXIO);
505 pp = &usp->us_ports[port_num];
507 /* set up everything for open */
508 rval = usbser_open_setup(rq, pp, minor, flag, cr);
510 USB_DPRINTF_L4(DPRINT_OPEN, pp->port_lh, "usbser_open: rval=%d", rval);
512 return (rval);
517 * port close
519 * some things driver should do when the last app closes the line:
521 * drain data;
522 * cancel break/delay;
523 * hangup line (if necessary);
524 * DSD close;
525 * cleanup soft state;
527 /*ARGSUSED*/
529 usbser_close(queue_t *rq, int flag, cred_t *cr)
531 usbser_port_t *pp = (usbser_port_t *)rq->q_ptr;
532 int online;
534 if (pp == NULL) {
536 return (ENXIO);
539 online = usbser_dev_is_online(pp->port_usp);
542 * in the closing state new activities will not be initiated
544 mutex_enter(&pp->port_mutex);
545 pp->port_state = USBSER_PORT_CLOSING;
547 if (online) {
548 /* drain the data */
549 usbser_close_drain(pp);
552 /* stop break/delay */
553 usbser_close_cancel_break(pp);
555 if (online) {
556 /* hangup line */
557 usbser_close_hangup(pp);
561 * close DSD, cleanup state and transition to 'closed' state
563 usbser_close_cleanup(pp);
564 mutex_exit(&pp->port_mutex);
566 USB_DPRINTF_L4(DPRINT_CLOSE, pp->port_lh, "usbser_close: end");
568 return (0);
573 * read side service routine: send as much as possible messages upstream
574 * and if there is still place on the queue, enable receive (if not already)
577 usbser_rsrv(queue_t *q)
579 usbser_port_t *pp = (usbser_port_t *)q->q_ptr;
580 mblk_t *mp;
582 usbser_st_rsrv++;
583 USB_DPRINTF_L4(DPRINT_RQ, pp->port_lh, "usbser_rsrv");
585 while (canputnext(q) && (mp = getq(q))) {
586 putnext(q, mp);
589 if (canputnext(q)) {
590 mutex_enter(&pp->port_mutex);
591 ASSERT(pp->port_state != USBSER_PORT_CLOSED);
593 if (USBSER_PORT_ACCESS_OK(pp)) {
594 usbser_thr_wake(&pp->port_rq_thread);
596 mutex_exit(&pp->port_mutex);
599 return (0);
604 * wput: put message on the queue and wake wq thread
607 usbser_wput(queue_t *q, mblk_t *mp)
609 usbser_port_t *pp = (usbser_port_t *)q->q_ptr;
611 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wput");
613 mutex_enter(&pp->port_mutex);
614 ASSERT(pp->port_state != USBSER_PORT_CLOSED);
616 /* ignore new messages if port is already closing */
617 if (pp->port_state == USBSER_PORT_CLOSING) {
618 freemsg(mp);
619 } else if (putq(q, mp)) {
621 * this counter represents amount of tx data on the wq.
622 * each time the data is passed to DSD for transmission,
623 * the counter is decremented accordingly
625 pp->port_wq_data_cnt += msgdsize(mp);
626 } else {
627 usbser_st_tx_data_loss++;
629 mutex_exit(&pp->port_mutex);
631 return (0);
636 * we need wsrv() routine to take advantage of STREAMS flow control:
637 * without it the framework will consider we are always able to process msgs
640 usbser_wsrv(queue_t *q)
642 usbser_port_t *pp = (usbser_port_t *)q->q_ptr;
644 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wsrv");
646 mutex_enter(&pp->port_mutex);
647 ASSERT(pp->port_state != USBSER_PORT_CLOSED);
649 if (USBSER_PORT_ACCESS_OK(pp)) {
650 usbser_thr_wake(&pp->port_wq_thread);
652 mutex_exit(&pp->port_mutex);
654 return (0);
659 * power entry point
662 usbser_power(dev_info_t *dip, int comp, int level)
664 void *statep;
665 usbser_state_t *usp;
666 int new_state;
667 int rval;
669 statep = ddi_get_driver_private(dip);
670 usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
672 USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
673 "usbser_power: dip=0x%p, comp=%d, level=%d",
674 (void *)dip, comp, level);
676 mutex_enter(&usp->us_mutex);
677 new_state = usp->us_dev_state;
678 mutex_exit(&usp->us_mutex);
680 /* let DSD do the job */
681 rval = USBSER_DS_USB_POWER(usp, comp, level, &new_state);
683 /* stay in sync with DSD */
684 mutex_enter(&usp->us_mutex);
685 usp->us_dev_state = new_state;
686 mutex_exit(&usp->us_mutex);
688 return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
694 * configuration entry point subroutines
695 * -------------------------------------
697 * rseq callback
699 static int
700 usbser_rseq_do_cb(rseq_t *rseq, int num, uintptr_t arg)
702 usbser_state_t *usp = (usbser_state_t *)arg;
703 int rval = rseq[num].r_do.s_rval;
704 char *name = rseq[num].r_do.s_name;
706 if (rval != DDI_SUCCESS) {
707 USB_DPRINTF_L2(DPRINT_ATTACH, usp->us_lh,
708 "do %s failed (%d)", name, rval);
710 return (RSEQ_UNDO);
711 } else {
713 return (RSEQ_OK);
719 * free soft state
721 static int
722 usbser_free_soft_state(usbser_state_t *usp)
724 ddi_soft_state_free(usp->us_statep, usp->us_instance);
726 return (USB_SUCCESS);
730 * init instance soft state
732 static int
733 usbser_init_soft_state(usbser_state_t *usp)
735 usp->us_lh = usb_alloc_log_hdl(usp->us_dip, "usbs[*].",
736 &usbser_errlevel, &usbser_errmask, &usbser_instance_debug,
738 mutex_init(&usp->us_mutex, NULL, MUTEX_DRIVER, NULL);
740 /* save state pointer for use in event callbacks */
741 ddi_set_driver_private(usp->us_dip, usp->us_statep);
743 usp->us_dev_state = USBSER_DEV_INIT;
745 return (DDI_SUCCESS);
749 * fini instance soft state
751 static int
752 usbser_fini_soft_state(usbser_state_t *usp)
754 usb_free_log_hdl(usp->us_lh);
755 mutex_destroy(&usp->us_mutex);
756 ddi_set_driver_private(usp->us_dip, NULL);
758 return (DDI_SUCCESS);
762 * attach entire device
764 static int
765 usbser_attach_dev(usbser_state_t *usp)
767 ds_attach_info_t ai;
768 int rval;
770 usp->us_dev_state = USB_DEV_ONLINE;
772 ai.ai_dip = usp->us_dip;
773 ai.ai_usb_events = &usbser_usb_events;
774 ai.ai_hdl = &usp->us_ds_hdl;
775 ai.ai_port_cnt = &usp->us_port_cnt;
777 rval = USBSER_DS_ATTACH(usp, &ai);
779 if ((rval != USB_SUCCESS) || (usp->us_ds_hdl == NULL) ||
780 (usp->us_port_cnt == 0)) {
781 USB_DPRINTF_L4(DPRINT_ATTACH, usp->us_lh, "usbser_attach_dev: "
782 "failed %d %p %d", rval, usp->us_ds_hdl, usp->us_port_cnt);
784 return (DDI_FAILURE);
787 USB_DPRINTF_L4(DPRINT_ATTACH, usp->us_lh,
788 "usbser_attach_dev: port_cnt = %d", usp->us_port_cnt);
790 return (DDI_SUCCESS);
795 * detach entire device
797 static void
798 usbser_detach_dev(usbser_state_t *usp)
800 USBSER_DS_DETACH(usp);
805 * attach each individual port
807 static int
808 usbser_attach_ports(usbser_state_t *usp)
810 int i;
811 usbser_port_t *pp;
812 ds_cb_t ds_cb;
815 * allocate port array
817 usp->us_ports = kmem_zalloc(usp->us_port_cnt *
818 sizeof (usbser_port_t), KM_SLEEP);
820 /* callback handlers */
821 ds_cb.cb_tx = usbser_tx_cb;
822 ds_cb.cb_rx = usbser_rx_cb;
823 ds_cb.cb_status = usbser_status_cb;
826 * initialize each port
828 for (i = 0; i < usp->us_port_cnt; i++) {
829 pp = &usp->us_ports[i];
832 * initialize data
834 pp->port_num = i;
835 pp->port_usp = usp;
836 pp->port_ds_ops = usp->us_ds_ops;
837 pp->port_ds_hdl = usp->us_ds_hdl;
839 /* allocate log handle */
840 (void) sprintf(pp->port_lh_name, "usbs[%d].", i);
841 pp->port_lh = usb_alloc_log_hdl(usp->us_dip,
842 pp->port_lh_name, &usbser_errlevel, &usbser_errmask,
843 &usbser_instance_debug, 0);
845 mutex_init(&pp->port_mutex, NULL, MUTEX_DRIVER, NULL);
846 cv_init(&pp->port_state_cv, NULL, CV_DEFAULT, NULL);
847 cv_init(&pp->port_act_cv, NULL, CV_DEFAULT, NULL);
848 cv_init(&pp->port_car_cv, NULL, CV_DEFAULT, NULL);
851 * init threads
853 pp->port_wq_thread.thr_port = pp;
854 pp->port_wq_thread.thr_func = usbser_wq_thread;
855 pp->port_wq_thread.thr_arg = (void *)&pp->port_wq_thread;
856 cv_init(&pp->port_wq_thread.thr_cv, NULL, CV_DEFAULT, NULL);
858 pp->port_rq_thread.thr_port = pp;
859 pp->port_rq_thread.thr_func = usbser_rq_thread;
860 pp->port_rq_thread.thr_arg = (void *)&pp->port_rq_thread;
861 cv_init(&pp->port_rq_thread.thr_cv, NULL, CV_DEFAULT, NULL);
864 * register callbacks
866 ds_cb.cb_arg = (caddr_t)pp;
867 USBSER_DS_REGISTER_CB(usp, i, &ds_cb);
869 pp->port_state = USBSER_PORT_CLOSED;
871 if (usbser_create_port_minor_nodes(usp, i) != USB_SUCCESS) {
872 usbser_detach_ports(usp);
874 return (DDI_FAILURE);
878 return (DDI_SUCCESS);
883 * create a pair of minor nodes for the port
885 static int
886 usbser_create_port_minor_nodes(usbser_state_t *usp, int port_num)
888 int instance = usp->us_instance;
889 minor_t minor;
890 char name[16];
893 * tty node
895 (void) sprintf(name, "%d", port_num);
896 minor = USBSER_MAKEMINOR(instance, port_num, 0);
898 if (ddi_create_minor_node(usp->us_dip, name,
899 S_IFCHR, minor, DDI_NT_SERIAL, 0) != DDI_SUCCESS) {
901 return (USB_FAILURE);
905 * dial-out node
907 (void) sprintf(name, "%d,cu", port_num);
908 minor = USBSER_MAKEMINOR(instance, port_num, OUTLINE);
910 if (ddi_create_minor_node(usp->us_dip, name,
911 S_IFCHR, minor, DDI_NT_SERIAL_DO, 0) != DDI_SUCCESS) {
913 return (USB_FAILURE);
916 return (USB_SUCCESS);
921 * detach each port individually
923 static void
924 usbser_detach_ports(usbser_state_t *usp)
926 int i;
927 int sz;
928 usbser_port_t *pp;
931 * remove all minor nodes
933 ddi_remove_minor_node(usp->us_dip, NULL);
935 for (i = 0; i < usp->us_port_cnt; i++) {
936 pp = &usp->us_ports[i];
938 if (pp->port_state != USBSER_PORT_CLOSED) {
939 ASSERT(pp->port_state == USBSER_PORT_NOT_INIT);
941 continue;
944 USBSER_DS_UNREGISTER_CB(usp, i);
946 mutex_destroy(&pp->port_mutex);
947 cv_destroy(&pp->port_state_cv);
948 cv_destroy(&pp->port_act_cv);
949 cv_destroy(&pp->port_car_cv);
951 cv_destroy(&pp->port_wq_thread.thr_cv);
952 cv_destroy(&pp->port_rq_thread.thr_cv);
954 usb_free_log_hdl(pp->port_lh);
958 * free memory
960 sz = usp->us_port_cnt * sizeof (usbser_port_t);
961 kmem_free(usp->us_ports, sz);
962 usp->us_ports = NULL;
967 * create a taskq with two threads per port (read and write sides)
969 static int
970 usbser_create_taskq(usbser_state_t *usp)
972 int nthr = usp->us_port_cnt * 2;
974 usp->us_taskq = ddi_taskq_create(usp->us_dip, "usbser_taskq",
975 nthr, TASKQ_DEFAULTPRI, 0);
977 return ((usp->us_taskq == NULL) ? DDI_FAILURE : DDI_SUCCESS);
981 static void
982 usbser_destroy_taskq(usbser_state_t *usp)
984 ddi_taskq_destroy(usp->us_taskq);
988 static void
989 usbser_set_dev_state_init(usbser_state_t *usp)
991 mutex_enter(&usp->us_mutex);
992 usp->us_dev_state = USBSER_DEV_INIT;
993 mutex_exit(&usp->us_mutex);
997 * hotplugging and power management
998 * ---------------------------------
1000 * disconnect event callback
1002 /*ARGSUSED*/
1003 static int
1004 usbser_disconnect_cb(dev_info_t *dip)
1006 void *statep;
1007 usbser_state_t *usp;
1009 statep = ddi_get_driver_private(dip);
1010 usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
1012 USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
1013 "usbser_disconnect_cb: dip=%p", (void *)dip);
1015 mutex_enter(&usp->us_mutex);
1016 switch (usp->us_dev_state) {
1017 case USB_DEV_ONLINE:
1018 case USB_DEV_PWRED_DOWN:
1019 /* prevent further activity */
1020 usp->us_dev_state = USB_DEV_DISCONNECTED;
1021 mutex_exit(&usp->us_mutex);
1023 /* see if any of the ports are open and do necessary handling */
1024 usbser_disconnect_ports(usp);
1026 /* call DSD to do any necessary work */
1027 if (USBSER_DS_DISCONNECT(usp) != USB_DEV_DISCONNECTED) {
1028 USB_DPRINTF_L2(DPRINT_EVENTS, usp->us_lh,
1029 "usbser_disconnect_cb: ds_disconnect failed");
1032 break;
1033 case USB_DEV_SUSPENDED:
1034 /* we remain suspended */
1035 default:
1036 mutex_exit(&usp->us_mutex);
1038 break;
1041 return (USB_SUCCESS);
1046 * reconnect event callback
1048 /*ARGSUSED*/
1049 static int
1050 usbser_reconnect_cb(dev_info_t *dip)
1052 void *statep;
1053 usbser_state_t *usp;
1055 statep = ddi_get_driver_private(dip);
1056 usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
1058 USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
1059 "usbser_reconnect_cb: dip=%p", (void *)dip);
1061 (void) usbser_restore_device_state(usp);
1063 return (USB_SUCCESS);
1068 * if any of the ports is open during disconnect,
1069 * send M_HANGUP message upstream and log a warning
1071 static void
1072 usbser_disconnect_ports(usbser_state_t *usp)
1074 usbser_port_t *pp;
1075 queue_t *rq;
1076 int complain = 0;
1077 int hangup = 0;
1078 timeout_id_t delay_id = 0;
1079 int i;
1081 if (usp->us_ports == NULL) {
1082 return;
1085 for (i = 0; i < usp->us_port_cnt; i++) {
1086 pp = &usp->us_ports[i];
1088 mutex_enter(&pp->port_mutex);
1089 if (pp->port_state == USBSER_PORT_OPEN ||
1090 USBSER_IS_OPENING(pp) ||
1091 pp->port_state == USBSER_PORT_CLOSING) {
1092 complain = 1;
1095 if (pp->port_state == USBSER_PORT_OPEN) {
1096 rq = pp->port_ttycommon.t_readq;
1099 * hangup the stream; will send actual
1100 * M_HANGUP message after releasing mutex
1102 pp->port_flags |= USBSER_FL_HUNGUP;
1103 hangup = 1;
1106 * cancel all activities
1108 usbser_release_port_act(pp, USBSER_ACT_ALL);
1110 delay_id = pp->port_delay_id;
1111 pp->port_delay_id = 0;
1113 /* mark disconnected */
1114 pp->port_state = USBSER_PORT_DISCONNECTED;
1115 cv_broadcast(&pp->port_state_cv);
1117 mutex_exit(&pp->port_mutex);
1119 if (hangup) {
1120 (void) putnextctl(rq, M_HANGUP);
1121 hangup = 0;
1125 * we couldn't untimeout while holding the mutex - do it now
1127 if (delay_id) {
1128 (void) untimeout(delay_id);
1129 delay_id = 0;
1134 * complain about disconnecting device while open
1136 if (complain) {
1137 USB_DPRINTF_L0(DPRINT_EVENTS, usp->us_lh, "device was "
1138 "disconnected while open. Data may have been lost");
1144 * do CPR suspend
1146 * We use a trivial CPR strategy - fail if any of the device's ports are open.
1147 * The problem with more sophisticated strategies is that each open port uses
1148 * two threads that sit in the loop until the port is closed, while CPR has to
1149 * stop all kernel threads to succeed. Stopping port threads is a rather
1150 * intrusive and delicate procedure; I leave it as an RFE for now.
1153 static int
1154 usbser_cpr_suspend(dev_info_t *dip)
1156 void *statep;
1157 usbser_state_t *usp;
1158 int new_state;
1159 int rval;
1161 statep = ddi_get_driver_private(dip);
1162 usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
1164 USB_DPRINTF_L4(DPRINT_EVENTS, usp->us_lh, "usbser_cpr_suspend");
1166 /* suspend each port first */
1167 if (usbser_suspend_ports(usp) != USB_SUCCESS) {
1168 USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
1169 "usbser_cpr_suspend: GSD failure");
1171 return (USB_FAILURE);
1174 new_state = USBSER_DS_SUSPEND(usp); /* let DSD do its part */
1176 mutex_enter(&usp->us_mutex);
1177 if (new_state == USB_DEV_SUSPENDED) {
1178 rval = USB_SUCCESS;
1179 } else {
1180 ASSERT(new_state == USB_DEV_ONLINE);
1181 rval = USB_FAILURE;
1183 usp->us_dev_state = new_state;
1184 mutex_exit(&usp->us_mutex);
1186 return (rval);
1190 static int
1191 usbser_suspend_ports(usbser_state_t *usp)
1193 usbser_port_t *pp;
1194 int i;
1196 for (i = 0; i < usp->us_port_cnt; i++) {
1197 pp = &usp->us_ports[i];
1199 mutex_enter(&pp->port_mutex);
1200 if (pp->port_state != USBSER_PORT_CLOSED) {
1201 mutex_exit(&pp->port_mutex);
1203 return (USB_FAILURE);
1205 mutex_exit(&pp->port_mutex);
1208 return (USB_SUCCESS);
1213 * do CPR resume
1215 * DSD will return USB_DEV_ONLINE in case of success
1217 static void
1218 usbser_cpr_resume(dev_info_t *dip)
1220 void *statep;
1221 usbser_state_t *usp;
1223 statep = ddi_get_driver_private(dip);
1224 usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
1226 USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh, "usbser_cpr_resume");
1228 (void) usbser_restore_device_state(usp);
1233 * restore device state after CPR resume or reconnect
1235 static int
1236 usbser_restore_device_state(usbser_state_t *usp)
1238 int new_state, current_state;
1240 /* needed as power up state of dev is "unknown" to system */
1241 (void) pm_busy_component(usp->us_dip, 0);
1242 (void) pm_raise_power(usp->us_dip, 0, USB_DEV_OS_FULL_PWR);
1244 mutex_enter(&usp->us_mutex);
1245 current_state = usp->us_dev_state;
1246 mutex_exit(&usp->us_mutex);
1248 ASSERT((current_state == USB_DEV_DISCONNECTED) ||
1249 (current_state == USB_DEV_SUSPENDED));
1252 * call DSD to perform device-specific work
1254 if (current_state == USB_DEV_DISCONNECTED) {
1255 new_state = USBSER_DS_RECONNECT(usp);
1256 } else {
1257 new_state = USBSER_DS_RESUME(usp);
1260 mutex_enter(&usp->us_mutex);
1261 usp->us_dev_state = new_state;
1262 mutex_exit(&usp->us_mutex);
1264 if (new_state == USB_DEV_ONLINE) {
1266 * restore ports state
1268 usbser_restore_ports_state(usp);
1271 (void) pm_idle_component(usp->us_dip, 0);
1273 return (USB_SUCCESS);
1278 * restore ports state after device reconnect/resume
1280 static void
1281 usbser_restore_ports_state(usbser_state_t *usp)
1283 usbser_port_t *pp;
1284 queue_t *rq;
1285 int i;
1287 for (i = 0; i < usp->us_port_cnt; i++) {
1288 pp = &usp->us_ports[i];
1290 mutex_enter(&pp->port_mutex);
1292 * only care about ports that are open
1294 if ((pp->port_state != USBSER_PORT_SUSPENDED) &&
1295 (pp->port_state != USBSER_PORT_DISCONNECTED)) {
1296 mutex_exit(&pp->port_mutex);
1298 continue;
1301 pp->port_state = USBSER_PORT_OPEN;
1304 * if the stream was hung up during disconnect, restore it
1306 if (pp->port_flags & USBSER_FL_HUNGUP) {
1307 pp->port_flags &= ~USBSER_FL_HUNGUP;
1308 rq = pp->port_ttycommon.t_readq;
1310 mutex_exit(&pp->port_mutex);
1311 (void) putnextctl(rq, M_UNHANGUP);
1312 mutex_enter(&pp->port_mutex);
1316 * restore serial parameters
1318 (void) usbser_port_program(pp);
1321 * wake anything that might be sleeping
1323 cv_broadcast(&pp->port_state_cv);
1324 cv_broadcast(&pp->port_act_cv);
1325 usbser_thr_wake(&pp->port_wq_thread);
1326 usbser_thr_wake(&pp->port_rq_thread);
1327 mutex_exit(&pp->port_mutex);
1333 * STREAMS subroutines
1334 * -------------------
1337 * port open state machine
1339 * here's a list of things that the driver has to do while open;
1340 * because device can be opened any number of times,
1341 * initial open has additional responsibilities:
1343 * if (initial_open) {
1344 * initialize soft state; \
1345 * DSD open; - see usbser_open_init()
1346 * dispatch threads; /
1348 * raise DTR;
1349 * wait for carrier (if necessary);
1351 * we should also take into consideration that two threads can try to open
1352 * the same physical port simultaneously (/dev/term/N and /dev/cua/N).
1354 * return values:
1355 * 0 - success;
1356 * >0 - fail with this error code;
1358 static int
1359 usbser_open_setup(queue_t *rq, usbser_port_t *pp, int minor, int flag,
1360 cred_t *cr)
1362 int rval = USBSER_CONTINUE;
1364 mutex_enter(&pp->port_mutex);
1366 * refer to port state diagram in the header file
1368 loop:
1369 switch (pp->port_state) {
1370 case USBSER_PORT_CLOSED:
1372 * initial open
1374 rval = usbser_open_init(pp, minor);
1376 break;
1377 case USBSER_PORT_OPENING_TTY:
1379 * dial-out thread can overtake the port
1380 * if tty open thread is sleeping waiting for carrier
1382 if ((minor & OUTLINE) && (pp->port_flags & USBSER_FL_WOPEN)) {
1383 pp->port_state = USBSER_PORT_OPENING_OUT;
1385 USB_DPRINTF_L3(DPRINT_OPEN, pp->port_lh,
1386 "usbser_open_state: overtake");
1389 /* FALLTHRU */
1390 case USBSER_PORT_OPENING_OUT:
1392 * if no other open in progress, setup the line
1394 if (USBSER_NO_OTHER_OPEN(pp, minor)) {
1395 rval = usbser_open_line_setup(pp, minor, flag);
1397 break;
1400 /* FALLTHRU */
1401 case USBSER_PORT_CLOSING:
1403 * wait until close active phase ends
1405 if (cv_wait_sig(&pp->port_state_cv, &pp->port_mutex) == 0) {
1406 rval = EINTR;
1409 break;
1410 case USBSER_PORT_OPEN:
1411 if ((pp->port_ttycommon.t_flags & TS_XCLUDE) &&
1412 secpolicy_excl_open(cr) != 0) {
1414 * exclusive use
1416 rval = EBUSY;
1417 } else if (USBSER_OPEN_IN_OTHER_MODE(pp, minor)) {
1419 * tty and dial-out modes are mutually exclusive
1421 rval = EBUSY;
1422 } else {
1424 * port is being re-open in the same mode
1426 rval = usbser_open_line_setup(pp, minor, flag);
1429 break;
1430 default:
1431 rval = ENXIO;
1433 break;
1436 if (rval == USBSER_CONTINUE) {
1438 goto loop;
1442 * initial open requires additional handling
1444 if (USBSER_IS_OPENING(pp)) {
1445 if (rval == USBSER_COMPLETE) {
1446 if (pp->port_state == USBSER_PORT_OPENING_OUT) {
1447 pp->port_flags |= USBSER_FL_OUT;
1449 pp->port_state = USBSER_PORT_OPEN;
1450 cv_broadcast(&pp->port_state_cv);
1452 usbser_open_queues_init(pp, rq);
1453 } else {
1454 usbser_open_fini(pp);
1457 mutex_exit(&pp->port_mutex);
1459 return (rval);
1464 * initialize the port when opened for the first time
1466 static int
1467 usbser_open_init(usbser_port_t *pp, int minor)
1469 usbser_state_t *usp = pp->port_usp;
1470 tty_common_t *tp = &pp->port_ttycommon;
1471 int rval = ENXIO;
1473 ASSERT(pp->port_state == USBSER_PORT_CLOSED);
1476 * init state
1478 pp->port_act = 0;
1479 pp->port_flags &= USBSER_FL_PRESERVE;
1480 pp->port_flowc = '\0';
1481 pp->port_wq_data_cnt = 0;
1483 if (minor & OUTLINE) {
1484 pp->port_state = USBSER_PORT_OPENING_OUT;
1485 } else {
1486 pp->port_state = USBSER_PORT_OPENING_TTY;
1490 * init termios settings
1492 tp->t_iflag = 0;
1493 tp->t_iocpending = NULL;
1494 tp->t_size.ws_row = tp->t_size.ws_col = 0;
1495 tp->t_size.ws_xpixel = tp->t_size.ws_ypixel = 0;
1496 tp->t_startc = CSTART;
1497 tp->t_stopc = CSTOP;
1499 usbser_check_port_props(pp);
1502 * dispatch wq and rq threads:
1503 * although queues are not enabled at this point,
1504 * we will need wq to run status processing callback
1506 usbser_thr_dispatch(&pp->port_wq_thread);
1507 usbser_thr_dispatch(&pp->port_rq_thread);
1510 * open DSD port
1512 mutex_exit(&pp->port_mutex);
1513 rval = USBSER_DS_OPEN_PORT(usp, pp->port_num);
1514 mutex_enter(&pp->port_mutex);
1516 if (rval != USB_SUCCESS) {
1518 return (ENXIO);
1520 pp->port_flags |= USBSER_FL_DSD_OPEN;
1523 * program port with default parameters
1525 if ((rval = usbser_port_program(pp)) != 0) {
1527 return (ENXIO);
1530 return (USBSER_CONTINUE);
1535 * create a pair of minor nodes for the port
1537 static void
1538 usbser_check_port_props(usbser_port_t *pp)
1540 dev_info_t *dip = pp->port_usp->us_dip;
1541 tty_common_t *tp = &pp->port_ttycommon;
1542 struct termios *termiosp;
1543 uint_t len;
1544 char name[20];
1547 * take default modes from "ttymodes" property if it exists
1549 if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ddi_root_node(), 0,
1550 "ttymodes", (uchar_t **)&termiosp, &len) == DDI_PROP_SUCCESS) {
1552 if (len == sizeof (struct termios)) {
1553 tp->t_cflag = termiosp->c_cflag;
1555 if (termiosp->c_iflag & (IXON | IXANY)) {
1556 tp->t_iflag =
1557 termiosp->c_iflag & (IXON | IXANY);
1558 tp->t_startc = termiosp->c_cc[VSTART];
1559 tp->t_stopc = termiosp->c_cc[VSTOP];
1562 ddi_prop_free(termiosp);
1566 * look for "ignore-cd" or "port-N-ignore-cd" property
1568 (void) sprintf(name, "port-%d-ignore-cd", pp->port_num);
1569 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1570 "ignore-cd", 0) ||
1571 ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, name, 0)) {
1572 pp->port_flags |= USBSER_FL_IGNORE_CD;
1573 } else {
1574 pp->port_flags &= ~USBSER_FL_IGNORE_CD;
1580 * undo what was done in usbser_open_init()
1582 static void
1583 usbser_open_fini(usbser_port_t *pp)
1585 uint_t port_num = pp->port_num;
1586 usbser_state_t *usp = pp->port_usp;
1589 * close DSD if it is open
1591 if (pp->port_flags & USBSER_FL_DSD_OPEN) {
1592 mutex_exit(&pp->port_mutex);
1593 if (USBSER_DS_CLOSE_PORT(usp, port_num) != USB_SUCCESS) {
1594 USB_DPRINTF_L2(DPRINT_CLOSE, pp->port_lh,
1595 "usbser_open_fini: CLOSE_PORT fail");
1597 mutex_enter(&pp->port_mutex);
1601 * cancel threads
1603 usbser_thr_cancel(&pp->port_wq_thread);
1604 usbser_thr_cancel(&pp->port_rq_thread);
1607 * unpdate soft state
1609 pp->port_state = USBSER_PORT_CLOSED;
1610 cv_broadcast(&pp->port_state_cv);
1611 cv_broadcast(&pp->port_car_cv);
1616 * setup serial line
1618 static int
1619 usbser_open_line_setup(usbser_port_t *pp, int minor, int flag)
1621 int rval;
1623 mutex_exit(&pp->port_mutex);
1625 * prevent opening a disconnected device
1627 if (!usbser_dev_is_online(pp->port_usp)) {
1628 mutex_enter(&pp->port_mutex);
1630 return (ENXIO);
1633 /* raise DTR on every open */
1634 (void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR, TIOCM_DTR);
1636 mutex_enter(&pp->port_mutex);
1638 * check carrier
1640 rval = usbser_open_carrier_check(pp, minor, flag);
1642 return (rval);
1647 * check carrier and wait if needed
1649 static int
1650 usbser_open_carrier_check(usbser_port_t *pp, int minor, int flag)
1652 tty_common_t *tp = &pp->port_ttycommon;
1653 int val = 0;
1654 int rval;
1656 if (pp->port_flags & USBSER_FL_IGNORE_CD) {
1657 tp->t_flags |= TS_SOFTCAR;
1661 * check carrier
1663 if (tp->t_flags & TS_SOFTCAR) {
1664 pp->port_flags |= USBSER_FL_CARR_ON;
1665 } else if (USBSER_DS_GET_MODEM_CTL(pp, TIOCM_CD, &val) != USB_SUCCESS) {
1667 return (ENXIO);
1668 } else if (val & TIOCM_CD) {
1669 pp->port_flags |= USBSER_FL_CARR_ON;
1670 } else {
1671 pp->port_flags &= ~USBSER_FL_CARR_ON;
1675 * don't block if 1) not allowed to, 2) this is a local device,
1676 * 3) opening in dial-out mode, or 4) carrier is already on
1678 if ((flag & (FNDELAY | FNONBLOCK)) || (tp->t_cflag & CLOCAL) ||
1679 (minor & OUTLINE) || (pp->port_flags & USBSER_FL_CARR_ON)) {
1681 return (USBSER_COMPLETE);
1685 * block until carrier up (only in tty mode)
1687 USB_DPRINTF_L4(DPRINT_OPEN, pp->port_lh,
1688 "usbser_open_carrier_check: waiting for carrier...");
1690 pp->port_flags |= USBSER_FL_WOPEN;
1692 rval = cv_wait_sig(&pp->port_car_cv, &pp->port_mutex);
1694 pp->port_flags &= ~USBSER_FL_WOPEN;
1696 if (rval == 0) {
1698 * interrupted with a signal
1700 return (EINTR);
1701 } else {
1703 * try again
1705 return (USBSER_CONTINUE);
1711 * during open, setup queues and message processing
1713 static void
1714 usbser_open_queues_init(usbser_port_t *pp, queue_t *rq)
1716 pp->port_ttycommon.t_readq = rq;
1717 pp->port_ttycommon.t_writeq = WR(rq);
1718 rq->q_ptr = WR(rq)->q_ptr = (caddr_t)pp;
1720 qprocson(rq);
1725 * clean up queues and message processing
1727 static void
1728 usbser_open_queues_fini(usbser_port_t *pp)
1730 queue_t *rq = pp->port_ttycommon.t_readq;
1732 mutex_exit(&pp->port_mutex);
1734 * clean up queues
1736 qprocsoff(rq);
1739 * free unused messages
1741 flushq(rq, FLUSHALL);
1742 flushq(WR(rq), FLUSHALL);
1744 rq->q_ptr = WR(rq)->q_ptr = NULL;
1745 ttycommon_close(&pp->port_ttycommon);
1746 mutex_enter(&pp->port_mutex);
1751 * during close, wait until pending data is gone or the signal is sent
1753 static void
1754 usbser_close_drain(usbser_port_t *pp)
1756 int need_drain;
1757 clock_t until;
1758 int rval = USB_SUCCESS;
1761 * port_wq_data_cnt indicates amount of data on the write queue,
1762 * which becomes zero when all data is submitted to DSD. But usbser
1763 * stays busy until it gets tx callback from DSD, signalling that
1764 * data has been sent over USB. To be continued in the next comment...
1766 until = ddi_get_lbolt() +
1767 drv_usectohz(USBSER_WQ_DRAIN_TIMEOUT * 1000000);
1769 while ((pp->port_wq_data_cnt > 0) && USBSER_PORT_IS_BUSY(pp)) {
1770 if ((rval = cv_timedwait_sig(&pp->port_act_cv, &pp->port_mutex,
1771 until)) <= 0) {
1773 break;
1777 /* don't drain if timed out or received a signal */
1778 need_drain = (pp->port_wq_data_cnt == 0) || !USBSER_PORT_IS_BUSY(pp) ||
1779 (rval != USB_SUCCESS);
1781 mutex_exit(&pp->port_mutex);
1783 * Once the data reaches USB serial box, it may still be stored in its
1784 * internal output buffer (FIFO). We call DSD drain to ensure that all
1785 * the data is transmitted transmitted over the serial line.
1787 if (need_drain) {
1788 rval = USBSER_DS_FIFO_DRAIN(pp, USBSER_TX_FIFO_DRAIN_TIMEOUT);
1789 if (rval != USB_SUCCESS) {
1790 (void) USBSER_DS_FIFO_FLUSH(pp, DS_TX);
1792 } else {
1793 (void) USBSER_DS_FIFO_FLUSH(pp, DS_TX);
1795 mutex_enter(&pp->port_mutex);
1800 * during close, cancel break/delay
1802 static void
1803 usbser_close_cancel_break(usbser_port_t *pp)
1805 timeout_id_t delay_id;
1807 if (pp->port_act & USBSER_ACT_BREAK) {
1808 delay_id = pp->port_delay_id;
1809 pp->port_delay_id = 0;
1811 mutex_exit(&pp->port_mutex);
1812 (void) untimeout(delay_id);
1813 (void) USBSER_DS_BREAK_CTL(pp, DS_OFF);
1814 mutex_enter(&pp->port_mutex);
1816 pp->port_act &= ~USBSER_ACT_BREAK;
1822 * during close, drop RTS/DTR if necessary
1824 static void
1825 usbser_close_hangup(usbser_port_t *pp)
1828 * drop DTR and RTS if HUPCL is set
1830 if (pp->port_ttycommon.t_cflag & HUPCL) {
1831 mutex_exit(&pp->port_mutex);
1832 (void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_RTS | TIOCM_DTR, 0);
1833 mutex_enter(&pp->port_mutex);
1839 * state cleanup during close
1841 static void
1842 usbser_close_cleanup(usbser_port_t *pp)
1844 usbser_open_queues_fini(pp);
1846 usbser_open_fini(pp);
1852 * thread management
1853 * -----------------
1856 * dispatch a thread
1858 static void
1859 usbser_thr_dispatch(usbser_thread_t *thr)
1861 usbser_port_t *pp = thr->thr_port;
1862 usbser_state_t *usp = pp->port_usp;
1863 int rval;
1865 ASSERT(mutex_owned(&pp->port_mutex));
1866 ASSERT((thr->thr_flags & USBSER_THR_RUNNING) == 0);
1868 thr->thr_flags = USBSER_THR_RUNNING;
1870 rval = ddi_taskq_dispatch(usp->us_taskq, thr->thr_func, thr->thr_arg,
1871 DDI_SLEEP);
1872 ASSERT(rval == DDI_SUCCESS);
1877 * cancel a thread
1879 static void
1880 usbser_thr_cancel(usbser_thread_t *thr)
1882 usbser_port_t *pp = thr->thr_port;
1884 ASSERT(mutex_owned(&pp->port_mutex));
1886 thr->thr_flags &= ~USBSER_THR_RUNNING;
1887 cv_signal(&thr->thr_cv);
1889 /* wait until the thread actually exits */
1890 do {
1891 cv_wait(&thr->thr_cv, &pp->port_mutex);
1893 } while ((thr->thr_flags & USBSER_THR_EXITED) == 0);
1898 * wake thread
1900 static void
1901 usbser_thr_wake(usbser_thread_t *thr)
1903 ASSERT(mutex_owned(&thr->thr_port->port_mutex));
1905 thr->thr_flags |= USBSER_THR_WAKE;
1906 cv_signal(&thr->thr_cv);
1911 * thread handling write queue requests
1913 static void
1914 usbser_wq_thread(void *arg)
1916 usbser_thread_t *thr = (usbser_thread_t *)arg;
1917 usbser_port_t *pp = thr->thr_port;
1919 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wq_thread: enter");
1921 mutex_enter(&pp->port_mutex);
1922 while (thr->thr_flags & USBSER_THR_RUNNING) {
1924 * when woken, see what we should do
1926 if (thr->thr_flags & USBSER_THR_WAKE) {
1927 thr->thr_flags &= ~USBSER_THR_WAKE;
1930 * status callback pending?
1932 if (pp->port_flags & USBSER_FL_STATUS_CB) {
1933 usbser_status_proc_cb(pp);
1936 usbser_wmsg(pp);
1937 } else {
1939 * sleep until woken up to do some work, e.g:
1940 * - new message arrives;
1941 * - data transmit completes;
1942 * - status callback pending;
1943 * - wq thread is cancelled;
1945 cv_wait(&thr->thr_cv, &pp->port_mutex);
1946 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh,
1947 "usbser_wq_thread: wakeup");
1950 thr->thr_flags |= USBSER_THR_EXITED;
1951 cv_signal(&thr->thr_cv);
1952 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wq_thread: exit");
1953 mutex_exit(&pp->port_mutex);
1958 * thread handling read queue requests
1960 static void
1961 usbser_rq_thread(void *arg)
1963 usbser_thread_t *thr = (usbser_thread_t *)arg;
1964 usbser_port_t *pp = thr->thr_port;
1966 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_rq_thread: enter");
1968 mutex_enter(&pp->port_mutex);
1969 while (thr->thr_flags & USBSER_THR_RUNNING) {
1971 * read service routine will wake us when
1972 * more space is available on the read queue
1974 if (thr->thr_flags & USBSER_THR_WAKE) {
1975 thr->thr_flags &= ~USBSER_THR_WAKE;
1978 * don't process messages until queue is enabled
1980 if (!pp->port_ttycommon.t_readq) {
1982 continue;
1986 * check whether we need to resume receive
1988 if (pp->port_flags & USBSER_FL_RX_STOPPED) {
1989 pp->port_flowc = pp->port_ttycommon.t_startc;
1990 usbser_inbound_flow_ctl(pp);
1994 * grab more data if available
1996 mutex_exit(&pp->port_mutex);
1997 usbser_rx_cb((caddr_t)pp);
1998 mutex_enter(&pp->port_mutex);
1999 } else {
2000 cv_wait(&thr->thr_cv, &pp->port_mutex);
2001 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh,
2002 "usbser_rq_thread: wakeup");
2005 thr->thr_flags |= USBSER_THR_EXITED;
2006 cv_signal(&thr->thr_cv);
2007 USB_DPRINTF_L4(DPRINT_RQ, pp->port_lh, "usbser_rq_thread: exit");
2008 mutex_exit(&pp->port_mutex);
2013 * DSD callbacks
2014 * -------------
2016 * Note: to avoid deadlocks with DSD, these callbacks
2017 * should not call DSD functions that can block.
2020 * transmit callback
2022 * invoked by DSD when the last byte of data is transmitted over USB
2024 static void
2025 usbser_tx_cb(caddr_t arg)
2027 usbser_port_t *pp = (usbser_port_t *)arg;
2028 int online;
2030 online = usbser_dev_is_online(pp->port_usp);
2032 mutex_enter(&pp->port_mutex);
2033 USB_DPRINTF_L4(DPRINT_TX_CB, pp->port_lh,
2034 "usbser_tx_cb: act=%x curthread=%p", pp->port_act,
2035 (void *)curthread);
2037 usbser_release_port_act(pp, USBSER_ACT_TX);
2040 * as long as port access is ok and the port is not busy on
2041 * TX, break, ctrl or delay, the wq_thread should be waken
2042 * to do further process for next message
2044 if (online && USBSER_PORT_ACCESS_OK(pp) &&
2045 !USBSER_PORT_IS_BUSY_NON_RX(pp)) {
2047 * wake wq thread for further data/ioctl processing
2049 usbser_thr_wake(&pp->port_wq_thread);
2051 mutex_exit(&pp->port_mutex);
2056 * receive callback
2058 * invoked by DSD when there is more data for us to pick
2060 static void
2061 usbser_rx_cb(caddr_t arg)
2063 usbser_port_t *pp = (usbser_port_t *)arg;
2064 queue_t *rq, *wq;
2065 mblk_t *mp; /* current mblk */
2066 mblk_t *data, *data_tail; /* M_DATA mblk list and its tail */
2067 mblk_t *emp; /* error (M_BREAK) mblk */
2069 USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh, "usbser_rx_cb");
2071 if (!usbser_dev_is_online(pp->port_usp)) {
2073 return;
2076 /* get data from DSD */
2077 if ((mp = USBSER_DS_RX(pp)) == NULL) {
2079 return;
2082 mutex_enter(&pp->port_mutex);
2083 if ((!USBSER_PORT_ACCESS_OK(pp)) ||
2084 ((pp->port_ttycommon.t_cflag & CREAD) == 0)) {
2085 freemsg(mp);
2086 mutex_exit(&pp->port_mutex);
2087 USB_DPRINTF_L3(DPRINT_RX_CB, pp->port_lh,
2088 "usbser_rx_cb: access not ok or receiver disabled");
2090 return;
2093 usbser_serialize_port_act(pp, USBSER_ACT_RX);
2095 rq = pp->port_ttycommon.t_readq;
2096 wq = pp->port_ttycommon.t_writeq;
2097 mutex_exit(&pp->port_mutex);
2100 * DSD data is a b_cont-linked list of M_DATA and M_BREAK blocks.
2101 * M_DATA is correctly received data.
2102 * M_BREAK is a character with either framing or parity error.
2104 * this loop runs through the list of mblks. when it meets an M_BREAK,
2105 * it sends all leading M_DATA's in one shot, then sends M_BREAK.
2106 * in the trivial case when list contains only M_DATA's, the loop
2107 * does nothing but set data variable.
2109 data = data_tail = NULL;
2110 while (mp) {
2112 * skip data until we meet M_BREAK or end of list
2114 if (DB_TYPE(mp) == M_DATA) {
2115 if (data == NULL) {
2116 data = mp;
2118 data_tail = mp;
2119 mp = mp->b_cont;
2121 continue;
2124 /* detach data list from mp */
2125 if (data_tail) {
2126 data_tail->b_cont = NULL;
2128 /* detach emp from the list */
2129 emp = mp;
2130 mp = mp->b_cont;
2131 emp->b_cont = NULL;
2133 /* DSD shouldn't send anything but M_DATA or M_BREAK */
2134 if ((DB_TYPE(emp) != M_BREAK) || (MBLKL(emp) != 2)) {
2135 freemsg(emp);
2136 USB_DPRINTF_L2(DPRINT_RX_CB, pp->port_lh,
2137 "usbser_rx_cb: bad message");
2139 continue;
2143 * first tweak and send M_DATA's
2145 if (data) {
2146 usbser_rx_massage_data(pp, data);
2147 usbser_rx_cb_put(pp, rq, wq, data);
2148 data = data_tail = NULL;
2152 * now tweak and send M_BREAK
2154 mutex_enter(&pp->port_mutex);
2155 usbser_rx_massage_mbreak(pp, emp);
2156 mutex_exit(&pp->port_mutex);
2157 usbser_rx_cb_put(pp, rq, wq, emp);
2160 /* send the rest of the data, if any */
2161 if (data) {
2162 usbser_rx_massage_data(pp, data);
2163 usbser_rx_cb_put(pp, rq, wq, data);
2166 mutex_enter(&pp->port_mutex);
2167 usbser_release_port_act(pp, USBSER_ACT_RX);
2168 mutex_exit(&pp->port_mutex);
2172 * the joys of termio -- this is to accomodate Unix98 assertion:
2174 * If PARENB is supported and is set, when PARMRK is set, and CSIZE is
2175 * set to CS8, and IGNPAR is clear, and ISTRIP is clear, a valid
2176 * character of '\377' is read as '\377', '\377'.
2178 * Posix Ref: Assertion 7.1.2.2-16(C)
2180 * this requires the driver to scan every incoming valid character
2182 static void
2183 usbser_rx_massage_data(usbser_port_t *pp, mblk_t *mp)
2185 tty_common_t *tp = &pp->port_ttycommon;
2186 uchar_t *p;
2187 mblk_t *newmp;
2188 int tailsz;
2190 /* avoid scanning if possible */
2191 mutex_enter(&pp->port_mutex);
2192 if (!((tp->t_cflag & PARENB) && (tp->t_iflag & PARMRK) &&
2193 ((tp->t_cflag & CSIZE) == CS8) &&
2194 ((tp->t_iflag & (IGNPAR|ISTRIP)) == 0))) {
2195 mutex_exit(&pp->port_mutex);
2197 return;
2199 mutex_exit(&pp->port_mutex);
2201 while (mp) {
2202 for (p = mp->b_rptr; p < mp->b_wptr; ) {
2203 if (*p++ != 0377) {
2205 continue;
2207 USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh,
2208 "usbser_rx_massage_data: mp=%p off=%ld(%ld)",
2209 (void *)mp, _PTRDIFF(p, mp->b_rptr) - 1,
2210 (long)MBLKL(mp));
2213 * insert another 0377 after this one. all data after
2214 * the original 0377 have to be copied to the new mblk
2216 tailsz = _PTRDIFF(mp->b_wptr, p);
2217 if ((newmp = allocb(tailsz + 1, BPRI_HI)) == NULL) {
2218 USB_DPRINTF_L2(DPRINT_RX_CB, pp->port_lh,
2219 "usbser_rx_massage_data: allocb failed");
2221 continue;
2224 /* fill in the new mblk */
2225 *newmp->b_wptr++ = 0377;
2226 if (tailsz > 0) {
2227 bcopy(p, newmp->b_wptr, tailsz);
2228 newmp->b_wptr += tailsz;
2230 /* shrink the original mblk */
2231 mp->b_wptr = p;
2233 newmp->b_cont = mp->b_cont;
2234 mp->b_cont = newmp;
2235 p = newmp->b_rptr + 1;
2236 mp = newmp;
2238 mp = mp->b_cont;
2243 * more joys of termio
2245 static void
2246 usbser_rx_massage_mbreak(usbser_port_t *pp, mblk_t *mp)
2248 tty_common_t *tp = &pp->port_ttycommon;
2249 uchar_t err, c;
2251 err = *mp->b_rptr;
2252 c = *(mp->b_rptr + 1);
2254 if ((err & (DS_FRAMING_ERR | DS_BREAK_ERR)) && (c == 0)) {
2255 /* break */
2256 mp->b_rptr += 2;
2257 } else if (!(tp->t_iflag & INPCK) && (err & (DS_PARITY_ERR))) {
2258 /* Posix Ref: Assertion 7.1.2.2-20(C) */
2259 mp->b_rptr++;
2260 DB_TYPE(mp) = M_DATA;
2261 } else {
2262 /* for ldterm to handle */
2263 mp->b_rptr++;
2266 USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh,
2267 "usbser_rx_massage_mbreak: type=%x len=%ld [0]=0%o",
2268 DB_TYPE(mp), (long)MBLKL(mp), (MBLKL(mp) > 0) ? *mp->b_rptr : 45);
2273 * in rx callback, try to send an mblk upstream
2275 static void
2276 usbser_rx_cb_put(usbser_port_t *pp, queue_t *rq, queue_t *wq, mblk_t *mp)
2278 if (canputnext(rq)) {
2279 putnext(rq, mp);
2280 } else if (canput(rq) && putq(rq, mp)) {
2282 * full queue indicates the need for inbound flow control
2284 (void) putctl(wq, M_STOPI);
2285 usbser_st_put_stopi++;
2287 USB_DPRINTF_L3(DPRINT_RX_CB, pp->port_lh,
2288 "usbser_rx_cb: cannot putnext, flow ctl");
2289 } else {
2290 freemsg(mp);
2291 usbser_st_rx_data_loss++;
2292 (void) putctl(wq, M_STOPI);
2293 usbser_st_put_stopi++;
2295 USB_DPRINTF_L1(DPRINT_RX_CB, pp->port_lh,
2296 "input overrun");
2302 * modem status change callback
2304 * each time external status lines are changed, DSD calls this routine
2306 static void
2307 usbser_status_cb(caddr_t arg)
2309 usbser_port_t *pp = (usbser_port_t *)arg;
2311 USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh, "usbser_status_cb");
2313 if (!usbser_dev_is_online(pp->port_usp)) {
2315 return;
2319 * actual processing will be done in usbser_status_proc_cb()
2320 * running in wq thread
2322 mutex_enter(&pp->port_mutex);
2323 if (USBSER_PORT_ACCESS_OK(pp) || USBSER_IS_OPENING(pp)) {
2324 pp->port_flags |= USBSER_FL_STATUS_CB;
2325 usbser_thr_wake(&pp->port_wq_thread);
2327 mutex_exit(&pp->port_mutex);
2332 * modem status change
2334 static void
2335 usbser_status_proc_cb(usbser_port_t *pp)
2337 tty_common_t *tp = &pp->port_ttycommon;
2338 queue_t *rq, *wq;
2339 int status;
2340 int drop_dtr = 0;
2341 int rq_msg = 0, wq_msg = 0;
2343 USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh, "usbser_status_proc_cb");
2345 pp->port_flags &= ~USBSER_FL_STATUS_CB;
2347 mutex_exit(&pp->port_mutex);
2348 if (!usbser_dev_is_online(pp->port_usp)) {
2349 mutex_enter(&pp->port_mutex);
2351 return;
2354 /* get modem status */
2355 if (USBSER_DS_GET_MODEM_CTL(pp, -1, &status) != USB_SUCCESS) {
2356 mutex_enter(&pp->port_mutex);
2358 return;
2361 mutex_enter(&pp->port_mutex);
2362 usbser_serialize_port_act(pp, USBSER_ACT_CTL);
2364 rq = pp->port_ttycommon.t_readq;
2365 wq = pp->port_ttycommon.t_writeq;
2368 * outbound flow control
2370 if (tp->t_cflag & CRTSCTS) {
2371 if (!(status & TIOCM_CTS)) {
2373 * CTS dropped, stop xmit
2375 if (!(pp->port_flags & USBSER_FL_TX_STOPPED)) {
2376 wq_msg = M_STOP;
2378 } else if (pp->port_flags & USBSER_FL_TX_STOPPED) {
2380 * CTS raised, resume xmit
2382 wq_msg = M_START;
2387 * check carrier
2389 if ((status & TIOCM_CD) || (tp->t_flags & TS_SOFTCAR)) {
2391 * carrier present
2393 if ((pp->port_flags & USBSER_FL_CARR_ON) == 0) {
2394 pp->port_flags |= USBSER_FL_CARR_ON;
2396 rq_msg = M_UNHANGUP;
2398 * wake open
2400 if (pp->port_flags & USBSER_FL_WOPEN) {
2401 cv_broadcast(&pp->port_car_cv);
2404 USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh,
2405 "usbser_status_cb: carr on");
2407 } else if (pp->port_flags & USBSER_FL_CARR_ON) {
2408 pp->port_flags &= ~USBSER_FL_CARR_ON;
2410 * carrier went away: if not local line, drop DTR
2412 if (!(tp->t_cflag & CLOCAL)) {
2413 drop_dtr = 1;
2414 rq_msg = M_HANGUP;
2416 if ((pp->port_flags & USBSER_FL_TX_STOPPED) && (wq_msg == 0)) {
2417 wq_msg = M_START;
2420 USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh,
2421 "usbser_status_cb: carr off");
2423 mutex_exit(&pp->port_mutex);
2425 USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh,
2426 "usbser_status_cb: rq_msg=%d wq_msg=%d", rq_msg, wq_msg);
2429 * commit postponed actions now
2430 * do so only if port is fully open (queues are enabled)
2432 if (rq) {
2433 if (rq_msg) {
2434 (void) putnextctl(rq, rq_msg);
2436 if (drop_dtr) {
2437 (void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR, 0);
2439 if (wq_msg) {
2440 (void) putctl(wq, wq_msg);
2444 mutex_enter(&pp->port_mutex);
2445 usbser_release_port_act(pp, USBSER_ACT_CTL);
2450 * serial support
2451 * --------------
2454 * this routine is run by wq thread every time it's woken,
2455 * i.e. when the queue contains messages to process
2457 static void
2458 usbser_wmsg(usbser_port_t *pp)
2460 queue_t *q = pp->port_ttycommon.t_writeq;
2461 mblk_t *mp;
2462 int msgtype;
2464 ASSERT(mutex_owned(&pp->port_mutex));
2466 if (q == NULL) {
2467 USB_DPRINTF_L3(DPRINT_WQ, pp->port_lh, "usbser_wmsg: q=NULL");
2469 return;
2471 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wmsg: q=%p act=%x 0x%x",
2472 (void *)q, pp->port_act, q->q_first ? DB_TYPE(q->q_first) : 0xff);
2474 while ((mp = getq(q)) != NULL) {
2475 msgtype = DB_TYPE(mp);
2476 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wmsg: "
2477 "type=%s (0x%x)", usbser_msgtype2str(msgtype), msgtype);
2479 switch (msgtype) {
2481 * high-priority messages
2483 case M_STOP:
2484 usbser_stop(pp, mp);
2486 break;
2487 case M_START:
2488 usbser_start(pp, mp);
2490 break;
2491 case M_STOPI:
2492 usbser_stopi(pp, mp);
2494 break;
2495 case M_STARTI:
2496 usbser_starti(pp, mp);
2498 break;
2499 case M_IOCDATA:
2500 usbser_iocdata(pp, mp);
2502 break;
2503 case M_FLUSH:
2504 usbser_flush(pp, mp);
2506 break;
2508 * normal-priority messages
2510 case M_BREAK:
2511 usbser_break(pp, mp);
2513 break;
2514 case M_DELAY:
2515 usbser_delay(pp, mp);
2517 break;
2518 case M_DATA:
2519 if (usbser_data(pp, mp) != USB_SUCCESS) {
2520 (void) putbq(q, mp);
2522 return;
2525 break;
2526 case M_IOCTL:
2527 if (usbser_ioctl(pp, mp) != USB_SUCCESS) {
2528 (void) putbq(q, mp);
2530 return;
2533 break;
2534 default:
2535 freemsg(mp);
2537 break;
2544 * process M_DATA message
2546 static int
2547 usbser_data(usbser_port_t *pp, mblk_t *mp)
2549 /* put off until current transfer ends or delay is over */
2550 if ((pp->port_act & USBSER_ACT_TX) ||
2551 (pp->port_act & USBSER_ACT_DELAY)) {
2553 return (USB_FAILURE);
2555 if (MBLKL(mp) <= 0) {
2556 freemsg(mp);
2558 return (USB_SUCCESS);
2561 pp->port_act |= USBSER_ACT_TX;
2562 pp->port_wq_data_cnt -= msgdsize(mp);
2564 mutex_exit(&pp->port_mutex);
2565 /* DSD is required to accept data block in any case */
2566 (void) USBSER_DS_TX(pp, mp);
2567 mutex_enter(&pp->port_mutex);
2569 return (USB_SUCCESS);
2574 * process an M_IOCTL message
2576 static int
2577 usbser_ioctl(usbser_port_t *pp, mblk_t *mp)
2579 tty_common_t *tp = &pp->port_ttycommon;
2580 queue_t *q = tp->t_writeq;
2581 struct iocblk *iocp;
2582 int cmd;
2583 mblk_t *datamp;
2584 int error = 0, rval = USB_SUCCESS;
2585 int val;
2587 ASSERT(mutex_owned(&pp->port_mutex));
2588 ASSERT(DB_TYPE(mp) == M_IOCTL);
2590 iocp = (struct iocblk *)mp->b_rptr;
2591 cmd = iocp->ioc_cmd;
2593 USB_DPRINTF_L4(DPRINT_IOCTL, pp->port_lh, "usbser_ioctl: "
2594 "mp=%p %s (0x%x)", (void *)mp, usbser_ioctl2str(cmd), cmd);
2596 if (tp->t_iocpending != NULL) {
2598 * We were holding an ioctl response pending the
2599 * availability of an mblk to hold data to be passed up;
2600 * another ioctl came through, which means that ioctl
2601 * must have timed out or been aborted.
2603 freemsg(tp->t_iocpending);
2604 tp->t_iocpending = NULL;
2607 switch (cmd) {
2608 case TIOCMGET:
2609 case TIOCMBIC:
2610 case TIOCMBIS:
2611 case TIOCMSET:
2612 case CONSOPENPOLLEDIO:
2613 case CONSCLOSEPOLLEDIO:
2614 case CONSSETABORTENABLE:
2615 case CONSGETABORTENABLE:
2617 * For the above ioctls do not call ttycommon_ioctl() because
2618 * this function frees up the message block (mp->b_cont) that
2619 * contains the address of the user variable where we need to
2620 * pass back the bit array.
2622 error = -1;
2623 usbser_serialize_port_act(pp, USBSER_ACT_CTL);
2624 mutex_exit(&pp->port_mutex);
2625 break;
2627 case TCSBRK:
2628 /* serialize breaks */
2629 if (pp->port_act & USBSER_ACT_BREAK)
2630 return (USB_FAILURE);
2631 /*FALLTHRU*/
2632 default:
2633 usbser_serialize_port_act(pp, USBSER_ACT_CTL);
2634 mutex_exit(&pp->port_mutex);
2635 (void) ttycommon_ioctl(tp, q, mp, &error);
2636 break;
2639 if (error == 0) {
2641 * ttycommon_ioctl() did most of the work
2642 * we just use the data it set up
2644 switch (cmd) {
2645 case TCSETSF:
2646 case TCSETSW:
2647 case TCSETA:
2648 case TCSETAW:
2649 case TCSETAF:
2650 (void) USBSER_DS_FIFO_DRAIN(pp, DS_TX);
2651 /*FALLTHRU*/
2653 case TCSETS:
2654 mutex_enter(&pp->port_mutex);
2655 error = usbser_port_program(pp);
2656 mutex_exit(&pp->port_mutex);
2657 break;
2659 goto end;
2661 } else if (error > 0) {
2662 USB_DPRINTF_L3(DPRINT_IOCTL, pp->port_lh, "usbser_ioctl: "
2663 "ttycommon_ioctl returned %d", error);
2664 goto end;
2668 * error < 0: ttycommon_ioctl() didn't do anything, we process it here
2670 error = 0;
2671 switch (cmd) {
2672 case TCSBRK:
2673 if ((error = miocpullup(mp, sizeof (int))) != 0)
2674 break;
2676 /* drain output */
2677 (void) USBSER_DS_FIFO_DRAIN(pp, USBSER_TX_FIFO_DRAIN_TIMEOUT);
2680 * if required, set break
2682 if (*(int *)mp->b_cont->b_rptr == 0) {
2683 if (USBSER_DS_BREAK_CTL(pp, DS_ON) != USB_SUCCESS) {
2684 error = EIO;
2685 break;
2688 mutex_enter(&pp->port_mutex);
2689 pp->port_act |= USBSER_ACT_BREAK;
2690 pp->port_delay_id = timeout(usbser_restart, pp,
2691 drv_usectohz(250000));
2692 mutex_exit(&pp->port_mutex);
2694 mioc2ack(mp, NULL, 0, 0);
2695 break;
2697 case TIOCSBRK: /* set break */
2698 if (USBSER_DS_BREAK_CTL(pp, DS_ON) != USB_SUCCESS)
2699 error = EIO;
2700 else
2701 mioc2ack(mp, NULL, 0, 0);
2702 break;
2704 case TIOCCBRK: /* clear break */
2705 if (USBSER_DS_BREAK_CTL(pp, DS_OFF) != USB_SUCCESS)
2706 error = EIO;
2707 else
2708 mioc2ack(mp, NULL, 0, 0);
2709 break;
2711 case TIOCMSET: /* set all modem bits */
2712 case TIOCMBIS: /* bis modem bits */
2713 case TIOCMBIC: /* bic modem bits */
2714 if (iocp->ioc_count == TRANSPARENT) {
2715 mcopyin(mp, NULL, sizeof (int), NULL);
2716 break;
2718 if ((error = miocpullup(mp, sizeof (int))) != 0)
2719 break;
2721 val = *(int *)mp->b_cont->b_rptr;
2722 if (cmd == TIOCMSET) {
2723 rval = USBSER_DS_SET_MODEM_CTL(pp, -1, val);
2724 } else if (cmd == TIOCMBIS) {
2725 rval = USBSER_DS_SET_MODEM_CTL(pp, val, -1);
2726 } else if (cmd == TIOCMBIC) {
2727 rval = USBSER_DS_SET_MODEM_CTL(pp, val, 0);
2729 if (rval == USB_SUCCESS)
2730 mioc2ack(mp, NULL, 0, 0);
2731 else
2732 error = EIO;
2733 break;
2735 case TIOCSILOOP:
2736 if (USBSER_DS_LOOPBACK_SUPPORTED(pp)) {
2737 if (USBSER_DS_LOOPBACK(pp, DS_ON) == USB_SUCCESS)
2738 mioc2ack(mp, NULL, 0, 0);
2739 else
2740 error = EIO;
2741 } else {
2742 error = EINVAL;
2744 break;
2746 case TIOCCILOOP:
2747 if (USBSER_DS_LOOPBACK_SUPPORTED(pp)) {
2748 if (USBSER_DS_LOOPBACK(pp, DS_OFF) == USB_SUCCESS)
2749 mioc2ack(mp, NULL, 0, 0);
2750 else
2751 error = EIO;
2752 } else {
2753 error = EINVAL;
2755 break;
2757 case TIOCMGET: /* get all modem bits */
2758 if ((datamp = allocb(sizeof (int), BPRI_MED)) == NULL) {
2759 error = EAGAIN;
2760 break;
2762 rval = USBSER_DS_GET_MODEM_CTL(pp, -1, (int *)datamp->b_rptr);
2763 if (rval != USB_SUCCESS) {
2764 error = EIO;
2765 break;
2767 if (iocp->ioc_count == TRANSPARENT)
2768 mcopyout(mp, NULL, sizeof (int), NULL, datamp);
2769 else
2770 mioc2ack(mp, datamp, sizeof (int), 0);
2771 break;
2773 case CONSOPENPOLLEDIO:
2774 error = usbser_polledio_init(pp);
2775 if (error != 0)
2776 break;
2778 error = miocpullup(mp, sizeof (struct cons_polledio *));
2779 if (error != 0)
2780 break;
2782 *(struct cons_polledio **)mp->b_cont->b_rptr = &usbser_polledio;
2784 mp->b_datap->db_type = M_IOCACK;
2785 break;
2787 case CONSCLOSEPOLLEDIO:
2788 usbser_polledio_fini(pp);
2789 mp->b_datap->db_type = M_IOCACK;
2790 iocp->ioc_error = 0;
2791 iocp->ioc_rval = 0;
2792 break;
2794 case CONSSETABORTENABLE:
2795 error = secpolicy_console(iocp->ioc_cr);
2796 if (error != 0)
2797 break;
2799 if (iocp->ioc_count != TRANSPARENT) {
2800 error = EINVAL;
2801 break;
2805 * To do: implement console abort support
2806 * This involves adding a console flag to usbser
2807 * state structure. If flag is set, parse input stream
2808 * for abort sequence (see asy for example).
2810 * For now, run mdb -K to get kmdb prompt.
2812 if (*(intptr_t *)mp->b_cont->b_rptr)
2813 usbser_console_abort = 1;
2814 else
2815 usbser_console_abort = 0;
2817 mp->b_datap->db_type = M_IOCACK;
2818 iocp->ioc_error = 0;
2819 iocp->ioc_rval = 0;
2820 break;
2822 case CONSGETABORTENABLE:
2823 /*CONSTANTCONDITION*/
2824 ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *));
2826 * Store the return value right in the payload
2827 * we were passed. Crude.
2829 mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL);
2830 *(boolean_t *)mp->b_cont->b_rptr = (usbser_console_abort != 0);
2831 break;
2833 default:
2834 error = EINVAL;
2835 break;
2837 end:
2838 if (error != 0)
2839 miocnak(q, mp, 0, error);
2840 else
2841 qreply(q, mp);
2843 mutex_enter(&pp->port_mutex);
2844 usbser_release_port_act(pp, USBSER_ACT_CTL);
2846 return (USB_SUCCESS);
2851 * process M_IOCDATA message
2853 static void
2854 usbser_iocdata(usbser_port_t *pp, mblk_t *mp)
2856 tty_common_t *tp = &pp->port_ttycommon;
2857 queue_t *q = tp->t_writeq;
2858 struct copyresp *csp;
2859 int cmd;
2860 int val;
2861 int rval = USB_FAILURE;
2863 ASSERT(mutex_owned(&pp->port_mutex));
2865 csp = (struct copyresp *)mp->b_rptr;
2866 cmd = csp->cp_cmd;
2868 if (csp->cp_rval != 0) {
2869 freemsg(mp);
2870 return;
2873 switch (cmd) {
2874 case TIOCMSET: /* set all modem bits */
2875 case TIOCMBIS: /* bis modem bits */
2876 case TIOCMBIC: /* bic modem bits */
2877 if ((mp->b_cont == NULL) ||
2878 (MBLKL(mp->b_cont) < sizeof (int))) {
2879 miocnak(q, mp, 0, EINVAL);
2880 break;
2882 val = *(int *)mp->b_cont->b_rptr;
2884 usbser_serialize_port_act(pp, USBSER_ACT_CTL);
2885 mutex_exit(&pp->port_mutex);
2887 if (cmd == TIOCMSET) {
2888 rval = USBSER_DS_SET_MODEM_CTL(pp, -1, val);
2889 } else if (cmd == TIOCMBIS) {
2890 rval = USBSER_DS_SET_MODEM_CTL(pp, val, -1);
2891 } else if (cmd == TIOCMBIC) {
2892 rval = USBSER_DS_SET_MODEM_CTL(pp, val, 0);
2895 if (mp->b_cont) {
2896 freemsg(mp->b_cont);
2897 mp->b_cont = NULL;
2900 if (rval == USB_SUCCESS)
2901 miocack(q, mp, 0, 0);
2902 else
2903 miocnak(q, mp, 0, EIO);
2905 mutex_enter(&pp->port_mutex);
2906 usbser_release_port_act(pp, USBSER_ACT_CTL);
2907 break;
2909 case TIOCMGET: /* get all modem bits */
2910 mutex_exit(&pp->port_mutex);
2911 miocack(q, mp, 0, 0);
2912 mutex_enter(&pp->port_mutex);
2913 break;
2915 default:
2916 mutex_exit(&pp->port_mutex);
2917 miocnak(q, mp, 0, EINVAL);
2918 mutex_enter(&pp->port_mutex);
2919 break;
2925 * handle M_START[I]/M_STOP[I] messages
2927 static void
2928 usbser_stop(usbser_port_t *pp, mblk_t *mp)
2930 usbser_st_mstop++;
2931 if (!(pp->port_flags & USBSER_FL_TX_STOPPED)) {
2932 usbser_serialize_port_act(pp, USBSER_ACT_CTL);
2933 pp->port_flags |= USBSER_FL_TX_STOPPED;
2935 mutex_exit(&pp->port_mutex);
2936 USBSER_DS_STOP(pp, DS_TX);
2937 mutex_enter(&pp->port_mutex);
2939 usbser_release_port_act(pp, USBSER_ACT_TX);
2940 usbser_release_port_act(pp, USBSER_ACT_CTL);
2942 freemsg(mp);
2946 static void
2947 usbser_start(usbser_port_t *pp, mblk_t *mp)
2949 usbser_st_mstart++;
2950 if (pp->port_flags & USBSER_FL_TX_STOPPED) {
2951 usbser_serialize_port_act(pp, USBSER_ACT_CTL);
2952 pp->port_flags &= ~USBSER_FL_TX_STOPPED;
2954 mutex_exit(&pp->port_mutex);
2955 USBSER_DS_START(pp, DS_TX);
2956 mutex_enter(&pp->port_mutex);
2957 usbser_release_port_act(pp, USBSER_ACT_CTL);
2959 freemsg(mp);
2963 static void
2964 usbser_stopi(usbser_port_t *pp, mblk_t *mp)
2966 usbser_st_mstopi++;
2967 usbser_serialize_port_act(pp, USBSER_ACT_CTL);
2968 pp->port_flowc = pp->port_ttycommon.t_stopc;
2969 usbser_inbound_flow_ctl(pp);
2970 usbser_release_port_act(pp, USBSER_ACT_CTL);
2971 freemsg(mp);
2974 static void
2975 usbser_starti(usbser_port_t *pp, mblk_t *mp)
2977 usbser_st_mstarti++;
2978 usbser_serialize_port_act(pp, USBSER_ACT_CTL);
2979 pp->port_flowc = pp->port_ttycommon.t_startc;
2980 usbser_inbound_flow_ctl(pp);
2981 usbser_release_port_act(pp, USBSER_ACT_CTL);
2982 freemsg(mp);
2986 * process M_FLUSH message
2988 static void
2989 usbser_flush(usbser_port_t *pp, mblk_t *mp)
2991 queue_t *q = pp->port_ttycommon.t_writeq;
2993 if (*mp->b_rptr & FLUSHW) {
2994 mutex_exit(&pp->port_mutex);
2995 (void) USBSER_DS_FIFO_FLUSH(pp, DS_TX); /* flush FIFO buffers */
2996 flushq(q, FLUSHDATA); /* flush write queue */
2997 mutex_enter(&pp->port_mutex);
2999 usbser_release_port_act(pp, USBSER_ACT_TX);
3001 *mp->b_rptr &= ~FLUSHW;
3003 if (*mp->b_rptr & FLUSHR) {
3005 * flush FIFO buffers
3007 mutex_exit(&pp->port_mutex);
3008 (void) USBSER_DS_FIFO_FLUSH(pp, DS_RX);
3009 flushq(RD(q), FLUSHDATA);
3010 qreply(q, mp);
3011 mutex_enter(&pp->port_mutex);
3012 } else {
3013 freemsg(mp);
3018 * process M_BREAK message
3020 static void
3021 usbser_break(usbser_port_t *pp, mblk_t *mp)
3023 int rval;
3026 * set the break and arrange for usbser_restart() to be called in 1/4 s
3028 mutex_exit(&pp->port_mutex);
3029 rval = USBSER_DS_BREAK_CTL(pp, DS_ON);
3030 mutex_enter(&pp->port_mutex);
3032 if (rval == USB_SUCCESS) {
3033 pp->port_act |= USBSER_ACT_BREAK;
3034 pp->port_delay_id = timeout(usbser_restart, pp,
3035 drv_usectohz(250000));
3037 freemsg(mp);
3042 * process M_DELAY message
3044 static void
3045 usbser_delay(usbser_port_t *pp, mblk_t *mp)
3048 * arrange for usbser_restart() to be called when the delay expires
3050 pp->port_act |= USBSER_ACT_DELAY;
3051 pp->port_delay_id = timeout(usbser_restart, pp,
3052 (clock_t)(*(uchar_t *)mp->b_rptr + 6));
3053 freemsg(mp);
3058 * restart output on a line after a delay or break timer expired
3060 static void
3061 usbser_restart(void *arg)
3063 usbser_port_t *pp = (usbser_port_t *)arg;
3065 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_restart");
3067 mutex_enter(&pp->port_mutex);
3068 /* if cancelled, return immediately */
3069 if (pp->port_delay_id == 0) {
3070 mutex_exit(&pp->port_mutex);
3072 return;
3074 pp->port_delay_id = 0;
3076 /* clear break if necessary */
3077 if (pp->port_act & USBSER_ACT_BREAK) {
3078 mutex_exit(&pp->port_mutex);
3079 (void) USBSER_DS_BREAK_CTL(pp, DS_OFF);
3080 mutex_enter(&pp->port_mutex);
3083 usbser_release_port_act(pp, USBSER_ACT_BREAK | USBSER_ACT_DELAY);
3085 /* wake wq thread to resume message processing */
3086 usbser_thr_wake(&pp->port_wq_thread);
3087 mutex_exit(&pp->port_mutex);
3092 * program port hardware with the chosen parameters
3093 * most of the operation is based on the values of 'c_iflag' and 'c_cflag'
3095 static int
3096 usbser_port_program(usbser_port_t *pp)
3098 tty_common_t *tp = &pp->port_ttycommon;
3099 int baudrate;
3100 int c_flag;
3101 ds_port_param_entry_t pe[6];
3102 ds_port_params_t params;
3103 int flow_ctl, ctl_val;
3104 int err = 0;
3106 baudrate = tp->t_cflag & CBAUD;
3107 if (tp->t_cflag & CBAUDEXT) {
3108 baudrate += 16;
3112 * set input speed same as output, as split speed not supported
3114 if (tp->t_cflag & (CIBAUD|CIBAUDEXT)) {
3115 tp->t_cflag &= ~(CIBAUD);
3116 if (baudrate > CBAUD) {
3117 tp->t_cflag |= CIBAUDEXT;
3118 tp->t_cflag |=
3119 (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD);
3120 } else {
3121 tp->t_cflag &= ~CIBAUDEXT;
3122 tp->t_cflag |= ((baudrate << IBSHIFT) & CIBAUD);
3126 c_flag = tp->t_cflag;
3129 * flow control
3131 flow_ctl = tp->t_iflag & (IXON | IXANY | IXOFF);
3132 if (c_flag & CRTSCTS) {
3133 flow_ctl |= CTSXON;
3135 if (c_flag & CRTSXOFF) {
3136 flow_ctl |= RTSXOFF;
3140 * fill in port parameters we need to set:
3142 * baud rate
3144 pe[0].param = DS_PARAM_BAUD;
3145 pe[0].val.ui = baudrate;
3147 /* stop bits */
3148 pe[1].param = DS_PARAM_STOPB;
3149 pe[1].val.ui = c_flag & CSTOPB;
3151 /* parity */
3152 pe[2].param = DS_PARAM_PARITY;
3153 pe[2].val.ui = c_flag & (PARENB | PARODD);
3155 /* char size */
3156 pe[3].param = DS_PARAM_CHARSZ;
3157 pe[3].val.ui = c_flag & CSIZE;
3159 /* start & stop chars */
3160 pe[4].param = DS_PARAM_XON_XOFF;
3161 pe[4].val.uc[0] = tp->t_startc;
3162 pe[4].val.uc[1] = tp->t_stopc;
3164 /* flow control */
3165 pe[5].param = DS_PARAM_FLOW_CTL;
3166 pe[5].val.ui = flow_ctl;
3168 params.tp_entries = &pe[0];
3169 params.tp_cnt = 6;
3171 /* control signals */
3172 ctl_val = TIOCM_DTR | TIOCM_RTS;
3173 if (baudrate == 0) {
3174 ctl_val &= ~TIOCM_DTR; /* zero baudrate means drop DTR */
3176 if (pp->port_flags & USBSER_FL_RX_STOPPED) {
3177 ctl_val &= ~TIOCM_RTS;
3180 /* submit */
3181 mutex_exit(&pp->port_mutex);
3182 err = USBSER_DS_SET_PORT_PARAMS(pp, &params);
3183 if (err != USB_SUCCESS) {
3184 mutex_enter(&pp->port_mutex);
3186 return (EINVAL);
3189 err = USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR | TIOCM_RTS, ctl_val);
3190 mutex_enter(&pp->port_mutex);
3192 return ((err == USB_SUCCESS) ? 0 : EIO);
3197 * check if any inbound flow control action needed
3199 static void
3200 usbser_inbound_flow_ctl(usbser_port_t *pp)
3202 tcflag_t need_hw;
3203 int rts;
3204 char c = pp->port_flowc;
3205 mblk_t *mp = NULL;
3207 USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh,
3208 "usbser_inbound_flow_ctl: c=%x cflag=%x port_flags=%x",
3209 c, pp->port_ttycommon.t_cflag, pp->port_flags);
3211 if (c == '\0') {
3213 return;
3215 pp->port_flowc = '\0';
3218 * if inbound hardware flow control enabled, we need to frob RTS
3220 need_hw = (pp->port_ttycommon.t_cflag & CRTSXOFF);
3221 if (c == pp->port_ttycommon.t_startc) {
3222 rts = TIOCM_RTS;
3223 pp->port_flags &= ~USBSER_FL_RX_STOPPED;
3224 } else {
3225 rts = 0;
3226 pp->port_flags |= USBSER_FL_RX_STOPPED;
3230 * if character flow control active, transmit a start or stop char,
3232 if (pp->port_ttycommon.t_iflag & IXOFF) {
3233 if ((mp = allocb(1, BPRI_LO)) == NULL) {
3234 USB_DPRINTF_L2(DPRINT_WQ, pp->port_lh,
3235 "usbser_inbound_flow_ctl: allocb failed");
3236 } else {
3237 *mp->b_wptr++ = c;
3238 pp->port_flags |= USBSER_ACT_TX;
3242 mutex_exit(&pp->port_mutex);
3243 if (need_hw) {
3244 (void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_RTS, rts);
3246 if (mp) {
3247 (void) USBSER_DS_TX(pp, mp);
3249 mutex_enter(&pp->port_mutex);
3254 * misc
3255 * ----
3258 * returns != 0 if device is online, 0 otherwise
3260 static int
3261 usbser_dev_is_online(usbser_state_t *usp)
3263 int rval;
3265 mutex_enter(&usp->us_mutex);
3266 rval = (usp->us_dev_state == USB_DEV_ONLINE);
3267 mutex_exit(&usp->us_mutex);
3269 return (rval);
3273 * serialize port activities defined by 'act' mask
3275 static void
3276 usbser_serialize_port_act(usbser_port_t *pp, int act)
3278 while (pp->port_act & act)
3279 cv_wait(&pp->port_act_cv, &pp->port_mutex);
3280 pp->port_act |= act;
3285 * indicate that port activity is finished
3287 static void
3288 usbser_release_port_act(usbser_port_t *pp, int act)
3290 pp->port_act &= ~act;
3291 cv_broadcast(&pp->port_act_cv);
3294 #ifdef DEBUG
3296 * message type to string and back conversion.
3298 * pardon breaks on the same line, but as long as cstyle doesn't
3299 * complain, I'd like to keep this form for trivial cases like this.
3300 * associative arrays in the kernel, anyone?
3302 static char *
3303 usbser_msgtype2str(int type) __unused;
3305 static char *
3306 usbser_msgtype2str(int type)
3308 char *str;
3310 switch (type) {
3311 case M_STOP: str = "M_STOP"; break;
3312 case M_START: str = "M_START"; break;
3313 case M_STOPI: str = "M_STOPI"; break;
3314 case M_STARTI: str = "M_STARTI"; break;
3315 case M_DATA: str = "M_DATA"; break;
3316 case M_DELAY: str = "M_DELAY"; break;
3317 case M_BREAK: str = "M_BREAK"; break;
3318 case M_IOCTL: str = "M_IOCTL"; break;
3319 case M_IOCDATA: str = "M_IOCDATA"; break;
3320 case M_FLUSH: str = "M_FLUSH"; break;
3321 case M_CTL: str = "M_CTL"; break;
3322 case M_READ: str = "M_READ"; break;
3323 default: str = "unknown"; break;
3326 return (str);
3329 static char *
3330 usbser_ioctl2str(int ioctl) __unused;
3332 static char *
3333 usbser_ioctl2str(int ioctl)
3335 char *str;
3337 switch (ioctl) {
3338 case TCGETA: str = "TCGETA"; break;
3339 case TCSETA: str = "TCSETA"; break;
3340 case TCSETAF: str = "TCSETAF"; break;
3341 case TCSETAW: str = "TCSETAW"; break;
3342 case TCSBRK: str = "TCSBRK"; break;
3343 case TCXONC: str = "TCXONC"; break;
3344 case TCFLSH: str = "TCFLSH"; break;
3345 case TCGETS: str = "TCGETS"; break;
3346 case TCSETS: str = "TCSETS"; break;
3347 case TCSETSF: str = "TCSETSF"; break;
3348 case TCSETSW: str = "TCSETSW"; break;
3349 case TIOCSBRK: str = "TIOCSBRK"; break;
3350 case TIOCCBRK: str = "TIOCCBRK"; break;
3351 case TIOCMSET: str = "TIOCMSET"; break;
3352 case TIOCMBIS: str = "TIOCMBIS"; break;
3353 case TIOCMBIC: str = "TIOCMBIC"; break;
3354 case TIOCMGET: str = "TIOCMGET"; break;
3355 case TIOCSILOOP: str = "TIOCSILOOP"; break;
3356 case TIOCCILOOP: str = "TIOCCILOOP"; break;
3357 case TCGETX: str = "TCGETX"; break;
3358 case TCSETX: str = "TCGETX"; break;
3359 case TCSETXW: str = "TCGETX"; break;
3360 case TCSETXF: str = "TCGETX"; break;
3361 default: str = "unknown"; break;
3364 return (str);
3366 #endif
3368 * Polled IO support
3371 /* called once by consconfig() when polledio is opened */
3372 static int
3373 usbser_polledio_init(usbser_port_t *pp)
3375 int err;
3376 usb_pipe_handle_t hdl;
3377 ds_ops_t *ds_ops = pp->port_ds_ops;
3379 /* only one serial line console supported */
3380 if (console_input != NULL)
3381 return (USB_FAILURE);
3383 /* check if underlying driver supports polled io */
3384 if (ds_ops->ds_version < DS_OPS_VERSION_V1 ||
3385 ds_ops->ds_out_pipe == NULL || ds_ops->ds_in_pipe == NULL)
3386 return (USB_FAILURE);
3388 /* init polled input pipe */
3389 hdl = ds_ops->ds_in_pipe(pp->port_ds_hdl, pp->port_num);
3390 err = usb_console_input_init(pp->port_usp->us_dip, hdl,
3391 &console_input_buf, &console_input);
3392 if (err)
3393 return (USB_FAILURE);
3395 /* init polled output pipe */
3396 hdl = ds_ops->ds_out_pipe(pp->port_ds_hdl, pp->port_num);
3397 err = usb_console_output_init(pp->port_usp->us_dip, hdl,
3398 &console_output);
3399 if (err) {
3400 (void) usb_console_input_fini(console_input);
3401 console_input = NULL;
3402 return (USB_FAILURE);
3405 return (USB_SUCCESS);
3408 /* called once by consconfig() when polledio is closed */
3409 /*ARGSUSED*/
3410 static void usbser_polledio_fini(usbser_port_t *pp)
3412 /* Since we can't move the console, there is nothing to do. */
3415 /*ARGSUSED*/
3416 static void
3417 usbser_polledio_enter(cons_polledio_arg_t arg)
3419 (void) usb_console_input_enter(console_input);
3420 (void) usb_console_output_enter(console_output);
3423 /*ARGSUSED*/
3424 static void
3425 usbser_polledio_exit(cons_polledio_arg_t arg)
3427 (void) usb_console_output_exit(console_output);
3428 (void) usb_console_input_exit(console_input);
3431 /*ARGSUSED*/
3432 static void
3433 usbser_putchar(cons_polledio_arg_t arg, uchar_t c)
3435 static uchar_t cr[2] = {'\r', '\n'};
3436 uint_t nout;
3438 if (c == '\n')
3439 (void) usb_console_write(console_output, cr, 2, &nout);
3440 else
3441 (void) usb_console_write(console_output, &c, 1, &nout);
3444 /*ARGSUSED*/
3445 static int
3446 usbser_getchar(cons_polledio_arg_t arg)
3448 while (!usbser_ischar(arg))
3451 return (*console_input_start++);
3454 /*ARGSUSED*/
3455 static boolean_t
3456 usbser_ischar(cons_polledio_arg_t arg)
3458 uint_t num_bytes;
3460 if (console_input_start < console_input_end)
3461 return (B_TRUE);
3463 if (usb_console_read(console_input, &num_bytes) != USB_SUCCESS)
3464 return (B_FALSE);
3466 console_input_start = console_input_buf;
3467 console_input_end = console_input_buf + num_bytes;
3469 return (num_bytes != 0);