Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // 2013.7
- //********************************************
- //** Copyright (C) WCH 2002-2013 ******
- //** Web: http://www.winchiphead.com ******
- //********************************************
- //** Driver for USB to serial adaptor CH34X**
- //** GCC **
- //********************************************
- // Support linux kernel version 2.6.25 and later
- //
- #include <linux/version.h>
- #ifndef KERNEL_VERSION
- #define KERNEL_VERSION(ver, rel, seq) ((ver << 16) | (rel << 8) | (seq))
- #endif
- #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/serial.h>
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/spinlock.h>
- #include <asm/uaccess.h>
- //#include <linux/uaccess.h>
- #include <linux/usb.h>
- #include <linux/usb/serial.h>
- #define DRIVER_DESC "WCH CH34x USB to serial adaptor driver"
- #define DRIVER_AUTHOR "<tech@wch.cn>"
- #define CH34x_VENDOR_ID 0x1A86
- #define CH340_PRODUCT_ID 0x7523
- #define CH341_PRODUCT_ID 0x5523
- #define CH34x_CLOSING_WAIT (30 * HZ)
- #define CH34x_BUF_SIZE 1024
- #define CH34x_TMP_BUF_SIZE 1024
- //Vendor define
- #define VENDOR_WRITE_TYPE 0x40
- #define VENDOR_READ_TYPE 0xC0
- #define VENDOR_READ 0x95
- #define VENDOR_WRITE 0x9A
- #define VENDOR_SERIAL_INIT 0xA1
- #define VENDOR_MODEM_OUT 0xA4
- #define VENDOR_VERSION 0x5F
- //For CMD 0xA4
- #define UART_CTS 0x01
- #define UART_DSR 0x02
- #define UART_RING 0x04
- #define UART_DCD 0x08
- #define CONTROL_OUT 0x10
- #define CONTROL_DTR 0x20
- #define CONTROL_RTS 0x40
- //Uart state
- #define UART_STATE 0x00
- #define UART_OVERRUN_ERROR 0x01
- #define UART_BREAK_ERROR //no define
- #define UART_PARITY_ERROR 0x02
- #define UART_FRAME_ERROR 0x06
- #define UART_RECV_ERROR 0x02
- #define UART_STATE_TRANSIENT_MASK 0x07
- //Port state
- #define PORTA_STATE 0x01
- #define PORTB_STATE 0x02
- #define PORTC_STATE 0x03
- //CH34x Baud Rate
- #define CH34x_BAUDRATE_FACTOR 1532620800
- #define CH34x_BAUDRATE_DIVMAX 3
- #define DEBUG_CH34x
- #undef DEBUG_CH34x
- #ifdef DEBUG_CH34x
- #define dbg_ch34x( format, arg... ) \
- printk( KERN_DEBUG "%d: " format "\n", __LINE__, ##arg )
- #else
- #define dbg_ch34x( format, arg... ) \
- do{ \
- if(0) \
- printk(KERN_DEBUG "%d: " format "\n", __LINE__, ##arg); \
- } while (0)
- #endif
- #ifdef DEBUG_CH34x
- #define err_ch34x( format, arg... ) \
- printk(KERN_ERR KBUILD_MODNAME ": " format "\n", ##arg)
- #else
- #define err_ch34x( format, arg... ) \
- do{ \
- if(0) \
- printk( KERN_ERR KBUILD_MODNAME ": " format "\n", ##arg)\
- }while(0)
- #endif
- // For debug
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,1))
- static int debug = 1;
- #endif
- struct ch34x_buf {
- unsigned int buf_size;
- char *buf_buf;
- char *buf_get;
- char *buf_put;
- };
- struct ch34x_private {
- spinlock_t lock; //access lock
- struct ch34x_buf *buf;
- int write_urb_in_use;
- unsigned baud_rate;
- wait_queue_head_t delta_msr_wait;
- u8 line_control;
- u8 line_status;
- u8 termios_initialized;
- };
- static struct usb_device_id id_table [] = {
- { USB_DEVICE(CH34x_VENDOR_ID, CH340_PRODUCT_ID) },
- { USB_DEVICE(CH34x_VENDOR_ID, CH341_PRODUCT_ID) },
- { } //End
- };
- MODULE_DEVICE_TABLE( usb, id_table );
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,5,2))
- static struct usb_driver ch34x_driver = {
- .name = "ch34x",
- .probe = usb_serial_probe,
- .disconnect = usb_serial_disconnect,
- .id_table = id_table,
- .suspend = usb_serial_suspend,
- .resume = usb_serial_resume,
- .no_dynamic_id = 1,
- .supports_autosuspend = 1,
- };
- #endif
- // ch34x_buf_alloc
- // Allocate a circular buffer and all associated memory
- static struct ch34x_buf *ch34x_buf_alloc( unsigned int size )
- {
- struct ch34x_buf *pb;
- if( size == 0 )
- return NULL;
- pb = kmalloc( sizeof(struct ch34x_buf), GFP_KERNEL );
- if( pb == NULL )
- return NULL;
- pb->buf_buf = kmalloc( size, GFP_KERNEL );
- if( pb->buf_buf == NULL ) {
- kfree(pb);
- return NULL;
- }
- pb->buf_size = size;
- pb->buf_get = pb->buf_put = pb->buf_buf;
- return pb;
- }
- // ch34x_buf_free
- // Free the buffer and all associated memory
- static void ch34x_buf_free( struct ch34x_buf *pb )
- {
- if( pb ) {
- kfree( pb->buf_buf );
- kfree( pb );
- }
- }
- // ch34x_buf_clear
- // Clear out all data in the circular buffer
- static void ch34x_buf_clear( struct ch34x_buf *pb )
- {
- if( pb != NULL )
- pb->buf_get = pb->buf_put;
- // equivalent to a get of all data available
- }
- // ch34x_buf_data_avail
- // Return the number of bytes of data available in he circular buffer
- static unsigned int ch34x_buf_data_avail( struct ch34x_buf *pb )
- {
- if( pb == NULL )
- return 0;
- return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size );
- }
- // ch34x_buf_space_avail
- // Return the number of bytes of space available in the circular
- static unsigned int ch34x_buf_space_avail( struct ch34x_buf *pb )
- {
- if( pb == NULL )
- return 0;
- return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size );
- }
- // ch34x_buf_put
- // Copy data from a user buffer and put it into the circular buffer.
- // Restrict to the amount of space available
- // Return the number of bytes copied
- static unsigned int ch34x_buf_put( struct ch34x_buf *pb,
- const char *buf, unsigned int count )
- {
- unsigned int len;
- if( pb == NULL )
- return 0;
- len = ch34x_buf_space_avail(pb);
- if( count > len )
- count = len;
- else if( count == 0 )
- return 0;
- len = pb->buf_buf + pb->buf_size - pb->buf_put;
- if( count > len ) {
- memcpy( pb->buf_put, buf, len );
- memcpy( pb->buf_buf, buf+len, count - len );
- pb->buf_put = pb->buf_buf + count - len;
- }
- else {
- memcpy( pb->buf_put, buf, count );
- if( count < len )
- pb->buf_put += count;
- else if( count == len )
- pb->buf_put = pb->buf_buf;
- }
- return count;
- }
- static unsigned int ch34x_buf_get( struct ch34x_buf *pb,
- char *buf, unsigned int count )
- {
- unsigned int len;
- if( pb == NULL )
- return 0;
- len = ch34x_buf_data_avail(pb);
- if( count > len )
- count = len;
- else if( count == 0 )
- return 0;
- len = pb->buf_buf + pb->buf_size - pb->buf_get;
- if( count > len ) {
- memcpy( buf, pb->buf_get, len );
- memcpy( buf+len, pb->buf_buf, count - len );
- pb->buf_get = pb->buf_buf + count - len;
- }
- else {
- memcpy( buf, pb->buf_get, count );
- if( count < len )
- pb->buf_get += count;
- else if( count == len )
- pb->buf_get = pb->buf_buf;
- }
- return count;
- }
- static int ch34x_vendor_read( __u8 request,
- __u16 value,
- __u16 index,
- struct usb_serial *serial,
- unsigned char *buf,
- __u16 len )
- {
- int retval;
- retval = usb_control_msg( serial->dev, usb_rcvctrlpipe(serial->dev, 0),
- request, VENDOR_READ_TYPE, value, index, buf, len, 1000 );
- dbg_ch34x("0x%x:0x%x:0x%x:0x%x %d - %d",
- VENDOR_READ_TYPE, request, value, index, retval, len );
- return retval;
- }
- static int ch34x_vendor_write( __u8 request,
- __u16 value,
- __u16 index,
- struct usb_serial *serial,
- unsigned char *buf,
- __u16 len )
- {
- int retval;
- retval = usb_control_msg( serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
- request,
- VENDOR_WRITE_TYPE,
- value, index, buf, len, 1000 );
- return retval;
- }
- static int set_control_lines( struct usb_serial *serial,
- u8 value )
- {
- int retval;
- retval = ch34x_vendor_write( VENDOR_MODEM_OUT, (unsigned short)value,
- 0x0000, serial, NULL, 0x00 );
- dbg_ch34x("%s - value=%d, retval=%d", __func__, value, retval );
- return retval;
- }
- static int ch34x_get_baud_rate( unsigned int baud_rate,
- unsigned char *a, unsigned char *b )
- {
- unsigned long factor = 0;
- short divisor = 0;
- if( !baud_rate )
- return -EINVAL;
- factor = (CH34x_BAUDRATE_FACTOR / baud_rate);
- divisor = CH34x_BAUDRATE_DIVMAX;
- while( (factor > 0xfff0) && divisor ) {
- factor >>= 3;
- divisor --;
- }
- if( factor > 0xfff0 )
- return -EINVAL;
- factor = 0x10000 - factor;
- *a = (factor & 0xff00) >> 8;
- *b = divisor;
- return 0;
- }
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
- static void ch34x_set_termios( struct tty_struct *tty,
- struct usb_serial_port *port, struct ktermios *old_termios )
- {
- #else
- static void ch34x_set_termios( struct usb_serial_port *port,
- struct ktermios *old_termios )
- {
- struct tty_struct *tty = port->tty;
- #endif
- struct usb_serial *serial = port->serial;
- struct ch34x_private *priv = usb_get_serial_port_data(port);
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,1)) //sure
- struct ktermios *termios = &tty->termios;
- #else
- struct ktermios *termios = tty->termios;
- #endif
- unsigned int baud_rate;
- unsigned int cflag;
- unsigned long flags;
- u8 control;
- unsigned char divisor = 0;
- unsigned char reg_count = 0;
- unsigned char factor = 0;
- unsigned char reg_value = 0;
- unsigned short value = 0;
- unsigned short index = 0;
- dbg_ch34x("%s - port:%d", __func__, port->number);
- spin_lock_irqsave( &priv->lock, flags );
- if( !priv->termios_initialized ) {
- *(termios) = tty_std_termios;
- termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- termios->c_ispeed = 9600;
- termios->c_ospeed = 9600;
- priv->termios_initialized = 1;
- }
- spin_unlock_irqrestore( &priv->lock, flags );
- /*
- * The ch34x is reported to lose bytes if you change serial setting
- * even to the same vaules as before. Thus we actually need to filter
- * in this specific case.
- */
- if( !tty_termios_hw_change(termios, old_termios) )
- return;
- cflag = termios->c_cflag;
- dbg_ch34x("%s (%d) cflag=0x%x\n", __func__, port->number, cflag );
- // Get the byte size
- switch( cflag & CSIZE )
- {
- case CS5:
- reg_value |= 0x00;
- break;
- case CS6:
- reg_value |= 0x01;
- break;
- case CS7:
- reg_value |= 0x02;
- break;
- case CS8:
- reg_value |= 0x03;
- break;
- default:
- reg_value |= 0x03;
- break;
- }
- dbg_ch34x("%s - data bits = %d", __func__, reg_value + 0x05 );
- // Figure out the stop bits
- if( cflag & CSTOPB ) {
- reg_value |= 0x04;
- dbg_ch34x("%s - stop bits = 2", __func__);
- }
- else
- dbg_ch34x("%s - stop bits = 1", __func__);
- // Determine the parity
- if( cflag & PARENB )
- if( cflag & PARODD ) {
- reg_value |= (0x08 | 0x00);
- dbg_ch34x("%s - parity = odd", __func__);
- }
- else {
- reg_value |= (0x08 | 0x10);
- dbg_ch34x("%s - parity = even", __func__);
- }
- else
- dbg_ch34x("%s - parity = none", __func__);
- // Determine the baud rate
- baud_rate = tty_get_baud_rate( tty );
- dbg_ch34x("%s = baud_rate = %d", __func__, baud_rate);
- ch34x_get_baud_rate( baud_rate, &factor, &divisor );
- dbg_ch34x("----->>>> baud_rate = %d, factor:0x%x, divisor:0x%x",
- baud_rate, factor, divisor );
- //enable SFR_UART RX and TX
- reg_value |= 0xc0;
- //enable SFR_UART Control register and timer
- reg_count |= 0x9c;
- value |= reg_count;
- value |= (unsigned short)reg_value << 8;
- index |= 0x80 | divisor;
- index |= (unsigned short)factor << 8;
- ch34x_vendor_write( VENDOR_SERIAL_INIT, value, index, serial, NULL, 0 );
- // change control lines if we are switching to or from B0
- 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, control );
- }
- else
- spin_unlock_irqrestore( &priv->lock, flags );
- if( cflag & CRTSCTS )
- ch34x_vendor_write( VENDOR_WRITE, 0x2727, 0x0101, serial, NULL, 0);
- // FIXME: Need to read back resulting baud rate
- if( baud_rate )
- tty_encode_baud_rate(tty, baud_rate, baud_rate);
- }
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,3))
- static int ch34x_tiocmget( struct tty_struct *tty )
- {
- struct usb_serial_port *port = tty->driver_data;
- #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
- static int ch34x_tiocmget( struct tty_struct *tty,
- struct file *filp )
- {
- struct usb_serial_port *port = tty->driver_data;
- #else
- static int ch34x_tiocmget( struct usb_serial_port *port,
- struct file *filp )
- {
- #endif
- struct ch34x_private *priv = usb_get_serial_port_data(port);
- unsigned long flags;
- unsigned int mcr;
- /*unsigned int msr;*/
- unsigned int retval;
- dbg_ch34x("%s - port:%d", __func__, port->number);
- if( !usb_get_intfdata( port->serial->interface) )
- return -ENODEV;
- spin_lock_irqsave( &priv->lock, flags );
- mcr = priv->line_control;
- spin_unlock_irqrestore( &priv->lock, flags );
- retval = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0) |
- ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0) |
- ((mcr & UART_CTS) ? TIOCM_CTS : 0) |
- ((mcr & UART_DSR) ? TIOCM_DSR : 0) |
- ((mcr & UART_RING) ? TIOCM_RI : 0) |
- ((mcr & UART_DCD) ? TIOCM_CD : 0);
- dbg_ch34x("%s - retval=0x%x", __func__, retval);
- return retval;
- }
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) && \
- LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) )
- static void ch34x_close( struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp )
- {
- #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) )
- static void ch34x_close( struct usb_serial_port *port )
- {
- struct tty_struct *tty = port->port.tty;
- #elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27))
- static void ch34x_close( struct usb_serial_port *port,
- struct file *filp )
- {
- struct tty_struct *tty = port->tty;
- #endif
- struct ch34x_private *priv = usb_get_serial_port_data(port);
- unsigned long flags;
- unsigned int c_cflag;
- int bps;
- long timeout;
- wait_queue_t wait;
- dbg_ch34x("%s - port:%d", __func__, port->number);
- // wait for data do drain from the buffer
- spin_lock_irqsave( &priv->lock, flags );
- timeout = CH34x_CLOSING_WAIT;
- init_waitqueue_entry( &wait, current );
- add_wait_queue( &tty->write_wait, &wait );
- for(;;) {
- set_current_state( TASK_INTERRUPTIBLE );
- if( ch34x_buf_data_avail(priv->buf) == 0 || timeout == 0 ||
- signal_pending(current) || port->serial->disconnected )
- break;
- spin_unlock_irqrestore( &priv->lock, flags );
- timeout = schedule_timeout( timeout );
- spin_lock_irqsave( &priv->lock, flags );
- }
- set_current_state( TASK_RUNNING );
- remove_wait_queue( &tty->write_wait, &wait );
- // clear out any remaining data in the buffer
- ch34x_buf_clear( priv->buf );
- spin_unlock_irqrestore( &priv->lock, flags );
- bps = tty_get_baud_rate( tty );
- if( bps > 1200 )
- timeout = max( (HZ * 2560) / bps, HZ / 10 );
- else
- timeout = 2 * HZ;
- schedule_timeout_interruptible(timeout);
- // shutdown our urbs
- usb_kill_urb(port->interrupt_in_urb);
- usb_kill_urb(port->read_urb);
- usb_kill_urb(port->write_urb);
- /*usb_serial_generic_close(port, filp);*/
- if( tty ) {
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,1)) //sure
- c_cflag = tty->termios.c_cflag;
- #else
- c_cflag = tty->termios->c_cflag;
- #endif
- if( c_cflag & HUPCL ) {
- // drop DTR and RTS
- spin_lock_irqsave( &priv->lock, flags );
- priv->line_control = 0;
- spin_unlock_irqrestore( &priv->lock, flags );
- set_control_lines( port->serial, 0 );
- }
- }
- }
- // kernel version
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) \
- && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
- static int ch34x_open( struct tty_struct *tty,
- struct usb_serial_port *port, struct file *filp )
- {
- #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
- static int ch34x_open( struct tty_struct *tty,
- struct usb_serial_port *port )
- {
- #elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27))
- static int ch34x_open( struct usb_serial_port *port,
- struct file *filp )
- {
- struct tty_struct *tty = port->tty;
- #endif
- struct ktermios tmp_termios;
- struct usb_serial *serial = port->serial;
- int retval;
- dbg_ch34x("%s - port:%d", __func__, port->number );
- usb_clear_halt( serial->dev, port->write_urb->pipe );
- usb_clear_halt( serial->dev, port->read_urb->pipe );
- if( tty ) {
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
- ch34x_set_termios( tty, port, &tmp_termios );
- #else
- ch34x_set_termios( port, &tmp_termios );
- #endif
- }
- dbg_ch34x("%s - submit read urb", __func__);
- port->read_urb->dev = serial->dev;
- retval = usb_submit_urb( port->read_urb, GFP_KERNEL );
- if(retval) {
- dev_err( &port->dev, "%s - failed submit read urb,error %d\n",
- __func__, retval );
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) && \
- LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) )
- ch34x_close(tty, port, NULL);
- #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
- ch34x_close(port);
- #elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27))
- ch34x_close(port, filp);
- #endif
- goto err_out;
- }
- dbg_ch34x("%s - submit interrupt urb", __func__ );
- port->interrupt_in_urb->dev = serial->dev;
- retval = usb_submit_urb( port->interrupt_in_urb, GFP_KERNEL );
- if(retval) {
- dev_err( &port->dev, "%s - failed submit interrupt urb,error %d\n",
- __func__, retval );
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) && \
- LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) )
- ch34x_close(tty, port, NULL);
- #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
- ch34x_close(port);
- #elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27))
- ch34x_close(port, filp);
- #endif
- goto err_out;
- }
- err_out:
- return retval;
- }
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,3))
- static int ch34x_tiocmset( struct tty_struct *tty,
- unsigned int set, unsigned int clear )
- {
- struct usb_serial_port *port = tty->driver_data;
- #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
- static int ch34x_tiocmset( struct tty_struct *tty,
- struct file *filp, unsigned int set, unsigned int clear )
- {
- struct usb_serial_port *port = tty->driver_data;
- #else
- static int ch34x_tiocmset( struct usb_serial_port *port,
- struct file *filp, unsigned int set, unsigned int clear )
- {
- #endif
- struct ch34x_private *priv = usb_get_serial_port_data(port);
- unsigned long flags;
- /*unsigned int mcr = priv->line_control;*/
- u8 control;
- dbg_ch34x("%s - port:%d", __func__, port->number);
- if( !usb_get_intfdata(port->serial->interface) )
- return -ENODEV;
- spin_lock_irqsave( &priv->lock, flags );
- if( set & TIOCM_RTS )
- priv->line_control |= CONTROL_RTS;
- if( set & TIOCM_DTR )
- priv->line_control |= CONTROL_DTR;
- if( clear & TIOCM_RTS )
- priv->line_control &= ~CONTROL_RTS;
- if( clear & TIOCM_DTR )
- priv->line_control &= ~CONTROL_DTR;
- control = priv->line_control;
- spin_unlock_irqrestore( &priv->lock, flags );
- return set_control_lines( port->serial, control );
- }
- static int wait_modem_info( struct usb_serial_port *port,
- unsigned int arg )
- {
- struct ch34x_private *priv = usb_get_serial_port_data(port);
- unsigned long flags;
- unsigned int prevstatus;
- unsigned int status;
- unsigned int changed;
- dbg_ch34x("%s -port:%d", __func__, port->number);
- spin_lock_irqsave( &priv->lock, flags );
- prevstatus = priv->line_status;
- spin_unlock_irqrestore( &priv->lock, flags );
- while(1) {
- interruptible_sleep_on( &priv->delta_msr_wait );
- // see if a signal did it
- if( signal_pending(current) )
- return -ERESTARTSYS;
- spin_lock_irqsave( &priv->lock, flags );
- status = priv->line_status;
- spin_unlock_irqrestore( &priv->lock, flags );
- changed = prevstatus ^ status;
- if( ((arg & TIOCM_RNG) && (changed & UART_RING)) ||
- ((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
- ((arg & TIOCM_CD) && (changed & UART_DCD)) ||
- ((arg & TIOCM_CTS) && (changed & UART_CTS)) )
- return 0;
- prevstatus = status;
- }
- // Not reatched
- return 0;
- }
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,3))
- static int ch34x_ioctl( struct tty_struct *tty,
- unsigned int cmd, unsigned long arg )
- {
- struct usb_serial_port *port = tty->driver_data;
- #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
- static int ch34x_ioctl( struct tty_struct *tty,
- struct file *filp, unsigned int cmd, unsigned long arg )
- {
- struct usb_serial_port *port = tty->driver_data;
- #else
- static int ch34x_ioctl( struct usb_serial_port *port,
- struct file *filp, unsigned int cmd, unsigned long arg )
- {
- //struct usb_serial_port *port = tty->driver_data;
- #endif
- dbg_ch34x("%s - port:%d, cmd=0x%04x", __func__, port->number, cmd);
- switch(cmd)
- {
- // Note here
- case TIOCMIWAIT:
- dbg_ch34x("%s - port:%d TIOCMIWAIT", __func__, port->number);
- return wait_modem_info(port, arg);
- default:
- dbg_ch34x("%s not supported=0x%04x", __func__, cmd);
- break;
- }
- return -ENOIOCTLCMD;
- }
- static void ch34x_send( struct usb_serial_port *port )
- {
- int count;
- int retval;
- struct ch34x_private *priv = usb_get_serial_port_data( port );
- unsigned long flags;
- dbg_ch34x("%s - port:%d", __func__, port->number );
- spin_lock_irqsave( &priv->lock, flags );
- if( priv->write_urb_in_use ) {
- spin_unlock_irqrestore( &priv->lock, flags );
- return;
- }
- count = ch34x_buf_get( priv->buf, port->write_urb->transfer_buffer,
- port->bulk_out_size );
- if( count == 0 ) {
- spin_unlock_irqrestore( &priv->lock, flags );
- return;
- }
- priv->write_urb_in_use = 1;
- spin_unlock_irqrestore( &priv->lock, flags );
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,1))
- usb_serial_debug_data( &port->dev, __func__, count,
- port->write_urb->transfer_buffer );
- #else
- usb_serial_debug_data( debug, &port->dev, __func__, count,
- port->write_urb->transfer_buffer );
- #endif
- port->write_urb->transfer_buffer_length = count;
- port->write_urb->dev = port->serial->dev;
- retval = usb_submit_urb( port->write_urb, GFP_ATOMIC );
- if( retval ) {
- dev_err( &port->dev, "%s - failed submitting write urb,error %d\n"
- , __func__, retval );
- priv->write_urb_in_use = 0;
- // reschedule ch34x_send
- }
- usb_serial_port_softint( port );
- }
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
- static int ch34x_write( struct tty_struct *tty,
- struct usb_serial_port *port, const unsigned char *buf, int count )
- #else
- static int ch34x_write( struct usb_serial_port *port,
- const unsigned char *buf, int count )
- #endif
- {
- struct ch34x_private *priv = usb_get_serial_port_data(port);
- unsigned long flags;
- dbg_ch34x("%s - port:%d, %d bytes", __func__, port->number, count);
- if( !count )
- return count;
- spin_lock_irqsave( &priv->lock, flags );
- count = ch34x_buf_put( priv->buf, buf, count );
- spin_unlock_irqrestore( &priv->lock, flags );
- ch34x_send(port);
- return count;
- }
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
- static int ch34x_write_room( struct tty_struct *tty )
- {
- struct usb_serial_port *port = tty->driver_data;
- #else
- static int ch34x_write_room( struct usb_serial_port *port )
- {
- #endif
- struct ch34x_private *priv = usb_get_serial_port_data( port );
- int room = 0;
- unsigned long flags;
- dbg_ch34x("%s - port:%d", __func__, port->number );
- spin_lock_irqsave( &priv->lock, flags );
- room = ch34x_buf_space_avail( priv->buf );
- spin_unlock_irqrestore( &priv->lock, flags );
- dbg_ch34x("%s - room:%d", __func__, room );
- return room;
- }
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
- static int ch34x_chars_in_buffer( struct tty_struct *tty )
- {
- struct usb_serial_port *port = tty->driver_data;
- #else
- static int ch34x_chars_in_buffer( struct usb_serial_port *port )
- {
- #endif
- struct ch34x_private *priv = usb_get_serial_port_data(port);
- int chars = 0;
- unsigned long flags;
- dbg_ch34x("%s - port:%d", __func__, port->number );
- spin_lock_irqsave( &priv->lock, flags );
- chars = ch34x_buf_data_avail( priv->buf );
- spin_unlock_irqrestore( &priv->lock, flags );
- dbg_ch34x("%s - chars:%d", __func__, chars );
- return chars;
- }
- static int ch34x_attach( struct usb_serial *serial )
- {
- struct ch34x_private *priv;
- int i;
- char buf[8];
- dbg_ch34x("%s", __func__);
- for( i = 0; i < serial->num_ports; ++i ) {
- priv = kzalloc( sizeof(struct ch34x_private), GFP_KERNEL );
- if( !priv )
- goto cleanup;
- spin_lock_init( &priv->lock );
- priv->buf = ch34x_buf_alloc( CH34x_BUF_SIZE );
- if( priv->buf == NULL ) {
- kfree( priv );
- goto cleanup;
- }
- init_waitqueue_head( &priv->delta_msr_wait );
- usb_set_serial_port_data( serial->port[i], priv );
- }
- ch34x_vendor_read( VENDOR_VERSION, 0x0000, 0x0000,
- serial, buf, 0x02 );
- ch34x_vendor_write( VENDOR_SERIAL_INIT, 0x0000, 0x0000,
- serial, NULL, 0x00 );
- ch34x_vendor_write( VENDOR_WRITE, 0x1312, 0xD982,
- serial, NULL, 0x00 );
- ch34x_vendor_write( VENDOR_WRITE, 0x0F2C, 0x0004,
- serial, NULL, 0x00 );
- ch34x_vendor_read( VENDOR_READ, 0x2518, 0x0000,
- serial, buf, 0x02 );
- ch34x_vendor_write( VENDOR_WRITE, 0x2727, 0x0000,
- serial, NULL, 0x00 );
- ch34x_vendor_write( VENDOR_MODEM_OUT, 0x009F, 0x0000,
- serial, NULL, 0x00 );
- return 0;
- cleanup:
- for( --i; i >= 0; --i ) {
- priv = usb_get_serial_port_data( serial->port[i] );
- ch34x_buf_free( priv->buf );
- kfree( priv );
- usb_set_serial_port_data( serial->port[i], NULL );
- }
- return -ENOMEM;
- }
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32))
- static void ch34x_shutdown( struct usb_serial *serial )
- {
- struct ch34x_private *priv;
- int i;
- dbg_ch34x("%s", __func__);
- for( i = 0; i < serial->num_ports; ++i ) {
- priv = usb_get_serial_port_data( serial->port[i] );
- if( priv ) {
- ch34x_buf_free( priv->buf );
- kfree( priv );
- usb_set_serial_port_data( serial->port[i], NULL );
- }
- }
- }
- #endif
- static void ch34x_update_line_status( struct usb_serial_port *port,
- unsigned char *data, unsigned int actual_length )
- {
- struct ch34x_private *priv = usb_get_serial_port_data( port );
- unsigned long flags;
- u8 length = UART_STATE + 0x04;
- if( actual_length < length )
- return;
- // Save off the uart status for others to look at
- spin_lock_irqsave( &priv->lock, flags );
- priv->line_status = data[UART_STATE];
- priv->line_control = data[PORTB_STATE];
- spin_unlock_irqrestore( &priv->lock, flags );
- wake_up_interruptible( &priv->delta_msr_wait );
- }
- static void ch34x_read_int_callback( struct urb *urb )
- {
- struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- unsigned char *data = urb->transfer_buffer;
- unsigned int actual_length = urb->actual_length;
- int status = urb->status;
- int retval;
- dbg_ch34x("%s port:%d", __func__, port->number );
- switch( status ) {
- case 0: //success
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN: //this urb is terminated, clean up
- dbg_ch34x("%s - urb shutting down with status:%d", __func__, status );
- return;
- default:
- dbg_ch34x("%s - nonzero urb status received:%d", __func__, status );
- goto exit;
- }
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,1))
- usb_serial_debug_data( &port->dev, __func__,
- urb->actual_length, urb->transfer_buffer );
- #else
- usb_serial_debug_data( debug, &port->dev,
- __func__, urb->actual_length, urb->transfer_buffer );
- #endif
- ch34x_update_line_status( port, data, actual_length );
- exit:
- retval = usb_submit_urb( urb, GFP_ATOMIC );
- if( retval )
- dev_err( &urb->dev->dev, "%s - usb_submit_urb failed with result %d\n",
- __func__, retval );
- }
- static void ch34x_read_bulk_callback( struct urb *urb )
- {
- struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct ch34x_private *priv = usb_get_serial_port_data( port );
- struct tty_struct *tty;
- unsigned char *data = urb->transfer_buffer;
- unsigned long flags;
- int i;
- int retval;
- int status = urb->status;
- u8 line_status;
- char tty_flag;
- dbg_ch34x("%s - port:%d", __func__, port->number );
- if( status ) {
- dbg_ch34x("%s - urb status=%d", __func__, status );
- if( status == -EPROTO ) {
- // CH34x mysteriously fails with -EPROTO reschedule the read
- dbg_ch34x("%s - caught -EPROTO, resubmitting the urb", __func__);
- urb->dev = port->serial->dev;
- retval = usb_submit_urb( urb, GFP_ATOMIC );
- if( retval ) {
- dev_err( &urb->dev->dev,
- "%s - failed resubmitting read urb, error %d\n",
- __func__, retval );
- return;
- }
- }
- dbg_ch34x("%s - unable to handle the error, exiting.", __func__);
- return;
- }
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,1))
- usb_serial_debug_data( &port->dev, __func__,
- urb->actual_length, data );
- #else
- usb_serial_debug_data( debug, &port->dev,
- __func__, urb->actual_length, data );
- #endif
- // get tty_flag from status
- tty_flag = TTY_NORMAL;
- spin_lock_irqsave( &priv->lock, flags );
- line_status = priv->line_status;
- priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
- spin_unlock_irqrestore( &priv->lock, flags );
- wake_up_interruptible( &priv->delta_msr_wait );
- // break takes precedence over parity,
- // which takes precedence over framing errors
- if( line_status & UART_PARITY_ERROR )
- tty_flag = TTY_PARITY;
- else if( line_status & UART_OVERRUN_ERROR )
- tty_flag = TTY_OVERRUN;
- else if( line_status & UART_FRAME_ERROR )
- tty_flag = TTY_FRAME;
- dbg_ch34x("%s - tty_flag=%d", __func__, tty_flag);
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
- tty = port->port.tty;
- #else
- tty = port->tty;
- #endif
- if( tty && urb->actual_length ) {
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,1))
- tty_buffer_request_room( tty->port, urb->actual_length + 1);
- #else
- tty_buffer_request_room( tty, urb->actual_length + 1 );
- #endif
- // overrun is special, not associated with a char
- if( line_status & UART_OVERRUN_ERROR )
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,1))
- tty_insert_flip_char( tty->port, 0, TTY_OVERRUN );
- #else
- tty_insert_flip_char( tty, 0, TTY_OVERRUN );
- #endif
- for( i = 0; i < urb->actual_length; ++i )
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,1))
- tty_insert_flip_char( tty->port, data[i], tty_flag );
- #else
- tty_insert_flip_char( tty, data[i], tty_flag );
- #endif
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,1))
- tty_flip_buffer_push( tty->port );
- #else
- tty_flip_buffer_push( tty );
- #endif
- }
- //Schedule the next read _if_ we are still open
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27))
- if( port->open_count )
- #endif
- {
- urb->dev = port->serial->dev;
- retval = usb_submit_urb( urb, GFP_ATOMIC );
- if( retval )
- dev_err( &urb->dev->dev,
- "%s - fialed resubmitting read urb, error %d\n",
- __func__, retval );
- }
- return;
- }
- static void ch34x_write_bulk_callback( struct urb *urb )
- {
- struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct ch34x_private *priv = usb_get_serial_port_data(port);
- int retval;
- int status = urb->status;
- dbg_ch34x("%s - port:%d", __func__, port->number );
- switch( status ) {
- case 0: //success
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- // this urb is terminated, clean up
- dbg_ch34x("%s - urb shutting down with status:%d", __func__, status);
- priv->write_urb_in_use = 0;
- return;
- default:
- // error in the urb, so we have to resubmit it
- dbg_ch34x("%s - Overflow in write", __func__);
- dbg_ch34x("%s - nonzero write bulk status received:%d", __func__, status);
- port->write_urb->transfer_buffer_length = 1;
- port->write_urb->dev = port->serial->dev;
- retval = usb_submit_urb(port->write_urb, GFP_ATOMIC);
- if( retval )
- dev_err( &urb->dev->dev,
- "%s - failed resubmitting write urv, error:%d\n",
- __func__, retval );
- else
- return;
- }
- priv->write_urb_in_use = 0;
- // send any buffered data
- ch34x_send(port);
- }
- static struct usb_serial_driver ch34x_device = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "ch34x",
- },
- .id_table = id_table,
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,5,1))
- .usb_driver = &ch34x_driver,
- #endif
- .num_ports = 1,
- .open = ch34x_open,
- .close = ch34x_close,
- .write = ch34x_write,
- .ioctl = ch34x_ioctl,
- .set_termios = ch34x_set_termios,
- .tiocmget = ch34x_tiocmget,
- .tiocmset = ch34x_tiocmset,
- .read_bulk_callback = ch34x_read_bulk_callback,
- .read_int_callback = ch34x_read_int_callback,
- .write_bulk_callback = ch34x_write_bulk_callback,
- .write_room = ch34x_write_room,
- .chars_in_buffer = ch34x_chars_in_buffer,
- .attach = ch34x_attach,
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) )
- .shutdown = ch34x_shutdown,
- #endif
- };
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,5))
- static struct usb_serial_driver *const serial_driver [] = {
- &ch34x_device, NULL
- };
- #endif
- static int __init ch34x_init(void)
- {
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,5))
- int retval = 0;
- retval = usb_serial_register( &ch34x_device );
- if( retval ) {
- goto err_usb_serial_register;
- }
- retval = usb_register( &ch34x_driver );
- if( retval ) {
- goto err_usb_register;
- }
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32))
- info( DRIVER_DESC );
- #endif
- return 0;
- err_usb_register:
- usb_deregister( &ch34x_driver );
- err_usb_serial_register:
- usb_serial_deregister( &ch34x_device );
- return retval;
- #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,1))
- return usb_serial_register_drivers( serial_driver,
- KBUILD_MODNAME, id_table );
- #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,5) && \
- LINUX_VERSION_CODE < KERNEL_VERSION(3,5,1))
- return usb_serial_register_drivers(&ch34x_driver, serial_driver);
- #endif
- }
- static void __exit ch34x_exit(void)
- {
- #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,5))
- usb_deregister( &ch34x_driver );
- usb_serial_deregister( &ch34x_device );
- #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,1))
- usb_serial_deregister_drivers( serial_driver );
- #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,5) && \
- LINUX_VERSION_CODE < KERNEL_VERSION(3,5,1))
- usb_serial_deregister_drivers(&ch34x_driver, serial_driver);
- #endif
- }
- module_init( ch34x_init );
- module_exit( ch34x_exit );
- MODULE_DESCRIPTION(DRIVER_DESC);
- MODULE_AUTHOR(DRIVER_AUTHOR);
- MODULE_LICENSE("GPL");
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement