pl2303_dsd.c revision cbab2b2687744cbfdc12fae90f8088127a0b266c
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
*
* USB Prolific PL2303 device-specific driver (DSD)
*
*/
#define USBDRV_MAJOR_VER 2
#define USBDRV_MINOR_VER 0
/*
* DSD operations
*/
static int pl2303_attach(ds_attach_info_t *);
static void pl2303_detach(ds_hdl_t);
/* power management */
static int pl2303_usb_power(ds_hdl_t, int, int, int *);
static int pl2303_suspend(ds_hdl_t);
static int pl2303_resume(ds_hdl_t);
static int pl2303_disconnect(ds_hdl_t);
static int pl2303_reconnect(ds_hdl_t);
/* standard UART operations */
/* data xfer */
/* polled I/O support */
/*
* Sub-routines
*/
/* configuration routines */
static void pl2303_cleanup(pl2303_state_t *, int);
static int pl2303_dev_attach(pl2303_state_t *);
static int pl2303_open_hw_port(pl2303_state_t *);
/* hotplug */
static int pl2303_restore_device_state(pl2303_state_t *);
static int pl2303_restore_port_state(pl2303_state_t *);
/* power management */
static int pl2303_create_pm_components(pl2303_state_t *);
static void pl2303_destroy_pm_components(pl2303_state_t *);
static int pl2303_pm_set_busy(pl2303_state_t *);
static void pl2303_pm_set_idle(pl2303_state_t *);
static int pl2303_pwrlvl0(pl2303_state_t *);
static int pl2303_pwrlvl1(pl2303_state_t *);
static int pl2303_pwrlvl2(pl2303_state_t *);
static int pl2303_pwrlvl3(pl2303_state_t *);
/* pipe operations */
static int pl2303_open_pipes(pl2303_state_t *);
static void pl2303_close_pipes(pl2303_state_t *);
static void pl2303_disconnect_pipes(pl2303_state_t *);
static int pl2303_reconnect_pipes(pl2303_state_t *);
/* pipe callbacks */
/* data transfer routines */
static int pl2303_rx_start(pl2303_state_t *);
static void pl2303_tx_start(pl2303_state_t *, int *);
static int pl2303_wait_tx_drain(pl2303_state_t *, int);
/* vendor-specific commands */
static int pl2303_cmd_set_rtscts(pl2303_state_t *);
static int pl2303_cmd_break(pl2303_state_t *, int);
static int pl2303_reg2mctl(uint8_t);
/* misc */
/*
* DSD ops structure
*/
NULL, /* HW don't support loopback */
};
/*
* baud code into baud rate
* value 0 means not supported in hardware
*
*/
static int pl2303_speedtab[] = {
0, /* B0 */
0, /* B50 */
75, /* B75 */
0, /* B110 */
0, /* B134 */
150, /* B150 */
0, /* B200 */
300, /* B300 */
600, /* B600 */
1200, /* B1200 */
1800, /* B1800 */
2400, /* B2400 */
4800, /* B4800 */
9600, /* B9600 */
19200, /* B19200 */
38400, /* B38400 */
57600, /* B57600 */
0, /* B76800 */
115200, /* B115200 */
0, /* B153600 */
230400, /* B230400 */
0, /* B307200 */
460800 /* B460800 */
};
/* debug support */
/*
* ds_attach
*/
static int
{
/* only one port */
return (USB_FAILURE);
}
0) != USB_SUCCESS) {
return (USB_FAILURE);
}
/*
* Check the chip type: pl2303_H, pl2303_X (or pl2303_HX(Chip A)),
* pl2303_HX(Chip D).
* pl2303_UNKNOWN means not supported chip type.
*/
"Chip Type: pl2303_H");
/*
* pl2303_HX(Chip A)and pl2303_X devices have different
* hardware, but from the view of device driver, they have
* the same software interface.
*
* So "pl2303_X" will stand for both pl2303_HX(Chip A)and
* pl2303_X devices in this driver.
*/
"Chip Type: pl2303_HX(Chip A) or pl2303_X");
"Chip Type: pl2303_HX(Chip D)");
} else {
"Chip Type: Unknown");
}
return (USB_FAILURE);
}
!= USB_SUCCESS) {
return (USB_FAILURE);
}
return (USB_FAILURE);
}
}
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* ds_detach
*/
static void
{
}
/*
* ds_register_cb
*/
/*ARGSUSED*/
static int
{
return (USB_SUCCESS);
}
/*
* ds_unregister_cb
*/
/*ARGSUSED*/
static void
{
}
/*
* ds_open_port
*/
/*ARGSUSED*/
static int
{
int rval = USB_FAILURE;
return (rval);
}
return (rval);
}
/* initialize hardware serial port */
if (rval == USB_SUCCESS) {
/* start to receive data */
return (USB_FAILURE);
}
} else {
}
return (rval);
}
/*
* ds_close_port
*/
/*ARGSUSED*/
static int
{
/* free resources and finalize state */
}
}
return (USB_SUCCESS);
}
/*
* power management
* ----------------
*
* ds_usb_power
*/
/*ARGSUSED*/
static int
{
int rval;
if (!pm) {
return (USB_FAILURE);
}
/*
* check if we are transitioning to a legal power level
*/
"illegal power level %d, pwr_states=%x",
return (USB_FAILURE);
}
/*
* if we are about to raise power and asked to lower power, fail
*/
return (USB_FAILURE);
}
switch (level) {
case USB_DEV_OS_PWR_OFF:
break;
case USB_DEV_OS_PWR_1:
break;
case USB_DEV_OS_PWR_2:
break;
case USB_DEV_OS_FULL_PWR:
break;
default:
ASSERT(0); /* cannot happen */
}
return (rval);
}
/*
* ds_suspend
*/
static int
{
int state;
return (state);
}
/*
* ds_resume
*/
static int
{
int current_state;
int rval;
if (current_state != USB_DEV_ONLINE) {
} else {
rval = USB_SUCCESS;
}
return (rval);
}
/*
* ds_disconnect
*/
static int
{
int state;
return (state);
}
/*
* ds_reconnect
*/
static int
{
return (pl2303_restore_device_state(plp));
}
/*
* standard UART operations
* ------------------------
*
*
* ds_set_port_params
*/
/*ARGSUSED*/
static int
{
int rval = USB_FAILURE;
int i;
int baud;
int cnt;
return (rval);
}
/*
* get Line Coding Structure Request
* including: baud rate, stop bit, parity type and data bit
*/
return (rval);
}
/* translate parameters into device-specific bits */
case DS_PARAM_BAUD:
/* if we don't support this speed, return USB_FAILURE */
"pl2303_set_port_params: bad baud %d", ui);
return (USB_FAILURE);
}
break;
case DS_PARAM_PARITY:
} else {
}
} else {
}
break;
case DS_PARAM_STOPB:
} else {
}
break;
case DS_PARAM_CHARSZ:
case CS5:
break;
case CS6:
break;
case CS7:
break;
case CS8:
default:
break;
}
break;
case DS_PARAM_XON_XOFF:
/*
* not supported by PL-2303H, HX chips
*/
/* not supported by PL-2303H chip */
switch (plp->pl_chiptype) {
case pl2303_H:
break;
case pl2303_X:
case pl2303_HX_CHIP_D:
| xon_char;
if (rval != USB_SUCCESS) {
"pl2303_set_port_params: "
"set XonXoff failed");
}
break;
case pl2303_UNKNOWN:
default:
break;
}
}
break;
case DS_PARAM_FLOW_CTL:
/* Hardware flow control */
!= USB_SUCCESS) {
"pl2303_set_port_params: "
"pl2303_cmd_set_rtscts failed");
}
}
break;
default:
break;
}
}
/* set new values for Line Coding Structure */
return (rval);
}
/* hardware need to get Line Coding Structure again */
}
return (rval);
}
}
return (USB_SUCCESS);
}
/*
* ds_set_modem_ctl
*/
/*ARGSUSED*/
static int
{
int rval = USB_FAILURE;
/* set RTS and DTR */
}
return (rval);
}
/*
* ds_get_modem_ctl
*/
/*ARGSUSED*/
static int
{
/* get RTS and DTR */
return (USB_SUCCESS);
}
/*
* ds_break_ctl
*/
/*ARGSUSED*/
static int
{
}
/*
* ds_tx
*/
/*ARGSUSED*/
static int
{
int xferd;
/*
* sanity checks
*/
return (USB_SUCCESS);
}
return (USB_SUCCESS);
}
return (USB_SUCCESS);
}
/*
* ds_rx
* the real data receiving is in pl2303_open_port
*/
/*ARGSUSED*/
static mblk_t *
{
return (mp);
}
/*
* ds_stop
*/
/*ARGSUSED*/
static void
{
}
}
/*
* ds_start
*/
/*ARGSUSED*/
static void
{
}
}
}
/*
* ds_fifo_flush
*/
/*ARGSUSED*/
static int
{
dir);
}
}
return (USB_SUCCESS);
}
/*
* ds_fifo_drain
*/
/*ARGSUSED*/
static int
{
int rval = USB_SUCCESS;
/*
* for the reason of hardware, set timeout 0
*/
return (USB_FAILURE);
}
/* wait 500 ms until hw fifo drains */
return (rval);
}
/*
* configuration routines
* ----------------------
*
* clean up routine
*/
static void
{
switch (level) {
default:
/* FALLTHRU */
case 5:
/* FALLTHRU */
case 4:
/* FALLTHRU */
case 3:
/* FALLTHRU */
case 2:
/* FALLTHRU */
case 1:
}
}
/*
* device specific attach
*/
static int
{
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* hotplug
* -------
*
*
* restore device state after CPR resume or reconnect
*/
static int
{
int state;
return (state);
}
return (state);
}
if (state == USB_DEV_DISCONNECTED) {
"Device has been reconnected but data may have been lost");
}
return (state);
}
/*
* init device state
*/
"pl2303_restore_device_state: failed");
}
return (state);
}
/*
* restore ports state after CPR resume or reconnect
*/
static int
{
int rval;
return (USB_SUCCESS);
}
/* open hardware serial port */
"pl2303_restore_ports_state: failed");
}
return (rval);
}
/*
* power management
* ----------------
*
*
* create PM components
*/
static int
{
"pl2303_create_pm_components: failed");
return (USB_SUCCESS);
}
return (USB_SUCCESS);
}
/*
* destroy PM components
*/
static void
{
int rval;
if (!pm)
return;
if (pm->pm_wakeup_enabled) {
if (rval != DDI_SUCCESS) {
"pl2303_destroy_pm_components:"
"raising power failed, rval=%d", rval);
}
if (rval != USB_SUCCESS) {
"pl2303_destroy_pm_components: disable "
"remote wakeup failed, rval=%d", rval);
}
}
}
}
/*
* mark device busy and raise power
*/
static int
{
int rval;
if (!pm) {
return (USB_SUCCESS);
}
/* if already marked busy, just increment the counter */
if (pm->pm_busy_cnt++ > 0) {
return (USB_SUCCESS);
}
return (USB_SUCCESS);
}
/* need to raise power */
if (rval != DDI_SUCCESS) {
}
return (USB_SUCCESS);
}
/*
* mark device idle
*/
static void
{
if (!pm) {
return;
}
/*
* if more ports use the device, do not mark as yet
*/
if (--pm->pm_busy_cnt > 0) {
return;
}
if (pm) {
(void) pm_idle_component(dip, 0);
}
}
/*
* Functions to handle power transition for OS levels 0 -> 3
* The same level as OS state, different from USB state
*/
static int
{
int rval;
switch (plp->pl_dev_state) {
case USB_DEV_ONLINE:
/* issue USB D3 command to the device */
/* FALLTHRU */
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
/* allow a disconnect/cpr'ed device to go to lower power */
return (USB_SUCCESS);
case USB_DEV_PWRED_DOWN:
default:
"pl2303_pwrlvl0: illegal device state");
return (USB_FAILURE);
}
}
static int
{
/* issue USB D2 command to the device */
return (USB_FAILURE);
}
static int
{
/* issue USB D1 command to the device */
return (USB_FAILURE);
}
static int
{
int rval;
switch (plp->pl_dev_state) {
case USB_DEV_PWRED_DOWN:
/* Issue USB D0 command to the device here */
/* FALLTHRU */
case USB_DEV_ONLINE:
/* we are already in full power */
/* FALLTHRU */
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
return (USB_SUCCESS);
default:
"pl2303_pwrlvl3: illegal device state");
return (USB_FAILURE);
}
}
/*
* pipe operations
* ---------------
*
*
*/
static int
{
/* get ep data */
alt = 0;
"pl2303_open_pipes: can't get ep data");
return (USB_FAILURE);
}
/* open pipes */
return (USB_FAILURE);
}
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
static void
{
if (plp->pl_bulkin_ph) {
USB_FLAGS_SLEEP, 0, 0);
}
if (plp->pl_bulkout_ph) {
USB_FLAGS_SLEEP, 0, 0);
}
}
static void
{
}
static int
{
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* pipe callbacks
* --------------
*
*
* bulk in common and exeception callback
*
*/
/*ARGSUSED*/
void
{
int data_len;
"cr=%d len=%d",
data_len);
/* save data and notify GSD */
}
}
/* receive more */
"pl2303_bulkin_cb: restart rx fail");
}
}
}
/*
* bulk out common and exeception callback
*/
/*ARGSUSED*/
void
{
int data_len;
"pl2303_bulkout_cb: cr=%d len=%d",
data_len);
}
/* notify GSD */
}
/* send more */
} else {
}
}
/*
* data transfer routines
* ----------------------
*
*
* start data receipt
*/
static int
{
int rval = USB_FAILURE;
if (rval != USB_SUCCESS) {
"pl2303_rx_start: xfer failed %d", rval);
}
if (rval != USB_SUCCESS) {
}
return (rval);
}
/*
* start data transmit
*/
static void
{
int len; /* bytes we can transmit */
int data_len; /* bytes in 'data' */
int copylen; /* bytes copy from 'mp' to 'data' */
int rval;
if (xferd) {
*xferd = 0;
}
return;
}
"pl2303_tx_start: pipe busy");
return;
}
/* send as much data as port can receive */
if (len == 0) {
return;
}
return;
}
/*
* copy no more than 'len' bytes from mblk chain to transmit mblk 'data'
*/
data_len = 0;
} else {
}
}
if (data_len <= 0) {
"pl2303_tx_start: copied zero bytes");
return;
}
if (rval != USB_SUCCESS) {
}
} else {
if (xferd) {
}
}
}
static int
{
int rval;
if (rval != USB_SUCCESS) {
"pl2303_send_data: xfer failed %d", rval);
}
return (rval);
}
/*
* wait until local tx buffer drains.
* 'timeout' is in seconds, zero means wait forever
*/
static int
{
int over = 0;
if (timeout > 0) {
/* whether timedout or signal pending */
} else {
/* whether a signal is pending */
}
}
}
/*
* device operations
* -----------------
*
*
* initialize hardware serial port
*/
static int
{
int rval = USB_SUCCESS;
/*
* initialize three Device Configuration Registers (DCR):
* DCR0, DCR1, and DCR2
*/
switch (plp->pl_chiptype) {
case (pl2303_H):
/* Set DCR0 */
DCR0_INIT_H)) != USB_SUCCESS) {
return (rval);
}
/* Set DCR1 */
DCR1_INIT_H)) != USB_SUCCESS) {
return (rval);
}
/* Set DCR2 */
DCR2_INIT_H)) != USB_SUCCESS) {
return (rval);
}
break;
case (pl2303_X):
case (pl2303_HX_CHIP_D):
/* Set DCR0 */
DCR0_INIT)) != USB_SUCCESS) {
return (rval);
}
/* Set DCR1 */
DCR1_INIT_X)) != USB_SUCCESS) {
return (rval);
}
/* Set DCR2 */
DCR2_INIT_X)) != USB_SUCCESS) {
return (rval);
}
/* reset Downstream data pipes */
RESET_DOWNSTREAM_DATA_PIPE, 0)) != USB_SUCCESS) {
return (rval);
}
/* reset Upstream data pipes */
RESET_UPSTREAM_DATA_PIPE, 0)) != USB_SUCCESS) {
return (rval);
}
break;
case (pl2303_UNKNOWN):
default:
"pl2303_open_hw_port: unknown chiptype");
rval = USB_FAILURE;
}
return (rval);
}
/*
* vendor-specific commands
* ------------------------
*
*
* Get_Line_Coding Request
*/
static int
{
int rval;
"pl2303_cmd_get_line: %x %x %x %x %x %x %x",
} else {
"pl2303_cmd_get_line: failed %d %d %x",
}
return (rval);
}
/*
* Set_Line_Coding Request
*/
static int
{
int rval;
"pl2303_cmd_set_line: %x %x %x %x %x %x %x",
if (rval != USB_SUCCESS) {
"pl2303_cmd_set_line: failed %d %d %x",
}
return (rval);
}
/*
* Set_Control_Line_State Request to RTS and DTR
*/
static int
{
PL2303_SET_CONTROL_REQUEST, 0, 0,
int rval;
if (rval != USB_SUCCESS) {
"pl2303_cmd_set_ctl: failed %d %d %x",
}
return (rval);
}
/*
* Vendor_Specific_Write Request
* wLength: 0
*/
static int
{
PL2303_VENDOR_WRITE_REQUEST, 0, 0,
int rval;
if (rval != USB_SUCCESS) {
"pl2303_cmd_vendor_write0: %x %x failed %d %d %x",
}
return (rval);
}
/*
* For Hardware flow control
*/
static int
{
/* Set DCR0 */
switch (plp->pl_chiptype) {
case pl2303_H:
case pl2303_X:
case pl2303_HX_CHIP_D:
case pl2303_UNKNOWN:
default:
return (USB_FAILURE);
}
}
/*
* Set TxD BREAK_ON or BREAK_OFF
*/
static int
{
PL2303_BREAK_REQUEST, 0, 0,
PL2303_BREAK_LENGTH, 0 };
int rval;
if (rval != USB_SUCCESS) {
"pl2303_cmd_break: failed rval=%d,cr=%d,cb_flags=0x%x",
}
return (rval);
}
/*
* for set_mod_ctl
*/
static void
{
} else {
*line_ctl &= ~PL2303_CONTROL_RTS;
}
}
} else {
*line_ctl &= ~PL2303_CONTROL_DTR;
}
}
}
/*
* for get_mod_ctl
*/
static int
{
int val = 0;
if (line_ctl & PL2303_CONTROL_RTS) {
}
if (line_ctl & PL2303_CONTROL_DTR) {
}
return (val);
}
/*
* misc routines
* -------------
*
*/
/*
* link a message block to tail of message
* account for the case when message is null
*/
static void
{
if (*mpp) {
} else {
}
}
/*
* put a message block at the head of the message
* account for the case when message is null
*/
static void
{
if (*mpp) {
}
}
/*ARGSUSED*/
static usb_pipe_handle_t
{
return (plp->pl_bulkout_ph);
}
/*ARGSUSED*/
static usb_pipe_handle_t
{
return (plp->pl_bulkin_ph);
}