root/usr/src/uts/common/sys/usb/clients/usbser/usbser_var.h
/*
 * 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 <sys/tty.h>
#include <sys/mkdev.h>
#include <sys/sunddi.h>
#include <sys/note.h>

#include <sys/usb/clients/usbser/usbser_dsdi.h>

#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 */