summaryrefslogtreecommitdiffstats
path: root/drivers/tty/synclinkmp.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2011-02-22 16:14:56 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2011-02-22 16:14:56 -0800
commita6afd9f3e819de4795fcd356e5bfad446e4323f2 (patch)
tree3402b3981867fd4057b4eb33583b6300ae93b9a6 /drivers/tty/synclinkmp.c
parent44ed76b78e158d852f640d533b7acc08b91f2132 (diff)
tty: move a number of tty drivers from drivers/char/ to drivers/tty/
As planned by Arnd Bergmann, this moves the following drivers from drivers/char/ to drivers/tty/ as that's where they really belong: amiserial nozomi synclink rocket cyclades moxa mxser isicom bfin_jtag_comm Cc: Arnd Bergmann <arnd@arndb.de> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: Jiri Slaby <jslaby@suse.cz> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/tty/synclinkmp.c')
-rw-r--r--drivers/tty/synclinkmp.c5600
1 files changed, 5600 insertions, 0 deletions
diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c
new file mode 100644
index 000000000000..327343694473
--- /dev/null
+++ b/drivers/tty/synclinkmp.c
@@ -0,0 +1,5600 @@
+/*
+ * $Id: synclinkmp.c,v 4.38 2005/07/15 13:29:44 paulkf Exp $
+ *
+ * Device driver for Microgate SyncLink Multiport
+ * high speed multiprotocol serial adapter.
+ *
+ * written by Paul Fulghum for Microgate Corporation
+ * paulkf@microgate.com
+ *
+ * Microgate and SyncLink are trademarks of Microgate Corporation
+ *
+ * Derived from serial.c written by Theodore Ts'o and Linus Torvalds
+ * 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_DEVICES 12
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.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/seq_file.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/init.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 <linux/synclink.h>
+
+#if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINKMP_MODULE))
+#define SYNCLINK_GENERIC_HDLC 1
+#else
+#define SYNCLINK_GENERIC_HDLC 0
+#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>
+
+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; */
+};
+
+/* size in bytes of DMA data buffers */
+#define SCABUFSIZE 1024
+#define SCA_MEM_SIZE 0x40000
+#define SCA_BASE_SIZE 512
+#define SCA_REG_SIZE 16
+#define SCA_MAX_PORTS 4
+#define SCAMAXDESC 128
+
+#define BUFFERLISTSIZE 4096
+
+/* SCA-I style DMA buffer descriptor */
+typedef struct _SCADESC
+{
+ u16 next; /* lower l6 bits of next descriptor addr */
+ u16 buf_ptr; /* lower 16 bits of buffer addr */
+ u8 buf_base; /* upper 8 bits of buffer addr */
+ u8 pad1;
+ u16 length; /* length of buffer */
+ u8 status; /* status of buffer */
+ u8 pad2;
+} SCADESC, *PSCADESC;
+
+typedef struct _SCADESC_EX
+{
+ /* device driver bookkeeping section */
+ char *virt_addr; /* virtual address of data buffer */
+ u16 phys_entry; /* lower 16-bits of physical address of this descriptor */
+} SCADESC_EX, *PSCADESC_EX;
+
+/* 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
+
+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 _synclinkmp_info {
+ void *if_ptr; /* General purpose pointer (used by SPPP) */
+ int magic;
+ struct tty_port port;
+ int line;
+ unsigned short close_delay;
+ unsigned short closing_wait; /* time to wait before closing */
+
+ struct mgsl_icount icount;
+
+ int timeout;
+ int x_char; /* xon/xoff character */
+ u16 read_status_mask1; /* break detection (SR1 indications) */
+ u16 read_status_mask2; /* parity/framing/overun (SR2 indications) */
+ unsigned char ignore_status_mask1; /* break detection (SR1 indications) */
+ unsigned char ignore_status_mask2; /* parity/framing/overun (SR2 indications) */
+ unsigned char *tx_buf;
+ int tx_put;
+ int tx_get;
+ int tx_count;
+
+ 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 _synclinkmp_info *next_device; /* device list link */
+ struct timer_list status_timer; /* input signal status check timer */
+
+ spinlock_t lock; /* spinlock for synchronizing with ISR */
+ struct work_struct task; /* task structure for scheduling bh */
+
+ u32 max_frame_size; /* as set by device config */
+
+ u32 pending_bh;
+
+ bool bh_running; /* Protection from multiple */
+ int isr_overflow;
+ bool 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;
+
+ char *buffer_list; /* virtual address of Rx & Tx buffer lists */
+ unsigned long buffer_list_phys;
+
+ unsigned int rx_buf_count; /* count of total allocated Rx buffers */
+ SCADESC *rx_buf_list; /* list of receive buffer entries */
+ SCADESC_EX rx_buf_list_ex[SCAMAXDESC]; /* list of receive buffer entries */
+ unsigned int current_rx_buf;
+
+ unsigned int tx_buf_count; /* count of total allocated Tx buffers */
+ SCADESC *tx_buf_list; /* list of transmit buffer entries */
+ SCADESC_EX tx_buf_list_ex[SCAMAXDESC]; /* list of transmit buffer entries */
+ unsigned int last_tx_buf;
+
+ unsigned char *tmp_rx_buf;
+ unsigned int tmp_rx_buf_count;
+
+ bool rx_enabled;
+ bool rx_overflow;
+
+ bool tx_enabled;
+ bool tx_active;
+ u32 idle_mode;
+
+ unsigned char ie0_value;
+ unsigned char ie1_value;
+ unsigned char ie2_value;
+ unsigned char ctrlreg_value;
+ unsigned char old_signals;
+
+ char device_name[25]; /* device instance name */
+
+ int port_count;
+ int adapter_num;
+ int port_num;
+
+ struct _synclinkmp_info *port_array[SCA_MAX_PORTS];
+
+ unsigned int bus_type; /* expansion bus type (ISA,EISA,PCI) */
+
+ unsigned int irq_level; /* interrupt level */
+ unsigned long irq_flags;
+ bool irq_requested; /* true if IRQ requested */
+
+ MGSL_PARAMS params; /* communications parameters */
+
+ unsigned char serial_signals; /* current serial signal states */
+
+ bool irq_occurred; /* for diagnostics use */
+ unsigned int init_error; /* Initialization startup error */
+
+ u32 last_mem_alloc;
+ unsigned char* memory_base; /* shared memory address (PCI only) */
+ u32 phys_memory_base;
+ int shared_mem_requested;
+
+ unsigned char* sca_base; /* HD64570 SCA Memory address */
+ u32 phys_sca_base;
+ u32 sca_offset;
+ bool sca_base_requested;
+
+ unsigned char* lcr_base; /* local config registers (PCI only) */
+ u32 phys_lcr_base;
+ u32 lcr_offset;
+ int lcr_mem_requested;
+
+ unsigned char* statctrl_base; /* status/control register memory */
+ u32 phys_statctrl_base;
+ u32 statctrl_offset;
+ bool sca_statctrl_requested;
+
+ u32 misc_ctrl_value;
+ char flag_buf[MAX_ASYNC_BUFFER_SIZE];
+ char char_buf[MAX_ASYNC_BUFFER_SIZE];
+ bool drop_rts_on_tx_done;
+
+ struct _input_signal_events input_signal_events;
+
+ /* SPPP/Cisco HDLC device parts */
+ int netcount;
+ spinlock_t netlock;
+
+#if SYNCLINK_GENERIC_HDLC
+ struct net_device *netdev;
+#endif
+
+} SLMP_INFO;
+
+#define MGSL_MAGIC 0x5401
+
+/*
+ * define serial signal status change macros
+ */
+#define MISCSTATUS_DCD_LATCHED (SerialSignal_DCD<<8) /* indicates change in DCD */
+#define MISCSTATUS_RI_LATCHED (SerialSignal_RI<<8) /* indicates change in RI */
+#define MISCSTATUS_CTS_LATCHED (SerialSignal_CTS<<8) /* indicates change in CTS */
+#define MISCSTATUS_DSR_LATCHED (SerialSignal_DSR<<8) /* change in DSR */
+
+/* Common Register macros */
+#define LPR 0x00
+#define PABR0 0x02
+#define PABR1 0x03
+#define WCRL 0x04
+#define WCRM 0x05
+#define WCRH 0x06
+#define DPCR 0x08
+#define DMER 0x09
+#define ISR0 0x10
+#define ISR1 0x11
+#define ISR2 0x12
+#define IER0 0x14
+#define IER1 0x15
+#define IER2 0x16
+#define ITCR 0x18
+#define INTVR 0x1a
+#define IMVR 0x1c
+
+/* MSCI Register macros */
+#define TRB 0x20
+#define TRBL 0x20
+#define TRBH 0x21
+#define SR0 0x22
+#define SR1 0x23
+#define SR2 0x24
+#define SR3 0x25
+#define FST 0x26
+#define IE0 0x28
+#define IE1 0x29
+#define IE2 0x2a
+#define FIE 0x2b
+#define CMD 0x2c
+#define MD0 0x2e
+#define MD1 0x2f
+#define MD2 0x30
+#define CTL 0x31
+#define SA0 0x32
+#define SA1 0x33
+#define IDL 0x34
+#define TMC 0x35
+#define RXS 0x36
+#define TXS 0x37
+#define TRC0 0x38
+#define TRC1 0x39
+#define RRC 0x3a
+#define CST0 0x3c
+#define CST1 0x3d
+
+/* Timer Register Macros */
+#define TCNT 0x60
+#define TCNTL 0x60
+#define TCNTH 0x61
+#define TCONR 0x62
+#define TCONRL 0x62
+#define TCONRH 0x63
+#define TMCS 0x64
+#define TEPR 0x65
+
+/* DMA Controller Register macros */
+#define DARL 0x80
+#define DARH 0x81
+#define DARB 0x82
+#define BAR 0x80
+#define BARL 0x80
+#define BARH 0x81
+#define BARB 0x82
+#define SAR 0x84
+#define SARL 0x84
+#define SARH 0x85
+#define SARB 0x86
+#define CPB 0x86
+#define CDA 0x88
+#define CDAL 0x88
+#define CDAH 0x89
+#define EDA 0x8a
+#define EDAL 0x8a
+#define EDAH 0x8b
+#define BFL 0x8c
+#define BFLL 0x8c
+#define BFLH 0x8d
+#define BCR 0x8e
+#define BCRL 0x8e
+#define BCRH 0x8f
+#define DSR 0x90
+#define DMR 0x91
+#define FCT 0x93
+#define DIR 0x94
+#define DCMD 0x95
+
+/* combine with timer or DMA register address */
+#define TIMER0 0x00
+#define TIMER1 0x08
+#define TIMER2 0x10
+#define TIMER3 0x18
+#define RXDMA 0x00
+#define TXDMA 0x20
+
+/* SCA Command Codes */
+#define NOOP 0x00
+#define TXRESET 0x01
+#define TXENABLE 0x02
+#define TXDISABLE 0x03
+#define TXCRCINIT 0x04
+#define TXCRCEXCL 0x05
+#define TXEOM 0x06
+#define TXABORT 0x07
+#define MPON 0x08
+#define TXBUFCLR 0x09
+#define RXRESET 0x11
+#define RXENABLE 0x12
+#define RXDISABLE 0x13
+#define RXCRCINIT 0x14
+#define RXREJECT 0x15
+#define SEARCHMP 0x16
+#define RXCRCEXCL 0x17
+#define RXCRCCALC 0x18
+#define CHRESET 0x21
+#define HUNT 0x31
+
+/* DMA command codes */
+#define SWABORT 0x01
+#define FEICLEAR 0x02
+
+/* IE0 */
+#define TXINTE BIT7
+#define RXINTE BIT6
+#define TXRDYE BIT1
+#define RXRDYE BIT0
+
+/* IE1 & SR1 */
+#define UDRN BIT7
+#define IDLE BIT6
+#define SYNCD BIT4
+#define FLGD BIT4
+#define CCTS BIT3
+#define CDCD BIT2
+#define BRKD BIT1
+#define ABTD BIT1
+#define GAPD BIT1
+#define BRKE BIT0
+#define IDLD BIT0
+
+/* IE2 & SR2 */
+#define EOM BIT7
+#define PMP BIT6
+#define SHRT BIT6
+#define PE BIT5
+#define ABT BIT5
+#define FRME BIT4
+#define RBIT BIT4
+#define OVRN BIT3
+#define CRCE BIT2
+
+
+/*
+ * Global linked list of SyncLink devices
+ */
+static SLMP_INFO *synclinkmp_device_list = NULL;
+static int synclinkmp_adapter_count = -1;
+static int synclinkmp_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;
+
+/*
+ * Array of user specified options for ISA adapters.
+ */
+static int debug_level = 0;
+static int maxframe[MAX_DEVICES] = {0,};
+
+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);
+
+static char *driver_name = "SyncLink MultiPort driver";
+static char *driver_version = "$Revision: 4.38 $";
+
+static int synclinkmp_init_one(struct pci_dev *dev,const struct pci_device_id *ent);
+static void synclinkmp_remove_one(struct pci_dev *dev);
+
+static struct pci_device_id synclinkmp_pci_tbl[] = {
+ { PCI_VENDOR_ID_MICROGATE, PCI_DEVICE_ID_MICROGATE_SCA, PCI_ANY_ID, PCI_ANY_ID, },
+ { 0, }, /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, synclinkmp_pci_tbl);
+
+MODULE_LICENSE("GPL");
+
+static struct pci_driver synclinkmp_pci_driver = {
+ .name = "synclinkmp",
+ .id_table = synclinkmp_pci_tbl,
+ .probe = synclinkmp_init_one,
+ .remove = __devexit_p(synclinkmp_remove_one),
+};
+
+
+static struct tty_driver *serial_driver;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+
+/* tty callbacks */
+
+static int open(struct tty_struct *tty, struct file * filp);
+static void close(struct tty_struct *tty, struct file * filp);
+static void hangup(struct tty_struct *tty);
+static void set_termios(struct tty_struct *tty, struct ktermios *old_termios);
+
+static int write(struct tty_struct *tty, const unsigned char *buf, int count);
+static int put_char(struct tty_struct *tty, unsigned char ch);
+static void send_xchar(struct tty_struct *tty, char ch);
+static void wait_until_sent(struct tty_struct *tty, int timeout);
+static int write_room(struct tty_struct *tty);
+static void flush_chars(struct tty_struct *tty);
+static void flush_buffer(struct tty_struct *tty);
+static void tx_hold(struct tty_struct *tty);
+static void tx_release(struct tty_struct *tty);
+
+static int ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
+static int chars_in_buffer(struct tty_struct *tty);
+static void throttle(struct tty_struct * tty);
+static void unthrottle(struct tty_struct * tty);
+static int set_break(struct tty_struct *tty, int break_state);
+
+#if SYNCLINK_GENERIC_HDLC
+#define dev_to_port(D) (dev_to_hdlc(D)->priv)
+static void hdlcdev_tx_done(SLMP_INFO *info);
+static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size);
+static int hdlcdev_init(SLMP_INFO *info);
+static void hdlcdev_exit(SLMP_INFO *info);
+#endif
+
+/* ioctl handlers */
+
+static int get_stats(SLMP_INFO *info, struct mgsl_icount __user *user_icount);
+static int get_params(SLMP_INFO *info, MGSL_PARAMS __user *params);
+static int set_params(SLMP_INFO *info, MGSL_PARAMS __user *params);
+static int get_txidle(SLMP_INFO *info, int __user *idle_mode);
+static int set_txidle(SLMP_INFO *info, int idle_mode);
+static int tx_enable(SLMP_INFO *info, int enable);
+static int tx_abort(SLMP_INFO *info);
+static int rx_enable(SLMP_INFO *info, int enable);
+static int modem_input_wait(SLMP_INFO *info,int arg);
+static int wait_mgsl_event(SLMP_INFO *info, int __user *mask_ptr);
+static int tiocmget(struct tty_struct *tty);
+static int tiocmset(struct tty_struct *tty,
+ unsigned int set, unsigned int clear);
+static int set_break(struct tty_struct *tty, int break_state);
+
+static void add_device(SLMP_INFO *info);
+static void device_init(int adapter_num, struct pci_dev *pdev);
+static int claim_resources(SLMP_INFO *info);
+static void release_resources(SLMP_INFO *info);
+
+static int startup(SLMP_INFO *info);
+static int block_til_ready(struct tty_struct *tty, struct file * filp,SLMP_INFO *info);
+static int carrier_raised(struct tty_port *port);
+static void shutdown(SLMP_INFO *info);
+static void program_hw(SLMP_INFO *info);
+static void change_params(SLMP_INFO *info);
+
+static bool init_adapter(SLMP_INFO *info);
+static bool register_test(SLMP_INFO *info);
+static bool irq_test(SLMP_INFO *info);
+static bool loopback_test(SLMP_INFO *info);
+static int adapter_test(SLMP_INFO *info);
+static bool memory_test(SLMP_INFO *info);
+
+static void reset_adapter(SLMP_INFO *info);
+static void reset_port(SLMP_INFO *info);
+static void async_mode(SLMP_INFO *info);
+static void hdlc_mode(SLMP_INFO *info);
+
+static void rx_stop(SLMP_INFO *info);
+static void rx_start(SLMP_INFO *info);
+static void rx_reset_buffers(SLMP_INFO *info);
+static void rx_free_frame_buffers(SLMP_INFO *info, unsigned int first, unsigned int last);
+static bool rx_get_frame(SLMP_INFO *info);
+
+static void tx_start(SLMP_INFO *info);
+static void tx_stop(SLMP_INFO *info);
+static void tx_load_fifo(SLMP_INFO *info);
+static void tx_set_idle(SLMP_INFO *info);
+static void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int count);
+
+static void get_signals(SLMP_INFO *info);
+static void set_signals(SLMP_INFO *info);
+static void enable_loopback(SLMP_INFO *info, int enable);
+static void set_rate(SLMP_INFO *info, u32 data_rate);
+
+static int bh_action(SLMP_INFO *info);
+static void bh_handler(struct work_struct *work);
+static void bh_receive(SLMP_INFO *info);
+static void bh_transmit(SLMP_INFO *info);
+static void bh_status(SLMP_INFO *info);
+static void isr_timer(SLMP_INFO *info);
+static void isr_rxint(SLMP_INFO *info);
+static void isr_rxrdy(SLMP_INFO *info);
+static void isr_txint(SLMP_INFO *info);
+static void isr_txrdy(SLMP_INFO *info);
+static void isr_rxdmaok(SLMP_INFO *info);
+static void isr_rxdmaerror(SLMP_INFO *info);
+static void isr_txdmaok(SLMP_INFO *info);
+static void isr_txdmaerror(SLMP_INFO *info);
+static void isr_io_pin(SLMP_INFO *info, u16 status);
+
+static int alloc_dma_bufs(SLMP_INFO *info);
+static void free_dma_bufs(SLMP_INFO *info);
+static int alloc_buf_list(SLMP_INFO *info);
+static int alloc_frame_bufs(SLMP_INFO *info, SCADESC *list, SCADESC_EX *list_ex,int count);
+static int alloc_tmp_rx_buf(SLMP_INFO *info);
+static void free_tmp_rx_buf(SLMP_INFO *info);
+
+static void load_pci_memory(SLMP_INFO *info, char* dest, const char* src, unsigned short count);
+static void trace_block(SLMP_INFO *info, const char* data, int count, int xmit);
+static void tx_timeout(unsigned long context);
+static void status_timeout(unsigned long context);
+
+static unsigned char read_reg(SLMP_INFO *info, unsigned char addr);
+static void write_reg(SLMP_INFO *info, unsigned char addr, unsigned char val);
+static u16 read_reg16(SLMP_INFO *info, unsigned char addr);
+static void write_reg16(SLMP_INFO *info, unsigned char addr, u16 val);
+static unsigned char read_status_reg(SLMP_INFO * info);
+static void write_control_reg(SLMP_INFO * info);
+
+
+static unsigned char rx_active_fifo_level = 16; // rx request FIFO activation level in bytes
+static unsigned char tx_active_fifo_level = 16; // tx request FIFO activation level in bytes
+static unsigned char tx_negate_fifo_level = 32; // tx request FIFO negation level in bytes
+
+static u32 misc_ctrl_value = 0x007e4040;
+static u32 lcr1_brdr_value = 0x00800028;
+
+static u32 read_ahead_count = 8;
+
+/* DPCR, DMA Priority Control
+ *
+ * 07..05 Not used, must be 0
+ * 04 BRC, bus release condition: 0=all transfers complete
+ * 1=release after 1 xfer on all channels
+ * 03 CCC, channel change condition: 0=every cycle
+ * 1=after each channel completes all xfers
+ * 02..00 PR<2..0>, priority 100=round robin
+ *
+ * 00000100 = 0x00
+ */
+static unsigned char dma_priority = 0x04;
+
+// Number of bytes that can be written to shared RAM
+// in a single write operation
+static u32 sca_pci_load_interval = 64;
+
+/*
+ * 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* synclinkmp_get_text_ptr(void);
+static void* synclinkmp_get_text_ptr(void) {return synclinkmp_get_text_ptr;}
+
+static inline int sanity_check(SLMP_INFO *info,
+ char *name, const char *routine)
+{
+#ifdef SANITY_CHECK
+ static const char *badmagic =
+ "Warning: bad magic number for synclinkmp_struct (%s) in %s\n";
+ static const char *badinfo =
+ "Warning: null synclinkmp_struct for (%s) in %s\n";
+
+ if (!info) {
+ printk(badinfo, name, routine);
+ return 1;
+ }
+ if (info->magic != MGSL_MAGIC) {
+ printk(badmagic, name, routine);
+ return 1;
+ }
+#else
+ if (!info)
+ return 1;
+#endif
+ return 0;
+}
+
+/**
+ * line discipline callback wrappers
+ *
+ * The wrappers maintain line discipline references
+ * while calling into the line discipline.
+ *
+ * ldisc_receive_buf - pass receive data to line discipline
+ */
+
+static void ldisc_receive_buf(struct tty_struct *tty,
+ const __u8 *data, char *flags, int count)
+{
+ struct tty_ldisc *ld;
+ if (!tty)
+ return;
+ ld = tty_ldisc_ref(tty);
+ if (ld) {
+ if (ld->ops->receive_buf)
+ ld->ops->receive_buf(tty, data, flags, count);
+ tty_ldisc_deref(ld);
+ }
+}
+
+/* tty callbacks */
+
+/* Called when a port is opened. Init and enable port.
+ */
+static int open(struct tty_struct *tty, struct file *filp)
+{
+ SLMP_INFO *info;
+ int retval, line;
+ unsigned long flags;
+
+ line = tty->index;
+ if ((line < 0) || (line >= synclinkmp_device_count)) {
+ printk("%s(%d): open with invalid line #%d.\n",
+ __FILE__,__LINE__,line);
+ return -ENODEV;
+ }
+
+ info = synclinkmp_device_list;
+ while(info && info->line != line)
+ info = info->next_device;
+ if (sanity_check(info, tty->name, "open"))
+ return -ENODEV;
+ if ( info->init_error ) {
+ printk("%s(%d):%s device is not allocated, init error=%d\n",
+ __FILE__,__LINE__,info->device_name,info->init_error);
+ return -ENODEV;
+ }
+
+ tty->driver_data = info;
+ info->port.tty = tty;
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):%s open(), old ref count = %d\n",
+ __FILE__,__LINE__,tty->driver->name, info->port.count);
+
+ /* If port is closing, signal caller to try again */
+ if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){
+ if (info->port.flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&info->port.close_wait);
+ retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+ goto cleanup;
+ }
+
+ info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+ spin_lock_irqsave(&info->netlock, flags);
+ if (info->netcount) {
+ retval = -EBUSY;
+ spin_unlock_irqrestore(&info->netlock, flags);
+ goto cleanup;
+ }
+ info->port.count++;
+ spin_unlock_irqrestore(&info->netlock, flags);
+
+ if (info->port.count == 1) {
+ /* 1st open on this device, init hardware */
+ retval = startup(info);
+ if (retval < 0)
+ goto cleanup;
+ }
+
+ retval = block_til_ready(tty, filp, info);
+ if (retval) {
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):%s block_til_ready() returned %d\n",
+ __FILE__,__LINE__, info->device_name, retval);
+ goto cleanup;
+ }
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):%s open() success\n",
+ __FILE__,__LINE__, info->device_name);
+ retval = 0;
+
+cleanup:
+ if (retval) {
+ if (tty->count == 1)
+ info->port.tty = NULL; /* tty layer will release tty struct */
+ if(info->port.count)
+ info->port.count--;
+ }
+
+ return retval;
+}
+
+/* Called when port is closed. Wait for remaining data to be
+ * sent. Disable port and free resources.
+ */
+static void close(struct tty_struct *tty, struct file *filp)
+{
+ SLMP_INFO * info = tty->driver_data;
+
+ if (sanity_check(info, tty->name, "close"))
+ return;
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):%s close() entry, count=%d\n",
+ __FILE__,__LINE__, info->device_name, info->port.count);
+
+ if (tty_port_close_start(&info->port, tty, filp) == 0)
+ goto cleanup;
+
+ mutex_lock(&info->port.mutex);
+ if (info->port.flags & ASYNC_INITIALIZED)
+ wait_until_sent(tty, info->timeout);
+
+ flush_buffer(tty);
+ tty_ldisc_flush(tty);
+ shutdown(info);
+ mutex_unlock(&info->port.mutex);
+
+ tty_port_close_end(&info->port, tty);
+ info->port.tty = NULL;
+cleanup:
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):%s close() exit, count=%d\n", __FILE__,__LINE__,
+ tty->driver->name, info->port.count);
+}
+
+/* Called by tty_hangup() when a hangup is signaled.
+ * This is the same as closing all open descriptors for the port.
+ */
+static void hangup(struct tty_struct *tty)
+{
+ SLMP_INFO *info = tty->driver_data;
+ unsigned long flags;
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):%s hangup()\n",
+ __FILE__,__LINE__, info->device_name );
+
+ if (sanity_check(info, tty->name, "hangup"))
+ return;
+
+ mutex_lock(&info->port.mutex);
+ flush_buffer(tty);
+ shutdown(info);
+
+ spin_lock_irqsave(&info->port.lock, flags);
+ info->port.count = 0;
+ info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
+ info->port.tty = NULL;
+ spin_unlock_irqrestore(&info->port.lock, flags);
+ mutex_unlock(&info->port.mutex);
+
+ wake_up_interruptible(&info->port.open_wait);
+}
+
+/* Set new termios settings
+ */
+static void set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+ SLMP_INFO *info = tty->driver_data;
+ unsigned long flags;
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):%s set_termios()\n", __FILE__,__LINE__,
+ tty->driver->name );
+
+ change_params(info);
+
+ /* Handle transition to B0 status */
+ if (old_termios->c_cflag & CBAUD &&
+ !(tty->termios->c_cflag & CBAUD)) {
+ info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+ spin_lock_irqsave(&info->lock,flags);
+ set_signals(info);
+ spin_unlock_irqrestore(&info->lock,flags);
+ }
+
+ /* Handle transition away from B0 status */
+ if (!(old_termios->c_cflag & CBAUD) &&
+ tty->termios->c_cflag & CBAUD) {
+ info->serial_signals |= SerialSignal_DTR;
+ if (!(tty->termios->c_cflag & CRTSCTS) ||
+ !test_bit(TTY_THROTTLED, &tty->flags)) {
+ info->serial_signals |= SerialSignal_RTS;
+ }
+ spin_lock_irqsave(&info->lock,flags);
+ set_signals(info);
+ spin_unlock_irqrestore(&info->lock,flags);
+ }
+
+ /* Handle turning off CRTSCTS */
+ if (old_termios->c_cflag & CRTSCTS &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ tx_release(tty);
+ }
+}
+
+/* Send a block of data
+ *
+ * Arguments:
+ *
+ * tty pointer to tty information structure
+ * buf pointer to buffer containing send data
+ * count size of send data in bytes
+ *
+ * Return Value: number of characters written
+ */
+static int write(struct tty_struct *tty,
+ const unsigned char *buf, int count)
+{
+ int c, ret = 0;
+ SLMP_INFO *info = tty->driver_data;
+ unsigned long flags;
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):%s write() count=%d\n",
+ __FILE__,__LINE__,info->device_name,count);
+
+ if (sanity_check(info, tty->name, "write"))
+ goto cleanup;
+
+ if (!info->tx_buf)
+ goto cleanup;
+
+ if (info->params.mode == MGSL_MODE_HDLC) {
+ if (count > info->max_frame_size) {
+ ret = -EIO;
+ goto cleanup;
+ }
+ if (info->tx_active)
+ goto cleanup;
+ if (info->tx_count) {
+ /* send accumulated data from send_char() calls */
+ /* as frame and wait before accepting more data. */
+ tx_load_dma_buffer(info, info->tx_buf, info->tx_count);
+ goto start;
+ }
+ ret = info->tx_count = count;
+ tx_load_dma_buffer(info, buf, count);
+ goto start;
+ }
+
+ for (;;) {
+ c = min_t(int, count,
+ min(info->max_frame_size - info->tx_count - 1,
+ info->max_frame_size - info->tx_put));
+ if (c <= 0)
+ break;
+
+ memcpy(info->tx_buf + info->tx_put, buf, c);
+
+ spin_lock_irqsave(&info->lock,flags);
+ info->tx_put += c;
+ if (info->tx_put >= info->max_frame_size)
+ info->tx_put -= info->max_frame_size;
+ info->tx_count += c;
+ spin_unlock_irqrestore(&info->lock,flags);
+
+ buf += c;
+ count -= c;
+ ret += c;
+ }
+
+ if (info->params.mode == MGSL_MODE_HDLC) {
+ if (count) {
+ ret = info->tx_count = 0;
+ goto cleanup;
+ }
+ tx_load_dma_buffer(info, info->tx_buf, info->tx_count);
+ }
+start:
+ if (info->tx_count && !tty->stopped && !tty->hw_stopped) {
+ spin_lock_irqsave(&info->lock,flags);
+ if (!info->tx_active)
+ tx_start(info);
+ spin_unlock_irqrestore(&info->lock,flags);
+ }
+
+cleanup:
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk( "%s(%d):%s write() returning=%d\n",
+ __FILE__,__LINE__,info->device_name,ret);
+ return ret;
+}
+
+/* Add a character to the transmit buffer.
+ */
+static int put_char(struct tty_struct *tty, unsigned char ch)
+{
+ SLMP_INFO *info = tty->driver_data;
+ unsigned long flags;
+ int ret = 0;
+
+ if ( debug_level >= DEBUG_LEVEL_INFO ) {
+ printk( "%s(%d):%s put_char(%d)\n",
+ __FILE__,__LINE__,info->device_name,ch);
+ }
+
+ if (sanity_check(info, tty->name, "put_char"))
+ return 0;
+
+ if (!info->tx_buf)
+ return 0;
+
+ spin_lock_irqsave(&info->lock,flags);
+
+ if ( (info->params.mode != MGSL_MODE_HDLC) ||
+ !info->tx_active ) {
+
+ if (info->tx_count < info->max_frame_size - 1) {
+ info->tx_buf[info->tx_put++] = ch;
+ if (info->tx_put >= info->max_frame_size)
+ info->tx_put -= info->max_frame_size;
+ info->tx_count++;
+ ret = 1;
+ }
+ }
+
+ spin_unlock_irqrestore(&info->lock,flags);
+ return ret;
+}
+
+/* Send a high-priority XON/XOFF character
+ */
+static void send_xchar(struct tty_struct *tty, char ch)
+{
+ SLMP_INFO *info = tty->driver_data;
+ unsigned long flags;
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):%s send_xchar(%d)\n",
+ __FILE__,__LINE__, info->device_name, ch );
+
+ if (sanity_check(info, tty->name, "send_xchar"))
+ return;
+
+ info->x_char = ch;
+ if (ch) {
+ /* Make sure transmit interrupts are on */
+ spin_lock_irqsave(&info->lock,flags);
+ if (!info->tx_enabled)
+ tx_start(info);
+ spin_unlock_irqrestore(&info->lock,flags);
+ }
+}
+
+/* Wait until the transmitter is empty.
+ */
+static void wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ SLMP_INFO * info = tty->driver_data;
+ unsigned long orig_jiffies, char_time;
+
+ if (!info )
+ return;
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):%s wait_until_sent() entry\n",
+ __FILE__,__LINE__, info->device_name );
+
+ if (sanity_check(info, tty->name, "wait_until_sent"))
+ return;
+
+ if (!test_bit(ASYNCB_INITIALIZED, &info->port.flags))
+ goto exit;
+
+ orig_jiffies = jiffies;
+
+ /* Set check interval to 1/5 of estimated time to
+ * send a character, and make it at least 1. The check
+ * interval should also be less than the timeout.
+ * Note: use tight timings here to satisfy the NIST-PCTS.
+ */
+
+ if ( info->params.data_rate ) {
+ char_time = info->timeout/(32 * 5);
+ if (!char_time)
+ char_time++;
+ } else
+ char_time = 1;
+
+ if (timeout)
+ char_time = min_t(unsigned long, char_time, timeout);
+
+ if ( info->params.mode == MGSL_MODE_HDLC ) {
+ while (info->tx_active) {
+ msleep_interruptible(jiffies_to_msecs(char_time));
+ if (signal_pending(current))
+ break;
+ if (timeout && time_after(jiffies, orig_jiffies + timeout))
+ break;
+ }
+ } else {
+ /*
+ * TODO: determine if there is something similar to USC16C32
+ * TXSTATUS_ALL_SENT status
+ */
+ while ( info->tx_active && info->tx_enabled) {
+ msleep_interruptible(jiffies_to_msecs(char_time));
+ if (signal_pending(current))
+ break;
+ if (timeout && time_after(jiffies, orig_jiffies + timeout))
+ break;
+ }
+ }
+
+exit:
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):%s wait_until_sent() exit\n",
+ __FILE__,__LINE__, info->device_name );
+}
+
+/* Return the count of free bytes in transmit buffer
+ */
+static int write_room(struct tty_struct *tty)
+{
+ SLMP_INFO *info = tty->driver_data;
+ int ret;
+
+ if (sanity_check(info, tty->name, "write_room"))