📄 mos7840.c
字号:
/* * 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, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Clean ups from Moschip version and a few ioctl implementations by: * Paul B Schroeder <pschroeder "at" uplogix "dot" com> * * 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/serial.h>#include <linux/usb.h>#include <linux/usb/serial.h>#include <asm/uaccess.h>/* * Version Information */#define DRIVER_VERSION "1.3.1"#define DRIVER_DESC "Moschip 7840/7820 USB Serial Driver"/* * 16C50 UART register defines */#define LCR_BITS_5 0x00 /* 5 bits/char */#define LCR_BITS_6 0x01 /* 6 bits/char */#define LCR_BITS_7 0x02 /* 7 bits/char */#define LCR_BITS_8 0x03 /* 8 bits/char */#define LCR_BITS_MASK 0x03 /* Mask for bits/char field */#define LCR_STOP_1 0x00 /* 1 stop bit */#define LCR_STOP_1_5 0x04 /* 1.5 stop bits (if 5 bits/char) */#define LCR_STOP_2 0x04 /* 2 stop bits (if 6-8 bits/char) */#define LCR_STOP_MASK 0x04 /* Mask for stop bits field */#define LCR_PAR_NONE 0x00 /* No parity */#define LCR_PAR_ODD 0x08 /* Odd parity */#define LCR_PAR_EVEN 0x18 /* Even parity */#define LCR_PAR_MARK 0x28 /* Force parity bit to 1 */#define LCR_PAR_SPACE 0x38 /* Force parity bit to 0 */#define LCR_PAR_MASK 0x38 /* Mask for parity field */#define LCR_SET_BREAK 0x40 /* Set Break condition */#define LCR_DL_ENABLE 0x80 /* Enable access to divisor latch */#define MCR_DTR 0x01 /* Assert DTR */#define MCR_RTS 0x02 /* Assert RTS */#define MCR_OUT1 0x04 /* Loopback only: Sets state of RI */#define MCR_MASTER_IE 0x08 /* Enable interrupt outputs */#define MCR_LOOPBACK 0x10 /* Set internal (digital) loopback mode */#define MCR_XON_ANY 0x20 /* Enable any char to exit XOFF mode */#define MOS7840_MSR_CTS 0x10 /* Current state of CTS */#define MOS7840_MSR_DSR 0x20 /* Current state of DSR */#define MOS7840_MSR_RI 0x40 /* Current state of RI */#define MOS7840_MSR_CD 0x80 /* Current state of CD *//* * Defines used for sending commands to port */#define WAIT_FOR_EVER (HZ * 0 ) /* timeout urb is wait for ever */#define MOS_WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */#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/* Requests */#define MCS_RD_RTYPE 0xC0#define MCS_WR_RTYPE 0x40#define MCS_RDREQ 0x0D#define MCS_WRREQ 0x0E#define MCS_CTRL_TIMEOUT 500#define VENDOR_READ_LENGTH (0x01)#define MAX_NAME_LEN 64#define ZLP_REG1 0x3A //Zero_Flag_Reg1 58#define ZLP_REG5 0x3E //Zero_Flag_Reg5 62/* For higher baud Rates use TIOCEXBAUD */#define TIOCEXBAUD 0x5462/* vendor id and device id defines */#define USB_VENDOR_ID_MOSCHIP 0x9710#define MOSCHIP_DEVICE_ID_7840 0x7840#define MOSCHIP_DEVICE_ID_7820 0x7820/* Interrupt Rotinue Defines */#define SERIAL_IIR_RLS 0x06#define SERIAL_IIR_MS 0x00/* * Emulation of the bit mask on the LINE STATUS REGISTER. */#define SERIAL_LSR_DR 0x0001#define SERIAL_LSR_OE 0x0002#define SERIAL_LSR_PE 0x0004#define SERIAL_LSR_FE 0x0008#define SERIAL_LSR_BI 0x0010#define MOS_MSR_DELTA_CTS 0x10#define MOS_MSR_DELTA_DSR 0x20#define MOS_MSR_DELTA_RI 0x40#define MOS_MSR_DELTA_CD 0x80// Serial Port register Address#define INTERRUPT_ENABLE_REGISTER ((__u16)(0x01))#define FIFO_CONTROL_REGISTER ((__u16)(0x02))#define LINE_CONTROL_REGISTER ((__u16)(0x03))#define MODEM_CONTROL_REGISTER ((__u16)(0x04))#define LINE_STATUS_REGISTER ((__u16)(0x05))#define MODEM_STATUS_REGISTER ((__u16)(0x06))#define SCRATCH_PAD_REGISTER ((__u16)(0x07))#define DIVISOR_LATCH_LSB ((__u16)(0x00))#define DIVISOR_LATCH_MSB ((__u16)(0x01))#define CLK_MULTI_REGISTER ((__u16)(0x02))#define CLK_START_VALUE_REGISTER ((__u16)(0x03))#define SERIAL_LCR_DLAB ((__u16)(0x0080))/* * URB POOL related defines */#define NUM_URBS 16 /* URB Count */#define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */static struct usb_device_id moschip_port_id_table[] = { {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)}, {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)}, {} /* terminating entry */};static __devinitdata struct usb_device_id moschip_id_table_combined[] = { {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)}, {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)}, {} /* terminating entry */};MODULE_DEVICE_TABLE(usb, moschip_id_table_combined);/* This structure holds all of the local port information */struct moschip_port { int port_num; /*Actual port number in the device(1,2,etc) */ struct urb *write_urb; /* write URB for this port */ struct urb *read_urb; /* read URB for this port */ struct urb *int_urb; __u8 shadowLCR; /* last LCR value received */ __u8 shadowMCR; /* last MCR value received */ char open; char open_ports; char zombie; wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */ wait_queue_head_t delta_msr_wait; /* for handling sleeping while waiting for msr change to happen */ int delta_msr_cond; struct async_icount icount; struct usb_serial_port *port; /* loop back to the owner of this object */ /*Offsets */ __u8 SpRegOffset; __u8 ControlRegOffset; __u8 DcrRegOffset; //for processing control URBS in interrupt context struct urb *control_urb; struct usb_ctrlrequest *dr; char *ctrl_buf; int MsrLsr; spinlock_t pool_lock; struct urb *write_urb_pool[NUM_URBS]; char busy[NUM_URBS];};static int debug;/* * mos7840_set_reg_sync * To set the Control register by calling usb_fill_control_urb function * by passing usb_sndctrlpipe function as parameter. */static int mos7840_set_reg_sync(struct usb_serial_port *port, __u16 reg, __u16 val){ struct usb_device *dev = port->serial->dev; val = val & 0x00ff; dbg("mos7840_set_reg_sync offset is %x, value %x\n", reg, val); return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, MCS_WR_RTYPE, val, reg, NULL, 0, MOS_WDR_TIMEOUT);}/* * mos7840_get_reg_sync * To set the Uart register by calling usb_fill_control_urb function by * passing usb_rcvctrlpipe function as parameter. */static int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg, __u16 * val){ struct usb_device *dev = port->serial->dev; int ret = 0; ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, MCS_RD_RTYPE, 0, reg, val, VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT); dbg("mos7840_get_reg_sync offset is %x, return val %x\n", reg, *val); *val = (*val) & 0x00ff; return ret;}/* * mos7840_set_uart_reg * To set the Uart register by calling usb_fill_control_urb function by * passing usb_sndctrlpipe function as parameter. */static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg, __u16 val){ struct usb_device *dev = port->serial->dev; val = val & 0x00ff; // For the UART control registers, the application number need to be Or'ed if (port->serial->num_ports == 4) { val |= (((__u16) port->number - (__u16) (port->serial->minor)) + 1) << 8; dbg("mos7840_set_uart_reg application number is %x\n", val); } else { if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) { val |= (((__u16) port->number - (__u16) (port->serial->minor)) + 1) << 8; dbg("mos7840_set_uart_reg application number is %x\n", val); } else { val |= (((__u16) port->number - (__u16) (port->serial->minor)) + 2) << 8; dbg("mos7840_set_uart_reg application number is %x\n", val); } } return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, MCS_WR_RTYPE, val, reg, NULL, 0, MOS_WDR_TIMEOUT);}/* * mos7840_get_uart_reg * To set the Control register by calling usb_fill_control_urb function * by passing usb_rcvctrlpipe function as parameter. */static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg, __u16 * val){ struct usb_device *dev = port->serial->dev; int ret = 0; __u16 Wval; //dbg("application number is %4x \n",(((__u16)port->number - (__u16)(port->serial->minor))+1)<<8); /*Wval is same as application number */ if (port->serial->num_ports == 4) { Wval = (((__u16) port->number - (__u16) (port->serial->minor)) + 1) << 8; dbg("mos7840_get_uart_reg application number is %x\n", Wval); } else { if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) { Wval = (((__u16) port->number - (__u16) (port->serial->minor)) + 1) << 8; dbg("mos7840_get_uart_reg application number is %x\n", Wval); } else { Wval = (((__u16) port->number - (__u16) (port->serial->minor)) + 2) << 8; dbg("mos7840_get_uart_reg application number is %x\n", Wval); } } ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, MCS_RD_RTYPE, Wval, reg, val, VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT); *val = (*val) & 0x00ff; return ret;}static void mos7840_dump_serial_port(struct moschip_port *mos7840_port){ dbg("***************************************\n"); dbg("SpRegOffset is %2x\n", mos7840_port->SpRegOffset); dbg("ControlRegOffset is %2x \n", mos7840_port->ControlRegOffset); dbg("DCRRegOffset is %2x \n", mos7840_port->DcrRegOffset); dbg("***************************************\n");}/************************************************************************//************************************************************************//* I N T E R F A C E F U N C T I O N S *//* I N T E R F A C E F U N C T I O N S *//************************************************************************//************************************************************************/static inline void mos7840_set_port_private(struct usb_serial_port *port, struct moschip_port *data){ usb_set_serial_port_data(port, (void *)data);}static inline struct moschip_port *mos7840_get_port_private(struct usb_serial_port *port){ return (struct moschip_port *)usb_get_serial_port_data(port);}static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr){ struct moschip_port *mos7840_port; struct async_icount *icount; mos7840_port = port; icount = &mos7840_port->icount; if (new_msr & (MOS_MSR_DELTA_CTS | MOS_MSR_DELTA_DSR | MOS_MSR_DELTA_RI | MOS_MSR_DELTA_CD)) { icount = &mos7840_port->icount; /* update input line counters */ if (new_msr & MOS_MSR_DELTA_CTS) { icount->cts++; smp_wmb(); } if (new_msr & MOS_MSR_DELTA_DSR) { icount->dsr++; smp_wmb(); } if (new_msr & MOS_MSR_DELTA_CD) { icount->dcd++; smp_wmb(); } if (new_msr & MOS_MSR_DELTA_RI) { icount->rng++; smp_wmb(); } }}static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr){ struct async_icount *icount; dbg("%s - %02x", __FUNCTION__, new_lsr); if (new_lsr & SERIAL_LSR_BI) { // // Parity and Framing errors only count if they // occur exclusive of a break being // received. // new_lsr &= (__u8) (SERIAL_LSR_OE | SERIAL_LSR_BI); } /* update input line counters */ icount = &port->icount; if (new_lsr & SERIAL_LSR_BI) { icount->brk++; smp_wmb(); } if (new_lsr & SERIAL_LSR_OE) { icount->overrun++; smp_wmb(); } if (new_lsr & SERIAL_LSR_PE) { icount->parity++; smp_wmb(); } if (new_lsr & SERIAL_LSR_FE) { icount->frame++; smp_wmb(); }}/************************************************************************//************************************************************************//* U S B C A L L B A C K F U N C T I O N S *//* U S B C A L L B A C K F U N C T I O N S *//************************************************************************//************************************************************************/static void mos7840_control_callback(struct urb *urb){ unsigned char *data; struct moschip_port *mos7840_port; __u8 regval = 0x0; int result = 0; int status = urb->status; mos7840_port = (struct moschip_port *)urb->context; switch (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__, status); return; default: dbg("%s - nonzero urb status received: %d", __FUNCTION__, status); goto exit; } dbg("%s urb buffer size is %d\n", __FUNCTION__, urb->actual_length); dbg("%s mos7840_port->MsrLsr is %d port %d\n", __FUNCTION__, mos7840_port->MsrLsr, mos7840_port->port_num); data = urb->transfer_buffer; regval = (__u8) data[0]; dbg("%s data is %x\n", __FUNCTION__, regval); if (mos7840_port->MsrLsr == 0) mos7840_handle_new_msr(mos7840_port, regval); else if (mos7840_port->MsrLsr == 1) mos7840_handle_new_lsr(mos7840_port, regval);exit: spin_lock(&mos7840_port->pool_lock); if (!mos7840_port->zombie) result = usb_submit_urb(mos7840_port->int_urb, GFP_ATOMIC); spin_unlock(&mos7840_port->pool_lock); if (result) { dev_err(&urb->dev->dev, "%s - Error %d submitting interrupt urb\n", __FUNCTION__, result); }}static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg, __u16 * val)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -