2002-04-09 12:14:34 -07:00
/*
* mos7720 . c
* Controls the Moschip 7720 usb to dual port serial convertor
*
* Copyright 2006 Moschip Semiconductor Tech . Ltd .
*
* 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 , version 2 of the License .
*
* Developed by :
* VijayaKumar . G . N . < vijaykumar @ aspirecom . net >
* AjayKumar < ajay @ aspirecom . net >
* Gurudeva . N . < gurudev @ aspirecom . net >
*
* Cleaned up from the original by :
* Greg Kroah - Hartman < gregkh @ suse . de >
*
* Originally based on drivers / usb / serial / io_edgeport . c which is :
* Copyright ( C ) 2000 Inside Out Networks , All rights reserved .
* Copyright ( C ) 2001 - 2002 Greg Kroah - Hartman < greg @ kroah . com >
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/tty.h>
# include <linux/tty_driver.h>
# include <linux/tty_flip.h>
# include <linux/module.h>
# include <linux/spinlock.h>
# include <linux/serial.h>
# include <linux/serial_reg.h>
# include <linux/usb.h>
# include <linux/usb/serial.h>
# include <asm/uaccess.h>
/*
* Version Information
*/
# define DRIVER_VERSION "1.0.0.4F"
# define DRIVER_AUTHOR "Aspire Communications pvt Ltd."
# define DRIVER_DESC "Moschip USB Serial Driver"
/* default urb timeout */
# define MOS_WDR_TIMEOUT (HZ * 5)
# define MOS_PORT1 0x0200
# define MOS_PORT2 0x0300
# define MOS_VENREG 0x0000
# define MOS_MAX_PORT 0x02
# define MOS_WRITE 0x0E
# define MOS_READ 0x0D
/* Interrupt Rotinue Defines */
# define SERIAL_IIR_RLS 0x06
# define SERIAL_IIR_RDA 0x04
# define SERIAL_IIR_CTI 0x0c
# define SERIAL_IIR_THR 0x02
# define SERIAL_IIR_MS 0x00
# define NUM_URBS 16 /* URB Count */
# define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */
/* This structure holds all of the local port information */
struct moschip_port
{
__u8 shadowLCR ; /* last LCR value received */
__u8 shadowMCR ; /* last MCR value received */
__u8 shadowMSR ; /* last MSR value received */
char open ;
struct async_icount icount ;
struct usb_serial_port * port ; /* loop back to the owner */
struct urb * write_urb_pool [ NUM_URBS ] ;
} ;
/* This structure holds all of the individual serial device information */
struct moschip_serial
{
int interrupt_started ;
} ;
static int debug ;
# define USB_VENDOR_ID_MOSCHIP 0x9710
# define MOSCHIP_DEVICE_ID_7720 0x7720
# define MOSCHIP_DEVICE_ID_7715 0x7715
static struct usb_device_id moschip_port_id_table [ ] = {
{ USB_DEVICE ( USB_VENDOR_ID_MOSCHIP , MOSCHIP_DEVICE_ID_7720 ) } ,
{ } /* terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , moschip_port_id_table ) ;
/*
* mos7720_interrupt_callback
* this is the callback function for when we have received data on the
* interrupt endpoint .
*/
static void mos7720_interrupt_callback ( struct urb * urb )
{
int result ;
int length ;
__u32 * data ;
unsigned int status ;
__u8 sp1 ;
__u8 sp2 ;
__u8 st ;
dbg ( " %s " , " : Entering \n " ) ;
if ( ! urb ) {
dbg ( " %s " , " Invalid Pointer !!!!: \n " ) ;
return ;
}
switch ( urb - > status ) {
case 0 :
/* success */
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
/* this urb is terminated, clean up */
dbg ( " %s - urb shutting down with status: %d " , __FUNCTION__ ,
urb - > status ) ;
return ;
default :
dbg ( " %s - nonzero urb status received: %d " , __FUNCTION__ ,
urb - > status ) ;
goto exit ;
}
length = urb - > actual_length ;
data = urb - > transfer_buffer ;
/* Moschip get 4 bytes
* Byte 1 IIR Port 1 ( port . number is 0 )
* Byte 2 IIR Port 2 ( port . number is 1 )
* Byte 3 - - - - - - - - - - - - - -
* Byte 4 FIFO status for both */
if ( length & & length > 4 ) {
dbg ( " Wrong data !!! " ) ;
return ;
}
status = * data ;
sp1 = ( status & 0xff000000 ) > > 24 ;
sp2 = ( status & 0x00ff0000 ) > > 16 ;
st = status & 0x000000ff ;
if ( ( sp1 & 0x01 ) | | ( sp2 & 0x01 ) ) {
/* No Interrupt Pending in both the ports */
dbg ( " No Interrupt !!! " ) ;
} else {
switch ( sp1 & 0x0f ) {
case SERIAL_IIR_RLS :
dbg ( " Serial Port 1: Receiver status error or address "
" bit detected in 9-bit mode \n " ) ;
break ;
case SERIAL_IIR_CTI :
dbg ( " Serial Port 1: Receiver time out " ) ;
break ;
case SERIAL_IIR_MS :
dbg ( " Serial Port 1: Modem status change " ) ;
break ;
}
switch ( sp2 & 0x0f ) {
case SERIAL_IIR_RLS :
dbg ( " Serial Port 2: Receiver status error or address "
" bit detected in 9-bit mode " ) ;
break ;
case SERIAL_IIR_CTI :
dbg ( " Serial Port 2: Receiver time out " ) ;
break ;
case SERIAL_IIR_MS :
dbg ( " Serial Port 2: Modem status change " ) ;
break ;
}
}
exit :
result = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( result )
dev_err ( & urb - > dev - > dev ,
" %s - Error %d submitting control urb \n " ,
__FUNCTION__ , result ) ;
return ;
}
/*
* mos7720_bulk_in_callback
* this is the callback function for when we have received data on the
* bulk in endpoint .
*/
static void mos7720_bulk_in_callback ( struct urb * urb )
{
int status ;
unsigned char * data ;
struct usb_serial_port * port ;
struct moschip_port * mos7720_port ;
struct tty_struct * tty ;
if ( urb - > status ) {
dbg ( " nonzero read bulk status received: %d " , urb - > status ) ;
return ;
}
mos7720_port = urb - > context ;
if ( ! mos7720_port ) {
dbg ( " %s " , " NULL mos7720_port pointer \n " ) ;
return ;
}
port = mos7720_port - > port ;
dbg ( " Entering...%s " , __FUNCTION__ ) ;
data = urb - > transfer_buffer ;
tty = port - > tty ;
if ( tty & & urb - > actual_length ) {
tty_buffer_request_room ( tty , urb - > actual_length ) ;
tty_insert_flip_string ( tty , data , urb - > actual_length ) ;
tty_flip_buffer_push ( tty ) ;
}
if ( ! port - > read_urb ) {
dbg ( " URB KILLED !!! " ) ;
return ;
}
if ( port - > read_urb - > status ! = - EINPROGRESS ) {
port - > read_urb - > dev = port - > serial - > dev ;
status = usb_submit_urb ( port - > read_urb , GFP_ATOMIC ) ;
if ( status )
dbg ( " usb_submit_urb(read bulk) failed, status = %d " ,
status ) ;
}
}
/*
* mos7720_bulk_out_data_callback
* this is the callback function for when we have finished sending serial
* data on the bulk out endpoint .
*/
static void mos7720_bulk_out_data_callback ( struct urb * urb )
{
struct moschip_port * mos7720_port ;
struct tty_struct * tty ;
if ( urb - > status ) {
dbg ( " nonzero write bulk status received:%d " , urb - > status ) ;
return ;
}
mos7720_port = urb - > context ;
if ( ! mos7720_port ) {
dbg ( " NULL mos7720_port pointer " ) ;
return ;
}
dbg ( " Entering ......... " ) ;
tty = mos7720_port - > port - > tty ;
if ( tty & & mos7720_port - > open ) {
/* let the tty driver wakeup if it has a special *
* write_wakeup function */
if ( ( tty - > flags & ( 1 < < TTY_DO_WRITE_WAKEUP ) ) & &
tty - > ldisc . write_wakeup )
( tty - > ldisc . write_wakeup ) ( tty ) ;
/* tell the tty driver that something has changed */
wake_up_interruptible ( & tty - > write_wait ) ;
}
/* schedule_work(&mos7720_port->port->work); */
}
/*
* send_mos_cmd
* this function will be used for sending command to device
*/
static int send_mos_cmd ( struct usb_serial * serial , __u8 request , __u16 value ,
__u16 index , void * data )
{
int status ;
unsigned int pipe ;
u16 product = le16_to_cpu ( serial - > dev - > descriptor . idProduct ) ;
__u8 requesttype ;
__u16 size = 0x0000 ;
if ( value < MOS_MAX_PORT ) {
if ( product = = MOSCHIP_DEVICE_ID_7715 ) {
value = value * 0x100 + 0x100 ;
} else {
value = value * 0x100 + 0x200 ;
}
} else {
value = 0x0000 ;
if ( ( product = = MOSCHIP_DEVICE_ID_7715 ) & &
( index ! = 0x08 ) ) {
dbg ( " serial->product== MOSCHIP_DEVICE_ID_7715 " ) ;
//index = 0x01 ;
}
}
if ( request = = MOS_WRITE ) {
request = ( __u8 ) MOS_WRITE ;
requesttype = ( __u8 ) 0x40 ;
value = value + ( __u16 ) * ( ( unsigned char * ) data ) ;
data = NULL ;
pipe = usb_sndctrlpipe ( serial - > dev , 0 ) ;
} else {
request = ( __u8 ) MOS_READ ;
requesttype = ( __u8 ) 0xC0 ;
size = 0x01 ;
pipe = usb_rcvctrlpipe ( serial - > dev , 0 ) ;
}
status = usb_control_msg ( serial - > dev , pipe , request , requesttype ,
value , index , data , size , MOS_WDR_TIMEOUT ) ;
if ( status < 0 )
dbg ( " Command Write failed Value %x index %x \n " , value , index ) ;
return status ;
}
static int mos7720_open ( struct usb_serial_port * port , struct file * filp )
{
struct usb_serial * serial ;
struct usb_serial_port * port0 ;
struct urb * urb ;
struct moschip_serial * mos7720_serial ;
struct moschip_port * mos7720_port ;
int response ;
int port_number ;
char data ;
int j ;
serial = port - > serial ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return - ENODEV ;
port0 = serial - > port [ 0 ] ;
mos7720_serial = usb_get_serial_data ( serial ) ;
if ( mos7720_serial = = NULL | | port0 = = NULL )
return - ENODEV ;
usb_clear_halt ( serial - > dev , port - > write_urb - > pipe ) ;
usb_clear_halt ( serial - > dev , port - > read_urb - > pipe ) ;
/* Initialising the write urb pool */
for ( j = 0 ; j < NUM_URBS ; + + j ) {
2006-12-06 20:33:16 -08:00
urb = usb_alloc_urb ( 0 , GFP_ATOMIC ) ;
2002-04-09 12:14:34 -07:00
mos7720_port - > write_urb_pool [ j ] = urb ;
if ( urb = = NULL ) {
err ( " No more urbs??? " ) ;
continue ;
}
urb - > transfer_buffer = kmalloc ( URB_TRANSFER_BUFFER_SIZE ,
GFP_KERNEL ) ;
if ( ! urb - > transfer_buffer ) {
err ( " %s-out of memory for urb buffers. " , __FUNCTION__ ) ;
continue ;
}
}
/* Initialize MCS7720 -- Write Init values to corresponding Registers
*
* Register Index
* 1 : IER
* 2 : FCR
* 3 : LCR
* 4 : MCR
*
* 0x08 : SP1 / 2 Control Reg
*/
port_number = port - > number - port - > serial - > minor ;
send_mos_cmd ( port - > serial , MOS_READ , port_number , UART_LSR , & data ) ;
dbg ( " SS::%p LSR:%x \n " , mos7720_port , data ) ;
dbg ( " Check:Sending Command .......... " ) ;
data = 0x02 ;
send_mos_cmd ( serial , MOS_WRITE , MOS_MAX_PORT , 0x01 , & data ) ;
data = 0x02 ;
send_mos_cmd ( serial , MOS_WRITE , MOS_MAX_PORT , 0x02 , & data ) ;
data = 0x00 ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x01 , & data ) ;
data = 0x00 ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x02 , & data ) ;
data = 0xCF ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x02 , & data ) ;
data = 0x03 ;
mos7720_port - > shadowLCR = data ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x03 , & data ) ;
data = 0x0b ;
mos7720_port - > shadowMCR = data ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x04 , & data ) ;
data = 0x0b ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x04 , & data ) ;
data = 0x00 ;
send_mos_cmd ( serial , MOS_READ , MOS_MAX_PORT , 0x08 , & data ) ;
data = 0x00 ;
send_mos_cmd ( serial , MOS_WRITE , MOS_MAX_PORT , 0x08 , & data ) ;
/* data = 0x00;
send_mos_cmd ( serial , MOS_READ , MOS_MAX_PORT , port_number + 1 , & data ) ;
data = 0x03 ;
send_mos_cmd ( serial , MOS_WRITE , MOS_MAX_PORT , port_number + 1 , & data ) ;
data = 0x00 ;
send_mos_cmd ( port - > serial , MOS_WRITE , MOS_MAX_PORT , port_number + 1 , & data ) ;
*/
data = 0x00 ;
send_mos_cmd ( serial , MOS_READ , MOS_MAX_PORT , 0x08 , & data ) ;
data = data | ( port - > number - port - > serial - > minor + 1 ) ;
send_mos_cmd ( serial , MOS_WRITE , MOS_MAX_PORT , 0x08 , & data ) ;
data = 0x83 ;
mos7720_port - > shadowLCR = data ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x03 , & data ) ;
data = 0x0c ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x00 , & data ) ;
data = 0x00 ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x01 , & data ) ;
data = 0x03 ;
mos7720_port - > shadowLCR = data ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x03 , & data ) ;
data = 0x0c ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x01 , & data ) ;
data = 0x0c ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x01 , & data ) ;
//Matrix
/* force low_latency on so that our tty_push actually forces *
* the data through , otherwise it is scheduled , and with *
* high data rates ( like with OHCI ) data can get lost . */
if ( port - > tty )
port - > tty - > low_latency = 1 ;
/* see if we've set up our endpoint info yet *
* ( can ' t set it up in mos7720_startup as the *
* structures were not set up at that time . ) */
if ( ! mos7720_serial - > interrupt_started ) {
dbg ( " Interrupt buffer NULL !!! " ) ;
/* not set up yet, so do it now */
mos7720_serial - > interrupt_started = 1 ;
dbg ( " To Submit URB !!! " ) ;
/* set up our interrupt urb */
usb_fill_int_urb ( port0 - > interrupt_in_urb , serial - > dev ,
usb_rcvintpipe ( serial - > dev ,
port - > interrupt_in_endpointAddress ) ,
port0 - > interrupt_in_buffer ,
port0 - > interrupt_in_urb - > transfer_buffer_length ,
mos7720_interrupt_callback , mos7720_port ,
port0 - > interrupt_in_urb - > interval ) ;
/* start interrupt read for this mos7720 this interrupt *
* will continue as long as the mos7720 is connected */
dbg ( " Submit URB over !!! " ) ;
response = usb_submit_urb ( port0 - > interrupt_in_urb , GFP_KERNEL ) ;
if ( response )
dev_err ( & port - > dev ,
" %s - Error %d submitting control urb " ,
__FUNCTION__ , response ) ;
}
/* set up our bulk in urb */
usb_fill_bulk_urb ( port - > read_urb , serial - > dev ,
usb_rcvbulkpipe ( serial - > dev ,
port - > bulk_in_endpointAddress ) ,
port - > bulk_in_buffer ,
port - > read_urb - > transfer_buffer_length ,
mos7720_bulk_in_callback , mos7720_port ) ;
response = usb_submit_urb ( port - > read_urb , GFP_KERNEL ) ;
if ( response )
dev_err ( & port - > dev ,
" %s - Error %d submitting read urb " , __FUNCTION__ , response ) ;
/* initialize our icount structure */
memset ( & ( mos7720_port - > icount ) , 0x00 , sizeof ( mos7720_port - > icount ) ) ;
/* initialize our port settings */
mos7720_port - > shadowMCR = UART_MCR_OUT2 ; /* Must set to enable ints! */
/* send a open port command */
mos7720_port - > open = 1 ;
return 0 ;
}
/*
* mos7720_chars_in_buffer
* this function is called by the tty driver when it wants to know how many
* bytes of data we currently have outstanding in the port ( data that has
* been written , but hasn ' t made it out the port yet )
* If successful , we return the number of bytes left to be written in the
* system ,
* Otherwise we return a negative error number .
*/
static int mos7720_chars_in_buffer ( struct usb_serial_port * port )
{
int i ;
int chars = 0 ;
struct moschip_port * mos7720_port ;
dbg ( " %s:entering ........... " , __FUNCTION__ ) ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL ) {
dbg ( " %s:leaving ........... " , __FUNCTION__ ) ;
return - ENODEV ;
}
for ( i = 0 ; i < NUM_URBS ; + + i ) {
if ( mos7720_port - > write_urb_pool [ i ] - > status = = - EINPROGRESS )
chars + = URB_TRANSFER_BUFFER_SIZE ;
}
dbg ( " %s - returns %d " , __FUNCTION__ , chars ) ;
return chars ;
}
static void mos7720_close ( struct usb_serial_port * port , struct file * filp )
{
struct usb_serial * serial ;
struct moschip_port * mos7720_port ;
char data ;
int j ;
dbg ( " mos7720_close:entering... " ) ;
serial = port - > serial ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return ;
for ( j = 0 ; j < NUM_URBS ; + + j )
usb_kill_urb ( mos7720_port - > write_urb_pool [ j ] ) ;
/* Freeing Write URBs */
for ( j = 0 ; j < NUM_URBS ; + + j ) {
if ( mos7720_port - > write_urb_pool [ j ] ) {
kfree ( mos7720_port - > write_urb_pool [ j ] - > transfer_buffer ) ;
usb_free_urb ( mos7720_port - > write_urb_pool [ j ] ) ;
}
}
/* While closing port, shutdown all bulk read, write *
* and interrupt read if they exists */
if ( serial - > dev ) {
dbg ( " Shutdown bulk write " ) ;
usb_kill_urb ( port - > write_urb ) ;
dbg ( " Shutdown bulk read " ) ;
usb_kill_urb ( port - > read_urb ) ;
}
data = 0x00 ;
send_mos_cmd ( serial , MOS_WRITE , port - > number - port - > serial - > minor ,
0x04 , & data ) ;
data = 0x00 ;
send_mos_cmd ( serial , MOS_WRITE , port - > number - port - > serial - > minor ,
0x01 , & data ) ;
mos7720_port - > open = 0 ;
dbg ( " Leaving %s " , __FUNCTION__ ) ;
}
static void mos7720_break ( struct usb_serial_port * port , int break_state )
{
unsigned char data ;
struct usb_serial * serial ;
struct moschip_port * mos7720_port ;
dbg ( " Entering %s " , __FUNCTION__ ) ;
serial = port - > serial ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return ;
if ( break_state = = - 1 )
data = mos7720_port - > shadowLCR | UART_LCR_SBC ;
else
data = mos7720_port - > shadowLCR & ~ UART_LCR_SBC ;
mos7720_port - > shadowLCR = data ;
send_mos_cmd ( serial , MOS_WRITE , port - > number - port - > serial - > minor ,
0x03 , & data ) ;
return ;
}
/*
* mos7720_write_room
* this function is called by the tty driver when it wants to know how many
* bytes of data we can accept for a specific port .
* If successful , we return the amount of room that we have for this port
* Otherwise we return a negative error number .
*/
static int mos7720_write_room ( struct usb_serial_port * port )
{
struct moschip_port * mos7720_port ;
int room = 0 ;
int i ;
dbg ( " %s:entering ........... " , __FUNCTION__ ) ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL ) {
dbg ( " %s:leaving ........... " , __FUNCTION__ ) ;
return - ENODEV ;
}
for ( i = 0 ; i < NUM_URBS ; + + i ) {
if ( mos7720_port - > write_urb_pool [ i ] - > status ! = - EINPROGRESS )
room + = URB_TRANSFER_BUFFER_SIZE ;
}
dbg ( " %s - returns %d " , __FUNCTION__ , room ) ;
return room ;
}
static int mos7720_write ( struct usb_serial_port * port ,
const unsigned char * data , int count )
{
int status ;
int i ;
int bytes_sent = 0 ;
int transfer_size ;
struct moschip_port * mos7720_port ;
struct usb_serial * serial ;
struct urb * urb ;
const unsigned char * current_position = data ;
dbg ( " %s:entering ........... " , __FUNCTION__ ) ;
serial = port - > serial ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL ) {
dbg ( " mos7720_port is NULL " ) ;
return - ENODEV ;
}
/* try to find a free urb in the list */
urb = NULL ;
for ( i = 0 ; i < NUM_URBS ; + + i ) {
if ( mos7720_port - > write_urb_pool [ i ] - > status ! = - EINPROGRESS ) {
urb = mos7720_port - > write_urb_pool [ i ] ;
dbg ( " URB:%d " , i ) ;
break ;
}
}
if ( urb = = NULL ) {
dbg ( " %s - no more free urbs " , __FUNCTION__ ) ;
goto exit ;
}
if ( urb - > transfer_buffer = = NULL ) {
urb - > transfer_buffer = kmalloc ( URB_TRANSFER_BUFFER_SIZE ,
GFP_KERNEL ) ;
if ( urb - > transfer_buffer = = NULL ) {
err ( " %s no more kernel memory... " , __FUNCTION__ ) ;
goto exit ;
}
}
transfer_size = min ( count , URB_TRANSFER_BUFFER_SIZE ) ;
memcpy ( urb - > transfer_buffer , current_position , transfer_size ) ;
usb_serial_debug_data ( debug , & port - > dev , __FUNCTION__ , transfer_size ,
urb - > transfer_buffer ) ;
/* fill urb with data and submit */
usb_fill_bulk_urb ( urb , serial - > dev ,
usb_sndbulkpipe ( serial - > dev ,
port - > bulk_out_endpointAddress ) ,
urb - > transfer_buffer , transfer_size ,
mos7720_bulk_out_data_callback , mos7720_port ) ;
/* send it down the pipe */
status = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( status ) {
err ( " %s - usb_submit_urb(write bulk) failed with status = %d " ,
__FUNCTION__ , status ) ;
bytes_sent = status ;
goto exit ;
}
bytes_sent = transfer_size ;
exit :
return bytes_sent ;
}
static void mos7720_throttle ( struct usb_serial_port * port )
{
struct moschip_port * mos7720_port ;
struct tty_struct * tty ;
int status ;
dbg ( " %s- port %d \n " , __FUNCTION__ , port - > number ) ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return ;
if ( ! mos7720_port - > open ) {
dbg ( " port not opened " ) ;
return ;
}
dbg ( " %s: Entering .......... " , __FUNCTION__ ) ;
tty = port - > tty ;
if ( ! tty ) {
dbg ( " %s - no tty available " , __FUNCTION__ ) ;
return ;
}
/* if we are implementing XON/XOFF, send the stop character */
if ( I_IXOFF ( tty ) ) {
unsigned char stop_char = STOP_CHAR ( tty ) ;
status = mos7720_write ( port , & stop_char , 1 ) ;
if ( status < = 0 )
return ;
}
/* if we are implementing RTS/CTS, toggle that line */
if ( tty - > termios - > c_cflag & CRTSCTS ) {
mos7720_port - > shadowMCR & = ~ UART_MCR_RTS ;
status = send_mos_cmd ( port - > serial , MOS_WRITE ,
port - > number - port - > serial - > minor ,
UART_MCR , & mos7720_port - > shadowMCR ) ;
if ( status ! = 0 )
return ;
}
}
static void mos7720_unthrottle ( struct usb_serial_port * port )
{
struct tty_struct * tty ;
int status ;
struct moschip_port * mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return ;
if ( ! mos7720_port - > open ) {
dbg ( " %s - port not opened " , __FUNCTION__ ) ;
return ;
}
dbg ( " %s: Entering .......... " , __FUNCTION__ ) ;
tty = port - > tty ;
if ( ! tty ) {
dbg ( " %s - no tty available " , __FUNCTION__ ) ;
return ;
}
/* if we are implementing XON/XOFF, send the start character */
if ( I_IXOFF ( tty ) ) {
unsigned char start_char = START_CHAR ( tty ) ;
status = mos7720_write ( port , & start_char , 1 ) ;
if ( status < = 0 )
return ;
}
/* if we are implementing RTS/CTS, toggle that line */
if ( tty - > termios - > c_cflag & CRTSCTS ) {
mos7720_port - > shadowMCR | = UART_MCR_RTS ;
status = send_mos_cmd ( port - > serial , MOS_WRITE ,
port - > number - port - > serial - > minor ,
UART_MCR , & mos7720_port - > shadowMCR ) ;
if ( status ! = 0 )
return ;
}
}
static int set_higher_rates ( struct moschip_port * mos7720_port ,
unsigned int baud )
{
unsigned char data ;
struct usb_serial_port * port ;
struct usb_serial * serial ;
int port_number ;
if ( mos7720_port = = NULL )
return - EINVAL ;
port = mos7720_port - > port ;
serial = port - > serial ;
/***********************************************
* Init Sequence for higher rates
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
dbg ( " Sending Setting Commands .......... " ) ;
port_number = port - > number - port - > serial - > minor ;
data = 0x000 ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x01 , & data ) ;
data = 0x000 ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x02 , & data ) ;
data = 0x0CF ;
send_mos_cmd ( serial , MOS_WRITE , port - > number , 0x02 , & data ) ;
data = 0x00b ;
mos7720_port - > shadowMCR = data ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x04 , & data ) ;
data = 0x00b ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x04 , & data ) ;
data = 0x000 ;
send_mos_cmd ( serial , MOS_READ , MOS_MAX_PORT , 0x08 , & data ) ;
data = 0x000 ;
send_mos_cmd ( serial , MOS_WRITE , MOS_MAX_PORT , 0x08 , & data ) ;
/***********************************************
* Set for higher rates *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
data = baud * 0x10 ;
send_mos_cmd ( serial , MOS_WRITE , MOS_MAX_PORT , port_number + 1 , & data ) ;
data = 0x003 ;
send_mos_cmd ( serial , MOS_READ , MOS_MAX_PORT , 0x08 , & data ) ;
data = 0x003 ;
send_mos_cmd ( serial , MOS_WRITE , MOS_MAX_PORT , 0x08 , & data ) ;
data = 0x02b ;
mos7720_port - > shadowMCR = data ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x04 , & data ) ;
data = 0x02b ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x04 , & data ) ;
/***********************************************
* Set DLL / DLM
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
data = mos7720_port - > shadowLCR | UART_LCR_DLAB ;
mos7720_port - > shadowLCR = data ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x03 , & data ) ;
data = 0x001 ; /* DLL */
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x00 , & data ) ;
data = 0x000 ; /* DLM */
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x01 , & data ) ;
data = mos7720_port - > shadowLCR & ~ UART_LCR_DLAB ;
mos7720_port - > shadowLCR = data ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x03 , & data ) ;
return 0 ;
}
/* baud rate information */
struct divisor_table_entry
{
__u32 baudrate ;
__u16 divisor ;
} ;
/* Define table of divisors for moschip 7720 hardware *
* These assume a 3.6864 MHz crystal , the standard / 16 , and *
* MCR .7 = 0. */
static struct divisor_table_entry divisor_table [ ] = {
{ 50 , 2304 } ,
{ 110 , 1047 } , /* 2094.545455 => 230450 => .0217 % over */
{ 134 , 857 } , /* 1713.011152 => 230398.5 => .00065% under */
{ 150 , 768 } ,
{ 300 , 384 } ,
{ 600 , 192 } ,
{ 1200 , 96 } ,
{ 1800 , 64 } ,
{ 2400 , 48 } ,
{ 4800 , 24 } ,
{ 7200 , 16 } ,
{ 9600 , 12 } ,
{ 19200 , 6 } ,
{ 38400 , 3 } ,
{ 57600 , 2 } ,
{ 115200 , 1 } ,
} ;
/*****************************************************************************
* calc_baud_rate_divisor
* this function calculates the proper baud rate divisor for the specified
* baud rate .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int calc_baud_rate_divisor ( int baudrate , int * divisor )
{
int i ;
__u16 custom ;
__u16 round1 ;
__u16 round ;
dbg ( " %s - %d " , __FUNCTION__ , baudrate ) ;
for ( i = 0 ; i < ARRAY_SIZE ( divisor_table ) ; i + + ) {
if ( divisor_table [ i ] . baudrate = = baudrate ) {
* divisor = divisor_table [ i ] . divisor ;
return 0 ;
}
}
/* After trying for all the standard baud rates *
* Try calculating the divisor for this baud rate */
if ( baudrate > 75 & & baudrate < 230400 ) {
/* get the divisor */
custom = ( __u16 ) ( 230400L / baudrate ) ;
/* Check for round off */
round1 = ( __u16 ) ( 2304000L / baudrate ) ;
round = ( __u16 ) ( round1 - ( custom * 10 ) ) ;
if ( round > 4 )
custom + + ;
* divisor = custom ;
dbg ( " Baud %d = %d " , baudrate , custom ) ;
return 0 ;
}
dbg ( " Baud calculation Failed... " ) ;
return - EINVAL ;
}
/*
* send_cmd_write_baud_rate
* this function sends the proper command to change the baud rate of the
* specified port .
*/
static int send_cmd_write_baud_rate ( struct moschip_port * mos7720_port ,
int baudrate )
{
struct usb_serial_port * port ;
struct usb_serial * serial ;
int divisor ;
int status ;
unsigned char data ;
unsigned char number ;
if ( mos7720_port = = NULL )
return - 1 ;
port = mos7720_port - > port ;
serial = port - > serial ;
dbg ( " %s: Entering .......... " , __FUNCTION__ ) ;
number = port - > number - port - > serial - > minor ;
dbg ( " %s - port = %d, baud = %d " , __FUNCTION__ , port - > number , baudrate ) ;
/* Calculate the Divisor */
status = calc_baud_rate_divisor ( baudrate , & divisor ) ;
if ( status ) {
err ( " %s - bad baud rate " , __FUNCTION__ ) ;
return status ;
}
/* Enable access to divisor latch */
data = mos7720_port - > shadowLCR | UART_LCR_DLAB ;
mos7720_port - > shadowLCR = data ;
send_mos_cmd ( serial , MOS_WRITE , number , UART_LCR , & data ) ;
/* Write the divisor */
data = ( ( unsigned char ) ( divisor & 0xff ) ) ;
send_mos_cmd ( serial , MOS_WRITE , number , 0x00 , & data ) ;
data = ( ( unsigned char ) ( ( divisor & 0xff00 ) > > 8 ) ) ;
send_mos_cmd ( serial , MOS_WRITE , number , 0x01 , & data ) ;
/* Disable access to divisor latch */
data = mos7720_port - > shadowLCR & ~ UART_LCR_DLAB ;
mos7720_port - > shadowLCR = data ;
send_mos_cmd ( serial , MOS_WRITE , number , 0x03 , & data ) ;
return status ;
}
/*
* change_port_settings
* This routine is called to set the UART on the device to match
* the specified new settings .
*/
static void change_port_settings ( struct moschip_port * mos7720_port ,
struct termios * old_termios )
{
struct usb_serial_port * port ;
struct usb_serial * serial ;
struct tty_struct * tty ;
int baud ;
unsigned cflag ;
unsigned iflag ;
__u8 mask = 0xff ;
__u8 lData ;
__u8 lParity ;
__u8 lStop ;
int status ;
int port_number ;
char data ;
if ( mos7720_port = = NULL )
return ;
port = mos7720_port - > port ;
serial = port - > serial ;
port_number = port - > number - port - > serial - > minor ;
dbg ( " %s - port %d " , __FUNCTION__ , port - > number ) ;
if ( ! mos7720_port - > open ) {
dbg ( " %s - port not opened " , __FUNCTION__ ) ;
return ;
}
tty = mos7720_port - > port - > tty ;
if ( ( ! tty ) | | ( ! tty - > termios ) ) {
dbg ( " %s - no tty structures " , __FUNCTION__ ) ;
return ;
}
dbg ( " %s: Entering .......... " , __FUNCTION__ ) ;
lData = UART_LCR_WLEN8 ;
lStop = 0x00 ; /* 1 stop bit */
lParity = 0x00 ; /* No parity */
cflag = tty - > termios - > c_cflag ;
iflag = tty - > termios - > c_iflag ;
/* Change the number of bits */
switch ( cflag & CSIZE ) {
case CS5 :
lData = UART_LCR_WLEN5 ;
mask = 0x1f ;
break ;
case CS6 :
lData = UART_LCR_WLEN6 ;
mask = 0x3f ;
break ;
case CS7 :
lData = UART_LCR_WLEN7 ;
mask = 0x7f ;
break ;
default :
case CS8 :
lData = UART_LCR_WLEN8 ;
break ;
}
/* Change the Parity bit */
if ( cflag & PARENB ) {
if ( cflag & PARODD ) {
lParity = UART_LCR_PARITY ;
dbg ( " %s - parity = odd " , __FUNCTION__ ) ;
} else {
lParity = ( UART_LCR_EPAR | UART_LCR_PARITY ) ;
dbg ( " %s - parity = even " , __FUNCTION__ ) ;
}
} else {
dbg ( " %s - parity = none " , __FUNCTION__ ) ;
}
if ( cflag & CMSPAR )
lParity = lParity | 0x20 ;
/* Change the Stop bit */
if ( cflag & CSTOPB ) {
lStop = UART_LCR_STOP ;
dbg ( " %s - stop bits = 2 " , __FUNCTION__ ) ;
} else {
lStop = 0x00 ;
dbg ( " %s - stop bits = 1 " , __FUNCTION__ ) ;
}
# define LCR_BITS_MASK 0x03 /* Mask for bits/char field */
# define LCR_STOP_MASK 0x04 /* Mask for stop bits field */
# define LCR_PAR_MASK 0x38 /* Mask for parity field */
/* Update the LCR with the correct value */
mos7720_port - > shadowLCR & = ~ ( LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK ) ;
mos7720_port - > shadowLCR | = ( lData | lParity | lStop ) ;
/* Disable Interrupts */
data = 0x00 ;
send_mos_cmd ( serial , MOS_WRITE , port - > number - port - > serial - > minor , UART_IER , & data ) ;
data = 0x00 ;
send_mos_cmd ( serial , MOS_WRITE , port_number , UART_FCR , & data ) ;
data = 0xcf ;
send_mos_cmd ( serial , MOS_WRITE , port_number , UART_FCR , & data ) ;
/* Send the updated LCR value to the mos7720 */
data = mos7720_port - > shadowLCR ;
send_mos_cmd ( serial , MOS_WRITE , port_number , UART_LCR , & data ) ;
data = 0x00b ;
mos7720_port - > shadowMCR = data ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x04 , & data ) ;
data = 0x00b ;
send_mos_cmd ( serial , MOS_WRITE , port_number , 0x04 , & data ) ;
/* set up the MCR register and send it to the mos7720 */
mos7720_port - > shadowMCR = UART_MCR_OUT2 ;
if ( cflag & CBAUD )
mos7720_port - > shadowMCR | = ( UART_MCR_DTR | UART_MCR_RTS ) ;
if ( cflag & CRTSCTS ) {
mos7720_port - > shadowMCR | = ( UART_MCR_XONANY ) ;
/* To set hardware flow control to the specified *
* serial port , in SP1 / 2 _CONTROL_REG */
if ( port - > number ) {
data = 0x001 ;
send_mos_cmd ( serial , MOS_WRITE , MOS_MAX_PORT ,
0x08 , & data ) ;
} else {
data = 0x002 ;
send_mos_cmd ( serial , MOS_WRITE , MOS_MAX_PORT ,
0x08 , & data ) ;
}
} else {
mos7720_port - > shadowMCR & = ~ ( UART_MCR_XONANY ) ;
}
data = mos7720_port - > shadowMCR ;
send_mos_cmd ( serial , MOS_WRITE , port_number , UART_MCR , & data ) ;
/* Determine divisor based on baud rate */
baud = tty_get_baud_rate ( tty ) ;
if ( ! baud ) {
/* pick a default, any default... */
dbg ( " Picked default baud... " ) ;
baud = 9600 ;
}
if ( baud > = 230400 ) {
set_higher_rates ( mos7720_port , baud ) ;
/* Enable Interrupts */
data = 0x0c ;
send_mos_cmd ( serial , MOS_WRITE , port_number , UART_IER , & data ) ;
return ;
}
dbg ( " %s - baud rate = %d " , __FUNCTION__ , baud ) ;
status = send_cmd_write_baud_rate ( mos7720_port , baud ) ;
/* Enable Interrupts */
data = 0x0c ;
send_mos_cmd ( serial , MOS_WRITE , port_number , UART_IER , & data ) ;
if ( port - > read_urb - > status ! = - EINPROGRESS ) {
port - > read_urb - > dev = serial - > dev ;
status = usb_submit_urb ( port - > read_urb , GFP_ATOMIC ) ;
if ( status )
dbg ( " usb_submit_urb(read bulk) failed, status = %d " ,
status ) ;
}
return ;
}
/*
* mos7720_set_termios
* this function is called by the tty driver when it wants to change the
* termios structure .
*/
static void mos7720_set_termios ( struct usb_serial_port * port ,
struct termios * old_termios )
{
int status ;
unsigned int cflag ;
struct usb_serial * serial ;
struct moschip_port * mos7720_port ;
struct tty_struct * tty ;
serial = port - > serial ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return ;
tty = port - > tty ;
if ( ! port - > tty | | ! port - > tty - > termios ) {
dbg ( " %s - no tty or termios " , __FUNCTION__ ) ;
return ;
}
if ( ! mos7720_port - > open ) {
dbg ( " %s - port not opened " , __FUNCTION__ ) ;
return ;
}
dbg ( " %s \n " , " setting termios - ASPIRE " ) ;
cflag = tty - > termios - > c_cflag ;
if ( ! cflag ) {
printk ( " %s %s \n " , __FUNCTION__ , " cflag is NULL " ) ;
return ;
}
/* check that they really want us to change something */
if ( old_termios ) {
if ( ( cflag = = old_termios - > c_cflag ) & &
( RELEVANT_IFLAG ( tty - > termios - > c_iflag ) = =
RELEVANT_IFLAG ( old_termios - > c_iflag ) ) ) {
dbg ( " Nothing to change " ) ;
return ;
}
}
dbg ( " %s - clfag %08x iflag %08x " , __FUNCTION__ ,
tty - > termios - > c_cflag ,
RELEVANT_IFLAG ( tty - > termios - > c_iflag ) ) ;
if ( old_termios )
dbg ( " %s - old clfag %08x old iflag %08x " , __FUNCTION__ ,
old_termios - > c_cflag ,
RELEVANT_IFLAG ( old_termios - > c_iflag ) ) ;
dbg ( " %s - port %d " , __FUNCTION__ , port - > number ) ;
/* change the port settings to the new ones specified */
change_port_settings ( mos7720_port , old_termios ) ;
if ( ! port - > read_urb ) {
dbg ( " %s " , " URB KILLED !!!!! \n " ) ;
return ;
}
if ( port - > read_urb - > status ! = - EINPROGRESS ) {
port - > read_urb - > dev = serial - > dev ;
status = usb_submit_urb ( port - > read_urb , GFP_ATOMIC ) ;
if ( status )
dbg ( " usb_submit_urb(read bulk) failed, status = %d " ,
status ) ;
}
return ;
}
/*
* get_lsr_info - get line status register info
*
* Purpose : Let user call ioctl ( ) to get info when the UART physically
* is emptied . On bus types like RS485 , the transmitter must
* release the bus after transmitting . This must be done when
* the transmit shift register is empty , not be done when the
* transmit holding register is empty . This functionality
* allows an RS485 driver to be written in user space .
*/
static int get_lsr_info ( struct moschip_port * mos7720_port ,
unsigned int __user * value )
{
int count ;
unsigned int result = 0 ;
count = mos7720_chars_in_buffer ( mos7720_port - > port ) ;
if ( count = = 0 ) {
dbg ( " %s -- Empty " , __FUNCTION__ ) ;
result = TIOCSER_TEMT ;
}
if ( copy_to_user ( value , & result , sizeof ( int ) ) )
return - EFAULT ;
return 0 ;
}
/*
* get_number_bytes_avail - get number of bytes available
*
* Purpose : Let user call ioctl to get the count of number of bytes available .
*/
static int get_number_bytes_avail ( struct moschip_port * mos7720_port ,
unsigned int __user * value )
{
unsigned int result = 0 ;
struct tty_struct * tty = mos7720_port - > port - > tty ;
if ( ! tty )
return - ENOIOCTLCMD ;
result = tty - > read_cnt ;
dbg ( " %s(%d) = %d " , __FUNCTION__ , mos7720_port - > port - > number , result ) ;
if ( copy_to_user ( value , & result , sizeof ( int ) ) )
return - EFAULT ;
return - ENOIOCTLCMD ;
}
static int set_modem_info ( struct moschip_port * mos7720_port , unsigned int cmd ,
unsigned int __user * value )
{
unsigned int mcr ;
unsigned int arg ;
unsigned char data ;
struct usb_serial_port * port ;
if ( mos7720_port = = NULL )
return - 1 ;
port = ( struct usb_serial_port * ) mos7720_port - > port ;
mcr = mos7720_port - > shadowMCR ;
if ( copy_from_user ( & arg , value , sizeof ( int ) ) )
return - EFAULT ;
switch ( cmd ) {
case TIOCMBIS :
if ( arg & TIOCM_RTS )
mcr | = UART_MCR_RTS ;
if ( arg & TIOCM_DTR )
mcr | = UART_MCR_RTS ;
if ( arg & TIOCM_LOOP )
mcr | = UART_MCR_LOOP ;
break ;
case TIOCMBIC :
if ( arg & TIOCM_RTS )
mcr & = ~ UART_MCR_RTS ;
if ( arg & TIOCM_DTR )
mcr & = ~ UART_MCR_RTS ;
if ( arg & TIOCM_LOOP )
mcr & = ~ UART_MCR_LOOP ;
break ;
case TIOCMSET :
/* turn off the RTS and DTR and LOOPBACK
* and then only turn on what was asked to */
mcr & = ~ ( UART_MCR_RTS | UART_MCR_DTR | UART_MCR_LOOP ) ;
mcr | = ( ( arg & TIOCM_RTS ) ? UART_MCR_RTS : 0 ) ;
mcr | = ( ( arg & TIOCM_DTR ) ? UART_MCR_DTR : 0 ) ;
mcr | = ( ( arg & TIOCM_LOOP ) ? UART_MCR_LOOP : 0 ) ;
break ;
}
mos7720_port - > shadowMCR = mcr ;
data = mos7720_port - > shadowMCR ;
send_mos_cmd ( port - > serial , MOS_WRITE ,
port - > number - port - > serial - > minor , UART_MCR , & data ) ;
return 0 ;
}
static int get_modem_info ( struct moschip_port * mos7720_port ,
unsigned int __user * value )
{
unsigned int result = 0 ;
unsigned int msr = mos7720_port - > shadowMSR ;
unsigned int mcr = mos7720_port - > shadowMCR ;
result = ( ( mcr & UART_MCR_DTR ) ? TIOCM_DTR : 0 ) /* 0x002 */
| ( ( mcr & UART_MCR_RTS ) ? TIOCM_RTS : 0 ) /* 0x004 */
| ( ( msr & UART_MSR_CTS ) ? TIOCM_CTS : 0 ) /* 0x020 */
| ( ( msr & UART_MSR_DCD ) ? TIOCM_CAR : 0 ) /* 0x040 */
| ( ( msr & UART_MSR_RI ) ? TIOCM_RI : 0 ) /* 0x080 */
| ( ( msr & UART_MSR_DSR ) ? TIOCM_DSR : 0 ) ; /* 0x100 */
dbg ( " %s -- %x " , __FUNCTION__ , result ) ;
if ( copy_to_user ( value , & result , sizeof ( int ) ) )
return - EFAULT ;
return 0 ;
}
static int get_serial_info ( struct moschip_port * mos7720_port ,
struct serial_struct __user * retinfo )
{
struct serial_struct tmp ;
if ( ! retinfo )
return - EFAULT ;
memset ( & tmp , 0 , sizeof ( tmp ) ) ;
tmp . type = PORT_16550A ;
tmp . line = mos7720_port - > port - > serial - > minor ;
tmp . port = mos7720_port - > port - > number ;
tmp . irq = 0 ;
tmp . flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ ;
tmp . xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE ;
tmp . baud_base = 9600 ;
tmp . close_delay = 5 * HZ ;
tmp . closing_wait = 30 * HZ ;
if ( copy_to_user ( retinfo , & tmp , sizeof ( * retinfo ) ) )
return - EFAULT ;
return 0 ;
}
static int mos7720_ioctl ( struct usb_serial_port * port , struct file * file ,
unsigned int cmd , unsigned long arg )
{
struct moschip_port * mos7720_port ;
struct async_icount cnow ;
struct async_icount cprev ;
struct serial_icounter_struct icount ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return - ENODEV ;
dbg ( " %s - port %d, cmd = 0x%x " , __FUNCTION__ , port - > number , cmd ) ;
switch ( cmd ) {
case TIOCINQ :
/* return number of bytes available */
dbg ( " %s (%d) TIOCINQ " , __FUNCTION__ , port - > number ) ;
return get_number_bytes_avail ( mos7720_port ,
( unsigned int __user * ) arg ) ;
break ;
case TIOCSERGETLSR :
dbg ( " %s (%d) TIOCSERGETLSR " , __FUNCTION__ , port - > number ) ;
return get_lsr_info ( mos7720_port , ( unsigned int __user * ) arg ) ;
return 0 ;
case TIOCMBIS :
case TIOCMBIC :
case TIOCMSET :
dbg ( " %s (%d) TIOCMSET/TIOCMBIC/TIOCMSET " , __FUNCTION__ ,
port - > number ) ;
return set_modem_info ( mos7720_port , cmd ,
( unsigned int __user * ) arg ) ;
case TIOCMGET :
dbg ( " %s (%d) TIOCMGET " , __FUNCTION__ , port - > number ) ;
return get_modem_info ( mos7720_port ,
( unsigned int __user * ) arg ) ;
case TIOCGSERIAL :
dbg ( " %s (%d) TIOCGSERIAL " , __FUNCTION__ , port - > number ) ;
return get_serial_info ( mos7720_port ,
( struct serial_struct __user * ) arg ) ;
case TIOCSSERIAL :
dbg ( " %s (%d) TIOCSSERIAL " , __FUNCTION__ , port - > number ) ;
break ;
case TIOCMIWAIT :
dbg ( " %s (%d) TIOCMIWAIT " , __FUNCTION__ , port - > number ) ;
cprev = mos7720_port - > icount ;
while ( 1 ) {
if ( signal_pending ( current ) )
return - ERESTARTSYS ;
cnow = mos7720_port - > icount ;
if ( cnow . rng = = cprev . rng & & cnow . dsr = = cprev . dsr & &
cnow . dcd = = cprev . dcd & & cnow . cts = = cprev . cts )
return - EIO ; /* no change => error */
if ( ( ( arg & TIOCM_RNG ) & & ( cnow . rng ! = cprev . rng ) ) | |
( ( arg & TIOCM_DSR ) & & ( cnow . dsr ! = cprev . dsr ) ) | |
( ( arg & TIOCM_CD ) & & ( cnow . dcd ! = cprev . dcd ) ) | |
( ( arg & TIOCM_CTS ) & & ( cnow . cts ! = cprev . cts ) ) ) {
return 0 ;
}
cprev = cnow ;
}
/* NOTREACHED */
break ;
case TIOCGICOUNT :
cnow = mos7720_port - > icount ;
icount . cts = cnow . cts ;
icount . dsr = cnow . dsr ;
icount . rng = cnow . rng ;
icount . dcd = cnow . dcd ;
icount . rx = cnow . rx ;
icount . tx = cnow . tx ;
icount . frame = cnow . frame ;
icount . overrun = cnow . overrun ;
icount . parity = cnow . parity ;
icount . brk = cnow . brk ;
icount . buf_overrun = cnow . buf_overrun ;
dbg ( " %s (%d) TIOCGICOUNT RX=%d, TX=%d " , __FUNCTION__ ,
port - > number , icount . rx , icount . tx ) ;
if ( copy_to_user ( ( void __user * ) arg , & icount , sizeof ( icount ) ) )
return - EFAULT ;
return 0 ;
}
return - ENOIOCTLCMD ;
}
static int mos7720_startup ( struct usb_serial * serial )
{
struct moschip_serial * mos7720_serial ;
struct moschip_port * mos7720_port ;
struct usb_device * dev ;
int i ;
char data ;
dbg ( " %s: Entering .......... " , __FUNCTION__ ) ;
if ( ! serial ) {
dbg ( " Invalid Handler " ) ;
return - ENODEV ;
}
dev = serial - > dev ;
/* create our private serial structure */
mos7720_serial = kzalloc ( sizeof ( struct moschip_serial ) , GFP_KERNEL ) ;
if ( mos7720_serial = = NULL ) {
err ( " %s - Out of memory " , __FUNCTION__ ) ;
return - ENOMEM ;
}
usb_set_serial_data ( serial , mos7720_serial ) ;
/* we set up the pointers to the endpoints in the mos7720_open *
* function , as the structures aren ' t created yet . */
/* set up port private structures */
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
mos7720_port = kzalloc ( sizeof ( struct moschip_port ) , GFP_KERNEL ) ;
if ( mos7720_port = = NULL ) {
err ( " %s - Out of memory " , __FUNCTION__ ) ;
usb_set_serial_data ( serial , NULL ) ;
kfree ( mos7720_serial ) ;
return - ENOMEM ;
}
/* Initialize all port interrupt end point to port 0 int
* endpoint . Our device has only one interrupt endpoint
* comman to all ports */
serial - > port [ i ] - > interrupt_in_endpointAddress = serial - > port [ 0 ] - > interrupt_in_endpointAddress ;
mos7720_port - > port = serial - > port [ i ] ;
usb_set_serial_port_data ( serial - > port [ i ] , mos7720_port ) ;
dbg ( " port number is %d " , serial - > port [ i ] - > number ) ;
dbg ( " serial number is %d " , serial - > minor ) ;
}
/* setting configuration feature to one */
usb_control_msg ( serial - > dev , usb_sndctrlpipe ( serial - > dev , 0 ) ,
( __u8 ) 0x03 , 0x00 , 0x01 , 0x00 , NULL , 0x00 , 5 * HZ ) ;
send_mos_cmd ( serial , MOS_READ , 0x00 , UART_LSR , & data ) ; // LSR For Port 1
dbg ( " LSR:%x " , data ) ;
send_mos_cmd ( serial , MOS_READ , 0x01 , UART_LSR , & data ) ; // LSR For Port 2
dbg ( " LSR:%x " , data ) ;
return 0 ;
}
static void mos7720_shutdown ( struct usb_serial * serial )
{
int i ;
/* free private structure allocated for serial port */
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
kfree ( usb_get_serial_port_data ( serial - > port [ i ] ) ) ;
usb_set_serial_port_data ( serial - > port [ i ] , NULL ) ;
}
/* free private structure allocated for serial device */
kfree ( usb_get_serial_data ( serial ) ) ;
usb_set_serial_data ( serial , NULL ) ;
}
static struct usb_serial_driver moschip7720_2port_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = " moschip7720 " ,
} ,
. description = " Moschip 2 port adapter " ,
. id_table = moschip_port_id_table ,
. num_interrupt_in = 1 ,
. num_bulk_in = 2 ,
. num_bulk_out = 2 ,
. num_ports = 2 ,
. open = mos7720_open ,
. close = mos7720_close ,
. throttle = mos7720_throttle ,
. unthrottle = mos7720_unthrottle ,
. attach = mos7720_startup ,
. shutdown = mos7720_shutdown ,
. ioctl = mos7720_ioctl ,
. set_termios = mos7720_set_termios ,
. write = mos7720_write ,
. write_room = mos7720_write_room ,
. chars_in_buffer = mos7720_chars_in_buffer ,
. break_ctl = mos7720_break ,
. read_bulk_callback = mos7720_bulk_in_callback ,
} ;
static struct usb_driver usb_driver = {
. name = " moschip7720 " ,
. probe = usb_serial_probe ,
. disconnect = usb_serial_disconnect ,
. id_table = moschip_port_id_table ,
} ;
static int __init moschip7720_init ( void )
{
int retval ;
dbg ( " %s: Entering .......... " , __FUNCTION__ ) ;
/* Register with the usb serial */
retval = usb_serial_register ( & moschip7720_2port_driver ) ;
if ( retval )
goto failed_port_device_register ;
info ( DRIVER_DESC " " DRIVER_VERSION ) ;
/* Register with the usb */
retval = usb_register ( & usb_driver ) ;
if ( retval )
goto failed_usb_register ;
return 0 ;
failed_usb_register :
usb_serial_deregister ( & moschip7720_2port_driver ) ;
failed_port_device_register :
return retval ;
}
static void __exit moschip7720_exit ( void )
{
usb_deregister ( & usb_driver ) ;
usb_serial_deregister ( & moschip7720_2port_driver ) ;
}
module_init ( moschip7720_init ) ;
module_exit ( moschip7720_exit ) ;
/* Module information */
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( debug , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " Debug enabled or not " ) ;