10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
52191Sszhou * Common Development and Distribution License (the "License").
62191Sszhou * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
229354STim.Marsland@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate *
290Sstevel@tonic-gate * USB generic serial driver (GSD)
300Sstevel@tonic-gate *
310Sstevel@tonic-gate */
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/param.h>
340Sstevel@tonic-gate #include <sys/stream.h>
350Sstevel@tonic-gate #include <sys/stropts.h>
360Sstevel@tonic-gate #include <sys/errno.h>
370Sstevel@tonic-gate #include <sys/cred.h>
380Sstevel@tonic-gate #include <sys/conf.h>
390Sstevel@tonic-gate #include <sys/stat.h>
400Sstevel@tonic-gate #include <sys/modctl.h>
410Sstevel@tonic-gate #include <sys/ddi.h>
420Sstevel@tonic-gate #include <sys/sunddi.h>
432191Sszhou #include <sys/sunndi.h>
440Sstevel@tonic-gate #include <sys/termio.h>
450Sstevel@tonic-gate #include <sys/termiox.h>
460Sstevel@tonic-gate #include <sys/stropts.h>
470Sstevel@tonic-gate #include <sys/stream.h>
480Sstevel@tonic-gate #include <sys/strsubr.h>
490Sstevel@tonic-gate #include <sys/strsun.h>
500Sstevel@tonic-gate #include <sys/strtty.h>
510Sstevel@tonic-gate #include <sys/policy.h>
522191Sszhou #include <sys/consdev.h>
530Sstevel@tonic-gate
540Sstevel@tonic-gate #include <sys/usb/usba.h>
550Sstevel@tonic-gate #include <sys/usb/clients/usbser/usbser_var.h>
560Sstevel@tonic-gate #include <sys/usb/clients/usbser/usbser_dsdi.h>
570Sstevel@tonic-gate #include <sys/usb/clients/usbser/usbser_rseq.h>
582191Sszhou #include <sys/usb/usba/genconsole.h>
590Sstevel@tonic-gate
600Sstevel@tonic-gate /* autoconfiguration subroutines */
610Sstevel@tonic-gate static int usbser_rseq_do_cb(rseq_t *, int, uintptr_t);
620Sstevel@tonic-gate static int usbser_free_soft_state(usbser_state_t *);
630Sstevel@tonic-gate static int usbser_init_soft_state(usbser_state_t *);
640Sstevel@tonic-gate static int usbser_fini_soft_state(usbser_state_t *);
650Sstevel@tonic-gate static int usbser_attach_dev(usbser_state_t *);
660Sstevel@tonic-gate static void usbser_detach_dev(usbser_state_t *);
670Sstevel@tonic-gate static int usbser_attach_ports(usbser_state_t *);
680Sstevel@tonic-gate static int usbser_create_port_minor_nodes(usbser_state_t *, int);
690Sstevel@tonic-gate static void usbser_detach_ports(usbser_state_t *);
700Sstevel@tonic-gate static int usbser_create_taskq(usbser_state_t *);
710Sstevel@tonic-gate static void usbser_destroy_taskq(usbser_state_t *);
720Sstevel@tonic-gate static void usbser_set_dev_state_init(usbser_state_t *);
730Sstevel@tonic-gate
740Sstevel@tonic-gate /* hotplugging and power management */
750Sstevel@tonic-gate static int usbser_disconnect_cb(dev_info_t *);
760Sstevel@tonic-gate static int usbser_reconnect_cb(dev_info_t *);
770Sstevel@tonic-gate static void usbser_disconnect_ports(usbser_state_t *);
780Sstevel@tonic-gate static int usbser_cpr_suspend(dev_info_t *);
790Sstevel@tonic-gate static int usbser_suspend_ports(usbser_state_t *);
800Sstevel@tonic-gate static void usbser_cpr_resume(dev_info_t *);
810Sstevel@tonic-gate static int usbser_restore_device_state(usbser_state_t *);
820Sstevel@tonic-gate static void usbser_restore_ports_state(usbser_state_t *);
830Sstevel@tonic-gate
840Sstevel@tonic-gate /* STREAMS subroutines */
850Sstevel@tonic-gate static int usbser_open_setup(queue_t *, usbser_port_t *, int, int,
860Sstevel@tonic-gate cred_t *);
870Sstevel@tonic-gate static int usbser_open_init(usbser_port_t *, int);
880Sstevel@tonic-gate static void usbser_check_port_props(usbser_port_t *);
890Sstevel@tonic-gate static void usbser_open_fini(usbser_port_t *);
900Sstevel@tonic-gate static int usbser_open_line_setup(usbser_port_t *, int, int);
910Sstevel@tonic-gate static int usbser_open_carrier_check(usbser_port_t *, int, int);
920Sstevel@tonic-gate static void usbser_open_queues_init(usbser_port_t *, queue_t *);
930Sstevel@tonic-gate static void usbser_open_queues_fini(usbser_port_t *);
940Sstevel@tonic-gate static void usbser_close_drain(usbser_port_t *);
950Sstevel@tonic-gate static void usbser_close_cancel_break(usbser_port_t *);
960Sstevel@tonic-gate static void usbser_close_hangup(usbser_port_t *);
970Sstevel@tonic-gate static void usbser_close_cleanup(usbser_port_t *);
980Sstevel@tonic-gate
990Sstevel@tonic-gate /* threads */
1000Sstevel@tonic-gate static void usbser_thr_dispatch(usbser_thread_t *);
1010Sstevel@tonic-gate static void usbser_thr_cancel(usbser_thread_t *);
1020Sstevel@tonic-gate static void usbser_thr_wake(usbser_thread_t *);
1030Sstevel@tonic-gate static void usbser_wq_thread(void *);
1040Sstevel@tonic-gate static void usbser_rq_thread(void *);
1050Sstevel@tonic-gate
1060Sstevel@tonic-gate /* DSD callbacks */
1070Sstevel@tonic-gate static void usbser_tx_cb(caddr_t);
1080Sstevel@tonic-gate static void usbser_rx_cb(caddr_t);
1090Sstevel@tonic-gate static void usbser_rx_massage_data(usbser_port_t *, mblk_t *);
1100Sstevel@tonic-gate static void usbser_rx_massage_mbreak(usbser_port_t *, mblk_t *);
1110Sstevel@tonic-gate static void usbser_rx_cb_put(usbser_port_t *, queue_t *, queue_t *,
1120Sstevel@tonic-gate mblk_t *);
1130Sstevel@tonic-gate static void usbser_status_cb(caddr_t);
1140Sstevel@tonic-gate static void usbser_status_proc_cb(usbser_port_t *);
1150Sstevel@tonic-gate
1160Sstevel@tonic-gate /* serial support */
1170Sstevel@tonic-gate static void usbser_wmsg(usbser_port_t *);
1180Sstevel@tonic-gate static int usbser_data(usbser_port_t *, mblk_t *);
1190Sstevel@tonic-gate static int usbser_ioctl(usbser_port_t *, mblk_t *);
1200Sstevel@tonic-gate static void usbser_iocdata(usbser_port_t *, mblk_t *);
1210Sstevel@tonic-gate static void usbser_stop(usbser_port_t *, mblk_t *);
1220Sstevel@tonic-gate static void usbser_start(usbser_port_t *, mblk_t *);
1230Sstevel@tonic-gate static void usbser_stopi(usbser_port_t *, mblk_t *);
1240Sstevel@tonic-gate static void usbser_starti(usbser_port_t *, mblk_t *);
1250Sstevel@tonic-gate static void usbser_flush(usbser_port_t *, mblk_t *);
1260Sstevel@tonic-gate static void usbser_break(usbser_port_t *, mblk_t *);
1270Sstevel@tonic-gate static void usbser_delay(usbser_port_t *, mblk_t *);
1280Sstevel@tonic-gate static void usbser_restart(void *);
1290Sstevel@tonic-gate static int usbser_port_program(usbser_port_t *);
1300Sstevel@tonic-gate static void usbser_inbound_flow_ctl(usbser_port_t *);
1310Sstevel@tonic-gate
1320Sstevel@tonic-gate /* misc */
1330Sstevel@tonic-gate static int usbser_dev_is_online(usbser_state_t *);
1340Sstevel@tonic-gate static void usbser_serialize_port_act(usbser_port_t *, int);
1350Sstevel@tonic-gate static void usbser_release_port_act(usbser_port_t *, int);
1360Sstevel@tonic-gate static char *usbser_msgtype2str(int);
1370Sstevel@tonic-gate static char *usbser_ioctl2str(int);
1380Sstevel@tonic-gate
1390Sstevel@tonic-gate
1400Sstevel@tonic-gate /* USBA events */
1410Sstevel@tonic-gate usb_event_t usbser_usb_events = {
1420Sstevel@tonic-gate usbser_disconnect_cb, /* disconnect */
1430Sstevel@tonic-gate usbser_reconnect_cb, /* reconnect */
1440Sstevel@tonic-gate NULL, /* pre-suspend */
1450Sstevel@tonic-gate NULL, /* pre-resume */
1460Sstevel@tonic-gate };
1470Sstevel@tonic-gate
1480Sstevel@tonic-gate /* debug support */
1496898Sfb209375 uint_t usbser_errlevel = USB_LOG_L4;
1506898Sfb209375 uint_t usbser_errmask = DPRINT_MASK_ALL;
1516898Sfb209375 uint_t usbser_instance_debug = (uint_t)-1;
1520Sstevel@tonic-gate
1532191Sszhou /* usb serial console */
1542191Sszhou static struct usbser_state *usbser_list;
1552191Sszhou static kmutex_t usbser_lock;
1562191Sszhou static int usbser_console_abort;
1572191Sszhou static usb_console_info_t console_input, console_output;
1582191Sszhou static uchar_t *console_input_buf;
1592191Sszhou static uchar_t *console_input_start, *console_input_end;
1602191Sszhou
1612326Ssl147100 _NOTE(SCHEME_PROTECTS_DATA("unshared", usbser_console_abort))
1622326Ssl147100 _NOTE(SCHEME_PROTECTS_DATA("unshared", console_input))
1632326Ssl147100 _NOTE(SCHEME_PROTECTS_DATA("unshared", console_output))
1642326Ssl147100 _NOTE(SCHEME_PROTECTS_DATA("unshared", console_input_start))
1652326Ssl147100 _NOTE(SCHEME_PROTECTS_DATA("unshared", console_input_end))
1662326Ssl147100
1672191Sszhou static void usbser_putchar(cons_polledio_arg_t, uchar_t);
1682191Sszhou static int usbser_getchar(cons_polledio_arg_t);
1692191Sszhou static boolean_t usbser_ischar(cons_polledio_arg_t);
1702191Sszhou static void usbser_polledio_enter(cons_polledio_arg_t);
1712191Sszhou static void usbser_polledio_exit(cons_polledio_arg_t);
1722191Sszhou static int usbser_polledio_init(usbser_port_t *);
1732191Sszhou static void usbser_polledio_fini(usbser_port_t *);
1742191Sszhou
1752191Sszhou static struct cons_polledio usbser_polledio = {
1762191Sszhou CONSPOLLEDIO_V1,
1772191Sszhou NULL, /* to be set later */
1782191Sszhou usbser_putchar,
1792191Sszhou usbser_getchar,
1802191Sszhou usbser_ischar,
1812191Sszhou usbser_polledio_enter,
1822191Sszhou usbser_polledio_exit
1832191Sszhou };
1842191Sszhou
1850Sstevel@tonic-gate /* various statistics. TODO: replace with kstats */
1860Sstevel@tonic-gate static int usbser_st_tx_data_loss = 0;
1870Sstevel@tonic-gate static int usbser_st_rx_data_loss = 0;
1880Sstevel@tonic-gate static int usbser_st_put_stopi = 0;
1890Sstevel@tonic-gate static int usbser_st_mstop = 0;
1900Sstevel@tonic-gate static int usbser_st_mstart = 0;
1910Sstevel@tonic-gate static int usbser_st_mstopi = 0;
1920Sstevel@tonic-gate static int usbser_st_mstarti = 0;
1930Sstevel@tonic-gate static int usbser_st_rsrv = 0;
1940Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("monotonic stats", usbser_st_{
1950Sstevel@tonic-gate tx_data_loss rx_data_loss put_stopi mstop mstart mstopi mstarti rsrv}))
1960Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unshared", usb_bulk_req_t))
1970Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unshared", usb_intr_req_t))
1980Sstevel@tonic-gate
1990Sstevel@tonic-gate /* taskq parameter */
2000Sstevel@tonic-gate extern pri_t minclsyspri;
2010Sstevel@tonic-gate
2020Sstevel@tonic-gate /*
2030Sstevel@tonic-gate * tell warlock not to worry about STREAMS structures
2040Sstevel@tonic-gate */
2050Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", iocblk datab msgb queue copyreq))
2060Sstevel@tonic-gate
2070Sstevel@tonic-gate /*
2080Sstevel@tonic-gate * modload support
2090Sstevel@tonic-gate */
2100Sstevel@tonic-gate extern struct mod_ops mod_miscops;
2110Sstevel@tonic-gate
2120Sstevel@tonic-gate static struct modlmisc modlmisc = {
2130Sstevel@tonic-gate &mod_miscops, /* Type of module */
2147020Sgd78059 "USB generic serial module"
2150Sstevel@tonic-gate };
2160Sstevel@tonic-gate
2170Sstevel@tonic-gate static struct modlinkage modlinkage = {
2180Sstevel@tonic-gate MODREV_1, (void *)&modlmisc, NULL
2190Sstevel@tonic-gate };
2200Sstevel@tonic-gate
2210Sstevel@tonic-gate
2220Sstevel@tonic-gate #define RSEQ(f1, f2) RSEQE(f1, usbser_rseq_do_cb, f2, NULL)
2230Sstevel@tonic-gate
2240Sstevel@tonic-gate
2250Sstevel@tonic-gate /*
2260Sstevel@tonic-gate * loadable module entry points
2270Sstevel@tonic-gate * ----------------------------
2280Sstevel@tonic-gate */
2290Sstevel@tonic-gate
2300Sstevel@tonic-gate int
_init(void)2310Sstevel@tonic-gate _init(void)
2320Sstevel@tonic-gate {
2332191Sszhou int err;
2342191Sszhou
2352191Sszhou mutex_init(&usbser_lock, NULL, MUTEX_DRIVER, (void *)NULL);
2362191Sszhou if (err = mod_install(&modlinkage))
2372191Sszhou mutex_destroy(&usbser_lock);
2382191Sszhou
2392191Sszhou return (err);
2400Sstevel@tonic-gate }
2410Sstevel@tonic-gate
2420Sstevel@tonic-gate
2430Sstevel@tonic-gate int
_fini(void)2440Sstevel@tonic-gate _fini(void)
2450Sstevel@tonic-gate {
2462191Sszhou int err;
2472191Sszhou
2482191Sszhou if (err = mod_remove(&modlinkage))
2492191Sszhou
2502191Sszhou return (err);
2512191Sszhou
2522191Sszhou mutex_destroy(&usbser_lock);
2532191Sszhou
2542191Sszhou return (0);
2550Sstevel@tonic-gate }
2560Sstevel@tonic-gate
2570Sstevel@tonic-gate
2580Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2590Sstevel@tonic-gate _info(struct modinfo *modinfop)
2600Sstevel@tonic-gate {
2610Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
2620Sstevel@tonic-gate }
2630Sstevel@tonic-gate
2640Sstevel@tonic-gate
2650Sstevel@tonic-gate /*
2660Sstevel@tonic-gate * soft state size
2670Sstevel@tonic-gate */
2680Sstevel@tonic-gate int
usbser_soft_state_size()2690Sstevel@tonic-gate usbser_soft_state_size()
2700Sstevel@tonic-gate {
2710Sstevel@tonic-gate return (sizeof (usbser_state_t));
2720Sstevel@tonic-gate }
2730Sstevel@tonic-gate
2740Sstevel@tonic-gate
2750Sstevel@tonic-gate /*
2760Sstevel@tonic-gate * autoconfiguration entry points
2770Sstevel@tonic-gate * ------------------------------
2780Sstevel@tonic-gate */
2790Sstevel@tonic-gate
2800Sstevel@tonic-gate /*ARGSUSED*/
2810Sstevel@tonic-gate int
usbser_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result,void * statep)2820Sstevel@tonic-gate usbser_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
2830Sstevel@tonic-gate void **result, void *statep)
2840Sstevel@tonic-gate {
2850Sstevel@tonic-gate int instance;
2860Sstevel@tonic-gate int ret = DDI_FAILURE;
2870Sstevel@tonic-gate usbser_state_t *usbserp;
2880Sstevel@tonic-gate
2890Sstevel@tonic-gate instance = USBSER_MINOR2INST(getminor((dev_t)arg));
2900Sstevel@tonic-gate
2910Sstevel@tonic-gate switch (infocmd) {
2920Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO:
2930Sstevel@tonic-gate *result = NULL;
2940Sstevel@tonic-gate usbserp = ddi_get_soft_state(statep, instance);
2950Sstevel@tonic-gate if (usbserp != NULL) {
2960Sstevel@tonic-gate *result = usbserp->us_dip;
2970Sstevel@tonic-gate if (*result != NULL) {
2980Sstevel@tonic-gate ret = DDI_SUCCESS;
2990Sstevel@tonic-gate }
3000Sstevel@tonic-gate }
3010Sstevel@tonic-gate
3020Sstevel@tonic-gate break;
3030Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE:
3040Sstevel@tonic-gate *result = (void *)(uintptr_t)instance;
3050Sstevel@tonic-gate ret = DDI_SUCCESS;
3060Sstevel@tonic-gate
3070Sstevel@tonic-gate break;
3080Sstevel@tonic-gate default:
3090Sstevel@tonic-gate break;
3100Sstevel@tonic-gate }
3110Sstevel@tonic-gate
3120Sstevel@tonic-gate return (ret);
3130Sstevel@tonic-gate }
3140Sstevel@tonic-gate
3150Sstevel@tonic-gate /*
3160Sstevel@tonic-gate * device attach
3170Sstevel@tonic-gate */
3180Sstevel@tonic-gate static rseq_t rseq_att[] = {
3190Sstevel@tonic-gate RSEQ(NULL, usbser_free_soft_state),
3200Sstevel@tonic-gate RSEQ(usbser_init_soft_state, usbser_fini_soft_state),
3210Sstevel@tonic-gate RSEQ(usbser_attach_dev, usbser_detach_dev),
3220Sstevel@tonic-gate RSEQ(usbser_attach_ports, usbser_detach_ports),
3230Sstevel@tonic-gate RSEQ(usbser_create_taskq, usbser_destroy_taskq),
3240Sstevel@tonic-gate RSEQ(NULL, usbser_set_dev_state_init)
3250Sstevel@tonic-gate };
3260Sstevel@tonic-gate
3272191Sszhou static void
usbser_insert(struct usbser_state * usp)3282191Sszhou usbser_insert(struct usbser_state *usp)
3292191Sszhou {
3302191Sszhou struct usbser_state *tmp;
3312191Sszhou
3322191Sszhou mutex_enter(&usbser_lock);
3332191Sszhou tmp = usbser_list;
3342191Sszhou if (tmp == NULL)
3352191Sszhou usbser_list = usp;
3362191Sszhou else {
3372191Sszhou while (tmp->us_next)
3382191Sszhou tmp = tmp->us_next;
3392191Sszhou tmp->us_next = usp;
3402191Sszhou }
3412191Sszhou mutex_exit(&usbser_lock);
3422191Sszhou }
3432191Sszhou
3442191Sszhou static void
usbser_remove(struct usbser_state * usp)3452191Sszhou usbser_remove(struct usbser_state *usp)
3462191Sszhou {
3472191Sszhou struct usbser_state *tmp, *prev = NULL;
3482191Sszhou
3492191Sszhou mutex_enter(&usbser_lock);
3502191Sszhou tmp = usbser_list;
3512191Sszhou while (tmp != usp) {
3522191Sszhou prev = tmp;
3532191Sszhou tmp = tmp->us_next;
3542191Sszhou }
3552191Sszhou ASSERT(tmp == usp); /* must exist, else attach/detach wrong */
3562191Sszhou if (prev)
3572191Sszhou prev->us_next = usp->us_next;
3582191Sszhou else
3592191Sszhou usbser_list = usp->us_next;
3602191Sszhou usp->us_next = NULL;
3612191Sszhou mutex_exit(&usbser_lock);
3622191Sszhou }
3632191Sszhou
3642191Sszhou /*
3652191Sszhou * Return the first serial device, with dip held. This is called
3662191Sszhou * from the console subsystem to place console on usb serial device.
3672191Sszhou */
3682191Sszhou dev_info_t *
usbser_first_device(void)3692191Sszhou usbser_first_device(void)
3702191Sszhou {
3712191Sszhou dev_info_t *dip = NULL;
3722191Sszhou
3732191Sszhou mutex_enter(&usbser_lock);
3742191Sszhou if (usbser_list) {
3752191Sszhou dip = usbser_list->us_dip;
3762191Sszhou ndi_hold_devi(dip);
3772191Sszhou }
3782191Sszhou mutex_exit(&usbser_lock);
3792191Sszhou
3802191Sszhou return (dip);
3812191Sszhou }
3822191Sszhou
3830Sstevel@tonic-gate int
usbser_attach(dev_info_t * dip,ddi_attach_cmd_t cmd,void * statep,ds_ops_t * ds_ops)3840Sstevel@tonic-gate usbser_attach(dev_info_t *dip, ddi_attach_cmd_t cmd,
3850Sstevel@tonic-gate void *statep, ds_ops_t *ds_ops)
3860Sstevel@tonic-gate {
3870Sstevel@tonic-gate int instance;
3880Sstevel@tonic-gate usbser_state_t *usp;
3890Sstevel@tonic-gate
3900Sstevel@tonic-gate instance = ddi_get_instance(dip);
3910Sstevel@tonic-gate
3920Sstevel@tonic-gate switch (cmd) {
3930Sstevel@tonic-gate case DDI_ATTACH:
3940Sstevel@tonic-gate
3950Sstevel@tonic-gate break;
3960Sstevel@tonic-gate case DDI_RESUME:
3970Sstevel@tonic-gate usbser_cpr_resume(dip);
3980Sstevel@tonic-gate
3990Sstevel@tonic-gate return (DDI_SUCCESS);
4000Sstevel@tonic-gate default:
4010Sstevel@tonic-gate
4020Sstevel@tonic-gate return (DDI_FAILURE);
4030Sstevel@tonic-gate }
4040Sstevel@tonic-gate
4050Sstevel@tonic-gate /* allocate and get soft state */
4060Sstevel@tonic-gate if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS) {
4070Sstevel@tonic-gate
4080Sstevel@tonic-gate return (DDI_FAILURE);
4090Sstevel@tonic-gate }
4100Sstevel@tonic-gate if ((usp = ddi_get_soft_state(statep, instance)) == NULL) {
4110Sstevel@tonic-gate ddi_soft_state_free(statep, instance);
4120Sstevel@tonic-gate
4130Sstevel@tonic-gate return (DDI_FAILURE);
4140Sstevel@tonic-gate }
4150Sstevel@tonic-gate
4160Sstevel@tonic-gate usp->us_statep = statep;
4170Sstevel@tonic-gate usp->us_dip = dip;
4180Sstevel@tonic-gate usp->us_instance = instance;
4190Sstevel@tonic-gate usp->us_ds_ops = ds_ops;
4200Sstevel@tonic-gate
4210Sstevel@tonic-gate if (rseq_do(rseq_att, NELEM(rseq_att), (uintptr_t)usp, 0) == RSEQ_OK) {
4220Sstevel@tonic-gate ddi_report_dev(dip);
4232191Sszhou usbser_insert(usp);
4240Sstevel@tonic-gate
4250Sstevel@tonic-gate return (DDI_SUCCESS);
4260Sstevel@tonic-gate } else {
4270Sstevel@tonic-gate
4280Sstevel@tonic-gate return (DDI_FAILURE);
4290Sstevel@tonic-gate }
4300Sstevel@tonic-gate }
4310Sstevel@tonic-gate
4320Sstevel@tonic-gate /*
4330Sstevel@tonic-gate * device detach
4340Sstevel@tonic-gate */
4350Sstevel@tonic-gate int
usbser_detach(dev_info_t * dip,ddi_detach_cmd_t cmd,void * statep)4360Sstevel@tonic-gate usbser_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, void *statep)
4370Sstevel@tonic-gate {
4380Sstevel@tonic-gate int instance = ddi_get_instance(dip);
4390Sstevel@tonic-gate usbser_state_t *usp;
4400Sstevel@tonic-gate int rval;
4410Sstevel@tonic-gate
4420Sstevel@tonic-gate usp = ddi_get_soft_state(statep, instance);
4430Sstevel@tonic-gate
4440Sstevel@tonic-gate switch (cmd) {
4450Sstevel@tonic-gate case DDI_DETACH:
4460Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_DETACH, usp->us_lh, "usbser_detach");
4472191Sszhou usbser_remove(usp);
4480Sstevel@tonic-gate (void) rseq_undo(rseq_att, NELEM(rseq_att), (uintptr_t)usp, 0);
4490Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_DETACH, NULL,
4505541Slg150142 "usbser_detach.%d: end", instance);
4510Sstevel@tonic-gate
4520Sstevel@tonic-gate return (DDI_SUCCESS);
4530Sstevel@tonic-gate case DDI_SUSPEND:
4540Sstevel@tonic-gate rval = usbser_cpr_suspend(dip);
4550Sstevel@tonic-gate
4560Sstevel@tonic-gate return ((rval == USB_SUCCESS)? DDI_SUCCESS : DDI_FAILURE);
4570Sstevel@tonic-gate default:
4580Sstevel@tonic-gate
4590Sstevel@tonic-gate return (DDI_FAILURE);
4600Sstevel@tonic-gate }
4610Sstevel@tonic-gate }
4620Sstevel@tonic-gate
4630Sstevel@tonic-gate /*
4640Sstevel@tonic-gate * STREAMS entry points
4650Sstevel@tonic-gate * --------------------
4660Sstevel@tonic-gate *
4670Sstevel@tonic-gate *
4680Sstevel@tonic-gate * port open
4690Sstevel@tonic-gate */
4700Sstevel@tonic-gate /*ARGSUSED*/
4710Sstevel@tonic-gate int
usbser_open(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr,void * statep)4720Sstevel@tonic-gate usbser_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr,
4730Sstevel@tonic-gate void *statep)
4740Sstevel@tonic-gate {
4750Sstevel@tonic-gate usbser_state_t *usp;
4760Sstevel@tonic-gate usbser_port_t *pp;
4770Sstevel@tonic-gate int minor = getminor(*dev);
4780Sstevel@tonic-gate int instance;
4790Sstevel@tonic-gate uint_t port_num;
4800Sstevel@tonic-gate int rval;
4810Sstevel@tonic-gate
4820Sstevel@tonic-gate instance = USBSER_MINOR2INST(minor);
4830Sstevel@tonic-gate if (instance < 0) {
4840Sstevel@tonic-gate
4850Sstevel@tonic-gate return (ENXIO);
4860Sstevel@tonic-gate }
4870Sstevel@tonic-gate
4880Sstevel@tonic-gate usp = ddi_get_soft_state(statep, instance);
4890Sstevel@tonic-gate if (usp == NULL) {
4900Sstevel@tonic-gate
4910Sstevel@tonic-gate return (ENXIO);
4920Sstevel@tonic-gate }
4930Sstevel@tonic-gate
4940Sstevel@tonic-gate /* don't allow to open disconnected device */
4950Sstevel@tonic-gate mutex_enter(&usp->us_mutex);
4960Sstevel@tonic-gate if (usp->us_dev_state == USB_DEV_DISCONNECTED) {
4970Sstevel@tonic-gate mutex_exit(&usp->us_mutex);
4980Sstevel@tonic-gate
4990Sstevel@tonic-gate return (ENXIO);
5000Sstevel@tonic-gate }
5010Sstevel@tonic-gate mutex_exit(&usp->us_mutex);
5020Sstevel@tonic-gate
5030Sstevel@tonic-gate /* get port soft state */
5040Sstevel@tonic-gate port_num = USBSER_MINOR2PORT(minor);
5050Sstevel@tonic-gate if (port_num >= usp->us_port_cnt) {
5060Sstevel@tonic-gate
5070Sstevel@tonic-gate return (ENXIO);
5080Sstevel@tonic-gate }
5090Sstevel@tonic-gate pp = &usp->us_ports[port_num];
5100Sstevel@tonic-gate
5110Sstevel@tonic-gate /* set up everything for open */
5120Sstevel@tonic-gate rval = usbser_open_setup(rq, pp, minor, flag, cr);
5130Sstevel@tonic-gate
5140Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_OPEN, pp->port_lh, "usbser_open: rval=%d", rval);
5150Sstevel@tonic-gate
5160Sstevel@tonic-gate return (rval);
5170Sstevel@tonic-gate }
5180Sstevel@tonic-gate
5190Sstevel@tonic-gate
5200Sstevel@tonic-gate /*
5210Sstevel@tonic-gate * port close
5220Sstevel@tonic-gate *
5230Sstevel@tonic-gate * some things driver should do when the last app closes the line:
5240Sstevel@tonic-gate *
5250Sstevel@tonic-gate * drain data;
5260Sstevel@tonic-gate * cancel break/delay;
5270Sstevel@tonic-gate * hangup line (if necessary);
5280Sstevel@tonic-gate * DSD close;
5290Sstevel@tonic-gate * cleanup soft state;
5300Sstevel@tonic-gate */
5310Sstevel@tonic-gate /*ARGSUSED*/
5320Sstevel@tonic-gate int
usbser_close(queue_t * rq,int flag,cred_t * cr)5330Sstevel@tonic-gate usbser_close(queue_t *rq, int flag, cred_t *cr)
5340Sstevel@tonic-gate {
5350Sstevel@tonic-gate usbser_port_t *pp = (usbser_port_t *)rq->q_ptr;
5360Sstevel@tonic-gate int online;
5370Sstevel@tonic-gate
5380Sstevel@tonic-gate if (pp == NULL) {
5390Sstevel@tonic-gate
5400Sstevel@tonic-gate return (ENXIO);
5410Sstevel@tonic-gate }
5420Sstevel@tonic-gate
5430Sstevel@tonic-gate online = usbser_dev_is_online(pp->port_usp);
5440Sstevel@tonic-gate
5450Sstevel@tonic-gate /*
5460Sstevel@tonic-gate * in the closing state new activities will not be initiated
5470Sstevel@tonic-gate */
5480Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
5490Sstevel@tonic-gate pp->port_state = USBSER_PORT_CLOSING;
5500Sstevel@tonic-gate
5510Sstevel@tonic-gate if (online) {
5520Sstevel@tonic-gate /* drain the data */
5530Sstevel@tonic-gate usbser_close_drain(pp);
5540Sstevel@tonic-gate }
5550Sstevel@tonic-gate
5560Sstevel@tonic-gate /* stop break/delay */
5570Sstevel@tonic-gate usbser_close_cancel_break(pp);
5580Sstevel@tonic-gate
5590Sstevel@tonic-gate if (online) {
5600Sstevel@tonic-gate /* hangup line */
5610Sstevel@tonic-gate usbser_close_hangup(pp);
5620Sstevel@tonic-gate }
5630Sstevel@tonic-gate
5640Sstevel@tonic-gate /*
5650Sstevel@tonic-gate * close DSD, cleanup state and transition to 'closed' state
5660Sstevel@tonic-gate */
5670Sstevel@tonic-gate usbser_close_cleanup(pp);
5680Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
5690Sstevel@tonic-gate
5700Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_CLOSE, pp->port_lh, "usbser_close: end");
5710Sstevel@tonic-gate
5720Sstevel@tonic-gate return (0);
5730Sstevel@tonic-gate }
5740Sstevel@tonic-gate
5750Sstevel@tonic-gate
5760Sstevel@tonic-gate /*
5770Sstevel@tonic-gate * read side service routine: send as much as possible messages upstream
5780Sstevel@tonic-gate * and if there is still place on the queue, enable receive (if not already)
5790Sstevel@tonic-gate */
5800Sstevel@tonic-gate int
usbser_rsrv(queue_t * q)5810Sstevel@tonic-gate usbser_rsrv(queue_t *q)
5820Sstevel@tonic-gate {
5830Sstevel@tonic-gate usbser_port_t *pp = (usbser_port_t *)q->q_ptr;
5840Sstevel@tonic-gate mblk_t *mp;
5850Sstevel@tonic-gate
5860Sstevel@tonic-gate usbser_st_rsrv++;
5870Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_RQ, pp->port_lh, "usbser_rsrv");
5880Sstevel@tonic-gate
5890Sstevel@tonic-gate while (canputnext(q) && (mp = getq(q))) {
5900Sstevel@tonic-gate putnext(q, mp);
5910Sstevel@tonic-gate }
5920Sstevel@tonic-gate
5930Sstevel@tonic-gate if (canputnext(q)) {
5940Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
5950Sstevel@tonic-gate ASSERT(pp->port_state != USBSER_PORT_CLOSED);
5960Sstevel@tonic-gate
5970Sstevel@tonic-gate if (USBSER_PORT_ACCESS_OK(pp)) {
5980Sstevel@tonic-gate usbser_thr_wake(&pp->port_rq_thread);
5990Sstevel@tonic-gate }
6000Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
6010Sstevel@tonic-gate }
6020Sstevel@tonic-gate
6030Sstevel@tonic-gate return (0);
6040Sstevel@tonic-gate }
6050Sstevel@tonic-gate
6060Sstevel@tonic-gate
6070Sstevel@tonic-gate /*
6080Sstevel@tonic-gate * wput: put message on the queue and wake wq thread
6090Sstevel@tonic-gate */
6100Sstevel@tonic-gate int
usbser_wput(queue_t * q,mblk_t * mp)6110Sstevel@tonic-gate usbser_wput(queue_t *q, mblk_t *mp)
6120Sstevel@tonic-gate {
6130Sstevel@tonic-gate usbser_port_t *pp = (usbser_port_t *)q->q_ptr;
6140Sstevel@tonic-gate
6150Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wput");
6160Sstevel@tonic-gate
6170Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
6180Sstevel@tonic-gate ASSERT(pp->port_state != USBSER_PORT_CLOSED);
6190Sstevel@tonic-gate
6200Sstevel@tonic-gate /* ignore new messages if port is already closing */
6210Sstevel@tonic-gate if (pp->port_state == USBSER_PORT_CLOSING) {
6220Sstevel@tonic-gate freemsg(mp);
6230Sstevel@tonic-gate } else if (putq(q, mp)) {
6240Sstevel@tonic-gate /*
6250Sstevel@tonic-gate * this counter represents amount of tx data on the wq.
6260Sstevel@tonic-gate * each time the data is passed to DSD for transmission,
6270Sstevel@tonic-gate * the counter is decremented accordingly
6280Sstevel@tonic-gate */
6290Sstevel@tonic-gate pp->port_wq_data_cnt += msgdsize(mp);
6300Sstevel@tonic-gate } else {
6310Sstevel@tonic-gate usbser_st_tx_data_loss++;
6320Sstevel@tonic-gate }
6330Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
6340Sstevel@tonic-gate
6350Sstevel@tonic-gate return (0);
6360Sstevel@tonic-gate }
6370Sstevel@tonic-gate
6380Sstevel@tonic-gate
6390Sstevel@tonic-gate /*
6400Sstevel@tonic-gate * we need wsrv() routine to take advantage of STREAMS flow control:
6410Sstevel@tonic-gate * without it the framework will consider we are always able to process msgs
6420Sstevel@tonic-gate */
6430Sstevel@tonic-gate int
usbser_wsrv(queue_t * q)6440Sstevel@tonic-gate usbser_wsrv(queue_t *q)
6450Sstevel@tonic-gate {
6460Sstevel@tonic-gate usbser_port_t *pp = (usbser_port_t *)q->q_ptr;
6470Sstevel@tonic-gate
6480Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wsrv");
6490Sstevel@tonic-gate
6500Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
6510Sstevel@tonic-gate ASSERT(pp->port_state != USBSER_PORT_CLOSED);
6520Sstevel@tonic-gate
6530Sstevel@tonic-gate if (USBSER_PORT_ACCESS_OK(pp)) {
6540Sstevel@tonic-gate usbser_thr_wake(&pp->port_wq_thread);
6550Sstevel@tonic-gate }
6560Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
6570Sstevel@tonic-gate
6580Sstevel@tonic-gate return (0);
6590Sstevel@tonic-gate }
6600Sstevel@tonic-gate
6610Sstevel@tonic-gate
6620Sstevel@tonic-gate /*
6630Sstevel@tonic-gate * power entry point
6640Sstevel@tonic-gate */
6650Sstevel@tonic-gate int
usbser_power(dev_info_t * dip,int comp,int level)6660Sstevel@tonic-gate usbser_power(dev_info_t *dip, int comp, int level)
6670Sstevel@tonic-gate {
6680Sstevel@tonic-gate void *statep;
6690Sstevel@tonic-gate usbser_state_t *usp;
6700Sstevel@tonic-gate int new_state;
6710Sstevel@tonic-gate int rval;
6720Sstevel@tonic-gate
6730Sstevel@tonic-gate statep = ddi_get_driver_private(dip);
6740Sstevel@tonic-gate usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
6750Sstevel@tonic-gate
6760Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
6776898Sfb209375 "usbser_power: dip=0x%p, comp=%d, level=%d",
6786898Sfb209375 (void *)dip, comp, level);
6790Sstevel@tonic-gate
6800Sstevel@tonic-gate mutex_enter(&usp->us_mutex);
6810Sstevel@tonic-gate new_state = usp->us_dev_state;
6820Sstevel@tonic-gate mutex_exit(&usp->us_mutex);
6830Sstevel@tonic-gate
6840Sstevel@tonic-gate /* let DSD do the job */
6850Sstevel@tonic-gate rval = USBSER_DS_USB_POWER(usp, comp, level, &new_state);
6860Sstevel@tonic-gate
6870Sstevel@tonic-gate /* stay in sync with DSD */
6880Sstevel@tonic-gate mutex_enter(&usp->us_mutex);
6890Sstevel@tonic-gate usp->us_dev_state = new_state;
6900Sstevel@tonic-gate mutex_exit(&usp->us_mutex);
6910Sstevel@tonic-gate
6920Sstevel@tonic-gate return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
6930Sstevel@tonic-gate }
6940Sstevel@tonic-gate
6950Sstevel@tonic-gate
6960Sstevel@tonic-gate /*
6970Sstevel@tonic-gate *
6980Sstevel@tonic-gate * configuration entry point subroutines
6990Sstevel@tonic-gate * -------------------------------------
7000Sstevel@tonic-gate *
7010Sstevel@tonic-gate * rseq callback
7020Sstevel@tonic-gate */
7030Sstevel@tonic-gate static int
usbser_rseq_do_cb(rseq_t * rseq,int num,uintptr_t arg)7040Sstevel@tonic-gate usbser_rseq_do_cb(rseq_t *rseq, int num, uintptr_t arg)
7050Sstevel@tonic-gate {
7060Sstevel@tonic-gate usbser_state_t *usp = (usbser_state_t *)arg;
7070Sstevel@tonic-gate int rval = rseq[num].r_do.s_rval;
7080Sstevel@tonic-gate char *name = rseq[num].r_do.s_name;
7090Sstevel@tonic-gate
7100Sstevel@tonic-gate if (rval != DDI_SUCCESS) {
7110Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_ATTACH, usp->us_lh,
7120Sstevel@tonic-gate "do %s failed (%d)", name, rval);
7130Sstevel@tonic-gate
7140Sstevel@tonic-gate return (RSEQ_UNDO);
7150Sstevel@tonic-gate } else {
7160Sstevel@tonic-gate
7170Sstevel@tonic-gate return (RSEQ_OK);
7180Sstevel@tonic-gate }
7190Sstevel@tonic-gate }
7200Sstevel@tonic-gate
7210Sstevel@tonic-gate
7220Sstevel@tonic-gate /*
7230Sstevel@tonic-gate * free soft state
7240Sstevel@tonic-gate */
7250Sstevel@tonic-gate static int
usbser_free_soft_state(usbser_state_t * usp)7260Sstevel@tonic-gate usbser_free_soft_state(usbser_state_t *usp)
7270Sstevel@tonic-gate {
7280Sstevel@tonic-gate ddi_soft_state_free(usp->us_statep, usp->us_instance);
7290Sstevel@tonic-gate
7300Sstevel@tonic-gate return (USB_SUCCESS);
7310Sstevel@tonic-gate }
7320Sstevel@tonic-gate
7330Sstevel@tonic-gate /*
7340Sstevel@tonic-gate * init instance soft state
7350Sstevel@tonic-gate */
7360Sstevel@tonic-gate static int
usbser_init_soft_state(usbser_state_t * usp)7370Sstevel@tonic-gate usbser_init_soft_state(usbser_state_t *usp)
7380Sstevel@tonic-gate {
7390Sstevel@tonic-gate usp->us_lh = usb_alloc_log_hdl(usp->us_dip, "usbs[*].",
7405541Slg150142 &usbser_errlevel, &usbser_errmask, &usbser_instance_debug,
7415541Slg150142 0);
7420Sstevel@tonic-gate mutex_init(&usp->us_mutex, NULL, MUTEX_DRIVER, (void *)NULL);
7430Sstevel@tonic-gate
7440Sstevel@tonic-gate /* save state pointer for use in event callbacks */
7450Sstevel@tonic-gate ddi_set_driver_private(usp->us_dip, usp->us_statep);
7460Sstevel@tonic-gate
7470Sstevel@tonic-gate usp->us_dev_state = USBSER_DEV_INIT;
7480Sstevel@tonic-gate
7490Sstevel@tonic-gate return (DDI_SUCCESS);
7500Sstevel@tonic-gate }
7510Sstevel@tonic-gate
7520Sstevel@tonic-gate /*
7530Sstevel@tonic-gate * fini instance soft state
7540Sstevel@tonic-gate */
7550Sstevel@tonic-gate static int
usbser_fini_soft_state(usbser_state_t * usp)7560Sstevel@tonic-gate usbser_fini_soft_state(usbser_state_t *usp)
7570Sstevel@tonic-gate {
7580Sstevel@tonic-gate usb_free_log_hdl(usp->us_lh);
7590Sstevel@tonic-gate mutex_destroy(&usp->us_mutex);
7600Sstevel@tonic-gate ddi_set_driver_private(usp->us_dip, NULL);
7610Sstevel@tonic-gate
7620Sstevel@tonic-gate return (DDI_SUCCESS);
7630Sstevel@tonic-gate }
7640Sstevel@tonic-gate
7650Sstevel@tonic-gate /*
7660Sstevel@tonic-gate * attach entire device
7670Sstevel@tonic-gate */
7680Sstevel@tonic-gate static int
usbser_attach_dev(usbser_state_t * usp)7690Sstevel@tonic-gate usbser_attach_dev(usbser_state_t *usp)
7700Sstevel@tonic-gate {
7710Sstevel@tonic-gate ds_attach_info_t ai;
7720Sstevel@tonic-gate int rval;
7730Sstevel@tonic-gate
7740Sstevel@tonic-gate usp->us_dev_state = USB_DEV_ONLINE;
7750Sstevel@tonic-gate
7760Sstevel@tonic-gate ai.ai_dip = usp->us_dip;
7770Sstevel@tonic-gate ai.ai_usb_events = &usbser_usb_events;
7780Sstevel@tonic-gate ai.ai_hdl = &usp->us_ds_hdl;
7790Sstevel@tonic-gate ai.ai_port_cnt = &usp->us_port_cnt;
7800Sstevel@tonic-gate
7810Sstevel@tonic-gate rval = USBSER_DS_ATTACH(usp, &ai);
7820Sstevel@tonic-gate
7830Sstevel@tonic-gate if ((rval != USB_SUCCESS) || (usp->us_ds_hdl == NULL) ||
7840Sstevel@tonic-gate (usp->us_port_cnt == 0)) {
7850Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_ATTACH, usp->us_lh, "usbser_attach_dev: "
7860Sstevel@tonic-gate "failed %d %p %d", rval, usp->us_ds_hdl, usp->us_port_cnt);
7870Sstevel@tonic-gate
7880Sstevel@tonic-gate return (DDI_FAILURE);
7890Sstevel@tonic-gate }
7900Sstevel@tonic-gate
7910Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_ATTACH, usp->us_lh,
7920Sstevel@tonic-gate "usbser_attach_dev: port_cnt = %d", usp->us_port_cnt);
7930Sstevel@tonic-gate
7940Sstevel@tonic-gate return (DDI_SUCCESS);
7950Sstevel@tonic-gate }
7960Sstevel@tonic-gate
7970Sstevel@tonic-gate
7980Sstevel@tonic-gate /*
7990Sstevel@tonic-gate * detach entire device
8000Sstevel@tonic-gate */
8010Sstevel@tonic-gate static void
usbser_detach_dev(usbser_state_t * usp)8020Sstevel@tonic-gate usbser_detach_dev(usbser_state_t *usp)
8030Sstevel@tonic-gate {
8040Sstevel@tonic-gate USBSER_DS_DETACH(usp);
8050Sstevel@tonic-gate }
8060Sstevel@tonic-gate
8070Sstevel@tonic-gate
8080Sstevel@tonic-gate /*
8090Sstevel@tonic-gate * attach each individual port
8100Sstevel@tonic-gate */
8110Sstevel@tonic-gate static int
usbser_attach_ports(usbser_state_t * usp)8120Sstevel@tonic-gate usbser_attach_ports(usbser_state_t *usp)
8130Sstevel@tonic-gate {
8140Sstevel@tonic-gate int i;
8150Sstevel@tonic-gate usbser_port_t *pp;
8160Sstevel@tonic-gate ds_cb_t ds_cb;
8170Sstevel@tonic-gate
8180Sstevel@tonic-gate /*
8190Sstevel@tonic-gate * allocate port array
8200Sstevel@tonic-gate */
8210Sstevel@tonic-gate usp->us_ports = kmem_zalloc(usp->us_port_cnt *
8225541Slg150142 sizeof (usbser_port_t), KM_SLEEP);
8230Sstevel@tonic-gate
8240Sstevel@tonic-gate /* callback handlers */
8250Sstevel@tonic-gate ds_cb.cb_tx = usbser_tx_cb;
8260Sstevel@tonic-gate ds_cb.cb_rx = usbser_rx_cb;
8270Sstevel@tonic-gate ds_cb.cb_status = usbser_status_cb;
8280Sstevel@tonic-gate
8290Sstevel@tonic-gate /*
8300Sstevel@tonic-gate * initialize each port
8310Sstevel@tonic-gate */
8320Sstevel@tonic-gate for (i = 0; i < usp->us_port_cnt; i++) {
8330Sstevel@tonic-gate pp = &usp->us_ports[i];
8340Sstevel@tonic-gate
8350Sstevel@tonic-gate /*
8360Sstevel@tonic-gate * initialize data
8370Sstevel@tonic-gate */
8380Sstevel@tonic-gate pp->port_num = i;
8390Sstevel@tonic-gate pp->port_usp = usp;
8400Sstevel@tonic-gate pp->port_ds_ops = usp->us_ds_ops;
8410Sstevel@tonic-gate pp->port_ds_hdl = usp->us_ds_hdl;
8420Sstevel@tonic-gate
8430Sstevel@tonic-gate /* allocate log handle */
8440Sstevel@tonic-gate (void) sprintf(pp->port_lh_name, "usbs[%d].", i);
8450Sstevel@tonic-gate pp->port_lh = usb_alloc_log_hdl(usp->us_dip,
8465541Slg150142 pp->port_lh_name, &usbser_errlevel, &usbser_errmask,
8475541Slg150142 &usbser_instance_debug, 0);
8480Sstevel@tonic-gate
8490Sstevel@tonic-gate mutex_init(&pp->port_mutex, NULL, MUTEX_DRIVER, (void *)NULL);
8500Sstevel@tonic-gate cv_init(&pp->port_state_cv, NULL, CV_DEFAULT, NULL);
8510Sstevel@tonic-gate cv_init(&pp->port_act_cv, NULL, CV_DEFAULT, NULL);
8520Sstevel@tonic-gate cv_init(&pp->port_car_cv, NULL, CV_DEFAULT, NULL);
8530Sstevel@tonic-gate
8540Sstevel@tonic-gate /*
8550Sstevel@tonic-gate * init threads
8560Sstevel@tonic-gate */
8570Sstevel@tonic-gate pp->port_wq_thread.thr_port = pp;
8580Sstevel@tonic-gate pp->port_wq_thread.thr_func = usbser_wq_thread;
8590Sstevel@tonic-gate pp->port_wq_thread.thr_arg = (void *)&pp->port_wq_thread;
8600Sstevel@tonic-gate cv_init(&pp->port_wq_thread.thr_cv, NULL, CV_DEFAULT, NULL);
8610Sstevel@tonic-gate
8620Sstevel@tonic-gate pp->port_rq_thread.thr_port = pp;
8630Sstevel@tonic-gate pp->port_rq_thread.thr_func = usbser_rq_thread;
8640Sstevel@tonic-gate pp->port_rq_thread.thr_arg = (void *)&pp->port_rq_thread;
8650Sstevel@tonic-gate cv_init(&pp->port_rq_thread.thr_cv, NULL, CV_DEFAULT, NULL);
8660Sstevel@tonic-gate
8670Sstevel@tonic-gate /*
8680Sstevel@tonic-gate * register callbacks
8690Sstevel@tonic-gate */
8700Sstevel@tonic-gate ds_cb.cb_arg = (caddr_t)pp;
8710Sstevel@tonic-gate USBSER_DS_REGISTER_CB(usp, i, &ds_cb);
8720Sstevel@tonic-gate
8730Sstevel@tonic-gate pp->port_state = USBSER_PORT_CLOSED;
8740Sstevel@tonic-gate
8750Sstevel@tonic-gate if (usbser_create_port_minor_nodes(usp, i) != USB_SUCCESS) {
8760Sstevel@tonic-gate usbser_detach_ports(usp);
8770Sstevel@tonic-gate
8780Sstevel@tonic-gate return (DDI_FAILURE);
8790Sstevel@tonic-gate }
8800Sstevel@tonic-gate }
8810Sstevel@tonic-gate
8820Sstevel@tonic-gate return (DDI_SUCCESS);
8830Sstevel@tonic-gate }
8840Sstevel@tonic-gate
8850Sstevel@tonic-gate
8860Sstevel@tonic-gate /*
8870Sstevel@tonic-gate * create a pair of minor nodes for the port
8880Sstevel@tonic-gate */
8890Sstevel@tonic-gate static int
usbser_create_port_minor_nodes(usbser_state_t * usp,int port_num)8900Sstevel@tonic-gate usbser_create_port_minor_nodes(usbser_state_t *usp, int port_num)
8910Sstevel@tonic-gate {
8920Sstevel@tonic-gate int instance = usp->us_instance;
8930Sstevel@tonic-gate minor_t minor;
8940Sstevel@tonic-gate char name[16];
8950Sstevel@tonic-gate
8960Sstevel@tonic-gate /*
8970Sstevel@tonic-gate * tty node
8980Sstevel@tonic-gate */
8990Sstevel@tonic-gate (void) sprintf(name, "%d", port_num);
9000Sstevel@tonic-gate minor = USBSER_MAKEMINOR(instance, port_num, 0);
9010Sstevel@tonic-gate
9020Sstevel@tonic-gate if (ddi_create_minor_node(usp->us_dip, name,
9030Sstevel@tonic-gate S_IFCHR, minor, DDI_NT_SERIAL, NULL) != DDI_SUCCESS) {
9040Sstevel@tonic-gate
9050Sstevel@tonic-gate return (USB_FAILURE);
9060Sstevel@tonic-gate }
9070Sstevel@tonic-gate
9080Sstevel@tonic-gate /*
9090Sstevel@tonic-gate * dial-out node
9100Sstevel@tonic-gate */
9110Sstevel@tonic-gate (void) sprintf(name, "%d,cu", port_num);
9120Sstevel@tonic-gate minor = USBSER_MAKEMINOR(instance, port_num, OUTLINE);
9130Sstevel@tonic-gate
9140Sstevel@tonic-gate if (ddi_create_minor_node(usp->us_dip, name,
9150Sstevel@tonic-gate S_IFCHR, minor, DDI_NT_SERIAL_DO, NULL) != DDI_SUCCESS) {
9160Sstevel@tonic-gate
9170Sstevel@tonic-gate return (USB_FAILURE);
9180Sstevel@tonic-gate }
9190Sstevel@tonic-gate
9200Sstevel@tonic-gate return (USB_SUCCESS);
9210Sstevel@tonic-gate }
9220Sstevel@tonic-gate
9230Sstevel@tonic-gate
9240Sstevel@tonic-gate /*
9250Sstevel@tonic-gate * detach each port individually
9260Sstevel@tonic-gate */
9270Sstevel@tonic-gate static void
usbser_detach_ports(usbser_state_t * usp)9280Sstevel@tonic-gate usbser_detach_ports(usbser_state_t *usp)
9290Sstevel@tonic-gate {
9300Sstevel@tonic-gate int i;
9310Sstevel@tonic-gate int sz;
9320Sstevel@tonic-gate usbser_port_t *pp;
9330Sstevel@tonic-gate
9340Sstevel@tonic-gate /*
9350Sstevel@tonic-gate * remove all minor nodes
9360Sstevel@tonic-gate */
9370Sstevel@tonic-gate ddi_remove_minor_node(usp->us_dip, NULL);
9380Sstevel@tonic-gate
9390Sstevel@tonic-gate for (i = 0; i < usp->us_port_cnt; i++) {
9400Sstevel@tonic-gate pp = &usp->us_ports[i];
9410Sstevel@tonic-gate
9420Sstevel@tonic-gate if (pp->port_state != USBSER_PORT_CLOSED) {
9430Sstevel@tonic-gate ASSERT(pp->port_state == USBSER_PORT_NOT_INIT);
9440Sstevel@tonic-gate
9450Sstevel@tonic-gate continue;
9460Sstevel@tonic-gate }
9470Sstevel@tonic-gate
9480Sstevel@tonic-gate USBSER_DS_UNREGISTER_CB(usp, i);
9490Sstevel@tonic-gate
9500Sstevel@tonic-gate mutex_destroy(&pp->port_mutex);
9510Sstevel@tonic-gate cv_destroy(&pp->port_state_cv);
9520Sstevel@tonic-gate cv_destroy(&pp->port_act_cv);
9530Sstevel@tonic-gate cv_destroy(&pp->port_car_cv);
9540Sstevel@tonic-gate
9550Sstevel@tonic-gate cv_destroy(&pp->port_wq_thread.thr_cv);
9560Sstevel@tonic-gate cv_destroy(&pp->port_rq_thread.thr_cv);
9570Sstevel@tonic-gate
9580Sstevel@tonic-gate usb_free_log_hdl(pp->port_lh);
9590Sstevel@tonic-gate }
9600Sstevel@tonic-gate
9610Sstevel@tonic-gate /*
9620Sstevel@tonic-gate * free memory
9630Sstevel@tonic-gate */
9640Sstevel@tonic-gate sz = usp->us_port_cnt * sizeof (usbser_port_t);
9650Sstevel@tonic-gate kmem_free(usp->us_ports, sz);
9660Sstevel@tonic-gate usp->us_ports = NULL;
9670Sstevel@tonic-gate }
9680Sstevel@tonic-gate
9690Sstevel@tonic-gate
9700Sstevel@tonic-gate /*
9710Sstevel@tonic-gate * create a taskq with two threads per port (read and write sides)
9720Sstevel@tonic-gate */
9730Sstevel@tonic-gate static int
usbser_create_taskq(usbser_state_t * usp)9740Sstevel@tonic-gate usbser_create_taskq(usbser_state_t *usp)
9750Sstevel@tonic-gate {
9760Sstevel@tonic-gate int nthr = usp->us_port_cnt * 2;
9770Sstevel@tonic-gate
9780Sstevel@tonic-gate usp->us_taskq = ddi_taskq_create(usp->us_dip, "usbser_taskq",
9790Sstevel@tonic-gate nthr, TASKQ_DEFAULTPRI, 0);
9800Sstevel@tonic-gate
9810Sstevel@tonic-gate return ((usp->us_taskq == NULL) ? DDI_FAILURE : DDI_SUCCESS);
9820Sstevel@tonic-gate }
9830Sstevel@tonic-gate
9840Sstevel@tonic-gate
9850Sstevel@tonic-gate static void
usbser_destroy_taskq(usbser_state_t * usp)9860Sstevel@tonic-gate usbser_destroy_taskq(usbser_state_t *usp)
9870Sstevel@tonic-gate {
9880Sstevel@tonic-gate ddi_taskq_destroy(usp->us_taskq);
9890Sstevel@tonic-gate }
9900Sstevel@tonic-gate
9910Sstevel@tonic-gate
9920Sstevel@tonic-gate static void
usbser_set_dev_state_init(usbser_state_t * usp)9930Sstevel@tonic-gate usbser_set_dev_state_init(usbser_state_t *usp)
9940Sstevel@tonic-gate {
9950Sstevel@tonic-gate mutex_enter(&usp->us_mutex);
9960Sstevel@tonic-gate usp->us_dev_state = USBSER_DEV_INIT;
9970Sstevel@tonic-gate mutex_exit(&usp->us_mutex);
9980Sstevel@tonic-gate }
9990Sstevel@tonic-gate
10000Sstevel@tonic-gate /*
10010Sstevel@tonic-gate * hotplugging and power management
10020Sstevel@tonic-gate * ---------------------------------
10030Sstevel@tonic-gate *
10040Sstevel@tonic-gate * disconnect event callback
10050Sstevel@tonic-gate */
10060Sstevel@tonic-gate /*ARGSUSED*/
10070Sstevel@tonic-gate static int
usbser_disconnect_cb(dev_info_t * dip)10080Sstevel@tonic-gate usbser_disconnect_cb(dev_info_t *dip)
10090Sstevel@tonic-gate {
10100Sstevel@tonic-gate void *statep;
10110Sstevel@tonic-gate usbser_state_t *usp;
10120Sstevel@tonic-gate
10130Sstevel@tonic-gate statep = ddi_get_driver_private(dip);
10140Sstevel@tonic-gate usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
10150Sstevel@tonic-gate
10160Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
10176898Sfb209375 "usbser_disconnect_cb: dip=%p", (void *)dip);
10180Sstevel@tonic-gate
10190Sstevel@tonic-gate mutex_enter(&usp->us_mutex);
10205541Slg150142 switch (usp->us_dev_state) {
10215541Slg150142 case USB_DEV_ONLINE:
10225541Slg150142 case USB_DEV_PWRED_DOWN:
10235541Slg150142 /* prevent further activity */
10245541Slg150142 usp->us_dev_state = USB_DEV_DISCONNECTED;
10250Sstevel@tonic-gate mutex_exit(&usp->us_mutex);
10260Sstevel@tonic-gate
10275541Slg150142 /* see if any of the ports are open and do necessary handling */
10285541Slg150142 usbser_disconnect_ports(usp);
10295541Slg150142
10305541Slg150142 /* call DSD to do any necessary work */
10315541Slg150142 if (USBSER_DS_DISCONNECT(usp) != USB_DEV_DISCONNECTED) {
10325541Slg150142 USB_DPRINTF_L2(DPRINT_EVENTS, usp->us_lh,
10335541Slg150142 "usbser_disconnect_cb: ds_disconnect failed");
10345541Slg150142 }
10355541Slg150142
10365541Slg150142 break;
10375541Slg150142 case USB_DEV_SUSPENDED:
10385541Slg150142 /* we remain suspended */
10395541Slg150142 default:
10405541Slg150142 mutex_exit(&usp->us_mutex);
10415541Slg150142
10425541Slg150142 break;
10430Sstevel@tonic-gate }
10440Sstevel@tonic-gate
10450Sstevel@tonic-gate return (USB_SUCCESS);
10460Sstevel@tonic-gate }
10470Sstevel@tonic-gate
10480Sstevel@tonic-gate
10490Sstevel@tonic-gate /*
10500Sstevel@tonic-gate * reconnect event callback
10510Sstevel@tonic-gate */
10520Sstevel@tonic-gate /*ARGSUSED*/
10530Sstevel@tonic-gate static int
usbser_reconnect_cb(dev_info_t * dip)10540Sstevel@tonic-gate usbser_reconnect_cb(dev_info_t *dip)
10550Sstevel@tonic-gate {
10560Sstevel@tonic-gate void *statep;
10570Sstevel@tonic-gate usbser_state_t *usp;
10580Sstevel@tonic-gate
10590Sstevel@tonic-gate statep = ddi_get_driver_private(dip);
10600Sstevel@tonic-gate usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
10610Sstevel@tonic-gate
10620Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
10636898Sfb209375 "usbser_reconnect_cb: dip=%p", (void *)dip);
10640Sstevel@tonic-gate
10650Sstevel@tonic-gate (void) usbser_restore_device_state(usp);
10660Sstevel@tonic-gate
10670Sstevel@tonic-gate return (USB_SUCCESS);
10680Sstevel@tonic-gate }
10690Sstevel@tonic-gate
10700Sstevel@tonic-gate
10710Sstevel@tonic-gate /*
10720Sstevel@tonic-gate * if any of the ports is open during disconnect,
10730Sstevel@tonic-gate * send M_HANGUP message upstream and log a warning
10740Sstevel@tonic-gate */
10750Sstevel@tonic-gate static void
usbser_disconnect_ports(usbser_state_t * usp)10760Sstevel@tonic-gate usbser_disconnect_ports(usbser_state_t *usp)
10770Sstevel@tonic-gate {
10780Sstevel@tonic-gate usbser_port_t *pp;
10790Sstevel@tonic-gate queue_t *rq;
10800Sstevel@tonic-gate int complain = 0;
10810Sstevel@tonic-gate int hangup = 0;
10820Sstevel@tonic-gate timeout_id_t delay_id = 0;
10830Sstevel@tonic-gate int i;
10840Sstevel@tonic-gate
10850Sstevel@tonic-gate if (usp->us_ports == NULL) {
10860Sstevel@tonic-gate return;
10870Sstevel@tonic-gate }
10880Sstevel@tonic-gate
10890Sstevel@tonic-gate for (i = 0; i < usp->us_port_cnt; i++) {
10900Sstevel@tonic-gate pp = &usp->us_ports[i];
10910Sstevel@tonic-gate
10920Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
10930Sstevel@tonic-gate if (pp->port_state == USBSER_PORT_OPEN ||
10940Sstevel@tonic-gate USBSER_IS_OPENING(pp) ||
10950Sstevel@tonic-gate pp->port_state == USBSER_PORT_CLOSING) {
10960Sstevel@tonic-gate complain = 1;
10970Sstevel@tonic-gate }
10980Sstevel@tonic-gate
10990Sstevel@tonic-gate if (pp->port_state == USBSER_PORT_OPEN) {
11000Sstevel@tonic-gate rq = pp->port_ttycommon.t_readq;
11010Sstevel@tonic-gate
11020Sstevel@tonic-gate /*
11030Sstevel@tonic-gate * hangup the stream; will send actual
11040Sstevel@tonic-gate * M_HANGUP message after releasing mutex
11050Sstevel@tonic-gate */
11060Sstevel@tonic-gate pp->port_flags |= USBSER_FL_HUNGUP;
11070Sstevel@tonic-gate hangup = 1;
11080Sstevel@tonic-gate
11090Sstevel@tonic-gate /*
11100Sstevel@tonic-gate * cancel all activities
11110Sstevel@tonic-gate */
11120Sstevel@tonic-gate usbser_release_port_act(pp, USBSER_ACT_ALL);
11130Sstevel@tonic-gate
11140Sstevel@tonic-gate delay_id = pp->port_delay_id;
11150Sstevel@tonic-gate pp->port_delay_id = 0;
11160Sstevel@tonic-gate
11170Sstevel@tonic-gate /* mark disconnected */
11180Sstevel@tonic-gate pp->port_state = USBSER_PORT_DISCONNECTED;
11190Sstevel@tonic-gate cv_broadcast(&pp->port_state_cv);
11200Sstevel@tonic-gate }
11210Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
11220Sstevel@tonic-gate
11230Sstevel@tonic-gate if (hangup) {
11240Sstevel@tonic-gate (void) putnextctl(rq, M_HANGUP);
11250Sstevel@tonic-gate hangup = 0;
11260Sstevel@tonic-gate }
11270Sstevel@tonic-gate
11280Sstevel@tonic-gate /*
11290Sstevel@tonic-gate * we couldn't untimeout while holding the mutex - do it now
11300Sstevel@tonic-gate */
11310Sstevel@tonic-gate if (delay_id) {
11320Sstevel@tonic-gate (void) untimeout(delay_id);
11330Sstevel@tonic-gate delay_id = 0;
11340Sstevel@tonic-gate }
11350Sstevel@tonic-gate }
11360Sstevel@tonic-gate
11370Sstevel@tonic-gate /*
11380Sstevel@tonic-gate * complain about disconnecting device while open
11390Sstevel@tonic-gate */
11400Sstevel@tonic-gate if (complain) {
11410Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_EVENTS, usp->us_lh, "device was "
11420Sstevel@tonic-gate "disconnected while open. Data may have been lost");
11430Sstevel@tonic-gate }
11440Sstevel@tonic-gate }
11450Sstevel@tonic-gate
11460Sstevel@tonic-gate
11470Sstevel@tonic-gate /*
11480Sstevel@tonic-gate * do CPR suspend
11490Sstevel@tonic-gate *
11500Sstevel@tonic-gate * We use a trivial CPR strategy - fail if any of the device's ports are open.
11510Sstevel@tonic-gate * The problem with more sophisticated strategies is that each open port uses
11520Sstevel@tonic-gate * two threads that sit in the loop until the port is closed, while CPR has to
11530Sstevel@tonic-gate * stop all kernel threads to succeed. Stopping port threads is a rather
11540Sstevel@tonic-gate * intrusive and delicate procedure; I leave it as an RFE for now.
11550Sstevel@tonic-gate *
11560Sstevel@tonic-gate */
11570Sstevel@tonic-gate static int
usbser_cpr_suspend(dev_info_t * dip)11580Sstevel@tonic-gate usbser_cpr_suspend(dev_info_t *dip)
11590Sstevel@tonic-gate {
11600Sstevel@tonic-gate void *statep;
11610Sstevel@tonic-gate usbser_state_t *usp;
11620Sstevel@tonic-gate int new_state;
11630Sstevel@tonic-gate int rval;
11640Sstevel@tonic-gate
11650Sstevel@tonic-gate statep = ddi_get_driver_private(dip);
11660Sstevel@tonic-gate usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
11670Sstevel@tonic-gate
11680Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_EVENTS, usp->us_lh, "usbser_cpr_suspend");
11690Sstevel@tonic-gate
11700Sstevel@tonic-gate /* suspend each port first */
11710Sstevel@tonic-gate if (usbser_suspend_ports(usp) != USB_SUCCESS) {
11720Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
11730Sstevel@tonic-gate "usbser_cpr_suspend: GSD failure");
11740Sstevel@tonic-gate
11750Sstevel@tonic-gate return (USB_FAILURE);
11760Sstevel@tonic-gate }
11770Sstevel@tonic-gate
11780Sstevel@tonic-gate new_state = USBSER_DS_SUSPEND(usp); /* let DSD do its part */
11790Sstevel@tonic-gate
11800Sstevel@tonic-gate mutex_enter(&usp->us_mutex);
11810Sstevel@tonic-gate if (new_state == USB_DEV_SUSPENDED) {
11820Sstevel@tonic-gate rval = USB_SUCCESS;
11830Sstevel@tonic-gate } else {
11840Sstevel@tonic-gate ASSERT(new_state == USB_DEV_ONLINE);
11850Sstevel@tonic-gate rval = USB_FAILURE;
11860Sstevel@tonic-gate }
11870Sstevel@tonic-gate usp->us_dev_state = new_state;
11880Sstevel@tonic-gate mutex_exit(&usp->us_mutex);
11890Sstevel@tonic-gate
11900Sstevel@tonic-gate return (rval);
11910Sstevel@tonic-gate }
11920Sstevel@tonic-gate
11930Sstevel@tonic-gate
11940Sstevel@tonic-gate static int
usbser_suspend_ports(usbser_state_t * usp)11950Sstevel@tonic-gate usbser_suspend_ports(usbser_state_t *usp)
11960Sstevel@tonic-gate {
11970Sstevel@tonic-gate usbser_port_t *pp;
11980Sstevel@tonic-gate int i;
11990Sstevel@tonic-gate
12000Sstevel@tonic-gate for (i = 0; i < usp->us_port_cnt; i++) {
12010Sstevel@tonic-gate pp = &usp->us_ports[i];
12020Sstevel@tonic-gate
12030Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
12040Sstevel@tonic-gate if (pp->port_state != USBSER_PORT_CLOSED) {
12050Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
12060Sstevel@tonic-gate
12070Sstevel@tonic-gate return (USB_FAILURE);
12080Sstevel@tonic-gate }
12090Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
12100Sstevel@tonic-gate }
12110Sstevel@tonic-gate
12120Sstevel@tonic-gate return (USB_SUCCESS);
12130Sstevel@tonic-gate }
12140Sstevel@tonic-gate
12150Sstevel@tonic-gate
12160Sstevel@tonic-gate /*
12170Sstevel@tonic-gate * do CPR resume
12180Sstevel@tonic-gate *
12190Sstevel@tonic-gate * DSD will return USB_DEV_ONLINE in case of success
12200Sstevel@tonic-gate */
12210Sstevel@tonic-gate static void
usbser_cpr_resume(dev_info_t * dip)12220Sstevel@tonic-gate usbser_cpr_resume(dev_info_t *dip)
12230Sstevel@tonic-gate {
12240Sstevel@tonic-gate void *statep;
12250Sstevel@tonic-gate usbser_state_t *usp;
12260Sstevel@tonic-gate
12270Sstevel@tonic-gate statep = ddi_get_driver_private(dip);
12280Sstevel@tonic-gate usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
12290Sstevel@tonic-gate
12300Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh, "usbser_cpr_resume");
12310Sstevel@tonic-gate
12320Sstevel@tonic-gate (void) usbser_restore_device_state(usp);
12330Sstevel@tonic-gate }
12340Sstevel@tonic-gate
12350Sstevel@tonic-gate
12360Sstevel@tonic-gate /*
12370Sstevel@tonic-gate * restore device state after CPR resume or reconnect
12380Sstevel@tonic-gate */
12390Sstevel@tonic-gate static int
usbser_restore_device_state(usbser_state_t * usp)12400Sstevel@tonic-gate usbser_restore_device_state(usbser_state_t *usp)
12410Sstevel@tonic-gate {
12420Sstevel@tonic-gate int new_state, current_state;
12430Sstevel@tonic-gate
12445541Slg150142 /* needed as power up state of dev is "unknown" to system */
12455541Slg150142 (void) pm_busy_component(usp->us_dip, 0);
12465541Slg150142 (void) pm_raise_power(usp->us_dip, 0, USB_DEV_OS_FULL_PWR);
12475541Slg150142
12480Sstevel@tonic-gate mutex_enter(&usp->us_mutex);
12490Sstevel@tonic-gate current_state = usp->us_dev_state;
12500Sstevel@tonic-gate mutex_exit(&usp->us_mutex);
12510Sstevel@tonic-gate
12520Sstevel@tonic-gate ASSERT((current_state == USB_DEV_DISCONNECTED) ||
12535541Slg150142 (current_state == USB_DEV_SUSPENDED));
12540Sstevel@tonic-gate
12550Sstevel@tonic-gate /*
12560Sstevel@tonic-gate * call DSD to perform device-specific work
12570Sstevel@tonic-gate */
12580Sstevel@tonic-gate if (current_state == USB_DEV_DISCONNECTED) {
12590Sstevel@tonic-gate new_state = USBSER_DS_RECONNECT(usp);
12600Sstevel@tonic-gate } else {
12610Sstevel@tonic-gate new_state = USBSER_DS_RESUME(usp);
12620Sstevel@tonic-gate }
12630Sstevel@tonic-gate
12640Sstevel@tonic-gate mutex_enter(&usp->us_mutex);
12650Sstevel@tonic-gate usp->us_dev_state = new_state;
12660Sstevel@tonic-gate mutex_exit(&usp->us_mutex);
12670Sstevel@tonic-gate
12680Sstevel@tonic-gate if (new_state == USB_DEV_ONLINE) {
12690Sstevel@tonic-gate /*
12700Sstevel@tonic-gate * restore ports state
12710Sstevel@tonic-gate */
12720Sstevel@tonic-gate usbser_restore_ports_state(usp);
12730Sstevel@tonic-gate }
12740Sstevel@tonic-gate
12755541Slg150142 (void) pm_idle_component(usp->us_dip, 0);
12765541Slg150142
12770Sstevel@tonic-gate return (USB_SUCCESS);
12780Sstevel@tonic-gate }
12790Sstevel@tonic-gate
12800Sstevel@tonic-gate
12810Sstevel@tonic-gate /*
12820Sstevel@tonic-gate * restore ports state after device reconnect/resume
12830Sstevel@tonic-gate */
12840Sstevel@tonic-gate static void
usbser_restore_ports_state(usbser_state_t * usp)12850Sstevel@tonic-gate usbser_restore_ports_state(usbser_state_t *usp)
12860Sstevel@tonic-gate {
12870Sstevel@tonic-gate usbser_port_t *pp;
12880Sstevel@tonic-gate queue_t *rq;
12890Sstevel@tonic-gate int i;
12900Sstevel@tonic-gate
12910Sstevel@tonic-gate for (i = 0; i < usp->us_port_cnt; i++) {
12920Sstevel@tonic-gate pp = &usp->us_ports[i];
12930Sstevel@tonic-gate
12940Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
12950Sstevel@tonic-gate /*
12960Sstevel@tonic-gate * only care about ports that are open
12970Sstevel@tonic-gate */
12980Sstevel@tonic-gate if ((pp->port_state != USBSER_PORT_SUSPENDED) &&
12990Sstevel@tonic-gate (pp->port_state != USBSER_PORT_DISCONNECTED)) {
13000Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
13010Sstevel@tonic-gate
13020Sstevel@tonic-gate continue;
13030Sstevel@tonic-gate }
13040Sstevel@tonic-gate
13050Sstevel@tonic-gate pp->port_state = USBSER_PORT_OPEN;
13060Sstevel@tonic-gate
13070Sstevel@tonic-gate /*
13080Sstevel@tonic-gate * if the stream was hung up during disconnect, restore it
13090Sstevel@tonic-gate */
13100Sstevel@tonic-gate if (pp->port_flags & USBSER_FL_HUNGUP) {
13110Sstevel@tonic-gate pp->port_flags &= ~USBSER_FL_HUNGUP;
13120Sstevel@tonic-gate rq = pp->port_ttycommon.t_readq;
13130Sstevel@tonic-gate
13140Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
13150Sstevel@tonic-gate (void) putnextctl(rq, M_UNHANGUP);
13160Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
13170Sstevel@tonic-gate }
13180Sstevel@tonic-gate
13190Sstevel@tonic-gate /*
13200Sstevel@tonic-gate * restore serial parameters
13210Sstevel@tonic-gate */
13220Sstevel@tonic-gate (void) usbser_port_program(pp);
13230Sstevel@tonic-gate
13240Sstevel@tonic-gate /*
13250Sstevel@tonic-gate * wake anything that might be sleeping
13260Sstevel@tonic-gate */
13270Sstevel@tonic-gate cv_broadcast(&pp->port_state_cv);
13280Sstevel@tonic-gate cv_broadcast(&pp->port_act_cv);
13290Sstevel@tonic-gate usbser_thr_wake(&pp->port_wq_thread);
13300Sstevel@tonic-gate usbser_thr_wake(&pp->port_rq_thread);
13310Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
13320Sstevel@tonic-gate }
13330Sstevel@tonic-gate }
13340Sstevel@tonic-gate
13350Sstevel@tonic-gate
13360Sstevel@tonic-gate /*
13370Sstevel@tonic-gate * STREAMS subroutines
13380Sstevel@tonic-gate * -------------------
13390Sstevel@tonic-gate *
13400Sstevel@tonic-gate *
13410Sstevel@tonic-gate * port open state machine
13420Sstevel@tonic-gate *
13430Sstevel@tonic-gate * here's a list of things that the driver has to do while open;
13440Sstevel@tonic-gate * because device can be opened any number of times,
13450Sstevel@tonic-gate * initial open has additional responsibilities:
13460Sstevel@tonic-gate *
13470Sstevel@tonic-gate * if (initial_open) {
13480Sstevel@tonic-gate * initialize soft state; \
13490Sstevel@tonic-gate * DSD open; - see usbser_open_init()
13500Sstevel@tonic-gate * dispatch threads; /
13510Sstevel@tonic-gate * }
13520Sstevel@tonic-gate * raise DTR;
13530Sstevel@tonic-gate * wait for carrier (if necessary);
13540Sstevel@tonic-gate *
13550Sstevel@tonic-gate * we should also take into consideration that two threads can try to open
13560Sstevel@tonic-gate * the same physical port simultaneously (/dev/term/N and /dev/cua/N).
13570Sstevel@tonic-gate *
13580Sstevel@tonic-gate * return values:
13590Sstevel@tonic-gate * 0 - success;
13600Sstevel@tonic-gate * >0 - fail with this error code;
13610Sstevel@tonic-gate */
13620Sstevel@tonic-gate static int
usbser_open_setup(queue_t * rq,usbser_port_t * pp,int minor,int flag,cred_t * cr)13630Sstevel@tonic-gate usbser_open_setup(queue_t *rq, usbser_port_t *pp, int minor, int flag,
13640Sstevel@tonic-gate cred_t *cr)
13650Sstevel@tonic-gate {
13660Sstevel@tonic-gate int rval = USBSER_CONTINUE;
13670Sstevel@tonic-gate
13680Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
13690Sstevel@tonic-gate /*
13700Sstevel@tonic-gate * refer to port state diagram in the header file
13710Sstevel@tonic-gate */
13720Sstevel@tonic-gate loop:
13730Sstevel@tonic-gate switch (pp->port_state) {
13740Sstevel@tonic-gate case USBSER_PORT_CLOSED:
13750Sstevel@tonic-gate /*
13760Sstevel@tonic-gate * initial open
13770Sstevel@tonic-gate */
13780Sstevel@tonic-gate rval = usbser_open_init(pp, minor);
13790Sstevel@tonic-gate
13800Sstevel@tonic-gate break;
13810Sstevel@tonic-gate case USBSER_PORT_OPENING_TTY:
13820Sstevel@tonic-gate /*
13830Sstevel@tonic-gate * dial-out thread can overtake the port
13840Sstevel@tonic-gate * if tty open thread is sleeping waiting for carrier
13850Sstevel@tonic-gate */
13860Sstevel@tonic-gate if ((minor & OUTLINE) && (pp->port_flags & USBSER_FL_WOPEN)) {
13870Sstevel@tonic-gate pp->port_state = USBSER_PORT_OPENING_OUT;
13880Sstevel@tonic-gate
13890Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_OPEN, pp->port_lh,
13900Sstevel@tonic-gate "usbser_open_state: overtake");
13910Sstevel@tonic-gate }
13920Sstevel@tonic-gate
13930Sstevel@tonic-gate /* FALLTHRU */
13940Sstevel@tonic-gate case USBSER_PORT_OPENING_OUT:
13950Sstevel@tonic-gate /*
13960Sstevel@tonic-gate * if no other open in progress, setup the line
13970Sstevel@tonic-gate */
13980Sstevel@tonic-gate if (USBSER_NO_OTHER_OPEN(pp, minor)) {
13990Sstevel@tonic-gate rval = usbser_open_line_setup(pp, minor, flag);
14000Sstevel@tonic-gate
14010Sstevel@tonic-gate break;
14020Sstevel@tonic-gate }
14030Sstevel@tonic-gate
14040Sstevel@tonic-gate /* FALLTHRU */
14050Sstevel@tonic-gate case USBSER_PORT_CLOSING:
14060Sstevel@tonic-gate /*
14070Sstevel@tonic-gate * wait until close active phase ends
14080Sstevel@tonic-gate */
14090Sstevel@tonic-gate if (cv_wait_sig(&pp->port_state_cv, &pp->port_mutex) == 0) {
14100Sstevel@tonic-gate rval = EINTR;
14110Sstevel@tonic-gate }
14120Sstevel@tonic-gate
14130Sstevel@tonic-gate break;
14140Sstevel@tonic-gate case USBSER_PORT_OPEN:
14150Sstevel@tonic-gate if ((pp->port_ttycommon.t_flags & TS_XCLUDE) &&
14165541Slg150142 secpolicy_excl_open(cr) != 0) {
14170Sstevel@tonic-gate /*
14180Sstevel@tonic-gate * exclusive use
14190Sstevel@tonic-gate */
14200Sstevel@tonic-gate rval = EBUSY;
14210Sstevel@tonic-gate } else if (USBSER_OPEN_IN_OTHER_MODE(pp, minor)) {
14220Sstevel@tonic-gate /*
14230Sstevel@tonic-gate * tty and dial-out modes are mutually exclusive
14240Sstevel@tonic-gate */
14250Sstevel@tonic-gate rval = EBUSY;
14260Sstevel@tonic-gate } else {
14270Sstevel@tonic-gate /*
14280Sstevel@tonic-gate * port is being re-open in the same mode
14290Sstevel@tonic-gate */
14300Sstevel@tonic-gate rval = usbser_open_line_setup(pp, minor, flag);
14310Sstevel@tonic-gate }
14320Sstevel@tonic-gate
14330Sstevel@tonic-gate break;
14340Sstevel@tonic-gate default:
14350Sstevel@tonic-gate rval = ENXIO;
14360Sstevel@tonic-gate
14370Sstevel@tonic-gate break;
14380Sstevel@tonic-gate }
14390Sstevel@tonic-gate
14400Sstevel@tonic-gate if (rval == USBSER_CONTINUE) {
14410Sstevel@tonic-gate
14420Sstevel@tonic-gate goto loop;
14430Sstevel@tonic-gate }
14440Sstevel@tonic-gate
14450Sstevel@tonic-gate /*
14460Sstevel@tonic-gate * initial open requires additional handling
14470Sstevel@tonic-gate */
14480Sstevel@tonic-gate if (USBSER_IS_OPENING(pp)) {
14490Sstevel@tonic-gate if (rval == USBSER_COMPLETE) {
14500Sstevel@tonic-gate if (pp->port_state == USBSER_PORT_OPENING_OUT) {
14510Sstevel@tonic-gate pp->port_flags |= USBSER_FL_OUT;
14520Sstevel@tonic-gate }
14530Sstevel@tonic-gate pp->port_state = USBSER_PORT_OPEN;
14540Sstevel@tonic-gate cv_broadcast(&pp->port_state_cv);
14550Sstevel@tonic-gate
14560Sstevel@tonic-gate usbser_open_queues_init(pp, rq);
14570Sstevel@tonic-gate } else {
14580Sstevel@tonic-gate usbser_open_fini(pp);
14590Sstevel@tonic-gate }
14600Sstevel@tonic-gate }
14610Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
14620Sstevel@tonic-gate
14630Sstevel@tonic-gate return (rval);
14640Sstevel@tonic-gate }
14650Sstevel@tonic-gate
14660Sstevel@tonic-gate
14670Sstevel@tonic-gate /*
14680Sstevel@tonic-gate * initialize the port when opened for the first time
14690Sstevel@tonic-gate */
14700Sstevel@tonic-gate static int
usbser_open_init(usbser_port_t * pp,int minor)14710Sstevel@tonic-gate usbser_open_init(usbser_port_t *pp, int minor)
14720Sstevel@tonic-gate {
14730Sstevel@tonic-gate usbser_state_t *usp = pp->port_usp;
14740Sstevel@tonic-gate tty_common_t *tp = &pp->port_ttycommon;
14750Sstevel@tonic-gate int rval = ENXIO;
14760Sstevel@tonic-gate
14770Sstevel@tonic-gate ASSERT(pp->port_state == USBSER_PORT_CLOSED);
14780Sstevel@tonic-gate
14790Sstevel@tonic-gate /*
14800Sstevel@tonic-gate * init state
14810Sstevel@tonic-gate */
14820Sstevel@tonic-gate pp->port_act = 0;
14830Sstevel@tonic-gate pp->port_flags &= USBSER_FL_PRESERVE;
14840Sstevel@tonic-gate pp->port_flowc = '\0';
14850Sstevel@tonic-gate pp->port_wq_data_cnt = 0;
14860Sstevel@tonic-gate
14870Sstevel@tonic-gate if (minor & OUTLINE) {
14880Sstevel@tonic-gate pp->port_state = USBSER_PORT_OPENING_OUT;
14890Sstevel@tonic-gate } else {
14900Sstevel@tonic-gate pp->port_state = USBSER_PORT_OPENING_TTY;
14910Sstevel@tonic-gate }
14920Sstevel@tonic-gate
14930Sstevel@tonic-gate /*
14940Sstevel@tonic-gate * init termios settings
14950Sstevel@tonic-gate */
14960Sstevel@tonic-gate tp->t_iflag = 0;
14970Sstevel@tonic-gate tp->t_iocpending = NULL;
14980Sstevel@tonic-gate tp->t_size.ws_row = tp->t_size.ws_col = 0;
14990Sstevel@tonic-gate tp->t_size.ws_xpixel = tp->t_size.ws_ypixel = 0;
15000Sstevel@tonic-gate tp->t_startc = CSTART;
15010Sstevel@tonic-gate tp->t_stopc = CSTOP;
15020Sstevel@tonic-gate
15030Sstevel@tonic-gate usbser_check_port_props(pp);
15040Sstevel@tonic-gate
15050Sstevel@tonic-gate /*
15060Sstevel@tonic-gate * dispatch wq and rq threads:
15070Sstevel@tonic-gate * although queues are not enabled at this point,
15080Sstevel@tonic-gate * we will need wq to run status processing callback
15090Sstevel@tonic-gate */
15100Sstevel@tonic-gate usbser_thr_dispatch(&pp->port_wq_thread);
15110Sstevel@tonic-gate usbser_thr_dispatch(&pp->port_rq_thread);
15120Sstevel@tonic-gate
15130Sstevel@tonic-gate /*
15140Sstevel@tonic-gate * open DSD port
15150Sstevel@tonic-gate */
15160Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
15170Sstevel@tonic-gate rval = USBSER_DS_OPEN_PORT(usp, pp->port_num);
15180Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
15190Sstevel@tonic-gate
15200Sstevel@tonic-gate if (rval != USB_SUCCESS) {
15210Sstevel@tonic-gate
15220Sstevel@tonic-gate return (ENXIO);
15230Sstevel@tonic-gate }
15240Sstevel@tonic-gate pp->port_flags |= USBSER_FL_DSD_OPEN;
15250Sstevel@tonic-gate
15260Sstevel@tonic-gate /*
15270Sstevel@tonic-gate * program port with default parameters
15280Sstevel@tonic-gate */
15290Sstevel@tonic-gate if ((rval = usbser_port_program(pp)) != 0) {
15300Sstevel@tonic-gate
15310Sstevel@tonic-gate return (ENXIO);
15320Sstevel@tonic-gate }
15330Sstevel@tonic-gate
15340Sstevel@tonic-gate return (USBSER_CONTINUE);
15350Sstevel@tonic-gate }
15360Sstevel@tonic-gate
15370Sstevel@tonic-gate
15380Sstevel@tonic-gate /*
15390Sstevel@tonic-gate * create a pair of minor nodes for the port
15400Sstevel@tonic-gate */
15410Sstevel@tonic-gate static void
usbser_check_port_props(usbser_port_t * pp)15420Sstevel@tonic-gate usbser_check_port_props(usbser_port_t *pp)
15430Sstevel@tonic-gate {
15440Sstevel@tonic-gate dev_info_t *dip = pp->port_usp->us_dip;
15450Sstevel@tonic-gate tty_common_t *tp = &pp->port_ttycommon;
15466898Sfb209375 struct termios *termiosp;
15470Sstevel@tonic-gate uint_t len;
15480Sstevel@tonic-gate char name[20];
15490Sstevel@tonic-gate
15500Sstevel@tonic-gate /*
15510Sstevel@tonic-gate * take default modes from "ttymodes" property if it exists
15520Sstevel@tonic-gate */
15530Sstevel@tonic-gate if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ddi_root_node(), 0,
15540Sstevel@tonic-gate "ttymodes", (uchar_t **)&termiosp, &len) == DDI_PROP_SUCCESS) {
15550Sstevel@tonic-gate
15560Sstevel@tonic-gate if (len == sizeof (struct termios)) {
15570Sstevel@tonic-gate tp->t_cflag = termiosp->c_cflag;
15580Sstevel@tonic-gate
15590Sstevel@tonic-gate if (termiosp->c_iflag & (IXON | IXANY)) {
15600Sstevel@tonic-gate tp->t_iflag =
15615541Slg150142 termiosp->c_iflag & (IXON | IXANY);
15620Sstevel@tonic-gate tp->t_startc = termiosp->c_cc[VSTART];
15630Sstevel@tonic-gate tp->t_stopc = termiosp->c_cc[VSTOP];
15640Sstevel@tonic-gate }
15650Sstevel@tonic-gate }
15660Sstevel@tonic-gate ddi_prop_free(termiosp);
15670Sstevel@tonic-gate }
15680Sstevel@tonic-gate
15690Sstevel@tonic-gate /*
15700Sstevel@tonic-gate * look for "ignore-cd" or "port-N-ignore-cd" property
15710Sstevel@tonic-gate */
15720Sstevel@tonic-gate (void) sprintf(name, "port-%d-ignore-cd", pp->port_num);
15730Sstevel@tonic-gate if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
15740Sstevel@tonic-gate "ignore-cd", 0) ||
15750Sstevel@tonic-gate ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, name, 0)) {
15760Sstevel@tonic-gate pp->port_flags |= USBSER_FL_IGNORE_CD;
15770Sstevel@tonic-gate } else {
15780Sstevel@tonic-gate pp->port_flags &= ~USBSER_FL_IGNORE_CD;
15790Sstevel@tonic-gate }
15800Sstevel@tonic-gate }
15810Sstevel@tonic-gate
15820Sstevel@tonic-gate
15830Sstevel@tonic-gate /*
15840Sstevel@tonic-gate * undo what was done in usbser_open_init()
15850Sstevel@tonic-gate */
15860Sstevel@tonic-gate static void
usbser_open_fini(usbser_port_t * pp)15870Sstevel@tonic-gate usbser_open_fini(usbser_port_t *pp)
15880Sstevel@tonic-gate {
15890Sstevel@tonic-gate uint_t port_num = pp->port_num;
15900Sstevel@tonic-gate usbser_state_t *usp = pp->port_usp;
15910Sstevel@tonic-gate
15920Sstevel@tonic-gate /*
15930Sstevel@tonic-gate * close DSD if it is open
15940Sstevel@tonic-gate */
15950Sstevel@tonic-gate if (pp->port_flags & USBSER_FL_DSD_OPEN) {
15960Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
15970Sstevel@tonic-gate if (USBSER_DS_CLOSE_PORT(usp, port_num) != USB_SUCCESS) {
15980Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_CLOSE, pp->port_lh,
15990Sstevel@tonic-gate "usbser_open_fini: CLOSE_PORT fail");
16000Sstevel@tonic-gate }
16010Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
16020Sstevel@tonic-gate }
16030Sstevel@tonic-gate
16040Sstevel@tonic-gate /*
16050Sstevel@tonic-gate * cancel threads
16060Sstevel@tonic-gate */
16070Sstevel@tonic-gate usbser_thr_cancel(&pp->port_wq_thread);
16080Sstevel@tonic-gate usbser_thr_cancel(&pp->port_rq_thread);
16090Sstevel@tonic-gate
16100Sstevel@tonic-gate /*
16110Sstevel@tonic-gate * unpdate soft state
16120Sstevel@tonic-gate */
16130Sstevel@tonic-gate pp->port_state = USBSER_PORT_CLOSED;
16140Sstevel@tonic-gate cv_broadcast(&pp->port_state_cv);
16150Sstevel@tonic-gate cv_broadcast(&pp->port_car_cv);
16160Sstevel@tonic-gate }
16170Sstevel@tonic-gate
16180Sstevel@tonic-gate
16190Sstevel@tonic-gate /*
16200Sstevel@tonic-gate * setup serial line
16210Sstevel@tonic-gate */
16220Sstevel@tonic-gate static int
usbser_open_line_setup(usbser_port_t * pp,int minor,int flag)16230Sstevel@tonic-gate usbser_open_line_setup(usbser_port_t *pp, int minor, int flag)
16240Sstevel@tonic-gate {
16250Sstevel@tonic-gate int rval;
16260Sstevel@tonic-gate
16270Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
16280Sstevel@tonic-gate /*
16290Sstevel@tonic-gate * prevent opening a disconnected device
16300Sstevel@tonic-gate */
16310Sstevel@tonic-gate if (!usbser_dev_is_online(pp->port_usp)) {
16320Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
16330Sstevel@tonic-gate
16340Sstevel@tonic-gate return (ENXIO);
16350Sstevel@tonic-gate }
16360Sstevel@tonic-gate
16370Sstevel@tonic-gate /* raise DTR on every open */
16380Sstevel@tonic-gate (void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR, TIOCM_DTR);
16390Sstevel@tonic-gate
16400Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
16410Sstevel@tonic-gate /*
16420Sstevel@tonic-gate * check carrier
16430Sstevel@tonic-gate */
16440Sstevel@tonic-gate rval = usbser_open_carrier_check(pp, minor, flag);
16450Sstevel@tonic-gate
16460Sstevel@tonic-gate return (rval);
16470Sstevel@tonic-gate }
16480Sstevel@tonic-gate
16490Sstevel@tonic-gate
16500Sstevel@tonic-gate /*
16510Sstevel@tonic-gate * check carrier and wait if needed
16520Sstevel@tonic-gate */
16530Sstevel@tonic-gate static int
usbser_open_carrier_check(usbser_port_t * pp,int minor,int flag)16540Sstevel@tonic-gate usbser_open_carrier_check(usbser_port_t *pp, int minor, int flag)
16550Sstevel@tonic-gate {
16560Sstevel@tonic-gate tty_common_t *tp = &pp->port_ttycommon;
16570Sstevel@tonic-gate int val = 0;
16580Sstevel@tonic-gate int rval;
16590Sstevel@tonic-gate
16600Sstevel@tonic-gate if (pp->port_flags & USBSER_FL_IGNORE_CD) {
16610Sstevel@tonic-gate tp->t_flags |= TS_SOFTCAR;
16620Sstevel@tonic-gate }
16630Sstevel@tonic-gate
16640Sstevel@tonic-gate /*
16650Sstevel@tonic-gate * check carrier
16660Sstevel@tonic-gate */
16670Sstevel@tonic-gate if (tp->t_flags & TS_SOFTCAR) {
16680Sstevel@tonic-gate pp->port_flags |= USBSER_FL_CARR_ON;
16690Sstevel@tonic-gate } else if (USBSER_DS_GET_MODEM_CTL(pp, TIOCM_CD, &val) != USB_SUCCESS) {
16700Sstevel@tonic-gate
16710Sstevel@tonic-gate return (ENXIO);
16720Sstevel@tonic-gate } else if (val & TIOCM_CD) {
16730Sstevel@tonic-gate pp->port_flags |= USBSER_FL_CARR_ON;
16740Sstevel@tonic-gate } else {
16750Sstevel@tonic-gate pp->port_flags &= ~USBSER_FL_CARR_ON;
16760Sstevel@tonic-gate }
16770Sstevel@tonic-gate
16780Sstevel@tonic-gate /*
16790Sstevel@tonic-gate * don't block if 1) not allowed to, 2) this is a local device,
16800Sstevel@tonic-gate * 3) opening in dial-out mode, or 4) carrier is already on
16810Sstevel@tonic-gate */
16820Sstevel@tonic-gate if ((flag & (FNDELAY | FNONBLOCK)) || (tp->t_cflag & CLOCAL) ||
16830Sstevel@tonic-gate (minor & OUTLINE) || (pp->port_flags & USBSER_FL_CARR_ON)) {
16840Sstevel@tonic-gate
16850Sstevel@tonic-gate return (USBSER_COMPLETE);
16860Sstevel@tonic-gate }
16870Sstevel@tonic-gate
16880Sstevel@tonic-gate /*
16890Sstevel@tonic-gate * block until carrier up (only in tty mode)
16900Sstevel@tonic-gate */
16910Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_OPEN, pp->port_lh,
16920Sstevel@tonic-gate "usbser_open_carrier_check: waiting for carrier...");
16930Sstevel@tonic-gate
16940Sstevel@tonic-gate pp->port_flags |= USBSER_FL_WOPEN;
16950Sstevel@tonic-gate
16960Sstevel@tonic-gate rval = cv_wait_sig(&pp->port_car_cv, &pp->port_mutex);
16970Sstevel@tonic-gate
16980Sstevel@tonic-gate pp->port_flags &= ~USBSER_FL_WOPEN;
16990Sstevel@tonic-gate
17000Sstevel@tonic-gate if (rval == 0) {
17010Sstevel@tonic-gate /*
17020Sstevel@tonic-gate * interrupted with a signal
17030Sstevel@tonic-gate */
17040Sstevel@tonic-gate return (EINTR);
17050Sstevel@tonic-gate } else {
17060Sstevel@tonic-gate /*
17070Sstevel@tonic-gate * try again
17080Sstevel@tonic-gate */
17090Sstevel@tonic-gate return (USBSER_CONTINUE);
17100Sstevel@tonic-gate }
17110Sstevel@tonic-gate }
17120Sstevel@tonic-gate
17130Sstevel@tonic-gate
17140Sstevel@tonic-gate /*
17150Sstevel@tonic-gate * during open, setup queues and message processing
17160Sstevel@tonic-gate */
17170Sstevel@tonic-gate static void
usbser_open_queues_init(usbser_port_t * pp,queue_t * rq)17180Sstevel@tonic-gate usbser_open_queues_init(usbser_port_t *pp, queue_t *rq)
17190Sstevel@tonic-gate {
17200Sstevel@tonic-gate pp->port_ttycommon.t_readq = rq;
17210Sstevel@tonic-gate pp->port_ttycommon.t_writeq = WR(rq);
17220Sstevel@tonic-gate rq->q_ptr = WR(rq)->q_ptr = (caddr_t)pp;
17230Sstevel@tonic-gate
17240Sstevel@tonic-gate qprocson(rq);
17250Sstevel@tonic-gate }
17260Sstevel@tonic-gate
17270Sstevel@tonic-gate
17280Sstevel@tonic-gate /*
17290Sstevel@tonic-gate * clean up queues and message processing
17300Sstevel@tonic-gate */
17310Sstevel@tonic-gate static void
usbser_open_queues_fini(usbser_port_t * pp)17320Sstevel@tonic-gate usbser_open_queues_fini(usbser_port_t *pp)
17330Sstevel@tonic-gate {
17340Sstevel@tonic-gate queue_t *rq = pp->port_ttycommon.t_readq;
17350Sstevel@tonic-gate
17360Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
17370Sstevel@tonic-gate /*
17380Sstevel@tonic-gate * clean up queues
17390Sstevel@tonic-gate */
17400Sstevel@tonic-gate qprocsoff(rq);
17410Sstevel@tonic-gate
17420Sstevel@tonic-gate /*
17430Sstevel@tonic-gate * free unused messages
17440Sstevel@tonic-gate */
17450Sstevel@tonic-gate flushq(rq, FLUSHALL);
17460Sstevel@tonic-gate flushq(WR(rq), FLUSHALL);
17470Sstevel@tonic-gate
17480Sstevel@tonic-gate rq->q_ptr = WR(rq)->q_ptr = NULL;
17490Sstevel@tonic-gate ttycommon_close(&pp->port_ttycommon);
17500Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
17510Sstevel@tonic-gate }
17520Sstevel@tonic-gate
17530Sstevel@tonic-gate
17540Sstevel@tonic-gate /*
17550Sstevel@tonic-gate * during close, wait until pending data is gone or the signal is sent
17560Sstevel@tonic-gate */
17570Sstevel@tonic-gate static void
usbser_close_drain(usbser_port_t * pp)17580Sstevel@tonic-gate usbser_close_drain(usbser_port_t *pp)
17590Sstevel@tonic-gate {
17600Sstevel@tonic-gate int need_drain;
17610Sstevel@tonic-gate clock_t until;
17620Sstevel@tonic-gate int rval;
17630Sstevel@tonic-gate
17640Sstevel@tonic-gate /*
17650Sstevel@tonic-gate * port_wq_data_cnt indicates amount of data on the write queue,
17660Sstevel@tonic-gate * which becomes zero when all data is submitted to DSD. But usbser
17670Sstevel@tonic-gate * stays busy until it gets tx callback from DSD, signalling that
17680Sstevel@tonic-gate * data has been sent over USB. To be continued in the next comment...
17690Sstevel@tonic-gate */
17700Sstevel@tonic-gate until = ddi_get_lbolt() +
17715541Slg150142 drv_usectohz(USBSER_WQ_DRAIN_TIMEOUT * 1000000);
17720Sstevel@tonic-gate
17730Sstevel@tonic-gate while ((pp->port_wq_data_cnt > 0) && USBSER_PORT_IS_BUSY(pp)) {
17740Sstevel@tonic-gate if ((rval = cv_timedwait_sig(&pp->port_act_cv, &pp->port_mutex,
17750Sstevel@tonic-gate until)) <= 0) {
17760Sstevel@tonic-gate
17770Sstevel@tonic-gate break;
17780Sstevel@tonic-gate }
17790Sstevel@tonic-gate }
17800Sstevel@tonic-gate
17810Sstevel@tonic-gate /* don't drain if timed out or received a signal */
17820Sstevel@tonic-gate need_drain = (pp->port_wq_data_cnt == 0) || !USBSER_PORT_IS_BUSY(pp) ||
17835541Slg150142 (rval != 0);
17840Sstevel@tonic-gate
17850Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
17860Sstevel@tonic-gate /*
17870Sstevel@tonic-gate * Once the data reaches USB serial box, it may still be stored in its
17880Sstevel@tonic-gate * internal output buffer (FIFO). We call DSD drain to ensure that all
17890Sstevel@tonic-gate * the data is transmitted transmitted over the serial line.
17900Sstevel@tonic-gate */
17910Sstevel@tonic-gate if (need_drain) {
17920Sstevel@tonic-gate rval = USBSER_DS_FIFO_DRAIN(pp, USBSER_TX_FIFO_DRAIN_TIMEOUT);
17930Sstevel@tonic-gate if (rval != USB_SUCCESS) {
17940Sstevel@tonic-gate (void) USBSER_DS_FIFO_FLUSH(pp, DS_TX);
17950Sstevel@tonic-gate }
17960Sstevel@tonic-gate } else {
17970Sstevel@tonic-gate (void) USBSER_DS_FIFO_FLUSH(pp, DS_TX);
17980Sstevel@tonic-gate }
17990Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
18000Sstevel@tonic-gate }
18010Sstevel@tonic-gate
18020Sstevel@tonic-gate
18030Sstevel@tonic-gate /*
18040Sstevel@tonic-gate * during close, cancel break/delay
18050Sstevel@tonic-gate */
18060Sstevel@tonic-gate static void
usbser_close_cancel_break(usbser_port_t * pp)18070Sstevel@tonic-gate usbser_close_cancel_break(usbser_port_t *pp)
18080Sstevel@tonic-gate {
18090Sstevel@tonic-gate timeout_id_t delay_id;
18100Sstevel@tonic-gate
18110Sstevel@tonic-gate if (pp->port_act & USBSER_ACT_BREAK) {
18120Sstevel@tonic-gate delay_id = pp->port_delay_id;
18130Sstevel@tonic-gate pp->port_delay_id = 0;
18140Sstevel@tonic-gate
18150Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
18160Sstevel@tonic-gate (void) untimeout(delay_id);
18170Sstevel@tonic-gate (void) USBSER_DS_BREAK_CTL(pp, DS_OFF);
18180Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
18190Sstevel@tonic-gate
18200Sstevel@tonic-gate pp->port_act &= ~USBSER_ACT_BREAK;
18210Sstevel@tonic-gate }
18220Sstevel@tonic-gate }
18230Sstevel@tonic-gate
18240Sstevel@tonic-gate
18250Sstevel@tonic-gate /*
18260Sstevel@tonic-gate * during close, drop RTS/DTR if necessary
18270Sstevel@tonic-gate */
18280Sstevel@tonic-gate static void
usbser_close_hangup(usbser_port_t * pp)18290Sstevel@tonic-gate usbser_close_hangup(usbser_port_t *pp)
18300Sstevel@tonic-gate {
18310Sstevel@tonic-gate /*
18320Sstevel@tonic-gate * drop DTR and RTS if HUPCL is set
18330Sstevel@tonic-gate */
18340Sstevel@tonic-gate if (pp->port_ttycommon.t_cflag & HUPCL) {
18350Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
18360Sstevel@tonic-gate (void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_RTS | TIOCM_DTR, 0);
18370Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
18380Sstevel@tonic-gate }
18390Sstevel@tonic-gate }
18400Sstevel@tonic-gate
18410Sstevel@tonic-gate
18420Sstevel@tonic-gate /*
18430Sstevel@tonic-gate * state cleanup during close
18440Sstevel@tonic-gate */
18450Sstevel@tonic-gate static void
usbser_close_cleanup(usbser_port_t * pp)18460Sstevel@tonic-gate usbser_close_cleanup(usbser_port_t *pp)
18470Sstevel@tonic-gate {
18480Sstevel@tonic-gate usbser_open_queues_fini(pp);
18490Sstevel@tonic-gate
18500Sstevel@tonic-gate usbser_open_fini(pp);
18510Sstevel@tonic-gate }
18520Sstevel@tonic-gate
18530Sstevel@tonic-gate
18540Sstevel@tonic-gate /*
18550Sstevel@tonic-gate *
18560Sstevel@tonic-gate * thread management
18570Sstevel@tonic-gate * -----------------
18580Sstevel@tonic-gate *
18590Sstevel@tonic-gate *
18600Sstevel@tonic-gate * dispatch a thread
18610Sstevel@tonic-gate */
18620Sstevel@tonic-gate static void
usbser_thr_dispatch(usbser_thread_t * thr)18630Sstevel@tonic-gate usbser_thr_dispatch(usbser_thread_t *thr)
18640Sstevel@tonic-gate {
18650Sstevel@tonic-gate usbser_port_t *pp = thr->thr_port;
18660Sstevel@tonic-gate usbser_state_t *usp = pp->port_usp;
18670Sstevel@tonic-gate int rval;
18680Sstevel@tonic-gate
18690Sstevel@tonic-gate ASSERT(mutex_owned(&pp->port_mutex));
18700Sstevel@tonic-gate ASSERT((thr->thr_flags & USBSER_THR_RUNNING) == 0);
18710Sstevel@tonic-gate
18720Sstevel@tonic-gate thr->thr_flags = USBSER_THR_RUNNING;
18730Sstevel@tonic-gate
18740Sstevel@tonic-gate rval = ddi_taskq_dispatch(usp->us_taskq, thr->thr_func, thr->thr_arg,
18755541Slg150142 DDI_SLEEP);
18760Sstevel@tonic-gate ASSERT(rval == DDI_SUCCESS);
18770Sstevel@tonic-gate }
18780Sstevel@tonic-gate
18790Sstevel@tonic-gate
18800Sstevel@tonic-gate /*
18810Sstevel@tonic-gate * cancel a thread
18820Sstevel@tonic-gate */
18830Sstevel@tonic-gate static void
usbser_thr_cancel(usbser_thread_t * thr)18840Sstevel@tonic-gate usbser_thr_cancel(usbser_thread_t *thr)
18850Sstevel@tonic-gate {
18860Sstevel@tonic-gate usbser_port_t *pp = thr->thr_port;
18870Sstevel@tonic-gate
18880Sstevel@tonic-gate ASSERT(mutex_owned(&pp->port_mutex));
18890Sstevel@tonic-gate
18900Sstevel@tonic-gate thr->thr_flags &= ~USBSER_THR_RUNNING;
18910Sstevel@tonic-gate cv_signal(&thr->thr_cv);
18920Sstevel@tonic-gate
18930Sstevel@tonic-gate /* wait until the thread actually exits */
18940Sstevel@tonic-gate do {
18951150Syz147069 cv_wait(&thr->thr_cv, &pp->port_mutex);
18961150Syz147069
18970Sstevel@tonic-gate } while ((thr->thr_flags & USBSER_THR_EXITED) == 0);
18980Sstevel@tonic-gate }
18990Sstevel@tonic-gate
19000Sstevel@tonic-gate
19010Sstevel@tonic-gate /*
19020Sstevel@tonic-gate * wake thread
19030Sstevel@tonic-gate */
19040Sstevel@tonic-gate static void
usbser_thr_wake(usbser_thread_t * thr)19050Sstevel@tonic-gate usbser_thr_wake(usbser_thread_t *thr)
19060Sstevel@tonic-gate {
19070Sstevel@tonic-gate usbser_port_t *pp = thr->thr_port;
19080Sstevel@tonic-gate
19090Sstevel@tonic-gate ASSERT(mutex_owned(&pp->port_mutex));
19100Sstevel@tonic-gate
19110Sstevel@tonic-gate thr->thr_flags |= USBSER_THR_WAKE;
19120Sstevel@tonic-gate cv_signal(&thr->thr_cv);
19130Sstevel@tonic-gate }
19140Sstevel@tonic-gate
19150Sstevel@tonic-gate
19160Sstevel@tonic-gate /*
19170Sstevel@tonic-gate * thread handling write queue requests
19180Sstevel@tonic-gate */
19190Sstevel@tonic-gate static void
usbser_wq_thread(void * arg)19200Sstevel@tonic-gate usbser_wq_thread(void *arg)
19210Sstevel@tonic-gate {
19220Sstevel@tonic-gate usbser_thread_t *thr = (usbser_thread_t *)arg;
19230Sstevel@tonic-gate usbser_port_t *pp = thr->thr_port;
19240Sstevel@tonic-gate
19250Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wq_thread: enter");
19260Sstevel@tonic-gate
19270Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
19280Sstevel@tonic-gate while (thr->thr_flags & USBSER_THR_RUNNING) {
19290Sstevel@tonic-gate /*
19300Sstevel@tonic-gate * when woken, see what we should do
19310Sstevel@tonic-gate */
19320Sstevel@tonic-gate if (thr->thr_flags & USBSER_THR_WAKE) {
19330Sstevel@tonic-gate thr->thr_flags &= ~USBSER_THR_WAKE;
19340Sstevel@tonic-gate
19350Sstevel@tonic-gate /*
19360Sstevel@tonic-gate * status callback pending?
19370Sstevel@tonic-gate */
19380Sstevel@tonic-gate if (pp->port_flags & USBSER_FL_STATUS_CB) {
19390Sstevel@tonic-gate usbser_status_proc_cb(pp);
19400Sstevel@tonic-gate }
19410Sstevel@tonic-gate
19420Sstevel@tonic-gate usbser_wmsg(pp);
19430Sstevel@tonic-gate } else {
19440Sstevel@tonic-gate /*
19450Sstevel@tonic-gate * sleep until woken up to do some work, e.g:
19460Sstevel@tonic-gate * - new message arrives;
19470Sstevel@tonic-gate * - data transmit completes;
19480Sstevel@tonic-gate * - status callback pending;
19490Sstevel@tonic-gate * - wq thread is cancelled;
19500Sstevel@tonic-gate */
19510Sstevel@tonic-gate cv_wait(&thr->thr_cv, &pp->port_mutex);
19520Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh,
19530Sstevel@tonic-gate "usbser_wq_thread: wakeup");
19540Sstevel@tonic-gate }
19550Sstevel@tonic-gate }
19560Sstevel@tonic-gate thr->thr_flags |= USBSER_THR_EXITED;
19570Sstevel@tonic-gate cv_signal(&thr->thr_cv);
19580Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wq_thread: exit");
19590Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
19600Sstevel@tonic-gate }
19610Sstevel@tonic-gate
19620Sstevel@tonic-gate
19630Sstevel@tonic-gate /*
19640Sstevel@tonic-gate * thread handling read queue requests
19650Sstevel@tonic-gate */
19660Sstevel@tonic-gate static void
usbser_rq_thread(void * arg)19670Sstevel@tonic-gate usbser_rq_thread(void *arg)
19680Sstevel@tonic-gate {
19690Sstevel@tonic-gate usbser_thread_t *thr = (usbser_thread_t *)arg;
19700Sstevel@tonic-gate usbser_port_t *pp = thr->thr_port;
19710Sstevel@tonic-gate
19720Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_rq_thread: enter");
19730Sstevel@tonic-gate
19740Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
19750Sstevel@tonic-gate while (thr->thr_flags & USBSER_THR_RUNNING) {
19760Sstevel@tonic-gate /*
19770Sstevel@tonic-gate * read service routine will wake us when
19780Sstevel@tonic-gate * more space is available on the read queue
19790Sstevel@tonic-gate */
19800Sstevel@tonic-gate if (thr->thr_flags & USBSER_THR_WAKE) {
19810Sstevel@tonic-gate thr->thr_flags &= ~USBSER_THR_WAKE;
19820Sstevel@tonic-gate
19830Sstevel@tonic-gate /*
19840Sstevel@tonic-gate * don't process messages until queue is enabled
19850Sstevel@tonic-gate */
19860Sstevel@tonic-gate if (!pp->port_ttycommon.t_readq) {
19870Sstevel@tonic-gate
19880Sstevel@tonic-gate continue;
19890Sstevel@tonic-gate }
19900Sstevel@tonic-gate
19910Sstevel@tonic-gate /*
19920Sstevel@tonic-gate * check whether we need to resume receive
19930Sstevel@tonic-gate */
19940Sstevel@tonic-gate if (pp->port_flags & USBSER_FL_RX_STOPPED) {
19950Sstevel@tonic-gate pp->port_flowc = pp->port_ttycommon.t_startc;
19960Sstevel@tonic-gate usbser_inbound_flow_ctl(pp);
19970Sstevel@tonic-gate }
19980Sstevel@tonic-gate
19990Sstevel@tonic-gate /*
20000Sstevel@tonic-gate * grab more data if available
20010Sstevel@tonic-gate */
20020Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
20030Sstevel@tonic-gate usbser_rx_cb((caddr_t)pp);
20040Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
20050Sstevel@tonic-gate } else {
20060Sstevel@tonic-gate cv_wait(&thr->thr_cv, &pp->port_mutex);
20070Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh,
20080Sstevel@tonic-gate "usbser_rq_thread: wakeup");
20090Sstevel@tonic-gate }
20100Sstevel@tonic-gate }
20110Sstevel@tonic-gate thr->thr_flags |= USBSER_THR_EXITED;
20120Sstevel@tonic-gate cv_signal(&thr->thr_cv);
20130Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_RQ, pp->port_lh, "usbser_rq_thread: exit");
20140Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
20150Sstevel@tonic-gate }
20160Sstevel@tonic-gate
20170Sstevel@tonic-gate
20180Sstevel@tonic-gate /*
20190Sstevel@tonic-gate * DSD callbacks
20200Sstevel@tonic-gate * -------------
20210Sstevel@tonic-gate *
20220Sstevel@tonic-gate * Note: to avoid deadlocks with DSD, these callbacks
20230Sstevel@tonic-gate * should not call DSD functions that can block.
20240Sstevel@tonic-gate *
20250Sstevel@tonic-gate *
20260Sstevel@tonic-gate * transmit callback
20270Sstevel@tonic-gate *
20280Sstevel@tonic-gate * invoked by DSD when the last byte of data is transmitted over USB
20290Sstevel@tonic-gate */
20300Sstevel@tonic-gate static void
usbser_tx_cb(caddr_t arg)20310Sstevel@tonic-gate usbser_tx_cb(caddr_t arg)
20320Sstevel@tonic-gate {
20330Sstevel@tonic-gate usbser_port_t *pp = (usbser_port_t *)arg;
20340Sstevel@tonic-gate int online;
20350Sstevel@tonic-gate
20360Sstevel@tonic-gate online = usbser_dev_is_online(pp->port_usp);
20370Sstevel@tonic-gate
20380Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
20390Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_TX_CB, pp->port_lh,
20406898Sfb209375 "usbser_tx_cb: act=%x curthread=%p", pp->port_act,
20416898Sfb209375 (void *)curthread);
20420Sstevel@tonic-gate
20430Sstevel@tonic-gate usbser_release_port_act(pp, USBSER_ACT_TX);
20440Sstevel@tonic-gate
204510061SRaymond.Chen@Sun.COM /*
204610061SRaymond.Chen@Sun.COM * as long as port access is ok and the port is not busy on
204710061SRaymond.Chen@Sun.COM * TX, break, ctrl or delay, the wq_thread should be waken
204810061SRaymond.Chen@Sun.COM * to do further process for next message
204910061SRaymond.Chen@Sun.COM */
205010061SRaymond.Chen@Sun.COM if (online && USBSER_PORT_ACCESS_OK(pp) &&
205110061SRaymond.Chen@Sun.COM !USBSER_PORT_IS_BUSY_NON_RX(pp)) {
20520Sstevel@tonic-gate /*
20530Sstevel@tonic-gate * wake wq thread for further data/ioctl processing
20540Sstevel@tonic-gate */
20550Sstevel@tonic-gate usbser_thr_wake(&pp->port_wq_thread);
20560Sstevel@tonic-gate }
20570Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
20580Sstevel@tonic-gate }
20590Sstevel@tonic-gate
20600Sstevel@tonic-gate
20610Sstevel@tonic-gate /*
20620Sstevel@tonic-gate * receive callback
20630Sstevel@tonic-gate *
20640Sstevel@tonic-gate * invoked by DSD when there is more data for us to pick
20650Sstevel@tonic-gate */
20660Sstevel@tonic-gate static void
usbser_rx_cb(caddr_t arg)20670Sstevel@tonic-gate usbser_rx_cb(caddr_t arg)
20680Sstevel@tonic-gate {
20690Sstevel@tonic-gate usbser_port_t *pp = (usbser_port_t *)arg;
20700Sstevel@tonic-gate queue_t *rq, *wq;
20710Sstevel@tonic-gate mblk_t *mp; /* current mblk */
20720Sstevel@tonic-gate mblk_t *data, *data_tail; /* M_DATA mblk list and its tail */
20730Sstevel@tonic-gate mblk_t *emp; /* error (M_BREAK) mblk */
20740Sstevel@tonic-gate
20750Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh, "usbser_rx_cb");
20760Sstevel@tonic-gate
20770Sstevel@tonic-gate if (!usbser_dev_is_online(pp->port_usp)) {
20780Sstevel@tonic-gate
20790Sstevel@tonic-gate return;
20800Sstevel@tonic-gate }
20810Sstevel@tonic-gate
20820Sstevel@tonic-gate /* get data from DSD */
20830Sstevel@tonic-gate if ((mp = USBSER_DS_RX(pp)) == NULL) {
20840Sstevel@tonic-gate
20850Sstevel@tonic-gate return;
20860Sstevel@tonic-gate }
20870Sstevel@tonic-gate
20880Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
20890Sstevel@tonic-gate if ((!USBSER_PORT_ACCESS_OK(pp)) ||
20900Sstevel@tonic-gate ((pp->port_ttycommon.t_cflag & CREAD) == 0)) {
20910Sstevel@tonic-gate freemsg(mp);
20920Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
20930Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_RX_CB, pp->port_lh,
20940Sstevel@tonic-gate "usbser_rx_cb: access not ok or receiver disabled");
20950Sstevel@tonic-gate
20960Sstevel@tonic-gate return;
20970Sstevel@tonic-gate }
20980Sstevel@tonic-gate
20990Sstevel@tonic-gate usbser_serialize_port_act(pp, USBSER_ACT_RX);
21000Sstevel@tonic-gate
21010Sstevel@tonic-gate rq = pp->port_ttycommon.t_readq;
21020Sstevel@tonic-gate wq = pp->port_ttycommon.t_writeq;
21030Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
21040Sstevel@tonic-gate
21050Sstevel@tonic-gate /*
21060Sstevel@tonic-gate * DSD data is a b_cont-linked list of M_DATA and M_BREAK blocks.
21070Sstevel@tonic-gate * M_DATA is correctly received data.
21080Sstevel@tonic-gate * M_BREAK is a character with either framing or parity error.
21090Sstevel@tonic-gate *
21100Sstevel@tonic-gate * this loop runs through the list of mblks. when it meets an M_BREAK,
21110Sstevel@tonic-gate * it sends all leading M_DATA's in one shot, then sends M_BREAK.
21120Sstevel@tonic-gate * in the trivial case when list contains only M_DATA's, the loop
21130Sstevel@tonic-gate * does nothing but set data variable.
21140Sstevel@tonic-gate */
21150Sstevel@tonic-gate data = data_tail = NULL;
21160Sstevel@tonic-gate while (mp) {
21170Sstevel@tonic-gate /*
21180Sstevel@tonic-gate * skip data until we meet M_BREAK or end of list
21190Sstevel@tonic-gate */
21200Sstevel@tonic-gate if (DB_TYPE(mp) == M_DATA) {
21210Sstevel@tonic-gate if (data == NULL) {
21220Sstevel@tonic-gate data = mp;
21230Sstevel@tonic-gate }
21240Sstevel@tonic-gate data_tail = mp;
21250Sstevel@tonic-gate mp = mp->b_cont;
21260Sstevel@tonic-gate
21270Sstevel@tonic-gate continue;
21280Sstevel@tonic-gate }
21290Sstevel@tonic-gate
21300Sstevel@tonic-gate /* detach data list from mp */
21310Sstevel@tonic-gate if (data_tail) {
21320Sstevel@tonic-gate data_tail->b_cont = NULL;
21330Sstevel@tonic-gate }
21340Sstevel@tonic-gate /* detach emp from the list */
21350Sstevel@tonic-gate emp = mp;
21360Sstevel@tonic-gate mp = mp->b_cont;
21370Sstevel@tonic-gate emp->b_cont = NULL;
21380Sstevel@tonic-gate
21390Sstevel@tonic-gate /* DSD shouldn't send anything but M_DATA or M_BREAK */
21400Sstevel@tonic-gate if ((DB_TYPE(emp) != M_BREAK) || (MBLKL(emp) != 2)) {
21410Sstevel@tonic-gate freemsg(emp);
21420Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_RX_CB, pp->port_lh,
21430Sstevel@tonic-gate "usbser_rx_cb: bad message");
21440Sstevel@tonic-gate
21450Sstevel@tonic-gate continue;
21460Sstevel@tonic-gate }
21470Sstevel@tonic-gate
21480Sstevel@tonic-gate /*
21490Sstevel@tonic-gate * first tweak and send M_DATA's
21500Sstevel@tonic-gate */
21510Sstevel@tonic-gate if (data) {
21520Sstevel@tonic-gate usbser_rx_massage_data(pp, data);
21530Sstevel@tonic-gate usbser_rx_cb_put(pp, rq, wq, data);
21540Sstevel@tonic-gate data = data_tail = NULL;
21550Sstevel@tonic-gate }
21560Sstevel@tonic-gate
21570Sstevel@tonic-gate /*
21580Sstevel@tonic-gate * now tweak and send M_BREAK
21590Sstevel@tonic-gate */
21600Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
21610Sstevel@tonic-gate usbser_rx_massage_mbreak(pp, emp);
21620Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
21630Sstevel@tonic-gate usbser_rx_cb_put(pp, rq, wq, emp);
21640Sstevel@tonic-gate }
21650Sstevel@tonic-gate
21660Sstevel@tonic-gate /* send the rest of the data, if any */
21670Sstevel@tonic-gate if (data) {
21680Sstevel@tonic-gate usbser_rx_massage_data(pp, data);
21690Sstevel@tonic-gate usbser_rx_cb_put(pp, rq, wq, data);
21700Sstevel@tonic-gate }
21710Sstevel@tonic-gate
21720Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
21730Sstevel@tonic-gate usbser_release_port_act(pp, USBSER_ACT_RX);
21740Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
21750Sstevel@tonic-gate }
21760Sstevel@tonic-gate
21770Sstevel@tonic-gate /*
21780Sstevel@tonic-gate * the joys of termio -- this is to accomodate Unix98 assertion:
21790Sstevel@tonic-gate *
21800Sstevel@tonic-gate * If PARENB is supported and is set, when PARMRK is set, and CSIZE is
21810Sstevel@tonic-gate * set to CS8, and IGNPAR is clear, and ISTRIP is clear, a valid
21820Sstevel@tonic-gate * character of '\377' is read as '\377', '\377'.
21830Sstevel@tonic-gate *
21840Sstevel@tonic-gate * Posix Ref: Assertion 7.1.2.2-16(C)
21850Sstevel@tonic-gate *
21860Sstevel@tonic-gate * this requires the driver to scan every incoming valid character
21870Sstevel@tonic-gate */
21880Sstevel@tonic-gate static void
usbser_rx_massage_data(usbser_port_t * pp,mblk_t * mp)21890Sstevel@tonic-gate usbser_rx_massage_data(usbser_port_t *pp, mblk_t *mp)
21900Sstevel@tonic-gate {
21910Sstevel@tonic-gate tty_common_t *tp = &pp->port_ttycommon;
21920Sstevel@tonic-gate uchar_t *p;
21930Sstevel@tonic-gate mblk_t *newmp;
21940Sstevel@tonic-gate int tailsz;
21950Sstevel@tonic-gate
21960Sstevel@tonic-gate /* avoid scanning if possible */
21970Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
21980Sstevel@tonic-gate if (!((tp->t_cflag & PARENB) && (tp->t_iflag & PARMRK) &&
21990Sstevel@tonic-gate ((tp->t_cflag & CSIZE) == CS8) &&
22000Sstevel@tonic-gate ((tp->t_iflag & (IGNPAR|ISTRIP)) == 0))) {
22010Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
22020Sstevel@tonic-gate
22030Sstevel@tonic-gate return;
22040Sstevel@tonic-gate }
22050Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
22060Sstevel@tonic-gate
22070Sstevel@tonic-gate while (mp) {
22080Sstevel@tonic-gate for (p = mp->b_rptr; p < mp->b_wptr; ) {
22090Sstevel@tonic-gate if (*p++ != 0377) {
22100Sstevel@tonic-gate
22110Sstevel@tonic-gate continue;
22120Sstevel@tonic-gate }
22130Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh,
22146898Sfb209375 "usbser_rx_massage_data: mp=%p off=%ld(%ld)",
22157492SZhigang.Lu@Sun.COM (void *)mp, _PTRDIFF(p, mp->b_rptr) - 1,
22166898Sfb209375 (long)MBLKL(mp));
22170Sstevel@tonic-gate
22180Sstevel@tonic-gate /*
22190Sstevel@tonic-gate * insert another 0377 after this one. all data after
22200Sstevel@tonic-gate * the original 0377 have to be copied to the new mblk
22210Sstevel@tonic-gate */
22227492SZhigang.Lu@Sun.COM tailsz = _PTRDIFF(mp->b_wptr, p);
22230Sstevel@tonic-gate if ((newmp = allocb(tailsz + 1, BPRI_HI)) == NULL) {
22240Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_RX_CB, pp->port_lh,
22250Sstevel@tonic-gate "usbser_rx_massage_data: allocb failed");
22260Sstevel@tonic-gate
22270Sstevel@tonic-gate continue;
22280Sstevel@tonic-gate }
22290Sstevel@tonic-gate
22300Sstevel@tonic-gate /* fill in the new mblk */
22310Sstevel@tonic-gate *newmp->b_wptr++ = 0377;
22320Sstevel@tonic-gate if (tailsz > 0) {
22330Sstevel@tonic-gate bcopy(p, newmp->b_wptr, tailsz);
22340Sstevel@tonic-gate newmp->b_wptr += tailsz;
22350Sstevel@tonic-gate }
22360Sstevel@tonic-gate /* shrink the original mblk */
22370Sstevel@tonic-gate mp->b_wptr = p;
22380Sstevel@tonic-gate
22390Sstevel@tonic-gate newmp->b_cont = mp->b_cont;
22400Sstevel@tonic-gate mp->b_cont = newmp;
22410Sstevel@tonic-gate p = newmp->b_rptr + 1;
22420Sstevel@tonic-gate mp = newmp;
22430Sstevel@tonic-gate }
22440Sstevel@tonic-gate mp = mp->b_cont;
22450Sstevel@tonic-gate }
22460Sstevel@tonic-gate }
22470Sstevel@tonic-gate
22480Sstevel@tonic-gate /*
22490Sstevel@tonic-gate * more joys of termio
22500Sstevel@tonic-gate */
22510Sstevel@tonic-gate static void
usbser_rx_massage_mbreak(usbser_port_t * pp,mblk_t * mp)22520Sstevel@tonic-gate usbser_rx_massage_mbreak(usbser_port_t *pp, mblk_t *mp)
22530Sstevel@tonic-gate {
22540Sstevel@tonic-gate tty_common_t *tp = &pp->port_ttycommon;
22550Sstevel@tonic-gate uchar_t err, c;
22560Sstevel@tonic-gate
22570Sstevel@tonic-gate err = *mp->b_rptr;
22580Sstevel@tonic-gate c = *(mp->b_rptr + 1);
22590Sstevel@tonic-gate
22600Sstevel@tonic-gate if ((err & (DS_FRAMING_ERR | DS_BREAK_ERR)) && (c == 0)) {
22610Sstevel@tonic-gate /* break */
22620Sstevel@tonic-gate mp->b_rptr += 2;
22630Sstevel@tonic-gate } else if (!(tp->t_iflag & INPCK) && (err & (DS_PARITY_ERR))) {
22640Sstevel@tonic-gate /* Posix Ref: Assertion 7.1.2.2-20(C) */
22650Sstevel@tonic-gate mp->b_rptr++;
22660Sstevel@tonic-gate DB_TYPE(mp) = M_DATA;
22670Sstevel@tonic-gate } else {
22680Sstevel@tonic-gate /* for ldterm to handle */
22690Sstevel@tonic-gate mp->b_rptr++;
22700Sstevel@tonic-gate }
22710Sstevel@tonic-gate
22720Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh,
22736898Sfb209375 "usbser_rx_massage_mbreak: type=%x len=%ld [0]=0%o",
22746898Sfb209375 DB_TYPE(mp), (long)MBLKL(mp), (MBLKL(mp) > 0) ? *mp->b_rptr : 45);
22750Sstevel@tonic-gate }
22760Sstevel@tonic-gate
22770Sstevel@tonic-gate
22780Sstevel@tonic-gate /*
22790Sstevel@tonic-gate * in rx callback, try to send an mblk upstream
22800Sstevel@tonic-gate */
22810Sstevel@tonic-gate static void
usbser_rx_cb_put(usbser_port_t * pp,queue_t * rq,queue_t * wq,mblk_t * mp)22820Sstevel@tonic-gate usbser_rx_cb_put(usbser_port_t *pp, queue_t *rq, queue_t *wq, mblk_t *mp)
22830Sstevel@tonic-gate {
22840Sstevel@tonic-gate if (canputnext(rq)) {
22850Sstevel@tonic-gate putnext(rq, mp);
22860Sstevel@tonic-gate } else if (canput(rq) && putq(rq, mp)) {
22870Sstevel@tonic-gate /*
22880Sstevel@tonic-gate * full queue indicates the need for inbound flow control
22890Sstevel@tonic-gate */
22900Sstevel@tonic-gate (void) putctl(wq, M_STOPI);
22910Sstevel@tonic-gate usbser_st_put_stopi++;
22920Sstevel@tonic-gate
22930Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_RX_CB, pp->port_lh,
22940Sstevel@tonic-gate "usbser_rx_cb: cannot putnext, flow ctl");
22950Sstevel@tonic-gate } else {
22960Sstevel@tonic-gate freemsg(mp);
22970Sstevel@tonic-gate usbser_st_rx_data_loss++;
22980Sstevel@tonic-gate (void) putctl(wq, M_STOPI);
22990Sstevel@tonic-gate usbser_st_put_stopi++;
23000Sstevel@tonic-gate
23010Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_RX_CB, pp->port_lh,
2302978Sfrits "input overrun");
23030Sstevel@tonic-gate }
23040Sstevel@tonic-gate }
23050Sstevel@tonic-gate
23060Sstevel@tonic-gate
23070Sstevel@tonic-gate /*
23080Sstevel@tonic-gate * modem status change callback
23090Sstevel@tonic-gate *
23100Sstevel@tonic-gate * each time external status lines are changed, DSD calls this routine
23110Sstevel@tonic-gate */
23120Sstevel@tonic-gate static void
usbser_status_cb(caddr_t arg)23130Sstevel@tonic-gate usbser_status_cb(caddr_t arg)
23140Sstevel@tonic-gate {
23150Sstevel@tonic-gate usbser_port_t *pp = (usbser_port_t *)arg;
23160Sstevel@tonic-gate
23170Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh, "usbser_status_cb");
23180Sstevel@tonic-gate
23190Sstevel@tonic-gate if (!usbser_dev_is_online(pp->port_usp)) {
23200Sstevel@tonic-gate
23210Sstevel@tonic-gate return;
23220Sstevel@tonic-gate }
23230Sstevel@tonic-gate
23240Sstevel@tonic-gate /*
23250Sstevel@tonic-gate * actual processing will be done in usbser_status_proc_cb()
23260Sstevel@tonic-gate * running in wq thread
23270Sstevel@tonic-gate */
23280Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
23290Sstevel@tonic-gate if (USBSER_PORT_ACCESS_OK(pp) || USBSER_IS_OPENING(pp)) {
23300Sstevel@tonic-gate pp->port_flags |= USBSER_FL_STATUS_CB;
23310Sstevel@tonic-gate usbser_thr_wake(&pp->port_wq_thread);
23320Sstevel@tonic-gate }
23330Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
23340Sstevel@tonic-gate }
23350Sstevel@tonic-gate
23360Sstevel@tonic-gate
23370Sstevel@tonic-gate /*
23380Sstevel@tonic-gate * modem status change
23390Sstevel@tonic-gate */
23400Sstevel@tonic-gate static void
usbser_status_proc_cb(usbser_port_t * pp)23410Sstevel@tonic-gate usbser_status_proc_cb(usbser_port_t *pp)
23420Sstevel@tonic-gate {
23430Sstevel@tonic-gate tty_common_t *tp = &pp->port_ttycommon;
23440Sstevel@tonic-gate queue_t *rq, *wq;
23450Sstevel@tonic-gate int status;
23460Sstevel@tonic-gate int drop_dtr = 0;
23470Sstevel@tonic-gate int rq_msg = 0, wq_msg = 0;
23480Sstevel@tonic-gate
23490Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh, "usbser_status_proc_cb");
23500Sstevel@tonic-gate
23510Sstevel@tonic-gate pp->port_flags &= ~USBSER_FL_STATUS_CB;
23520Sstevel@tonic-gate
23530Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
23540Sstevel@tonic-gate if (!usbser_dev_is_online(pp->port_usp)) {
23550Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
23560Sstevel@tonic-gate
23570Sstevel@tonic-gate return;
23580Sstevel@tonic-gate }
23590Sstevel@tonic-gate
23600Sstevel@tonic-gate /* get modem status */
23610Sstevel@tonic-gate if (USBSER_DS_GET_MODEM_CTL(pp, -1, &status) != USB_SUCCESS) {
23620Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
23630Sstevel@tonic-gate
23640Sstevel@tonic-gate return;
23650Sstevel@tonic-gate }
23660Sstevel@tonic-gate
23670Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
23680Sstevel@tonic-gate usbser_serialize_port_act(pp, USBSER_ACT_CTL);
23690Sstevel@tonic-gate
23700Sstevel@tonic-gate rq = pp->port_ttycommon.t_readq;
23710Sstevel@tonic-gate wq = pp->port_ttycommon.t_writeq;
23720Sstevel@tonic-gate
23730Sstevel@tonic-gate /*
23740Sstevel@tonic-gate * outbound flow control
23750Sstevel@tonic-gate */
23760Sstevel@tonic-gate if (tp->t_cflag & CRTSCTS) {
23770Sstevel@tonic-gate if (!(status & TIOCM_CTS)) {
23780Sstevel@tonic-gate /*
23790Sstevel@tonic-gate * CTS dropped, stop xmit
23800Sstevel@tonic-gate */
23810Sstevel@tonic-gate if (!(pp->port_flags & USBSER_FL_TX_STOPPED)) {
23820Sstevel@tonic-gate wq_msg = M_STOP;
23830Sstevel@tonic-gate }
23840Sstevel@tonic-gate } else if (pp->port_flags & USBSER_FL_TX_STOPPED) {
23850Sstevel@tonic-gate /*
23860Sstevel@tonic-gate * CTS raised, resume xmit
23870Sstevel@tonic-gate */
23880Sstevel@tonic-gate wq_msg = M_START;
23890Sstevel@tonic-gate }
23900Sstevel@tonic-gate }
23910Sstevel@tonic-gate
23920Sstevel@tonic-gate /*
23930Sstevel@tonic-gate * check carrier
23940Sstevel@tonic-gate */
23950Sstevel@tonic-gate if ((status & TIOCM_CD) || (tp->t_flags & TS_SOFTCAR)) {
23960Sstevel@tonic-gate /*
23970Sstevel@tonic-gate * carrier present
23980Sstevel@tonic-gate */
23990Sstevel@tonic-gate if ((pp->port_flags & USBSER_FL_CARR_ON) == 0) {
24000Sstevel@tonic-gate pp->port_flags |= USBSER_FL_CARR_ON;
24010Sstevel@tonic-gate
24020Sstevel@tonic-gate rq_msg = M_UNHANGUP;
24030Sstevel@tonic-gate /*
24040Sstevel@tonic-gate * wake open
24050Sstevel@tonic-gate */
24060Sstevel@tonic-gate if (pp->port_flags & USBSER_FL_WOPEN) {
24070Sstevel@tonic-gate cv_broadcast(&pp->port_car_cv);
24080Sstevel@tonic-gate }
24090Sstevel@tonic-gate
24100Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh,
24110Sstevel@tonic-gate "usbser_status_cb: carr on");
24120Sstevel@tonic-gate }
24130Sstevel@tonic-gate } else if (pp->port_flags & USBSER_FL_CARR_ON) {
24140Sstevel@tonic-gate pp->port_flags &= ~USBSER_FL_CARR_ON;
24150Sstevel@tonic-gate /*
24160Sstevel@tonic-gate * carrier went away: if not local line, drop DTR
24170Sstevel@tonic-gate */
24180Sstevel@tonic-gate if (!(tp->t_cflag & CLOCAL)) {
24190Sstevel@tonic-gate drop_dtr = 1;
24200Sstevel@tonic-gate rq_msg = M_HANGUP;
24210Sstevel@tonic-gate }
24220Sstevel@tonic-gate if ((pp->port_flags & USBSER_FL_TX_STOPPED) && (wq_msg == 0)) {
24230Sstevel@tonic-gate wq_msg = M_START;
24240Sstevel@tonic-gate }
24250Sstevel@tonic-gate
24260Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh,
24270Sstevel@tonic-gate "usbser_status_cb: carr off");
24280Sstevel@tonic-gate }
24290Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
24300Sstevel@tonic-gate
24310Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh,
24320Sstevel@tonic-gate "usbser_status_cb: rq_msg=%d wq_msg=%d", rq_msg, wq_msg);
24330Sstevel@tonic-gate
24340Sstevel@tonic-gate /*
24350Sstevel@tonic-gate * commit postponed actions now
24360Sstevel@tonic-gate * do so only if port is fully open (queues are enabled)
24370Sstevel@tonic-gate */
24380Sstevel@tonic-gate if (rq) {
24390Sstevel@tonic-gate if (rq_msg) {
24400Sstevel@tonic-gate (void) putnextctl(rq, rq_msg);
24410Sstevel@tonic-gate }
24420Sstevel@tonic-gate if (drop_dtr) {
24430Sstevel@tonic-gate (void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR, 0);
24440Sstevel@tonic-gate }
24450Sstevel@tonic-gate if (wq_msg) {
24460Sstevel@tonic-gate (void) putctl(wq, wq_msg);
24470Sstevel@tonic-gate }
24480Sstevel@tonic-gate }
24490Sstevel@tonic-gate
24500Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
24510Sstevel@tonic-gate usbser_release_port_act(pp, USBSER_ACT_CTL);
24520Sstevel@tonic-gate }
24530Sstevel@tonic-gate
24540Sstevel@tonic-gate
24550Sstevel@tonic-gate /*
24560Sstevel@tonic-gate * serial support
24570Sstevel@tonic-gate * --------------
24580Sstevel@tonic-gate *
24590Sstevel@tonic-gate *
24600Sstevel@tonic-gate * this routine is run by wq thread every time it's woken,
24610Sstevel@tonic-gate * i.e. when the queue contains messages to process
24620Sstevel@tonic-gate */
24630Sstevel@tonic-gate static void
usbser_wmsg(usbser_port_t * pp)24640Sstevel@tonic-gate usbser_wmsg(usbser_port_t *pp)
24650Sstevel@tonic-gate {
24660Sstevel@tonic-gate queue_t *q = pp->port_ttycommon.t_writeq;
24670Sstevel@tonic-gate mblk_t *mp;
24680Sstevel@tonic-gate int msgtype;
24690Sstevel@tonic-gate
24700Sstevel@tonic-gate ASSERT(mutex_owned(&pp->port_mutex));
24710Sstevel@tonic-gate
24720Sstevel@tonic-gate if (q == NULL) {
24730Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_WQ, pp->port_lh, "usbser_wmsg: q=NULL");
24740Sstevel@tonic-gate
24750Sstevel@tonic-gate return;
24760Sstevel@tonic-gate }
24770Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wmsg: q=%p act=%x 0x%x",
24786898Sfb209375 (void *)q, pp->port_act, q->q_first ? DB_TYPE(q->q_first) : 0xff);
24790Sstevel@tonic-gate
24800Sstevel@tonic-gate while ((mp = getq(q)) != NULL) {
24810Sstevel@tonic-gate msgtype = DB_TYPE(mp);
24820Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wmsg: "
24830Sstevel@tonic-gate "type=%s (0x%x)", usbser_msgtype2str(msgtype), msgtype);
24840Sstevel@tonic-gate
24850Sstevel@tonic-gate switch (msgtype) {
24860Sstevel@tonic-gate /*
24870Sstevel@tonic-gate * high-priority messages
24880Sstevel@tonic-gate */
24890Sstevel@tonic-gate case M_STOP:
24900Sstevel@tonic-gate usbser_stop(pp, mp);
24910Sstevel@tonic-gate
24920Sstevel@tonic-gate break;
24930Sstevel@tonic-gate case M_START:
24940Sstevel@tonic-gate usbser_start(pp, mp);
24950Sstevel@tonic-gate
24960Sstevel@tonic-gate break;
24970Sstevel@tonic-gate case M_STOPI:
24980Sstevel@tonic-gate usbser_stopi(pp, mp);
24990Sstevel@tonic-gate
25000Sstevel@tonic-gate break;
25010Sstevel@tonic-gate case M_STARTI:
25020Sstevel@tonic-gate usbser_starti(pp, mp);
25030Sstevel@tonic-gate
25040Sstevel@tonic-gate break;
25050Sstevel@tonic-gate case M_IOCDATA:
25060Sstevel@tonic-gate usbser_iocdata(pp, mp);
25070Sstevel@tonic-gate
25080Sstevel@tonic-gate break;
25090Sstevel@tonic-gate case M_FLUSH:
25100Sstevel@tonic-gate usbser_flush(pp, mp);
25110Sstevel@tonic-gate
25120Sstevel@tonic-gate break;
25130Sstevel@tonic-gate /*
25140Sstevel@tonic-gate * normal-priority messages
25150Sstevel@tonic-gate */
25160Sstevel@tonic-gate case M_BREAK:
25170Sstevel@tonic-gate usbser_break(pp, mp);
25180Sstevel@tonic-gate
25190Sstevel@tonic-gate break;
25200Sstevel@tonic-gate case M_DELAY:
25210Sstevel@tonic-gate usbser_delay(pp, mp);
25220Sstevel@tonic-gate
25230Sstevel@tonic-gate break;
25240Sstevel@tonic-gate case M_DATA:
25250Sstevel@tonic-gate if (usbser_data(pp, mp) != USB_SUCCESS) {
25260Sstevel@tonic-gate (void) putbq(q, mp);
25270Sstevel@tonic-gate
25280Sstevel@tonic-gate return;
25290Sstevel@tonic-gate }
25300Sstevel@tonic-gate
25310Sstevel@tonic-gate break;
25320Sstevel@tonic-gate case M_IOCTL:
25330Sstevel@tonic-gate if (usbser_ioctl(pp, mp) != USB_SUCCESS) {
25340Sstevel@tonic-gate (void) putbq(q, mp);
25350Sstevel@tonic-gate
25360Sstevel@tonic-gate return;
25370Sstevel@tonic-gate }
25380Sstevel@tonic-gate
25390Sstevel@tonic-gate break;
25400Sstevel@tonic-gate default:
25410Sstevel@tonic-gate freemsg(mp);
25420Sstevel@tonic-gate
25430Sstevel@tonic-gate break;
25440Sstevel@tonic-gate }
25450Sstevel@tonic-gate }
25460Sstevel@tonic-gate }
25470Sstevel@tonic-gate
25480Sstevel@tonic-gate
25490Sstevel@tonic-gate /*
25500Sstevel@tonic-gate * process M_DATA message
25510Sstevel@tonic-gate */
25520Sstevel@tonic-gate static int
usbser_data(usbser_port_t * pp,mblk_t * mp)25530Sstevel@tonic-gate usbser_data(usbser_port_t *pp, mblk_t *mp)
25540Sstevel@tonic-gate {
25550Sstevel@tonic-gate /* put off until current transfer ends or delay is over */
25560Sstevel@tonic-gate if ((pp->port_act & USBSER_ACT_TX) ||
25570Sstevel@tonic-gate (pp->port_act & USBSER_ACT_DELAY)) {
25580Sstevel@tonic-gate
25590Sstevel@tonic-gate return (USB_FAILURE);
25600Sstevel@tonic-gate }
25617020Sgd78059 if (MBLKL(mp) <= 0) {
25620Sstevel@tonic-gate freemsg(mp);
25630Sstevel@tonic-gate
25640Sstevel@tonic-gate return (USB_SUCCESS);
25650Sstevel@tonic-gate }
25660Sstevel@tonic-gate
25670Sstevel@tonic-gate pp->port_act |= USBSER_ACT_TX;
25680Sstevel@tonic-gate pp->port_wq_data_cnt -= msgdsize(mp);
25690Sstevel@tonic-gate
25700Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
25710Sstevel@tonic-gate /* DSD is required to accept data block in any case */
25720Sstevel@tonic-gate (void) USBSER_DS_TX(pp, mp);
25730Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
25740Sstevel@tonic-gate
25750Sstevel@tonic-gate return (USB_SUCCESS);
25760Sstevel@tonic-gate }
25770Sstevel@tonic-gate
25780Sstevel@tonic-gate
25790Sstevel@tonic-gate /*
25800Sstevel@tonic-gate * process an M_IOCTL message
25810Sstevel@tonic-gate */
25820Sstevel@tonic-gate static int
usbser_ioctl(usbser_port_t * pp,mblk_t * mp)25830Sstevel@tonic-gate usbser_ioctl(usbser_port_t *pp, mblk_t *mp)
25840Sstevel@tonic-gate {
25850Sstevel@tonic-gate tty_common_t *tp = &pp->port_ttycommon;
25860Sstevel@tonic-gate queue_t *q = tp->t_writeq;
25870Sstevel@tonic-gate struct iocblk *iocp;
25880Sstevel@tonic-gate int cmd;
25890Sstevel@tonic-gate mblk_t *datamp;
25900Sstevel@tonic-gate int error = 0, rval;
25910Sstevel@tonic-gate int val;
25920Sstevel@tonic-gate
25930Sstevel@tonic-gate ASSERT(mutex_owned(&pp->port_mutex));
25940Sstevel@tonic-gate ASSERT(DB_TYPE(mp) == M_IOCTL);
25950Sstevel@tonic-gate
25960Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr;
25970Sstevel@tonic-gate cmd = iocp->ioc_cmd;
25980Sstevel@tonic-gate
25990Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_IOCTL, pp->port_lh, "usbser_ioctl: "
26006898Sfb209375 "mp=%p %s (0x%x)", (void *)mp, usbser_ioctl2str(cmd), cmd);
26010Sstevel@tonic-gate
26020Sstevel@tonic-gate if (tp->t_iocpending != NULL) {
26030Sstevel@tonic-gate /*
26040Sstevel@tonic-gate * We were holding an ioctl response pending the
26050Sstevel@tonic-gate * availability of an mblk to hold data to be passed up;
26060Sstevel@tonic-gate * another ioctl came through, which means that ioctl
26070Sstevel@tonic-gate * must have timed out or been aborted.
26080Sstevel@tonic-gate */
26090Sstevel@tonic-gate freemsg(tp->t_iocpending);
26100Sstevel@tonic-gate tp->t_iocpending = NULL;
26110Sstevel@tonic-gate }
26120Sstevel@tonic-gate
26130Sstevel@tonic-gate switch (cmd) {
26140Sstevel@tonic-gate case TIOCMGET:
26150Sstevel@tonic-gate case TIOCMBIC:
26160Sstevel@tonic-gate case TIOCMBIS:
26170Sstevel@tonic-gate case TIOCMSET:
26182191Sszhou case CONSOPENPOLLEDIO:
26192191Sszhou case CONSCLOSEPOLLEDIO:
26202191Sszhou case CONSSETABORTENABLE:
26212191Sszhou case CONSGETABORTENABLE:
26220Sstevel@tonic-gate /*
26230Sstevel@tonic-gate * For the above ioctls do not call ttycommon_ioctl() because
26240Sstevel@tonic-gate * this function frees up the message block (mp->b_cont) that
26250Sstevel@tonic-gate * contains the address of the user variable where we need to
26260Sstevel@tonic-gate * pass back the bit array.
26270Sstevel@tonic-gate */
26280Sstevel@tonic-gate error = -1;
26290Sstevel@tonic-gate usbser_serialize_port_act(pp, USBSER_ACT_CTL);
26300Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
26310Sstevel@tonic-gate break;
26329354STim.Marsland@Sun.COM
26330Sstevel@tonic-gate case TCSBRK:
26340Sstevel@tonic-gate /* serialize breaks */
26359354STim.Marsland@Sun.COM if (pp->port_act & USBSER_ACT_BREAK)
26360Sstevel@tonic-gate return (USB_FAILURE);
26379354STim.Marsland@Sun.COM /*FALLTHRU*/
26380Sstevel@tonic-gate default:
26390Sstevel@tonic-gate usbser_serialize_port_act(pp, USBSER_ACT_CTL);
26400Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
26410Sstevel@tonic-gate (void) ttycommon_ioctl(tp, q, mp, &error);
26429354STim.Marsland@Sun.COM break;
26430Sstevel@tonic-gate }
26440Sstevel@tonic-gate
26450Sstevel@tonic-gate if (error == 0) {
26460Sstevel@tonic-gate /*
26470Sstevel@tonic-gate * ttycommon_ioctl() did most of the work
26480Sstevel@tonic-gate * we just use the data it set up
26490Sstevel@tonic-gate */
26500Sstevel@tonic-gate switch (cmd) {
26510Sstevel@tonic-gate case TCSETSF:
26520Sstevel@tonic-gate case TCSETSW:
26530Sstevel@tonic-gate case TCSETA:
26540Sstevel@tonic-gate case TCSETAW:
26550Sstevel@tonic-gate case TCSETAF:
26560Sstevel@tonic-gate (void) USBSER_DS_FIFO_DRAIN(pp, DS_TX);
26579354STim.Marsland@Sun.COM /*FALLTHRU*/
26589354STim.Marsland@Sun.COM
26590Sstevel@tonic-gate case TCSETS:
26600Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
26610Sstevel@tonic-gate error = usbser_port_program(pp);
26620Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
26630Sstevel@tonic-gate break;
26640Sstevel@tonic-gate }
26650Sstevel@tonic-gate goto end;
26669354STim.Marsland@Sun.COM
26670Sstevel@tonic-gate } else if (error > 0) {
26680Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_IOCTL, pp->port_lh, "usbser_ioctl: "
26690Sstevel@tonic-gate "ttycommon_ioctl returned %d", error);
26700Sstevel@tonic-gate goto end;
26710Sstevel@tonic-gate }
26720Sstevel@tonic-gate
26730Sstevel@tonic-gate /*
26740Sstevel@tonic-gate * error < 0: ttycommon_ioctl() didn't do anything, we process it here
26750Sstevel@tonic-gate */
26760Sstevel@tonic-gate error = 0;
26770Sstevel@tonic-gate switch (cmd) {
26780Sstevel@tonic-gate case TCSBRK:
26799354STim.Marsland@Sun.COM if ((error = miocpullup(mp, sizeof (int))) != 0)
26800Sstevel@tonic-gate break;
26819354STim.Marsland@Sun.COM
26820Sstevel@tonic-gate /* drain output */
26830Sstevel@tonic-gate (void) USBSER_DS_FIFO_DRAIN(pp, USBSER_TX_FIFO_DRAIN_TIMEOUT);
26849354STim.Marsland@Sun.COM
26850Sstevel@tonic-gate /*
26860Sstevel@tonic-gate * if required, set break
26870Sstevel@tonic-gate */
26880Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr == 0) {
26890Sstevel@tonic-gate if (USBSER_DS_BREAK_CTL(pp, DS_ON) != USB_SUCCESS) {
26900Sstevel@tonic-gate error = EIO;
26910Sstevel@tonic-gate break;
26920Sstevel@tonic-gate }
26939354STim.Marsland@Sun.COM
26940Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
26950Sstevel@tonic-gate pp->port_act |= USBSER_ACT_BREAK;
26960Sstevel@tonic-gate pp->port_delay_id = timeout(usbser_restart, pp,
26975541Slg150142 drv_usectohz(250000));
26980Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
26990Sstevel@tonic-gate }
27009354STim.Marsland@Sun.COM mioc2ack(mp, NULL, 0, 0);
27010Sstevel@tonic-gate break;
27029354STim.Marsland@Sun.COM
27039354STim.Marsland@Sun.COM case TIOCSBRK: /* set break */
27049354STim.Marsland@Sun.COM if (USBSER_DS_BREAK_CTL(pp, DS_ON) != USB_SUCCESS)
27050Sstevel@tonic-gate error = EIO;
27069354STim.Marsland@Sun.COM else
27079354STim.Marsland@Sun.COM mioc2ack(mp, NULL, 0, 0);
27080Sstevel@tonic-gate break;
27099354STim.Marsland@Sun.COM
27109354STim.Marsland@Sun.COM case TIOCCBRK: /* clear break */
27119354STim.Marsland@Sun.COM if (USBSER_DS_BREAK_CTL(pp, DS_OFF) != USB_SUCCESS)
27120Sstevel@tonic-gate error = EIO;
27139354STim.Marsland@Sun.COM else
27149354STim.Marsland@Sun.COM mioc2ack(mp, NULL, 0, 0);
27150Sstevel@tonic-gate break;
27169354STim.Marsland@Sun.COM
27179354STim.Marsland@Sun.COM case TIOCMSET: /* set all modem bits */
27189354STim.Marsland@Sun.COM case TIOCMBIS: /* bis modem bits */
27199354STim.Marsland@Sun.COM case TIOCMBIC: /* bic modem bits */
27200Sstevel@tonic-gate if (iocp->ioc_count == TRANSPARENT) {
27210Sstevel@tonic-gate mcopyin(mp, NULL, sizeof (int), NULL);
27220Sstevel@tonic-gate break;
27230Sstevel@tonic-gate }
27249354STim.Marsland@Sun.COM if ((error = miocpullup(mp, sizeof (int))) != 0)
27250Sstevel@tonic-gate break;
27260Sstevel@tonic-gate
27270Sstevel@tonic-gate val = *(int *)mp->b_cont->b_rptr;
27280Sstevel@tonic-gate if (cmd == TIOCMSET) {
27290Sstevel@tonic-gate rval = USBSER_DS_SET_MODEM_CTL(pp, -1, val);
27300Sstevel@tonic-gate } else if (cmd == TIOCMBIS) {
27310Sstevel@tonic-gate rval = USBSER_DS_SET_MODEM_CTL(pp, val, -1);
27320Sstevel@tonic-gate } else if (cmd == TIOCMBIC) {
27330Sstevel@tonic-gate rval = USBSER_DS_SET_MODEM_CTL(pp, val, 0);
27340Sstevel@tonic-gate }
27359354STim.Marsland@Sun.COM if (rval == USB_SUCCESS)
27369354STim.Marsland@Sun.COM mioc2ack(mp, NULL, 0, 0);
27379354STim.Marsland@Sun.COM else
27380Sstevel@tonic-gate error = EIO;
27390Sstevel@tonic-gate break;
27409354STim.Marsland@Sun.COM
27419354STim.Marsland@Sun.COM case TIOCSILOOP:
27420Sstevel@tonic-gate if (USBSER_DS_LOOPBACK_SUPPORTED(pp)) {
27439354STim.Marsland@Sun.COM if (USBSER_DS_LOOPBACK(pp, DS_ON) == USB_SUCCESS)
27449354STim.Marsland@Sun.COM mioc2ack(mp, NULL, 0, 0);
27459354STim.Marsland@Sun.COM else
27460Sstevel@tonic-gate error = EIO;
27470Sstevel@tonic-gate } else {
27480Sstevel@tonic-gate error = EINVAL;
27490Sstevel@tonic-gate }
27500Sstevel@tonic-gate break;
27519354STim.Marsland@Sun.COM
27529354STim.Marsland@Sun.COM case TIOCCILOOP:
27539354STim.Marsland@Sun.COM if (USBSER_DS_LOOPBACK_SUPPORTED(pp)) {
27549354STim.Marsland@Sun.COM if (USBSER_DS_LOOPBACK(pp, DS_OFF) == USB_SUCCESS)
27559354STim.Marsland@Sun.COM mioc2ack(mp, NULL, 0, 0);
27569354STim.Marsland@Sun.COM else
27579354STim.Marsland@Sun.COM error = EIO;
27589354STim.Marsland@Sun.COM } else {
27599354STim.Marsland@Sun.COM error = EINVAL;
27609354STim.Marsland@Sun.COM }
27619354STim.Marsland@Sun.COM break;
27629354STim.Marsland@Sun.COM
27639354STim.Marsland@Sun.COM case TIOCMGET: /* get all modem bits */
27649354STim.Marsland@Sun.COM if ((datamp = allocb(sizeof (int), BPRI_MED)) == NULL) {
27650Sstevel@tonic-gate error = EAGAIN;
27660Sstevel@tonic-gate break;
27670Sstevel@tonic-gate }
27680Sstevel@tonic-gate rval = USBSER_DS_GET_MODEM_CTL(pp, -1, (int *)datamp->b_rptr);
27690Sstevel@tonic-gate if (rval != USB_SUCCESS) {
27700Sstevel@tonic-gate error = EIO;
27710Sstevel@tonic-gate break;
27720Sstevel@tonic-gate }
27739354STim.Marsland@Sun.COM if (iocp->ioc_count == TRANSPARENT)
27740Sstevel@tonic-gate mcopyout(mp, NULL, sizeof (int), NULL, datamp);
27759354STim.Marsland@Sun.COM else
27769354STim.Marsland@Sun.COM mioc2ack(mp, datamp, sizeof (int), 0);
27770Sstevel@tonic-gate break;
27789354STim.Marsland@Sun.COM
27792191Sszhou case CONSOPENPOLLEDIO:
27802191Sszhou error = usbser_polledio_init(pp);
27812191Sszhou if (error != 0)
27822191Sszhou break;
27832191Sszhou
27842191Sszhou error = miocpullup(mp, sizeof (struct cons_polledio *));
27852191Sszhou if (error != 0)
27862191Sszhou break;
27872191Sszhou
27882191Sszhou *(struct cons_polledio **)mp->b_cont->b_rptr = &usbser_polledio;
2789*10481SGuoqing.Zhu@Sun.COM
2790*10481SGuoqing.Zhu@Sun.COM mp->b_datap->db_type = M_IOCACK;
27912191Sszhou break;
27929354STim.Marsland@Sun.COM
27932191Sszhou case CONSCLOSEPOLLEDIO:
27942191Sszhou usbser_polledio_fini(pp);
2795*10481SGuoqing.Zhu@Sun.COM mp->b_datap->db_type = M_IOCACK;
2796*10481SGuoqing.Zhu@Sun.COM iocp->ioc_error = 0;
2797*10481SGuoqing.Zhu@Sun.COM iocp->ioc_rval = 0;
27982191Sszhou break;
27999354STim.Marsland@Sun.COM
28002191Sszhou case CONSSETABORTENABLE:
28012191Sszhou error = secpolicy_console(iocp->ioc_cr);
28022191Sszhou if (error != 0)
28032191Sszhou break;
28042191Sszhou
28052191Sszhou if (iocp->ioc_count != TRANSPARENT) {
28062191Sszhou error = EINVAL;
28072191Sszhou break;
28082191Sszhou }
28092191Sszhou
28102191Sszhou /*
28112191Sszhou * To do: implement console abort support
28122191Sszhou * This involves adding a console flag to usbser
28132191Sszhou * state structure. If flag is set, parse input stream
28142191Sszhou * for abort sequence (see asy for example).
28152191Sszhou *
28162191Sszhou * For now, run mdb -K to get kmdb prompt.
28172191Sszhou */
28182191Sszhou if (*(intptr_t *)mp->b_cont->b_rptr)
28192191Sszhou usbser_console_abort = 1;
28202191Sszhou else
28212191Sszhou usbser_console_abort = 0;
2822*10481SGuoqing.Zhu@Sun.COM
2823*10481SGuoqing.Zhu@Sun.COM mp->b_datap->db_type = M_IOCACK;
2824*10481SGuoqing.Zhu@Sun.COM iocp->ioc_error = 0;
2825*10481SGuoqing.Zhu@Sun.COM iocp->ioc_rval = 0;
28262191Sszhou break;
28279354STim.Marsland@Sun.COM
28282191Sszhou case CONSGETABORTENABLE:
28292191Sszhou /*CONSTANTCONDITION*/
28302191Sszhou ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *));
28312191Sszhou /*
28322191Sszhou * Store the return value right in the payload
28332191Sszhou * we were passed. Crude.
28342191Sszhou */
28352191Sszhou mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL);
28362191Sszhou *(boolean_t *)mp->b_cont->b_rptr = (usbser_console_abort != 0);
28372191Sszhou break;
28389354STim.Marsland@Sun.COM
28390Sstevel@tonic-gate default:
28400Sstevel@tonic-gate error = EINVAL;
28410Sstevel@tonic-gate break;
28420Sstevel@tonic-gate }
28430Sstevel@tonic-gate end:
28449354STim.Marsland@Sun.COM if (error != 0)
28459354STim.Marsland@Sun.COM miocnak(q, mp, 0, error);
28469354STim.Marsland@Sun.COM else
28479354STim.Marsland@Sun.COM qreply(q, mp);
28480Sstevel@tonic-gate
28490Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
28500Sstevel@tonic-gate usbser_release_port_act(pp, USBSER_ACT_CTL);
28510Sstevel@tonic-gate
28520Sstevel@tonic-gate return (USB_SUCCESS);
28530Sstevel@tonic-gate }
28540Sstevel@tonic-gate
28550Sstevel@tonic-gate
28560Sstevel@tonic-gate /*
28570Sstevel@tonic-gate * process M_IOCDATA message
28580Sstevel@tonic-gate */
28590Sstevel@tonic-gate static void
usbser_iocdata(usbser_port_t * pp,mblk_t * mp)28600Sstevel@tonic-gate usbser_iocdata(usbser_port_t *pp, mblk_t *mp)
28610Sstevel@tonic-gate {
28620Sstevel@tonic-gate tty_common_t *tp = &pp->port_ttycommon;
28630Sstevel@tonic-gate queue_t *q = tp->t_writeq;
28640Sstevel@tonic-gate struct copyresp *csp;
28650Sstevel@tonic-gate int cmd;
28660Sstevel@tonic-gate int val;
28670Sstevel@tonic-gate int rval;
28680Sstevel@tonic-gate
28690Sstevel@tonic-gate ASSERT(mutex_owned(&pp->port_mutex));
28700Sstevel@tonic-gate
28710Sstevel@tonic-gate csp = (struct copyresp *)mp->b_rptr;
28720Sstevel@tonic-gate cmd = csp->cp_cmd;
28730Sstevel@tonic-gate
28740Sstevel@tonic-gate if (csp->cp_rval != 0) {
28750Sstevel@tonic-gate freemsg(mp);
28760Sstevel@tonic-gate return;
28770Sstevel@tonic-gate }
28780Sstevel@tonic-gate
28790Sstevel@tonic-gate switch (cmd) {
28809354STim.Marsland@Sun.COM case TIOCMSET: /* set all modem bits */
28819354STim.Marsland@Sun.COM case TIOCMBIS: /* bis modem bits */
28829354STim.Marsland@Sun.COM case TIOCMBIC: /* bic modem bits */
28830Sstevel@tonic-gate if ((mp->b_cont == NULL) ||
28840Sstevel@tonic-gate (MBLKL(mp->b_cont) < sizeof (int))) {
28850Sstevel@tonic-gate miocnak(q, mp, 0, EINVAL);
28860Sstevel@tonic-gate break;
28870Sstevel@tonic-gate }
28880Sstevel@tonic-gate val = *(int *)mp->b_cont->b_rptr;
28890Sstevel@tonic-gate
28900Sstevel@tonic-gate usbser_serialize_port_act(pp, USBSER_ACT_CTL);
28910Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
28929354STim.Marsland@Sun.COM
28930Sstevel@tonic-gate if (cmd == TIOCMSET) {
28940Sstevel@tonic-gate rval = USBSER_DS_SET_MODEM_CTL(pp, -1, val);
28950Sstevel@tonic-gate } else if (cmd == TIOCMBIS) {
28960Sstevel@tonic-gate rval = USBSER_DS_SET_MODEM_CTL(pp, val, -1);
28970Sstevel@tonic-gate } else if (cmd == TIOCMBIC) {
28980Sstevel@tonic-gate rval = USBSER_DS_SET_MODEM_CTL(pp, val, 0);
28990Sstevel@tonic-gate }
29000Sstevel@tonic-gate
29010Sstevel@tonic-gate if (mp->b_cont) {
29020Sstevel@tonic-gate freemsg(mp->b_cont);
29030Sstevel@tonic-gate mp->b_cont = NULL;
29040Sstevel@tonic-gate }
29059354STim.Marsland@Sun.COM
29069354STim.Marsland@Sun.COM if (rval == USB_SUCCESS)
29070Sstevel@tonic-gate miocack(q, mp, 0, 0);
29089354STim.Marsland@Sun.COM else
29090Sstevel@tonic-gate miocnak(q, mp, 0, EIO);
29109354STim.Marsland@Sun.COM
29110Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
29120Sstevel@tonic-gate usbser_release_port_act(pp, USBSER_ACT_CTL);
29130Sstevel@tonic-gate break;
29149354STim.Marsland@Sun.COM
29159354STim.Marsland@Sun.COM case TIOCMGET: /* get all modem bits */
29160Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
29170Sstevel@tonic-gate miocack(q, mp, 0, 0);
29180Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
29190Sstevel@tonic-gate break;
29209354STim.Marsland@Sun.COM
29210Sstevel@tonic-gate default:
29220Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
29230Sstevel@tonic-gate miocnak(q, mp, 0, EINVAL);
29240Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
29250Sstevel@tonic-gate break;
29260Sstevel@tonic-gate }
29270Sstevel@tonic-gate }
29280Sstevel@tonic-gate
29290Sstevel@tonic-gate
29300Sstevel@tonic-gate /*
29310Sstevel@tonic-gate * handle M_START[I]/M_STOP[I] messages
29320Sstevel@tonic-gate */
29330Sstevel@tonic-gate static void
usbser_stop(usbser_port_t * pp,mblk_t * mp)29340Sstevel@tonic-gate usbser_stop(usbser_port_t *pp, mblk_t *mp)
29350Sstevel@tonic-gate {
29360Sstevel@tonic-gate usbser_st_mstop++;
29370Sstevel@tonic-gate if (!(pp->port_flags & USBSER_FL_TX_STOPPED)) {
29380Sstevel@tonic-gate usbser_serialize_port_act(pp, USBSER_ACT_CTL);
29390Sstevel@tonic-gate pp->port_flags |= USBSER_FL_TX_STOPPED;
29400Sstevel@tonic-gate
29410Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
29420Sstevel@tonic-gate USBSER_DS_STOP(pp, DS_TX);
29430Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
29440Sstevel@tonic-gate
29450Sstevel@tonic-gate usbser_release_port_act(pp, USBSER_ACT_TX);
29460Sstevel@tonic-gate usbser_release_port_act(pp, USBSER_ACT_CTL);
29470Sstevel@tonic-gate }
29480Sstevel@tonic-gate freemsg(mp);
29490Sstevel@tonic-gate }
29500Sstevel@tonic-gate
29510Sstevel@tonic-gate
29520Sstevel@tonic-gate static void
usbser_start(usbser_port_t * pp,mblk_t * mp)29530Sstevel@tonic-gate usbser_start(usbser_port_t *pp, mblk_t *mp)
29540Sstevel@tonic-gate {
29550Sstevel@tonic-gate usbser_st_mstart++;
29560Sstevel@tonic-gate if (pp->port_flags & USBSER_FL_TX_STOPPED) {
29570Sstevel@tonic-gate usbser_serialize_port_act(pp, USBSER_ACT_CTL);
29580Sstevel@tonic-gate pp->port_flags &= ~USBSER_FL_TX_STOPPED;
29590Sstevel@tonic-gate
29600Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
29610Sstevel@tonic-gate USBSER_DS_START(pp, DS_TX);
29620Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
29630Sstevel@tonic-gate usbser_release_port_act(pp, USBSER_ACT_CTL);
29640Sstevel@tonic-gate }
29650Sstevel@tonic-gate freemsg(mp);
29660Sstevel@tonic-gate }
29670Sstevel@tonic-gate
29680Sstevel@tonic-gate
29690Sstevel@tonic-gate static void
usbser_stopi(usbser_port_t * pp,mblk_t * mp)29700Sstevel@tonic-gate usbser_stopi(usbser_port_t *pp, mblk_t *mp)
29710Sstevel@tonic-gate {
29720Sstevel@tonic-gate usbser_st_mstopi++;
29730Sstevel@tonic-gate usbser_serialize_port_act(pp, USBSER_ACT_CTL);
29740Sstevel@tonic-gate pp->port_flowc = pp->port_ttycommon.t_stopc;
29750Sstevel@tonic-gate usbser_inbound_flow_ctl(pp);
29760Sstevel@tonic-gate usbser_release_port_act(pp, USBSER_ACT_CTL);
29770Sstevel@tonic-gate freemsg(mp);
29780Sstevel@tonic-gate }
29790Sstevel@tonic-gate
29800Sstevel@tonic-gate static void
usbser_starti(usbser_port_t * pp,mblk_t * mp)29810Sstevel@tonic-gate usbser_starti(usbser_port_t *pp, mblk_t *mp)
29820Sstevel@tonic-gate {
29830Sstevel@tonic-gate usbser_st_mstarti++;
29840Sstevel@tonic-gate usbser_serialize_port_act(pp, USBSER_ACT_CTL);
29850Sstevel@tonic-gate pp->port_flowc = pp->port_ttycommon.t_startc;
29860Sstevel@tonic-gate usbser_inbound_flow_ctl(pp);
29870Sstevel@tonic-gate usbser_release_port_act(pp, USBSER_ACT_CTL);
29880Sstevel@tonic-gate freemsg(mp);
29890Sstevel@tonic-gate }
29900Sstevel@tonic-gate
29910Sstevel@tonic-gate /*
29920Sstevel@tonic-gate * process M_FLUSH message
29930Sstevel@tonic-gate */
29940Sstevel@tonic-gate static void
usbser_flush(usbser_port_t * pp,mblk_t * mp)29950Sstevel@tonic-gate usbser_flush(usbser_port_t *pp, mblk_t *mp)
29960Sstevel@tonic-gate {
29970Sstevel@tonic-gate queue_t *q = pp->port_ttycommon.t_writeq;
29980Sstevel@tonic-gate
29990Sstevel@tonic-gate if (*mp->b_rptr & FLUSHW) {
30000Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
30010Sstevel@tonic-gate (void) USBSER_DS_FIFO_FLUSH(pp, DS_TX); /* flush FIFO buffers */
30020Sstevel@tonic-gate flushq(q, FLUSHDATA); /* flush write queue */
30030Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
30040Sstevel@tonic-gate
30050Sstevel@tonic-gate usbser_release_port_act(pp, USBSER_ACT_TX);
30060Sstevel@tonic-gate
30070Sstevel@tonic-gate *mp->b_rptr &= ~FLUSHW;
30080Sstevel@tonic-gate }
30090Sstevel@tonic-gate if (*mp->b_rptr & FLUSHR) {
30100Sstevel@tonic-gate /*
30110Sstevel@tonic-gate * flush FIFO buffers
30120Sstevel@tonic-gate */
30130Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
30140Sstevel@tonic-gate (void) USBSER_DS_FIFO_FLUSH(pp, DS_RX);
30150Sstevel@tonic-gate flushq(RD(q), FLUSHDATA);
30160Sstevel@tonic-gate qreply(q, mp);
30170Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
30180Sstevel@tonic-gate } else {
30190Sstevel@tonic-gate freemsg(mp);
30200Sstevel@tonic-gate }
30210Sstevel@tonic-gate }
30220Sstevel@tonic-gate
30230Sstevel@tonic-gate /*
30240Sstevel@tonic-gate * process M_BREAK message
30250Sstevel@tonic-gate */
30260Sstevel@tonic-gate static void
usbser_break(usbser_port_t * pp,mblk_t * mp)30270Sstevel@tonic-gate usbser_break(usbser_port_t *pp, mblk_t *mp)
30280Sstevel@tonic-gate {
30290Sstevel@tonic-gate int rval;
30300Sstevel@tonic-gate
30310Sstevel@tonic-gate /*
30320Sstevel@tonic-gate * set the break and arrange for usbser_restart() to be called in 1/4 s
30330Sstevel@tonic-gate */
30340Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
30350Sstevel@tonic-gate rval = USBSER_DS_BREAK_CTL(pp, DS_ON);
30360Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
30370Sstevel@tonic-gate
30380Sstevel@tonic-gate if (rval == USB_SUCCESS) {
30390Sstevel@tonic-gate pp->port_act |= USBSER_ACT_BREAK;
30400Sstevel@tonic-gate pp->port_delay_id = timeout(usbser_restart, pp,
30415541Slg150142 drv_usectohz(250000));
30420Sstevel@tonic-gate }
30430Sstevel@tonic-gate freemsg(mp);
30440Sstevel@tonic-gate }
30450Sstevel@tonic-gate
30460Sstevel@tonic-gate
30470Sstevel@tonic-gate /*
30480Sstevel@tonic-gate * process M_DELAY message
30490Sstevel@tonic-gate */
30500Sstevel@tonic-gate static void
usbser_delay(usbser_port_t * pp,mblk_t * mp)30510Sstevel@tonic-gate usbser_delay(usbser_port_t *pp, mblk_t *mp)
30520Sstevel@tonic-gate {
30530Sstevel@tonic-gate /*
30540Sstevel@tonic-gate * arrange for usbser_restart() to be called when the delay expires
30550Sstevel@tonic-gate */
30560Sstevel@tonic-gate pp->port_act |= USBSER_ACT_DELAY;
30570Sstevel@tonic-gate pp->port_delay_id = timeout(usbser_restart, pp,
30585541Slg150142 (clock_t)(*(uchar_t *)mp->b_rptr + 6));
30590Sstevel@tonic-gate freemsg(mp);
30600Sstevel@tonic-gate }
30610Sstevel@tonic-gate
30620Sstevel@tonic-gate
30630Sstevel@tonic-gate /*
30640Sstevel@tonic-gate * restart output on a line after a delay or break timer expired
30650Sstevel@tonic-gate */
30660Sstevel@tonic-gate static void
usbser_restart(void * arg)30670Sstevel@tonic-gate usbser_restart(void *arg)
30680Sstevel@tonic-gate {
30690Sstevel@tonic-gate usbser_port_t *pp = (usbser_port_t *)arg;
30700Sstevel@tonic-gate
30710Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_restart");
30720Sstevel@tonic-gate
30730Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
30740Sstevel@tonic-gate /* if cancelled, return immediately */
30750Sstevel@tonic-gate if (pp->port_delay_id == 0) {
30760Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
30770Sstevel@tonic-gate
30780Sstevel@tonic-gate return;
30790Sstevel@tonic-gate }
30800Sstevel@tonic-gate pp->port_delay_id = 0;
30810Sstevel@tonic-gate
30820Sstevel@tonic-gate /* clear break if necessary */
30830Sstevel@tonic-gate if (pp->port_act & USBSER_ACT_BREAK) {
30840Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
30850Sstevel@tonic-gate (void) USBSER_DS_BREAK_CTL(pp, DS_OFF);
30860Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
30870Sstevel@tonic-gate }
30880Sstevel@tonic-gate
30890Sstevel@tonic-gate usbser_release_port_act(pp, USBSER_ACT_BREAK | USBSER_ACT_DELAY);
30900Sstevel@tonic-gate
30910Sstevel@tonic-gate /* wake wq thread to resume message processing */
30920Sstevel@tonic-gate usbser_thr_wake(&pp->port_wq_thread);
30930Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
30940Sstevel@tonic-gate }
30950Sstevel@tonic-gate
30960Sstevel@tonic-gate
30970Sstevel@tonic-gate /*
30980Sstevel@tonic-gate * program port hardware with the chosen parameters
30990Sstevel@tonic-gate * most of the operation is based on the values of 'c_iflag' and 'c_cflag'
31000Sstevel@tonic-gate */
31010Sstevel@tonic-gate static int
usbser_port_program(usbser_port_t * pp)31020Sstevel@tonic-gate usbser_port_program(usbser_port_t *pp)
31030Sstevel@tonic-gate {
31040Sstevel@tonic-gate tty_common_t *tp = &pp->port_ttycommon;
31050Sstevel@tonic-gate int baudrate;
31060Sstevel@tonic-gate int c_flag;
31070Sstevel@tonic-gate ds_port_param_entry_t pe[6];
31080Sstevel@tonic-gate ds_port_params_t params;
31090Sstevel@tonic-gate int flow_ctl, ctl_val;
31100Sstevel@tonic-gate int err = 0;
31110Sstevel@tonic-gate
31120Sstevel@tonic-gate baudrate = tp->t_cflag & CBAUD;
31130Sstevel@tonic-gate if (tp->t_cflag & CBAUDEXT) {
31140Sstevel@tonic-gate baudrate += 16;
31150Sstevel@tonic-gate }
31160Sstevel@tonic-gate
31170Sstevel@tonic-gate /*
31180Sstevel@tonic-gate * set input speed same as output, as split speed not supported
31190Sstevel@tonic-gate */
31200Sstevel@tonic-gate if (tp->t_cflag & (CIBAUD|CIBAUDEXT)) {
31210Sstevel@tonic-gate tp->t_cflag &= ~(CIBAUD);
31220Sstevel@tonic-gate if (baudrate > CBAUD) {
31230Sstevel@tonic-gate tp->t_cflag |= CIBAUDEXT;
31240Sstevel@tonic-gate tp->t_cflag |=
31255541Slg150142 (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD);
31260Sstevel@tonic-gate } else {
31270Sstevel@tonic-gate tp->t_cflag &= ~CIBAUDEXT;
31280Sstevel@tonic-gate tp->t_cflag |= ((baudrate << IBSHIFT) & CIBAUD);
31290Sstevel@tonic-gate }
31300Sstevel@tonic-gate }
31310Sstevel@tonic-gate
31320Sstevel@tonic-gate c_flag = tp->t_cflag;
31330Sstevel@tonic-gate
31340Sstevel@tonic-gate /*
31350Sstevel@tonic-gate * flow control
31360Sstevel@tonic-gate */
31370Sstevel@tonic-gate flow_ctl = tp->t_iflag & (IXON | IXANY | IXOFF);
31380Sstevel@tonic-gate if (c_flag & CRTSCTS) {
31390Sstevel@tonic-gate flow_ctl |= CTSXON;
31400Sstevel@tonic-gate }
31410Sstevel@tonic-gate if (c_flag & CRTSXOFF) {
31420Sstevel@tonic-gate flow_ctl |= RTSXOFF;
31430Sstevel@tonic-gate }
31440Sstevel@tonic-gate
31450Sstevel@tonic-gate /*
31460Sstevel@tonic-gate * fill in port parameters we need to set:
31470Sstevel@tonic-gate *
31480Sstevel@tonic-gate * baud rate
31490Sstevel@tonic-gate */
31500Sstevel@tonic-gate pe[0].param = DS_PARAM_BAUD;
31510Sstevel@tonic-gate pe[0].val.ui = baudrate;
31520Sstevel@tonic-gate
31530Sstevel@tonic-gate /* stop bits */
31540Sstevel@tonic-gate pe[1].param = DS_PARAM_STOPB;
31550Sstevel@tonic-gate pe[1].val.ui = c_flag & CSTOPB;
31560Sstevel@tonic-gate
31570Sstevel@tonic-gate /* parity */
31580Sstevel@tonic-gate pe[2].param = DS_PARAM_PARITY;
31590Sstevel@tonic-gate pe[2].val.ui = c_flag & (PARENB | PARODD);
31600Sstevel@tonic-gate
31610Sstevel@tonic-gate /* char size */
31620Sstevel@tonic-gate pe[3].param = DS_PARAM_CHARSZ;
31630Sstevel@tonic-gate pe[3].val.ui = c_flag & CSIZE;
31640Sstevel@tonic-gate
31650Sstevel@tonic-gate /* start & stop chars */
31660Sstevel@tonic-gate pe[4].param = DS_PARAM_XON_XOFF;
31670Sstevel@tonic-gate pe[4].val.uc[0] = tp->t_startc;
31680Sstevel@tonic-gate pe[4].val.uc[1] = tp->t_stopc;
31690Sstevel@tonic-gate
31700Sstevel@tonic-gate /* flow control */
31710Sstevel@tonic-gate pe[5].param = DS_PARAM_FLOW_CTL;
31720Sstevel@tonic-gate pe[5].val.ui = flow_ctl;
31730Sstevel@tonic-gate
31740Sstevel@tonic-gate params.tp_entries = &pe[0];
31750Sstevel@tonic-gate params.tp_cnt = 6;
31760Sstevel@tonic-gate
31770Sstevel@tonic-gate /* control signals */
31780Sstevel@tonic-gate ctl_val = TIOCM_DTR | TIOCM_RTS;
31790Sstevel@tonic-gate if (baudrate == 0) {
31800Sstevel@tonic-gate ctl_val &= ~TIOCM_DTR; /* zero baudrate means drop DTR */
31810Sstevel@tonic-gate }
31820Sstevel@tonic-gate if (pp->port_flags & USBSER_FL_RX_STOPPED) {
31830Sstevel@tonic-gate ctl_val &= ~TIOCM_RTS;
31840Sstevel@tonic-gate }
31850Sstevel@tonic-gate
31860Sstevel@tonic-gate /* submit */
31870Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
31880Sstevel@tonic-gate err = USBSER_DS_SET_PORT_PARAMS(pp, ¶ms);
31890Sstevel@tonic-gate if (err != USB_SUCCESS) {
31900Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
31910Sstevel@tonic-gate
31920Sstevel@tonic-gate return (EINVAL);
31930Sstevel@tonic-gate }
31940Sstevel@tonic-gate
31950Sstevel@tonic-gate err = USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR | TIOCM_RTS, ctl_val);
31960Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
31970Sstevel@tonic-gate
31980Sstevel@tonic-gate return ((err == USB_SUCCESS) ? 0 : EIO);
31990Sstevel@tonic-gate }
32000Sstevel@tonic-gate
32010Sstevel@tonic-gate
32020Sstevel@tonic-gate /*
32030Sstevel@tonic-gate * check if any inbound flow control action needed
32040Sstevel@tonic-gate */
32050Sstevel@tonic-gate static void
usbser_inbound_flow_ctl(usbser_port_t * pp)32060Sstevel@tonic-gate usbser_inbound_flow_ctl(usbser_port_t *pp)
32070Sstevel@tonic-gate {
32080Sstevel@tonic-gate tcflag_t need_hw;
32090Sstevel@tonic-gate int rts;
32100Sstevel@tonic-gate char c = pp->port_flowc;
32110Sstevel@tonic-gate mblk_t *mp = NULL;
32120Sstevel@tonic-gate
32130Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh,
32140Sstevel@tonic-gate "usbser_inbound_flow_ctl: c=%x cflag=%x port_flags=%x",
32150Sstevel@tonic-gate c, pp->port_ttycommon.t_cflag, pp->port_flags);
32160Sstevel@tonic-gate
32170Sstevel@tonic-gate if (c == '\0') {
32180Sstevel@tonic-gate
32190Sstevel@tonic-gate return;
32200Sstevel@tonic-gate }
32210Sstevel@tonic-gate pp->port_flowc = '\0';
32220Sstevel@tonic-gate
32230Sstevel@tonic-gate /*
32240Sstevel@tonic-gate * if inbound hardware flow control enabled, we need to frob RTS
32250Sstevel@tonic-gate */
32260Sstevel@tonic-gate need_hw = (pp->port_ttycommon.t_cflag & CRTSXOFF);
32270Sstevel@tonic-gate if (c == pp->port_ttycommon.t_startc) {
32280Sstevel@tonic-gate rts = TIOCM_RTS;
32290Sstevel@tonic-gate pp->port_flags &= ~USBSER_FL_RX_STOPPED;
32300Sstevel@tonic-gate } else {
32310Sstevel@tonic-gate rts = 0;
32320Sstevel@tonic-gate pp->port_flags |= USBSER_FL_RX_STOPPED;
32330Sstevel@tonic-gate }
32340Sstevel@tonic-gate
32350Sstevel@tonic-gate /*
32360Sstevel@tonic-gate * if character flow control active, transmit a start or stop char,
32370Sstevel@tonic-gate */
32380Sstevel@tonic-gate if (pp->port_ttycommon.t_iflag & IXOFF) {
32390Sstevel@tonic-gate if ((mp = allocb(1, BPRI_LO)) == NULL) {
32400Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_WQ, pp->port_lh,
32410Sstevel@tonic-gate "usbser_inbound_flow_ctl: allocb failed");
32420Sstevel@tonic-gate } else {
32430Sstevel@tonic-gate *mp->b_wptr++ = c;
32440Sstevel@tonic-gate pp->port_flags |= USBSER_ACT_TX;
32450Sstevel@tonic-gate }
32460Sstevel@tonic-gate }
32470Sstevel@tonic-gate
32480Sstevel@tonic-gate mutex_exit(&pp->port_mutex);
32490Sstevel@tonic-gate if (need_hw) {
32500Sstevel@tonic-gate (void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_RTS, rts);
32510Sstevel@tonic-gate }
32520Sstevel@tonic-gate if (mp) {
32530Sstevel@tonic-gate (void) USBSER_DS_TX(pp, mp);
32540Sstevel@tonic-gate }
32550Sstevel@tonic-gate mutex_enter(&pp->port_mutex);
32560Sstevel@tonic-gate }
32570Sstevel@tonic-gate
32580Sstevel@tonic-gate
32590Sstevel@tonic-gate /*
32600Sstevel@tonic-gate * misc
32610Sstevel@tonic-gate * ----
32620Sstevel@tonic-gate *
32630Sstevel@tonic-gate *
32649354STim.Marsland@Sun.COM * returns != 0 if device is online, 0 otherwise
32650Sstevel@tonic-gate */
32660Sstevel@tonic-gate static int
usbser_dev_is_online(usbser_state_t * usp)32670Sstevel@tonic-gate usbser_dev_is_online(usbser_state_t *usp)
32680Sstevel@tonic-gate {
32690Sstevel@tonic-gate int rval;
32700Sstevel@tonic-gate
32710Sstevel@tonic-gate mutex_enter(&usp->us_mutex);
32720Sstevel@tonic-gate rval = (usp->us_dev_state == USB_DEV_ONLINE);
32730Sstevel@tonic-gate mutex_exit(&usp->us_mutex);
32740Sstevel@tonic-gate
32750Sstevel@tonic-gate return (rval);
32760Sstevel@tonic-gate }
32770Sstevel@tonic-gate
32780Sstevel@tonic-gate /*
32790Sstevel@tonic-gate * serialize port activities defined by 'act' mask
32800Sstevel@tonic-gate */
32810Sstevel@tonic-gate static void
usbser_serialize_port_act(usbser_port_t * pp,int act)32820Sstevel@tonic-gate usbser_serialize_port_act(usbser_port_t *pp, int act)
32830Sstevel@tonic-gate {
32849354STim.Marsland@Sun.COM while (pp->port_act & act)
32850Sstevel@tonic-gate cv_wait(&pp->port_act_cv, &pp->port_mutex);
32860Sstevel@tonic-gate pp->port_act |= act;
32870Sstevel@tonic-gate }
32880Sstevel@tonic-gate
32890Sstevel@tonic-gate
32900Sstevel@tonic-gate /*
32910Sstevel@tonic-gate * indicate that port activity is finished
32920Sstevel@tonic-gate */
32930Sstevel@tonic-gate static void
usbser_release_port_act(usbser_port_t * pp,int act)32940Sstevel@tonic-gate usbser_release_port_act(usbser_port_t *pp, int act)
32950Sstevel@tonic-gate {
32960Sstevel@tonic-gate pp->port_act &= ~act;
32970Sstevel@tonic-gate cv_broadcast(&pp->port_act_cv);
32980Sstevel@tonic-gate }
32990Sstevel@tonic-gate
33000Sstevel@tonic-gate
33010Sstevel@tonic-gate /*
33020Sstevel@tonic-gate * message type to string and back conversion.
33030Sstevel@tonic-gate *
33040Sstevel@tonic-gate * pardon breaks on the same line, but as long as cstyle doesn't
33050Sstevel@tonic-gate * complain, I'd like to keep this form for trivial cases like this.
33060Sstevel@tonic-gate * associative arrays in the kernel, anyone?
33070Sstevel@tonic-gate */
33080Sstevel@tonic-gate static char *
usbser_msgtype2str(int type)33090Sstevel@tonic-gate usbser_msgtype2str(int type)
33100Sstevel@tonic-gate {
33110Sstevel@tonic-gate char *str;
33120Sstevel@tonic-gate
33130Sstevel@tonic-gate switch (type) {
33140Sstevel@tonic-gate case M_STOP: str = "M_STOP"; break;
33150Sstevel@tonic-gate case M_START: str = "M_START"; break;
33160Sstevel@tonic-gate case M_STOPI: str = "M_STOPI"; break;
33170Sstevel@tonic-gate case M_STARTI: str = "M_STARTI"; break;
33180Sstevel@tonic-gate case M_DATA: str = "M_DATA"; break;
33190Sstevel@tonic-gate case M_DELAY: str = "M_DELAY"; break;
33200Sstevel@tonic-gate case M_BREAK: str = "M_BREAK"; break;
33210Sstevel@tonic-gate case M_IOCTL: str = "M_IOCTL"; break;
33220Sstevel@tonic-gate case M_IOCDATA: str = "M_IOCDATA"; break;
33230Sstevel@tonic-gate case M_FLUSH: str = "M_FLUSH"; break;
33240Sstevel@tonic-gate case M_CTL: str = "M_CTL"; break;
33250Sstevel@tonic-gate case M_READ: str = "M_READ"; break;
33260Sstevel@tonic-gate default: str = "unknown"; break;
33270Sstevel@tonic-gate }
33280Sstevel@tonic-gate
33290Sstevel@tonic-gate return (str);
33300Sstevel@tonic-gate }
33310Sstevel@tonic-gate
33320Sstevel@tonic-gate
33330Sstevel@tonic-gate static char *
usbser_ioctl2str(int ioctl)33340Sstevel@tonic-gate usbser_ioctl2str(int ioctl)
33350Sstevel@tonic-gate {
33360Sstevel@tonic-gate char *str;
33370Sstevel@tonic-gate
33380Sstevel@tonic-gate switch (ioctl) {
33390Sstevel@tonic-gate case TCGETA: str = "TCGETA"; break;
33400Sstevel@tonic-gate case TCSETA: str = "TCSETA"; break;
33410Sstevel@tonic-gate case TCSETAF: str = "TCSETAF"; break;
33420Sstevel@tonic-gate case TCSETAW: str = "TCSETAW"; break;
33430Sstevel@tonic-gate case TCSBRK: str = "TCSBRK"; break;
33440Sstevel@tonic-gate case TCXONC: str = "TCXONC"; break;
33450Sstevel@tonic-gate case TCFLSH: str = "TCFLSH"; break;
33460Sstevel@tonic-gate case TCGETS: str = "TCGETS"; break;
33470Sstevel@tonic-gate case TCSETS: str = "TCSETS"; break;
33480Sstevel@tonic-gate case TCSETSF: str = "TCSETSF"; break;
33490Sstevel@tonic-gate case TCSETSW: str = "TCSETSW"; break;
33500Sstevel@tonic-gate case TIOCSBRK: str = "TIOCSBRK"; break;
33510Sstevel@tonic-gate case TIOCCBRK: str = "TIOCCBRK"; break;
33520Sstevel@tonic-gate case TIOCMSET: str = "TIOCMSET"; break;
33530Sstevel@tonic-gate case TIOCMBIS: str = "TIOCMBIS"; break;
33540Sstevel@tonic-gate case TIOCMBIC: str = "TIOCMBIC"; break;
33550Sstevel@tonic-gate case TIOCMGET: str = "TIOCMGET"; break;
33569354STim.Marsland@Sun.COM case TIOCSILOOP: str = "TIOCSILOOP"; break;
33579354STim.Marsland@Sun.COM case TIOCCILOOP: str = "TIOCCILOOP"; break;
33580Sstevel@tonic-gate case TCGETX: str = "TCGETX"; break;
33590Sstevel@tonic-gate case TCSETX: str = "TCGETX"; break;
33600Sstevel@tonic-gate case TCSETXW: str = "TCGETX"; break;
33610Sstevel@tonic-gate case TCSETXF: str = "TCGETX"; break;
33620Sstevel@tonic-gate default: str = "unknown"; break;
33630Sstevel@tonic-gate }
33640Sstevel@tonic-gate
33650Sstevel@tonic-gate return (str);
33660Sstevel@tonic-gate }
33672191Sszhou
33682191Sszhou /*
33692191Sszhou * Polled IO support
33702191Sszhou */
33712191Sszhou
33726898Sfb209375 /* called once by consconfig() when polledio is opened */
33732191Sszhou static int
usbser_polledio_init(usbser_port_t * pp)33742191Sszhou usbser_polledio_init(usbser_port_t *pp)
33752191Sszhou {
33762191Sszhou int err;
33772191Sszhou usb_pipe_handle_t hdl;
33782191Sszhou ds_ops_t *ds_ops = pp->port_ds_ops;
33792191Sszhou
33802191Sszhou /* only one serial line console supported */
33812191Sszhou if (console_input != NULL)
33822191Sszhou return (USB_FAILURE);
33832191Sszhou
33842191Sszhou /* check if underlying driver supports polled io */
33852191Sszhou if (ds_ops->ds_version < DS_OPS_VERSION_V1 ||
33862191Sszhou ds_ops->ds_out_pipe == NULL || ds_ops->ds_in_pipe == NULL)
33872191Sszhou return (USB_FAILURE);
33882191Sszhou
33892191Sszhou /* init polled input pipe */
33902191Sszhou hdl = ds_ops->ds_in_pipe(pp->port_ds_hdl, pp->port_num);
33912191Sszhou err = usb_console_input_init(pp->port_usp->us_dip, hdl,
33922191Sszhou &console_input_buf, &console_input);
33932191Sszhou if (err)
33942191Sszhou return (USB_FAILURE);
33952191Sszhou
33962191Sszhou /* init polled output pipe */
33972191Sszhou hdl = ds_ops->ds_out_pipe(pp->port_ds_hdl, pp->port_num);
33982191Sszhou err = usb_console_output_init(pp->port_usp->us_dip, hdl,
33992191Sszhou &console_output);
34002191Sszhou if (err) {
34012191Sszhou (void) usb_console_input_fini(console_input);
34022191Sszhou console_input = NULL;
34032191Sszhou return (USB_FAILURE);
34042191Sszhou }
34052191Sszhou
34062191Sszhou return (USB_SUCCESS);
34072191Sszhou }
34082191Sszhou
34096898Sfb209375 /* called once by consconfig() when polledio is closed */
34102191Sszhou /*ARGSUSED*/
usbser_polledio_fini(usbser_port_t * pp)34112191Sszhou static void usbser_polledio_fini(usbser_port_t *pp)
34122191Sszhou {
34132191Sszhou /* Since we can't move the console, there is nothing to do. */
34142191Sszhou }
34152191Sszhou
34162191Sszhou /*ARGSUSED*/
34172191Sszhou static void
usbser_polledio_enter(cons_polledio_arg_t arg)34182191Sszhou usbser_polledio_enter(cons_polledio_arg_t arg)
34192191Sszhou {
34202191Sszhou (void) usb_console_input_enter(console_input);
34212191Sszhou (void) usb_console_output_enter(console_output);
34222191Sszhou }
34232191Sszhou
34242191Sszhou /*ARGSUSED*/
34252191Sszhou static void
usbser_polledio_exit(cons_polledio_arg_t arg)34262191Sszhou usbser_polledio_exit(cons_polledio_arg_t arg)
34272191Sszhou {
34282191Sszhou (void) usb_console_output_exit(console_output);
34292191Sszhou (void) usb_console_input_exit(console_input);
34302191Sszhou }
34312191Sszhou
34322191Sszhou /*ARGSUSED*/
34332191Sszhou static void
usbser_putchar(cons_polledio_arg_t arg,uchar_t c)34342191Sszhou usbser_putchar(cons_polledio_arg_t arg, uchar_t c)
34352191Sszhou {
34362191Sszhou static uchar_t cr[2] = {'\r', '\n'};
34372191Sszhou uint_t nout;
34382191Sszhou
34392191Sszhou if (c == '\n')
34402191Sszhou (void) usb_console_write(console_output, cr, 2, &nout);
34412191Sszhou else
34422191Sszhou (void) usb_console_write(console_output, &c, 1, &nout);
34432191Sszhou }
34442191Sszhou
34452191Sszhou /*ARGSUSED*/
34462191Sszhou static int
usbser_getchar(cons_polledio_arg_t arg)34472191Sszhou usbser_getchar(cons_polledio_arg_t arg)
34482191Sszhou {
34492191Sszhou while (!usbser_ischar(arg))
34502191Sszhou ;
34512191Sszhou
34522191Sszhou return (*console_input_start++);
34532191Sszhou }
34542191Sszhou
34552191Sszhou /*ARGSUSED*/
34562191Sszhou static boolean_t
usbser_ischar(cons_polledio_arg_t arg)34572191Sszhou usbser_ischar(cons_polledio_arg_t arg)
34582191Sszhou {
34592191Sszhou uint_t num_bytes;
34602191Sszhou
34612191Sszhou if (console_input_start < console_input_end)
34629354STim.Marsland@Sun.COM return (B_TRUE);
34632191Sszhou
34642191Sszhou if (usb_console_read(console_input, &num_bytes) != USB_SUCCESS)
34659354STim.Marsland@Sun.COM return (B_FALSE);
34662191Sszhou
34672191Sszhou console_input_start = console_input_buf;
34682191Sszhou console_input_end = console_input_buf + num_bytes;
34692191Sszhou
34702191Sszhou return (num_bytes != 0);
34712191Sszhou }
3472