📄 synclink_cs.c
字号:
/* * linux/drivers/char/pcmcia/synclink_cs.c * * $Id: synclink_cs.c,v 4.34 2005/09/08 13:20:54 paulkf Exp $ * * Device driver for Microgate SyncLink PC Card * multiprotocol serial adapter. * * written by Paul Fulghum for Microgate Corporation * paulkf@microgate.com * * Microgate and SyncLink are trademarks of Microgate Corporation * * This code is released under the GNU General Public License (GPL) * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq))#if defined(__i386__)# define BREAKPOINT() asm(" int $3");#else# define BREAKPOINT() { }#endif#define MAX_DEVICE_COUNT 4#include <linux/config.h> #include <linux/module.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/time.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/serial.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/netdevice.h>#include <linux/vmalloc.h>#include <linux/init.h>#include <asm/serial.h>#include <linux/delay.h>#include <linux/ioctl.h>#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/dma.h>#include <linux/bitops.h>#include <asm/types.h>#include <linux/termios.h>#include <linux/workqueue.h>#include <linux/hdlc.h>#include <pcmcia/cs_types.h>#include <pcmcia/cs.h>#include <pcmcia/cistpl.h>#include <pcmcia/cisreg.h>#include <pcmcia/ds.h>#ifdef CONFIG_HDLC_MODULE#define CONFIG_HDLC 1#endif#define GET_USER(error,value,addr) error = get_user(value,addr)#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0#define PUT_USER(error,value,addr) error = put_user(value,addr)#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0#include <asm/uaccess.h>#include "linux/synclink.h"static MGSL_PARAMS default_params = { MGSL_MODE_HDLC, /* unsigned long mode */ 0, /* unsigned char loopback; */ HDLC_FLAG_UNDERRUN_ABORT15, /* unsigned short flags; */ HDLC_ENCODING_NRZI_SPACE, /* unsigned char encoding; */ 0, /* unsigned long clock_speed; */ 0xff, /* unsigned char addr_filter; */ HDLC_CRC_16_CCITT, /* unsigned short crc_type; */ HDLC_PREAMBLE_LENGTH_8BITS, /* unsigned char preamble_length; */ HDLC_PREAMBLE_PATTERN_NONE, /* unsigned char preamble; */ 9600, /* unsigned long data_rate; */ 8, /* unsigned char data_bits; */ 1, /* unsigned char stop_bits; */ ASYNC_PARITY_NONE /* unsigned char parity; */};typedef struct{ int count; unsigned char status; char data[1];} RXBUF;/* The queue of BH actions to be performed */#define BH_RECEIVE 1#define BH_TRANSMIT 2#define BH_STATUS 4#define IO_PIN_SHUTDOWN_LIMIT 100#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))struct _input_signal_events { int ri_up; int ri_down; int dsr_up; int dsr_down; int dcd_up; int dcd_down; int cts_up; int cts_down;};/* * Device instance data structure */ typedef struct _mgslpc_info { void *if_ptr; /* General purpose pointer (used by SPPP) */ int magic; int flags; int count; /* count of opens */ int line; unsigned short close_delay; unsigned short closing_wait; /* time to wait before closing */ struct mgsl_icount icount; struct tty_struct *tty; int timeout; int x_char; /* xon/xoff character */ int blocked_open; /* # of blocked opens */ unsigned char read_status_mask; unsigned char ignore_status_mask; unsigned char *tx_buf; int tx_put; int tx_get; int tx_count; /* circular list of fixed length rx buffers */ unsigned char *rx_buf; /* memory allocated for all rx buffers */ int rx_buf_total_size; /* size of memory allocated for rx buffers */ int rx_put; /* index of next empty rx buffer */ int rx_get; /* index of next full rx buffer */ int rx_buf_size; /* size in bytes of single rx buffer */ int rx_buf_count; /* total number of rx buffers */ int rx_frame_count; /* number of full rx buffers */ wait_queue_head_t open_wait; wait_queue_head_t close_wait; wait_queue_head_t status_event_wait_q; wait_queue_head_t event_wait_q; struct timer_list tx_timer; /* HDLC transmit timeout timer */ struct _mgslpc_info *next_device; /* device list link */ unsigned short imra_value; unsigned short imrb_value; unsigned char pim_value; spinlock_t lock; struct work_struct task; /* task structure for scheduling bh */ u32 max_frame_size; u32 pending_bh; int bh_running; int bh_requested; int dcd_chkcount; /* check counts to prevent */ int cts_chkcount; /* too many IRQs if a signal */ int dsr_chkcount; /* is floating */ int ri_chkcount; int rx_enabled; int rx_overflow; int tx_enabled; int tx_active; int tx_aborting; u32 idle_mode; int if_mode; /* serial interface selection (RS-232, v.35 etc) */ char device_name[25]; /* device instance name */ unsigned int io_base; /* base I/O address of adapter */ unsigned int irq_level; MGSL_PARAMS params; /* communications parameters */ unsigned char serial_signals; /* current serial signal states */ char irq_occurred; /* for diagnostics use */ char testing_irq; unsigned int init_error; /* startup error (DIAGS) */ char flag_buf[MAX_ASYNC_BUFFER_SIZE]; BOOLEAN drop_rts_on_tx_done; struct _input_signal_events input_signal_events; /* PCMCIA support */ struct pcmcia_device *p_dev; dev_node_t node; int stop; /* SPPP/Cisco HDLC device parts */ int netcount; int dosyncppp; spinlock_t netlock;#ifdef CONFIG_HDLC struct net_device *netdev;#endif} MGSLPC_INFO;#define MGSLPC_MAGIC 0x5402/* * The size of the serial xmit buffer is 1 page, or 4096 bytes */#define TXBUFSIZE 4096 #define CHA 0x00 /* channel A offset */#define CHB 0x40 /* channel B offset *//* * FIXME: PPC has PVR defined in asm/reg.h. For now we just undef it. */#undef PVR#define RXFIFO 0#define TXFIFO 0#define STAR 0x20#define CMDR 0x20#define RSTA 0x21#define PRE 0x21#define MODE 0x22#define TIMR 0x23#define XAD1 0x24#define XAD2 0x25#define RAH1 0x26#define RAH2 0x27#define DAFO 0x27#define RAL1 0x28#define RFC 0x28#define RHCR 0x29#define RAL2 0x29#define RBCL 0x2a#define XBCL 0x2a#define RBCH 0x2b#define XBCH 0x2b#define CCR0 0x2c#define CCR1 0x2d#define CCR2 0x2e#define CCR3 0x2f#define VSTR 0x34#define BGR 0x34#define RLCR 0x35#define AML 0x36#define AMH 0x37#define GIS 0x38#define IVA 0x38#define IPC 0x39#define ISR 0x3a#define IMR 0x3a#define PVR 0x3c#define PIS 0x3d#define PIM 0x3d#define PCR 0x3e#define CCR4 0x3f // IMR/ISR #define IRQ_BREAK_ON BIT15 // rx break detected#define IRQ_DATAOVERRUN BIT14 // receive data overflow#define IRQ_ALLSENT BIT13 // all sent#define IRQ_UNDERRUN BIT12 // transmit data underrun#define IRQ_TIMER BIT11 // timer interrupt#define IRQ_CTS BIT10 // CTS status change#define IRQ_TXREPEAT BIT9 // tx message repeat#define IRQ_TXFIFO BIT8 // transmit pool ready#define IRQ_RXEOM BIT7 // receive message end#define IRQ_EXITHUNT BIT6 // receive frame start#define IRQ_RXTIME BIT6 // rx char timeout#define IRQ_DCD BIT2 // carrier detect status change#define IRQ_OVERRUN BIT1 // receive frame overflow#define IRQ_RXFIFO BIT0 // receive pool full // STAR #define XFW BIT6 // transmit FIFO write enable#define CEC BIT2 // command executing#define CTS BIT1 // CTS state #define PVR_DTR BIT0#define PVR_DSR BIT1#define PVR_RI BIT2#define PVR_AUTOCTS BIT3#define PVR_RS232 0x20 /* 0010b */#define PVR_V35 0xe0 /* 1110b */#define PVR_RS422 0x40 /* 0100b */ /* Register access functions */ #define write_reg(info, reg, val) outb((val),(info)->io_base + (reg))#define read_reg(info, reg) inb((info)->io_base + (reg))#define read_reg16(info, reg) inw((info)->io_base + (reg)) #define write_reg16(info, reg, val) outw((val), (info)->io_base + (reg)) #define set_reg_bits(info, reg, mask) \ write_reg(info, (reg), \ (unsigned char) (read_reg(info, (reg)) | (mask))) #define clear_reg_bits(info, reg, mask) \ write_reg(info, (reg), \ (unsigned char) (read_reg(info, (reg)) & ~(mask))) /* * interrupt enable/disable routines */ static void irq_disable(MGSLPC_INFO *info, unsigned char channel, unsigned short mask) { if (channel == CHA) { info->imra_value |= mask; write_reg16(info, CHA + IMR, info->imra_value); } else { info->imrb_value |= mask; write_reg16(info, CHB + IMR, info->imrb_value); }}static void irq_enable(MGSLPC_INFO *info, unsigned char channel, unsigned short mask) { if (channel == CHA) { info->imra_value &= ~mask; write_reg16(info, CHA + IMR, info->imra_value); } else { info->imrb_value &= ~mask; write_reg16(info, CHB + IMR, info->imrb_value); }}#define port_irq_disable(info, mask) \ { info->pim_value |= (mask); write_reg(info, PIM, info->pim_value); }#define port_irq_enable(info, mask) \ { info->pim_value &= ~(mask); write_reg(info, PIM, info->pim_value); }static void rx_start(MGSLPC_INFO *info);static void rx_stop(MGSLPC_INFO *info);static void tx_start(MGSLPC_INFO *info);static void tx_stop(MGSLPC_INFO *info);static void tx_set_idle(MGSLPC_INFO *info);static void get_signals(MGSLPC_INFO *info);static void set_signals(MGSLPC_INFO *info);static void reset_device(MGSLPC_INFO *info);static void hdlc_mode(MGSLPC_INFO *info);static void async_mode(MGSLPC_INFO *info);static void tx_timeout(unsigned long context);static int ioctl_common(MGSLPC_INFO *info, unsigned int cmd, unsigned long arg);#ifdef CONFIG_HDLC#define dev_to_port(D) (dev_to_hdlc(D)->priv)static void hdlcdev_tx_done(MGSLPC_INFO *info);static void hdlcdev_rx(MGSLPC_INFO *info, char *buf, int size);static int hdlcdev_init(MGSLPC_INFO *info);static void hdlcdev_exit(MGSLPC_INFO *info);#endifstatic void trace_block(MGSLPC_INFO *info,const char* data, int count, int xmit);static BOOLEAN register_test(MGSLPC_INFO *info);static BOOLEAN irq_test(MGSLPC_INFO *info);static int adapter_test(MGSLPC_INFO *info);static int claim_resources(MGSLPC_INFO *info);static void release_resources(MGSLPC_INFO *info);static void mgslpc_add_device(MGSLPC_INFO *info);static void mgslpc_remove_device(MGSLPC_INFO *info);static int rx_get_frame(MGSLPC_INFO *info);static void rx_reset_buffers(MGSLPC_INFO *info);static int rx_alloc_buffers(MGSLPC_INFO *info);static void rx_free_buffers(MGSLPC_INFO *info);static irqreturn_t mgslpc_isr(int irq, void *dev_id, struct pt_regs * regs);/* * Bottom half interrupt handlers */static void bh_handler(void* Context);static void bh_transmit(MGSLPC_INFO *info);static void bh_status(MGSLPC_INFO *info);/* * ioctl handlers */static int tiocmget(struct tty_struct *tty, struct file *file);static int tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear);static int get_stats(MGSLPC_INFO *info, struct mgsl_icount __user *user_icount);static int get_params(MGSLPC_INFO *info, MGSL_PARAMS __user *user_params);static int set_params(MGSLPC_INFO *info, MGSL_PARAMS __user *new_params);static int get_txidle(MGSLPC_INFO *info, int __user *idle_mode);static int set_txidle(MGSLPC_INFO *info, int idle_mode);static int set_txenable(MGSLPC_INFO *info, int enable);static int tx_abort(MGSLPC_INFO *info);static int set_rxenable(MGSLPC_INFO *info, int enable);static int wait_events(MGSLPC_INFO *info, int __user *mask);static MGSLPC_INFO *mgslpc_device_list = NULL;static int mgslpc_device_count = 0;/* * Set this param to non-zero to load eax with the * .text section address and breakpoint on module load. * This is useful for use with gdb and add-symbol-file command. */static int break_on_load=0;/* * Driver major number, defaults to zero to get auto * assigned major number. May be forced as module parameter. */static int ttymajor=0;static int debug_level = 0;static int maxframe[MAX_DEVICE_COUNT] = {0,};static int dosyncppp[MAX_DEVICE_COUNT] = {1,1,1,1};module_param(break_on_load, bool, 0);module_param(ttymajor, int, 0);module_param(debug_level, int, 0);module_param_array(maxframe, int, NULL, 0);module_param_array(dosyncppp, int, NULL, 0);MODULE_LICENSE("GPL");static char *driver_name = "SyncLink PC Card driver";static char *driver_version = "$Revision: 4.34 $";static struct tty_driver *serial_driver;/* number of characters left in xmit buffer before we ask for more */#define WAKEUP_CHARS 256static void mgslpc_change_params(MGSLPC_INFO *info);static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout);/* PCMCIA prototypes */static int mgslpc_config(struct pcmcia_device *link);static void mgslpc_release(u_long arg);static void mgslpc_detach(struct pcmcia_device *p_dev);/* * 1st function defined in .text section. Calling this function in * init_module() followed by a breakpoint allows a remote debugger * (gdb) to get the .text address for the add-symbol-file command. * This allows remote debugging of dynamically loadable modules. */static void* mgslpc_get_text_ptr(void){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -