[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <E603DC592C92B54A89CEF6B0919A0B1CAAAA787DA5@SOLO.hq.farsitecommunications.com>
Date: Wed, 18 Sep 2013 11:12:25 +0100
From: Kevin Curtis <Kevin.Curtis@...site.com>
To: "netdev@...r.kernel.org" <netdev@...r.kernel.org>
CC: "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
"kernel-janitors@...r.kernel.org" <kernel-janitors@...r.kernel.org>,
Dermot Smith <dermot.smith@...site.com>
Subject: [PATCH 006/007] WAN Drivers: Update farsync driver and introduce
fsflex driver
Farsite Communications FarSync driver update
Patch 6 of 7
Introduce the fsflex driver.
This driver is functionally equivalent to the farsync driver, and so
can be used with the Generic HDLC, and ppp daemon.
Signed-off-by: Kevin Curtis <kevin.curtis@...site.com>
---
diff -uprN -X linux-3.10.1/Documentation/dontdiff linux-3.10.1/drivers/net/wan/fsflex.c linux-3.10.1_new/drivers/net/wan/fsflex.c
--- linux-3.10.1/drivers/net/wan/fsflex.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-3.10.1_new/drivers/net/wan/fsflex.c 2013-09-16 16:30:06.651104873 +0100
@@ -0,0 +1,5693 @@
+/*
+ * FarSync Flex driver for Linux (2.6.x kernel version)
+ *
+ * Copyright (C) 2001-2013 FarSite Communications Ltd.
+ * www.farsite.com
+ *
+ * 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.
+ *
+ * Author: Kevin Curtis <kevin.curtis@...site.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/poll.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/init.h>
+#include <linux/if_arp.h>
+#include <linux/uaccess.h>
+#include <linux/if.h>
+#include <linux/interrupt.h>
+#include <linux/hdlc.h>
+#include <linux/io.h>
+#include <linux/usb.h>
+#include <linux/delay.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+/* TTY includes */
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <linux/scatterlist.h>
+#define FST_BUILD_NO "-k219"
+#include "farsync.h"
+#include "uss_cmn.h"
+
+/* Module info */
+MODULE_AUTHOR("K. P. Curtis <kevin.curtis@...site.com>");
+MODULE_DESCRIPTION("FarSync Flex WAN driver. FarSite Communications Ltd.");
+MODULE_LICENSE("GPL");
+
+/* Driver configuration and global parameters
+ * ==========================================
+ */
+
+#define FST_PORT_NAME "hdlc"
+#define FST_DRIVER_TYPE "WAN"
+
+/* Number of ports (per card) and cards supported
+ */
+#define FST_MAX_PORTS 1
+#define FST_MAX_CARDS 32
+#define FST_MAX_USB_BUFFER ((32*1024)+8)
+
+/* Enable this #define if you want an oem driver without hooks into syncppp
+ * This means the driver can be loaded without the presence of the
+ * syncppp module
+ */
+
+/* Modules parameters and associated varaibles
+ */
+#define FST_HIGH_WATER_MARK 12 /* Point at which we flow control
+ * network layer
+ */
+#define FST_LOW_WATER_MARK 8 /* Point at which we remove flow
+ * control from network layer
+ */
+#define MAX_URBS_OUTSTANDING 1
+#define FST_MIN_DMA_LEN 64 /* do DMA on transfers > 64 bytes */
+
+#define FIRST_FLEX_V2_MINOR_ID 3 /* A Minor id of 3 means flex V2 */
+
+int fst_txq_low = FST_LOW_WATER_MARK;
+int fst_txq_high = FST_HIGH_WATER_MARK;
+int fst_min_dma_len = FST_MIN_DMA_LEN;
+int fst_dmathr = 0xdd00dd00;
+int fst_iocinfo_version = FST_VERSION_CURRENT;
+int fst_max_reads = 7;
+int fst_excluded_cards;
+int fst_excluded_list[FST_MAX_CARDS];
+int fst_num_serials;
+char *fst_serials[FST_MAX_CARDS];
+
+module_param(fst_txq_low, int, S_IRUGO);
+module_param(fst_txq_high, int, S_IRUGO);
+module_param(fst_min_dma_len, int, S_IRUGO);
+module_param(fst_dmathr, int, S_IRUGO);
+module_param(fst_iocinfo_version, int, S_IRUGO);
+module_param(fst_max_reads, int, S_IRUGO);
+module_param(fst_excluded_cards, int, S_IRUGO);
+module_param_array(fst_excluded_list, int, NULL, S_IRUGO);
+module_param_array(fst_serials, charp, &fst_num_serials, S_IRUGO);
+
+/* Default parameters for the link
+ */
+#define FST_TX_QUEUE_LEN 100 /* At 8Mbps a longer queue length is
+ * useful, the syncppp module forces
+ * this down assuming a slower line I
+ * guess.
+ */
+#define FST_TXQ_DEPTH 16 /* This one is for the buffering
+ * of frames on the way down to the
+ * card so that we can keep the card
+ * busy and maximise throughput
+ */
+#define FST_MAX_MTU (32*1024) /* Limitation of hdlc controller */
+#define FST_DEF_MTU 1500 /* Common sane value */
+#define FST_MAX_ATM_MTU (32*53) /* 1500 bytes should fit in 32 cells */
+#define MAX_PPP_HEADER 10 /* Allowance for PPP overhead when
+ * allocating skbs or checking lengths
+ */
+
+#define FST_TX_TIMEOUT (10*HZ)
+#define FST_MAX_SEGMENTS (FST_MAX_MTU/128) /* Maximum tx/rx desriptors per
+ * frame
+ */
+
+#ifdef ARPHRD_RAWHDLC
+#define ARPHRD_MYTYPE ARPHRD_RAWHDLC /* Raw frames */
+#else
+#define ARPHRD_MYTYPE ARPHRD_HDLC /* Cisco-HDLC (keepalives etc) */
+#endif
+
+/* Mailbox values. (portMailbox) */
+#define NOP 0 /* No operation */
+#define ACK 1 /* Positive acknowledgement to PC driver */
+#define NAK 2 /* Negative acknowledgement to PC driver */
+#define STARTPORT 3 /* Start an HDLC port */
+#define STOPPORT 4 /* Stop an HDLC port */
+#define ABORTTX 5 /* Abort the transmitter for a port */
+#define SETV24O 6 /* Set V24 outputs */
+#define ASSERTSIGNALS 7
+#define RELEASESIGNALS 8
+#define SETRXCONFIG 9
+#define SETTXCONFIG 10
+#define GETSIGNALS 11
+#define SETSIGNALS SETV24O
+#define RECONFIG 12
+#define SETPRESERVE 13
+#define GETPRESERVE 14
+#define FLUSHBUFFERS 15
+#define SETXON 16
+#define SETXOFF 17
+#define GETPORTSTATS 18
+#define RESETPORTSTATS 19
+#define SETLINESPEED 20
+
+#define FST_MEMSIZE 0x100000 /* Size of card memory (1Mb) */
+
+#define REQUIRED_TX_BUFFERS 8
+#define REQUIRED_RX_BUFFERS 8
+#define REQUIRED_TX_BUFFER_SIZE (8*1024)
+#define REQUIRED_RX_BUFFER_SIZE (8*1024)
+
+/* Rx and Tx status byte bit values
+ */
+
+#define RX_FRAM 0x80 /* Rx: framing error */
+#define RX_OFLO 0x40 /* Rx: overflow error */
+#define RX_CRC 0x20 /* Rx: CRC error */
+#define RX_ABRT 0x10 /* Rx: buffer error */
+#define TX_URUN 0x40 /* Tx: Tx underrun */
+#define TX_ABRT 0x10 /* Tx: Tx abort */
+
+#define usb_buffer_alloc usb_alloc_coherent
+#define usb_buffer_free usb_free_coherent
+
+typedef struct fst_fifo {
+ u16 fifo_length;
+ u16 read_idx;
+ u16 write_idx;
+ short overflow_idx;
+ u8 data[4];
+} FIFO, *PFIFO;
+/* Device driver private information
+ * =================================
+ */
+
+/* Something to count interrupts
+ */
+struct fst_ints {
+ unsigned long int_186;
+ unsigned long int_txdma;
+ unsigned long int_rxdma;
+ unsigned long int_no_work;
+};
+
+struct fst_ints fst_int_counter[FST_MAX_CARDS];
+
+struct fst_tx_buckets {
+ unsigned long txb0;
+ unsigned long txb1;
+ unsigned long txb2;
+ unsigned long txb4;
+ unsigned long txb8;
+};
+
+struct fst_rx_buckets {
+ unsigned long rxb0;
+ unsigned long rxb1;
+ unsigned long rxb2;
+ unsigned long rxb4;
+ unsigned long rxb8;
+};
+
+struct fst_tx_buckets tx_buckets[FST_MAX_CARDS];
+struct fst_rx_buckets rx_buckets[FST_MAX_CARDS];
+
+/* A structure to queue received frame at the new char interface
+ */
+struct fst_char_rx_frame {
+ struct sk_buff *frame;
+ struct fst_char_rx_frame *next;
+ struct fst_char_rx_frame *prev;
+};
+
+/* A multi-part tx/rx queue structure
+ * For long frame support a frame can be upto 32*1024 - 2 bytes in length
+ * This would occupy 4 tx/rx desriptors (each of 8k)
+ */
+struct fst_queue_info {
+ int count; /* Total size of frame */
+ int segment_cnt; /* Number of descriptors required/used */
+ int current_seg; /* Which segment we are currently doing */
+ unsigned char flags; /* Start and End frame flags */
+ unsigned char error_recovery;
+ struct sk_buff *frame; /* The complete skb */
+};
+
+/* Per port (line or channel) information
+ */
+struct fst_port_info {
+ struct net_device *dev; /* device struct - must be first */
+ struct fst_card_info *card; /* Card we're associated with */
+ int index; /* Port index on the card */
+ int mode; /* Are we transparent or not */
+ int proto; /* Protocol we are running */
+ int hwif; /* Line hardware (line_interface copy) */
+ int v24IpSts;
+ int v24OpSts;
+ int run; /* Port is running */
+ int ignore_carrier; /* Allow tx regardless of carrier state */
+ int hdlc_proto; /* The proto as hdlc understands it */
+ int num_tx_buffers; /* No of tx buffers in card window */
+ int num_rx_buffers; /* No of rx buffers in card window */
+ int tx_buffer_size; /* Size of tx buffers in card window */
+ int rx_buffer_size; /* Size of rx buffers in card window */
+ int transmit_msb_first; /* Transmit MSB First */
+ int receive_msb_first; /* Receive MSB First */
+ int rxpos; /* Next Rx buffer to use */
+ int txpos; /* Next Tx buffer to use */
+ int txipos; /* Next Tx buffer to check for free */
+ int start; /* Indication of start/stop to network */
+ int notify_mode; /* Application has selected the notify event */
+ int monitor_mode; /* Application has selected monitor */
+ unsigned int sequence; /* Monitor sequence no */
+ int port_mode; /* DSL normal or active */
+ /*
+ * A sixteen entry transmit queue
+ */
+ int txqs; /* index to get next buffer to tx */
+ int txqe; /* index to queue next packet */
+ struct fst_queue_info txq[FST_TXQ_DEPTH]; /* The sent segments info */
+ struct fst_queue_info rxq; /* The received segments info */
+ struct file *char_file;
+ int char_opens;
+ spinlock_t rxf_lock; /* Lock for queue head access */
+ struct fst_char_rx_frame *char_inq;
+ struct fst_char_rx_frame *char_inq_end;
+ unsigned char char_inq_max_len; /* Only allow this many frames */
+ unsigned char char_inq_threshold; /* Generate a notify if */
+ wait_queue_head_t pollq; /* poll() support */
+ wait_queue_head_t readq; /* Blocking read support (char) */
+ wait_queue_head_t writeq; /* Blocking write support (cahr) */
+ FLEX_SERCONF usb_config;
+ FLEX_CARD_INFO usb_card_info;
+ int minor_dev_no;
+ char low_latency;
+ char fstioc_info_ver;
+ char readv_mode;
+ unsigned char flow_controlled;
+ unsigned char exception;
+ struct fst_fifo *fifo_rxdata;
+ struct fst_fifo fifo_txdata;
+ char compat;
+ unsigned int features;
+};
+
+/* Per card information
+ */
+struct fst_card_info {
+ char *mem; /* Card memory mapped to kernel space */
+ char *ctlmem; /* Control memory for PCI cards */
+ unsigned int phys_mem; /* Physical memory window address */
+ unsigned int phys_ctlmem; /* Physical control memory address */
+ unsigned int irq; /* Interrupt request line number */
+ unsigned int nports; /* Number of serial ports */
+ unsigned int type; /* Type index of card */
+ unsigned int state; /* State of card */
+ spinlock_t card_lock; /* Lock for SMP access */
+ spinlock_t fifo_lock;
+ wait_queue_head_t fifo_waitq; /* A queue for a fifo response */
+ int fifo_complete;
+ unsigned short pci_conf; /* PCI card config in I/O space */
+ /* Per port info */
+ struct fst_port_info *ports[FST_MAX_PORTS];
+ struct usb_device *udev; /* usb device */
+ struct usb_interface *interface;/* usb interface */
+ unsigned char *bulk_in_buffer; /* buffer to receive data */
+ size_t bulk_in_size; /* size of receive buffer */
+ unsigned char *bulk_out_buffer; /* buffer to xmit data */
+ size_t bulk_out_size; /* size of xmit buffer */
+ __u8 bulk_in_endpoint_addr; /* addr of bulk in endpoint */
+ __u8 bulk_out_endpoint_addr; /* addr of bulk out endpoint */
+ unsigned char *int_in_buffer; /* buffer to receive data */
+ size_t int_in_size; /* size of receive buffer */
+ int actual_length;
+ __u8 int_in_endpoint_addr; /* addr of bulk in endpoint */
+ __u8 int_out_endpoint_addr; /* addr of bulk out endpoint */
+ __u8 int_interval;
+ __u8 tx_urb_cnt; /* Outstanding Tx urbs allowed */
+ struct semaphore limit_sem; /* limiting # of writes in progress */
+ struct urb *int_urb;
+ struct urb *bulk_urb;
+ struct urb *tx_bulk_urb;
+ char usb_trace_buffer[128];
+ int usb_trace_ptr;
+ int card_no; /* Inst of the card on the system */
+ int family; /* TxP or TxU */
+ int dmarx_in_progress;
+ int dmatx_in_progress;
+ unsigned long int_count;
+ unsigned long int_time_ave;
+ void *rx_dma_handle_host;
+ dma_addr_t rx_dma_handle_card;
+ void *tx_dma_handle_host;
+ dma_addr_t tx_dma_handle_card;
+ struct sk_buff *dma_skb_rx;
+ struct fst_port_info *dma_port_rx;
+ struct fst_port_info *dma_port_tx;
+ int dma_len_rx;
+ int dma_len_tx;
+ int dma_txpos;
+ int dma_rxpos;
+ int dma_tx_flags;
+ int last_tx_port;
+ int card_mode; /* What we think the identify mode state is */
+ struct device *sync_cdev;
+};
+
+/* Debug support */
+#if FST_DEBUG
+
+static int fst_debug_mask = { FST_DEBUG };
+
+/* Most common debug activity is to print something if the corresponding bit
+ * is set in the debug mask. Note: this uses a non-ANSI extension in GCC to
+ * support variable numbers of macro parameters. The inverted if prevents us
+ * eating someone else's else clause.
+ */
+#define fst_dbg(F, fmt, args...) \
+do { \
+ if (fst_debug_mask & (F)) \
+ printk(KERN_DEBUG pr_fmt(fmt), ##args); \
+} while (0)
+#else
+#define fst_dbg(F, fmt, args...) \
+do { \
+ if (0) \
+ printk(KERN_DEBUG pr_fmt(fmt), ##args); \
+} while (0)
+#endif
+
+/* Device Driver Work Queues
+ *
+ * So that we don't spend too much time processing events in the
+ * Interrupt Service routine, we will declare a work queue per Card
+ * and make the ISR schedule a task in the queue for later execution.
+ */
+
+static void do_bottom_half_tx(struct fst_card_info *card);
+static void do_bottom_half_rx(struct fst_card_info *card);
+static void fst_process_tx_work_q(unsigned long work_q);
+static void fst_process_int_work_q(unsigned long work_q);
+
+static void fst_process_threshold_work_q(struct work_struct *work);
+static void fst_process_port_stats_work_q(struct work_struct *work);
+static void fst_process_rx_status(int rx_status, char *name);
+static int gen_mon_packet(struct sk_buff *skb, struct fst_port_info *port,
+ int tx_rx_ind);
+static void fst_q_work_item(u64 *queue, int card_index);
+static void fst_issue_cmd(struct fst_port_info *port, unsigned short cmd);
+
+DECLARE_TASKLET(fst_tx_task, fst_process_tx_work_q, 0);
+DECLARE_TASKLET(fst_int_task, fst_process_int_work_q, 0);
+
+static void fst_process_tty_work(struct work_struct *work);
+static void fst_process_async_tty_work(struct work_struct *work);
+
+static struct work_struct thresh_work_q;
+static struct work_struct port_stats_work_q;
+
+unsigned int fst_ncards;
+struct fst_card_info *fst_cards_list[FST_MAX_CARDS];
+struct fst_port_info *fst_ports_list[FST_MAX_CARDS * FST_MAX_PORTS];
+struct net_device *fst_net_list[FST_MAX_CARDS * FST_MAX_PORTS];
+spinlock_t fst_work_q_lock;
+u64 fst_work_txq;
+u64 fst_work_intq;
+u64 fst_threshq;
+u64 fst_pstatsq;
+int last_segment_cnt;
+
+/* We need to send an idle cell when using mpoa
+ * encapsulation
+ */
+char atm_idle_cell[53];
+
+static unsigned int fst_minor;
+static struct class *farsync_class;
+
+static unsigned int fst_major; /* The char driver major device number.
+ * Setting it to 0 will cause us to use
+ * dynamic allocation during module load
+ */
+
+static char *type_strings[] = {
+ "no hardware", /* Should never be seen */
+ "FarSync T2P ",
+ "FarSync T4P ",
+ "FarSync T1U ",
+ "FarSync T2U ",
+ "FarSync T4U ",
+ "FarSync TE1 ",
+ "FarSync DSL-S1",
+ "FarSync T4E ",
+ "FarSync Flex-1 ",
+ "FarSync T4Ue ",
+ "FarSync T2Ue ",
+ "FarSync T4E+ ",
+ "FarSync T2U-PMC",
+ "FarSync TE1e ",
+ "FarSync T2Ee ",
+ "FarSync T4Ee ",
+ "FarSync Flex-1 (v2)"
+};
+
+static char *state_strings[] = {
+ "Uninitialised", /* Should never be seen */
+ "Reset",
+ "Downloading",
+ "Starting",
+ "Running",
+ "Bad Version",
+ "Halted",
+ "I Failed"
+};
+
+static void fst_openport(struct fst_port_info *port);
+static void fst_closeport(struct fst_port_info *port);
+static int fst_start_xmit(struct sk_buff *skb, struct net_device *dev);
+
+/* Convert an HDLC device pointer into a port info pointer and similar */
+#define dev_to_port(D) (dev_to_hdlc(D)->priv)
+#define port_to_dev(P) ((P)->dev)
+#define port_to_stats(P, S) (P->dev->stats.S)
+#define hdlc_stats(D) (&(D->stats))
+
+/* Sooner or later you can't avoid a forward declaration */
+static int fst_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+static void fst_notify_state_change(struct fst_card_info *card,
+ struct fst_port_info *port);
+static int post_a_read_bulk_urb(struct fst_card_info *usb_dev);
+static int post_a_read_int_urb(struct fst_card_info *usb_dev);
+static int flex_get_config(struct fst_card_info *usb_dev,
+ struct fst_port_info *port,
+ struct fstioc_info *info);
+static int flex_set_config(struct fst_card_info *usb_dev,
+ struct fstioc_info *info);
+static void flex_delete(struct fst_card_info *dev)
+{
+
+ usb_put_dev(dev->udev);
+ kfree(dev);
+}
+
+static void flex_serconf_to_le(FLEX_SERCONF *usb_config);
+static void flex_serconf_to_cpu(FLEX_SERCONF *usb_config);
+static void flex_irex_to_le(IREX *stats);
+static void flex_irex_to_cpu(IREX *stats);
+static void flex_fstioc_req_to_le(struct fstioc_req *sys_request);
+static void flex_fstioc_req_to_cpu(struct fstioc_req *sys_request);
+static void flex_card_info_to_le(FLEX_CARD_INFO *usb_card_info);
+static void flex_card_info_to_cpu(FLEX_CARD_INFO *usb_card_info);
+static void flex_txrx_preamble_to_cpu(FSCMN_TXRX_PREAMBLE *header);
+
+#define FIFO_ASY_CMD 0
+#define FIFO_ASY_TX 1
+#define FIFO_ASY_MSG 2
+#define FIFO_ASY_RX 3
+#define FIFO_ASY_RSP 4
+#define FIFO_ASY_RXEVNT 5
+#define FIFO_PORT_RX 6
+#define FIFO_PORT_TX 7
+
+#define FST_RX_DATA_FIFO_LEN 4096
+#define FST_RX_DATA_FIFO_HIGH (3*1024)
+#define FST_RX_DATA_FIFO_LOW (1*1024)
+
+#define FST_MAX_FARSYNC_PORTS 4
+#define FST_L1_DEV_NAME "hdlc"
+
+int find_last_hdlc_device(void)
+{
+ char devname[16];
+ char devnum[16];
+ struct net_device *dev;
+ int i;
+
+ /* Ask the system how many hdlcX cards there are ...
+ */
+
+ for (i = 0; i < FST_MAX_CARDS * FST_MAX_FARSYNC_PORTS; i++) {
+ strcpy(devname, FST_L1_DEV_NAME);
+ sprintf(devnum, "%d", i);
+ strcat(devname, devnum);
+ dev = dev_get_by_name(&init_net, devname);
+ if (dev == NULL) {
+ fst_dbg(DBG_TTY, "Missing device at %d\n", i);
+ break; /* End loop early */
+ } else {
+ dev_put(dev);
+ }
+ }
+ fst_dbg(DBG_TTY,
+ "There were %d farsync tty ports, starting flex tty ports at %d\n",
+ i, i);
+ return i;
+}
+
+static void fst_reset_rx_fifo(struct fst_port_info *port)
+{
+ int used_space;
+
+ used_space = ((port->fifo_rxdata->write_idx -
+ port->fifo_rxdata->read_idx));
+ if (used_space < 0) {
+ fst_dbg(DBG_FIFO, "adjusting used_space %d\n", used_space);
+ used_space = used_space + FST_RX_DATA_FIFO_LEN;
+ }
+ fst_dbg(DBG_FIFO, "%s: Resetting Rx Fifo %d %d\n", port->dev->name,
+ port->fifo_rxdata->write_idx, port->fifo_rxdata->read_idx);
+ fst_dbg(DBG_FIFO, "%s: %d bytes left in fifo\n", port->dev->name,
+ used_space);
+ port->fifo_rxdata->fifo_length = (u16) (FST_RX_DATA_FIFO_LEN + 1);
+ port->fifo_rxdata->read_idx =
+ (u16) (port->fifo_rxdata->fifo_length - 1);
+ port->fifo_rxdata->write_idx =
+ (u16) (port->fifo_rxdata->fifo_length - 1);
+ port->fifo_rxdata->overflow_idx = -1;
+}
+
+static int fst_alloc_rx_fifo(struct fst_port_info *port)
+{
+
+ /* Allocate the data for the fifo
+ */
+ port->fifo_rxdata = kmalloc(FST_RX_DATA_FIFO_LEN
+ + sizeof(struct fst_fifo), GFP_ATOMIC);
+ if (port->fifo_rxdata == NULL) {
+ fst_dbg(DBG_FIFO,
+ "fifo_init: can't allocate %u bytes for Fifo\n",
+ (unsigned int)(FST_RX_DATA_FIFO_LEN +
+ sizeof(struct fst_fifo)));
+ return 1;
+ }
+ fst_dbg(DBG_ASY, "%s: Fifo memory allocated at %p\n", port->dev->name,
+ port->fifo_rxdata);
+ /*
+ * Now initialise it
+ */
+ /* must setup the fifo's length */
+ port->fifo_rxdata->fifo_length = (u16) (FST_RX_DATA_FIFO_LEN + 1);
+ port->fifo_rxdata->read_idx =
+ (u16) (port->fifo_rxdata->fifo_length - 1);
+ port->fifo_rxdata->write_idx =
+ (u16) (port->fifo_rxdata->fifo_length - 1);
+ port->fifo_rxdata->overflow_idx = -1;
+ port->fifo_rxdata->data[0] = 'F';
+ port->fifo_rxdata->data[1] = '1';
+ port->fifo_rxdata->data[2] = 'D';
+ port->fifo_rxdata->data[3] = '0';
+ return 0;
+}
+
+struct net_device *get_net_device(int if_index)
+{
+ struct net_device *dev;
+
+ dev = fst_ports_list[if_index]->dev;
+ fst_dbg(DBG_ASS, "get_net_device: returning %s\n", dev->name);
+ return dev;
+}
+
+/* Sooner or later you can't avoid a forward declaration */
+static int fst_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+
+static const speed_t baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 230400, 460800,
+ 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
+ 2500000, 3000000, 3500000, 4000000
+};
+
+static int n_baud_table = ARRAY_SIZE(baud_table);
+
+static unsigned int get_baud(tcflag_t c_cflag)
+{
+ unsigned int baud;
+
+ baud = c_cflag & CBAUD;
+ if (baud & CBAUDEX) {
+ baud &= ~CBAUDEX;
+
+ if (baud < 1 || baud + 15 > n_baud_table)
+ c_cflag &= ~CBAUDEX;
+ else
+ baud += 15;
+ }
+ baud = baud_table[baud];
+ fst_dbg(DBG_IOCTL, "Baud rate was %d\n", baud);
+ return baud;
+}
+
+static unsigned int get_rtscts(tcflag_t c_cflag)
+{
+ unsigned int rtscts;
+
+ rtscts = c_cflag & CRTSCTS;
+ fst_dbg(DBG_IOCTL, "rtscts was %x\n", rtscts);
+ if (rtscts)
+ return 1;
+ else
+ return 0;
+}
+
+static unsigned int get_xonxoff(tcflag_t c_iflag)
+{
+ unsigned int xon, xoff;
+
+ xon = c_iflag & IXON;
+ xoff = c_iflag & IXOFF;
+ fst_dbg(DBG_IOCTL, "xon was %x xoff was %x\n", xon, xoff);
+ if (xon && xoff)
+ return 2;
+ else
+ return 0;
+}
+
+static unsigned int get_char_size(tcflag_t c_cflag)
+{
+ unsigned int char_size;
+
+ char_size = c_cflag & CSIZE;
+ fst_dbg(DBG_IOCTL, "Character size = %d\n", char_size);
+ switch (char_size) {
+ case CS5:
+ fst_dbg(DBG_IOCTL, "Character size set as 5 bits\n");
+ return 5;
+
+ case CS6:
+ fst_dbg(DBG_IOCTL, "Character size set as 6 bits\n");
+ return 6;
+
+ case CS7:
+ fst_dbg(DBG_IOCTL, "Character size set as 7 bits\n");
+ return 7;
+
+ case CS8:
+ default:
+ fst_dbg(DBG_IOCTL, "Character size set as 8 bits\n");
+ return 8;
+
+ }
+}
+
+static unsigned int get_stop_bits(tcflag_t c_cflag)
+{
+ unsigned int stop_bits;
+
+ stop_bits = c_cflag & CSTOPB;
+ fst_dbg(DBG_IOCTL, "Stop bits = %d\n", stop_bits);
+ if (stop_bits) {
+ fst_dbg(DBG_IOCTL, "2 stop bits selected\n");
+ return 2;
+ }
+ fst_dbg(DBG_IOCTL, "1 stop bit selected\n");
+ return 1;
+}
+
+static unsigned int get_parity(tcflag_t c_cflag)
+{
+ unsigned int parity_enable, parity;
+
+ parity_enable = c_cflag & PARENB;
+ parity = c_cflag & PARODD;
+ fst_dbg(DBG_IOCTL, "Parity enable is %d and parity odd is %d\n",
+ parity_enable, parity);
+ if (parity_enable) {
+ if (parity) {
+ fst_dbg(DBG_IOCTL, "Returning Odd Parity\n");
+ return 1;
+ } else {
+ fst_dbg(DBG_IOCTL, "Returning Even Parity\n");
+ return 2;
+ }
+ } else {
+ fst_dbg(DBG_IOCTL, "Parity not enabled\n");
+ return 0;
+ }
+}
+
+static void
+init_async_parameters(struct fst_port_info *port,
+ unsigned int char_size, unsigned int stop_bits,
+ unsigned int parity, unsigned int flow_control)
+{
+ int retval;
+
+ fst_dbg(DBG_IOCTL, "%s: Initialising async parameters\n",
+ port->dev->name);
+ flex_serconf_to_le(&port->usb_config);
+ retval = usb_control_msg(port->card->udev,
+ usb_rcvctrlpipe(port->card->udev, 0),
+ USS_CMD_GET_CONFIG, 0xc0, 0, 0,
+ &port->usb_config,
+ sizeof(FLEX_SERCONF), 10000);
+ flex_serconf_to_cpu(&port->usb_config);
+ if (retval != sizeof(port->usb_config))
+ fst_dbg(DBG_ASS, "Get Config error errno = %d\n", retval);
+ port->usb_config.flow_control = flow_control;
+ port->usb_config.xonchar = 0x11;
+ port->usb_config.xoffchar = 0x13;
+ port->usb_config.data_bits = char_size;
+ port->usb_config.stopbits = stop_bits;
+ port->usb_config.parity = parity;
+ fst_dbg(DBG_IOCTL, "%s: Reconfiguring Port\n", port->dev->name);
+ flex_serconf_to_le(&port->usb_config);
+ retval = usb_control_msg(port->card->udev,
+ usb_sndctrlpipe(port->card->udev, 0),
+ USS_CMD_SET_CONFIG, 0x40, 0, 0,
+ &port->usb_config,
+ sizeof(FLEX_SERCONF), 10000);
+ flex_serconf_to_cpu(&port->usb_config);
+ if (retval > 0)
+ fst_dbg(DBG_IOCTL, "Set config response received\n");
+ else
+ fst_dbg(DBG_ASS, "Set Config error errno = %d\n", retval);
+}
+
+static void
+configure_async_parameters(struct fst_port_info *port, unsigned int baud,
+ unsigned int char_size, unsigned int stop_bits,
+ unsigned int parity, unsigned int flow_control)
+{
+ int retval;
+
+ fst_dbg(DBG_IOCTL, "%s: Setting async parameters\n", port->dev->name);
+ if (port->run)
+ fst_issue_cmd(port, STOPPORT);
+ port->usb_config.flow_control = COM_FLOW_CONTROL_NONE;
+ if ((flow_control == 1) || (flow_control == 3))
+ port->usb_config.flow_control = COM_FLOW_CONTROL_RTSCTS;
+ if (flow_control == 2)
+ port->usb_config.flow_control = COM_FLOW_CONTROL_XONXOFF;
+ fst_dbg(DBG_IOCTL, "Flow control chosen as %d\n",
+ port->usb_config.flow_control);
+ port->usb_config.xonchar = 0x11;
+ port->usb_config.xoffchar = 0x13;
+ port->usb_config.line_speed = baud;
+ port->usb_config.clock = 1;
+ port->usb_config.data_bits = char_size;
+ if (stop_bits == 2)
+ port->usb_config.stopbits = COM_STOP_BITS_2;
+ else
+ port->usb_config.stopbits = COM_STOP_BITS_1;
+
+ if (parity) {
+ if (parity == 1)
+ port->usb_config.parity = COM_ODD_PARITY;
+ else
+ port->usb_config.parity = COM_EVEN_PARITY;
+ } else {
+ port->usb_config.parity = COM_NO_PARITY;
+ }
+ fst_dbg(DBG_IOCTL, "%s: Reconfiguring Port\n", port->dev->name);
+ flex_serconf_to_le(&port->usb_config);
+ retval = usb_control_msg(port->card->udev,
+ usb_sndctrlpipe(port->card->udev, 0),
+ USS_CMD_SET_CONFIG, 0x40, 0, 0,
+ &port->usb_config,
+ sizeof(FLEX_SERCONF), 10000);
+ flex_serconf_to_cpu(&port->usb_config);
+
+ if (retval > 0)
+ fst_dbg(DBG_IOCTL, "Set config response received\n");
+ else
+ fst_dbg(DBG_ASS, "Set Config error errno = %d\n", retval);
+ if (port->run)
+ fst_issue_cmd(port, STARTPORT);
+}
+
+#define MAX_TRANSFER FST_MAX_USB_BUFFER
+#define FST_CHAR_INQ_MAX_LEN 100;
+
+/* Char tty interface for async PPP */
+#define FST_TTY_NPORTS (FST_MAX_CARDS*FST_MAX_PORTS)
+#define FST_TTY_MAJOR 191
+#define FST_TTY_MINOR_START 0
+/* tty interface state */
+#define FST_TTY_ST_CLOSED 0 /* Initial state */
+#define FST_TTY_ST_OPEN 1 /* Has at least one open */
+#define FST_TTY_ST_DATA 2 /* Open and used for data */
+#define FST_TTY_ST_DISC 3 /* Open but line has dropped */
+
+/* TTY functions prototype */
+static int fst_tty_open(struct tty_struct *tty, struct file *flip);
+static void fst_tty_close(struct tty_struct *tty, struct file *flip);
+static int fst_tty_write(struct tty_struct *tty, const unsigned char *buf,
+ int count);
+static int fst_tty_write_room(struct tty_struct *tty);
+static int fst_tty_chars_in_buffer(struct tty_struct *tty);
+int fst_tty_tiocmset(struct tty_struct *, unsigned int, unsigned int);
+int fst_tty_tiocmget(struct tty_struct *);
+int fst_tty_tiocmset(struct tty_struct *, unsigned int, unsigned int);
+int fst_tty_tiocmget(struct tty_struct *);
+static void fst_tty_flush_buffer(struct tty_struct *tty);
+static void fst_tty_hangup(struct tty_struct *tty);
+
+static void fst_tty_set_termios(struct tty_struct *tty, struct ktermios *old);
+
+static struct tty_driver *serial_drv;
+
+typedef struct _fst_tty_area {
+ struct tty_port tty_port;
+ int state; /* state of the TTY interface */
+ int num_open;
+ unsigned int tty_minor; /* minor this interface */
+ struct fst_port_info *port; /* ptr. to port info */
+ unsigned char name[20]; /* interf. name + "-tty" */
+ struct tty_struct *tty;
+ struct sk_buff *skb;
+ int len; /* async */
+ char *data; /* async */
+ struct work_struct fst_tty_work;
+ struct work_struct fst_async_tty_work;
+} st_fst_tty_area;
+
+static const struct tty_operations fst_tty_ops = {
+ .open = fst_tty_open,
+ .close = fst_tty_close,
+ .write = fst_tty_write,
+ .write_room = fst_tty_write_room,
+ .chars_in_buffer = fst_tty_chars_in_buffer,
+ .tiocmset = fst_tty_tiocmset,
+ .tiocmget = fst_tty_tiocmget,
+ .set_termios = fst_tty_set_termios,
+ .flush_buffer = fst_tty_flush_buffer,
+ .hangup = fst_tty_hangup,
+};
+
+st_fst_tty_area fst_tty_area[FST_TTY_NPORTS];
+int fst_tty_refcount;
+int fst_tty_unreg_flag;
+int fst_tty_cnt;
+
+void fst_tty_uninit(void)
+{
+ /* Unload the tty driver
+ */
+ int res;
+
+ pr_info("Unregister the flex tty driver\n");
+ res = tty_unregister_driver(serial_drv);
+ if (res) {
+ fst_dbg(DBG_ASS,
+ "ERROR ->unregister the tty driver error=%d\n",
+ res);
+ }
+ put_tty_driver(serial_drv);
+}
+
+int fst_tty_init_body(void)
+{
+ int base_port_number = 0;
+
+ base_port_number = find_last_hdlc_device();
+
+ /* initialize tty driver struct */
+ serial_drv = alloc_tty_driver(FST_TTY_NPORTS);
+ if (serial_drv == NULL)
+ return 0;
+ serial_drv->magic = TTY_DRIVER_MAGIC;
+ serial_drv->driver_name = "flex_tty";
+ serial_drv->name = "tty_hdlc";
+ serial_drv->major = 0;
+ serial_drv->name_base = base_port_number;
+ serial_drv->minor_start = 0; /* Use the whole of the minor range */
+ serial_drv->num = FST_TTY_NPORTS;
+ serial_drv->type = TTY_DRIVER_TYPE_SERIAL;
+ serial_drv->subtype = SERIAL_TYPE_NORMAL;
+ serial_drv->init_termios = tty_std_termios;
+ serial_drv->init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ serial_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+
+ /* interface routines from the upper tty layer to the tty driver */
+ tty_set_operations(serial_drv, &fst_tty_ops);
+
+ /* register the TTY driver */
+ if (tty_register_driver(serial_drv)) {
+ pr_info("fsc: Failed to register serial driver!\n");
+ put_tty_driver(serial_drv);
+ return 0;
+ }
+ return 1; /* OK */
+}
+
+int fst_tty_init(void)
+{
+ /* Load the tty driver
+ */
+ int retval = 0;
+
+ pr_info("Initialising flex tty driver\n");
+ retval = fst_tty_init_body();
+ if (retval)
+ memset((void *)fst_tty_area, 0,
+ sizeof(st_fst_tty_area) * FST_TTY_NPORTS);
+ return retval;
+}
+
+static int fst_tty_open(struct tty_struct *tty, struct file *file)
+{
+ /* Open a farsync port
+ * It must not already be open by the network stack
+ */
+ int port_num;
+ st_fst_tty_area *fst_tty;
+ struct net_device *dev;
+ struct fst_port_info *port;
+ char dev_name[16];
+
+ fst_dbg(DBG_ASS, "fst_tty_open\n");
+ if (!tty)
+ return -ENODEV;
+
+ port_num = tty->index;
+ if ((port_num < 0) || (port_num >= FST_TTY_NPORTS)) {
+ fst_dbg(DBG_ASS, "fst_tty: open invalid flex minor %i\n",
+ port_num);
+ return -ENODEV;
+ }
+
+ fst_tty = &fst_tty_area[port_num];
+ INIT_WORK(&fst_tty->fst_tty_work, fst_process_tty_work);
+ INIT_WORK(&fst_tty->fst_async_tty_work, fst_process_async_tty_work);
+
+ if (fst_tty->num_open == 0) {
+ /* We need to find the dev device and see if it is open
+ */
+ dev = get_net_device(port_num);
+ if (dev == NULL) {
+ fst_dbg(DBG_ASS,
+ "Cannot open char device %s because net device not found\n",
+ dev->name);
+ return -ENXIO;
+ }
+ port = dev_to_port(dev);
+ if (port == NULL) {
+ fst_dbg(DBG_ASS,
+ "Cannot open char device %s because port is not created\n",
+ dev->name);
+ return -EACCES;
+ }
+ if (port->run) {
+ /* It is likely that the network device is active
+ * so refuse this open
+ */
+ return -EBUSY;
+ }
+ /* first open of this tty */
+ fst_tty->state = FST_TTY_ST_OPEN;
+ fst_tty->tty = tty;
+ tty->driver_data = &fst_tty_area[port_num];
+ fst_tty->num_open = 0;
+ fst_tty->tty_minor = port_num + FST_TTY_MINOR_START;
+
+ strcpy(dev_name, "tty_");
+ strcat(dev_name, dev->name);
+ strcpy(fst_tty->name, dev->name);
+ fst_dbg(DBG_ASS,
+ "%s: Initializing Flex TTY Sync Driver, tty major#%d minor#%i\n",
+ fst_tty->name, FST_TTY_MAJOR, fst_tty->tty_minor);
+
+ /* Set a flag to indicate we are now using the char interface
+ * for data read and write
+ */
+ port->char_file = file;
+ /* Open the physical port if not already open
+ */
+ if (!port->port_mode)
+ fst_openport(port);
+ fst_tty->port = port;
+ try_module_get(THIS_MODULE);
+ fst_tty_cnt++;
+ pr_info("TTY driver flex port %s is now open\n",
+ fst_tty->name);
+ }
+ fst_tty->num_open++;
+ return 0;
+}
+
+static void fst_tty_close(struct tty_struct *tty, struct file *flip)
+{
+ /*
+ * Close a farsync port
+ */
+ st_fst_tty_area *fst_tty;
+ unsigned long flags;
+
+ if (!tty || !tty->driver_data) {
+ fst_dbg(DBG_ASS, "fst_tty: no Flex TTY in close\n");
+ return;
+ }
+
+ fst_tty = (st_fst_tty_area *) tty->driver_data;
+
+ if ((fst_tty->tty != tty) || (fst_tty->state < FST_TTY_ST_OPEN)) {
+ fst_dbg(DBG_ASS, "%s: TTY is not opened\n", fst_tty->name);
+ return;
+ }
+
+ if (!fst_tty->num_open) {
+ fst_dbg(DBG_ASS, "%s: TTY is closed\n", fst_tty->name);
+ return;
+ }
+
+ if (--fst_tty->num_open > 0) {
+ fst_dbg(DBG_ASS, "%s: TTY closing the second open\n",
+ fst_tty->name);
+ return;
+ }
+
+ fst_tty_cnt--;
+ if (fst_tty->state != FST_TTY_ST_CLOSED) {
+ spin_lock_irqsave(&fst_tty->port->card->card_lock, flags);
+ fst_tty->tty = NULL;
+ fst_tty->state = FST_TTY_ST_CLOSED;
+ spin_unlock_irqrestore(&fst_tty->port->card->card_lock, flags);
+ fst_closeport(fst_tty->port);
+ fst_tty->port->run = 0;
+ fst_tty->port->char_file = NULL;
+ }
+ module_put(THIS_MODULE);
+ pr_info("TTY driver flex port %s is now closed\n", fst_tty->name);
+ return;
+}
+
+static int fst_tty_write(struct tty_struct *tty, const unsigned char *buf,
+ int count)
+{
+ /* Send data to the farsync port
+ */
+ st_fst_tty_area *fst_tty;
+ struct sk_buff *skb;
+ int retval;
+ int from_user = 0;
+
+ if (!tty || !tty->driver_data) {
+ fst_dbg(DBG_ASS, "Could not find Flex TTY device in write\n");
+ return -ENODEV;
+ }
+
+ fst_tty = (st_fst_tty_area *) tty->driver_data;
+
+ if ((fst_tty->tty != tty) || (fst_tty->state < FST_TTY_ST_OPEN)) {
+ fst_dbg(DBG_ASS, "%s: TTY device is not opened\n",
+ fst_tty->name);
+ return -ENODEV;
+ }
+
+ if (count > FST_MAX_MTU) {
+ fst_dbg(DBG_ASS, "%s: Size of write is too large\n",
+ fst_tty->name);
+ return -EINVAL; /* frame too big */
+ }
+
+ if (fst_tty->state == FST_TTY_ST_CLOSED) {
+ fst_dbg(DBG_TTY,
+ "%s: discarding %d bytes of write data on a closed device\n",
+ fst_tty->name, count);
+ return -ENODEV;
+ }
+ fst_dbg(DBG_TTY, "%s: fst_tty_write %s data len=%i\n", fst_tty->name,
+ (from_user) ? "from user" : "from kernel", count);
+ /* No special treatment for async mode, unlike farsync driver
+ */
+ skb = dev_alloc_skb(count);
+ if (skb == NULL) {
+ fst_dbg(DBG_ASS, "fst_tty_write: can't allocate skb\n");
+ return -EFAULT;
+ }
+
+ /* Read it in */
+ if (from_user) {
+ fst_dbg(DBG_TTY, "Value of from user is %d\n", from_user);
+ if (__copy_from_user(skb_put(skb, count), buf, count)) {
+ dev_kfree_skb(skb);
+ return -EFAULT;
+ }
+ } else {
+ memcpy(skb_put(skb, count), buf, count);
+ }
+ fst_tty->state = FST_TTY_ST_DATA;
+ retval = fst_start_xmit(skb, port_to_dev(fst_tty->port));
+ if (retval != count) {
+ fst_dbg(DBG_TTY,
+ "%s: tty_write asked to transmit %d, transmitted %d\n",
+ fst_tty->name, count, retval);
+ }
+ return retval;
+}
+
+static int fst_tty_write_room(struct tty_struct *tty)
+{
+ st_fst_tty_area *fst_tty;
+ int room = FST_MAX_MTU;
+
+ if (!tty || !tty->driver_data) {
+ fst_dbg(DBG_ASS, "Could not find TTY device to write room\n");
+ return -ENODEV;
+ }
+
+ fst_tty = (st_fst_tty_area *) tty->driver_data;
+
+ if ((fst_tty->tty != tty) || (fst_tty->state < FST_TTY_ST_OPEN)) {
+ fst_dbg(DBG_ASS, "%s: TTY device is not opened\n",
+ fst_tty->name);
+ return -ENODEV;
+ }
+
+ fst_dbg(DBG_ASS, "%s: write room\n", fst_tty->name);
+ if (fst_tty->port->start)
+ room = 0;
+ fst_dbg(DBG_TTY, "%s: tty_write_room returning %d\n", fst_tty->name,
+ room);
+ return room;
+}
+
+static int fst_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ st_fst_tty_area *fst_tty;
+
+ if (!tty || !tty->driver_data) {
+ fst_dbg(DBG_ASS,
+ "Could not find TTY device for chars in buffer\n");
+ return -ENODEV;
+ }
+
+ fst_tty = (st_fst_tty_area *) tty->driver_data;
+
+ if ((fst_tty->tty != tty) || (fst_tty->state < FST_TTY_ST_OPEN)) {
+ fst_dbg(DBG_ASS, "%s: TTY device is not opened\n",
+ fst_tty->name);
+ return -ENODEV;
+ }
+ fst_dbg(DBG_TTY, "%s: tty_chars_in_buffer returning 0\n",
+ fst_tty->name);
+ return 0;
+}
+
+static void fst_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+ st_fst_tty_area *fst_tty;
+ unsigned int baud;
+ unsigned int stop_bits;
+ unsigned int char_size;
+ unsigned int parity;
+ unsigned int rtscts;
+ unsigned int xonxoff;
+
+ fst_dbg(DBG_TTY, "tty_set_termios\n");
+ if (!tty || !tty->driver_data) {
+ fst_dbg(DBG_ASS, "Could not find TTY for tiocmset\n");
+ return;
+ }
+ fst_tty = (st_fst_tty_area *) tty->driver_data;
+ old = &fst_tty->tty->termios;
+ fst_dbg(DBG_TTY, "%s: Setting %x\n", fst_tty->name, old->c_cflag);
+ baud = get_baud(old->c_cflag);
+ char_size = get_char_size(old->c_cflag);
+ stop_bits = get_stop_bits(old->c_cflag);
+ parity = get_parity(old->c_cflag);
+ rtscts = get_rtscts(old->c_cflag);
+ xonxoff = get_xonxoff(old->c_iflag);
+ /* If we are in async mode we need to configure the async parameters
+ */
+ if (fst_tty->port->mode == FST_MODE_ASYNC) {
+ configure_async_parameters(fst_tty->port, baud, char_size,
+ stop_bits, parity, rtscts | xonxoff);
+ }
+}
+
+int fst_tty_tiocmset(struct tty_struct *tty, unsigned int set,
+ unsigned int clear)
+{
+ st_fst_tty_area *fst_tty;
+ unsigned long outputs;
+
+ fst_dbg(DBG_TTY, "%s: set:%x clear:%x\n", __func__, set, clear);
+
+ if (!tty || !tty->driver_data) {
+ fst_dbg(DBG_ASS, "Could not find TTY for tiocmset\n");
+ return -ENODEV;
+ }
+
+ fst_tty = (st_fst_tty_area *) tty->driver_data;
+
+ if (set & TIOCM_RTS) {
+ fst_dbg(DBG_TTY, "Set RTS\n");
+ outputs = OPSTS_RTS | fst_tty->port->v24OpSts;
+ fst_tty->port->v24OpSts = outputs;
+ if (fst_tty->port->run)
+ fst_issue_cmd(fst_tty->port, SETV24O);
+ }
+
+ if (set & TIOCM_DTR) {
+ fst_dbg(DBG_TTY, "Set DTR\n");
+ outputs = OPSTS_DTR | fst_tty->port->v24OpSts;
+ fst_tty->port->v24OpSts = outputs;
+ if (fst_tty->port->run)
+ fst_issue_cmd(fst_tty->port, SETV24O);
+ }
+
+ if (clear & TIOCM_RTS) {
+ fst_dbg(DBG_TTY, "Clear RTS\n");
+ outputs = OPSTS_RTS ^ fst_tty->port->v24OpSts;
+ fst_tty->port->v24OpSts = outputs;
+ if (fst_tty->port->run)
+ fst_issue_cmd(fst_tty->port, SETV24O);
+ }
+ if (clear & TIOCM_DTR) {
+ fst_dbg(DBG_TTY, "Clear DTR\n");
+ outputs = OPSTS_DTR ^ fst_tty->port->v24OpSts;
+ fst_tty->port->v24OpSts = outputs;
+ if (fst_tty->port->run)
+ fst_issue_cmd(fst_tty->port, SETV24O);
+ }
+ return 0;
+}
+
+int fst_tty_tiocmget(struct tty_struct *tty)
+{
+ unsigned int signals = 0;
+ unsigned long out = 0;
+
+ st_fst_tty_area *fst_tty = (st_fst_tty_area *) tty->driver_data;
+ fst_tty = (st_fst_tty_area *) tty->driver_data;
+
+ fst_dbg(DBG_TTY, "%s: tiocmget\n", fst_tty->name);
+
+ signals = fst_tty->port->v24OpSts;
+ if (signals & OPSTS_RTS)
+ out |= TIOCM_RTS;
+ if (signals & OPSTS_DTR)
+ out |= TIOCM_DTR;
+ fst_issue_cmd(fst_tty->port, GETSIGNALS);
+ signals = fst_tty->port->v24IpSts;
+ if (signals & IPSTS_CTS)
+ out |= TIOCM_CTS;
+ if (signals & IPSTS_DSR)
+ out |= TIOCM_DSR;
+ if (signals & IPSTS_DCD)
+ out |= TIOCM_CD;
+ fst_dbg(DBG_TTY, "Returning signals as %lx\n", out);
+ return out;
+}
+
+static void fst_tty_flush_buffer(struct tty_struct *tty)
+{
+ st_fst_tty_area *fst_tty;
+
+ if (!tty || !tty->driver_data) {
+ fst_dbg(DBG_ASS, "Could not find TTY to flush buffer\n");
+ return;
+ }
+
+ fst_tty = (st_fst_tty_area *) tty->driver_data;
+
+ if ((fst_tty->tty != tty) || (fst_tty->state < FST_TTY_ST_OPEN)) {
+ fst_dbg(DBG_ASS, "%s: TTY device is not opened\n",
+ fst_tty->name);
+ return;
+ }
+
+ fst_dbg(DBG_TTY, "%s: call wake_up_interruptible\n", fst_tty->name);
+
+ wake_up_interruptible(&tty->write_wait);
+
+ return;
+}
+
+static void fst_tty_hangup(struct tty_struct *tty)
+{
+ st_fst_tty_area *fst_tty;
+
+ if (!tty || !tty->driver_data) {
+ fst_dbg(DBG_ASS, "Could not find TTY device to hangup\n");
+ return;
+ }
+
+ fst_tty = (st_fst_tty_area *) tty->driver_data;
+
+ if ((fst_tty->tty != tty) || (fst_tty->state < FST_TTY_ST_OPEN)) {
+ fst_dbg(DBG_ASS, "%s: TTY device is not opened\n",
+ fst_tty->name);
+ return;
+ }
+ fst_dbg(DBG_ASS, "fst_tty_hangup\n");
+}
+
+static void fst_process_async_tty_work(struct work_struct *work)
+{
+
+ st_fst_tty_area *fst_tty;
+ struct fst_port_info *port;
+ char *data;
+ int len;
+ struct tty_ldisc *ld;
+
+ if (fst_tty_cnt == 0)
+ return;
+
+ /* ToDo: Note that the third parameter to the ldisc receive_buf
+ * function is a pointer to an array of status bytes which is
+ * the same length as the data. When we move on to getting the
+ * status bytes from the async interface we can implement this.
+ * For the moment it is a NULL pointer
+ */
+ fst_tty = container_of(work, st_fst_tty_area, fst_async_tty_work);
+ port = fst_tty->port;
+ len = fst_tty->len;
+ data = fst_tty->data;
+
+ fst_tty->state = FST_TTY_ST_DATA;
+ ld = tty_ldisc_ref(fst_tty->tty);
+ if (ld) {
+ if (fst_tty->tty && (ld->ops->receive_buf)) {
+ fst_dbg(DBG_TTY,
+ "%s: call line disc. receive_buf of length %d first byte is %x\n",
+ fst_tty->name, len, data[0]);
+ ld->ops->receive_buf(fst_tty->tty, data, NULL, len);
+ tty_ldisc_deref(ld);
+ port_to_stats(port, rx_packets)++;
+ port_to_stats(port, rx_bytes) += len;
+ } else {
+ fst_dbg(DBG_ASS,
+ "%s: frame dropped. receive_buf of length %d\n",
+ fst_tty->name, len);
+ port_to_stats(port, rx_dropped)++;
+ }
+ }
+}
+
+static void fst_process_tty_work(struct work_struct *work)
+{
+ struct fst_port_info *port;
+ st_fst_tty_area *fst_tty;
+ struct sk_buff *skb;
+ struct tty_ldisc *ld;
+
+ if (fst_tty_cnt == 0)
+ return;
+
+ fst_tty = container_of(work, st_fst_tty_area, fst_tty_work);
+ port = fst_tty->port;
+ skb = fst_tty->skb;
+
+ ld = tty_ldisc_ref(fst_tty->tty);
+ if (ld) {
+ if ((fst_tty->tty) && (ld->ops->receive_buf)) {
+ fst_dbg(DBG_ASS,
+ "%s: call line disc. receive_buf of length %d\n",
+ fst_tty->name, skb->len);
+
+ ld->ops->receive_buf(fst_tty->tty, skb->data,
+ NULL, skb->len);
+ } else {
+ fst_dbg(DBG_ASS,
+ "%s: frame dropped. receive_buf of length %d\n",
+ fst_tty->name, skb->len);
+ }
+ tty_ldisc_deref(ld);
+ }
+ dev_kfree_skb(skb);
+}
+
+/* Card memory image access */
+static const struct file_operations fst_mem_fops = {
+};
+
+/* Control signal change interrupt event
+ */
+static void
+fst_intr_ctlchg(struct fst_card_info *card, struct fst_port_info *port,
+ int signals)
+{
+ if (signals & (((port->hwif == X21) || (port->hwif == X21D))
+ ? IPSTS_INDICATE : IPSTS_DCD)) {
+ if ((!netif_carrier_ok(port_to_dev(port)))
+ && (!port->ignore_carrier)) {
+ fst_dbg(DBG_ASS, "%s: DCD active\n", port->dev->name);
+ netif_carrier_on(port_to_dev(port));
+ fst_notify_state_change(card, port);
+ }
+ } else {
+ if ((netif_carrier_ok(port_to_dev(port)))
+ && (!port->ignore_carrier)) {
+ fst_dbg(DBG_ASS, "%s: DCD lost\n", port->dev->name);
+ netif_carrier_off(port_to_dev(port));
+ fst_notify_state_change(card, port);
+ if (fst_tty_area[port->minor_dev_no].state ==
+ FST_TTY_ST_DATA)
+ tty_hangup(fst_tty_area
+ [port->minor_dev_no].tty);
+ }
+ }
+}
+
+/* Log Rx Errors */
+static void
+fst_log_rx_error(struct fst_card_info *card, struct fst_port_info *port,
+ unsigned char status)
+{
+ /* Increment the appropriate error counter */
+ port_to_stats(port, rx_errors)++;
+ if (status & RX_FRAM) {
+ port_to_stats(port, rx_frame_errors)++;
+ printk_ratelimited("%s: Rx frame error %x\n",
+ port_to_dev(port)->name, status);
+ }
+ if (status & RX_OFLO) {
+ port_to_stats(port, rx_fifo_errors)++;
+ printk_ratelimited("%s: Rx fifo error %x\n",
+ port_to_dev(port)->name, status);
+ }
+ if (status & RX_CRC) {
+ port_to_stats(port, rx_crc_errors)++;
+ printk_ratelimited("%s: Rx crc error %x\n",
+ port_to_dev(port)->name, status);
+ }
+ if (status & RX_ABRT) {
+ port_to_stats(port, rx_length_errors)++;
+ printk_ratelimited("%s: Rx hardware error %x\n",
+ port_to_dev(port)->name, status);
+ }
+ if (port->notify_mode == FST_NOTIFY_EXTENDED)
+ fst_notify_state_change(port->card, port);
+}
+
+/* Log Tx Errors */
+static void
+fst_log_tx_error(struct fst_card_info *card, struct fst_port_info *port,
+ unsigned char status)
+{
+ /* Increment the appropriate error counter
+ */
+ port_to_stats(port, tx_errors)++;
+ if (status & TX_URUN) {
+ port_to_stats(port, tx_fifo_errors)++;
+ printk_ratelimited("%s: Tx underrun error %d\n",
+ port_to_dev(port)->name, status);
+ }
+ if (status & TX_ABRT) {
+ port_to_stats(port, tx_aborted_errors)++;
+ printk_ratelimited("%s: Tx abort error %d\n",
+ port_to_dev(port)->name, status);
+ }
+ if (port->notify_mode == FST_NOTIFY_EXTENDED)
+ fst_notify_state_change(port->card, port);
+}
+
+static unsigned char reverse[256] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0,
+ 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8,
+ 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4,
+ 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc,
+ 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2,
+ 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda,
+ 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6,
+ 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde,
+ 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1,
+ 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9,
+ 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5,
+ 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd,
+ 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3,
+ 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb,
+ 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7,
+ 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf,
+ 0x3f, 0xbf, 0x7f, 0xff
+};
+
+void fst_reverse_bits(unsigned char *buff, int length)
+{
+ int i;
+
+ for (i = 0; i < length; i++)
+ buff[i] = reverse[buff[i]];
+ return;
+}
+
+#define usb_buffer_alloc usb_alloc_coherent
+#define usb_buffer_free usb_free_coherent
+
+static void deallocate_urbs_and_buffers(struct fst_card_info *dev)
+{
+ /* free up our allocated buffers */
+ fst_dbg(DBG_INIT, "Freeing a trace buffer at %p\n",
+ dev->int_in_buffer);
+ usb_buffer_free(dev->udev, dev->int_in_size,
+ dev->int_in_buffer, dev->int_urb->transfer_dma);
+ fst_dbg(DBG_INIT, "Freeing a read buffer at %p\n",
+ dev->bulk_in_buffer);
+ usb_buffer_free(dev->udev, MAX_TRANSFER,
+ dev->bulk_in_buffer, dev->bulk_urb->transfer_dma);
+ fst_dbg(DBG_INIT, "Freeing a write buffer at %p\n",
+ dev->bulk_out_buffer);
+ usb_buffer_free(dev->udev, MAX_TRANSFER, dev->bulk_out_buffer,
+ dev->tx_bulk_urb->transfer_dma);
+ usb_free_urb(dev->int_urb);
+ usb_free_urb(dev->bulk_urb);
+ usb_free_urb(dev->tx_bulk_urb);
+}
+
+static int allocate_urbs_and_buffers(struct fst_card_info *dev)
+{
+ dev->int_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!dev->int_urb) {
+ fst_dbg(DBG_ASS, "Could not allocate interrupt urb\n");
+ return -ENOMEM;
+ }
+
+ dev->bulk_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!dev->bulk_urb) {
+ fst_dbg(DBG_ASS, "Could not allocate bulk urb\n");
+ usb_free_urb(dev->int_urb);
+ return -ENOMEM;
+ }
+ dev->tx_bulk_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!dev->tx_bulk_urb) {
+ fst_dbg(DBG_ASS, "Could not allocate tx bulk urb\n");
+ usb_free_urb(dev->int_urb);
+ usb_free_urb(dev->bulk_urb);
+ return -ENOMEM;
+ }
+
+ dev->int_in_buffer = usb_buffer_alloc(dev->udev,
+ dev->int_in_size, GFP_ATOMIC,
+ &dev->int_urb->transfer_dma);
+ if (!dev->int_in_buffer) {
+ usb_free_urb(dev->int_urb);
+ usb_free_urb(dev->bulk_urb);
+ usb_free_urb(dev->tx_bulk_urb);
+ return -ENOMEM;
+ }
+ fst_dbg(DBG_INIT, "Allocating a trace buffer at %p\n",
+ dev->int_in_buffer);
+ dev->bulk_in_buffer =
+ usb_buffer_alloc(dev->udev, MAX_TRANSFER, GFP_ATOMIC,
+ &dev->bulk_urb->transfer_dma);
+ if (!dev->bulk_in_buffer) {
+ usb_buffer_free(dev->udev, dev->int_in_size,
+ dev->int_urb->transfer_buffer,
+ dev->int_urb->transfer_dma);
+ usb_free_urb(dev->int_urb);
+ usb_free_urb(dev->bulk_urb);
+ usb_free_urb(dev->tx_bulk_urb);
+ return -ENOMEM;
+ }
+ fst_dbg(DBG_INIT, "Allocating a read buffer at %p\n",
+ dev->bulk_in_buffer);
+ dev->bulk_out_buffer =
+ usb_buffer_alloc(dev->udev, MAX_TRANSFER, GFP_ATOMIC,
+ &dev->tx_bulk_urb->transfer_dma);
+ if (!dev->bulk_out_buffer) {
+ usb_buffer_free(dev->udev, dev->int_in_size,
+ dev->int_urb->transfer_buffer,
+ dev->int_urb->transfer_dma);
+ usb_buffer_free(dev->udev, MAX_TRANSFER,
+ dev->bulk_urb->transfer_buffer,
+ dev->bulk_urb->transfer_dma);
+ usb_free_urb(dev->int_urb);
+ usb_free_urb(dev->bulk_urb);
+ usb_free_urb(dev->tx_bulk_urb);
+ return -ENOMEM;
+ }
+ fst_dbg(DBG_INIT, "Allocating a write buffer at %p\n",
+ dev->bulk_out_buffer);
+ return 0;
+}
+
+static void sync_write_bulk_callback(struct urb *urb)
+{
+ struct fst_card_info *dev;
+ struct fst_port_info *port;
+
+ dev = (struct fst_card_info *)urb->context;
+ port = dev->ports[0];
+ dev->tx_urb_cnt++;
+
+ /* sync/async unlink faults aren't errors */
+ if (urb->status &&
+ !(urb->status == -ENOENT ||
+ urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) {
+ pr_info("%s - nonzero write bulk status received: %d\n",
+ __func__, urb->status);
+ }
+
+ if (urb != dev->tx_bulk_urb) {
+ fst_dbg(DBG_ASS,
+ "Completion urb is not current outstanding urb\n");
+ }
+ /* Inc stats
+ */
+ port_to_stats(port, tx_packets)++;
+ port_to_stats(port, tx_bytes) += (urb->actual_length - 8);
+ port->dev->trans_start = jiffies;
+ /* Schedule another tx attempt
+ */
+ if ((port->run) && (!urb->status)) {
+ fst_q_work_item(&fst_work_txq, dev->card_no);
+ tasklet_schedule(&fst_tx_task);
+ } else {
+ fst_dbg(DBG_TX, "Not scheduling another send, port closed\n");
+ }
+ fst_dbg(DBG_TX, "Finished write bulk callback\n");
+}
+
+static void decode_header(char *buff, struct fst_card_info *card,
+ struct fst_port_info *port)
+{
+ struct _FLEX_TXRX_PREAMBLE *header;
+ unsigned char status_ok_mask = 0xa0;
+
+ header = (struct _FLEX_TXRX_PREAMBLE *)buff;
+ flex_txrx_preamble_to_cpu(header);
+ if (header->port & 1) {
+ /* Ignore port B status change messages for now
+ * They shouldn't happen in the normal course of events
+ */
+ fst_dbg(DBG_ASS, "%s: Status message for port B %x\n",
+ port_to_dev(port)->name, header->port);
+ return;
+ }
+ header->port = header->port & 0x0f0;
+ switch (header->port) {
+ case RXDATA_STATUS:
+ {
+ if (port->mode == FST_MODE_TRANSPARENT)
+ status_ok_mask = 0x80;
+ header->status ^= status_ok_mask;
+ if (header->status) {
+ pr_err
+ ("%s: Rx Data status is %x should be %x\n",
+ port_to_dev(port)->name, header->status,
+ status_ok_mask);
+ fst_log_rx_error(card, port,
+ header->status ^
+ status_ok_mask);
+ }
+ break;
+ }
+ case TXDATA_STATUS:
+ {
+ if (header->status) {
+ pr_err("%s: Tx Data status is %x\n",
+ port_to_dev(port)->name,
+ header->status);
+ fst_log_tx_error(card, port, header->status);
+ }
+ break;
+ }
+ case SIGNAL_CHANGE:
+ {
+ pr_err("%s: Signal Change Status status is %x\n",
+ port_to_dev(port)->name, header->status);
+ fst_intr_ctlchg(card, port, header->status);
+ break;
+ }
+ case TXRX_ERROR:
+ {
+ printk_ratelimited("%s: TxRx error status is %x\n",
+ port_to_dev(port)->name,
+ header->status);
+ fst_q_work_item(&fst_pstatsq, port->card->card_no);
+ schedule_work(&port_stats_work_q);
+ break;
+ }
+ default:
+ pr_err("%s: Unknown preamble status %x\n",
+ port_to_dev(port)->name, header->port);
+ }
+}
+
+/* Mark it for our own raw sockets interface
+ */
+static __be16 farsync_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+ skb->dev = dev;
+ skb_reset_mac_header(skb);
+ skb->pkt_type = PACKET_HOST;
+ return htons(ETH_P_CUST);
+}
+
+static void sync_read_bulk_callback(struct urb *urb)
+{
+ st_fst_tty_area *fst_tty;
+
+ struct fst_card_info *dev;
+ struct fst_port_info *port;
+ int rx_status = NET_RX_SUCCESS;
+ __u16 *len_ptr;
+
+ dev = (struct fst_card_info *)urb->context;
+ port = dev->ports[0];
+
+ /* sync/async unlink faults aren't errors */
+ if (urb->status &&
+ !(urb->status == -ENOENT || urb->status == -ECONNRESET)) {
+ pr_info("%s - nonzero read bulk status received: %d\n",
+ __func__, urb->status);
+ return;
+ }
+
+ len_ptr = (u16 *) &(dev->bulk_in_buffer[4]);
+ dev->actual_length = le16_to_cpu(*len_ptr);
+ fst_dbg(DBG_RX, "Length of data in hdr is %d\n", dev->actual_length);
+ fst_dbg(DBG_RX, "Length of data from urb is %d\n", urb->actual_length);
+ fst_dbg(DBG_RX, "Header is %x %x %x %x %x %x %x %x\n",
+ dev->bulk_in_buffer[0], dev->bulk_in_buffer[1],
+ dev->bulk_in_buffer[2], dev->bulk_in_buffer[3],
+ dev->bulk_in_buffer[4], dev->bulk_in_buffer[5],
+ dev->bulk_in_buffer[6], dev->bulk_in_buffer[7]);
+ decode_header(dev->bulk_in_buffer, dev, port);
+ if (dev->actual_length != urb->actual_length - 8) {
+ fst_dbg(DBG_RX,
+ "Urb reports different length to header %d %d\n",
+ urb->actual_length, dev->actual_length);
+ }
+ if (dev->actual_length > port->dev->mtu + MAX_PPP_HEADER) {
+ fst_dbg(DBG_ASS, "%s: Received frame longer %d than MTU %d\n",
+ port->dev->name, dev->actual_length, port->dev->mtu);
+ post_a_read_bulk_urb(dev); /* Post a read for data */
+ return;
+ }
+ if (port->mode == FST_MODE_ASYNC) {
+ if (dev->actual_length) {
+ fst_tty = &fst_tty_area[port->minor_dev_no];
+ fst_tty->len = dev->actual_length;
+ fst_tty->data = &dev->bulk_in_buffer[8];
+ schedule_work(&(fst_tty->fst_async_tty_work));
+ }
+ post_a_read_bulk_urb(dev); /* Post a read for data */
+ return;
+ }
+ if ((dev->actual_length > 0) && (port->run)) {
+ port->rxq.frame = dev_alloc_skb(port->dev->mtu);
+ if (port->rxq.frame == NULL) {
+ fst_dbg(DBG_ASS, "intr_rx: can't allocate buffer\n");
+ port_to_stats(port, rx_dropped)++;
+ port->rxq.error_recovery++;
+ return;
+ }
+ port->rxq.flags = 0;
+ port->rxq.count = dev->actual_length;
+ port->rxq.segment_cnt = 1;
+ /* If we need to reverse the bits, then this is where we do it
+ */
+ if (port->receive_msb_first) {
+ fst_reverse_bits(&dev->bulk_in_buffer[8],
+ dev->actual_length);
+ }
+ memcpy(skb_put(port->rxq.frame, dev->actual_length),
+ &(dev->bulk_in_buffer[8]), dev->actual_length);
+ port_to_stats(port, rx_bytes) += dev->actual_length;
+ port_to_stats(port, rx_packets)++;
+ fst_dbg(DBG_RX, "Pushing frame up the stack %d bytes\n",
+ dev->actual_length);
+ /* Push upstream */
+ skb_reset_mac_header(port->rxq.frame);
+ port->rxq.frame->dev = port->dev;
+ if (port->proto == FST_RAW)
+ port->rxq.frame->protocol =
+ farsync_type_trans(port->rxq.frame,
+ port_to_dev(port));
+ else {
+ if (port->char_file) {
+ port->rxq.frame->protocol =
+ htons(ETH_P_WAN_PPP);
+ } else {
+ if (port->hdlc_proto == IF_PROTO_HDLC) {
+ port->rxq.frame->protocol =
+ htons(ETH_P_IP);
+ } else {
+ if (port->hdlc_proto ==
+ IF_PROTO_HDLC_ETH) {
+ port->rxq.frame->protocol =
+ htons(ETH_P_8021Q);
+ } else {
+ port->rxq.frame->protocol =
+ hdlc_type_trans(port->
+ rxq.frame,
+ port_to_dev
+ (port));
+ }
+ }
+ }
+ }
+ if (port->monitor_mode)
+ gen_mon_packet(port->rxq.frame, port, FST_MON_RX);
+ if (port->run) {
+ /* Char or network interface?
+ */
+ if (port->char_file) {
+ st_fst_tty_area *fst_tty;
+ fst_tty = &fst_tty_area[port->minor_dev_no];
+ fst_tty->skb = port->rxq.frame;
+ schedule_work(&(fst_tty->fst_tty_work));
+
+ } else {
+ if (port->run)
+ rx_status = netif_rx(port->rxq.frame);
+ else {
+ dev_kfree_skb(port->rxq.frame);
+ fst_dbg(DBG_ASS,
+ "Discarding frame, port closed\n");
+ }
+ fst_process_rx_status(rx_status,
+ port->dev->name);
+ }
+ if (rx_status == NET_RX_DROP)
+ port_to_stats(port, rx_dropped)++;
+ port->dev->last_rx = jiffies;
+ } else {
+ fst_dbg(DBG_RX,
+ "Frame received after port closed. Ignored %d\n",
+ dev->actual_length);
+ dev_kfree_skb(port->rxq.frame);
+ }
+ port->rxq.frame = NULL;
+ } else {
+ fst_dbg(DBG_RX, "Ignoring a zero length urb\n");
+ }
+ post_a_read_bulk_urb(dev); /* Post a read for data */
+}
+
+static void sync_read_int_callback(struct urb *urb)
+{
+ struct fst_card_info *dev;
+
+ fst_dbg(DBG_RX, "In read int callback\n");
+ dev = (struct fst_card_info *)urb->context;
+
+ /* sync/async unlink faults aren't errors */
+ if (urb->status &&
+ !(urb->status == -ENOENT || urb->status == -ECONNRESET)) {
+ pr_info("%s - nonzero read int status received: %d\n",
+ __func__, urb->status);
+ return;
+ }
+ dev->usb_trace_buffer[dev->usb_trace_ptr++] = dev->int_in_buffer[0];
+ if (dev->int_in_buffer[0] == 0) {
+ pr_info("fst_usb_trace: %s", dev->usb_trace_buffer);
+ dev->usb_trace_ptr = 0;
+ }
+ if (urb->actual_length)
+ post_a_read_int_urb(dev);
+}
+
+static int post_a_read_int_urb(struct fst_card_info *usb_dev)
+{
+ int retval;
+ struct urb *urb = NULL;
+
+ urb = usb_dev->int_urb;
+
+ /* initialize the urb properly */
+ usb_fill_int_urb(urb, usb_dev->udev,
+ usb_rcvintpipe(usb_dev->udev,
+ usb_dev->int_in_endpoint_addr),
+ usb_dev->int_in_buffer, usb_dev->int_in_size,
+ sync_read_int_callback,
+ usb_dev, usb_dev->int_interval);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_flags |= URB_ZERO_PACKET;
+
+ /* post a read on the int in port */
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval) {
+ pr_err("%s - failed submitting an int read urb, error %d",
+ __func__, retval);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int post_a_read_bulk_urb(struct fst_card_info *usb_dev)
+{
+ int retval;
+ int read_length;
+ struct urb *urb = NULL;
+
+ urb = usb_dev->bulk_urb;
+ read_length = usb_dev->ports[0]->rx_buffer_size + 8;
+
+ /* initialize the urb properly */
+ usb_fill_bulk_urb(urb, usb_dev->udev,
+ usb_rcvbulkpipe(usb_dev->udev,
+ usb_dev->bulk_in_endpoint_addr),
+ usb_dev->bulk_in_buffer, read_length,
+ sync_read_bulk_callback, usb_dev);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* post a read on the bulk in port */
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval) {
+ pr_err("%s - failed submitting aread bulk urb, error %d",
+ __func__, retval);
+ return retval;
+ }
+
+ return 0;
+}
+
+static void fst_q_work_item(u64 *queue, int card_index)
+{
+ unsigned long flags;
+ u64 mask;
+
+ /* Grab the queue exclusively
+ */
+ spin_lock_irqsave(&fst_work_q_lock, flags);
+
+ /* Making an entry in the queue is simply a matter of setting
+ * a bit for the card indicating that there is work to do in the
+ * bottom half for the card. Note the limitation of 64 cards.
+ * That ought to be enough
+ */
+ mask = (u64) 1 << card_index;
+ *queue |= mask;
+ spin_unlock_irqrestore(&fst_work_q_lock, flags);
+}
+
+static void fst_process_threshold_work_q(struct work_struct *work)
+{
+ unsigned long flags;
+ u64 workq;
+ int i;
+ struct fst_port_info *port;
+
+ /* Grab the queue exclusively
+ */
+ fst_dbg(DBG_TX, "fst_process_threshold_work_q\n");
+ spin_lock_irqsave(&fst_work_q_lock, flags);
+ workq = fst_threshq;
+ fst_threshq = 0;
+ spin_unlock_irqrestore(&fst_work_q_lock, flags);
+ /* Call the bottom half for each card with work waiting
+ */
+ for (i = 0; i < FST_MAX_CARDS; i++) {
+ if (workq & 0x01) {
+ if (fst_cards_list[i] != NULL) {
+ port = fst_cards_list[i]->ports[0];
+ fst_dbg(DBG_TX,
+ "Calling issue command to set signals on %d\n",
+ i);
+ if (port->usb_config.flow_control ==
+ COM_FLOW_CONTROL_RTSCTS)
+ fst_issue_cmd(port, SETV24O);
+ if (port->usb_config.flow_control ==
+ COM_FLOW_CONTROL_XONXOFF) {
+ if (port->v24OpSts & OPSTS_RTS)
+ fst_issue_cmd(port, SETXON);
+ else
+ fst_issue_cmd(port, SETXOFF);
+ }
+ }
+ }
+ workq = workq >> 1;
+ }
+}
+
+static void fst_process_port_stats_work_q(struct work_struct *work)
+{
+ unsigned long flags;
+ u64 workq;
+ int i;
+ struct fst_port_info *port;
+
+ /* Grab the queue exclusively
+ */
+ fst_dbg(DBG_TX, "fst_process_port_stats_work_q\n");
+ spin_lock_irqsave(&fst_work_q_lock, flags);
+ workq = fst_pstatsq;
+ fst_pstatsq = 0;
+ spin_unlock_irqrestore(&fst_work_q_lock, flags);
+ /* Call the bottom half for each card with work waiting
+ */
+ for (i = 0; i < FST_MAX_CARDS; i++) {
+ if (workq & 0x01) {
+ if (fst_cards_list[i] != NULL) {
+ port = fst_cards_list[i]->ports[0];
+ fst_dbg(DBG_TX,
+ "Calling issue command to get port stats on %d\n",
+ i);
+ fst_issue_cmd(port, GETPORTSTATS);
+ fst_dbg(DBG_TX,
+ "Calling issue command to reset port stats on %d\n",
+ i);
+ fst_issue_cmd(port, RESETPORTSTATS);
+ }
+ }
+ workq = workq >> 1;
+ }
+}
+
+static void fst_process_tx_work_q(unsigned long work_q)
+{
+ unsigned long flags;
+ u64 work_txq;
+ int i;
+
+ /* Grab the queue exclusively
+ */
+ fst_dbg(DBG_TX, "fst_process_tx_work_q\n");
+ spin_lock_irqsave(&fst_work_q_lock, flags);
+ work_txq = fst_work_txq;
+ fst_work_txq = 0;
+ spin_unlock_irqrestore(&fst_work_q_lock, flags);
+
+ /* Call the bottom half for each card with work waiting
+ */
+ for (i = 0; i < FST_MAX_CARDS; i++) {
+ if (work_txq & 0x01) {
+ if (fst_cards_list[i] != NULL) {
+ fst_dbg(DBG_TX, "Calling tx bh for card %d\n",
+ i);
+ do_bottom_half_tx(fst_cards_list[i]);
+ }
+ }
+ work_txq = work_txq >> 1;
+ }
+}
+
+static void fst_process_int_work_q(unsigned long work_q)
+{
+ unsigned long flags;
+ u64 work_intq;
+ int i;
+
+ /* Grab the queue exclusively
+ */
+ fst_dbg(DBG_INTR, "fst_process_int_work_q\n");
+ spin_lock_irqsave(&fst_work_q_lock, flags);
+ work_intq = fst_work_intq;
+ fst_work_intq = 0;
+ spin_unlock_irqrestore(&fst_work_q_lock, flags);
+
+ /* Call the bottom half for each card with work waiting
+ */
+ for (i = 0; i < FST_MAX_CARDS; i++) {
+ if (work_intq & 0x01) {
+ if (fst_cards_list[i] != NULL) {
+ fst_dbg(DBG_INTR,
+ "Calling rx & tx bh for card %d\n", i);
+ do_bottom_half_rx(fst_cards_list[i]);
+ /* Calling the tx BH directly could mean that
+ * it runs twice concurrently. So we have to
+ * schedule it
+ */
+ fst_q_work_item(&fst_work_txq, i);
+ tasklet_schedule(&fst_tx_task);
+ }
+ }
+ work_intq = work_intq >> 1;
+ }
+}
+
+static int fst_proc_info(struct seq_file *m, void *v)
+{
+ struct seq_file *buffer = m;
+ struct fst_card_info *card;
+ int c;
+ int port_count = 0;
+
+ seq_printf(buffer,
+ "FarSync Flex %s Driver version %s - Patch Level %s - Build %s\n",
+ FST_DRIVER_TYPE, FST_USER_VERSION, FST_PATCH_LEVEL,
+ FST_ADDITIONAL);
+ seq_printf(buffer, "%d Cards found\n", fst_ncards);
+ for (c = 0; c < FST_MAX_CARDS; c++) {
+ card = fst_cards_list[c];
+ if (card) {
+ seq_printf(buffer,
+ "\t%s-%s: %s (%s),\t%d ports, State: %s\n",
+ port_to_dev(card->ports[0])->name,
+ port_to_dev(card->
+ ports[card->nports - 1])->name,
+ type_strings[card->type],
+ card->ports[0]->usb_card_info.sz_serial_no,
+ card->nports, state_strings[card->state]);
+ port_count += card->nports;
+ }
+ }
+ seq_printf(buffer, "Total number of ports = %d\n", port_count);
+ seq_printf(buffer, "Total number of async connects = %d\n",
+ fst_tty_cnt);
+ return 0;
+}
+
+/* Process the result of trying to pass a recieved frame up the stack
+ */
+static void fst_process_rx_status(int rx_status, char *name)
+{
+ switch (rx_status) {
+ case NET_RX_SUCCESS:
+ {
+ /* Nothing to do here */
+ break;
+ }
+
+ case NET_RX_DROP:
+ {
+ pr_err("%s: Received packet dropped\n", name);
+ break;
+ }
+ }
+}
+
+static int gen_mon_packet(struct sk_buff *skb, struct fst_port_info *port,
+ int tx_rx_ind)
+{
+ /* We need to duplicate the skb and send it up to
+ * the monitor application
+ */
+ struct sk_buff *mon_skb;
+ int rx_status = NET_RX_SUCCESS;
+ struct fstioc_mon header;
+
+ /* Allocate SKB. Length of frame to monitor + length of the
+ * header we prepend.
+ */
+ mon_skb = dev_alloc_skb(skb->len + sizeof(struct fstioc_mon));
+ if (mon_skb == NULL) {
+ fst_dbg(DBG_INTR, "gen_mon_packet: can't allocate skb\n");
+ return 1;
+ }
+
+ /* Setup the header
+ */
+ header.version = FSTIOC_MON_VERSION;
+ header.tx_rx_ind = tx_rx_ind;
+ header.sequence = port->sequence++;
+ header.timestamp = jiffies * 1000 / HZ;
+ header.length = skb->len;
+
+ memcpy(skb_put(mon_skb, sizeof(struct fstioc_mon)), &header,
+ sizeof(struct fstioc_mon));
+
+ /* Push upstream */
+ fst_dbg(DBG_INTR, "Pushing monitor frame up the stack\n");
+ mon_skb->protocol = htons(ETH_P_DIAG);
+ mon_skb->pkt_type = PACKET_HOST;
+ skb_reset_mac_header(mon_skb);
+ mon_skb->dev = port_to_dev(port);
+ memcpy(skb_put(mon_skb, skb->len), skb->data, skb->len);
+ fst_dbg(DBG_INTR, "Monitor packet length is %d\n", mon_skb->len);
+ if (port->run)
+ rx_status = netif_rx(mon_skb);
+ else {
+ dev_kfree_skb(mon_skb);
+ fst_dbg(DBG_ASS, "Discarding monitor frame, port closed\n");
+ }
+ fst_process_rx_status(rx_status, port_to_dev(port)->name);
+ if (rx_status == NET_RX_DROP) {
+ fst_dbg(DBG_ASS, "Could not deleiver monitor message");
+ return 1;
+ }
+ return 0;
+}
+
+static void
+fst_notify_state_change(struct fst_card_info *card, struct fst_port_info *port)
+{
+ struct sk_buff *skb;
+ int rx_status = NET_RX_SUCCESS;
+
+ fst_dbg(DBG_INTR, "fst_notify_state_change: %s\n",
+ port_to_dev(port)->name);
+ if (port->notify_mode) {
+ /*
+ * Notify mode is on. Basic or Extended?
+ */
+ if (port->notify_mode == FST_NOTIFY_EXTENDED) {
+ /*
+ * Just signal select that something has happened
+ */
+ port->exception++;
+ wake_up_interruptible(&port->pollq);
+ return;
+ }
+ /* Basic mode is send up a zero length message
+ * Note that we can't do this for the async interface
+ * How do you write a zero length message into a fifo?
+ */
+ if (port->mode == FST_MODE_ASYNC)
+ return;
+ /*
+ * Otherwise proceed. Allocate the SKB
+ */
+ skb = dev_alloc_skb(0);
+ if (skb == NULL) {
+ fst_dbg(DBG_INTR,
+ "notify_state_change: can't allocate skb\n");
+ }
+
+ /* We know the length we need to receive, = 0
+ */
+ skb_put(skb, 0);
+
+ /* Push upstream */
+ fst_dbg(DBG_INTR, "Pushing frame up the stack\n");
+ if (port->proto == FST_HDLC || port->proto == FST_PPP) {
+ /* Mark for further processing by sPPP module */
+ skb->protocol = htons(ETH_P_WAN_PPP);
+ } else {
+ /* DEC customer specific protocol (since nothing
+ * defined for marking raw data), at least one other
+ * driver uses this value for this purpose.
+ */
+ skb->protocol = htons(ETH_P_CUST);
+ skb->pkt_type = PACKET_HOST;
+ }
+ skb_reset_mac_header(skb);
+ skb->dev = port_to_dev(port);
+ if (port->char_file) {
+ st_fst_tty_area *fst_tty;
+ fst_tty = &fst_tty_area[port->minor_dev_no];
+ fst_tty->skb = skb;
+
+ schedule_work(&(fst_tty->fst_tty_work));
+ } else {
+ if (port->run) {
+ rx_status = netif_rx(skb);
+ } else {
+ dev_kfree_skb(skb);
+ fst_dbg(DBG_ASS,
+ "Discarding state change message, port closed\n");
+ }
+ fst_process_rx_status(rx_status,
+ port_to_dev(port)->name);
+ }
+ if (rx_status == NET_RX_DROP) {
+ fst_dbg(DBG_ASS,
+ "Could not deliver state change message");
+ }
+ } else {
+ fst_dbg(DBG_INTR, "Notify mode not on\n");
+ }
+
+}
+
+#define CMD_BUF_LEN 4
+/* Issue a Mailbox command for a port.
+ * Note we issue them on a fire and forget basis, not expecting to see an
+ * error and not waiting for completion.
+ */
+static void fst_issue_cmd(struct fst_port_info *port, unsigned short cmd)
+{
+ struct fst_card_info *usb_dev;
+ int retval;
+ char sigs[CMD_BUF_LEN];
+
+ usb_dev = port->card;
+ switch (cmd) {
+ case STARTPORT:
+ {
+ fst_dbg(DBG_CMD, "Sending start port\n");
+ retval = usb_control_msg(usb_dev->udev,
+ usb_sndctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_START_PORT, 0x40, 0, 0,
+ NULL, 0, 10000);
+ if (retval == 0) {
+ fst_dbg(DBG_CMD,
+ "Start port response received\n");
+ } else {
+ fst_dbg(DBG_ASS,
+ "Start port error errno = %d\n",
+ retval);
+ }
+ break;
+ }
+
+ case STOPPORT:
+ {
+ fst_dbg(DBG_CMD, "Sending stop port\n");
+ retval = usb_control_msg(usb_dev->udev,
+ usb_sndctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_STOP_PORT, 0x40, 0, 0,
+ NULL, 0, 10000);
+ if (retval == 0) {
+ fst_dbg(DBG_CMD,
+ "Stop port response received\n");
+ } else {
+ fst_dbg(DBG_ASS,
+ "Stop port error errno = %d\n",
+ retval);
+ }
+ break;
+ }
+ case ASSERTSIGNALS:
+ {
+ sigs[0] = 0x03;
+ sigs[1] = 0x00;
+ sigs[2] = 0x00;
+ sigs[3] = 0x00;
+
+ fst_dbg(DBG_CMD, "Raising control signals\n");
+
+ retval = usb_control_msg(usb_dev->udev,
+ usb_sndctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_SET_SIGNALS, 0x40, 0,
+ 0, &sigs, CMD_BUF_LEN, 10000);
+
+ if (retval == CMD_BUF_LEN) {
+ fst_dbg(DBG_CMD,
+ "Set signals up response received\n");
+ } else {
+ fst_dbg(DBG_ASS,
+ "Set signals up error errno = %d\n",
+ retval);
+ }
+ port->v24OpSts = 0x3;
+ break;
+ }
+ case RELEASESIGNALS:
+ {
+ sigs[0] = 0x00;
+ sigs[1] = 0x00;
+ sigs[2] = 0x00;
+ sigs[3] = 0x00;
+ fst_dbg(DBG_CMD, "Lowering control signals\n");
+ retval = usb_control_msg(usb_dev->udev,
+ usb_sndctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_SET_SIGNALS, 0x40, 0,
+ 0, &sigs, CMD_BUF_LEN, 10000);
+
+ if (retval == CMD_BUF_LEN) {
+ fst_dbg(DBG_CMD,
+ "Set signals down response received\n");
+ } else {
+ fst_dbg(DBG_ASS,
+ "Set signals down error errno = %d\n",
+ retval);
+ }
+ port->v24OpSts = 0;
+ break;
+ }
+ case GETSIGNALS:
+ {
+ int data = 0;
+ fst_dbg(DBG_CMD, "Getting control signals\n");
+ retval = usb_control_msg(usb_dev->udev,
+ usb_rcvctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_GET_SIGNALS, 0xC0, 0,
+ 0, &data, 4, 10000);
+ le32_to_cpus(&data);
+ if (retval == 4) {
+ fst_dbg(DBG_CMD,
+ "Get signals response received %x\n",
+ data);
+ port->v24IpSts = data & 0x0f;
+ } else {
+ fst_dbg(DBG_ASS,
+ "Get signals error errno = %d\n",
+ retval);
+ }
+ break;
+ }
+ case SETSIGNALS:
+ {
+ int data = port->v24OpSts;
+
+ fst_dbg(DBG_CMD, "Setting control signals\n");
+ cpu_to_le32s(&data);
+ retval = usb_control_msg(usb_dev->udev,
+ usb_sndctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_SET_SIGNALS, 0x40, 0,
+ 0, &data, CMD_BUF_LEN, 10000);
+ le32_to_cpus(&data);
+ if (retval == CMD_BUF_LEN) {
+ fst_dbg(DBG_CMD,
+ "Set signals response received\n");
+ } else {
+ fst_dbg(DBG_ASS,
+ "Set signals up error errno = %d\n",
+ retval);
+ }
+ break;
+ }
+
+ case RECONFIG:
+ {
+ fst_dbg(DBG_CMD, "%s: Sending Reconfig async port\n",
+ port->dev->name);
+ }
+ break;
+
+ case FLUSHBUFFERS:
+ {
+ int data = 0x03;
+ fst_dbg(DBG_CMD, "%s: Sending flush buffer\n",
+ port->dev->name);
+
+ cpu_to_le32s(&data);
+ retval = usb_control_msg(usb_dev->udev,
+ usb_sndctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_FLUSH_BUFFERS, 0x40, 0,
+ 0, &data, CMD_BUF_LEN, 10000);
+ le32_to_cpus(&data);
+ if (retval == CMD_BUF_LEN) {
+ fst_dbg(DBG_CMD,
+ "Flush buffers response received\n");
+ } else {
+ fst_dbg(DBG_ASS,
+ "Flush Buffers error errno = %d\n",
+ retval);
+ }
+ }
+ break;
+
+ case SETPRESERVE:
+ {
+ int data = 1;
+
+ fst_dbg(DBG_CMD, "Setting preserve control signals\n");
+ cpu_to_le32s(&data);
+ retval = usb_control_msg(usb_dev->udev,
+ usb_sndctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_SET_PRESERVE_SIGNALS,
+ 0x40, 0, 0, &data, CMD_BUF_LEN,
+ 10000);
+ le32_to_cpus(&data);
+ if (retval == CMD_BUF_LEN) {
+ fst_dbg(DBG_CMD,
+ "Set Preserve Signals response received\n");
+ } else {
+ fst_dbg(DBG_ASS,
+ "Set Preserve Signals up error errno = %d\n",
+ retval);
+ }
+ }
+ break;
+
+ case GETPRESERVE:
+ {
+ int data = 0;
+ fst_dbg(DBG_CMD, "Getting Preserve Signals\n");
+ cpu_to_le32s(&data);
+ retval = usb_control_msg(usb_dev->udev,
+ usb_rcvctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_GET_PRESERVE_SIGNALS,
+ 0xC0, 0, 0, &data, 4, 10000);
+ le32_to_cpus(&data);
+ if (retval == 4) {
+ fst_dbg(DBG_CMD,
+ "Get Preserve Signals response received %x\n",
+ data);
+ if (data)
+ port->v24OpSts |= 0x0100;
+ else
+ port->v24OpSts &= !0x0100;
+ } else {
+ fst_dbg(DBG_ASS,
+ "Get Preserve Signals error errno = %d\n",
+ retval);
+ }
+ }
+ break;
+
+ case SETXON:
+ {
+ fst_dbg(DBG_CMD, "%s: Sending XON Command\n",
+ port->dev->name);
+
+ retval = usb_control_msg(usb_dev->udev,
+ usb_sndctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_SEND_XON, 0x40, 0, 0,
+ NULL, 0, 10000);
+ if (retval == 0) {
+ fst_dbg(DBG_CMD, "XON response received\n");
+ } else {
+ fst_dbg(DBG_ASS, "Set XON error errno = %d\n",
+ retval);
+ }
+ }
+ break;
+
+ case SETXOFF:
+ {
+ fst_dbg(DBG_CMD, "%s: Sending XOFF Command\n",
+ port->dev->name);
+
+ retval = usb_control_msg(usb_dev->udev,
+ usb_sndctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_SEND_XOFF, 0x40, 0, 0,
+ NULL, 0, 10000);
+ if (retval == 0) {
+ fst_dbg(DBG_CMD, "XOFF response received\n");
+ } else {
+ fst_dbg(DBG_ASS, "Set XOFF error errno = %d\n",
+ retval);
+ }
+ }
+ break;
+
+ case GETPORTSTATS:
+ {
+ IREX stats;
+ unsigned char tx_status = 0;
+ unsigned char rx_status = 0;
+
+ fst_dbg(DBG_CMD, "%s: Sending GETPORTSTATS Command\n",
+ port->dev->name);
+ flex_irex_to_le(&stats);
+ retval = usb_control_msg(usb_dev->udev,
+ usb_rcvctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_GET_PORT_STATS, 0xC0,
+ 0, 0, &stats, sizeof(IREX),
+ 10000);
+ flex_irex_to_cpu(&stats);
+ if (retval == sizeof(IREX)) {
+ fst_dbg(DBG_CMD,
+ "GETPORTSTATS response received\n");
+ fst_dbg(DBG_CMD, "Status count = %d\n",
+ stats.interface_record.status_count);
+ fst_dbg(DBG_CMD, "v24_in = %x ",
+ stats.interface_record.v24_in);
+ fst_dbg(DBG_CMD, "v24_out = %x\n",
+ stats.interface_record.v24_out);
+ if (stats.interface_record.status_array[0]) {
+ fst_dbg(DBG_CMD, "%s: Rx CRC\n",
+ port->dev->name);
+ rx_status |= RX_CRC;
+ }
+ if (stats.interface_record.status_array[1]) {
+ fst_dbg(DBG_CMD, "%s: Rx Overflow\n",
+ port->dev->name);
+ rx_status |= RX_OFLO;
+ }
+ if (stats.interface_record.status_array[2]) {
+ fst_dbg(DBG_CMD,
+ "%s: Rx frame less than 4 bytes\n",
+ port->dev->name);
+ rx_status |= RX_FRAM;
+ }
+ if (stats.interface_record.status_array[3]) {
+ fst_dbg(DBG_CMD,
+ "%s: Rx ending on non octet boundary\n",
+ port->dev->name);
+ rx_status |= RX_FRAM;
+ }
+ if (stats.interface_record.status_array[4]) {
+ fst_dbg(DBG_CMD,
+ "%s: Rx Aborted frame\n",
+ port->dev->name);
+ rx_status |= RX_ABRT;
+ }
+ if (stats.interface_record.status_array[5]) {
+ fst_dbg(DBG_CMD,
+ "%s: Tranmitter Interrupt underrun\n",
+ port->dev->name);
+ tx_status |= TX_URUN;
+ }
+ if (stats.interface_record.status_array[6]) {
+ fst_dbg(DBG_CMD,
+ "%s: Reciever Interrupt overrun\n",
+ port->dev->name);
+ rx_status |= RX_OFLO;
+ }
+ if (stats.interface_record.status_array[7]) {
+ fst_dbg(DBG_CMD,
+ "%s: DCD lost during frame reception\n",
+ port->dev->name);
+ }
+ if (stats.interface_record.status_array[8]) {
+ fst_dbg(DBG_CMD,
+ "%s: CTS lost while transmitting\n",
+ port->dev->name);
+ }
+ if (stats.interface_record.status_array[9]) {
+ fst_dbg(DBG_CMD, "%s: DSR dropped\n",
+ port->dev->name);
+ }
+ if (stats.interface_record.status_array[10]) {
+ fst_dbg(DBG_CMD,
+ "%s: Hardware Adapter Failure\n",
+ port->dev->name);
+ }
+ if (rx_status)
+ fst_log_rx_error(usb_dev, port,
+ rx_status);
+ if (tx_status)
+ fst_log_tx_error(usb_dev, port,
+ tx_status);
+ } else {
+ fst_dbg(DBG_CMD,
+ "Set GETPORTSTATS error errno = %d\n",
+ retval);
+ }
+ }
+ break;
+
+ case RESETPORTSTATS:
+ {
+ fst_dbg(DBG_CMD,
+ "%s: Sending RESETPORTSTATS Command\n",
+ port->dev->name);
+
+ retval = usb_control_msg(usb_dev->udev,
+ usb_sndctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_RESET_PORT_STATS, 0x40,
+ 0, 0, NULL, 0, 10000);
+ if (retval == 0) {
+ fst_dbg(DBG_CMD,
+ "RESETPORTSTATS response received\n");
+ } else {
+ fst_dbg(DBG_CMD,
+ "Set RESETPORTSTATS error errno = %d\n",
+ retval);
+ }
+ }
+ break;
+
+ case SETLINESPEED:
+ {
+ fst_dbg(DBG_CMD, "%s: Setting the line speed\n",
+ port->dev->name);
+
+ retval = usb_control_msg(usb_dev->udev,
+ usb_sndctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_SET_LINESPEED, 0x40,
+ 0, 0,
+ &port->usb_config.line_speed,
+ 4, 10000);
+ if (retval == 0) {
+ fst_dbg(DBG_CMD,
+ "set LINESPEED response received\n");
+ } else {
+ fst_dbg(DBG_ASS,
+ "Set LINESPEED error errno = %d\n",
+ retval);
+ }
+ }
+ break;
+
+ default:
+ pr_err("Invalid Isuue Command %d\n", cmd);
+ }
+}
+
+/* Port output signals control
+ */
+static inline void
+fst_op_raise(struct fst_port_info *port, unsigned int outputs)
+{
+ outputs |= port->v24OpSts;
+ port->v24OpSts = outputs;
+ if (port->run)
+ fst_issue_cmd(port, ASSERTSIGNALS);
+ /* If we are in ignore carrier mode, then now is the
+ * time to tell the network layer that the carrier is on
+ */
+ if (port->ignore_carrier) {
+ if (!netif_carrier_ok(port_to_dev(port))) {
+ fst_dbg(DBG_OPEN, "Net carrier on\n");
+ netif_carrier_on(port_to_dev(port));
+ }
+ }
+}
+
+static inline void
+fst_op_lower(struct fst_port_info *port, unsigned int outputs)
+{
+ int preserve = port->v24OpSts & 0x100;
+
+ fst_dbg(DBG_ASS, "Preserve signals = %d\n", preserve);
+ outputs = ~(outputs & port->v24OpSts);
+ port->v24OpSts = outputs & 0x03;
+ if (preserve)
+ fst_dbg(DBG_ASS, "%s: Leaving signals alone\n",
+ port->dev->name);
+ else {
+ /* Need to lower control signals here, if we are not
+ * in the preserve signals mode
+ */
+ fst_dbg(DBG_ASS, "%s: Lowering signals\n", port->dev->name);
+ fst_issue_cmd(port, RELEASESIGNALS);
+
+ /* If we are in ignore carrier mode, then now is the
+ * time to tell the network layer that carrier is off
+ */
+ if (port->ignore_carrier)
+ netif_carrier_off(port_to_dev(port));
+ }
+}
+
+/* Setup port Rx buffers */
+static void fst_rx_config(struct fst_port_info *port)
+{
+ int pi;
+ struct fst_card_info *card;
+
+ pi = port->index;
+ card = port->card;
+}
+
+/* Setup port Tx buffers */
+static void fst_tx_config(struct fst_port_info *port)
+{
+ int pi;
+ struct fst_card_info *card;
+
+ pi = port->index;
+ card = port->card;
+}
+
+/* Check that the shared memory configuration is one that we can handle
+ * and that some basic parameters are correct
+ */
+static void check_started_ok(struct fst_card_info *card)
+{
+ return;
+
+}
+
+static void do_bottom_half_tx(struct fst_card_info *card)
+{
+ struct fst_port_info *port;
+ int retval = 0;
+ struct urb *urb = NULL;
+ char *buf = NULL;
+ size_t writesize;
+ int pi;
+ int txq_length;
+ struct sk_buff *skb;
+ unsigned long flags;
+ int tx_length;
+ __u16 *len_ptr;
+
+ /* Find a free buffer for the transmit
+ * Step through each port on this card
+ */
+
+ fst_dbg(DBG_TX, "do_bottom_half_tx\n");
+ for (pi = 0; pi < card->nports; pi++) {
+ port = card->ports[pi];
+ if ((port->dev == NULL) || (!port->run))
+ continue;
+
+ if (card->tx_urb_cnt <= 0) {
+ fst_dbg(DBG_TX,
+ "Max URBs in progress limit reached\n");
+ return;
+ }
+
+ spin_lock_irqsave(&card->card_lock, flags);
+ txq_length = port->txqe - port->txqs;
+ if (txq_length < 0) {
+ /* This is the case where one has wrapped and the
+ * maths gives us a negative number
+ */
+ txq_length = txq_length + FST_TXQ_DEPTH;
+ }
+ spin_unlock_irqrestore(&card->card_lock, flags);
+ if (txq_length > 0) {
+ /* There is something to send
+ */
+ skb = port->txq[port->txqs].frame;
+
+ /* Decrement the segment count. It tells us how
+ * many more bits to the frame there are left to do
+ */
+ port->txq[port->txqs].segment_cnt--;
+ tx_length = skb->len;
+ port->txq[port->txqs].current_seg++;
+
+ writesize =
+ min_t(size_t, (size_t) tx_length + 8,
+ (size_t) port->tx_buffer_size + 8);
+ /* copy the data and set the required indicators on the
+ * card.
+ */
+
+ /* verify that we actually have some data to write */
+ if (tx_length == 0)
+ goto exit;
+
+ urb = card->tx_bulk_urb;
+ buf = card->bulk_out_buffer;
+
+ memcpy(&buf[8], skb->data, tx_length);
+ /* If we need to reverse the bits, this is where we
+ * do it
+ */
+ if (port->transmit_msb_first)
+ fst_reverse_bits(&buf[8], tx_length);
+ /* Set the length in the pre-able
+ */
+ len_ptr = (__u16 *) &buf[4];
+ *len_ptr = cpu_to_le16((__u16) tx_length);
+ /* initialize the urb properly */
+ usb_fill_bulk_urb(urb, card->udev,
+ usb_sndbulkpipe(card->udev,
+ card->bulk_out_endpoint_addr),
+ buf, writesize,
+ sync_write_bulk_callback, card);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* send the data out the bulk port */
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval) {
+ pr_err("%s - failed submitting , ", __func__);
+ pr_err("write urb error %d", retval);
+ port->txqs++;
+ if (port->txqs == FST_TXQ_DEPTH)
+ port->txqs = 0;
+ goto exit;
+ } else {
+ card->tx_urb_cnt--;
+ }
+ wake_up_interruptible(&port->writeq);
+ spin_lock_irqsave(&card->card_lock, flags);
+ port->txqs++;
+ if (port->txqs == FST_TXQ_DEPTH)
+ port->txqs = 0;
+ if (port->txqs == port->txqe) {
+ fst_dbg(DBG_TX,
+ "Card %d Port %d: Tx queue Now empty\n",
+ card->card_no, port->index);
+ fst_notify_state_change(card, port);
+ wake_up_interruptible(&port->pollq);
+ }
+ spin_unlock_irqrestore(&card->card_lock, flags);
+ /* If we have flow control on, can we now release it?
+ */
+ if (port->start) {
+ if (txq_length < fst_txq_low) {
+ fst_dbg(DBG_ASS,
+ "%s: Start the network layer\n",
+ port->dev->name);
+ if (!port->char_file) {
+ netif_wake_queue(port->dev);
+ } else {
+ st_fst_tty_area *fst_tty;
+ fst_tty =
+ &fst_tty_area[port->index];
+ fst_dbg(DBG_TTY,
+ "%s: Waking up tty writes\n",
+ fst_tty->name);
+ tty_wakeup(fst_tty->tty);
+ }
+ port->start = 0;
+ }
+ }
+exit:
+ dev_kfree_skb(skb);
+ return;
+ } else {
+ /* Nothing to send so break out of the while loop
+ */
+ break;
+ }
+ if (++port->txpos >= port->num_tx_buffers)
+ port->txpos = 0;
+ }
+ return;
+}
+
+static void do_bottom_half_rx(struct fst_card_info *card)
+{
+ fst_dbg(DBG_ASS, "Do bottom half rx\n");
+ return;
+}
+
+static int check_combination(int num, int size)
+{
+ /* Check that the number and size do not exceed the following table
+ *
+ * num of buffs max size
+ * 2 32K
+ * 4 16K
+ * 8 8K
+ * 16 4K
+ * 32 2K
+ * 64 1K
+ * 128 0.5K
+ */
+ int err = 0;
+ switch (num) {
+ case 2:
+ {
+ if (size > 32 * 1024)
+ err++;
+ break;
+ }
+ case 4:
+ {
+ if (size > 16 * 1024)
+ err++;
+ break;
+ }
+ case 8:
+ {
+ if (size > 8 * 1024)
+ err++;
+ break;
+ }
+ case 16:
+ {
+ if (size > 4 * 1024)
+ err++;
+ break;
+ }
+ case 32:
+ {
+ if (size > 2 * 1024)
+ err++;
+ break;
+ }
+ case 64:
+ {
+ if (size > 1 * 1024)
+ err++;
+ break;
+ }
+ case 128:
+ {
+ if (size > 1024 / 2)
+ err++;
+ break;
+ }
+ default:
+ {
+ pr_err("Number of buffers must be 2,4,8,16,32,64, ");
+ pr_err("or 128 : %d\n",
+ num);
+ err++;
+ }
+ }
+ if (err)
+ return 1;
+ return 0;
+}
+
+static int validate_buffer_config(struct fstioc_info *info)
+{
+ /* We need some checks on buffer configurations because the
+ * card itself doesn't do any
+ */
+ if ((info->num_tx_buffers == 0) || (info->num_rx_buffers == 0)) {
+ pr_err("Num Tx Buffers or Num Rx Buffers is zero\n");
+ return 1;
+ }
+ if ((info->num_tx_buffers == 1) || (info->num_rx_buffers == 1)) {
+ pr_err("Num Tx Buffers or Num Rx Buffers is 1\n");
+ return 1;
+ }
+ if ((info->num_tx_buffers > 128) || (info->num_rx_buffers > 128)) {
+ pr_err("Num Tx Buffers or Num Rx Buffers > 128\n");
+ return 1;
+ }
+ if ((info->tx_buffer_size == 0) || (info->rx_buffer_size == 0)) {
+ pr_err("Tx Buffer Size or Rx Buffer Size is zero\n");
+ return 1;
+ }
+ if ((info->tx_buffer_size > 32 * 1024)
+ || (info->rx_buffer_size > 32 * 1024)) {
+ pr_err("Tx Buffer Size or Rx Buffer Size > 32*1024\n");
+ return 1;
+ }
+ if (check_combination(info->num_tx_buffers, info->tx_buffer_size)) {
+ pr_err("Invalid num/size combination on Tx Buffers\n");
+ return 1;
+ }
+ if (check_combination(info->num_rx_buffers, info->rx_buffer_size)) {
+ pr_err("Invalid num/size combination on Rx Buffers\n");
+ return 1;
+ }
+ return 0;
+}
+
+static int purge_tx_queue(struct fst_card_info *card,
+ struct fst_port_info *port)
+{
+ int txq_length;
+ int i;
+ int start;
+ unsigned long flags;
+
+ /* Purge all frames on the tx queue
+ */
+
+ fst_dbg(DBG_TX, "Purging all frame from the tx queue\n");
+ /* Is there anything there?
+ */
+ spin_lock_irqsave(&card->card_lock, flags);
+ start = port->txqs;
+ txq_length = port->txqe - port->txqs;
+ if (txq_length < 0) {
+ /*This is the case where the next free has wrapped but the
+ * last used hasn't
+ */
+ txq_length = txq_length + FST_TXQ_DEPTH;
+ }
+ port->txqs = port->txqe;
+ spin_unlock_irqrestore(&card->card_lock, flags);
+ if (txq_length == 0)
+ return 0; /* Nothing there */
+ /* de-queue the buffer's and deallocate the resources
+ */
+ pr_info("%s: Purging %d frames from the transmit queue\n",
+ port->dev->name, txq_length);
+ for (i = 0; i < txq_length; i++) {
+ if (port->txq[start].frame) {
+ fst_dbg(DBG_TX, "Purging buffer %d at %p\n", start,
+ port->txq[start].frame);
+ dev_kfree_skb(port->txq[start].frame);
+ port->txq[start].frame = NULL;
+ } else {
+ pr_info("txq purge found NULL pointer\n");
+ }
+ start++;
+ if (start == FST_TXQ_DEPTH)
+ start = 0;
+ }
+ return 0;
+}
+
+static int parse_custom_clock_rate_structure(struct fst_port_info *port,
+ struct fstioc_custom_rate_config
+ *custom_clock_rate)
+{
+ /* Check that the supplied buffer looks like it may contain a valid
+ * set of values
+ */
+ if (custom_clock_rate->version != FST_CUSTOM_RATE_CONFIG_VERSION) {
+ fst_dbg(DBG_ASS, "Custom clock rate structure is not the ");
+ fst_dbg(DBG_ASS, "version expected %d %d\n",
+ FST_CUSTOM_RATE_CONFIG_VERSION,
+ custom_clock_rate->version);
+ return 1;
+ }
+ if (!((custom_clock_rate->multiplier ==
+ (FST_CUSTOM_CLOCK_MULTIPLIER_1))
+ || (custom_clock_rate->multiplier ==
+ (FST_CUSTOM_CLOCK_MULTIPLIER_16)))) {
+ fst_dbg(DBG_ASS,
+ "Custom clock rate multiplier is not expected value (1 or 16) %d\n",
+ custom_clock_rate->multiplier);
+ return 2;
+ }
+ if (custom_clock_rate->clock_type > FST_CUSTOM_RATE_CLOCK_LOW_MASTER) {
+ fst_dbg(DBG_ASS,
+ "Custom clock type is not in the rage expected (0 - 2) %d\n",
+ custom_clock_rate->clock_type);
+ return 3;
+ }
+ if (strlen(custom_clock_rate->rate_info) !=
+ FST_CUSTOM_RATE_CONFIG_LENGTH - 1) {
+ fst_dbg(DBG_ASS,
+ "Custom clock rate info is not the length expected %d %d\n",
+ (int)strlen(custom_clock_rate->rate_info),
+ FST_CUSTOM_RATE_CONFIG_LENGTH - 1);
+ return 4;
+ }
+ /* Validation passed
+ */
+ return 0;
+}
+
+unsigned int to_int(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'A' && c <= 'F')
+ return 10 + c - 'A';
+ if (c >= 'a' && c <= 'f')
+ return 10 + c - 'a';
+ return -1;
+}
+
+static int strtohex(unsigned char *source, unsigned char *dest, int size)
+{
+ int count = 0;
+ int loops = 0;
+
+ loops = (int)strlen(source) / 2;
+ fst_dbg(DBG_IOCTL, "strtohex of length %d\n", loops);
+ for (count = 0; count < loops; count++) {
+ dest[count] =
+ 16 * to_int(source[2 * count]) +
+ to_int(source[2 * count + 1]);
+ }
+ dest[count] = 0xff;
+ return 0;
+}
+
+static int
+set_custom_clock_rate(struct fst_port_info *port,
+ struct fstioc_custom_rate_config *custom_clock_rate)
+{
+ struct fstioc_req sys_request;
+ char sz_rate_info[(FST_CUSTOM_RATE_CONFIG_LENGTH * 2) + 1];
+ unsigned char buf[FST_CUSTOM_RATE_CONFIG_LENGTH];
+ unsigned char checksum[16];
+ struct crypto_hash *tfm;
+ struct hash_desc desc;
+ struct scatterlist sg;
+ char sz_checksum[3];
+ char *psz_clock_type = NULL;
+ int i;
+ int retval;
+
+#define MD5_SUM_LENGTH 23
+
+ /* Prepare the string and construct the usb command and send it
+ */
+ memset(&sys_request, 0, sizeof(struct fstioc_req));
+ memset(buf, 0x00, sizeof(buf));
+ sys_request.msg_type = custom_clock_rate->multiplier;
+ sys_request.msg_len = sizeof(struct fstioc_req);
+ sys_request.i_reg_idx = port->index;
+
+ fst_dbg(DBG_IOCTL, "Supplied rate = %d and rate info is %s\n",
+ custom_clock_rate->rate, custom_clock_rate->rate_info);
+
+ switch (custom_clock_rate->clock_type) {
+ case FST_CUSTOM_RATE_CLOCK_LOW_SLAVE:
+ psz_clock_type = "0000";
+ break;
+ case FST_CUSTOM_RATE_CLOCK_LOW_MASTER:
+ psz_clock_type = "0101";
+ break;
+ case FST_CUSTOM_RATE_CLOCK_SLAVE:
+ default:
+ psz_clock_type = "0100";
+ break;
+ }
+
+ sprintf(sz_rate_info, "%08X", ntohl(custom_clock_rate->rate));
+ strcat(sz_rate_info, "0");
+ strcat(sz_rate_info, custom_clock_rate->rate_info);
+ strcat(sz_rate_info, psz_clock_type);
+ fst_dbg(DBG_IOCTL, "Temp1 ascii sz_rate_info=%s\n", sz_rate_info);
+ fst_dbg(DBG_IOCTL, "Length of string to convert is %d\n",
+ (int)strlen(sz_rate_info));
+ strtohex(sz_rate_info, buf, sizeof(buf) - 1);
+ fst_dbg(DBG_IOCTL, "Temp2 ascii sz_rate_info=%s\n", sz_rate_info);
+ fst_dbg(DBG_IOCTL, "Temp hex buf: ");
+ for (i = 0; i < sizeof(buf); i++)
+ fst_dbg(DBG_IOCTL, "%02x ", buf[i]);
+ fst_dbg(DBG_IOCTL, "\n");
+
+ fst_dbg(DBG_IOCTL, "Temp3 ascii sz_rate_info=%s\n", sz_rate_info);
+
+ /* Use the Kernel Crypto API to calculate the MD5Sum of the updated
+ * rate string
+ */
+ tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
+ desc.tfm = tfm;
+ desc.flags = 0;
+ crypto_hash_init(&desc);
+ sg_init_one(&sg, buf, MD5_SUM_LENGTH);
+ crypto_hash_update(&desc, &sg, MD5_SUM_LENGTH);
+ memset(checksum, 0, sizeof(checksum));
+ crypto_hash_final(&desc, checksum);
+ fst_dbg(DBG_IOCTL, "Checksum: ");
+ for (i = 0; i < sizeof(checksum); i++)
+ fst_dbg(DBG_IOCTL, "%02x ", checksum[i]);
+ fst_dbg(DBG_IOCTL, "\n");
+
+ sprintf(sz_checksum, "%02X", checksum[0]);
+ strcat(sz_rate_info, sz_checksum);
+ fst_dbg(DBG_IOCTL, "Temp7 ascii sz_rate_info=%s\n", sz_rate_info);
+ strtohex(sz_rate_info, sys_request.u_msg,
+ FST_CUSTOM_RATE_CONFIG_LENGTH);
+ crypto_free_hash(tfm);
+ /* Now we can send the request to Flex
+ */
+ flex_fstioc_req_to_le(&sys_request);
+ retval = usb_control_msg(port->card->udev,
+ usb_sndctrlpipe(port->card->udev, 0),
+ USS_CMD_SEND_SECURE_MSG, 0x40, 0, 0,
+ &sys_request, sizeof(sys_request), 10000);
+ flex_fstioc_req_to_cpu(&sys_request);
+ if (retval != sizeof(sys_request)) {
+ fst_dbg(DBG_ASS, "%s: Error in sending USB Control Message, retval is %d\n",
+ port->dev->name, retval);
+ return retval;
+ }
+ /* We are always going to wait for a reply
+ */
+ fst_dbg(DBG_IOCTL, "Waiting for a reply\n");
+ flex_fstioc_req_to_le(&sys_request);
+ retval = usb_control_msg(port->card->udev,
+ usb_rcvctrlpipe(port->card->udev, 0),
+ USS_CMD_RECV_SECURE_RSP, 0xC0, 0, 0,
+ &sys_request, sizeof(sys_request), 10000);
+ flex_fstioc_req_to_cpu(&sys_request);
+ if (retval != sizeof(sys_request)) {
+ fst_dbg(DBG_ASS, "Set Custom Clock Rate: Retval is %d\n",
+ retval);
+ return retval;
+ }
+ fst_dbg(DBG_IOCTL, "Returning from set_custom_clock_rate\n");
+ return 0;
+}
+
+static int
+fst_set_iface(struct fst_card_info *usb_dev, struct fst_port_info *port,
+ struct ifreq *ifr)
+{
+ sync_serial_settings sync;
+ int i;
+ int retval;
+
+ if (ifr->ifr_settings.size != sizeof(sync))
+ return -ENOMEM;
+ if (copy_from_user
+ (&sync, ifr->ifr_settings.ifs_ifsu.sync, sizeof(sync))) {
+ return -EFAULT;
+ }
+
+ if (sync.loopback)
+ return -EINVAL;
+
+ i = port->index;
+
+ switch (ifr->ifr_settings.type) {
+ case IF_IFACE_V35:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_V35;
+ port->hwif = V35;
+ break;
+
+ case IF_IFACE_V24:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_V24;
+ port->hwif = V24;
+ break;
+
+ case IF_IFACE_X21:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_X21;
+ port->hwif = X21;
+ break;
+
+ case IF_IFACE_X21D:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_X21D;
+ port->hwif = X21D;
+ break;
+
+ case IF_IFACE_RS530_449:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_RS530;
+ port->hwif = RS530_449;
+ break;
+
+ case IF_IFACE_RS485:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_RS485;
+ port->hwif = RS485;
+ break;
+
+ case IF_IFACE_RS485_FDX:
+ usb_dev->ports[0]->usb_config.iface =
+ FSCMN_INTERFACE_RS485_FDX;
+ port->hwif = RS485_FDX;
+ break;
+
+ case IF_IFACE_UX35C:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_X21;
+ port->hwif = UX35C;
+ break;
+
+ case IF_IFACE_SYNC_SERIAL:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (sync.clock_type) {
+ case CLOCK_EXT:
+ usb_dev->ports[0]->usb_config.clock = 0;
+ break;
+
+ case CLOCK_INT:
+ usb_dev->ports[0]->usb_config.clock = 1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ usb_dev->ports[0]->usb_config.line_speed = sync.clock_rate;
+ flex_serconf_to_le(&usb_dev->ports[0]->usb_config);
+ retval = usb_control_msg(usb_dev->udev, usb_sndctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_SET_CONFIG, 0x40, 0, 0,
+ &usb_dev->ports[0]->usb_config,
+ sizeof(FLEX_SERCONF), 10000);
+ flex_serconf_to_cpu(&usb_dev->ports[0]->usb_config);
+ if (retval > 0) {
+ fst_dbg(DBG_IOCTL, "Set config response received\n");
+ return 0;
+ } else {
+ fst_dbg(DBG_ASS, "Set Config error errno = %d\n", retval);
+ return -EINVAL;
+ }
+}
+
+static int
+fst_get_iface(struct fst_card_info *usb_dev, struct fst_port_info *port,
+ struct ifreq *ifr)
+{
+ sync_serial_settings sync;
+ int i;
+
+ /* First check what line type is set, we'll default to reporting X.21
+ */
+ switch (port->hwif) {
+ case V35:
+ ifr->ifr_settings.type = IF_IFACE_V35;
+ break;
+ case V24:
+ ifr->ifr_settings.type = IF_IFACE_V24;
+ break;
+ case X21D:
+ ifr->ifr_settings.type = IF_IFACE_X21D;
+ break;
+ case RS530_449:
+ ifr->ifr_settings.type = IF_IFACE_RS530_449;
+ break;
+ case RS485:
+ ifr->ifr_settings.type = IF_IFACE_RS485;
+ break;
+ case RS485_FDX:
+ ifr->ifr_settings.type = IF_IFACE_RS485_FDX;
+ break;
+ case UX35C:
+ ifr->ifr_settings.type = IF_IFACE_UX35C;
+ break;
+
+ case X21:
+ default:
+ ifr->ifr_settings.type = IF_IFACE_X21;
+ break;
+ }
+ if (ifr->ifr_settings.size == 0)
+ return 0; /* only type requested */
+ if (ifr->ifr_settings.size < sizeof(sync))
+ return -ENOMEM;
+
+ i = port->index;
+ sync.clock_rate = usb_dev->ports[0]->usb_config.line_speed;
+ /* Lucky card and linux use same encoding here */
+ sync.clock_type = usb_dev->ports[0]->usb_config.clock ==
+ INTCLK ? CLOCK_INT : CLOCK_EXT;
+ sync.loopback = 0;
+
+ if (copy_to_user(ifr->ifr_settings.ifs_ifsu.sync,
+ &sync, sizeof(sync)))
+ return -EFAULT;
+
+ ifr->ifr_settings.size = sizeof(sync);
+ return 0;
+}
+
+static int fst_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct fst_card_info *card;
+ struct fst_port_info *port;
+ struct fstioc_control_request *control_request;
+ struct fstioc_info info;
+ struct fstioc_status state;
+ struct fstioc_req sys_request;
+ struct fstioc_custom_rate_config custom_clock_rate;
+ int retval = 0;
+ int len;
+ struct fstioc_cmd my_cmd;
+ struct fstioc_char_data char_data;
+ char fstioc_info_ver;
+ char readv_mode;
+ int info_size;
+ unsigned char signals;
+ char sigs[CMD_BUF_LEN];
+ unsigned long copied = 0;
+ int speed;
+
+ fst_dbg(DBG_IOCTL, "ioctl: %x, %p\n", cmd, ifr->ifr_data);
+ port = dev_to_port(dev);
+ card = port->card;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ switch (cmd) {
+ case FSTCPURESET:
+ card->state = FST_RESET;
+ return 0;
+
+ case FSTCPURELEASE:
+ card->state = FST_STARTING;
+ return 0;
+
+ case FSTWRITE: /* Code write (download) */
+ /* First copy in the header with the length and offset of data
+ * to write
+ */
+ if (ifr->ifr_data == NULL)
+ return -EINVAL;
+ control_request =
+ kmalloc(sizeof(struct fst_card_info), GFP_KERNEL);
+ if (control_request == NULL) {
+ pr_err("%s: insufficient memory for FSTWITE data\n",
+ port->dev->name);
+ return -ENOMEM;
+ }
+ if (copy_from_user(control_request, ifr->ifr_data,
+ sizeof(struct fstioc_control_request))) {
+ kfree(control_request);
+ return -EFAULT;
+ }
+ /* Check the version of the request
+ * We only support version 1 so far
+ */
+ if (control_request->u_version != 1) {
+ fst_dbg(DBG_ASS,
+ "Invalid version number in fstwrite\n");
+ kfree(control_request);
+ return -EINVAL;
+ }
+ if (control_request->b_direction) {
+ /*
+ * To device
+ */
+ retval = usb_control_msg(card->udev,
+ usb_sndctrlpipe(card->udev,
+ 0),
+ control_request->by_request,
+ 0x40,
+ control_request->w_value,
+ control_request->w_index,
+ &control_request->data,
+ control_request->w_data_length,
+ 10000);
+ if (retval > 0) {
+ fst_dbg(DBG_IOCTL,
+ "Set control response received\n");
+ } else {
+ fst_dbg(DBG_ASS,
+ "Set Control error errno = %d\n",
+ retval);
+ }
+ } else {
+ /* From device */
+ retval = usb_control_msg(card->udev,
+ usb_rcvctrlpipe(card->udev, 0),
+ control_request->by_request,
+ 0xc0, control_request->w_value,
+ control_request->w_index,
+ &control_request->data,
+ control_request->w_data_length,
+ 10000);
+ if (retval > 0) {
+ fst_dbg(DBG_IOCTL,
+ "Get control response received\n");
+ control_request->w_data_length = retval;
+ if (copy_to_user(ifr->ifr_data,
+ control_request,
+ sizeof(struct fstioc_control_request))) {
+ kfree(control_request);
+ return -EFAULT;
+ }
+ } else {
+ fst_dbg(DBG_ASS,
+ "Get Control error errno = %d\n",
+ retval);
+ }
+ }
+ kfree(control_request);
+ return 0;
+
+ case FSTGETSHELL:
+
+ if (ifr->ifr_data == NULL)
+ return -EINVAL;
+
+ info_size = flex_get_config(card, port, &info);
+ if (info_size) {
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &info, info_size);
+ else if (copy_to_user(ifr->ifr_data,
+ &info, info_size)) {
+ return -EFAULT;
+ }
+ return 0;
+ } else {
+ return -EFAULT;
+ }
+
+ case FSTGETCONF:
+
+ /* If card has just been started check the shared memory config
+ * version and marker
+ */
+ if (card->state == FST_STARTING) {
+ check_started_ok(card);
+ }
+
+ if (card->state != FST_RUNNING)
+ return -ENODEV;
+
+ if (ifr->ifr_data == NULL)
+ return -EINVAL;
+
+ if (!access_ok(VERIFY_WRITE, ifr->ifr_data, sizeof(info))) {
+ fst_dbg(DBG_ASS,
+ "%s: fstgetconf: Can't access address %p\n",
+ dev->name, ifr->ifr_data);
+ return -EFAULT;
+ }
+ memset(&info, 0, sizeof(struct fstioc_info));
+ info_size = flex_get_config(card, port, &info);
+ if (info_size) {
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &info, info_size);
+ else if (copy_to_user(ifr->ifr_data,
+ &info, info_size)) {
+ return -EFAULT;
+ }
+ return 0;
+ } else {
+ return -EFAULT;
+ }
+
+ case FSTSETCONF:
+
+ if (card->state != FST_RUNNING) {
+ pr_err("Attempt to configure card %d in non-running",
+ card->card_no);
+ pr_err(" state (%d)\n", card->state);
+ return -EIO;
+ }
+ if (!access_ok(VERIFY_READ, ifr->ifr_data, sizeof(info))) {
+ fst_dbg(DBG_ASS,
+ "%s: fstsetconf: Can't access address %p\n",
+ dev->name, ifr->ifr_data);
+ return -EFAULT;
+ }
+ memset(&info, 0, sizeof(struct fstioc_info));
+ if (port->compat == 1)
+ memcpy(&info, ifr->ifr_data, sizeof(info));
+ else
+ retval = copy_from_user(&info, ifr->ifr_data,
+ sizeof(info));
+ if (retval)
+ return -EFAULT;
+
+ if (validate_buffer_config(&info))
+ return -EFAULT;
+
+ if (flex_set_config(card, &info))
+ return 0;
+ else
+ return -EFAULT;
+
+ case FSTSNOTIFY:
+ /* The application layer above wants to toggle the state notify
+ * mechanism.
+ */
+ if (copy_from_user
+ (&port->notify_mode, ifr->ifr_data, sizeof(int))) {
+ return -EFAULT;
+ }
+ fst_dbg(DBG_IOCTL, "%s: Setting Notify mode to %d\n",
+ port->dev->name, port->notify_mode);
+ return 0;
+
+ case FSTSETMON:
+ /* The application layer above wants to monitor tx and rx data
+ * mechanism.
+ */
+ if (copy_from_user
+ (&port->monitor_mode, ifr->ifr_data, sizeof(int))) {
+ return -EFAULT;
+ }
+ fst_dbg(DBG_IOCTL,
+ "Card %d port %d: Setting Monitor mode to %d\n",
+ card->card_no, port->index, port->monitor_mode);
+ return 0;
+
+ case FSTGSTATE:
+ /* The application layer above wants the carrier state
+ */
+ state.carrier_state = netif_carrier_ok(port->dev);
+ /* and the txq is a simple length */
+ state.txq_length = port->txqe - port->txqs;
+ if (state.txq_length < 0) {
+ /* This is the case where the next free has wrapped
+ * but the last used hasn't
+ */
+ state.txq_length = state.txq_length + FST_TXQ_DEPTH;
+ }
+ state.rxq_length = 0;
+ if (port->notify_mode == FST_NOTIFY_EXTENDED) {
+ /*
+ * Update the stats
+ */
+ memcpy(&state.stats, hdlc_stats(port->dev),
+ sizeof(struct fst_device_stats));
+ len = sizeof(struct fstioc_status);
+ } else {
+ len = FST_NOTIFY_BASIC_SIZE;
+ }
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &state, len);
+ else if (copy_to_user(ifr->ifr_data, &state, len))
+ return -EFAULT;
+ return 0;
+
+ case FSTSYSREQ:
+ /* Pass the command through to the card. If the command
+ * generates a response, wait for it. The application
+ * suspends untill the command completes.
+ */
+ fst_dbg(DBG_IOCTL, "FSTSYSREQ received\n");
+ if (copy_from_user
+ (&sys_request, ifr->ifr_data, sizeof(sys_request))) {
+ fst_dbg(DBG_ASS,
+ "Could not copy sysrequest from user\n");
+ return -EFAULT;
+ }
+ fst_dbg(DBG_IOCTL, "The request is as follows:\n");
+ fst_dbg(DBG_IOCTL, "msg_type = %x\n", sys_request.msg_type);
+ fst_dbg(DBG_IOCTL, "Sending the card the request\n");
+
+ flex_fstioc_req_to_le(&sys_request);
+ retval = usb_control_msg(card->udev,
+ usb_sndctrlpipe(card->udev, 0),
+ USS_CMD_SEND_SECURE_MSG, 0x40, 0, 0,
+ &sys_request,
+ sizeof(sys_request), 10000);
+ flex_fstioc_req_to_cpu(&sys_request);
+ if (retval != sizeof(sys_request)) {
+ fst_dbg(DBG_ASS,
+ "%s: Error in sending USB Control Message, retval is %x\n",
+ port->dev->name, retval);
+ return retval;
+ }
+ /* We are always going to wait for a reply */
+ fst_dbg(DBG_IOCTL, "Waiting for a reply\n");
+ flex_fstioc_req_to_le(&sys_request);
+ retval = usb_control_msg(card->udev,
+ usb_rcvctrlpipe(card->udev, 0),
+ USS_CMD_RECV_SECURE_RSP, 0xC0, 0, 0,
+ &sys_request,
+ sizeof(sys_request), 10000);
+ flex_fstioc_req_to_cpu(&sys_request);
+
+ if (retval != sizeof(sys_request)) {
+ fst_dbg(DBG_ASS, "fstsysreq: Retval is %d\n", retval);
+ return retval;
+ }
+ /* Copy the buffer back and we are complete */
+ if (copy_to_user(ifr->ifr_data, &sys_request,
+ sizeof(sys_request))) {
+ fst_dbg(DBG_IOCTL,
+ "Error copying data back to user\n");
+ return -EFAULT;
+ }
+ fst_dbg(DBG_IOCTL, "Returning from FSTSYSREQ\n");
+ return 0;
+
+ case SIOCWANDEV:
+ switch (ifr->ifr_settings.type) {
+ case IF_GET_IFACE:
+ return fst_get_iface(card, port, ifr);
+
+ case IF_IFACE_SYNC_SERIAL:
+ case IF_IFACE_V35:
+ case IF_IFACE_V24:
+ case IF_IFACE_X21:
+ case IF_IFACE_X21D:
+ case IF_IFACE_RS530_449:
+ case IF_IFACE_RS485:
+ case IF_IFACE_RS485_FDX:
+ case IF_IFACE_UX35C:
+ return fst_set_iface(card, port, ifr);
+
+ case IF_PROTO_RAW:
+ port->proto = FST_RAW;
+ return 0;
+
+ case IF_GET_PROTO:
+ if (port->proto == FST_RAW) {
+ ifr->ifr_settings.type = IF_PROTO_RAW;
+ return 0;
+ }
+ retval = hdlc_ioctl(dev, ifr, cmd);
+ if (retval == -EINVAL) {
+ /*
+ * Protocol not set yet
+ */
+ port->proto = FST_GEN_HDLC;
+ ifr->ifr_settings.type = FST_GEN_HDLC;
+ retval = 0;
+ }
+ return retval;
+
+ default:
+ port->proto = FST_GEN_HDLC;
+ fst_dbg(DBG_IOCTL, "Passing this type to hdlc %x\n",
+ ifr->ifr_settings.type);
+ port->hdlc_proto = ifr->ifr_settings.type;
+ return hdlc_ioctl(dev, ifr, cmd);
+ }
+
+ case FSTCMD:
+ /* A general purpose command/response mechanism
+ * First, get it.
+ */
+
+ if (port->compat == 1)
+ memcpy(&my_cmd, ifr->ifr_data, sizeof(my_cmd));
+ else if (copy_from_user(&my_cmd, ifr->ifr_data,
+ sizeof(my_cmd))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: Could not access user buffers\n");
+ return -EFAULT;
+ }
+ if (my_cmd.version != 1) {
+ fst_dbg(DBG_ASS, "fstcmd: bad version %d\n",
+ my_cmd.version);
+ return -EINVAL;
+ }
+ switch (my_cmd.command) {
+ case FSTCMD_GET_SERIAL:
+
+ if (copy_to_user(my_cmd.data_ptr,
+ card->ports[0]->
+ usb_card_info.sz_serial_no,
+ strlen(card->ports[0]->
+ usb_card_info.sz_serial_no))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy serial back\n");
+ return -EFAULT;
+ }
+ my_cmd.output_data_len =
+ strlen(card->ports[0]->usb_card_info.sz_serial_no);
+ /* And invert the status field to show we have done
+ * the command
+ */
+ my_cmd.status = ~my_cmd.status;
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd));
+ else if (copy_to_user
+ (ifr->ifr_data, &my_cmd, sizeof(my_cmd))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy status back\n");
+ return -EFAULT;
+ }
+ fst_dbg(DBG_IOCTL, "Returning serial number %s",
+ card->ports[0]->usb_card_info.sz_serial_no);
+ return 0;
+
+ case FSTCMD_SET_V24O:
+ if (my_cmd.input_data_len < 1) {
+ fst_dbg(DBG_ASS,
+ "(%s) Cannot set V24 signals , no data provided %d\n",
+ port->dev->name,
+ my_cmd.input_data_len);
+ return -EFAULT;
+ }
+ if (copy_from_user(&signals, my_cmd.data_ptr, 1)) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not set v24 signals\n");
+ return -EFAULT;
+ }
+
+ if (signals >
+ (OPSTS_RTS + OPSTS_DTR + OPSTS_DSRS + OPSTS_SS +
+ OPSTS_LL + OPSTS_DCD + OPSTS_RL)) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: v24 signals not valid %d\n",
+ signals);
+ return -EFAULT;
+ }
+ sigs[0] = signals;
+ sigs[1] = 0x00;
+ sigs[2] = 0x00;
+ sigs[3] = 0x00;
+ fst_dbg(DBG_IOCTL,
+ "Setting V24 Signals %x\n", sigs[0]);
+ retval = usb_control_msg(card->udev,
+ usb_sndctrlpipe(card->udev,
+ 0),
+ USS_CMD_SET_SIGNALS, 0x40, 0,
+ 0, &sigs, CMD_BUF_LEN, 10000);
+ if (retval == CMD_BUF_LEN) {
+ fst_dbg(DBG_IOCTL,
+ "Set signals down response received\n");
+ card->ports[0]->v24OpSts = signals & 0x03;
+ } else {
+ fst_dbg(DBG_ASS,
+ "Set signals down error errno = %d\n",
+ retval);
+ return -EFAULT;
+ }
+ my_cmd.output_data_len = 0;
+ /* And invert the status field to show we have done
+ * the command
+ */
+ my_cmd.status = ~my_cmd.status;
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd));
+ else if (copy_to_user
+ (ifr->ifr_data, &my_cmd, sizeof(my_cmd))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy status back\n");
+ return -EFAULT;
+ }
+ return 0;
+ case FSTCMD_GET_VERSION:
+ if (copy_to_user
+ (my_cmd.data_ptr, &port->fstioc_info_ver, 1)) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy fstioc_info version back\n");
+ return -EFAULT;
+ }
+ my_cmd.output_data_len = sizeof(fstioc_info_ver);
+ /* And invert the status field to show we have
+ * Understood and complete the command
+ */
+ my_cmd.status = ~my_cmd.status;
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd));
+ else if (copy_to_user
+ (ifr->ifr_data, &my_cmd, sizeof(my_cmd))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy status back\n");
+ return -EFAULT;
+ }
+ return 0;
+
+ case FSTCMD_SET_VERSION:
+ if (my_cmd.input_data_len > 0) {
+ if (copy_from_user(&fstioc_info_ver,
+ my_cmd.data_ptr, 1)) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not get fstioc_info version\n");
+ return -EFAULT;
+ }
+ if ((fstioc_info_ver < FST_VERSION_OLD) ||
+ (fstioc_info_ver > FST_VERSION_CURRENT)) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: setting an invalid fstioc_info version %d\n",
+ fstioc_info_ver);
+ return -EFAULT;
+ }
+ my_cmd.output_data_len = 0;
+ /* And invert the status field to show we
+ * have done the command
+ */
+ my_cmd.status = ~my_cmd.status;
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &my_cmd,
+ sizeof(my_cmd));
+ else if (copy_to_user
+ (ifr->ifr_data, &my_cmd,
+ sizeof(my_cmd))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy status back\n");
+ return -EFAULT;
+ }
+ port->fstioc_info_ver = fstioc_info_ver;
+ } else {
+ fst_dbg(DBG_ASS,
+ "(%s) Cannot copy fstioc_info version, data buffer too small %d %d\n",
+ port->dev->name, 1,
+ my_cmd.input_data_len);
+ return -EFAULT;
+ }
+ return 0;
+
+ case FSTCMD_GET_INTS:
+ if (port->compat == 1)
+ memcpy(my_cmd.data_ptr,
+ &fst_int_counter[port->card->card_no],
+ sizeof(struct fst_ints));
+ else
+ copied = copy_to_user(my_cmd.data_ptr,
+ &fst_int_counter
+ [port->card->card_no],
+ sizeof(struct fst_ints));
+ if (copied) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy all the int stats back %lu\n",
+ copied);
+ return -EFAULT;
+ }
+ my_cmd.output_data_len = sizeof(struct fst_ints);
+ /* And invert the status field to show we have done
+ * the command
+ */
+ my_cmd.status = ~my_cmd.status;
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd));
+ else if (copy_to_user
+ (ifr->ifr_data, &my_cmd, sizeof(my_cmd))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy status back\n");
+ return -EFAULT;
+ }
+ return 0;
+
+ case FSTCMD_RESET_STATS:
+ memset(hdlc_stats(port->dev), 0,
+ sizeof(struct fst_device_stats));
+ my_cmd.output_data_len = 0;
+ /* And invert the status field to show we have done
+ * the command
+ */
+ my_cmd.status = ~my_cmd.status;
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd));
+ else if (copy_to_user(ifr->ifr_data, &my_cmd,
+ sizeof(my_cmd))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy status back\n");
+ return -EFAULT;
+ }
+ return 0;
+
+ case FSTCMD_SET_READV:
+ if (my_cmd.input_data_len > 0) {
+ if (copy_from_user(&readv_mode,
+ my_cmd.data_ptr, 1)) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not get readv_mode\n");
+ return -EFAULT;
+ }
+ fst_dbg(DBG_IOCTL,
+ "Setting readv mode to %d\n",
+ readv_mode);
+ if ((readv_mode < FST_READV_NORMAL) ||
+ (readv_mode > FST_READV_SYNC2)) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: setting an invalid readv mode %d\n",
+ readv_mode);
+ return -EFAULT;
+ }
+ my_cmd.output_data_len = 0;
+ /* And invert the status field to show we
+ * have done the command
+ */
+ my_cmd.status = ~my_cmd.status;
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &my_cmd,
+ sizeof(my_cmd));
+ else if (copy_to_user
+ (ifr->ifr_data, &my_cmd,
+ sizeof(my_cmd))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy status back\n");
+ return -EFAULT;
+ }
+ port->readv_mode = readv_mode;
+ } else {
+ fst_dbg(DBG_ASS,
+ "(%s) Cannot copy readv_mode, data buffer too small %d %d\n",
+ port->dev->name, 1,
+ my_cmd.input_data_len);
+ return -EFAULT;
+ }
+ return 0;
+
+ case FSTCMD_SET_CHAR:
+ if (my_cmd.input_data_len <=
+ sizeof(struct fstioc_char_data)) {
+ if (copy_from_user
+ (&char_data, my_cmd.data_ptr,
+ sizeof(struct fstioc_char_data))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not get char data\n");
+ return -EFAULT;
+ }
+ fst_dbg(DBG_IOCTL,
+ "Setting char data to %d %d\n",
+ char_data.queue_len,
+ char_data.threshold);
+ port->char_inq_max_len = char_data.queue_len;
+ port->char_inq_threshold = char_data.threshold;
+ my_cmd.output_data_len = 0;
+ /* And invert the status field to show we
+ * have done the command
+ */
+ my_cmd.status = ~my_cmd.status;
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &my_cmd,
+ sizeof(my_cmd));
+ else if (copy_to_user
+ (ifr->ifr_data, &my_cmd,
+ sizeof(my_cmd))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy status back\n");
+ return -EFAULT;
+ }
+ } else {
+ fst_dbg(DBG_ASS,
+ "(%s) Cannot copy char_data, data buffer too small %d %d\n",
+ port->dev->name, 1,
+ my_cmd.input_data_len);
+ return -EFAULT;
+ }
+ return 0;
+
+ case FSTCMD_GET_PRESERVE_SIGNALS:
+ fst_issue_cmd(port, GETPRESERVE);
+ if (port->v24OpSts & 0x100)
+ signals = 1;
+ else
+ signals = 0;
+ if (copy_to_user(my_cmd.data_ptr, &signals, 1)) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not return preserve signals\n");
+ return -EFAULT;
+ }
+ my_cmd.output_data_len = sizeof(signals);
+ /* And invert the status field to show we have done
+ * the command
+ */
+ my_cmd.status = ~my_cmd.status;
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd));
+ else if (copy_to_user
+ (ifr->ifr_data, &my_cmd, sizeof(my_cmd))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy status back\n");
+ return -EFAULT;
+ }
+ return 0;
+
+ case FSTCMD_SET_PRESERVE_SIGNALS:
+ fst_issue_cmd(port, SETPRESERVE);
+ my_cmd.output_data_len = 0;
+ /* And invert the status field to show we have done
+ * the command
+ */
+ my_cmd.status = ~my_cmd.status;
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd));
+ else if (copy_to_user(ifr->ifr_data, &my_cmd,
+ sizeof(my_cmd))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy status back\n");
+ return -EFAULT;
+ }
+ return 0;
+
+ case FSTCMD_UPDATE_CLOCK:
+ if (my_cmd.input_data_len > 0) {
+ if (copy_from_user(&speed,
+ my_cmd.data_ptr,
+ sizeof(int))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not get speed setting\n");
+ return -EFAULT;
+ }
+ } else {
+ fst_dbg(DBG_ASS,
+ "(%s) Cannot copy speed setting, data buffer too small %d %d\n",
+ port->dev->name, 1,
+ my_cmd.input_data_len);
+ return -EFAULT;
+ }
+ fst_dbg(DBG_ASS, "%s: Update clock speed to %d\n",
+ port->dev->name, speed);
+ port->usb_config.line_speed = speed;
+ fst_issue_cmd(port, SETLINESPEED);
+ my_cmd.output_data_len = 0;
+ /* And invert the status field to show we have done
+ * the command
+ */
+ my_cmd.status = ~my_cmd.status;
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd));
+ else if (copy_to_user(ifr->ifr_data, &my_cmd,
+ sizeof(my_cmd))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy status back\n");
+ return -EFAULT;
+ }
+ return 0;
+
+ case FSTCMD_SET_CUSTOM_RATE:
+ if (!port->features) {
+ fst_dbg(DBG_ASS, "%s: custom clock rates not",
+ port->dev->name);
+ fst_dbg(DBG_ASS, " supported on this port\n");
+ return -EIO;
+ }
+ if (my_cmd.input_data_len >=
+ (unsigned int)sizeof(struct
+ fstioc_custom_rate_config)) {
+ if (copy_from_user
+ (&custom_clock_rate, my_cmd.data_ptr,
+ sizeof(struct fstioc_custom_rate_config))) {
+ fst_dbg(DBG_ASS, "fstcmd: could not ");
+ fst_dbg(DBG_ASS, "get custom rate ");
+ fst_dbg(DBG_ASS, "config structure\n");
+
+ return -EFAULT;
+ }
+ } else {
+ fst_dbg(DBG_ASS, "(%s) Cannot copy ",
+ port->dev->name);
+ fst_dbg(DBG_ASS, "custom_rate_config, data ");
+ fst_dbg(DBG_ASS, "buffer too small %lu %u\n",
+ sizeof(struct fstioc_custom_rate_config),
+ my_cmd.input_data_len);
+ return -EFAULT;
+ }
+ retval =
+ parse_custom_clock_rate_structure(port,
+ &custom_clock_rate);
+ if (retval) {
+ fst_dbg(DBG_ASS, "(%s) Invalid custom",
+ port->dev->name);
+ fst_dbg(DBG_ASS, "_rate_config structure, ");
+ fst_dbg(DBG_ASS, "%d\n", retval);
+ return -EINVAL;
+ }
+ retval =
+ set_custom_clock_rate(port, &custom_clock_rate);
+ if (retval) {
+ fst_dbg(DBG_ASS, "(%s) Error in setting ",
+ port->dev->name);
+ fst_dbg(DBG_ASS, "custom clock rate, %d\n",
+ retval);
+ return -EINVAL;
+ }
+ /* Everything worked */
+ my_cmd.output_data_len = 0;
+ /* And invert the status field to show we have done
+ * the command
+ */
+ my_cmd.status = ~my_cmd.status;
+ if (port->compat == 1)
+ memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd));
+ else if (copy_to_user(ifr->ifr_data, &my_cmd,
+ sizeof(my_cmd))) {
+ fst_dbg(DBG_ASS,
+ "fstcmd: could not copy status back\n");
+ return -EFAULT;
+ }
+ return 0;
+
+ default:
+ fst_dbg(DBG_ASS, "Unrecognised FST Command %d\n",
+ my_cmd.command);
+ return -EINVAL;
+ }
+ default:
+ /* Not one of ours. Pass it through to sPPP package */
+ return hdlc_ioctl(dev, ifr, cmd);
+ }
+ return -EINVAL;
+}
+
+static void fst_openport(struct fst_port_info *port)
+{
+ int txq_length;
+
+ /* Only init things if card is actually running. This allows open to
+ * succeed for downloads etc.
+ */
+ fst_dbg(DBG_OPEN, "Opening port %s\n", port->dev->name);
+ fst_dbg(DBG_OPEN, "Tx buffers = %d of size %d\n", port->num_tx_buffers,
+ port->tx_buffer_size);
+ fst_dbg(DBG_OPEN, "Rx buffers = %d of size %d\n", port->num_rx_buffers,
+ port->rx_buffer_size);
+
+ last_segment_cnt = 0;
+ if (port->card->state == FST_RUNNING) {
+ if (port->run) {
+ fst_dbg(DBG_OPEN,
+ "open: found port already running\n");
+ } else {
+ fst_rx_config(port);
+ fst_tx_config(port);
+ fst_issue_cmd(port, STARTPORT);
+ port->run = 1;
+ fst_op_raise(port, OPSTS_RTS | OPSTS_DTR);
+ txq_length = port->txqe - port->txqs;
+ port->txqe = 0;
+ port->txqs = 0;
+ port->rxq.error_recovery = 0;
+ port->char_inq = NULL;
+ port->char_inq_end = NULL;
+ }
+ }
+}
+
+static void fst_closeport(struct fst_port_info *port)
+{
+ fst_dbg(DBG_OPEN, "Closing port %s\n", port->dev->name);
+ if (port->card->state == FST_RUNNING) {
+ /*
+ * Need to lower control signals here
+ */
+ fst_op_lower(port, OPSTS_RTS | OPSTS_DTR);
+ fst_issue_cmd(port, STOPPORT);
+ if (port->mode == FST_MODE_ASYNC)
+ fst_reset_rx_fifo(port);
+ fst_issue_cmd(port, FLUSHBUFFERS);
+ port->run = 0;
+ purge_tx_queue(port->card, port);
+ } else {
+ fst_dbg(DBG_OPEN, "close: card not running\n");
+ }
+}
+
+static int fst_open(struct net_device *dev)
+{
+ int err;
+ struct fst_card_info *card;
+ struct fst_port_info *port;
+
+ port = dev_to_port(dev);
+ card = port->card;
+ fst_dbg(DBG_OPEN, "fst_open on port %s\n", dev->name);
+
+ if (card->state != FST_RUNNING) {
+ fst_dbg(DBG_ASS,
+ "%s: Cannot open port if card is not loaded\n",
+ dev->name);
+ return -ENODEV;
+ }
+ if ((port->run) && (port->char_file)) {
+ if (!port->monitor_mode) {
+ fst_dbg(DBG_ASS, "%s: Port is already open\n",
+ dev->name);
+ return -EBUSY;
+ } else {
+ fst_dbg(DBG_ASS,
+ "%s: Opening network port to monitor char interface\n",
+ dev->name);
+ }
+ }
+
+ if (port->proto != FST_RAW) {
+ err = hdlc_open(dev);
+ if (err)
+ return err;
+ }
+ if (!port->char_file)
+ fst_openport(port);
+ netif_wake_queue(dev);
+ if (!try_module_get(THIS_MODULE))
+ return -EBUSY;
+ return 0;
+}
+
+static int fst_close(struct net_device *dev)
+{
+ struct fst_port_info *port;
+ struct fst_card_info *card;
+ unsigned long flags;
+
+ port = dev_to_port(dev);
+ if (port == NULL)
+ fst_dbg(DBG_ASS, "fst_close: port is NULL\n");
+ card = port->card;
+ if (card == NULL)
+ fst_dbg(DBG_ASS, "fst_close: card is NULL\n");
+ fst_dbg(DBG_OPEN, "Port Close: %s\n", dev->name);
+ netif_stop_queue(dev);
+
+ if (card->tx_urb_cnt != MAX_URBS_OUTSTANDING) {
+ if (card->tx_bulk_urb) {
+ fst_dbg(DBG_OPEN, "Cancel a Tx urb\n");
+ /* Stop sending data */
+ usb_kill_urb(card->tx_bulk_urb);
+ }
+ fst_dbg(DBG_ASS, "Closing port with urbs outstanding\n");
+ }
+
+ if (!port->char_file) {
+ /* Only close the port if really not in use
+ */
+ fst_closeport(port);
+ }
+
+ spin_lock_irqsave(&card->card_lock, flags);
+ if (!port->char_file) {
+ while (port->txqe != port->txqs) {
+ fst_dbg(DBG_OPEN, "Purging tx queue\n");
+ fst_dbg(DBG_OPEN,
+ "%s: Closing port with data in tx queue\n",
+ dev->name);
+ dev_kfree_skb(port->txq[port->txqs].frame);
+ port->txq[port->txqs].frame = NULL;
+ port->txqs++;
+ if (port->txqs == FST_TXQ_DEPTH)
+ port->txqs = 0;
+ }
+ }
+ spin_unlock_irqrestore(&card->card_lock, flags);
+ if (port->proto != FST_RAW)
+ hdlc_close(dev);
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+static int
+fst_attach(struct net_device *dev, unsigned short encoding,
+ unsigned short parity)
+{
+ /* Setting currently fixed in FarSync card so we check and forget
+ */
+ if (encoding != ENCODING_NRZ || parity != PARITY_CRC16_PR1_CCITT)
+ return -EINVAL;
+ return 0;
+}
+
+static void fst_tx_timeout(struct net_device *dev)
+{
+ struct fst_port_info *port;
+ struct fst_card_info *card;
+ struct net_device_stats *stats = hdlc_stats(dev);
+
+ pr_info("fst_tx_timeout on %s\n", dev->name);
+ port = dev_to_port(dev);
+ card = port->card;
+ stats->tx_errors++;
+ stats->tx_aborted_errors++;
+ fst_dbg(DBG_ASS, "Tx timeout card %d port %d\n",
+ card->card_no, port->index);
+ purge_tx_queue(card, port);
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
+ port->start = 0;
+ if (port->notify_mode == FST_NOTIFY_EXTENDED)
+ fst_notify_state_change(port->card, port);
+}
+
+static int fst_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct net_device_stats *stats = hdlc_stats(dev);
+ struct fst_card_info *card;
+ struct fst_port_info *port;
+ unsigned long flags;
+ int txq_length;
+ struct sk_buff *new_skb;
+ int count = 0;
+
+ if (dev == NULL) {
+ fst_dbg(DBG_ASS, "fst_start_xmit: dev is null\n");
+ dev_kfree_skb(skb);
+ stats->tx_errors++;
+ stats->tx_dropped++;
+ return 0;
+ }
+ port = dev_to_port(dev);
+ if (port == NULL) {
+ fst_dbg(DBG_ASS, "fst_start_xmit: port is null\n");
+ dev_kfree_skb(skb);
+ stats->tx_errors++;
+ stats->tx_dropped++;
+ return 0;
+ }
+ card = port->card;
+ if (card == NULL) {
+ fst_dbg(DBG_ASS, "fst_start_xmit: card is null\n");
+ stats->tx_errors++;
+ dev_kfree_skb(skb);
+ stats->tx_dropped++;
+ return 0;
+ }
+ fst_dbg(DBG_TX, "fst_start_xmit: length = %d\n", skb->len);
+
+ /* Drop packet with error if we don't have carrier */
+ if ((!netif_carrier_ok(dev)) && (!port->ignore_carrier)) {
+ dev_kfree_skb(skb);
+ stats->tx_errors++;
+ stats->tx_carrier_errors++;
+ fst_dbg(DBG_ASS,
+ "Tried to transmit but no carrier on card %d port %d\n",
+ card->card_no, port->index);
+ if (port->notify_mode == FST_NOTIFY_EXTENDED)
+ fst_notify_state_change(port->card, port);
+ return 0;
+ }
+
+ /* Drop it if it's too big! MTU failure ? */
+ if (skb->len > FST_MAX_MTU) {
+ pr_err("Tx Packet too large %d vs %d\n", skb->len,
+ FST_MAX_MTU);
+ dev_kfree_skb(skb);
+ stats->tx_errors++;
+ stats->tx_dropped++;
+ if (port->notify_mode == FST_NOTIFY_EXTENDED)
+ fst_notify_state_change(port->card, port);
+ return 0;
+ }
+
+ /* Drop it if it's bigger than the tx_buffer_size */
+ if (skb->len > port->tx_buffer_size) {
+ pr_err("Tx Packet too large %d vs buffer %d\n", skb->len,
+ port->tx_buffer_size);
+ dev_kfree_skb(skb);
+ stats->tx_errors++;
+ stats->tx_dropped++;
+ if (port->notify_mode == FST_NOTIFY_EXTENDED)
+ fst_notify_state_change(port->card, port);
+ return 0;
+ }
+
+ /* Drop it if it's port not running */
+ if (!port->run) {
+ pr_err("Tx When port closed\n");
+ dev_kfree_skb(skb);
+ stats->tx_errors++;
+ stats->tx_dropped++;
+ if (port->notify_mode == FST_NOTIFY_EXTENDED)
+ fst_notify_state_change(port->card, port);
+ return 0;
+ }
+ count = skb->len;
+ /* Drop it if it is too small */
+ if (skb->len <= 0) {
+ fst_dbg(DBG_ASS, "fst_start_xmit: packet too small %d\n",
+ skb->len);
+ dev_kfree_skb(skb);
+ stats->tx_errors++;
+ stats->tx_dropped++;
+ return 0;
+ }
+ if (skb->len == 0)
+ fst_dbg(DBG_ASS, "Transmitting a zero length frame\n");
+
+ /* We are always going to queue the packet
+ * so that the bottom half is the only place we tx from
+ * Check there is room in the port txq
+ */
+ spin_lock_irqsave(&card->card_lock, flags);
+ txq_length = port->txqe - port->txqs;
+ if (txq_length < 0) {
+ /* This is the case where the next free has wrapped but the
+ * last used hasn't
+ */
+ txq_length = txq_length + FST_TXQ_DEPTH;
+ }
+ spin_unlock_irqrestore(&card->card_lock, flags);
+ if (txq_length >= fst_txq_high) {
+ /* We have got enough buffers in the pipeline. Ask the network
+ * layer to stop sending frames down
+ */
+ port->start = 1; /* I'm using this to signal stop sent up */
+ if (!port->char_file) {
+ fst_dbg(DBG_ASS, "%s: Stop the network layer\n",
+ dev->name);
+ netif_stop_queue(dev);
+ } else {
+ /* If the char interface say we can't do it at all
+ */
+ dev_kfree_skb(skb);
+ return 0;
+ }
+ }
+ if (txq_length == FST_TXQ_DEPTH - 1) {
+ /* This shouldn't have happened but such is life
+ */
+ dev_kfree_skb(skb);
+ port_to_stats(port, tx_errors)++;
+ port_to_stats(port, tx_dropped)++;
+ fst_dbg(DBG_ASS, "Tx queue overflow card %d port %d\n",
+ card->card_no, port->index);
+ if (port->notify_mode == FST_NOTIFY_EXTENDED)
+ fst_notify_state_change(port->card, port);
+ return 0;
+ }
+
+ /* queue the buffer
+ */
+ new_skb = skb;
+ if (new_skb == NULL) {
+ fst_dbg(DBG_ASS, "Error in queuing atm pdu\n");
+ return 0;
+ }
+ if (port->monitor_mode)
+ gen_mon_packet(new_skb, port, FST_MON_TX);
+ spin_lock_irqsave(&card->card_lock, flags);
+ port->txq[port->txqe].frame = new_skb;
+ port->txq[port->txqe].count = new_skb->len;
+ port->txq[port->txqe].segment_cnt =
+ ((new_skb->len - 1) / port->tx_buffer_size) + 1;
+ port->txq[port->txqe].current_seg = 0;
+ port->txq[port->txqe].flags = 0;
+ port->txqe++;
+ if (port->txqe == FST_TXQ_DEPTH)
+ port->txqe = 0;
+ spin_unlock_irqrestore(&card->card_lock, flags);
+
+ /* Scehdule the bottom half which now does transmit processing */
+ fst_q_work_item(&fst_work_txq, card->card_no);
+ tasklet_schedule(&fst_tx_task);
+ if (port->char_file)
+ return count;
+ else
+ return 0;
+}
+
+/* Sysdev stuff for adding the serial number attribute to the char device
+ */
+static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fst_card_info *card = dev_get_drvdata(dev);
+ ssize_t ret = 0;
+
+ /* no lock needed for this */
+
+ strcpy(buf, card->ports[0]->usb_card_info.sz_serial_no);
+ ret = strlen(buf);
+ return ret;
+}
+
+static DEVICE_ATTR(serial, 0644, serial_show, NULL);
+
+static const struct net_device_ops fst_ops = {
+ .ndo_open = fst_open,
+ .ndo_stop = fst_close,
+ .ndo_change_mtu = hdlc_change_mtu,
+ .ndo_start_xmit = hdlc_start_xmit,
+ .ndo_do_ioctl = fst_ioctl,
+ .ndo_tx_timeout = fst_tx_timeout,
+};
+
+/* Card setup having checked hardware resources.
+ * Should be pretty bizarre if we get an error here (kernel memory
+ * exhaustion is one possibility). If we do see a problem we report it
+ * via a printk and leave the corresponding interface and all that follow
+ * disabled.
+ */
+
+static void fst_init_card(struct fst_card_info *card, char *dev_name)
+{
+ int i;
+ int err;
+ struct fst_port_info *port;
+ char char_dev_name[16];
+ struct device *tty_device = NULL;
+
+ /* Assign the minor device number
+ */
+ for (i = 0; i < FST_MAX_CARDS * FST_MAX_PORTS; i++) {
+ if (fst_ports_list[i] == NULL) {
+ fst_minor = i;
+ break;
+ }
+ }
+ /* We're working on a number of ports based on the card ID. If the
+ * firmware detects something different later (should never happen)
+ * we'll have to revise it in some way then.
+ */
+ spin_lock_init(&card->card_lock);
+ spin_lock_init(&card->fifo_lock);
+ init_waitqueue_head(&card->fifo_waitq);
+ for (i = 0; i < card->nports; i++) {
+ struct net_device *dev;
+ hdlc_device *hdlc;
+
+ card->ports[i] = kmalloc(sizeof(struct fst_port_info),
+ GFP_KERNEL);
+ if (card->ports[i] == NULL) {
+ pr_err("FarSync Flex found but insufficient memory ");
+ pr_err("for driver device storage\n");
+ return;
+ }
+ port = card->ports[i];
+ memset(port, 0, sizeof(struct fst_port_info));
+ dev = alloc_hdlcdev(port);
+ dev->netdev_ops = &fst_ops;
+ SET_NETDEV_DEV(dev, &card->udev->dev);
+ err = register_hdlc_device(dev);
+ if (err < 0) {
+ int j;
+ pr_err("Cannot register HDLC device for port %d", i);
+ pr_err(" (errno %d)\n", -err);
+ for (j = i; j < card->nports; j++) {
+ free_netdev(card->ports[j]->dev);
+ card->ports[j]->dev = NULL;
+ kfree(card->ports[j]);
+ }
+ card->nports = i;
+ break;
+ }
+ port->dev = dev;
+ port->card = card;
+ port->index = i;
+ port->run = 0;
+ port->proto = FST_GEN_HDLC;
+ port->num_tx_buffers = REQUIRED_TX_BUFFERS;
+ port->num_rx_buffers = REQUIRED_RX_BUFFERS;
+ port->tx_buffer_size = REQUIRED_TX_BUFFER_SIZE;
+ port->rx_buffer_size = REQUIRED_RX_BUFFER_SIZE;
+ port->fstioc_info_ver = fst_iocinfo_version;
+ port->char_file = NULL;
+ port->char_inq = NULL;
+ port->char_inq_end = NULL;
+ port->hwif = X21;
+ port->v24OpSts = 0;
+ port->minor_dev_no = fst_minor;
+ spin_lock_init(&port->rxf_lock);
+ init_waitqueue_head(&port->pollq);
+ init_waitqueue_head(&port->readq);
+ init_waitqueue_head(&port->writeq);
+ fst_ports_list[fst_minor] = port;
+ hdlc = dev_to_hdlc(dev);
+ fst_dbg(DBG_INIT, "Allocated device name was %s\n", dev->name);
+ if (fst_alloc_rx_fifo(port)) {
+ pr_err("%s: Cannot allocate rx fifo\n",
+ port->dev->name);
+ kfree(dev);
+ card->nports = i;
+ break;
+ }
+ /* Fill in remainder of the net device info
+ * Since this is a PCI setup this is purely
+ * informational. Give them the buffer addresses
+ * and basic card I/O.
+ */
+ dev->base_addr = card->pci_conf;
+ dev->irq = card->irq;
+
+ dev->tx_queue_len = FST_TX_QUEUE_LEN;
+ dev->addr_len = 8;
+ strcpy(dev->dev_addr,
+ card->ports[0]->usb_card_info.sz_serial_no);
+ dev->watchdog_timeo = FST_TX_TIMEOUT;
+ hdlc->attach = fst_attach;
+ hdlc->xmit = fst_start_xmit;
+ fst_dbg(DBG_INIT, "fst_major is %x: fst_minor is %d\n",
+ fst_major, fst_minor);
+ fst_dbg(DBG_INIT, "Register the tty device %d\n", fst_minor);
+ tty_port_init(&fst_tty_area[fst_minor].tty_port);
+ tty_device =
+ tty_port_register_device(&fst_tty_area[fst_minor].tty_port,
+ serial_drv, fst_minor, NULL);
+
+ fst_tty_area[fst_minor].tty_port.tty =
+ fst_tty_area[fst_minor].tty;
+ strcpy(char_dev_name, "");
+ strcat(char_dev_name, dev->name);
+ card->sync_cdev = device_create(farsync_class, NULL,
+ MKDEV(fst_major, fst_minor),
+ NULL, char_dev_name,
+ fst_minor);
+ if (card->sync_cdev < 0) {
+ pr_err("Cannot create udev entry for %s\n",
+ port_to_dev(card->ports[0])->name);
+ } else {
+ int rc;
+
+ dev_set_drvdata(card->sync_cdev, card);
+ /* register the attributes */
+ rc = device_create_file(card->sync_cdev,
+ &dev_attr_serial);
+ if (rc) {
+ fst_dbg(DBG_ASS,
+ "Couldn't register sync serial no attribute\n");
+ }
+ }
+ init_async_parameters(port, 8, COM_STOP_BITS_1,
+ COM_NO_PARITY, COM_FLOW_CONTROL_NONE);
+ port->char_inq_max_len = FST_CHAR_INQ_MAX_LEN;
+ }
+}
+
+/* Get a minor range for your devices from the usb maintainer */
+#define USB_MINOR_BASE 192
+#define USB_VENDOR_ID 0x1afd
+#define USB_PRODUCT_ID 0x0721
+#define WRITES_IN_FLIGHT 8
+
+/* table of devices that work with this driver */
+static struct usb_device_id flex_table[] = {
+ {USB_DEVICE(USB_VENDOR_ID, USB_PRODUCT_ID)},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, flex_table);
+
+/* usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with the driver core
+ */
+static struct usb_class_driver flex_class = {
+ .name = "flex%d",
+ .fops = &fst_mem_fops,
+ .minor_base = USB_MINOR_BASE,
+};
+
+static int flex_get_config(struct fst_card_info *usb_dev,
+ struct fst_port_info *port,
+ struct fstioc_info *info)
+{
+ int retval = 0;
+ int retval2 = 0;
+ int retval3 = 0;
+ int retval4 = 1;
+ int clocks = 0;
+ int features = 0;
+
+ fst_dbg(DBG_IOCTL, "Getting port config\n");
+ flex_serconf_to_le(&port->usb_config);
+ retval = usb_control_msg(usb_dev->udev, usb_rcvctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_GET_CONFIG, 0xc0, 0, 0,
+ &port->usb_config,
+ sizeof(FLEX_SERCONF), 10000);
+ flex_serconf_to_cpu(&port->usb_config);
+ flex_card_info_to_le(&port->usb_card_info);
+ retval2 = usb_control_msg(usb_dev->udev, usb_rcvctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_GET_CARD_INFO, 0xc0, 0, 0,
+ &port->usb_card_info,
+ sizeof(FLEX_CARD_INFO), 10000);
+ if (info) {
+ info->card_rev_major = port->usb_card_info.u_major_rev;
+ info->card_rev_minor = port->usb_card_info.u_minor_rev;
+ info->card_rev_build = port->usb_card_info.u_build_state;
+ }
+ flex_card_info_to_cpu(&port->usb_card_info);
+ cpu_to_le32s(&clocks);
+ retval3 = usb_control_msg(usb_dev->udev,
+ usb_rcvctrlpipe(usb_dev->udev, 0),
+ USS_CMD_GET_CLOCK_STATUS, 0xC0, 0, 0,
+ &clocks, 4, 10000);
+ le32_to_cpus(&clocks);
+ /* If we are a Flex 2 or above then check the features
+ */
+ if (port->usb_card_info.u_minor_rev >= FIRST_FLEX_V2_MINOR_ID) {
+ cpu_to_le32s(&features);
+ retval4 = usb_control_msg(usb_dev->udev,
+ usb_rcvctrlpipe(usb_dev->udev, 0),
+ USS_CMD_GET_FEATURE_MASK, 0xC0, 0, 0,
+ &features, 4, 10000);
+ le32_to_cpus(&features);
+ }
+ if ((retval > 0) && (retval2 > 0) && (retval3 > 0) && (retval4 > 0)
+ && (info != NULL)) {
+ fst_dbg(DBG_IOCTL, "Get config response received\n");
+ info->valid =
+ ((usb_dev->state == FST_RUNNING) ? FSTVAL_ALL : FSTVAL_CARD)
+#if FST_DEBUG
+ | FSTVAL_DEBUG
+#endif
+ ;
+ info->valid = 1;
+ if (clocks)
+ info->clock_status = 0;
+ else
+ info->clock_status = 1;
+ info->synth_ability = 0;
+ if (features) {
+ /*This means it is a flex v2 or later, so synthisiser
+ * is supported
+ */
+ info->synth_ability = 1;
+ }
+ port->features = features;
+ info->features = (unsigned int)features;
+ info->nports = usb_dev->nports;
+ info->iocinfo_version = port->fstioc_info_ver;
+ info->type = usb_dev->type;
+ info->state = usb_dev->state;
+ info->index = 0;
+ info->smc_firmware_version =
+ port->usb_card_info.u_software_version;
+
+ info->line_interface = port->hwif;
+ info->proto = port->proto;
+ info->valid = FSTVAL_ALL;
+ info->internal_clock = port->usb_config.clock;
+ info->line_speed = port->usb_config.line_speed;
+ info->termination = port->usb_config.termination;
+ info->ignore_carrier = port->ignore_carrier;
+ switch (port->usb_config.encoding) {
+ case FSCMN_ENCODING_NRZ:
+ info->coding = CODING_NRZ;
+ break;
+ case FSCMN_ENCODING_NRZI:
+ info->coding = CODING_NRZI;
+ break;
+ case FSCMN_ENCODING_FM0:
+ info->coding = CODING_FM0;
+ break;
+ case FSCMN_ENCODING_FM1:
+ info->coding = CODING_FM1;
+ break;
+ case FSCMN_ENCODING_MANCHESTER:
+ info->coding = CODING_MANCHESTER;
+ break;
+ case FSCMN_ENCODING_DMAN:
+ info->coding = CODING_DIFF_MANCHESTER;
+ break;
+ default:
+ info->coding = CODING_NRZ;
+ break;
+ }
+ info->est_line_speed = 0;
+ fst_issue_cmd(port, GETSIGNALS);
+ info->v24IpSts = port->v24IpSts;
+ info->v24OpSts = port->v24OpSts;
+ info->extended_clocking = 0;
+ info->cable_status = 0;
+ info->card_mode = usb_dev->card_mode;
+#if FST_DEBUG
+ info->debug = fst_debug_mask;
+#else
+ info->debug = 0;
+#endif
+ info->transparent_mode = port->usb_config.mode;
+
+ info->transmit_msb_first = port->transmit_msb_first;
+ info->receive_msb_first = port->receive_msb_first;
+
+ info->async_ability = 1;
+ info->invert_clock = port->usb_config.rxclock;
+ info->num_tx_buffers = port->usb_config.num_tx_buffer;
+ info->num_rx_buffers = port->usb_config.num_rx_buffer;
+ info->tx_buffer_size = port->usb_config.size_tx_buffer;
+ info->rx_buffer_size = port->usb_config.size_rx_buffer;
+ info->enable_nrzi_clocking =
+ port->usb_config.enable_nrzi_clocking;
+ fst_dbg(DBG_IOCTL,
+ "Async parameters returned as bits %d parity %d stop %d fc %d xon %d xof %d\n",
+ info->async_conf.word_length, info->async_conf.parity,
+ info->async_conf.stop_bits,
+ info->async_conf.flow_control,
+ info->async_conf.xon_char, info->async_conf.xoff_char);
+ /* Extended Clocking
+ */
+ if (port->usb_config.extended_clocking) {
+ info->extended_clocking = 0x80;
+ if (port->usb_config.internal_tx_clock)
+ info->extended_clocking |= 0x01;
+ if (port->usb_config.internal_rx_clock)
+ info->extended_clocking |= 0x02;
+ }
+ if (port->usb_config.extended_config) {
+ info->extended_clocking |= 0x80;
+ if (port->usb_config.extended_config & EXT_CONFIG_TTT)
+ info->extended_clocking |= 0x04;
+ if (port->usb_config.extended_config & EXT_CONFIG_RTT)
+ info->extended_clocking |= 0x08;
+ }
+ if ((!port->usb_config.extended_clocking) &&
+ (!port->usb_config.extended_config)) {
+ info->extended_clocking = 0;
+ }
+ /* Update the stats
+ */
+ memcpy(&info->stats, hdlc_stats(port->dev),
+ sizeof(struct fst_device_stats));
+ /* Copy in the async config
+ */
+ info->async_conf.flow_control = port->usb_config.flow_control;
+ info->async_conf.stop_bits = port->usb_config.stopbits;
+ info->async_conf.parity = port->usb_config.parity;
+ info->async_conf.word_length = port->usb_config.data_bits;
+ info->async_conf.xon_char = port->usb_config.xonchar;
+ info->async_conf.xoff_char = port->usb_config.xoffchar;
+ /* and now return the size of the structure according
+ * to the version set
+ */
+ switch (port->fstioc_info_ver) {
+ case FST_VERSION_CURRENT:
+ return fstioc_info_sz_current;
+
+ case FST_VERSION_V1:
+ return fstioc_info_sz_ver1;
+
+ case FST_VERSION_V2:
+ return fstioc_info_sz_ver2;
+
+ case FST_VERSION_V3:
+ return fstioc_info_sz_ver3;
+
+ default:
+ return fstioc_info_sz_old;
+ }
+ } else {
+ if ((retval != sizeof(port->usb_config)) ||
+ (retval2 != sizeof(port->usb_card_info)) ||
+ (retval3 != 4)) {
+ fst_dbg(DBG_ASS,
+ "Get Config error errno = %d %d %d %p\n",
+ retval, retval2, retval3, info);
+ }
+ return 0;
+ }
+}
+
+static int flex_set_config(struct fst_card_info *usb_dev,
+ struct fstioc_info *info)
+{
+ int retval;
+
+ fst_dbg(DBG_IOCTL, "Setting port config\n");
+ if (info->card_mode != usb_dev->card_mode) {
+ char mode[CMD_BUF_LEN];
+ if (info->card_mode) {
+ mode[0] = 1;
+ mode[1] = 0;
+ mode[2] = 0;
+ mode[3] = 0;
+ } else {
+ mode[0] = 0;
+ mode[1] = 0;
+ mode[2] = 0;
+ mode[3] = 0;
+ }
+ retval = usb_control_msg(usb_dev->udev,
+ usb_sndctrlpipe(usb_dev->udev, 0),
+ USS_CMD_SET_IDENTIFY_MODE, 0x40, 0, 0,
+ &mode, CMD_BUF_LEN, 10000);
+ usb_dev->card_mode = info->card_mode;
+ if (retval == CMD_BUF_LEN) {
+ fst_dbg(DBG_IOCTL,
+ "Set identify mode response received\n");
+ } else {
+ fst_dbg(DBG_ASS,
+ "Set identify mode error errno = %d\n",
+ retval);
+ }
+ }
+ switch (info->line_interface) {
+ case V24:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_V24;
+ break;
+ case X21:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_X21;
+ break;
+ case V35:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_V35;
+ break;
+ case X21D:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_X21D;
+ break;
+ case NOCABLE:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_NO_CABLE;
+ break;
+ case RS530_449:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_RS530;
+ break;
+ case RS485:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_RS485;
+ break;
+ case RS485_FDX:
+ usb_dev->ports[0]->usb_config.iface =
+ FSCMN_INTERFACE_RS485_FDX;
+ break;
+ default:
+ usb_dev->ports[0]->usb_config.iface = FSCMN_INTERFACE_V24;
+ break;
+ }
+ usb_dev->ports[0]->hwif = info->line_interface;
+ usb_dev->ports[0]->proto = info->proto;
+ usb_dev->ports[0]->usb_config.clock = info->internal_clock;
+ usb_dev->ports[0]->usb_config.line_speed = info->line_speed;
+ usb_dev->ports[0]->ignore_carrier = info->ignore_carrier;
+ switch (info->coding) {
+ case CODING_NRZ:
+ usb_dev->ports[0]->usb_config.encoding = FSCMN_ENCODING_NRZ;
+ break;
+ case CODING_NRZI:
+ usb_dev->ports[0]->usb_config.encoding = FSCMN_ENCODING_NRZI;
+ break;
+ case CODING_FM0:
+ usb_dev->ports[0]->usb_config.encoding = FSCMN_ENCODING_FM0;
+ break;
+ case CODING_FM1:
+ usb_dev->ports[0]->usb_config.encoding = FSCMN_ENCODING_FM1;
+ break;
+ case CODING_MANCHESTER:
+ usb_dev->ports[0]->usb_config.encoding =
+ FSCMN_ENCODING_MANCHESTER;
+ break;
+ case CODING_DIFF_MANCHESTER:
+ usb_dev->ports[0]->usb_config.encoding = FSCMN_ENCODING_DMAN;
+ break;
+ default:
+ usb_dev->ports[0]->usb_config.encoding = FSCMN_ENCODING_NRZ;
+ break;
+ }
+
+ usb_dev->ports[0]->usb_config.termination = info->termination;
+ usb_dev->ports[0]->mode = info->transparent_mode;
+ usb_dev->ports[0]->usb_config.mode = info->transparent_mode;
+ usb_dev->ports[0]->transmit_msb_first = info->transmit_msb_first;
+ usb_dev->ports[0]->receive_msb_first = info->receive_msb_first;
+ usb_dev->ports[0]->usb_config.bit_order = 0;
+ usb_dev->ports[0]->usb_config.start = info->tx_rx_start;
+ if (usb_dev->ports[0]->fstioc_info_ver > FST_VERSION_V1) {
+ usb_dev->ports[0]->usb_config.data_bits =
+ info->async_conf.word_length;
+ usb_dev->ports[0]->usb_config.parity = info->async_conf.parity;
+ usb_dev->ports[0]->usb_config.stopbits =
+ info->async_conf.stop_bits;
+ usb_dev->ports[0]->usb_config.flow_control =
+ info->async_conf.flow_control;
+ usb_dev->ports[0]->usb_config.timeout_enable = 0; /* unused */
+ usb_dev->ports[0]->usb_config.timeout_length = 0; /* unused */
+ fst_dbg(DBG_IOCTL,
+ "Async parameters set as bits %d parity %d stop %d fc %d xon %d xof %d\n",
+ info->async_conf.word_length, info->async_conf.parity,
+ info->async_conf.stop_bits,
+ info->async_conf.flow_control,
+ info->async_conf.xon_char, info->async_conf.xoff_char);
+ }
+#if FST_DEBUG
+ fst_debug_mask = info->debug;
+#endif
+ usb_dev->ports[0]->usb_config.rxclock = info->invert_clock;
+ usb_dev->ports[0]->usb_config.num_tx_buffer =
+ usb_dev->ports[0]->num_tx_buffers = info->num_tx_buffers;
+ usb_dev->ports[0]->usb_config.num_rx_buffer =
+ usb_dev->ports[0]->num_rx_buffers = info->num_rx_buffers;
+ usb_dev->ports[0]->usb_config.size_tx_buffer =
+ usb_dev->ports[0]->tx_buffer_size = info->tx_buffer_size;
+ usb_dev->ports[0]->usb_config.size_rx_buffer =
+ usb_dev->ports[0]->rx_buffer_size = info->rx_buffer_size;
+ usb_dev->ports[0]->usb_config.preserve_signals =
+ usb_dev->ports[0]->v24OpSts & 0x100;
+ usb_dev->ports[0]->usb_config.async_receive_mode = FSCMN_ASYNC_RX_CHAR;
+ usb_dev->ports[0]->usb_config.enable_nrzi_clocking =
+ info->enable_nrzi_clocking;
+ /* Extended Clocking feature */
+ if ((info->extended_clocking) && ((info->line_interface == V24)
+ || (info->line_interface ==
+ RS530_449))) {
+ usb_dev->ports[0]->usb_config.clock = 0;
+ usb_dev->ports[0]->usb_config.extended_clocking = 1;
+ if (info->extended_clocking & 1)
+ usb_dev->ports[0]->usb_config.internal_tx_clock = 1;
+ else
+ usb_dev->ports[0]->usb_config.internal_tx_clock = 0;
+ if (info->extended_clocking & 2)
+ usb_dev->ports[0]->usb_config.internal_rx_clock = 1;
+ else
+ usb_dev->ports[0]->usb_config.internal_rx_clock = 0;
+ } else {
+ /* Reset extended clocking because wrong line type
+ */
+ usb_dev->ports[0]->usb_config.extended_clocking = 0;
+ usb_dev->ports[0]->usb_config.internal_tx_clock = 0;
+ usb_dev->ports[0]->usb_config.internal_rx_clock = 0;
+ }
+ fst_dbg(DBG_IOCTL, "Extended Clocking is %x\n",
+ info->extended_clocking);
+ /* Extended Config feature */
+ if ((info->extended_clocking) && ((info->line_interface == V24)
+ || (info->line_interface ==
+ RS530_449))) {
+ usb_dev->ports[0]->usb_config.clock = 0;
+ if (info->extended_clocking & 4) {
+ usb_dev->ports[0]->usb_config.extended_config =
+ EXT_CONFIG_TTT;
+ }
+ if (info->extended_clocking & 8) {
+ usb_dev->ports[0]->usb_config.extended_config =
+ EXT_CONFIG_RTT;
+ }
+ } else {
+ /* Reset extended config because wrong line type
+ */
+ usb_dev->ports[0]->usb_config.extended_config = 0;
+ }
+ fst_dbg(DBG_IOCTL, "Extended Clocking is %x\n",
+ info->extended_clocking);
+
+ flex_serconf_to_le(&usb_dev->ports[0]->usb_config);
+ retval = usb_control_msg(usb_dev->udev, usb_sndctrlpipe(usb_dev->udev,
+ 0),
+ USS_CMD_SET_CONFIG, 0x40, 0, 0,
+ &usb_dev->ports[0]->usb_config,
+ sizeof(FLEX_SERCONF), 10000);
+ flex_serconf_to_cpu(&usb_dev->ports[0]->usb_config);
+ if (retval > 0) {
+ fst_dbg(DBG_IOCTL, "Set config response received\n");
+ return 1;
+ } else {
+ fst_dbg(DBG_ASS, "Set Config error errno = %d\n", retval);
+ return 0;
+ }
+}
+
+/* Initialise usb interface when detected.
+ * Returns 0 to indicate success, or errno otherwise.
+ */
+static int flex_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ static int firsttime_done;
+ struct fst_card_info *dev;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ size_t buffer_size;
+ int i;
+ int retval = 0;
+ int instance = 0;
+ char dev_name[16];
+
+ if (!firsttime_done) {
+ pr_info("Flex USB WAN driver " FST_USER_VERSION
+ "-" FST_PATCH_LEVEL "-" FST_PLATFORM FST_ADDITIONAL
+ " (c) 2001-2013 FarSite Communications Ltd.\n");
+ firsttime_done = 1;
+#if FST_DEBUG
+ fst_dbg(DBG_ASS, "The value of debug mask is %x\n",
+ fst_debug_mask);
+#endif
+ }
+
+ /* set the defaults */
+ strcpy(dev_name, FST_PORT_NAME);
+ strcat(dev_name, "%d");
+
+ /* We are going to be clever and allow certain cards not to be
+ * configured. An exclude list can be provided in /etc/modules.conf
+ */
+ if (fst_excluded_cards != 0) {
+ /*
+ * There are cards to exclude
+ *
+ */
+ for (i = 0; i < fst_excluded_cards; i++) {
+ if (1 == fst_excluded_list[i]) {
+ fst_dbg(DBG_ASS,
+ "Flex USB device %d not assigned\n", 1);
+ return -EBUSY;
+ }
+ }
+ }
+
+ /* Allocate driver private data */
+ dev = kmalloc(sizeof(struct fst_card_info), GFP_KERNEL);
+ if (dev == NULL) {
+ pr_err("Flex USB device found but insufficient memory for");
+ pr_err(" driver storage\n");
+ return -ENOMEM;
+ }
+ fst_dbg(DBG_USB, "dev allocated at %p\n", dev);
+ memset(dev, 0, sizeof(struct fst_card_info));
+
+ dev->family = 0;
+ dev->nports = 1;
+ dev->state = FST_RUNNING;
+ dev->udev = usb_get_dev(interface_to_usbdev(interface));
+ dev->interface = interface;
+
+ fst_dbg(DBG_USB, "type %d nports %d irq %d\n", dev->type,
+ dev->nports, dev->irq);
+
+ sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
+ dev->tx_urb_cnt = MAX_URBS_OUTSTANDING;
+ /* set up the endpoint information
+ * use only the first bulk-in and bulk-out endpoints
+ */
+ iface_desc = interface->cur_altsetting;
+ fst_dbg(DBG_USB, "The number of endpoints on this device are %d\n",
+ iface_desc->desc.bNumEndpoints);
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ /* Get Bulk end point addresses
+ */
+ if (!dev->bulk_in_endpoint_addr &&
+ ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_IN) &&
+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_BULK)) {
+ /* we found a bulk in endpoint */
+ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ dev->bulk_in_size = buffer_size;
+ dev->bulk_in_endpoint_addr =
+ endpoint->bEndpointAddress;
+ dev->bulk_in_buffer = NULL;
+ }
+
+ if (!dev->bulk_out_endpoint_addr &&
+ ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_OUT) &&
+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_BULK)) {
+ /* we found a bulk out endpoint */
+ dev->bulk_out_endpoint_addr =
+ endpoint->bEndpointAddress;
+ }
+ /* Get Interrupt end point addresses
+ */
+ if (!dev->int_in_endpoint_addr &&
+ ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_IN) &&
+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_INT)) {
+ /* we found a int in endpoint */
+ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ dev->int_in_size = buffer_size;
+ dev->int_in_endpoint_addr = endpoint->bEndpointAddress;
+ dev->int_in_buffer = NULL;
+ }
+
+ if (!dev->int_out_endpoint_addr &&
+ ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_OUT) &&
+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_INT)) {
+ /* we found a int out endpoint */
+ dev->int_out_endpoint_addr =
+ endpoint->bEndpointAddress;
+ dev->int_interval = endpoint->bInterval;
+ if ((dev->int_interval != 2)
+ && (dev->int_interval != 16)) {
+ pr_err("Interval value on int endpoint not ");
+ pr_err("as expected %d\n",
+ dev->int_interval);
+ dev->int_interval = 4;
+ }
+ dev->int_interval = 4;
+ /* Larger values can cause a problem */
+ }
+ }
+ if (!(dev->bulk_in_endpoint_addr && dev->bulk_out_endpoint_addr)) {
+ pr_err
+ ("Could not find both bulk-in and bulk-out endpoints");
+ goto error;
+ }
+ if (!(dev->int_in_endpoint_addr && dev->int_out_endpoint_addr)) {
+ pr_err
+ ("Could not find both int-in %d and int-out %d endpoints",
+ dev->int_in_endpoint_addr, dev->int_out_endpoint_addr);
+ }
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, dev);
+
+ /* allocate urbs */
+ retval = allocate_urbs_and_buffers(dev);
+ if (retval)
+ goto error;
+
+ /* Remainder of card setup */
+ dev->fifo_complete = 1;
+ fst_init_card(dev, dev_name);
+ flex_get_config(dev, dev->ports[0], NULL);
+ if (dev->ports[0]->usb_card_info.u_minor_rev >=
+ FIRST_FLEX_V2_MINOR_ID) {
+ fst_dbg(DBG_USB, "Flex Type Version 2 (minor rev = %d\n",
+ dev->ports[0]->usb_card_info.u_minor_rev);
+ dev->type = FST_TYPE_FLEX2;
+ } else {
+ fst_dbg(DBG_USB, "Flex Type Version 1 (minor rev = %d\n",
+ dev->ports[0]->usb_card_info.u_minor_rev);
+ dev->type = FST_TYPE_FLEX1;
+ }
+ strcpy(dev->ports[0]->dev->dev_addr,
+ dev->ports[0]->usb_card_info.sz_serial_no);
+ pr_info("%s-%s: (%s) %s, %d ports\n", dev->ports[0]->dev->name,
+ dev->ports[dev->nports - 1]->dev->name,
+ dev->ports[0]->usb_card_info.sz_serial_no,
+ type_strings[dev->type],
+ dev->nports);
+ /* we can register the device now, as it is ready */
+ retval = usb_register_dev(interface, &flex_class);
+ if (retval) {
+ /* something prevented us from registering this driver */
+ pr_err("Not able to get a minor for this device.");
+ usb_set_intfdata(interface, NULL);
+ goto error;
+ }
+ /* let the user know what node this device is now attached to */
+ instance = interface->minor % USB_MINOR_BASE;
+ pr_info("USB flex device now attached to Minor %d\n", instance);
+ fst_cards_list[instance] = dev;
+ dev->card_no = instance;
+ fst_ncards++; /* Record instance and bump it */
+ fst_dbg(DBG_USB, "Setting the interface to start switch to 80MHz\n");
+ i = usb_set_interface(dev->udev, 0, 0);
+ if (i)
+ fst_dbg(DBG_ASS, "Error in call to usb_set_interface %d\n", i);
+ dev->actual_length = 0;
+ dev->usb_trace_ptr = 0;
+ post_a_read_int_urb(dev); /* Start reading trace from card */
+ post_a_read_bulk_urb(dev); /* Post a read for data */
+ return 0; /* Success */
+
+ /* Failure. Release resources */
+error:
+ if (dev)
+ flex_delete(dev);
+ return retval;
+}
+
+/* Cleanup and close down a card
+ */
+static void flex_disconnect(struct usb_interface *interface)
+{
+ struct fst_card_info *dev;
+ int minor = interface->minor % USB_MINOR_BASE;
+ int i;
+
+ dev = usb_get_intfdata(interface);
+ fst_dbg(DBG_USB, "%s: Closing usb device for minor %d\n",
+ dev->ports[0]->dev->name, minor);
+
+ /* give back our minor */
+ device_remove_file(dev->sync_cdev, &dev_attr_serial);
+ device_destroy(farsync_class, MKDEV(fst_major, minor));
+ usb_deregister_dev(interface, &flex_class);
+ for (i = 0; i < dev->nports; i++) {
+ if (dev->ports[i]->char_file) {
+ fst_dbg(DBG_USB, "port is %p %p\n",
+ dev->ports[i]->char_file->private_data,
+ &dev->ports[i]);
+ if (dev->ports[i]->char_file->private_data ==
+ &dev->ports[i]) {
+ fst_dbg(DBG_USB,
+ "Zapped files pointer to us\n");
+ dev->ports[i]->char_file->private_data = NULL;
+ }
+ }
+ fst_tty_area[minor].state = FST_TTY_ST_DISC;
+ fst_dbg(DBG_TTY,
+ "Calling tty unregister device for port %d\n", minor);
+ tty_port_destroy(&fst_tty_area[minor].tty_port);
+ tty_unregister_device(serial_drv, minor);
+ if (dev->ports[i]->fifo_rxdata) {
+ /* Deallocate the rx fifo
+ */
+ u16 used_space;
+ used_space =
+ (u16) ((dev->ports[i]->fifo_rxdata->write_idx -
+ dev->ports[i]->fifo_rxdata->read_idx)
+ % dev->ports[i]->fifo_rxdata->fifo_length);
+ fst_dbg(DBG_ASS, "%s: Releasing Fifo memory at %p\n",
+ dev->ports[i]->dev->name,
+ dev->ports[i]->fifo_rxdata);
+ fst_dbg(DBG_ASS, "%s: %d bytes left in fifo\n",
+ dev->ports[i]->dev->name, used_space);
+ kfree(dev->ports[i]->fifo_rxdata);
+ dev->ports[i]->fifo_rxdata = NULL;
+ }
+ unregister_hdlc_device(dev->ports[i]->dev);
+ kfree(dev->ports[i]->dev);
+ fst_ports_list[dev->ports[i]->minor_dev_no] = NULL;
+ while (dev->ports[i]->txqe != dev->ports[i]->txqs) {
+ pr_info("Purging tx queue %d %d\n",
+ dev->ports[i]->txqe,
+ dev->ports[i]->txqs);
+ pr_info("%s: Closing port with data in tx queue %p\n",
+ dev->ports[i]->dev->name,
+ dev->ports[i]->txq[dev->ports[i]->txqs].frame);
+ dev_kfree_skb(dev->ports[i]->
+ txq[dev->ports[i]->txqs].frame);
+ dev->ports[i]->txqs++;
+ if (dev->ports[i]->txqs == FST_TXQ_DEPTH)
+ dev->ports[i]->txqs = 0;
+ }
+ kfree(dev->ports[i]);
+ }
+ fst_cards_list[dev->card_no] = NULL;
+ fst_ncards--;
+ /* decrement our usage count */
+ deallocate_urbs_and_buffers(dev);
+ usb_set_intfdata(interface, NULL);
+ flex_delete(dev);
+}
+
+static struct usb_driver fst_driver = {
+ .name = "fsflex",
+ .probe = flex_probe,
+ .disconnect = flex_disconnect,
+ .id_table = flex_table,
+};
+
+static int farsync_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, fst_proc_info, NULL);
+}
+
+static const struct file_operations farsync_proc_fops = {
+ .open = farsync_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init fst_init(void)
+{
+ int i;
+
+ INIT_WORK(&thresh_work_q, fst_process_threshold_work_q);
+ INIT_WORK(&port_stats_work_q, fst_process_port_stats_work_q);
+ for (i = 0; i < FST_MAX_CARDS; i++)
+ fst_cards_list[i] = NULL;
+ for (i = 0; i < FST_MAX_CARDS * FST_MAX_PORTS; i++) {
+ fst_ports_list[i] = NULL;
+ fst_net_list[i] = NULL;
+ }
+ spin_lock_init(&fst_work_q_lock);
+ /* Create the /proc entry for /proc/fsflex
+ */
+ proc_create("fsflex", 0, NULL, &farsync_proc_fops);
+ /* Register the char driver */
+ fst_tty_init();
+ fst_major = FST_TTY_MAJOR;
+ fst_dbg(DBG_ASS, "Creating the device class for fsflex\n");
+ farsync_class = class_create(THIS_MODULE, "fsflex");
+ i = usb_register(&fst_driver);
+ if (i) {
+ pr_err("usb_register failed. Error number %d", i);
+ return i;
+ }
+ pr_info("fst_iocinfo_version set to %d\n", fst_iocinfo_version);
+ return 0;
+}
+
+static void __exit fst_cleanup_module(void)
+{
+ usb_deregister(&fst_driver);
+ /* Remove the char device driver entry */
+ fst_tty_uninit();
+ remove_proc_entry("fsflex", NULL);
+ class_destroy(farsync_class);
+ pr_info("Unloading module fsflex\n");
+}
+
+/* Converts the FLEX_SERCONF structure from native to Little Endian
+ */
+static void flex_serconf_to_le(FLEX_SERCONF *usb_config)
+{
+ cpu_to_le32s(&usb_config->u_version);
+ cpu_to_le32s(&usb_config->iface);
+ cpu_to_le32s(&usb_config->termination);
+ cpu_to_le32s(&usb_config->line_speed);
+ cpu_to_le32s(&usb_config->clock);
+ cpu_to_le32s(&usb_config->encoding);
+ cpu_to_le32s(&usb_config->mode);
+ cpu_to_le32s(&usb_config->sync_char);
+ cpu_to_le32s(&usb_config->bit_order);
+ cpu_to_le32s(&usb_config->rxclock);
+ cpu_to_le32s(&usb_config->start);
+ cpu_to_le32s(&usb_config->num_tx_buffer);
+ cpu_to_le32s(&usb_config->num_rx_buffer);
+ cpu_to_le32s(&usb_config->size_tx_buffer);
+ cpu_to_le32s(&usb_config->size_rx_buffer);
+ cpu_to_le32s(&usb_config->data_bits);
+ cpu_to_le32s(&usb_config->parity);
+ cpu_to_le32s(&usb_config->stopbits);
+ cpu_to_le32s(&usb_config->flow_control);
+ cpu_to_le32s(&usb_config->timeout_enable);
+ cpu_to_le32s(&usb_config->timeout_length);
+ cpu_to_le32s(&usb_config->monitor_mode);
+ cpu_to_le32s(&usb_config->extended_clocking);
+ cpu_to_le32s(&usb_config->internal_tx_clock);
+ cpu_to_le32s(&usb_config->internal_rx_clock);
+ cpu_to_le32s(&usb_config->enable_nrzi_clocking);
+ cpu_to_le32s(&usb_config->xonchar);
+ cpu_to_le32s(&usb_config->xoffchar);
+ cpu_to_le32s(&usb_config->preserve_signals);
+ cpu_to_le32s(&usb_config->async_receive_mode);
+ cpu_to_le32s(&usb_config->crc_reset);
+ cpu_to_le32s(&usb_config->extended_config);
+}
+
+/* Converts the FLEX_SERCONF struct from Little Endian to native
+ */
+static void flex_serconf_to_cpu(FLEX_SERCONF *usb_config)
+{
+ le32_to_cpus(&usb_config->u_version);
+ le32_to_cpus(&usb_config->iface);
+ le32_to_cpus(&usb_config->termination);
+ le32_to_cpus(&usb_config->line_speed);
+ le32_to_cpus(&usb_config->clock);
+ le32_to_cpus(&usb_config->encoding);
+ le32_to_cpus(&usb_config->mode);
+ le32_to_cpus(&usb_config->sync_char);
+ le32_to_cpus(&usb_config->bit_order);
+ le32_to_cpus(&usb_config->rxclock);
+ le32_to_cpus(&usb_config->start);
+ le32_to_cpus(&usb_config->num_tx_buffer);
+ le32_to_cpus(&usb_config->num_rx_buffer);
+ le32_to_cpus(&usb_config->size_tx_buffer);
+ le32_to_cpus(&usb_config->size_rx_buffer);
+ le32_to_cpus(&usb_config->data_bits);
+ le32_to_cpus(&usb_config->parity);
+ le32_to_cpus(&usb_config->stopbits);
+ le32_to_cpus(&usb_config->flow_control);
+ le32_to_cpus(&usb_config->timeout_enable);
+ le32_to_cpus(&usb_config->timeout_length);
+ le32_to_cpus(&usb_config->monitor_mode);
+ le32_to_cpus(&usb_config->extended_clocking);
+ le32_to_cpus(&usb_config->internal_tx_clock);
+ le32_to_cpus(&usb_config->internal_rx_clock);
+ le32_to_cpus(&usb_config->enable_nrzi_clocking);
+ le32_to_cpus(&usb_config->xonchar);
+ le32_to_cpus(&usb_config->xoffchar);
+ le32_to_cpus(&usb_config->preserve_signals);
+ le32_to_cpus(&usb_config->async_receive_mode);
+ le32_to_cpus(&usb_config->crc_reset);
+ le32_to_cpus(&usb_config->extended_config);
+}
+
+/* Convert the Flex Interface Record definition into Little Endian,
+ * ready for USB
+ */
+static void flex_irex_to_le(IREX *stats)
+{
+ int i;
+ cpu_to_le32s(&stats->interface_record.rx_frame_count);
+ cpu_to_le32s(&stats->interface_record.tx_max_fr_size_now);
+ cpu_to_le32s(&stats->interface_record.status_count);
+ for (i = 0; i < SAMAXSTAT; i++)
+ cpu_to_le32s(&stats->interface_record.status_array[i]);
+ cpu_to_le32s(&stats->status_count);
+ cpu_to_le32s(&stats->opened_count);
+ cpu_to_le32s(&stats->tx_request_count);
+ cpu_to_le32s(&stats->tx_complete_count);
+ cpu_to_le32s(&stats->rx_posted_count);
+ cpu_to_le32s(&stats->rx_complete_count);
+}
+
+/* Converts the IREX object from Little Endian to native byte order
+ */
+static void flex_irex_to_cpu(IREX *stats)
+{
+ int i;
+ le32_to_cpus(&stats->interface_record.rx_frame_count);
+ le32_to_cpus(&stats->interface_record.tx_max_fr_size_now);
+ le32_to_cpus(&stats->interface_record.status_count);
+ for (i = 0; i < SAMAXSTAT; i++)
+ le32_to_cpus(&stats->interface_record.status_array[i]);
+ le32_to_cpus(&stats->status_count);
+ le32_to_cpus(&stats->opened_count);
+ le32_to_cpus(&stats->tx_request_count);
+ le32_to_cpus(&stats->tx_complete_count);
+ le32_to_cpus(&stats->rx_posted_count);
+ le32_to_cpus(&stats->rx_complete_count);
+}
+
+/* Converts the struct fstioc_req from native byte order to Little Endian
+ */
+static void flex_fstioc_req_to_le(struct fstioc_req *sys_request)
+{
+ cpu_to_le16s(&sys_request->msg_type);
+ cpu_to_le16s(&sys_request->msg_len);
+ cpu_to_le16s(&sys_request->ret_code);
+ cpu_to_le16s(&sys_request->i_reg_idx);
+ cpu_to_le16s(&sys_request->value);
+}
+
+/* Converts the struct fstioc_req from native to Little Endian byte order
+ */
+static void flex_fstioc_req_to_cpu(struct fstioc_req *sys_request)
+{
+ le16_to_cpus(&sys_request->msg_type);
+ le16_to_cpus(&sys_request->msg_len);
+ le16_to_cpus(&sys_request->ret_code);
+ le16_to_cpus(&sys_request->i_reg_idx);
+ le16_to_cpus(&sys_request->value);
+}
+
+/* Converts flex_card_info from native to Little Endian (USB format)
+ */
+static void flex_card_info_to_le(FLEX_CARD_INFO *usb_card_info)
+{
+ unsigned short i;
+ cpu_to_le32s(&usb_card_info->u_version);
+ cpu_to_le32s(&usb_card_info->u_major_rev);
+ cpu_to_le32s(&usb_card_info->u_minor_rev);
+ cpu_to_le32s(&usb_card_info->u_build_state);
+ cpu_to_le32s(&usb_card_info->u_cpu_speed);
+ cpu_to_le32s(&usb_card_info->u_mode);
+ cpu_to_le32s(&usb_card_info->u_software_version);
+ cpu_to_le32s(&usb_card_info->u_arm_version);
+ cpu_to_le32s(&usb_card_info->uflash_manf_id);
+ cpu_to_le32s(&usb_card_info->uflash_dev_id);
+ cpu_to_le32s(&usb_card_info->u_serocco_version);
+ for (i = 0; i < 17; i++)
+ cpu_to_le32s(&usb_card_info->spare[i]);
+}
+
+/* Converts the flex_card_info back from USB's Little Endian format into native
+ */
+static void flex_card_info_to_cpu(FLEX_CARD_INFO *usb_card_info)
+{
+ unsigned short i;
+ le32_to_cpus(&usb_card_info->u_version);
+ le32_to_cpus(&usb_card_info->u_major_rev);
+ le32_to_cpus(&usb_card_info->u_minor_rev);
+ le32_to_cpus(&usb_card_info->u_build_state);
+ le32_to_cpus(&usb_card_info->u_cpu_speed);
+ le32_to_cpus(&usb_card_info->u_mode);
+ le32_to_cpus(&usb_card_info->u_software_version);
+ le32_to_cpus(&usb_card_info->u_arm_version);
+ le32_to_cpus(&usb_card_info->uflash_manf_id);
+ le32_to_cpus(&usb_card_info->uflash_dev_id);
+ le32_to_cpus(&usb_card_info->u_serocco_version);
+ for (i = 0; i < 17; i++)
+ le32_to_cpus(&usb_card_info->spare[i]);
+}
+
+/* Convert the header back from USB's Little Endian format to native
+ */
+static void flex_txrx_preamble_to_cpu(FSCMN_TXRX_PREAMBLE *header)
+{
+ le32_to_cpus(&header->timestamp);
+ le16_to_cpus(&header->length);
+}
+
+module_init(fst_init);
+module_exit(fst_cleanup_module);
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists