2007-03-27 14:34:30 +04:00
/*
* Ours Technology Inc . OTi - 6858 USB to serial adapter driver .
*
* Copyleft ( C ) 2007 Kees Lemmens ( adapted for kernel 2.6 .20 )
* Copyright ( C ) 2006 Tomasz Michal Lukaszewski ( FIXME : add e - mail )
* Copyright ( C ) 2001 - 2004 Greg Kroah - Hartman ( greg @ kroah . com )
* Copyright ( C ) 2003 IBM Corp .
*
* Many thanks to the authors of pl2303 driver : all functions in this file
* are heavily based on pl2303 code , buffering code is a 1 - to - 1 copy .
*
* Warning ! You use this driver on your own risk ! The only official
* description of this device I have is datasheet from manufacturer ,
* and it doesn ' t contain almost any information needed to write a driver .
* Almost all knowlegde used while writing this driver was gathered by :
* - analyzing traffic between device and the M $ Windows 2000 driver ,
* - trying different bit combinations and checking pin states
* with a voltmeter ,
* - receiving malformed frames and producing buffer overflows
* to learn how errors are reported ,
* So , THIS CODE CAN DESTROY OTi - 6858 AND ANY OTHER DEVICES , THAT ARE
* CONNECTED TO IT !
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License .
*
2008-07-22 14:15:36 +04:00
* See Documentation / usb / usb - serial . txt for more information on using this
* driver
2007-03-27 14:34:30 +04:00
*
* TODO :
* - implement correct flushing for ioctls and oti6858_close ( )
* - check how errors ( rx overflow , parity error , framing error ) are reported
* - implement oti6858_break_ctl ( )
* - implement more ioctls
* - test / implement flow control
* - allow setting custom baud rates
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/tty.h>
# include <linux/tty_driver.h>
# include <linux/tty_flip.h>
# include <linux/serial.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/spinlock.h>
# include <linux/usb.h>
# include <linux/usb/serial.h>
2008-07-22 14:15:36 +04:00
# include <linux/uaccess.h>
2010-05-16 22:33:51 +04:00
# include <linux/kfifo.h>
2007-03-27 14:34:30 +04:00
# include "oti6858.h"
# define OTI6858_DESCRIPTION \
" Ours Technology Inc. OTi-6858 USB to serial adapter driver "
# define OTI6858_AUTHOR "Tomasz Michal Lukaszewski <FIXME@FIXME>"
2010-01-10 17:34:24 +03:00
static const struct usb_device_id id_table [ ] = {
2007-03-27 14:34:30 +04:00
{ USB_DEVICE ( OTI6858_VENDOR_ID , OTI6858_PRODUCT_ID ) } ,
{ }
} ;
MODULE_DEVICE_TABLE ( usb , id_table ) ;
/* requests */
# define OTI6858_REQ_GET_STATUS (USB_DIR_IN | USB_TYPE_VENDOR | 0x00)
# define OTI6858_REQ_T_GET_STATUS 0x01
# define OTI6858_REQ_SET_LINE (USB_DIR_OUT | USB_TYPE_VENDOR | 0x00)
# define OTI6858_REQ_T_SET_LINE 0x00
# define OTI6858_REQ_CHECK_TXBUFF (USB_DIR_IN | USB_TYPE_VENDOR | 0x01)
# define OTI6858_REQ_T_CHECK_TXBUFF 0x00
/* format of the control packet */
struct oti6858_control_pkt {
2008-04-28 10:00:16 +04:00
__le16 divisor ; /* baud rate = 96000000 / (16 * divisor), LE */
2007-03-27 14:34:30 +04:00
# define OTI6858_MAX_BAUD_RATE 3000000
u8 frame_fmt ;
# define FMT_STOP_BITS_MASK 0xc0
# define FMT_STOP_BITS_1 0x00
# define FMT_STOP_BITS_2 0x40 /* 1.5 stop bits if FMT_DATA_BITS_5 */
# define FMT_PARITY_MASK 0x38
# define FMT_PARITY_NONE 0x00
# define FMT_PARITY_ODD 0x08
# define FMT_PARITY_EVEN 0x18
# define FMT_PARITY_MARK 0x28
# define FMT_PARITY_SPACE 0x38
# define FMT_DATA_BITS_MASK 0x03
# define FMT_DATA_BITS_5 0x00
# define FMT_DATA_BITS_6 0x01
# define FMT_DATA_BITS_7 0x02
# define FMT_DATA_BITS_8 0x03
u8 something ; /* always equals 0x43 */
u8 control ; /* settings of flow control lines */
# define CONTROL_MASK 0x0c
# define CONTROL_DTR_HIGH 0x08
# define CONTROL_RTS_HIGH 0x04
u8 tx_status ;
# define TX_BUFFER_EMPTIED 0x09
u8 pin_state ;
# define PIN_MASK 0x3f
2014-01-03 01:49:34 +04:00
# define PIN_MSR_MASK 0x1b
2007-03-27 14:34:30 +04:00
# define PIN_RTS 0x20 /* output pin */
# define PIN_CTS 0x10 /* input pin, active low */
# define PIN_DSR 0x08 /* input pin, active low */
# define PIN_DTR 0x04 /* output pin */
# define PIN_RI 0x02 /* input pin, active low */
# define PIN_DCD 0x01 /* input pin, active low */
u8 rx_bytes_avail ; /* number of bytes in rx buffer */ ;
} ;
# define OTI6858_CTRL_PKT_SIZE sizeof(struct oti6858_control_pkt)
# define OTI6858_CTRL_EQUALS_PENDING(a, priv) \
2008-07-22 14:15:36 +04:00
( ( ( a ) - > divisor = = ( priv ) - > pending_setup . divisor ) \
2007-03-27 14:34:30 +04:00
& & ( ( a ) - > control = = ( priv ) - > pending_setup . control ) \
2008-07-22 14:15:36 +04:00
& & ( ( a ) - > frame_fmt = = ( priv ) - > pending_setup . frame_fmt ) )
2007-03-27 14:34:30 +04:00
/* function prototypes */
2009-09-20 00:13:26 +04:00
static int oti6858_open ( struct tty_struct * tty , struct usb_serial_port * port ) ;
2009-06-11 15:26:29 +04:00
static void oti6858_close ( struct usb_serial_port * port ) ;
2008-07-22 14:09:07 +04:00
static void oti6858_set_termios ( struct tty_struct * tty ,
struct usb_serial_port * port , struct ktermios * old ) ;
2009-09-20 00:13:33 +04:00
static void oti6858_init_termios ( struct tty_struct * tty ) ;
2007-03-27 14:34:30 +04:00
static void oti6858_read_int_callback ( struct urb * urb ) ;
static void oti6858_read_bulk_callback ( struct urb * urb ) ;
static void oti6858_write_bulk_callback ( struct urb * urb ) ;
2008-07-22 14:09:07 +04:00
static int oti6858_write ( struct tty_struct * tty , struct usb_serial_port * port ,
2007-03-27 14:34:30 +04:00
const unsigned char * buf , int count ) ;
2008-07-22 14:09:07 +04:00
static int oti6858_write_room ( struct tty_struct * tty ) ;
static int oti6858_chars_in_buffer ( struct tty_struct * tty ) ;
2011-02-14 19:26:14 +03:00
static int oti6858_tiocmget ( struct tty_struct * tty ) ;
2011-02-14 19:26:50 +03:00
static int oti6858_tiocmset ( struct tty_struct * tty ,
2007-03-27 14:34:30 +04:00
unsigned int set , unsigned int clear ) ;
2017-01-03 18:39:59 +03:00
static int oti6858_attach ( struct usb_serial * serial ) ;
2012-10-17 18:31:32 +04:00
static int oti6858_port_probe ( struct usb_serial_port * port ) ;
static int oti6858_port_remove ( struct usb_serial_port * port ) ;
2007-03-27 14:34:30 +04:00
/* device info */
static struct usb_serial_driver oti6858_device = {
. driver = {
. owner = THIS_MODULE ,
. name = " oti6858 " ,
} ,
. id_table = id_table ,
. num_ports = 1 ,
. open = oti6858_open ,
. close = oti6858_close ,
. write = oti6858_write ,
. set_termios = oti6858_set_termios ,
2009-09-20 00:13:33 +04:00
. init_termios = oti6858_init_termios ,
2007-03-27 14:34:30 +04:00
. tiocmget = oti6858_tiocmget ,
. tiocmset = oti6858_tiocmset ,
2014-01-03 01:49:35 +04:00
. tiocmiwait = usb_serial_generic_tiocmiwait ,
2007-03-27 14:34:30 +04:00
. read_bulk_callback = oti6858_read_bulk_callback ,
. read_int_callback = oti6858_read_int_callback ,
. write_bulk_callback = oti6858_write_bulk_callback ,
. write_room = oti6858_write_room ,
. chars_in_buffer = oti6858_chars_in_buffer ,
2017-01-03 18:39:59 +03:00
. attach = oti6858_attach ,
2012-10-17 18:31:32 +04:00
. port_probe = oti6858_port_probe ,
. port_remove = oti6858_port_remove ,
2007-03-27 14:34:30 +04:00
} ;
2012-02-23 23:57:18 +04:00
static struct usb_serial_driver * const serial_drivers [ ] = {
& oti6858_device , NULL
} ;
2007-03-27 14:34:30 +04:00
struct oti6858_private {
spinlock_t lock ;
struct oti6858_control_pkt status ;
struct {
u8 read_urb_in_use ;
u8 write_urb_in_use ;
} flags ;
struct delayed_work delayed_write_work ;
struct {
2008-04-28 10:00:16 +04:00
__le16 divisor ;
2007-03-27 14:34:30 +04:00
u8 frame_fmt ;
u8 control ;
} pending_setup ;
u8 transient ;
u8 setup_done ;
struct delayed_work delayed_setup_work ;
2008-07-22 14:15:36 +04:00
struct usb_serial_port * port ; /* USB port with which associated */
2007-03-27 14:34:30 +04:00
} ;
static void setup_line ( struct work_struct * work )
{
2008-07-22 14:15:36 +04:00
struct oti6858_private * priv = container_of ( work ,
struct oti6858_private , delayed_setup_work . work ) ;
2007-03-27 14:34:30 +04:00
struct usb_serial_port * port = priv - > port ;
struct oti6858_control_pkt * new_setup ;
unsigned long flags ;
int result ;
2008-07-22 14:15:36 +04:00
new_setup = kmalloc ( OTI6858_CTRL_PKT_SIZE , GFP_KERNEL ) ;
2013-12-29 22:22:56 +04:00
if ( ! new_setup ) {
2007-03-27 14:34:30 +04:00
/* we will try again */
2008-07-22 14:15:36 +04:00
schedule_delayed_work ( & priv - > delayed_setup_work ,
msecs_to_jiffies ( 2 ) ) ;
2007-03-27 14:34:30 +04:00
return ;
}
result = usb_control_msg ( port - > serial - > dev ,
usb_rcvctrlpipe ( port - > serial - > dev , 0 ) ,
OTI6858_REQ_T_GET_STATUS ,
OTI6858_REQ_GET_STATUS ,
0 , 0 ,
new_setup , OTI6858_CTRL_PKT_SIZE ,
100 ) ;
if ( result ! = OTI6858_CTRL_PKT_SIZE ) {
2008-03-04 03:08:34 +03:00
dev_err ( & port - > dev , " %s(): error reading status \n " , __func__ ) ;
2007-03-27 14:34:30 +04:00
kfree ( new_setup ) ;
/* we will try again */
2008-07-22 14:15:36 +04:00
schedule_delayed_work ( & priv - > delayed_setup_work ,
msecs_to_jiffies ( 2 ) ) ;
2007-03-27 14:34:30 +04:00
return ;
}
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( ! OTI6858_CTRL_EQUALS_PENDING ( new_setup , priv ) ) {
new_setup - > divisor = priv - > pending_setup . divisor ;
new_setup - > control = priv - > pending_setup . control ;
new_setup - > frame_fmt = priv - > pending_setup . frame_fmt ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
result = usb_control_msg ( port - > serial - > dev ,
usb_sndctrlpipe ( port - > serial - > dev , 0 ) ,
OTI6858_REQ_T_SET_LINE ,
OTI6858_REQ_SET_LINE ,
0 , 0 ,
new_setup , OTI6858_CTRL_PKT_SIZE ,
100 ) ;
} else {
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
result = 0 ;
}
kfree ( new_setup ) ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( result ! = OTI6858_CTRL_PKT_SIZE )
priv - > transient = 0 ;
priv - > setup_done = 1 ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2012-09-14 22:50:32 +04:00
dev_dbg ( & port - > dev , " %s(): submitting interrupt urb \n " , __func__ ) ;
2009-10-07 20:07:10 +04:00
result = usb_submit_urb ( port - > interrupt_in_urb , GFP_KERNEL ) ;
2007-03-27 14:34:30 +04:00
if ( result ! = 0 ) {
2012-09-14 22:50:32 +04:00
dev_err ( & port - > dev , " %s(): usb_submit_urb() failed with error %d \n " ,
__func__ , result ) ;
2007-03-27 14:34:30 +04:00
}
}
2010-04-29 00:59:35 +04:00
static void send_data ( struct work_struct * work )
2007-03-27 14:34:30 +04:00
{
2008-07-22 14:15:36 +04:00
struct oti6858_private * priv = container_of ( work ,
struct oti6858_private , delayed_write_work . work ) ;
2007-03-27 14:34:30 +04:00
struct usb_serial_port * port = priv - > port ;
int count = 0 , result ;
unsigned long flags ;
2009-12-29 01:01:56 +03:00
u8 * allow ;
2007-03-27 14:34:30 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( priv - > flags . write_urb_in_use ) {
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2008-07-22 14:15:36 +04:00
schedule_delayed_work ( & priv - > delayed_write_work ,
msecs_to_jiffies ( 2 ) ) ;
2007-03-27 14:34:30 +04:00
return ;
}
priv - > flags . write_urb_in_use = 1 ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2010-05-16 22:33:52 +04:00
spin_lock_irqsave ( & port - > lock , flags ) ;
count = kfifo_len ( & port - > write_fifo ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2007-03-27 14:34:30 +04:00
if ( count > port - > bulk_out_size )
count = port - > bulk_out_size ;
if ( count ! = 0 ) {
2009-12-29 01:01:56 +03:00
allow = kmalloc ( 1 , GFP_KERNEL ) ;
2013-12-29 22:22:56 +04:00
if ( ! allow )
2009-12-29 01:01:56 +03:00
return ;
2013-12-29 22:22:56 +04:00
2007-03-27 14:34:30 +04:00
result = usb_control_msg ( port - > serial - > dev ,
usb_rcvctrlpipe ( port - > serial - > dev , 0 ) ,
OTI6858_REQ_T_CHECK_TXBUFF ,
OTI6858_REQ_CHECK_TXBUFF ,
2009-12-29 01:01:56 +03:00
count , 0 , allow , 1 , 100 ) ;
if ( result ! = 1 | | * allow ! = 0 )
2007-03-27 14:34:30 +04:00
count = 0 ;
2009-12-29 01:01:56 +03:00
kfree ( allow ) ;
2007-03-27 14:34:30 +04:00
}
if ( count = = 0 ) {
priv - > flags . write_urb_in_use = 0 ;
2012-09-14 22:50:32 +04:00
dev_dbg ( & port - > dev , " %s(): submitting interrupt urb \n " , __func__ ) ;
2009-10-07 20:07:10 +04:00
result = usb_submit_urb ( port - > interrupt_in_urb , GFP_NOIO ) ;
2007-03-27 14:34:30 +04:00
if ( result ! = 0 ) {
2012-09-14 22:50:32 +04:00
dev_err ( & port - > dev , " %s(): usb_submit_urb() failed with error %d \n " ,
__func__ , result ) ;
2007-03-27 14:34:30 +04:00
}
return ;
}
2010-05-16 22:33:52 +04:00
count = kfifo_out_locked ( & port - > write_fifo ,
2010-05-16 22:33:51 +04:00
port - > write_urb - > transfer_buffer ,
2010-05-16 22:33:52 +04:00
count , & port - > lock ) ;
2007-03-27 14:34:30 +04:00
port - > write_urb - > transfer_buffer_length = count ;
2009-10-07 20:07:10 +04:00
result = usb_submit_urb ( port - > write_urb , GFP_NOIO ) ;
2007-03-27 14:34:30 +04:00
if ( result ! = 0 ) {
2012-09-14 22:50:32 +04:00
dev_err_console ( port , " %s(): usb_submit_urb() failed with error %d \n " ,
__func__ , result ) ;
2007-03-27 14:34:30 +04:00
priv - > flags . write_urb_in_use = 0 ;
}
usb_serial_port_softint ( port ) ;
}
2017-01-03 18:39:59 +03:00
static int oti6858_attach ( struct usb_serial * serial )
{
unsigned char num_ports = serial - > num_ports ;
if ( serial - > num_bulk_in < num_ports | |
serial - > num_bulk_out < num_ports | |
serial - > num_interrupt_in < num_ports ) {
dev_err ( & serial - > interface - > dev , " missing endpoints \n " ) ;
return - ENODEV ;
}
return 0 ;
}
2012-10-17 18:31:32 +04:00
static int oti6858_port_probe ( struct usb_serial_port * port )
2007-03-27 14:34:30 +04:00
{
2008-07-22 14:15:36 +04:00
struct oti6858_private * priv ;
2007-03-27 14:34:30 +04:00
2012-10-17 18:31:32 +04:00
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
spin_lock_init ( & priv - > lock ) ;
priv - > port = port ;
INIT_DELAYED_WORK ( & priv - > delayed_setup_work , setup_line ) ;
INIT_DELAYED_WORK ( & priv - > delayed_write_work , send_data ) ;
usb_set_serial_port_data ( port , priv ) ;
2013-06-26 18:47:23 +04:00
port - > port . drain_delay = 256 ; /* FIXME: check the FIFO length */
2012-10-17 18:31:32 +04:00
return 0 ;
}
static int oti6858_port_remove ( struct usb_serial_port * port )
{
struct oti6858_private * priv ;
priv = usb_get_serial_port_data ( port ) ;
kfree ( priv ) ;
return 0 ;
2007-03-27 14:34:30 +04:00
}
2008-07-22 14:09:07 +04:00
static int oti6858_write ( struct tty_struct * tty , struct usb_serial_port * port ,
2007-03-27 14:34:30 +04:00
const unsigned char * buf , int count )
{
if ( ! count )
return count ;
2010-05-16 22:33:52 +04:00
count = kfifo_in_locked ( & port - > write_fifo , buf , count , & port - > lock ) ;
2007-03-27 14:34:30 +04:00
return count ;
}
2008-07-22 14:09:07 +04:00
static int oti6858_write_room ( struct tty_struct * tty )
2007-03-27 14:34:30 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2007-03-27 14:34:30 +04:00
int room = 0 ;
unsigned long flags ;
2010-05-16 22:33:52 +04:00
spin_lock_irqsave ( & port - > lock , flags ) ;
room = kfifo_avail ( & port - > write_fifo ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2007-03-27 14:34:30 +04:00
return room ;
}
2008-07-22 14:09:07 +04:00
static int oti6858_chars_in_buffer ( struct tty_struct * tty )
2007-03-27 14:34:30 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2007-03-27 14:34:30 +04:00
int chars = 0 ;
unsigned long flags ;
2010-05-16 22:33:52 +04:00
spin_lock_irqsave ( & port - > lock , flags ) ;
chars = kfifo_len ( & port - > write_fifo ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2007-03-27 14:34:30 +04:00
return chars ;
}
2009-09-20 00:13:33 +04:00
static void oti6858_init_termios ( struct tty_struct * tty )
{
2012-07-14 18:31:47 +04:00
tty - > termios = tty_std_termios ;
tty - > termios . c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL ;
tty - > termios . c_ispeed = 38400 ;
tty - > termios . c_ospeed = 38400 ;
2009-09-20 00:13:33 +04:00
}
2008-07-22 14:09:07 +04:00
static void oti6858_set_termios ( struct tty_struct * tty ,
struct usb_serial_port * port , struct ktermios * old_termios )
2007-03-27 14:34:30 +04:00
{
struct oti6858_private * priv = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
unsigned int cflag ;
u8 frame_fmt , control ;
2008-04-28 10:00:16 +04:00
__le16 divisor ;
2007-03-27 14:34:30 +04:00
int br ;
2012-07-14 18:31:47 +04:00
cflag = tty - > termios . c_cflag ;
2007-03-27 14:34:30 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
divisor = priv - > pending_setup . divisor ;
frame_fmt = priv - > pending_setup . frame_fmt ;
control = priv - > pending_setup . control ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
frame_fmt & = ~ FMT_DATA_BITS_MASK ;
switch ( cflag & CSIZE ) {
2008-07-22 14:15:36 +04:00
case CS5 :
frame_fmt | = FMT_DATA_BITS_5 ;
break ;
case CS6 :
frame_fmt | = FMT_DATA_BITS_6 ;
break ;
case CS7 :
frame_fmt | = FMT_DATA_BITS_7 ;
break ;
default :
case CS8 :
frame_fmt | = FMT_DATA_BITS_8 ;
break ;
2007-03-27 14:34:30 +04:00
}
/* manufacturer claims that this device can work with baud rates
* up to 3 Mbps ; I ' ve tested it only on 115200 bps , so I can ' t
* guarantee that any other baud rate will work ( especially
* the higher ones )
*/
2008-07-22 14:09:07 +04:00
br = tty_get_baud_rate ( tty ) ;
2007-03-27 14:34:30 +04:00
if ( br = = 0 ) {
divisor = 0 ;
2008-01-19 19:02:37 +03:00
} else {
2007-03-27 14:34:30 +04:00
int real_br ;
2008-04-28 10:00:16 +04:00
int new_divisor ;
2008-01-19 19:02:37 +03:00
br = min ( br , OTI6858_MAX_BAUD_RATE ) ;
2007-03-27 14:34:30 +04:00
2008-04-28 10:00:16 +04:00
new_divisor = ( 96000000 + 8 * br ) / ( 16 * br ) ;
real_br = 96000000 / ( 16 * new_divisor ) ;
divisor = cpu_to_le16 ( new_divisor ) ;
2008-07-22 14:09:07 +04:00
tty_encode_baud_rate ( tty , real_br , real_br ) ;
2007-03-27 14:34:30 +04:00
}
frame_fmt & = ~ FMT_STOP_BITS_MASK ;
2008-07-22 14:15:36 +04:00
if ( ( cflag & CSTOPB ) ! = 0 )
2007-03-27 14:34:30 +04:00
frame_fmt | = FMT_STOP_BITS_2 ;
2008-07-22 14:15:36 +04:00
else
2007-03-27 14:34:30 +04:00
frame_fmt | = FMT_STOP_BITS_1 ;
frame_fmt & = ~ FMT_PARITY_MASK ;
if ( ( cflag & PARENB ) ! = 0 ) {
2008-07-22 14:15:36 +04:00
if ( ( cflag & PARODD ) ! = 0 )
2007-03-27 14:34:30 +04:00
frame_fmt | = FMT_PARITY_ODD ;
2008-07-22 14:15:36 +04:00
else
2007-03-27 14:34:30 +04:00
frame_fmt | = FMT_PARITY_EVEN ;
} else {
frame_fmt | = FMT_PARITY_NONE ;
}
control & = ~ CONTROL_MASK ;
if ( ( cflag & CRTSCTS ) ! = 0 )
control | = ( CONTROL_DTR_HIGH | CONTROL_RTS_HIGH ) ;
/* change control lines if we are switching to or from B0 */
/* FIXME:
spin_lock_irqsave ( & priv - > lock , flags ) ;
control = priv - > line_control ;
if ( ( cflag & CBAUD ) = = B0 )
priv - > line_control & = ~ ( CONTROL_DTR | CONTROL_RTS ) ;
else
priv - > line_control | = ( CONTROL_DTR | CONTROL_RTS ) ;
if ( control ! = priv - > line_control ) {
control = priv - > line_control ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
set_control_lines ( serial - > dev , control ) ;
} else {
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
}
*/
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( divisor ! = priv - > pending_setup . divisor
| | control ! = priv - > pending_setup . control
| | frame_fmt ! = priv - > pending_setup . frame_fmt ) {
priv - > pending_setup . divisor = divisor ;
priv - > pending_setup . control = control ;
priv - > pending_setup . frame_fmt = frame_fmt ;
}
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
}
2009-09-20 00:13:26 +04:00
static int oti6858_open ( struct tty_struct * tty , struct usb_serial_port * port )
2007-03-27 14:34:30 +04:00
{
struct oti6858_private * priv = usb_get_serial_port_data ( port ) ;
struct usb_serial * serial = port - > serial ;
struct oti6858_control_pkt * buf ;
unsigned long flags ;
int result ;
usb_clear_halt ( serial - > dev , port - > write_urb - > pipe ) ;
usb_clear_halt ( serial - > dev , port - > read_urb - > pipe ) ;
2008-07-22 14:15:36 +04:00
buf = kmalloc ( OTI6858_CTRL_PKT_SIZE , GFP_KERNEL ) ;
2013-12-29 22:22:56 +04:00
if ( ! buf )
2007-03-27 14:34:30 +04:00
return - ENOMEM ;
result = usb_control_msg ( serial - > dev , usb_rcvctrlpipe ( serial - > dev , 0 ) ,
OTI6858_REQ_T_GET_STATUS ,
OTI6858_REQ_GET_STATUS ,
0 , 0 ,
buf , OTI6858_CTRL_PKT_SIZE ,
100 ) ;
if ( result ! = OTI6858_CTRL_PKT_SIZE ) {
/* assume default (after power-on reset) values */
buf - > divisor = cpu_to_le16 ( 0x009c ) ; /* 38400 bps */
buf - > frame_fmt = 0x03 ; /* 8N1 */
buf - > something = 0x43 ;
buf - > control = 0x4c ; /* DTR, RTS */
buf - > tx_status = 0x00 ;
buf - > pin_state = 0x5b ; /* RTS, CTS, DSR, DTR, RI, DCD */
buf - > rx_bytes_avail = 0x00 ;
}
spin_lock_irqsave ( & priv - > lock , flags ) ;
memcpy ( & priv - > status , buf , OTI6858_CTRL_PKT_SIZE ) ;
priv - > pending_setup . divisor = buf - > divisor ;
priv - > pending_setup . frame_fmt = buf - > frame_fmt ;
priv - > pending_setup . control = buf - > control ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
kfree ( buf ) ;
2012-09-14 22:50:32 +04:00
dev_dbg ( & port - > dev , " %s(): submitting interrupt urb \n " , __func__ ) ;
2007-03-27 14:34:30 +04:00
result = usb_submit_urb ( port - > interrupt_in_urb , GFP_KERNEL ) ;
if ( result ! = 0 ) {
2012-09-14 22:50:32 +04:00
dev_err ( & port - > dev , " %s(): usb_submit_urb() failed with error %d \n " ,
__func__ , result ) ;
2009-06-11 15:26:29 +04:00
oti6858_close ( port ) ;
2011-11-10 17:58:32 +04:00
return result ;
2007-03-27 14:34:30 +04:00
}
/* setup termios */
2008-07-22 14:09:07 +04:00
if ( tty )
2013-06-26 18:47:31 +04:00
oti6858_set_termios ( tty , port , NULL ) ;
2013-06-26 18:47:23 +04:00
2007-03-27 14:34:30 +04:00
return 0 ;
}
2009-06-11 15:26:29 +04:00
static void oti6858_close ( struct usb_serial_port * port )
2007-03-27 14:34:30 +04:00
{
struct oti6858_private * priv = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
2010-05-16 22:33:52 +04:00
spin_lock_irqsave ( & port - > lock , flags ) ;
2007-03-27 14:34:30 +04:00
/* clear out any remaining data in the buffer */
2010-05-16 22:33:52 +04:00
kfifo_reset_out ( & port - > write_fifo ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2007-03-27 14:34:30 +04:00
2012-09-14 22:50:32 +04:00
dev_dbg ( & port - > dev , " %s(): after buf_clear() \n " , __func__ ) ;
2007-03-27 14:34:30 +04:00
/* cancel scheduled setup */
2010-12-24 18:14:20 +03:00
cancel_delayed_work_sync ( & priv - > delayed_setup_work ) ;
cancel_delayed_work_sync ( & priv - > delayed_write_work ) ;
2007-03-27 14:34:30 +04:00
/* shutdown our urbs */
2012-09-14 22:50:32 +04:00
dev_dbg ( & port - > dev , " %s(): shutting down urbs \n " , __func__ ) ;
2007-03-27 14:34:30 +04:00
usb_kill_urb ( port - > write_urb ) ;
usb_kill_urb ( port - > read_urb ) ;
usb_kill_urb ( port - > interrupt_in_urb ) ;
}
2011-02-14 19:26:50 +03:00
static int oti6858_tiocmset ( struct tty_struct * tty ,
2007-03-27 14:34:30 +04:00
unsigned int set , unsigned int clear )
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2007-03-27 14:34:30 +04:00
struct oti6858_private * priv = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
u8 control ;
2012-09-14 22:50:32 +04:00
dev_dbg ( & port - > dev , " %s(set = 0x%08x, clear = 0x%08x) \n " ,
__func__ , set , clear ) ;
2007-03-27 14:34:30 +04:00
/* FIXME: check if this is correct (active high/low) */
spin_lock_irqsave ( & priv - > lock , flags ) ;
control = priv - > pending_setup . control ;
if ( ( set & TIOCM_RTS ) ! = 0 )
control | = CONTROL_RTS_HIGH ;
if ( ( set & TIOCM_DTR ) ! = 0 )
control | = CONTROL_DTR_HIGH ;
if ( ( clear & TIOCM_RTS ) ! = 0 )
control & = ~ CONTROL_RTS_HIGH ;
if ( ( clear & TIOCM_DTR ) ! = 0 )
control & = ~ CONTROL_DTR_HIGH ;
2008-07-22 14:15:36 +04:00
if ( control ! = priv - > pending_setup . control )
2007-03-27 14:34:30 +04:00
priv - > pending_setup . control = control ;
2008-07-22 14:15:36 +04:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2007-03-27 14:34:30 +04:00
return 0 ;
}
2011-02-14 19:26:14 +03:00
static int oti6858_tiocmget ( struct tty_struct * tty )
2007-03-27 14:34:30 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2007-03-27 14:34:30 +04:00
struct oti6858_private * priv = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
unsigned pin_state ;
unsigned result = 0 ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
pin_state = priv - > status . pin_state & PIN_MASK ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
/* FIXME: check if this is correct (active high/low) */
if ( ( pin_state & PIN_RTS ) ! = 0 )
result | = TIOCM_RTS ;
if ( ( pin_state & PIN_CTS ) ! = 0 )
result | = TIOCM_CTS ;
if ( ( pin_state & PIN_DSR ) ! = 0 )
result | = TIOCM_DSR ;
if ( ( pin_state & PIN_DTR ) ! = 0 )
result | = TIOCM_DTR ;
if ( ( pin_state & PIN_RI ) ! = 0 )
result | = TIOCM_RI ;
if ( ( pin_state & PIN_DCD ) ! = 0 )
result | = TIOCM_CD ;
2012-09-14 22:50:32 +04:00
dev_dbg ( & port - > dev , " %s() = 0x%08x \n " , __func__ , result ) ;
2007-03-27 14:34:30 +04:00
return result ;
}
static void oti6858_read_int_callback ( struct urb * urb )
{
2008-02-24 13:41:47 +03:00
struct usb_serial_port * port = urb - > context ;
2007-03-27 14:34:30 +04:00
struct oti6858_private * priv = usb_get_serial_port_data ( port ) ;
int transient = 0 , can_recv = 0 , resubmit = 1 ;
2007-06-16 02:44:13 +04:00
int status = urb - > status ;
2007-03-27 14:34:30 +04:00
2007-06-16 02:44:13 +04:00
switch ( status ) {
2007-03-27 14:34:30 +04:00
case 0 :
/* success */
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
/* this urb is terminated, clean up */
2012-09-14 22:50:32 +04:00
dev_dbg ( & urb - > dev - > dev , " %s(): urb shutting down with status: %d \n " ,
__func__ , status ) ;
2007-03-27 14:34:30 +04:00
return ;
default :
2012-09-14 22:50:32 +04:00
dev_dbg ( & urb - > dev - > dev , " %s(): nonzero urb status received: %d \n " ,
__func__ , status ) ;
2007-03-27 14:34:30 +04:00
break ;
}
2007-06-16 02:44:13 +04:00
if ( status = = 0 & & urb - > actual_length = = OTI6858_CTRL_PKT_SIZE ) {
2007-03-27 14:34:30 +04:00
struct oti6858_control_pkt * xs = urb - > transfer_buffer ;
unsigned long flags ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( ! priv - > transient ) {
if ( ! OTI6858_CTRL_EQUALS_PENDING ( xs , priv ) ) {
if ( xs - > rx_bytes_avail = = 0 ) {
priv - > transient = 4 ;
priv - > setup_done = 0 ;
resubmit = 0 ;
2012-09-14 22:50:32 +04:00
dev_dbg ( & port - > dev , " %s(): scheduling setup_line() \n " , __func__ ) ;
2007-03-27 14:34:30 +04:00
schedule_delayed_work ( & priv - > delayed_setup_work , 0 ) ;
}
}
} else {
if ( OTI6858_CTRL_EQUALS_PENDING ( xs , priv ) ) {
priv - > transient = 0 ;
} else if ( ! priv - > setup_done ) {
resubmit = 0 ;
} else if ( - - priv - > transient = = 0 ) {
if ( xs - > rx_bytes_avail = = 0 ) {
priv - > transient = 4 ;
priv - > setup_done = 0 ;
resubmit = 0 ;
2012-09-14 22:50:32 +04:00
dev_dbg ( & port - > dev , " %s(): scheduling setup_line() \n " , __func__ ) ;
2007-03-27 14:34:30 +04:00
schedule_delayed_work ( & priv - > delayed_setup_work , 0 ) ;
}
}
}
if ( ! priv - > transient ) {
2014-01-03 01:49:34 +04:00
u8 delta = xs - > pin_state ^ priv - > status . pin_state ;
2014-01-03 01:49:35 +04:00
if ( delta & PIN_MSR_MASK ) {
if ( delta & PIN_CTS )
port - > icount . cts + + ;
if ( delta & PIN_DSR )
port - > icount . dsr + + ;
if ( delta & PIN_RI )
port - > icount . rng + + ;
if ( delta & PIN_DCD )
port - > icount . dcd + + ;
2013-03-21 15:37:19 +04:00
wake_up_interruptible ( & port - > port . delta_msr_wait ) ;
2014-01-03 01:49:35 +04:00
}
2014-01-03 01:49:34 +04:00
2007-03-27 14:34:30 +04:00
memcpy ( & priv - > status , xs , OTI6858_CTRL_PKT_SIZE ) ;
}
if ( ! priv - > transient & & xs - > rx_bytes_avail ! = 0 ) {
can_recv = xs - > rx_bytes_avail ;
priv - > flags . read_urb_in_use = 1 ;
}
transient = priv - > transient ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
}
if ( can_recv ) {
int result ;
result = usb_submit_urb ( port - > read_urb , GFP_ATOMIC ) ;
if ( result ! = 0 ) {
priv - > flags . read_urb_in_use = 0 ;
dev_err ( & port - > dev , " %s(): usb_submit_urb() failed, "
2008-03-04 03:08:34 +03:00
" error %d \n " , __func__ , result ) ;
2007-03-27 14:34:30 +04:00
} else {
resubmit = 0 ;
}
} else if ( ! transient ) {
unsigned long flags ;
2010-05-16 22:33:52 +04:00
int count ;
spin_lock_irqsave ( & port - > lock , flags ) ;
count = kfifo_len ( & port - > write_fifo ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2007-03-27 14:34:30 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2010-05-16 22:33:52 +04:00
if ( priv - > flags . write_urb_in_use = = 0 & & count ! = 0 ) {
2008-07-22 14:15:36 +04:00
schedule_delayed_work ( & priv - > delayed_write_work , 0 ) ;
2007-03-27 14:34:30 +04:00
resubmit = 0 ;
}
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
}
if ( resubmit ) {
int result ;
2012-09-14 22:50:32 +04:00
/* dev_dbg(&urb->dev->dev, "%s(): submitting interrupt urb\n", __func__); */
2007-03-27 14:34:30 +04:00
result = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( result ! = 0 ) {
dev_err ( & urb - > dev - > dev ,
" %s(): usb_submit_urb() failed with "
2008-03-04 03:08:34 +03:00
" error %d \n " , __func__ , result ) ;
2007-03-27 14:34:30 +04:00
}
}
}
static void oti6858_read_bulk_callback ( struct urb * urb )
{
2008-02-24 13:41:47 +03:00
struct usb_serial_port * port = urb - > context ;
2007-03-27 14:34:30 +04:00
struct oti6858_private * priv = usb_get_serial_port_data ( port ) ;
unsigned char * data = urb - > transfer_buffer ;
unsigned long flags ;
2007-06-16 02:44:13 +04:00
int status = urb - > status ;
2008-01-19 19:02:37 +03:00
int result ;
2007-03-27 14:34:30 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
priv - > flags . read_urb_in_use = 0 ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2007-06-16 02:44:13 +04:00
if ( status ! = 0 ) {
2012-09-14 22:50:32 +04:00
dev_dbg ( & urb - > dev - > dev , " %s(): unable to handle the error, exiting \n " , __func__ ) ;
2007-03-27 14:34:30 +04:00
return ;
}
2013-01-03 18:53:06 +04:00
if ( urb - > actual_length > 0 ) {
2013-01-03 18:53:04 +04:00
tty_insert_flip_string ( & port - > port , data , urb - > actual_length ) ;
2013-01-03 18:53:06 +04:00
tty_flip_buffer_push ( & port - > port ) ;
2007-03-27 14:34:30 +04:00
}
2010-02-17 18:05:47 +03:00
/* schedule the interrupt urb */
result = usb_submit_urb ( port - > interrupt_in_urb , GFP_ATOMIC ) ;
if ( result ! = 0 & & result ! = - EPERM ) {
dev_err ( & port - > dev , " %s(): usb_submit_urb() failed, "
" error %d \n " , __func__ , result ) ;
2007-03-27 14:34:30 +04:00
}
}
static void oti6858_write_bulk_callback ( struct urb * urb )
{
2008-02-24 13:41:47 +03:00
struct usb_serial_port * port = urb - > context ;
2007-03-27 14:34:30 +04:00
struct oti6858_private * priv = usb_get_serial_port_data ( port ) ;
2007-06-16 02:44:13 +04:00
int status = urb - > status ;
2007-03-27 14:34:30 +04:00
int result ;
2007-06-16 02:44:13 +04:00
switch ( status ) {
2007-03-27 14:34:30 +04:00
case 0 :
/* success */
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
/* this urb is terminated, clean up */
2012-09-14 22:50:32 +04:00
dev_dbg ( & urb - > dev - > dev , " %s(): urb shutting down with status: %d \n " , __func__ , status ) ;
2007-03-27 14:34:30 +04:00
priv - > flags . write_urb_in_use = 0 ;
return ;
default :
/* error in the urb, so we have to resubmit it */
2012-09-14 22:50:32 +04:00
dev_dbg ( & urb - > dev - > dev , " %s(): nonzero write bulk status received: %d \n " , __func__ , status ) ;
dev_dbg ( & urb - > dev - > dev , " %s(): overflow in write \n " , __func__ ) ;
2007-03-27 14:34:30 +04:00
port - > write_urb - > transfer_buffer_length = 1 ;
result = usb_submit_urb ( port - > write_urb , GFP_ATOMIC ) ;
if ( result ) {
2012-02-10 16:20:51 +04:00
dev_err_console ( port , " %s(): usb_submit_urb() failed, "
2008-03-04 03:08:34 +03:00
" error %d \n " , __func__ , result ) ;
2007-03-27 14:34:30 +04:00
} else {
return ;
}
}
priv - > flags . write_urb_in_use = 0 ;
2008-07-22 14:15:36 +04:00
/* schedule the interrupt urb if we are still open */
2012-09-14 22:50:32 +04:00
dev_dbg ( & port - > dev , " %s(): submitting interrupt urb \n " , __func__ ) ;
2007-03-27 14:34:30 +04:00
result = usb_submit_urb ( port - > interrupt_in_urb , GFP_ATOMIC ) ;
if ( result ! = 0 ) {
dev_err ( & port - > dev , " %s(): failed submitting int urb, "
2008-03-04 03:08:34 +03:00
" error %d \n " , __func__ , result ) ;
2007-03-27 14:34:30 +04:00
}
}
2012-05-09 02:46:14 +04:00
module_usb_serial_driver ( serial_drivers , id_table ) ;
2007-03-27 14:34:30 +04:00
MODULE_DESCRIPTION ( OTI6858_DESCRIPTION ) ;
MODULE_AUTHOR ( OTI6858_AUTHOR ) ;
MODULE_LICENSE ( " GPL " ) ;