/* * Prolific USB Serial Adapter Driver * * Copyright (C) 2001 * Prolific Technology Inc. * * This program is largely derived from work by the linux-usb group * and associated source files. Please see the usb/serial files for * individual credits and copyrights. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USB_SERIAL_DEBUG static int debug = 1; #else static int debug; #endif #include "usb-serial.h" #include "pl2303.h" #define TRUE 1 #define FALSE 0 /* function prototypes for a Prolific USB Serial Adapter */ static int prolific_sa_startup (struct usb_serial *serial); static void prolific_sa_shutdown (struct usb_serial *serial); static int prolific_sa_open (struct usb_serial_port *port, struct file *filp); static void prolific_sa_close (struct usb_serial_port *port, struct file *filp); static void prolific_sa_read_int_callback (struct urb *urb); static void prolific_sa_set_termios (struct usb_serial_port *port, struct termios * old); static int prolific_sa_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); static void prolific_sa_break_ctl (struct usb_serial_port *port, int break_state ); static void prolific_set_dcr_state(struct usb_serial_port *port, int set); static __devinitdata struct usb_device_id id_table_combined [] = { { USB_DEVICE(PROLIFIC_SA_VID, PROLIFIC_SA_PID) }, { } /* Terminating entry */ }; static __devinitdata struct usb_device_id prolific_sa_table [] = { { USB_DEVICE(PROLIFIC_SA_VID, PROLIFIC_SA_PID) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, id_table_combined); /* All of the device info needed for the Prolific serial converter */ struct usb_serial_device_type prolific_sa_device = { name: "Prolific USB Serial Adapter", id_table: prolific_sa_table, /* the Prolific device */ needs_interrupt_in: MUST_HAVE, /* this device must have an interrupt in endpoint */ needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ num_interrupt_in: 1, num_bulk_in: 1, num_bulk_out: 1, num_ports: 1, open: prolific_sa_open, close: prolific_sa_close, read_int_callback: prolific_sa_read_int_callback, /* How we get the status info */ ioctl: prolific_sa_ioctl, set_termios: prolific_sa_set_termios, break_ctl: prolific_sa_break_ctl, startup: prolific_sa_startup, shutdown: prolific_sa_shutdown, }; struct line_coding { unsigned long dwDTERate; unsigned char bCharFormat; unsigned char bParityType; unsigned char bDatabits; }; struct prolific_sa_private { unsigned long control_state; unsigned char last_lsr; unsigned char last_msr; int bad_flow_control; struct line_coding lineCoding; unsigned short RTSDTRState; }; /* * *************************************************************************** * Prolific USB Serial Adapter specific driver functions * *************************************************************************** */ #define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ /* assumes that struct usb_serial *serial is available */ #define BSA_USB_CMD(r,v) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), \ (r), PROLIFIC_SA_SET_REQUEST_CLASS_TYPE, \ (v), 0, NULL, 0, WDR_TIMEOUT) #define BSA_USB_CMD_SET_DATA(r,v,data) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), \ (r), PROLIFIC_SA_SET_REQUEST_CLASS_TYPE, \ (v), 0, data, sizeof(data), WDR_TIMEOUT) #define BSA_USB_CMD_SET_DATA_LEN(r,v,data,size) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), \ (r), PROLIFIC_SA_SET_REQUEST_CLASS_TYPE, \ (v), 0, data, size, WDR_TIMEOUT) #define BSA_USB_CMD_SET_VENDOR(r,v,idx) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), \ (r), PROLIFIC_SA_SET_REQUEST_VENDOR_TYPE, \ (v), (idx), NULL, 0, WDR_TIMEOUT) #define BSA_USB_CMD_GET_VENDOR_DATA(r,v,data,size) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), \ (r), PROLIFIC_SA_GET_REQUEST_VENDOR_TYPE, \ (v), 0, data, size, WDR_TIMEOUT) /* do some startup allocations not currently performed by usb_serial_probe() */ static int prolific_sa_startup (struct usb_serial *serial) { struct usb_device *dev = serial->dev; struct prolific_sa_private *priv; /* allocate the private data structure */ serial->port->private = kmalloc(sizeof(struct prolific_sa_private), GFP_KERNEL); if (!serial->port->private) return (-1); /* error */ priv = (struct prolific_sa_private *)serial->port->private; /* set initial values for control structures */ priv->control_state = 0; priv->last_lsr = 0; priv->last_msr = 0; /* see comments at top of file */ priv->bad_flow_control = (dev->descriptor.bcdDevice <= 0x0206) ? 1 : 0; info("bcdDevice: %04x, bfc: %d", dev->descriptor.bcdDevice, priv->bad_flow_control); init_waitqueue_head(&serial->port->write_wait); return (0); } static void prolific_sa_shutdown (struct usb_serial *serial) { int i; dbg (__FUNCTION__); /* stop reads and writes on all ports */ for (i=0; i < serial->num_ports; ++i) { while (serial->port[i].open_count > 0) { prolific_sa_close (&serial->port[i], NULL); } /* My special items, the standard routines free my urbs */ if (serial->port[i].private) kfree(serial->port[i].private); } } static int prolific_sa_open (struct usb_serial_port *port, struct file *filp) { unsigned long flags; dbg(__FUNCTION__" port %d", port->number); spin_lock_irqsave (&port->port_lock, flags); ++port->open_count; MOD_INC_USE_COUNT; if (!port->active) { port->active = 1; /*Start reading from the device*/ /* TODO: Look at possibility of submitting mulitple URBs to device to * enhance buffering. Win trace shows 16 initial read URBs. */ port->read_urb->dev = port->serial->dev; if (usb_submit_urb(port->read_urb)) err("usb_submit_urb(read bulk) failed"); port->interrupt_in_urb->dev = port->serial->dev; if (usb_submit_urb(port->interrupt_in_urb)) err(" usb_submit_urb(read int) failed"); } spin_unlock_irqrestore (&port->port_lock, flags); return 0; } /* prolific_sa_open */ static void prolific_sa_close (struct usb_serial_port *port, struct file *filp) { unsigned long flags; dbg(__FUNCTION__" port %d", port->number); spin_lock_irqsave (&port->port_lock, flags); --port->open_count; MOD_DEC_USE_COUNT; if (port->open_count <= 0) { /* shutdown our bulk reads and writes */ usb_unlink_urb (port->write_urb); usb_unlink_urb (port->read_urb); usb_unlink_urb (port->interrupt_in_urb); /* wgg - do I need this? I think so. */ port->active = 0; } spin_unlock_irqrestore (&port->port_lock, flags); } /* prolific_sa_close */ static void prolific_sa_read_int_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct prolific_sa_private *priv = (struct prolific_sa_private *)port->private; struct usb_serial *serial; unsigned char *data = urb->transfer_buffer; /* the urb might have been killed. */ if (urb->status) return; if (port_paranoia_check (port, "prolific_sa_read_interrupt")) return; serial = port->serial; if (serial_paranoia_check (serial, "prolific_sa_read_interrupt")) return; usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); /* Handle known interrupt data */ /* ignore data[0] and data[1] */ priv->last_msr = data[PROLIFIC_SA_MSR_INDEX]; dbg("interrupt: 0x%02x", priv->last_msr); /* Record Control Line states */ if (priv->last_msr & PROLIFIC_SA_MSR_DSR) priv->control_state |= TIOCM_DSR; else priv->control_state &= ~TIOCM_DSR; if (priv->last_msr & PROLIFIC_SA_MSR_CTS) priv->control_state |= TIOCM_CTS; else priv->control_state &= ~TIOCM_CTS; if (priv->last_msr & PROLIFIC_SA_MSR_RI) priv->control_state |= TIOCM_RI; else priv->control_state &= ~TIOCM_RI; if (priv->last_msr & PROLIFIC_SA_MSR_CD) priv->control_state |= TIOCM_CD; else priv->control_state &= ~TIOCM_CD; /* Now to report any errors */ priv->last_lsr = data[PROLIFIC_SA_LSR_INDEX]; #if 0 if (priv->last_lsr & PROLIFIC_SA_LSR_ERR) { tty = port->tty; /* Overrun Error */ if (priv->last_lsr & PROLIFIC_SA_LSR_OE) { } /* Parity Error */ if (priv->last_lsr & PROLIFIC_SA_LSR_PE) { } /* Framing Error */ if (priv->last_lsr & PROLIFIC_SA_LSR_FE) { } /* Break Indicator */ if (priv->last_lsr & PROLIFIC_SA_LSR_BI) { } } #endif /* INT urbs are automatically re-submitted */ } static void prolific_sa_set_termios (struct usb_serial_port *port, struct termios *old_termios) { struct usb_serial *serial = port->serial; struct prolific_sa_private *priv = (struct prolific_sa_private *)port->private; unsigned int cflag = port->tty->termios->c_cflag; unsigned int old_cflag = old_termios->c_cflag; unsigned long rate = 115200; unsigned char format = 0, parity = 0, size = 8; info(__FUNCTION__ " - cflag: 0x%4X, old_cflag: 0x%4X", cflag, old_cflag); /* Set the baud rate */ if( (cflag&CBAUD) != (old_cflag&CBAUD) ) { /* reassert DTR and (maybe) RTS on transition from B0 */ if( (old_cflag&CBAUD) == B0 ) { priv->control_state |= (TIOCM_DTR|TIOCM_RTS); if (BSA_USB_CMD(PROLIFIC_SA_SET_CTRL_LINE_REQUEST, priv->RTSDTRState |= 0x0001) < 0) err("Set DTR error"); /* don't set RTS if using hardware flow control */ if (!(old_cflag&CRTSCTS) ) if (BSA_USB_CMD(PROLIFIC_SA_SET_CTRL_LINE_REQUEST, priv->RTSDTRState |= 0x0002) < 0) err("Set RTS error"); } switch(cflag & CBAUD) { case B150: rate = 150; break; case B300: rate = 300; break; case B600: rate = 600; break; case B1200: rate = 1200; break; case B2400: rate = 2400; break; case B4800: rate = 4800; break; case B9600: rate = 9600; break; case B19200: rate = 19200; break; case B38400: rate = 38400; break; case B115200: rate = 115200; break; case B230400: rate = 230400; break; case B460800: rate = 460800; break; } if ((cflag & CBAUD) == B0 ) { /* Drop RTS and DTR */ priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); if (BSA_USB_CMD(PROLIFIC_SA_SET_CTRL_LINE_REQUEST, priv->RTSDTRState &= 0xFFFE) < 0) err("DTR LOW error"); if (BSA_USB_CMD(PROLIFIC_SA_SET_CTRL_LINE_REQUEST, priv->RTSDTRState &= 0xFFFD) < 0) err("RTS LOW error"); } } /* set the parity */ if( (cflag&(PARENB|PARODD)) != (old_cflag&(PARENB|PARODD)) ) { if (cflag & PARENB) parity = (cflag & PARODD) ? 0x01 : 0x02; else parity = 0; } /* set the number of data bits */ if( (cflag&CSIZE) != (old_cflag&CSIZE) ) { switch(cflag & CSIZE) { case CS5: size = 5; break; case CS6: size = 6; break; case CS7: size = 7; break; case CS8: size = 8; break; } } /* set the number of stop bits */ if( (cflag&CSTOPB) != (old_cflag&CSTOPB) ) format = (cflag & CSTOPB) ? 0x02 : 0x00; /* Set flow control */ if( (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) { if (cflag & CRTSCTS) prolific_set_dcr_state(port, TRUE); else prolific_set_dcr_state(port, FALSE); } priv->lineCoding.dwDTERate = rate; priv->lineCoding.bCharFormat = format; priv->lineCoding.bParityType = parity; priv->lineCoding.bDatabits = size; dbg(__FUNCTION__ "==> dwDTERate: %ld - bCharFormat: %d - bParityType: %d - bDatabits: %d", rate, format, parity, size); if (BSA_USB_CMD_SET_DATA_LEN(PROLIFIC_SA_SET_LINECODING_REQUEST, 0, &priv->lineCoding, 7) < 0) err("Set_line_coding request error"); } /* prolific_sa_set_termios */ static void prolific_sa_break_ctl( struct usb_serial_port *port, int break_state ) { struct usb_serial *serial = port->serial; if(break_state) { if(BSA_USB_CMD(PROLIFIC_SA_SET_BREAK_REQUEST, 0xFFFF) < 0) err("Set break_ctl %d fail", break_state); } else { if(BSA_USB_CMD(PROLIFIC_SA_SET_BREAK_REQUEST, 0) < 0) err("Set break_ctl %d fail", break_state); } } static int prolific_sa_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) { struct usb_serial *serial = port->serial; __u16 urb_value; /* Will hold the new flags */ struct prolific_sa_private *priv = (struct prolific_sa_private *)port->private; int ret, mask; info(__FUNCTION__ " cmd: %04x", cmd); /* Based on code from acm.c and others */ switch (cmd) { case TIOCMGET: dbg("TIOCMGET: control_state - 0x%04x",(unsigned int)priv->control_state); return put_user(priv->control_state, (unsigned long *) arg); break; case TIOCMSET: /* Turns on and off the lines as specified by the mask */ case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */ case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */ if ((ret = get_user(mask, (unsigned long *) arg))) return ret; if ((cmd == TIOCMSET) || (mask & TIOCM_RTS)) { /* RTS needs set */ urb_value = ((cmd == TIOCMSET) && (mask & TIOCM_RTS)) || (cmd == TIOCMBIS) ? 1 : 0; if (urb_value) priv->control_state |= TIOCM_RTS; else priv->control_state &= ~TIOCM_RTS; if(urb_value) { if ((ret = BSA_USB_CMD(PROLIFIC_SA_SET_CTRL_LINE_REQUEST, priv->RTSDTRState |= 0x0002)) < 0) { err("Set RTS error %d", ret); return(ret); } } else { if ((ret = BSA_USB_CMD(PROLIFIC_SA_SET_CTRL_LINE_REQUEST, priv->RTSDTRState &= 0xFFFD)) < 0) { err("Clear RTS error %d", ret); return(ret); } } if ((cmd == TIOCMSET) || (mask & TIOCM_DTR)) { /* DTR needs set */ urb_value = ((cmd == TIOCMSET) && (mask & TIOCM_DTR)) || (cmd == TIOCMBIS) ? 1 : 0; if (urb_value) priv->control_state |= TIOCM_DTR; else priv->control_state &= ~TIOCM_DTR; if(urb_value) { if ((ret = BSA_USB_CMD(PROLIFIC_SA_SET_CTRL_LINE_REQUEST, priv->RTSDTRState |= 0x0001)) < 0) { err("Set DTR error %d", ret); return(ret); } } else { if ((ret = BSA_USB_CMD(PROLIFIC_SA_SET_CTRL_LINE_REQUEST, priv->RTSDTRState &= 0xFFFE)) < 0) { err("Clear DTR error %d", ret); return(ret); } } } } break; case TIOCMIWAIT: /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ /* TODO */ return( 0 ); case TIOCGICOUNT: /* return count of modemline transitions */ /* TODO */ return 0; default: dbg("prolific_sa_ioctl arg not supported - 0x%04x",cmd); return(-ENOIOCTLCMD); break; } return 0; } /* prolific_sa_ioctl */ static void prolific_set_dcr_state(struct usb_serial_port *port, int set) { struct usb_serial *serial = port->serial; unsigned short code = 0; unsigned char old_value = 0; unsigned short new_value; info(__FUNCTION__ " - set: %d", set); if (BSA_USB_CMD_GET_VENDOR_DATA(PROLIFIC_SA_VENDOR_REQUEST, code, &old_value, sizeof(old_value)) < 0) err("Get DCR request error"); if(set) new_value = old_value | 0x40; else new_value = old_value & 0x3F; if (BSA_USB_CMD_SET_VENDOR(PROLIFIC_SA_VENDOR_REQUEST, code, new_value) < 0) err("Set DCR request error"); } static int __init prolific_sa_init (void) { usb_serial_register (&prolific_sa_device); return 0; } static void __exit prolific_sa_exit (void) { usb_serial_deregister (&prolific_sa_device); } module_init (prolific_sa_init); module_exit (prolific_sa_exit); MODULE_DESCRIPTION("USB Prolific Serial converter driver"); MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Debug enabled or not");