/*
* 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
* 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.
*/
/*
*
* USB generic serial driver (GSD)
*
*/
/* autoconfiguration subroutines */
static int usbser_free_soft_state(usbser_state_t *);
static int usbser_init_soft_state(usbser_state_t *);
static int usbser_fini_soft_state(usbser_state_t *);
static int usbser_attach_dev(usbser_state_t *);
static void usbser_detach_dev(usbser_state_t *);
static int usbser_attach_ports(usbser_state_t *);
static int usbser_create_port_minor_nodes(usbser_state_t *, int);
static void usbser_detach_ports(usbser_state_t *);
static int usbser_create_taskq(usbser_state_t *);
static void usbser_destroy_taskq(usbser_state_t *);
static void usbser_set_dev_state_init(usbser_state_t *);
/* hotplugging and power management */
static int usbser_disconnect_cb(dev_info_t *);
static int usbser_reconnect_cb(dev_info_t *);
static void usbser_disconnect_ports(usbser_state_t *);
static int usbser_cpr_suspend(dev_info_t *);
static int usbser_suspend_ports(usbser_state_t *);
static void usbser_cpr_resume(dev_info_t *);
static int usbser_restore_device_state(usbser_state_t *);
static void usbser_restore_ports_state(usbser_state_t *);
/* STREAMS subroutines */
cred_t *);
static int usbser_open_init(usbser_port_t *, int);
static void usbser_check_port_props(usbser_port_t *);
static void usbser_open_fini(usbser_port_t *);
static int usbser_open_line_setup(usbser_port_t *, int, int);
static int usbser_open_carrier_check(usbser_port_t *, int, int);
static void usbser_open_queues_fini(usbser_port_t *);
static void usbser_close_drain(usbser_port_t *);
static void usbser_close_cancel_break(usbser_port_t *);
static void usbser_close_hangup(usbser_port_t *);
static void usbser_close_cleanup(usbser_port_t *);
/* threads */
static void usbser_thr_dispatch(usbser_thread_t *);
static void usbser_thr_cancel(usbser_thread_t *);
static void usbser_thr_wake(usbser_thread_t *);
static void usbser_wq_thread(void *);
static void usbser_rq_thread(void *);
/* DSD callbacks */
static void usbser_tx_cb(caddr_t);
static void usbser_rx_cb(caddr_t);
mblk_t *);
static void usbser_status_cb(caddr_t);
static void usbser_status_proc_cb(usbser_port_t *);
/* serial support */
static void usbser_wmsg(usbser_port_t *);
static void usbser_restart(void *);
static int usbser_port_program(usbser_port_t *);
static void usbser_inbound_flow_ctl(usbser_port_t *);
/* misc */
static int usbser_dev_is_online(usbser_state_t *);
static void usbser_serialize_port_act(usbser_port_t *, int);
static void usbser_release_port_act(usbser_port_t *, int);
#ifdef DEBUG
static char *usbser_msgtype2str(int);
static char *usbser_ioctl2str(int);
#endif
/* USBA events */
usbser_disconnect_cb, /* disconnect */
usbser_reconnect_cb, /* reconnect */
NULL, /* pre-suspend */
NULL, /* pre-resume */
};
/* debug support */
/* usb serial console */
static int usbser_console_abort;
static int usbser_getchar(cons_polledio_arg_t);
static void usbser_polledio_enter(cons_polledio_arg_t);
static void usbser_polledio_exit(cons_polledio_arg_t);
static int usbser_polledio_init(usbser_port_t *);
static void usbser_polledio_fini(usbser_port_t *);
NULL, /* to be set later */
};
/* various statistics. TODO: replace with kstats */
static int usbser_st_tx_data_loss = 0;
static int usbser_st_rx_data_loss = 0;
static int usbser_st_put_stopi = 0;
static int usbser_st_mstop = 0;
static int usbser_st_mstart = 0;
static int usbser_st_mstopi = 0;
static int usbser_st_mstarti = 0;
static int usbser_st_rsrv = 0;
/* taskq parameter */
extern pri_t minclsyspri;
/*
* tell warlock not to worry about STREAMS structures
*/
/*
* modload support
*/
extern struct mod_ops mod_miscops;
&mod_miscops, /* Type of module */
"USB generic serial module"
};
};
/*
* loadable module entry points
* ----------------------------
*/
int
_init(void)
{
int err;
return (err);
}
int
_fini(void)
{
int err;
return (err);
return (0);
}
int
{
}
/*
* soft state size
*/
int
{
return (sizeof (usbser_state_t));
}
/*
* autoconfiguration entry points
* ------------------------------
*/
/*ARGSUSED*/
int
{
int instance;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
ret = DDI_SUCCESS;
}
}
break;
case DDI_INFO_DEVT2INSTANCE:
ret = DDI_SUCCESS;
break;
default:
break;
}
return (ret);
}
/*
* device attach
*/
};
static void
{
tmp = usbser_list;
usbser_list = usp;
else {
}
}
static void
{
tmp = usbser_list;
}
if (prev)
else
}
/*
* Return the first serial device, with dip held. This is called
* from the console subsystem to place console on usb serial device.
*/
usbser_first_device(void)
{
if (usbser_list) {
}
return (dip);
}
int
{
int instance;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/* allocate and get soft state */
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
} else {
return (DDI_FAILURE);
}
}
/*
* device detach
*/
int
{
int rval;
switch (cmd) {
case DDI_DETACH:
"usbser_detach.%d: end", instance);
return (DDI_SUCCESS);
case DDI_SUSPEND:
default:
return (DDI_FAILURE);
}
}
/*
* STREAMS entry points
* --------------------
*
*
* port open
*/
/*ARGSUSED*/
int
void *statep)
{
int instance;
int rval;
if (instance < 0) {
return (ENXIO);
}
return (ENXIO);
}
/* don't allow to open disconnected device */
return (ENXIO);
}
/* get port soft state */
return (ENXIO);
}
/* set up everything for open */
return (rval);
}
/*
* port close
*
* some things driver should do when the last app closes the line:
*
* drain data;
* hangup line (if necessary);
* DSD close;
* cleanup soft state;
*/
/*ARGSUSED*/
int
{
int online;
return (ENXIO);
}
/*
* in the closing state new activities will not be initiated
*/
if (online) {
/* drain the data */
}
if (online) {
/* hangup line */
}
/*
* close DSD, cleanup state and transition to 'closed' state
*/
return (0);
}
/*
* read side service routine: send as much as possible messages upstream
* and if there is still place on the queue, enable receive (if not already)
*/
int
{
}
if (canputnext(q)) {
if (USBSER_PORT_ACCESS_OK(pp)) {
}
}
return (0);
}
/*
* wput: put message on the queue and wake wq thread
*/
int
{
/* ignore new messages if port is already closing */
/*
* this counter represents amount of tx data on the wq.
* each time the data is passed to DSD for transmission,
* the counter is decremented accordingly
*/
} else {
}
return (0);
}
/*
* we need wsrv() routine to take advantage of STREAMS flow control:
* without it the framework will consider we are always able to process msgs
*/
int
{
if (USBSER_PORT_ACCESS_OK(pp)) {
}
return (0);
}
/*
* power entry point
*/
int
{
void *statep;
int new_state;
int rval;
"usbser_power: dip=0x%p, comp=%d, level=%d",
/* let DSD do the job */
/* stay in sync with DSD */
}
/*
*
* configuration entry point subroutines
* -------------------------------------
*
* rseq callback
*/
static int
{
if (rval != DDI_SUCCESS) {
return (RSEQ_UNDO);
} else {
return (RSEQ_OK);
}
}
/*
* free soft state
*/
static int
{
return (USB_SUCCESS);
}
/*
* init instance soft state
*/
static int
{
0);
/* save state pointer for use in event callbacks */
return (DDI_SUCCESS);
}
/*
* fini instance soft state
*/
static int
{
return (DDI_SUCCESS);
}
/*
* attach entire device
*/
static int
{
int rval;
(usp->us_port_cnt == 0)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* detach entire device
*/
static void
{
}
/*
* attach each individual port
*/
static int
{
int i;
/*
* allocate port array
*/
sizeof (usbser_port_t), KM_SLEEP);
/* callback handlers */
/*
* initialize each port
*/
for (i = 0; i < usp->us_port_cnt; i++) {
/*
* initialize data
*/
/* allocate log handle */
&usbser_instance_debug, 0);
/*
* init threads
*/
/*
* register callbacks
*/
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*
* create a pair of minor nodes for the port
*/
static int
{
/*
* tty node
*/
return (USB_FAILURE);
}
/*
* dial-out node
*/
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* detach each port individually
*/
static void
{
int i;
int sz;
/*
* remove all minor nodes
*/
for (i = 0; i < usp->us_port_cnt; i++) {
continue;
}
}
/*
* free memory
*/
}
/*
* create a taskq with two threads per port (read and write sides)
*/
static int
{
nthr, TASKQ_DEFAULTPRI, 0);
}
static void
{
}
static void
{
}
/*
* hotplugging and power management
* ---------------------------------
*
* disconnect event callback
*/
/*ARGSUSED*/
static int
{
void *statep;
"usbser_disconnect_cb: dip=%p", (void *)dip);
switch (usp->us_dev_state) {
case USB_DEV_ONLINE:
case USB_DEV_PWRED_DOWN:
/* prevent further activity */
/* see if any of the ports are open and do necessary handling */
/* call DSD to do any necessary work */
"usbser_disconnect_cb: ds_disconnect failed");
}
break;
case USB_DEV_SUSPENDED:
/* we remain suspended */
default:
break;
}
return (USB_SUCCESS);
}
/*
* reconnect event callback
*/
/*ARGSUSED*/
static int
{
void *statep;
"usbser_reconnect_cb: dip=%p", (void *)dip);
(void) usbser_restore_device_state(usp);
return (USB_SUCCESS);
}
/*
* if any of the ports is open during disconnect,
* send M_HANGUP message upstream and log a warning
*/
static void
{
int complain = 0;
int hangup = 0;
int i;
return;
}
for (i = 0; i < usp->us_port_cnt; i++) {
USBSER_IS_OPENING(pp) ||
complain = 1;
}
/*
* hangup the stream; will send actual
* M_HANGUP message after releasing mutex
*/
hangup = 1;
/*
* cancel all activities
*/
pp->port_delay_id = 0;
/* mark disconnected */
}
if (hangup) {
hangup = 0;
}
/*
* we couldn't untimeout while holding the mutex - do it now
*/
if (delay_id) {
delay_id = 0;
}
}
/*
* complain about disconnecting device while open
*/
if (complain) {
"disconnected while open. Data may have been lost");
}
}
/*
* do CPR suspend
*
* We use a trivial CPR strategy - fail if any of the device's ports are open.
* The problem with more sophisticated strategies is that each open port uses
* two threads that sit in the loop until the port is closed, while CPR has to
* stop all kernel threads to succeed. Stopping port threads is a rather
* intrusive and delicate procedure; I leave it as an RFE for now.
*
*/
static int
{
void *statep;
int new_state;
int rval;
/* suspend each port first */
"usbser_cpr_suspend: GSD failure");
return (USB_FAILURE);
}
if (new_state == USB_DEV_SUSPENDED) {
rval = USB_SUCCESS;
} else {
rval = USB_FAILURE;
}
return (rval);
}
static int
{
int i;
for (i = 0; i < usp->us_port_cnt; i++) {
return (USB_FAILURE);
}
}
return (USB_SUCCESS);
}
/*
* do CPR resume
*
* DSD will return USB_DEV_ONLINE in case of success
*/
static void
{
void *statep;
(void) usbser_restore_device_state(usp);
}
/*
* restore device state after CPR resume or reconnect
*/
static int
{
/* needed as power up state of dev is "unknown" to system */
(current_state == USB_DEV_SUSPENDED));
/*
* call DSD to perform device-specific work
*/
if (current_state == USB_DEV_DISCONNECTED) {
} else {
}
if (new_state == USB_DEV_ONLINE) {
/*
* restore ports state
*/
}
return (USB_SUCCESS);
}
/*
*/
static void
{
int i;
for (i = 0; i < usp->us_port_cnt; i++) {
/*
* only care about ports that are open
*/
continue;
}
/*
* if the stream was hung up during disconnect, restore it
*/
}
/*
* restore serial parameters
*/
(void) usbser_port_program(pp);
/*
* wake anything that might be sleeping
*/
}
}
/*
* STREAMS subroutines
* -------------------
*
*
* port open state machine
*
* here's a list of things that the driver has to do while open;
* because device can be opened any number of times,
* initial open has additional responsibilities:
*
* if (initial_open) {
* initialize soft state; \
* DSD open; - see usbser_open_init()
* dispatch threads; /
* }
* raise DTR;
* wait for carrier (if necessary);
*
* we should also take into consideration that two threads can try to open
*
* return values:
* 0 - success;
* >0 - fail with this error code;
*/
static int
{
/*
* refer to port state diagram in the header file
*/
loop:
switch (pp->port_state) {
case USBSER_PORT_CLOSED:
/*
* initial open
*/
break;
case USBSER_PORT_OPENING_TTY:
/*
* dial-out thread can overtake the port
* if tty open thread is sleeping waiting for carrier
*/
"usbser_open_state: overtake");
}
/* FALLTHRU */
case USBSER_PORT_OPENING_OUT:
/*
* if no other open in progress, setup the line
*/
break;
}
/* FALLTHRU */
case USBSER_PORT_CLOSING:
/*
* wait until close active phase ends
*/
}
break;
case USBSER_PORT_OPEN:
secpolicy_excl_open(cr) != 0) {
/*
* exclusive use
*/
/*
* tty and dial-out modes are mutually exclusive
*/
} else {
/*
* port is being re-open in the same mode
*/
}
break;
default:
break;
}
if (rval == USBSER_CONTINUE) {
goto loop;
}
/*
* initial open requires additional handling
*/
if (USBSER_IS_OPENING(pp)) {
if (rval == USBSER_COMPLETE) {
}
} else {
}
}
return (rval);
}
/*
* initialize the port when opened for the first time
*/
static int
{
/*
* init state
*/
pp->port_wq_data_cnt = 0;
} else {
}
/*
* init termios settings
*/
/*
* dispatch wq and rq threads:
* although queues are not enabled at this point,
* we will need wq to run status processing callback
*/
/*
* open DSD port
*/
if (rval != USB_SUCCESS) {
return (ENXIO);
}
/*
* program port with default parameters
*/
return (ENXIO);
}
return (USBSER_CONTINUE);
}
/*
* create a pair of minor nodes for the port
*/
static void
{
/*
* take default modes from "ttymodes" property if it exists
*/
}
}
}
/*
* look for "ignore-cd" or "port-N-ignore-cd" property
*/
"ignore-cd", 0) ||
} else {
}
}
/*
* undo what was done in usbser_open_init()
*/
static void
{
/*
* close DSD if it is open
*/
"usbser_open_fini: CLOSE_PORT fail");
}
}
/*
* cancel threads
*/
/*
* unpdate soft state
*/
}
/*
* setup serial line
*/
static int
{
int rval;
/*
* prevent opening a disconnected device
*/
return (ENXIO);
}
/* raise DTR on every open */
/*
* check carrier
*/
return (rval);
}
/*
* check carrier and wait if needed
*/
static int
{
int val = 0;
int rval;
}
/*
* check carrier
*/
return (ENXIO);
} else {
}
/*
* don't block if 1) not allowed to, 2) this is a local device,
* 3) opening in dial-out mode, or 4) carrier is already on
*/
return (USBSER_COMPLETE);
}
/*
* block until carrier up (only in tty mode)
*/
"usbser_open_carrier_check: waiting for carrier...");
if (rval == 0) {
/*
* interrupted with a signal
*/
return (EINTR);
} else {
/*
* try again
*/
return (USBSER_CONTINUE);
}
}
/*
* during open, setup queues and message processing
*/
static void
{
}
/*
* clean up queues and message processing
*/
static void
{
/*
* clean up queues
*/
/*
* free unused messages
*/
}
/*
* during close, wait until pending data is gone or the signal is sent
*/
static void
{
int need_drain;
/*
* port_wq_data_cnt indicates amount of data on the write queue,
* which becomes zero when all data is submitted to DSD. But usbser
* stays busy until it gets tx callback from DSD, signalling that
* data has been sent over USB. To be continued in the next comment...
*/
until = ddi_get_lbolt() +
until)) <= 0) {
break;
}
}
/* don't drain if timed out or received a signal */
(rval != USB_SUCCESS);
/*
* Once the data reaches USB serial box, it may still be stored in its
* internal output buffer (FIFO). We call DSD drain to ensure that all
* the data is transmitted transmitted over the serial line.
*/
if (need_drain) {
if (rval != USB_SUCCESS) {
}
} else {
}
}
/*
*/
static void
{
pp->port_delay_id = 0;
}
}
/*
*/
static void
{
/*
* drop DTR and RTS if HUPCL is set
*/
}
}
/*
* state cleanup during close
*/
static void
{
}
/*
*
* thread management
* -----------------
*
*
* dispatch a thread
*/
static void
{
int rval;
}
/*
* cancel a thread
*/
static void
{
/* wait until the thread actually exits */
do {
}
/*
* wake thread
*/
static void
{
}
/*
* thread handling write queue requests
*/
static void
{
/*
* when woken, see what we should do
*/
/*
* status callback pending?
*/
}
} else {
/*
* sleep until woken up to do some work, e.g:
* - new message arrives;
* - data transmit completes;
* - status callback pending;
* - wq thread is cancelled;
*/
"usbser_wq_thread: wakeup");
}
}
}
/*
* thread handling read queue requests
*/
static void
{
/*
* read service routine will wake us when
* more space is available on the read queue
*/
/*
* don't process messages until queue is enabled
*/
continue;
}
/*
* check whether we need to resume receive
*/
}
/*
* grab more data if available
*/
} else {
"usbser_rq_thread: wakeup");
}
}
}
/*
* DSD callbacks
* -------------
*
* Note: to avoid deadlocks with DSD, these callbacks
* should not call DSD functions that can block.
*
*
* transmit callback
*
* invoked by DSD when the last byte of data is transmitted over USB
*/
static void
{
int online;
(void *)curthread);
/*
* as long as port access is ok and the port is not busy on
* TX, break, ctrl or delay, the wq_thread should be waken
* to do further process for next message
*/
/*
*/
}
}
/*
* receive callback
*
* invoked by DSD when there is more data for us to pick
*/
static void
{
return;
}
/* get data from DSD */
return;
}
if ((!USBSER_PORT_ACCESS_OK(pp)) ||
"usbser_rx_cb: access not ok or receiver disabled");
return;
}
/*
* DSD data is a b_cont-linked list of M_DATA and M_BREAK blocks.
* M_DATA is correctly received data.
* M_BREAK is a character with either framing or parity error.
*
* this loop runs through the list of mblks. when it meets an M_BREAK,
* it sends all leading M_DATA's in one shot, then sends M_BREAK.
* in the trivial case when list contains only M_DATA's, the loop
* does nothing but set data variable.
*/
while (mp) {
/*
* skip data until we meet M_BREAK or end of list
*/
}
continue;
}
/* detach data list from mp */
if (data_tail) {
}
/* detach emp from the list */
/* DSD shouldn't send anything but M_DATA or M_BREAK */
"usbser_rx_cb: bad message");
continue;
}
/*
* first tweak and send M_DATA's
*/
if (data) {
}
/*
* now tweak and send M_BREAK
*/
}
/* send the rest of the data, if any */
if (data) {
}
}
/*
* the joys of termio -- this is to accomodate Unix98 assertion:
*
* If PARENB is supported and is set, when PARMRK is set, and CSIZE is
* set to CS8, and IGNPAR is clear, and ISTRIP is clear, a valid
* character of '\377' is read as '\377', '\377'.
*
* Posix Ref: Assertion 7.1.2.2-16(C)
*
* this requires the driver to scan every incoming valid character
*/
static void
{
uchar_t *p;
int tailsz;
/* avoid scanning if possible */
return;
}
while (mp) {
if (*p++ != 0377) {
continue;
}
"usbser_rx_massage_data: mp=%p off=%ld(%ld)",
/*
* insert another 0377 after this one. all data after
* the original 0377 have to be copied to the new mblk
*/
"usbser_rx_massage_data: allocb failed");
continue;
}
/* fill in the new mblk */
if (tailsz > 0) {
}
/* shrink the original mblk */
}
}
}
/*
* more joys of termio
*/
static void
{
/* break */
/* Posix Ref: Assertion 7.1.2.2-20(C) */
} else {
/* for ldterm to handle */
}
"usbser_rx_massage_mbreak: type=%x len=%ld [0]=0%o",
}
/*
* in rx callback, try to send an mblk upstream
*/
static void
{
if (canputnext(rq)) {
/*
* full queue indicates the need for inbound flow control
*/
"usbser_rx_cb: cannot putnext, flow ctl");
} else {
"input overrun");
}
}
/*
* modem status change callback
*
* each time external status lines are changed, DSD calls this routine
*/
static void
{
return;
}
/*
* actual processing will be done in usbser_status_proc_cb()
* running in wq thread
*/
}
}
/*
* modem status change
*/
static void
{
int status;
int drop_dtr = 0;
return;
}
/* get modem status */
return;
}
/*
* outbound flow control
*/
/*
* CTS dropped, stop xmit
*/
}
/*
* CTS raised, resume xmit
*/
}
}
/*
* check carrier
*/
/*
* carrier present
*/
rq_msg = M_UNHANGUP;
/*
* wake open
*/
}
"usbser_status_cb: carr on");
}
/*
* carrier went away: if not local line, drop DTR
*/
drop_dtr = 1;
}
}
"usbser_status_cb: carr off");
}
/*
* commit postponed actions now
* do so only if port is fully open (queues are enabled)
*/
if (rq) {
if (rq_msg) {
}
if (drop_dtr) {
}
if (wq_msg) {
}
}
}
/*
* serial support
* --------------
*
*
* this routine is run by wq thread every time it's woken,
* i.e. when the queue contains messages to process
*/
static void
{
int msgtype;
if (q == NULL) {
return;
}
switch (msgtype) {
/*
* high-priority messages
*/
case M_STOP:
break;
case M_START:
break;
case M_STOPI:
break;
case M_STARTI:
break;
case M_IOCDATA:
break;
case M_FLUSH:
break;
/*
* normal-priority messages
*/
case M_BREAK:
break;
case M_DELAY:
break;
case M_DATA:
return;
}
break;
case M_IOCTL:
return;
}
break;
default:
break;
}
}
}
/*
* process M_DATA message
*/
static int
{
/* put off until current transfer ends or delay is over */
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/* DSD is required to accept data block in any case */
return (USB_SUCCESS);
}
/*
* process an M_IOCTL message
*/
static int
{
int cmd;
int val;
/*
* We were holding an ioctl response pending the
* availability of an mblk to hold data to be passed up;
* another ioctl came through, which means that ioctl
* must have timed out or been aborted.
*/
}
switch (cmd) {
case TIOCMGET:
case TIOCMBIC:
case TIOCMBIS:
case TIOCMSET:
case CONSOPENPOLLEDIO:
case CONSCLOSEPOLLEDIO:
case CONSSETABORTENABLE:
case CONSGETABORTENABLE:
/*
* For the above ioctls do not call ttycommon_ioctl() because
* this function frees up the message block (mp->b_cont) that
* contains the address of the user variable where we need to
* pass back the bit array.
*/
error = -1;
break;
case TCSBRK:
/* serialize breaks */
return (USB_FAILURE);
/*FALLTHRU*/
default:
break;
}
if (error == 0) {
/*
* ttycommon_ioctl() did most of the work
* we just use the data it set up
*/
switch (cmd) {
case TCSETSF:
case TCSETSW:
case TCSETA:
case TCSETAW:
case TCSETAF:
/*FALLTHRU*/
case TCSETS:
break;
}
goto end;
} else if (error > 0) {
"ttycommon_ioctl returned %d", error);
goto end;
}
/*
* error < 0: ttycommon_ioctl() didn't do anything, we process it here
*/
error = 0;
switch (cmd) {
case TCSBRK:
break;
/* drain output */
/*
* if required, set break
*/
break;
}
drv_usectohz(250000));
}
break;
case TIOCSBRK: /* set break */
else
break;
case TIOCCBRK: /* clear break */
else
break;
case TIOCMSET: /* set all modem bits */
case TIOCMBIS: /* bis modem bits */
case TIOCMBIC: /* bic modem bits */
break;
}
break;
}
if (rval == USB_SUCCESS)
else
break;
case TIOCSILOOP:
if (USBSER_DS_LOOPBACK_SUPPORTED(pp)) {
else
} else {
}
break;
case TIOCCILOOP:
if (USBSER_DS_LOOPBACK_SUPPORTED(pp)) {
else
} else {
}
break;
case TIOCMGET: /* get all modem bits */
break;
}
if (rval != USB_SUCCESS) {
break;
}
else
break;
case CONSOPENPOLLEDIO:
if (error != 0)
break;
if (error != 0)
break;
break;
case CONSCLOSEPOLLEDIO:
break;
case CONSSETABORTENABLE:
if (error != 0)
break;
break;
}
/*
* To do: implement console abort support
* This involves adding a console flag to usbser
* state structure. If flag is set, parse input stream
* for abort sequence (see asy for example).
*
* For now, run mdb -K to get kmdb prompt.
*/
usbser_console_abort = 1;
else
usbser_console_abort = 0;
break;
case CONSGETABORTENABLE:
/*CONSTANTCONDITION*/
/*
* Store the return value right in the payload
* we were passed. Crude.
*/
break;
default:
break;
}
end:
if (error != 0)
else
return (USB_SUCCESS);
}
/*
* process M_IOCDATA message
*/
static void
{
int cmd;
int val;
return;
}
switch (cmd) {
case TIOCMSET: /* set all modem bits */
case TIOCMBIS: /* bis modem bits */
case TIOCMBIC: /* bic modem bits */
break;
}
}
}
if (rval == USB_SUCCESS)
else
break;
case TIOCMGET: /* get all modem bits */
break;
default:
break;
}
}
/*
* handle M_START[I]/M_STOP[I] messages
*/
static void
{
}
}
static void
{
}
}
static void
{
}
static void
{
}
/*
* process M_FLUSH message
*/
static void
{
}
/*
* flush FIFO buffers
*/
} else {
}
}
/*
* process M_BREAK message
*/
static void
{
int rval;
/*
* set the break and arrange for usbser_restart() to be called in 1/4 s
*/
if (rval == USB_SUCCESS) {
drv_usectohz(250000));
}
}
/*
* process M_DELAY message
*/
static void
{
/*
* arrange for usbser_restart() to be called when the delay expires
*/
}
/*
* restart output on a line after a delay or break timer expired
*/
static void
{
/* if cancelled, return immediately */
if (pp->port_delay_id == 0) {
return;
}
pp->port_delay_id = 0;
/* clear break if necessary */
}
/* wake wq thread to resume message processing */
}
/*
* program port hardware with the chosen parameters
* most of the operation is based on the values of 'c_iflag' and 'c_cflag'
*/
static int
{
int baudrate;
int c_flag;
int err = 0;
baudrate += 16;
}
/*
* set input speed same as output, as split speed not supported
*/
} else {
}
}
/*
* flow control
*/
}
}
/*
* fill in port parameters we need to set:
*
* baud rate
*/
/* stop bits */
/* parity */
/* char size */
/* start & stop chars */
/* flow control */
/* control signals */
if (baudrate == 0) {
}
}
/* submit */
if (err != USB_SUCCESS) {
return (EINVAL);
}
}
/*
* check if any inbound flow control action needed
*/
static void
{
int rts;
char c = pp->port_flowc;
"usbser_inbound_flow_ctl: c=%x cflag=%x port_flags=%x",
if (c == '\0') {
return;
}
/*
* if inbound hardware flow control enabled, we need to frob RTS
*/
} else {
rts = 0;
}
/*
* if character flow control active, transmit a start or stop char,
*/
"usbser_inbound_flow_ctl: allocb failed");
} else {
}
}
if (need_hw) {
}
if (mp) {
}
}
/*
* misc
* ----
*
*
* returns != 0 if device is online, 0 otherwise
*/
static int
{
int rval;
return (rval);
}
/*
* serialize port activities defined by 'act' mask
*/
static void
{
}
/*
* indicate that port activity is finished
*/
static void
{
}
#ifdef DEBUG
/*
* message type to string and back conversion.
*
* pardon breaks on the same line, but as long as cstyle doesn't
* complain, I'd like to keep this form for trivial cases like this.
* associative arrays in the kernel, anyone?
*/
static char *
{
char *str;
switch (type) {
default: str = "unknown"; break;
}
return (str);
}
static char *
{
char *str;
switch (ioctl) {
default: str = "unknown"; break;
}
return (str);
}
#endif
/*
* Polled IO support
*/
/* called once by consconfig() when polledio is opened */
static int
{
int err;
/* only one serial line console supported */
if (console_input != NULL)
return (USB_FAILURE);
/* check if underlying driver supports polled io */
return (USB_FAILURE);
/* init polled input pipe */
if (err)
return (USB_FAILURE);
/* init polled output pipe */
if (err) {
(void) usb_console_input_fini(console_input);
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/* called once by consconfig() when polledio is closed */
/*ARGSUSED*/
{
/* Since we can't move the console, there is nothing to do. */
}
/*ARGSUSED*/
static void
{
(void) usb_console_input_enter(console_input);
(void) usb_console_output_enter(console_output);
}
/*ARGSUSED*/
static void
{
(void) usb_console_output_exit(console_output);
(void) usb_console_input_exit(console_input);
}
/*ARGSUSED*/
static void
{
if (c == '\n')
else
}
/*ARGSUSED*/
static int
{
while (!usbser_ischar(arg))
;
return (*console_input_start++);
}
/*ARGSUSED*/
static boolean_t
{
return (B_TRUE);
return (B_FALSE);
return (num_bytes != 0);
}