/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_USB_USBSER_VAR_H #define _SYS_USB_USBSER_VAR_H /* * USB-to-serial driver definitions */ #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif typedef struct usbser_state usbser_state_t; typedef struct usbser_port usbser_port_t; /* * because put() and srv() routines are not allowed to block, usbser * provides each port with two threads: for read and write processing * this structure describes the data associated with a usbser thread */ typedef struct usbser_thread { kcondvar_t thr_cv; /* cv for request wait */ uint_t thr_flags; /* state flags */ usbser_port_t *thr_port; /* port owner of this thread */ void (*thr_func)(void *); /* function to be run */ void *thr_arg; /* function argument */ } usbser_thread_t; /* * thr_flags */ enum { USBSER_THR_RUNNING = 0x01, /* thread is running */ USBSER_THR_WAKE = 0x02, /* wake requested */ USBSER_THR_EXITED = 0x04 /* thread exited */ }; /* * additional device state */ #define USBSER_DEV_INIT 0x80 /* device is being initialized */ /* * per instance data */ struct usbser_state { struct usbser_state *us_next; /* linked list */ dev_info_t *us_dip; /* device information */ kmutex_t us_mutex; /* structure lock */ void *us_statep; /* soft state anchor */ int us_instance; /* instance number */ ds_ops_t *us_ds_ops; /* DSD operations */ ds_hdl_t us_ds_hdl; /* DSD device handle */ uint_t us_port_cnt; /* port count */ usbser_port_t *us_ports; /* array of port structs */ uint_t us_dev_state; /* USB device state */ usb_log_handle_t us_lh; /* USB log handle */ ddi_taskq_t *us_taskq; /* taskq for command handling */ }; _NOTE(MUTEX_PROTECTS_DATA(usbser_state::us_mutex, usbser_state::us_dev_state)) /* * per port data */ struct usbser_port { kmutex_t port_mutex; /* structure lock */ usbser_state_t *port_usp; /* back pointer to state */ char port_lh_name[16]; /* log handle name */ usb_log_handle_t port_lh; /* log handle */ ds_ops_t *port_ds_ops; /* copy from usbser_state */ ds_hdl_t port_ds_hdl; /* copy from usbser_state */ uint_t port_num; /* port number */ uint_t port_state; /* port state */ uint_t port_act; /* current activities on port */ uint_t port_flags; /* port flags */ kcondvar_t port_state_cv; /* port state cv */ kcondvar_t port_act_cv; /* port activity cv */ kcondvar_t port_car_cv; /* port carrier cv */ uint_t port_wq_data_cnt; /* amount of unsent data */ usbser_thread_t port_wq_thread; /* wq thread */ usbser_thread_t port_rq_thread; /* rq thread */ tty_common_t port_ttycommon; /* tty driver common data */ uchar_t port_flowc; /* flow control char */ timeout_id_t port_delay_id; /* delay/break timeout id */ }; _NOTE(MUTEX_PROTECTS_DATA(usbser_port::port_mutex, usbser_port)) _NOTE(DATA_READABLE_WITHOUT_LOCK(usbser_port::{ port_usp port_lh port_ds_ops port_ds_hdl port_num port_ttycommon.t_{readq writeq} })) _NOTE(LOCK_ORDER(usbser_state::us_mutex usbser_port::port_mutex)) /* * port_state: * * USBSER_PORT_NOT_INIT * | ^ * | | * attach detach * | | * | | +----open[1]----> USBSER_PORT_OPENING_TTY ------+ * | | | | | | * v | | | | | * USBSER_PORT_CLOSED <---device error---< overtake[2] | * | | | | v * | | | v | * | +----open[1]----> USBSER_PORT_OPENING_OUT | * | | | * | | +-------------------+ * | | | * | v v * USBSER_PORT_CLOSING <-----close----- USBSER_PORT_OPEN <-----------+ * ^ | ^ --------+ | * | | | | | * | | | | | * | v | v | * +------close----- USBSER_PORT_DISCONNECTED USBSER_PORT_SUSPENDED * * Notes: * * [1] for each physical port N two device nodes are created: * * /dev/term/N (tty mode) * /dev/cua/N (dial-out mode) * * the port can only be opened in one of these modes at a time. * difference between the two is that in tty mode the driver * will block in open(9E) until the CD (Carrier Detect) pin comes up, * while in dial-out mode CD is ignored. opening and closing states * help to avoid race conditions between two threads trying to open/close * one physical port in two different modes simultaneously. * * [2] tty mode open may be blocked waiting for carrier. * if dial-out mode open happens at this time, it is allowed * for it to overtake the port; from zs(4D) man page: * * This allows a modem to be attached to /dev/term/[n] * and used for dial-in (by enabling the line for login in /etc/inittab) * and also used for dial-out (by tip(1) or uucp(1C)) as /dev/cua/[n] * when no one is logged in on the line. */ enum { USBSER_PORT_NOT_INIT = 0, /* port not initialized */ USBSER_PORT_CLOSED, /* port is closed */ USBSER_PORT_OPENING_TTY, /* tty open in progress */ USBSER_PORT_OPENING_OUT, /* dial-out open in progress */ USBSER_PORT_OPEN, /* port is open */ USBSER_PORT_SUSPENDED, /* port is suspended */ USBSER_PORT_DISCONNECTED, /* port is disconnected */ USBSER_PORT_CLOSING /* close() is in progress */ }; /* constants used by state machine implementation */ enum { USBSER_CONTINUE = -1, USBSER_COMPLETE = 0 }; /* * port_act: current activities on the port. * only one activity of each type is allowed at a time. */ enum { USBSER_ACT_TX = 0x0001, /* transmitting data */ USBSER_ACT_RX = 0x0002, /* receiving data */ USBSER_ACT_CTL = 0x0004, /* controlling the device */ USBSER_ACT_BREAK = 0x0010, /* doing break */ USBSER_ACT_DELAY = 0x0020, /* doing delay */ USBSER_ACT_ALL = 0xffff /* all actions (must be >0) */ }; /* * port_flags */ enum { USBSER_FL_OUT = 0x0001, /* dial-out */ USBSER_FL_WOPEN = 0x0002, /* waiting in open() */ USBSER_FL_CARR_ON = 0x0004, /* carrier is on */ USBSER_FL_TX_STOPPED = 0x0008, /* output stopped */ USBSER_FL_RX_STOPPED = 0x0010, /* input stopped */ USBSER_FL_HUNGUP = 0x0020, /* stream is hung up */ USBSER_FL_DSD_OPEN = 0x0040, /* DSD is open */ USBSER_FL_STATUS_CB = 0x0080, /* status callback pending */ USBSER_FL_IGNORE_CD = 0x0100, /* ignore carrier detect */ USBSER_FL_PRESERVE = USBSER_FL_IGNORE_CD /* flags that need to */ /* be preserved across opens */ }; /* * current sun compiler does not seem to inline static leaf routines at O3 * so we have to use preprocessor macros to make up for compiler disability * * can we access the port? */ #define USBSER_PORT_ACCESS_OK(pp) ((pp)->port_state == USBSER_PORT_OPEN) /* * is port doing something? */ #define USBSER_PORT_IS_BUSY(pp) ((pp)->port_act != 0) /* port is busy on TX, delay, break, ctrl */ #define USBSER_PORT_IS_BUSY_NON_RX(pp) \ (((pp)->port_act & (USBSER_ACT_DELAY | USBSER_ACT_CTL | \ USBSER_ACT_BREAK | USBSER_ACT_TX)) != 0) /* * is the port opening? */ #define USBSER_IS_OPENING(pp) \ (((pp)->port_state == USBSER_PORT_OPENING_TTY) || \ ((pp)->port_state == USBSER_PORT_OPENING_OUT)) /* * determine, while we are trying to open the port, * whether it is currently being open in the opposite mode */ #define USBSER_NO_OTHER_OPEN(pp, minor) \ ((((minor) & OUTLINE) && \ ((pp)->port_state == USBSER_PORT_OPENING_OUT)) || \ (!((minor) & OUTLINE) && ((pp)->port_state == USBSER_PORT_OPENING_TTY))) /* * determine, while we are trying to open the port, * whether it is already open in the opposite mode */ #define USBSER_OPEN_IN_OTHER_MODE(pp, minor) \ ((((minor) & OUTLINE) && !((pp)->port_flags & USBSER_FL_OUT)) || \ (!((minor) & OUTLINE) && ((pp)->port_flags & USBSER_FL_OUT))) /* * minor number manipulation */ enum { MAXPORTS_PER_DEVICE_SHIFT = 4, MAXPORTS_PER_DEVICE = (1 << MAXPORTS_PER_DEVICE_SHIFT), MAXPORTS_PER_DEVICE_MASK = (MAXPORTS_PER_DEVICE - 1), OUTLINE = (1 << (NBITSMINOR32 - 1)) }; #define USBSER_MAKEMINOR(instance, port, outline) \ ((port) | ((instance) << MAXPORTS_PER_DEVICE_SHIFT) | (outline)) #define USBSER_MINOR2INST(minor) \ (((minor) & ~(OUTLINE | MAXPORTS_PER_DEVICE_MASK)) \ >> MAXPORTS_PER_DEVICE_SHIFT) #define USBSER_MINOR2PORT(minor) ((minor) & MAXPORTS_PER_DEVICE_MASK) /* * various tunables * * timeouts are in seconds */ enum { USBSER_TX_FIFO_DRAIN_TIMEOUT = 5, /* tx fifo drain timeout */ USBSER_WQ_DRAIN_TIMEOUT = 2, /* wq drain timeout */ USBSER_SUSPEND_TIMEOUT = 10 /* cpr suspend timeout */ }; /* * debug printing masks */ #define DPRINT_ATTACH 0x00000001 #define DPRINT_DETACH 0x00000002 #define DPRINT_OPEN 0x00000004 #define DPRINT_CLOSE 0x00000008 #define DPRINT_WQ 0x00000010 #define DPRINT_RQ 0x00000020 #define DPRINT_IOCTL 0x00000040 #define DPRINT_RX_CB 0x00000100 #define DPRINT_TX_CB 0x00000200 #define DPRINT_STATUS_CB 0x00000400 #define DPRINT_EVENTS 0x00001000 #define DPRINT_CPR 0x00002000 #define DPRINT_MASK_ALL 0xFFFFFFFF /* * misc macros */ #define NELEM(a) (sizeof (a) / sizeof (*(a))) /* * shortcuts to DSD operations */ #define USBSER_DS_ATTACH(usp, aip) usp->us_ds_ops->ds_attach(aip) #define USBSER_DS_DETACH(usp) usp->us_ds_ops->ds_detach(usp->us_ds_hdl) #define USBSER_DS_OPEN_PORT(usp, port_num) \ usp->us_ds_ops->ds_open_port(usp->us_ds_hdl, port_num) #define USBSER_DS_CLOSE_PORT(usp, port_num) \ usp->us_ds_ops->ds_close_port(usp->us_ds_hdl, port_num) #define USBSER_DS_REGISTER_CB(usp, port_num, cb) \ usp->us_ds_ops->ds_register_cb(usp->us_ds_hdl, port_num, cb) #define USBSER_DS_UNREGISTER_CB(usp, port_num) \ usp->us_ds_ops->ds_unregister_cb(usp->us_ds_hdl, port_num) /* power management */ #define USBSER_DS_USB_POWER(usp, comp, level, new_statep) \ usp->us_ds_ops->ds_usb_power(usp->us_ds_hdl, comp, level, new_statep) #define USBSER_DS_SUSPEND(usp) usp->us_ds_ops->ds_suspend(usp->us_ds_hdl) #define USBSER_DS_RESUME(usp) usp->us_ds_ops->ds_resume(usp->us_ds_hdl) #define USBSER_DS_DISCONNECT(usp) usp->us_ds_ops->ds_disconnect(usp->us_ds_hdl) #define USBSER_DS_RECONNECT(usp) usp->us_ds_ops->ds_reconnect(usp->us_ds_hdl) /* standard UART operations */ #define USBSER_DS_SET_PORT_PARAMS(pp, params) \ pp->port_ds_ops->ds_set_port_params(pp->port_ds_hdl, pp->port_num, \ params) #define USBSER_DS_SET_MODEM_CTL(pp, mask, val) \ pp->port_ds_ops->ds_set_modem_ctl(pp->port_ds_hdl, pp->port_num, mask, \ val) #define USBSER_DS_GET_MODEM_CTL(pp, mask, valp) \ pp->port_ds_ops->ds_get_modem_ctl(pp->port_ds_hdl, pp->port_num, \ mask, valp) #define USBSER_DS_BREAK_CTL(pp, val) \ pp->port_ds_ops->ds_break_ctl(pp->port_ds_hdl, pp->port_num, val) #define USBSER_DS_LOOPBACK(pp, val) \ pp->port_ds_ops->ds_loopback(pp->port_ds_hdl, pp->port_num, val) /* data xfer */ #define USBSER_DS_TX(pp, mp) \ pp->port_ds_ops->ds_tx(pp->port_ds_hdl, pp->port_num, mp) #define USBSER_DS_RX(pp) \ pp->port_ds_ops->ds_rx(pp->port_ds_hdl, pp->port_num) #define USBSER_DS_STOP(pp, dir) \ pp->port_ds_ops->ds_stop(pp->port_ds_hdl, pp->port_num, dir) #define USBSER_DS_START(pp, dir) \ pp->port_ds_ops->ds_start(pp->port_ds_hdl, pp->port_num, dir) /* fifos */ #define USBSER_DS_FIFO_FLUSH(pp, mask) \ pp->port_ds_ops->ds_fifo_flush(pp->port_ds_hdl, pp->port_num, mask) #define USBSER_DS_FIFO_DRAIN(pp, tmout) \ pp->port_ds_ops->ds_fifo_drain(pp->port_ds_hdl, pp->port_num, tmout) /* check for supported operations */ #define USBSER_DS_LOOPBACK_SUPPORTED(pp) (pp->port_ds_ops->ds_loopback != 0) #ifdef __cplusplus } #endif #endif /* _SYS_USB_USBSER_VAR_H */