diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2011-02-22 16:14:56 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-02-22 16:14:56 -0800 |
commit | a6afd9f3e819de4795fcd356e5bfad446e4323f2 (patch) | |
tree | 3402b3981867fd4057b4eb33583b6300ae93b9a6 /drivers/tty/synclinkmp.c | |
parent | 44ed76b78e158d852f640d533b7acc08b91f2132 (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.c | 5600 |
1 files changed, 5600 insertions, 0 deletions
diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c new file mode 100644 index 00000000000..32734369447 --- /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; + |