* [PATCH] wan: new driver retina
@ 2007-10-25 6:49 Matti Linnanvuori
2007-10-25 9:18 ` Jeff Garzik
0 siblings, 1 reply; 8+ messages in thread
From: Matti Linnanvuori @ 2007-10-25 6:49 UTC (permalink / raw)
To: akpm, jgarzik, info; +Cc: netdev
From: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
Adding Retina G.703 and G.SHDSL driver.
Signed-off-by: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
---
Fixing bugs and style according to netdev mailing list comments.
diff -Napur linux-2.6.23/drivers/net/wan/Kconfig linux-2.6.24/drivers/net/wan/Kconfig
--- linux-2.6.23/drivers/net/wan/Kconfig 2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/drivers/net/wan/Kconfig 2007-10-25 09:23:19.933170522 +0300
@@ -494,4 +494,15 @@ config SBNI_MULTILINE
If unsure, say N.
+config RETINA
+ tristate "Retina support"
+ depends on PCI
+ help
+ Driver for Retina C5400 and E2200 network PCI cards, which
+ support G.703, G.SHDSL with Ethernet encapsulation or
+ character device stream.
+
+ To compile this driver as a module, choose M here: the
+ module will be called retina.
+
endif # WAN
diff -Napur linux-2.6.23/drivers/net/wan/Makefile linux-2.6.24/drivers/net/wan/Makefile
--- linux-2.6.23/drivers/net/wan/Makefile 2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/drivers/net/wan/Makefile 2007-10-23 12:31:17.598640178 +0300
@@ -42,6 +42,7 @@ obj-$(CONFIG_C101) += c101.o
obj-$(CONFIG_WANXL) += wanxl.o
obj-$(CONFIG_PCI200SYN) += pci200syn.o
obj-$(CONFIG_PC300TOO) += pc300too.o
+obj-$(CONFIG_RETINA) += retina.o
clean-files := wanxlfw.inc
$(obj)/wanxl.o: $(obj)/wanxlfw.inc
diff -Napur linux-2.6.23/drivers/net/wan/retina.c linux-2.6.24/drivers/net/wan/retina.c
--- linux-2.6.23/drivers/net/wan/retina.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.24/drivers/net/wan/retina.c 2007-10-25 09:43:59.029038615 +0300
@@ -0,0 +1,3709 @@
+/* retina.c: */
+
+/*
+ This driver is based on:
+
+ /drivers/net/fepci.c
+ FEPCI (Frame Engine for PCI) driver for Linux operating system
+
+ Copyright (C) 2002-2003 Jouni Kujala, Flexibilis Oy.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ All the drivers derived from or based on this code fall under the
+ GPL and must retain the copyright and license notice.
+*/
+
+#define DRV_NAME "retina"
+#define DRV_VERSION "1.2.7"
+
+/* Keep this if you want to have point-to-point links
+ ! Only interfaces listed in retina_ptp_interfaces will
+ be created in PtP mode
+ see retina_ptp_interfaces */
+#define FEPCI_POINT_TO_POINT
+
+/* need to update MODULE_PARM also */
+#define MAX_DEVICES 32u
+
+#define MAX_TX_UNITS 256u
+#define MAX_RX_UNITS 256u
+
+#define MAX_UNIT_SZ_ORDER 10u
+
+#define TX_RING_SIZE 8u
+#define RX_RING_SIZE 8u
+
+/* need to update MODULE_PARM also */
+#define CHANNELS 4u
+
+#define RX_FIFO_THRESHOLD_PACKET_MODE 0x4
+#define TX_FIFO_THRESHOLD_PACKET_MODE 0x4
+#define TX_DESC_THRESHOLD_PACKET_MODE 0x4
+
+#define RX_FIFO_THRESHOLD_STREAM_MODE 0x4
+#define TX_FIFO_THRESHOLD_STREAM_MODE 0x7
+#define TX_DESC_THRESHOLD_STREAM_MODE 0x1
+
+/* need to update MODULE_PARM also */
+#define MAX_INTERFACES (CHANNELS*MAX_DEVICES)
+
+static const char fepci_name[] = "retina";
+static const char fepci_alarm_manager_name[] = "retina alarm manager";
+static const char fepci_NAME[] = "RETINA";
+static const char fepci_netdev_name[] = "dcpxx";
+static const char fepci_proc_entry_name[] = "driver/retina";
+
+static unsigned int find_cnt;
+
+#ifdef FEPCI_POINT_TO_POINT
+static char *retina_ptp_interfaces[MAX_INTERFACES];
+static int retina_noarp_with_ptp = 1;
+#endif /* FEPCI_POINT_TO_POINT */
+
+#define fepci_features_proc_entry_name "driver/retina/%02x:%02x.%02x/features"
+#define fepci_settings_proc_entry_name "driver/retina/%02x:%02x.%02x/settings"
+#define fepci_status_proc_entry_name "driver/retina/%02x:%02x.%02x/status"
+
+/* Time in jiffies before concluding that the transmitter is hung */
+#define TX_TIMEOUT (5 * HZ)
+
+#include "retina.h"
+#include <linux/mm.h>
+#include <linux/random.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/version.h>
+#include <linux/pfn.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/unaligned.h>
+#include <asm/pgtable.h>
+
+MODULE_VERSION(DRV_VERSION);
+
+/* PCI I/O space extent */
+#define FEPCI_SIZE 0x20000
+#define PCI_IOTYPE (PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY)
+
+static struct pci_device_id fepci_pci_tbl[] __devinitdata = {
+ {0x1FC0, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x1FC0, 0x0301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+MODULE_DESCRIPTION("Frame Engine for PCI (FEPCI)");
+MODULE_AUTHOR("Jouni Kujala");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, fepci_pci_tbl);
+
+/* Linux appears to drop POINTOPOINT,BROADCAST and NOARP flags in SIOCSFLAGS
+ * This workaround allows load time per interface ptp mode configuration.
+ * Runtime ptp mode changes would either require changes to Linux or
+ * use of proprietary ioctls, which ifconfig knows nothing about anyway
+ */
+
+static unsigned interfaces = MAX_INTERFACES;
+#ifdef FEPCI_POINT_TO_POINT
+module_param_array(retina_ptp_interfaces, charp, &interfaces, S_IRUGO);
+module_param(retina_noarp_with_ptp, bool, S_IRUGO);
+MODULE_PARM_DESC(retina_noarp_with_ptp,
+ "0 to disable NOARP, "
+ "1 to enable NOARP on pointopoint interfaces");
+#endif
+
+struct fepci_ch_private {
+ unsigned int channel_number;
+ struct net_device *this_dev;
+
+ struct fepci_card_private *this_card_priv;
+
+ unsigned int reg_rxctrl;
+ unsigned int reg_txctrl;
+
+ struct fepci_desc *rx_desc; /* rx_ring start */
+ struct fepci_desc *tx_desc; /* tx_ring start */
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+
+ struct timer_list timer;
+ struct net_device_stats stats;
+
+ unsigned int rx_buf_sz; /* MTU+slack */
+ unsigned int cur_tx; /* the next filled tx_descriptor */
+ /* in stream mode the desc which is being transmitted */
+ /* rx_descriptor where next packet transferred */
+ unsigned int cur_rx;
+ /* in stream mode the desc which is being received */
+
+/* stream mode: */
+ bool in_eth_mode;
+ bool in_stream_mode;
+ bool stream_on;
+ u32 *rx_buffer;
+ u32 *tx_buffer;
+ unsigned bufsize_order; /* 10=1kB,11=2kB,12=4kB...16=64kB */
+ unsigned bufsize;
+ unsigned unit_sz_order; /* 8=256B...14=16kB */
+ unsigned unit_sz;
+ unsigned units; /* 2,4,8,16,...,256 */
+ /* fake units (and pointers) are for faking larger unit sizes to
+ * the user than what is the maximum internal unit size in FEPCI */
+ unsigned fake_unit_sz_order;
+ unsigned fake_unit_sz;
+ unsigned fake_units;
+ u32 *tx_unit[MAX_TX_UNITS];
+ u32 *rx_unit[MAX_RX_UNITS];
+ unsigned cur_tx_unit; /* last sent tx_unit */
+ /* rx_unit where to next packet is transferred */
+ unsigned cur_rx_unit;
+/* char device: */
+ unsigned minor; /* currently the same as card_nuber */
+/* debugging stuff for stream mode: */
+ /* rx-errors in descriptors */
+ unsigned rx_desc_fifo_err_stream;
+ unsigned rx_desc_size_err_stream;
+ unsigned rx_desc_octet_err_stream;
+ unsigned rx_desc_line_err_stream;
+ /* tx-errors in descriptors */
+ unsigned tx_desc_fifo_err_stream;
+ /* rx-errors in interrupts */
+ unsigned rx_int_fifo_err_stream;
+ unsigned rx_int_frame_dropped_err_stream;
+ /* tx-errors in interrupts */
+ unsigned tx_int_fifo_err_stream;
+/* other: */
+ /* tx interrupts since last timer interrupt */
+ unsigned tx_interrupts_since_last_timer;
+};
+
+struct fepci_card_private {
+ unsigned int card_number;
+ u8 *ioaddr;
+ /* Process ID of the current mailbox user
+ * (for whom it is reserved for) */
+ unsigned int ioctl_saved_pid;
+ struct pci_dev *pci_dev;
+ struct fepci_ch_private *ch_privates[CHANNELS];
+
+ wait_queue_head_t alarm_manager_wait_q;
+ struct timer_list mailbox_timer;
+
+ wait_queue_head_t stream_receive_q;
+ wait_queue_head_t stream_transmit_q;
+ wait_queue_head_t stream_both_q;
+
+ struct rw_semaphore semaphore;
+};
+
+/* Offsets to the FEPCI registers */
+enum fepci_offsets {
+ reg_custom = 0x40,
+
+ reg_first_int_mask = 0x80,
+ reg_first_int_status = 0xc0,
+
+ reg_first_rxctrl = 0x4000,
+ to_next_rxctrl = 0x80,
+
+ reg_first_txctrl = 0x6000,
+ to_next_txctrl = 0x80,
+
+ first_rx_desc = 0x10000,
+ to_next_ch_rx_desc = 0x200,
+
+ first_tx_desc = 0x18000,
+ to_next_ch_tx_desc = 0x200,
+};
+
+enum reg_custom_bits {
+ AM_interrupt_mask = 0x1,
+ AM_interrupt_status = 0x100,
+};
+
+enum reg_receive_control {
+ Rx_fifo_threshold = 0x7,
+ Receive_enable = 0x80000000,
+};
+
+enum reg_transmit_control {
+ Tx_fifo_threshold = 0x7,
+ Tx_desc_threshold = 0x700,
+ Transmit_enable = 0x80000000,
+};
+
+enum int_bits {
+ MaskFrameReceived = 0x01, MaskRxFifoError =
+ 0x02, MaskRxFrameDroppedError = 0x04,
+ MaskFrameTransmitted = 0x40, MaskTxFifoError = 0x80,
+ MaskAllInts = 0xc7,
+ IntrFrameReceived = 0x01, IntrRxFifoError =
+ 0x02, IntrRxFrameDroppedError = 0x04,
+ IntrFrameTransmitted = 0x40, IntrTxFifoError = 0x80,
+ IntrAllInts = 0xc7,
+};
+
+/* The FEPCI Rx and Tx buffer descriptors
+ * Elements are written as 32 bit for endian portability */
+
+struct fepci_desc {
+ u32 desc_a;
+ u32 desc_b;
+};
+
+enum desc_b_bits {
+ frame_length = 0xFFF,
+ fifo_error = 0x10000,
+ size_error = 0x20000,
+ crc_error = 0x40000,
+ octet_error = 0x80000,
+ line_error = 0x100000,
+ enable_transfer = 0x80000000,
+ transfer_not_done = 0x80000000,
+};
+
+/* global variables (common to whole driver, all the cards): */
+static int major; /* char device major number */
+static struct fepci_card_private card_privates[MAX_DEVICES];
+static unsigned long stream_pointers;
+static struct proc_dir_entry *proc_root_entry;
+
+static void set_int_mask(int channel, u_char value,
+ struct fepci_card_private *cp)
+{
+ void *address;
+ unsigned shift, oldvalue;
+ pr_debug("set_int_mask\n");
+ address = (cp->ioaddr) + reg_first_int_mask + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ oldvalue = readl((void *)address);
+ oldvalue &= ~(0xff << shift); /* clear bits */
+ oldvalue |= value << shift; /* set bits */
+ writel(oldvalue, (void *)address);
+}
+
+static void clear_int(unsigned channel, unsigned value, void *ioaddr)
+{
+ void *address;
+ unsigned shift, longvalue;
+ pr_debug("clear_int\n");
+ address = ioaddr + reg_first_int_status + (channel / 4) * 4;
+ shift = 8 * (channel % 4);
+ longvalue = value << shift;
+ writel(~longvalue, (void *)address);
+}
+
+static u_char get_int_status(int channel, void *ioaddr)
+{
+ void *address;
+ unsigned shift, oldvalue;
+ pr_debug("get_int_status\n");
+ address = ioaddr + reg_first_int_status + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ oldvalue = readl((void *)address);
+ oldvalue &= (0xff << shift); /* clear other bits */
+ return (oldvalue >> shift);
+}
+
+static void fillregisterswith_00(void *ioaddr)
+{
+ pr_debug("fillregisterswith_00\n");
+ writel(0x0, (void *)(ioaddr + reg_first_rxctrl));
+ writel(0x0, (void *)(ioaddr + reg_first_txctrl));
+ writel(0x0, (void *)(ioaddr + reg_first_int_mask));
+ writel(0x0, (void *)(ioaddr + reg_first_int_status));
+ writel(0x0, (void *)(ioaddr + first_rx_desc));
+ writel(0x0, (void *)(ioaddr + first_tx_desc));
+}
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int fepci_open(struct net_device *dev);
+static void fepci_timer(unsigned long data);
+static void fepci_tx_timeout(struct net_device *dev);
+static void fepci_init_ring(struct net_device *dev);
+static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t fepci_interrupt(int irq, void *dev_instance);
+static int fepci_rx(struct net_device *dev);
+static int fepci_close(struct net_device *dev);
+static struct net_device_stats *fepci_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static void fepci_remove_one(struct pci_dev *pdev);
+
+/* proc filesystem functions introduced: */
+
+static int fepci_proc_init_driver(void);
+static void fepci_proc_cleanup_driver(void);
+static void fepci_proc_init_card(int card_number, void *card_data);
+static void fepci_proc_cleanup_card(int card_number);
+
+/* char device operations: */
+
+static ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+ loff_t *f_pos);
+static int fepci_char_open(struct inode *inode, struct file *filp);
+static int fepci_char_release(struct inode *inode, struct file *filp);
+static int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma);
+static int fepci_char_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+struct file_operations fepci_char_fops = {
+ .read = fepci_char_read,
+ .ioctl = fepci_char_ioctl,
+ .open = fepci_char_open,
+ .release = fepci_char_release,
+ .mmap = fepci_char_mmap
+};
+
+static int fepci_char_open(struct inode *inode, struct file *filp)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ pr_debug("fepci_char_open\n");
+ if (unlikely(minor >= find_cnt || card_privates[minor].pci_dev == NULL))
+ return -ENXIO;
+ filp->f_op = &fepci_char_fops;
+ if (unlikely(!try_module_get(THIS_MODULE)))
+ return -EBUSY;
+ return 0;
+}
+
+static int fepci_char_release(struct inode *inode, struct file *filp)
+{
+ pr_debug("fepci_char_release\n");
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+static void fepci_vma_open(struct vm_area_struct *vma)
+{
+ pr_debug("fepci_vma_open\n");
+}
+
+static void fepci_vma_close(struct vm_area_struct *vma)
+{
+ pr_debug("fepci_vma_close\n");
+ module_put(THIS_MODULE);
+}
+
+static struct vm_operations_struct fepci_vm_ops = {
+ .open = fepci_vma_open,
+ .close = fepci_vma_close
+};
+
+static int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long size = vma->vm_end - vma->vm_start;
+
+ unsigned long virtual_address = 0;
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_ops = &fepci_vm_ops;
+ vma->vm_file = filp;
+
+ if (offset == STREAM_BUFFER_POINTER_AREA) {
+ virtual_address = stream_pointers;
+ if (virtual_address == 0) {
+ printk(KERN_WARNING "%s: mmap: internal error.\n",
+ fepci_name);
+ return -ENOMEM;
+ }
+ if (size > (1 << PAGE_SHIFT)) {
+ printk(KERN_WARNING
+ "%s: mmap: area size over range.\n", fepci_name);
+ return -EINVAL;
+ }
+ } else {
+ unsigned int page;
+
+ unsigned int card;
+ unsigned int channel;
+ unsigned int area; /* 0=rx, 1=tx */
+
+ card = (offset >> CARD_ADDRESS_SHIFT) & 0xf;
+ channel = (offset >> CHANNEL_ADDRESS_SHIFT) & 0xf;
+ area = (offset >> AREA_ADDRESS_SHIFT) & 0xf;
+ page = (offset & 0xffff); /* >> PAGE_SHIFT; */
+
+ if (area == 0) {
+ /* if there really is such card */
+ if (card < find_cnt && card_privates[card].pci_dev)
+ virtual_address =
+ (unsigned long)card_privates[card].
+ ch_privates[channel]->rx_buffer;
+ else
+ goto INVALID;
+ } else if (area == 1) {
+ /* if there really is such card */
+ if (card < find_cnt && card_privates[card].pci_dev)
+ virtual_address =
+ (unsigned long)card_privates[card].
+ ch_privates[channel]->tx_buffer;
+ else
+ goto INVALID;
+ } else {
+INVALID:
+ pr_debug("%s: mmap: invalid address 0x%lx\n",
+ fepci_NAME, virtual_address);
+ return -EINVAL;
+ }
+ if (unlikely(virtual_address == 0))
+ goto INVALID;
+ }
+
+ if (unlikely(!try_module_get(THIS_MODULE)))
+ return -EBUSY;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ {
+ unsigned pfn = PFN_DOWN(virt_to_phys((void *)virtual_address));
+ int error = io_remap_pfn_range(vma, vma->vm_start, pfn,
+ size, vma->vm_page_prot);
+ if (unlikely(error))
+ return error;
+ }
+ fepci_vma_open(vma);
+ return 0;
+}
+
+/* mmap operations end */
+
+/* char operations start */
+
+static void fepci_copy_to_user(unsigned long to, void *from, unsigned long len,
+ int shrink)
+{
+ unsigned int i;
+ pr_debug("fepci_copy_to_user\n");
+ if (shrink) {
+ for (i = 0; i < len; i += 2) {
+ put_user((((unsigned long *)from)[i / 2]) & 0xff,
+ (unsigned char *)(to + i));
+ put_user((((unsigned long *)from)[i / 2]) >> 8,
+ (unsigned char *)(to + i + 1));
+ }
+ } else {
+ for (i = 0; i < len; i += 4)
+ put_user(((unsigned long *)from)[i / 4],
+ (unsigned long *)(to + i));
+ }
+}
+
+static void fepci_copy_from_user(void *to, unsigned long from,
+ unsigned long len, int enlarge)
+{
+ unsigned int i;
+ if (enlarge) {
+ for (i = 0; i < len; i += 2) {
+ unsigned char temp1;
+ unsigned char temp2;
+ get_user(temp1, (unsigned char *)(from + i));
+ get_user(temp2, (unsigned char *)(from + i + 1));
+ *((unsigned long *)(to + i * 2)) = temp1 + (temp2 << 8);
+ }
+ } else {
+ for (i = 0; i < len; i += 4)
+ get_user(((unsigned long *)to)[i / 4],
+ (unsigned long *)(from + i));
+ }
+}
+
+static unsigned get_semafore(struct fepci_real_mailbox *mailbox)
+{
+ unsigned semafore = readb(&mailbox->Semafore_Mail_number);
+ pr_debug("get_semafore = %x\n", semafore);
+ return semafore;
+}
+
+static void set_semafore(struct fepci_real_mailbox *mailbox, unsigned semafore)
+{
+ unsigned number = readl(&mailbox->Semafore_Mail_number);
+ pr_debug("got number %u at %p.\n", number,
+ &mailbox->Semafore_Mail_number);
+ number = ((number & ~0xFF) | semafore) + (1 << 8);
+ pr_debug
+ ("increases the mail number to %u at the same time at %p.\n",
+ number, &mailbox->Semafore_Mail_number);
+ writel(number, &mailbox->Semafore_Mail_number);
+ pr_debug("set_semafore %p, %u returns 0.\n", mailbox, semafore);
+}
+
+static void fepci_mailbox_timer(unsigned long data)
+{
+ int card_number = data;
+ unsigned int *saved_pid = &card_privates[card_number].ioctl_saved_pid;
+ void *ioaddr = card_privates[card_number].ioaddr;
+ struct fepci_real_mailbox *real_mailbox =
+ (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
+
+ set_semafore(real_mailbox, 0x0);
+ *saved_pid = 0;
+}
+
+static int fepci_char_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ void *ioaddr;
+ struct fepci_real_mailbox *real_mailbox;
+ int retval = 0;
+ unsigned int *saved_pid;
+ unsigned int my_pid;
+
+ if (minor >= find_cnt || card_privates[minor].pci_dev == NULL) {
+ printk(KERN_WARNING
+ "%s: trying to access a card that does not exist.\n",
+ fepci_NAME);
+ /* if trying to access a card that does not exist */
+ return -ENXIO;
+ }
+
+ pr_debug("fepci_char_ioctl minor %u.\n", minor);
+
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ if (!access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ if (!access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+
+ ioaddr = card_privates[minor].ioaddr;
+ real_mailbox =
+ (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
+ saved_pid = &card_privates[minor].ioctl_saved_pid;
+ my_pid = current->pid;
+
+ switch (cmd) {
+ case FEPCI_IOCTL_STREAM_TRANSMIT_POLL:
+ /* here: arg == channel number */
+ if (arg < 0 || arg >= CHANNELS
+ || !(card_privates[minor].ch_privates[arg]->stream_on))
+ return 0x2;
+ {
+ u32 pointer = *USER_TX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+ wait_event_interruptible((card_privates[minor].
+ stream_transmit_q)
+ ,
+ (pointer !=
+ *USER_TX_S_FAKE_POINTER
+ (minor, arg,
+ stream_pointers)));
+ return 0x1;
+ }
+ return retval;
+ case FEPCI_IOCTL_STREAM_RECEIVE_POLL:
+ /* here: arg == channel number */
+ if (arg < 0 || arg >= CHANNELS
+ || !(card_privates[minor].ch_privates[arg]->stream_on))
+ return 0x2;
+ {
+ u32 pointer = *USER_RX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+ wait_event_interruptible((card_privates[minor].
+ stream_receive_q)
+ ,
+ (pointer !=
+ *USER_RX_S_FAKE_POINTER
+ (minor, arg,
+ stream_pointers)));
+ retval = 0x1;
+ }
+ return retval;
+ case FEPCI_IOCTL_STREAM_BOTH_POLL:
+ /* here: arg == channel number */
+ if (arg < 0 || arg >= CHANNELS
+ || !(card_privates[minor].ch_privates[arg]->stream_on))
+ return 0x2;
+ {
+ u32 temp_tx_pointer =
+ *USER_TX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+ u32 temp_rx_pointer =
+ *USER_RX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+
+ wait_event_interruptible((card_privates[minor].
+ stream_both_q)
+ ,
+ (temp_tx_pointer !=
+ *USER_TX_S_FAKE_POINTER
+ (minor, arg, stream_pointers))
+ || (temp_rx_pointer !=
+ *USER_RX_S_FAKE_POINTER
+ (minor, arg,
+ stream_pointers)));
+ retval = 0x1;
+ }
+ return retval;
+ case FEPCI_IOCTL_R_SHARED_MEM:
+ pr_debug(" %s: ioctl read shared mem commanded.\n",
+ fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_SHARED_MEM_OFFSETT,
+ _IOC_SIZE(cmd), 0);
+ break;
+ case FEPCI_IOCTL_W_SHARED_MEM:
+ pr_debug(" %s: ioctl write shared mem commanded.\n",
+ fepci_NAME);
+ fepci_copy_from_user(ioaddr + FEPCI_SHARED_MEM_OFFSETT,
+ arg, _IOC_SIZE(cmd), 0);
+ break;
+ case FEPCI_IOCTL_G_IDENTIFICATION:
+ pr_debug(" %s: IOCTL_G_IDENTIFICATION commanded.\n",
+ fepci_NAME);
+ fepci_copy_to_user(arg,
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_G_FEATURES:
+ pr_debug(" %s: IOCTL_G_FEATURES commanded.\n", fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_FEATURES_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_G_SETTINGS:
+ pr_debug(" %s: IOCTL_G_SETTINGS commanded.\n", fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_SETTINGS_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_G_STATUS:
+ pr_debug(" %s: IOCTL_G_STATUS commanded.\n", fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_STATUS_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_B_POLL:
+ pr_debug(" %s: IOCTL_B_POLL commanded.\n", fepci_NAME);
+ retval = get_semafore(real_mailbox);
+ if ((retval == 0x20 || retval == 0x21 || retval == 0x40)
+ && *saved_pid != my_pid)
+ retval = 0x7;
+ del_timer_sync(&card_privates[minor].mailbox_timer);
+ card_privates[minor].mailbox_timer.expires = jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.function =
+ &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+ break;
+ case FEPCI_IOCTL_B_GRAB:
+ pr_debug("%s: IOCTL_B_GRAB commanded.\n", fepci_NAME);
+ if ((my_pid != *saved_pid) && (*saved_pid != 0)) {
+ retval = 0x2;
+ break;
+ }
+ pr_debug("%s: IOCTL_B_GRAB getting semaphore.\n", fepci_NAME);
+ if (get_semafore(real_mailbox) == 0x0) {
+ pr_debug("%s: IOCTL_B_GRAB setting semaphore.\n",
+ fepci_NAME);
+ set_semafore(real_mailbox, 0x40);
+ pr_debug("%s: IOCTL_B_GRAB sleeping.\n", fepci_NAME);
+ msleep(1); /* delay at least 1 millisecond */
+ pr_debug
+ ("%s: IOCTL_B_GRAB getting semaphore again.\n",
+ fepci_NAME);
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ retval = 0x0;
+ pr_debug
+ ("%s: IOCTL_B_GRAB saving pid to %p.\n",
+ fepci_NAME, saved_pid);
+ *saved_pid = my_pid;
+ pr_debug
+ ("%s: IOCTL_B_GRAB deleting timer at %p.\n",
+ fepci_NAME,
+ &card_privates[minor].mailbox_timer);
+ del_timer_sync(&card_privates[minor].
+ mailbox_timer);
+ card_privates[minor].mailbox_timer.
+ expires = jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.
+ function = &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+ break;
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ default:
+ retval = 0xff;
+ }
+ } else {
+ switch (get_semafore(real_mailbox)) {
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ default:
+ retval = 0xff;
+ }
+ }
+ break;
+ case FEPCI_IOCTL_B_RELEASE:
+ pr_debug(" %s: IOCTL_B_RELEASE commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ break;
+ }
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ case 0x20:
+ retval = 0x0;
+ set_semafore(real_mailbox, 0x0);
+ *saved_pid = 0;
+ del_timer(&card_privates[minor].mailbox_timer);
+ break;
+ case 0x21:
+ retval = 0x04;
+ break;
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ default:
+ retval = 0xff;
+ }
+ break;
+ case FEPCI_IOCTL_B_S_CMAIL:
+ pr_debug(" %s: IOCTL_B_S_CMAIL commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ break;
+ }
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ case 0x20:
+ case 0x21:
+ /* copy the mailbox */
+ fepci_copy_from_user(ioaddr +
+ FEPCI_MAILBOX_OFFSETT + 4,
+ arg + 2, _IOC_SIZE(cmd) - 2, 1);
+ /* semafore -> 10 */
+ set_semafore(real_mailbox, 0x10);
+ retval = 0x0;
+
+ del_timer_sync(&card_privates[minor].mailbox_timer);
+ card_privates[minor].mailbox_timer.expires =
+ jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.function =
+ &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+
+ break;
+
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ case 0x0:
+ retval = 0x3;
+ break;
+ default:
+ retval = 0xff;
+ }
+ break;
+ case FEPCI_IOCTL_B_S_QMAIL:
+ pr_debug(" %s: IOCTL_B_S_QMAIL commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ break;
+ }
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ case 0x20:
+ case 0x21:
+ /* copy the mailbox; */
+ fepci_copy_from_user(ioaddr +
+ FEPCI_MAILBOX_OFFSETT + 4,
+ arg + 2, _IOC_SIZE(cmd) - 2, 1);
+ /* semafore -> 11 */
+ set_semafore(real_mailbox, 0x11);
+ retval = 0x0;
+
+ del_timer_sync(&card_privates[minor].mailbox_timer);
+ card_privates[minor].mailbox_timer.expires =
+ jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.function =
+ &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+
+ break;
+
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ case 0x0:
+ retval = 0x3;
+ break;
+ default:
+ retval = 0xff;
+ }
+ break;
+ case FEPCI_IOCTL_B_G_MAIL:
+ pr_debug(" %s: IOCTL_B_G_MAIL commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ } else {
+ switch (get_semafore(real_mailbox)) {
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ case 0x40:
+ case 0x20:
+ case 0x21:
+ retval = 0x0;
+ fepci_copy_to_user(arg,
+ ioaddr +
+ FEPCI_MAILBOX_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+
+ del_timer_sync(&card_privates[minor].
+ mailbox_timer);
+ card_privates[minor].mailbox_timer.
+ expires = jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.
+ function = &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+
+ break;
+ case 0x0:
+ retval = 0x3;
+ break;
+ default:
+ retval = 0xff;
+ }
+ }
+ if (retval != 0) {
+ static unsigned char seven = 7;
+ /* copy four lowest bytes from the mailbox */
+ fepci_copy_to_user(arg,
+ ioaddr + FEPCI_MAILBOX_OFFSETT,
+ 4, 1);
+ /* lowest byte = 0x7 */
+ __put_user(arg, &seven);
+ }
+ break;
+ case FEPCI_IOCTL_ALARM_MANAGER:
+ pr_debug(" %s: IOCTL_ALARM_MANAGER commanded.\n", fepci_NAME);
+ interruptible_sleep_on(&(card_privates[minor].
+ alarm_manager_wait_q));
+ return retval;
+ default:
+ pr_debug(" %s: Unknown ioctl command 0x%x.\n", fepci_NAME,
+ cmd);
+ return -ENOTTY;
+ }
+ return retval;
+}
+
+static ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+ loff_t *f_pos)
+{
+ pr_debug("fepci_char_read\n");
+ if (count > 1)
+ count = 1;
+ if (unlikely(copy_to_user(buf, "\n", count)))
+ return -EFAULT;
+ return count;
+}
+
+static int fepci_register_char_device(void)
+{
+ int error =
+ register_chrdev(0 /* dynamic */ , fepci_name, &fepci_char_fops);
+ if (unlikely(error < 0))
+ printk(KERN_WARNING
+ "%s: unable to register char device.\n", fepci_NAME);
+ else
+ pr_debug("%s: registered char device, major:0x%x.\n",
+ fepci_NAME, error);
+ return error;
+}
+
+static void fepci_unregister_char_device(void)
+{
+ unregister_chrdev(major, fepci_name);
+}
+
+/* char operations end */
+
+/* stream operations start */
+
+static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance);
+static int fepci_stream_close(struct net_device *dev);
+
+static int fepci_stream_open(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned tx_pages, rx_pages, tx_order, rx_order;
+ unsigned page_number;
+ unsigned int i;
+
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_eth_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Interface is in Ethernet mode, "
+ "cannot open stream interface.\n", fepci_NAME);
+BUSY:
+ return -EBUSY;
+ }
+ if (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Interface is already in stream mode, "
+ "cannot open stream interface.\n", fepci_NAME);
+ goto BUSY;
+ }
+
+ if (unlikely(fp->this_card_priv->pci_dev == NULL)) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -ENXIO;
+ }
+
+ fp->bufsize = 1 << fp->bufsize_order;
+
+ if (fp->fake_unit_sz_order < 5) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Unit size has to be at least 32 Bytes.\n",
+ fepci_NAME);
+INVALID:
+ return (-EINVAL);
+ }
+
+ if (fp->fake_unit_sz_order >= fp->bufsize_order) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Bufsize has to be greater than unit size.\n",
+ fepci_NAME);
+ goto INVALID;
+ }
+
+ if (fp->fake_unit_sz_order >= MAX_UNIT_SZ_ORDER) {
+ fp->unit_sz_order = MAX_UNIT_SZ_ORDER;
+ } else {
+ fp->unit_sz_order = fp->fake_unit_sz_order;
+ }
+
+ fp->fake_unit_sz = 1 << fp->fake_unit_sz_order;
+ fp->unit_sz = 1 << fp->unit_sz_order;
+ fp->units = 1 << (fp->bufsize_order - fp->unit_sz_order);
+ fp->fake_units = 1 << (fp->bufsize_order - fp->fake_unit_sz_order);
+
+ /* reserve memory */
+ if (fp->bufsize_order < PAGE_SHIFT) {
+ rx_order = 0;
+ tx_order = 0;
+ rx_pages = 1;
+ tx_pages = 1;
+ } else {
+ tx_order = fp->bufsize_order - PAGE_SHIFT;
+ tx_pages = 1 << tx_order;
+ rx_order = tx_order + 1;
+ rx_pages = 1 << rx_order;
+ }
+ fp->in_stream_mode = 1;
+ fp->tx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, tx_order);
+ if (!fp->tx_buffer)
+ goto NO_MEMORY;
+ fp->rx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, rx_order);
+ if (!fp->rx_buffer) {
+NO_MEMORY:
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate memory for buffers.\n",
+ fepci_NAME);
+ fepci_stream_close(dev);
+ return (-ENOMEM);
+ }
+
+ for (page_number = 0; page_number < rx_pages; page_number++)
+ /* make pages reserved to allow remappping pages
+ with io_remap_pfn_range */
+ SetPageReserved(virt_to_page
+ ((unsigned long)fp->rx_buffer +
+ (page_number << PAGE_SHIFT)));
+ for (page_number = 0; page_number < tx_pages; page_number++)
+ /* make pages reserved to allow remappping pages
+ with io_remap_pfn_range */
+ SetPageReserved(virt_to_page
+ ((unsigned long)fp->tx_buffer +
+ (page_number << PAGE_SHIFT)));
+
+ for (i = 0; i < (fp->bufsize) / 4; i++)
+ fp->tx_buffer[i] = 0xffffffff;
+
+ /* + fp->channel_number; */
+ *USER_RX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
+ stream_pointers) = 0;
+ /* + fp->channel_number; */
+ *USER_TX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
+ stream_pointers) = 0;
+ /* + fp->channel_number; */
+ *USER_RX_S_FAKE_POINTER(fp->this_card_priv->card_number,
+ fp->channel_number, stream_pointers) = 0;
+ /* + fp->channel_number; */
+ *USER_TX_S_FAKE_POINTER(fp->this_card_priv->card_number,
+ fp->channel_number, stream_pointers) = 0;
+
+ pr_debug("%s: Bufsize is 0x%x.\n", fepci_NAME, fp->bufsize);
+ pr_debug("%s: Unit_size is 0x%x.\n", fepci_NAME, fp->unit_sz);
+ pr_debug("%s: Number of units is 0x%x.\n", fepci_NAME, fp->units);
+
+ pr_debug("%s: Fake_unit_size is 0x%x.\n", fepci_NAME,
+ fp->fake_unit_sz);
+ pr_debug("%s: Number of fake units is 0x%x.\n", fepci_NAME,
+ fp->fake_units);
+
+ /* init ring buffers */
+ for (i = 0; i < MAX_RX_UNITS; i++)
+ fp->rx_unit[i] =
+ (u32 *) ((u32) (fp->rx_buffer) + (fp->unit_sz * i));
+ for (i = 0; i < MAX_TX_UNITS; i++)
+ fp->tx_unit[i] =
+ (u32 *) ((u32) (fp->tx_buffer) + (fp->unit_sz * i));
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ writel(0, &fp->rx_desc[i].desc_a);
+ writel(0, &fp->rx_desc[i].desc_b);
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ writel(0, &fp->tx_desc[i].desc_a);
+ writel(0, &fp->tx_desc[i].desc_b);
+ }
+
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static int fepci_stream_start(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned i;
+ down_write(&fp->this_card_priv->semaphore);
+
+ if (fp->in_stream_mode == 0) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Interface is not in stream mode, "
+ "streaming cannot be started.\n", fepci_NAME);
+ return (-EBUSY);
+ }
+ if (fp->stream_on) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Streaming is already on, "
+ "streaming cannot be started.\n", fepci_NAME);
+ return (-EBUSY);
+ }
+
+ {
+ /* reserve irq */
+ int error = request_irq(dev->irq, &fepci_stream_interrupt,
+ IRQF_SHARED, dev->name, dev);
+ if (error) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate IRQ %d, error 0x%x\n",
+ fepci_NAME, dev->irq, error);
+ return -ENOMEM;
+ }
+ }
+
+ fp->stream_on = 1;
+
+ /* sending &receiving on, start from the beginning of the buffer */
+ fp->cur_tx_unit = 0;
+ fp->cur_rx_unit = 0;
+ fp->cur_tx = 0;
+ fp->cur_rx = 0;
+
+ /* all the descriptors ready to go: */
+ for (i = 0; i < min(RX_RING_SIZE, TX_RING_SIZE); i++) {
+ dma_addr_t address = pci_map_single(fp->this_card_priv->pci_dev,
+ fp->
+ rx_unit[(fp->cur_rx_unit +
+ i) % fp->units],
+ fp->unit_sz,
+ PCI_DMA_FROMDEVICE);
+ if (unlikely(pci_dma_mapping_error(address)))
+ printk(KERN_WARNING
+ "%s: failed to map DMA buffer.\n", fepci_NAME);
+ else {
+ writel(address,
+ &fp->rx_desc[(fp->cur_rx + i) &
+ (RX_RING_SIZE - 1)].desc_a);
+ if (!
+ (readl
+ (&fp->
+ rx_desc[(fp->cur_rx + i) & (RX_RING_SIZE -
+ 1)].
+ desc_b) & enable_transfer))
+ writel(enable_transfer,
+ &fp->rx_desc[(fp->cur_rx + i) %
+ RX_RING_SIZE].desc_b);
+ }
+ address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ fp->tx_unit[(fp->cur_tx_unit + i) %
+ fp->units], fp->unit_sz,
+ PCI_DMA_TODEVICE);
+ writel(address,
+ &fp->
+ tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE - 1)].desc_a);
+ if (unlikely(pci_dma_mapping_error(address)))
+ printk(KERN_WARNING
+ "%s: failed to map DMA buffer.\n", fepci_NAME);
+ else {
+ if (!
+ (readl
+ (&fp->
+ tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE -
+ 1)].
+ desc_b) & enable_transfer))
+ writel(enable_transfer |
+ (fp->unit_sz & frame_length),
+ &fp->
+ tx_desc[(fp->cur_tx +
+ i) & (TX_RING_SIZE -
+ 1)].desc_b);
+ }
+ }
+
+ /* irq on */
+ set_int_mask(fp->channel_number,
+ MaskFrameReceived | MaskFrameTransmitted |
+ MaskRxFifoError | MaskRxFrameDroppedError |
+ MaskTxFifoError, fp->this_card_priv);
+ {
+ void *ioaddr = (void *)dev->base_addr;
+ /* Start Rx and Tx channels */
+ writel(Receive_enable |
+ (Rx_fifo_threshold & RX_FIFO_THRESHOLD_STREAM_MODE),
+ (void *)(ioaddr + fp->reg_rxctrl));
+ writel((Transmit_enable |
+ (Tx_desc_threshold &
+ (TX_DESC_THRESHOLD_STREAM_MODE << 8)) |
+ (Tx_fifo_threshold & TX_FIFO_THRESHOLD_STREAM_MODE)),
+ (void *)(ioaddr + fp->reg_txctrl));
+ }
+ up_write(&fp->this_card_priv->semaphore);
+
+ return 0;
+}
+
+static int fepci_stream_stop(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ void *ioaddr = (void *)dev->base_addr;
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_stream_mode == 0) {
+ up_write(&fp->this_card_priv->semaphore);
+ return (1);
+ }
+ fp->stream_on = 0;
+ /* Stop Rx and Tx channels. */
+ writel(0x0, (void *)(ioaddr + fp->reg_rxctrl));
+ writel(0x0, (void *)(ioaddr + fp->reg_txctrl));
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
+
+ /* unregister irq */
+ free_irq(dev->irq, dev);
+
+ {
+ unsigned i = min(RX_RING_SIZE, TX_RING_SIZE) - 1;
+ do {
+ dma_addr_t bus_address = readl(&fp->rx_desc[i].desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz,
+ PCI_DMA_FROMDEVICE);
+ bus_address = readl(&fp->tx_desc[i].desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz, PCI_DMA_TODEVICE);
+ }
+ while (i--);
+ }
+
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static int fepci_stream_close(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned rx_pages, tx_pages, rx_order, tx_order;
+
+ if (fepci_stream_stop(dev))
+ return -ENODEV;
+ down_write(&fp->this_card_priv->semaphore);
+ if (!(fp->in_stream_mode)) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -ENODEV;
+ }
+ /* release memory */
+ if (fp->bufsize_order < PAGE_SHIFT) {
+ rx_order = 0;
+ tx_order = 0;
+ rx_pages = 1;
+ tx_pages = 1;
+ } else {
+ rx_order = (int)((fp->bufsize_order) - PAGE_SHIFT + 1);
+ rx_pages = 1 << rx_order;
+ tx_order = (int)((fp->bufsize_order) - PAGE_SHIFT);
+ tx_pages = 1 << tx_order;
+ }
+ if (fp->rx_buffer) {
+ unsigned page_number;
+ for (page_number = 0; page_number < rx_pages; page_number++)
+ /* turn pages back to non-reserved */
+ ClearPageReserved(virt_to_page
+ ((unsigned long)fp->rx_buffer +
+ (page_number << PAGE_SHIFT)));
+ free_pages((unsigned long)fp->rx_buffer, rx_order);
+ fp->rx_buffer = NULL;
+ }
+ if (fp->tx_buffer) {
+ unsigned page_number;
+ for (page_number = 0; page_number < tx_pages; page_number++)
+ /* turn pages back to non-reserved */
+ ClearPageReserved(virt_to_page
+ ((unsigned long)fp->tx_buffer +
+ (page_number << PAGE_SHIFT)));
+ free_pages((unsigned long)fp->tx_buffer, tx_order);
+ fp->tx_buffer = NULL;
+ }
+
+ fp->in_stream_mode = 0;
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ struct fepci_ch_private *fp = dev->priv;
+ void *ioaddr = (void *)dev->base_addr;
+ unsigned intr_status = get_int_status(fp->channel_number, ioaddr);
+ unsigned fifo, dropped;
+ unsigned int temp_rx;
+ unsigned int temp_rx_unit;
+ unsigned int temp_tx;
+ unsigned int temp_tx_unit;
+
+ if (unlikely(!intr_status))
+ return IRQ_NONE;
+ clear_int(fp->channel_number, intr_status, ioaddr);
+
+ /* debugging */
+ fifo = intr_status & IntrRxFifoError;
+ if (fifo)
+ fp->rx_int_fifo_err_stream++;
+ dropped = intr_status & IntrRxFrameDroppedError;
+ if (dropped)
+ fp->rx_int_frame_dropped_err_stream++;
+ if (intr_status & IntrTxFifoError)
+ fp->tx_int_fifo_err_stream++;
+ /* first update cur_rx, and do stuff if it has moved
+ (+ packets have been received) */
+ {
+ temp_rx = fp->cur_rx;
+ /* has been received */
+ while ((readl(&fp->rx_desc[fp->cur_rx].desc_b) &
+ transfer_not_done) == 0
+ /* stop if made one round */
+ && temp_rx != ((fp->cur_rx + 1) & (RX_RING_SIZE - 1))) {
+ dma_addr_t bus_address = readl(&fp->rx_desc[fp->cur_rx].
+ desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz,
+ PCI_DMA_FROMDEVICE);
+ fp->cur_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
+ fp->cur_rx_unit = (fp->cur_rx_unit + 1);
+ fp->cur_rx_unit *= fp->cur_rx_unit < fp->units;
+
+ *USER_RX_S_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) = fp->cur_rx_unit;
+ *USER_RX_S_FAKE_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) =
+ fp->cur_rx_unit * fp->unit_sz / fp->fake_unit_sz;
+ wake_up_interruptible(&(fp->this_card_priv->
+ stream_receive_q));
+ wake_up_interruptible(&(fp->this_card_priv->
+ stream_both_q));
+ }
+ }
+ /* from the first uninitialized descriptor to cur_rx */
+ temp_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
+ temp_rx_unit = (fp->cur_rx_unit + 1);
+ temp_rx_unit *= temp_rx_unit < fp->units;
+
+ while (temp_rx != fp->cur_rx) {
+ unsigned desc_b = readl(&fp->rx_desc[temp_rx].desc_b);
+ if ((desc_b & transfer_not_done) == 0) {
+ bool fifo = (desc_b & fifo_error) != 0;
+ bool size = (desc_b & size_error) != 0;
+ bool octet = (desc_b & octet_error) != 0;
+ bool line = (desc_b & line_error) != 0;
+ dma_addr_t bus_address;
+ /* update debug counters */
+ if (fifo)
+ fp->rx_desc_fifo_err_stream++;
+ else if (size)
+ fp->rx_desc_size_err_stream++;
+ else if (octet)
+ fp->rx_desc_octet_err_stream++;
+ else if (line)
+ fp->rx_desc_line_err_stream++;
+ /* initialize the descriptor for transfer */
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ fp->rx_unit[temp_rx_unit],
+ fp->unit_sz, PCI_DMA_FROMDEVICE);
+ if (likely(!pci_dma_mapping_error(bus_address))) {
+ writel(bus_address,
+ &fp->rx_desc[temp_rx].desc_a);
+ writel(enable_transfer,
+ &fp->rx_desc[temp_rx].desc_b);
+ } else
+ printk(KERN_WARNING
+ "%s: failed to map DMA for reception.\n",
+ fepci_NAME);
+ }
+ temp_rx = (temp_rx + 1) & (RX_RING_SIZE - 1);
+ temp_rx_unit = (temp_rx_unit + 1);
+ temp_rx_unit *= temp_rx_unit < fp->units;
+ }
+
+ /* first update cur_tx, and do stuff if it has moved
+ (+ packets have been transmitted) */
+ temp_tx = fp->cur_tx;
+ /* has been transmitted? */
+ while ((readl(&fp->tx_desc[fp->cur_tx].desc_b) &
+ transfer_not_done) == 0
+ /* stop if made one round */
+ && temp_tx != ((fp->cur_tx + 1) & (TX_RING_SIZE - 1))) {
+ dma_addr_t bus_address = readl(&fp->tx_desc[fp->cur_tx].
+ desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz, PCI_DMA_TODEVICE);
+ fp->cur_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
+ fp->cur_tx_unit = (fp->cur_tx_unit + 1);
+ fp->cur_tx_unit *= fp->cur_tx_unit < fp->units;
+ *USER_TX_S_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) = fp->cur_tx_unit;
+ *USER_TX_S_FAKE_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) =
+ fp->cur_tx_unit * fp->unit_sz / fp->fake_unit_sz;
+ wake_up_interruptible(&(fp->this_card_priv->
+ stream_transmit_q));
+ wake_up_interruptible(&(fp->this_card_priv->
+ stream_both_q));
+ }
+
+ /* from the first uninitialized descriptor to cur_tx */
+ temp_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
+ temp_tx_unit = (fp->cur_tx_unit + 1);
+ temp_tx_unit *= temp_tx_unit < fp->units;
+
+ while (temp_tx != fp->cur_tx) {
+ unsigned desc_b = readl(&fp->tx_desc[temp_tx].desc_b);
+ if ((desc_b & transfer_not_done) == 0) {
+ dma_addr_t bus_address;
+ /* update debug counters */
+ if (desc_b & fifo_error)
+ fp->tx_desc_fifo_err_stream++;
+ /* initialize the desctiptor for transfer */
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ fp->tx_unit[temp_tx_unit],
+ fp->unit_sz, PCI_DMA_TODEVICE);
+ writel(bus_address, &fp->tx_desc[temp_tx].desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ writel(enable_transfer |
+ (fp->unit_sz & frame_length),
+ &fp->tx_desc[temp_tx].desc_b);
+ else
+ printk(KERN_WARNING
+ "%s: failed to map tranmission DMA.\n",
+ fepci_NAME);
+ }
+ temp_tx = (temp_tx + 1) & (TX_RING_SIZE - 1);
+ temp_tx_unit = (temp_tx_unit + 1);
+ temp_tx_unit *= temp_tx_unit < fp->units;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* stream operations end */
+
+static int fepci_rebuild_header(struct sk_buff *skb)
+{
+ pr_debug("fepci_rebuild_header\n");
+ return 0;
+}
+
+static inline u16 get_common_reg_word(void *ioaddr, unsigned long offsett)
+{
+ u16 word;
+ pr_debug("get_common_reg_word ioaddr %p, offsett %lu\n", ioaddr,
+ offsett);
+ __clear_bit(0, &offsett);
+ pr_debug("get_common_reg_word %p\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1));
+ word = le16_to_cpu(readw
+ (ioaddr + FEPCI_IDENTIFICATION_OFFSETT +
+ (offsett << 1)));
+ pr_debug("get_common_reg_word %p: %hu\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1), word);
+ return word;
+}
+
+static irqreturn_t alarm_manager_interrupt(int irq, void *pointer)
+{
+ struct fepci_card_private *card_private = pointer;
+ void *ioaddr = card_private->ioaddr;
+ /* check int status */
+ if (readl((void *)(ioaddr + reg_custom)) & AM_interrupt_status) {
+ /* clear int (zero everything, but the mask bit) */
+ writel(readl((void *)(ioaddr + reg_custom)) &
+ AM_interrupt_mask, (void *)(ioaddr + reg_custom));
+ /* wake queue */
+ wake_up(&(card_private->alarm_manager_wait_q));
+ }
+
+ return IRQ_HANDLED;
+}
+
+#ifdef FEPCI_POINT_TO_POINT
+static int is_ptp_interface(struct net_device *dev)
+{
+ char **p_ptp_if_name = retina_ptp_interfaces;
+ unsigned int i = interfaces;
+ while (i > 0 && *p_ptp_if_name != NULL) {
+ if (!strncmp(dev->name, *p_ptp_if_name, sizeof(dev->name)))
+ return 1;
+ p_ptp_if_name++;
+ i--;
+ }
+ return 0;
+}
+#endif /* FEPCI_POINT_TO_POINT */
+
+static int __devinit fepci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev = NULL;
+ struct fepci_ch_private *fp = NULL;
+ int i;
+ unsigned j;
+ resource_size_t real_ioaddr;
+ void *ioaddr;
+ unsigned position;
+
+ i = pci_enable_device(pdev);
+ if (i) {
+ printk(KERN_WARNING "%s: pci_enable_device returned %x.\n",
+ fepci_NAME, i);
+ return i;
+ }
+
+ pci_set_master(pdev);
+
+ i = pci_request_regions(pdev, (char *)fepci_name);
+ if (i) {
+ printk(KERN_WARNING
+ "%s: pci_request_regions returned %x.\n", fepci_NAME, i);
+ pci_disable_device(pdev);
+ return i;
+ }
+
+ /* make sure above region is MMIO */
+ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+ printk(KERN_WARNING "%s: region not MMIO region\n", fepci_NAME);
+ goto ERR_1;
+ }
+
+ j = pci_resource_len(pdev, 0);
+ if (j < FEPCI_SIZE) {
+ printk(KERN_WARNING
+ "%s: resource length %u less than required %u.\n",
+ fepci_NAME, j, FEPCI_SIZE);
+ goto ERR_1;
+ }
+
+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+ printk(KERN_WARNING "%s: no suitable DMA available.\n",
+ fepci_NAME);
+ goto ERR_1;
+ }
+
+ real_ioaddr = pci_resource_start(pdev, 0);
+ ioaddr = ioremap_nocache(real_ioaddr, FEPCI_SIZE);
+ pr_debug("ioremap_nocache = %p.\n", ioaddr);
+ if (!ioaddr) {
+ printk(KERN_WARNING "%s: remapping failed.\n", fepci_NAME);
+ goto ERR_1_5;
+ }
+ position = 0;
+ for (; position < MAX_DEVICES; position++) {
+ down_write(&card_privates[position].semaphore);
+ if (card_privates[position].pci_dev == NULL) {
+ card_privates[position].pci_dev = pdev;
+ if (position == find_cnt)
+ find_cnt++;
+ goto FOUND;
+ }
+ up_write(&card_privates[position].semaphore);
+ }
+ printk(KERN_WARNING
+ "%s: no space to inialize device #%u.\n",
+ fepci_NAME, MAX_DEVICES + 1);
+ goto ERR_2;
+FOUND:
+ card_privates[position].card_number = position;
+ card_privates[position].ioaddr = ioaddr;
+ card_privates[position].pci_dev = pdev;
+ pr_debug("fillregisterswith_00 %p.\n", ioaddr);
+ fillregisterswith_00(ioaddr);
+
+ fepci_proc_init_card(position, (void *)&(card_privates[position]));
+
+ init_waitqueue_head(&(card_privates[position].alarm_manager_wait_q));
+ init_waitqueue_head(&(card_privates[position].stream_transmit_q));
+ init_waitqueue_head(&(card_privates[position].stream_receive_q));
+ init_waitqueue_head(&(card_privates[position].stream_both_q));
+
+ init_timer(&card_privates[position].mailbox_timer);
+
+ pr_debug("request_irq %d, %s.\n", pdev->irq, fepci_alarm_manager_name);
+ i = request_irq(pdev->irq, &alarm_manager_interrupt,
+ IRQF_SHARED,
+ fepci_alarm_manager_name, &card_privates[position]);
+ if (i) {
+ up_write(&card_privates[position].semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate IRQ %d alarm manager: 0x%x\n",
+ fepci_NAME, pdev->irq, i);
+ goto ERR_2;
+ }
+ pr_debug("alarm manager int on %p.\n", (void *)(ioaddr + reg_custom));
+ writel(AM_interrupt_mask, (void *)(ioaddr + reg_custom));
+ /* alarm manager int on */
+
+ for (j = 0; j < CHANNELS; j++) {
+ pr_debug("alloc_etherdev %u.\n", j);
+ dev = alloc_etherdev(sizeof(struct fepci_ch_private));
+ if (!dev) {
+ printk(KERN_WARNING
+ "%s: cannot allocate ethernet device\n",
+ fepci_NAME);
+ continue;
+ }
+
+ fp = dev->priv;
+ fp->minor = position; /* * CHANNELS + j; */
+ /* name := xxx01..xxxnn */
+ memcpy(dev->name, fepci_netdev_name, 6);
+ /* dev->name[3]= j+'0'; channel number -> ascii */
+ /* minor number -> ascii */
+ dev->name[4] = ((fp->minor * CHANNELS + j) % 10) + '0';
+ /* minor number -> ascii */
+ dev->name[3] = ((fp->minor * CHANNELS + j) / 10) + '0';
+
+ SET_MODULE_OWNER(dev);
+ dev_dbg(&dev->dev, "clear_int %u, %x, %p.\n", j,
+ IntrAllInts, ioaddr);
+ clear_int(j, IntrAllInts, ioaddr);
+ dev_dbg(&dev->dev, "ether_setup.\n");
+ ether_setup(dev);
+
+ random_ether_addr(dev->dev_addr);
+ /* HW_ADDR is got using the mailbox: */
+ {
+ struct fepci_real_mailbox *real_mailbox =
+ (struct fepci_real_mailbox *)
+ (ioaddr + FEPCI_MAILBOX_OFFSETT);
+ unsigned long waituntil;
+
+ set_semafore(real_mailbox, 0x40);
+ writel(0x1 /*size */ +
+ (0x8 << 8) /* get mac command */ ,
+ &real_mailbox->Size_Command);
+ set_semafore(real_mailbox, 0x11);
+
+ waituntil = jiffies + HZ;
+ while (time_before(jiffies, waituntil) &&
+ get_semafore(real_mailbox) != 0x20) {
+ dev_dbg(&dev->dev,
+ "jiffies %lu < waituntil %lu.\n",
+ jiffies, waituntil);
+ schedule_timeout(0);
+ }
+
+ /* 14.5.2004 JT: Made this safer. */
+ if (get_semafore(real_mailbox) == 0x20) {
+ dev->dev_addr[5] =
+ readb(&real_mailbox->Data[0 + 3 * j]);
+ dev->dev_addr[4] =
+ readb(((u8 *) & real_mailbox->
+ Data[0 + 3 * j]) + 1);
+ dev->dev_addr[3] =
+ readb(&real_mailbox->Data[1 + 3 * j]);
+ dev->dev_addr[2] =
+ readb(((u8 *) & real_mailbox->
+ Data[1 + 3 * j]) + 1);
+ dev->dev_addr[1] =
+ readb(&real_mailbox->Data[2 + 3 * j]);
+ dev->dev_addr[0] =
+ readb(((u8 *) & real_mailbox->
+ Data[2 + 3 * j]) + 1);
+ }
+
+ set_semafore(real_mailbox, 0x0);
+ }
+ dev->addr_len = 6;
+
+ dev->base_addr = (unsigned long)ioaddr;
+ dev->irq = pdev->irq;
+ dev_dbg(&dev->dev, "alarm pci_set_drvdata %p.\n", pdev);
+ if (j == 0)
+ pci_set_drvdata(pdev, dev);
+
+ fp->rx_desc =
+ (struct fepci_desc *)(dev->base_addr + first_rx_desc +
+ j * to_next_ch_rx_desc);
+ fp->tx_desc =
+ (struct fepci_desc *)(dev->base_addr + first_tx_desc +
+ j * to_next_ch_tx_desc);
+
+ fp->channel_number = j; /*channel in this device */
+ fp->this_dev = dev;
+
+ fp->this_card_priv = &card_privates[position];
+ fp->this_card_priv->ch_privates[j] = fp;
+
+ fp->cur_tx = 0;
+
+ fp->in_stream_mode = 0;
+ fp->in_eth_mode = 0;
+
+ fp->reg_rxctrl = reg_first_rxctrl + j * to_next_rxctrl;
+ fp->reg_txctrl = reg_first_txctrl + j * to_next_txctrl;
+
+ /* The FEPCI specific entries in the device structure */
+ dev->open = &fepci_open;
+ dev->hard_start_xmit = &fepci_start_xmit;
+ dev->stop = &fepci_close;
+ dev->get_stats = &fepci_get_stats;
+ dev->set_multicast_list = &set_rx_mode;
+ dev->do_ioctl = &netdev_ioctl;
+ dev->tx_timeout = fepci_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+#ifdef FEPCI_POINT_TO_POINT
+ if (is_ptp_interface(dev)) {
+ dev->flags |= (IFF_POINTOPOINT);
+ dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
+ if (retina_noarp_with_ptp) {
+ dev->rebuild_header = fepci_rebuild_header;
+ dev->flags |= (IFF_NOARP);
+ }
+ }
+#endif
+ dev_dbg(&dev->dev, "register_netdev.\n");
+ i = register_netdev(dev);
+ if (i) {
+ printk(KERN_WARNING
+ "%s: register_netdev failed 0x%x.\n",
+ fepci_NAME, i);
+ continue;
+ }
+
+ for (i = 0; i < 5; i++)
+ dev_dbg(&dev->dev, "%2.2x:", dev->dev_addr[i]);
+ dev_dbg(&dev->dev, "%2.2x, IRQ %d.\n", dev->dev_addr[i],
+ pdev->irq);
+ }
+ up_write(&card_privates[position].semaphore);
+ pr_debug("fepci_init_one %u.\n", position);
+ return 0;
+
+ERR_2:
+ iounmap(ioaddr);
+ERR_1_5:
+ pci_set_drvdata(pdev, NULL);
+ERR_1:
+ pci_disable_device(pdev);
+ pci_release_regions(pdev);
+ return -ENODEV;
+}
+
+static int fepci_open(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+
+ down_write(&fp->this_card_priv->semaphore);
+
+ if (fp->this_card_priv->pci_dev == NULL) {
+ up_write(&fp->this_card_priv->semaphore);
+ fepci_close(dev);
+ return -ENXIO;
+ }
+
+ while (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ fepci_stream_close(dev);
+ down_write(&fp->this_card_priv->semaphore);
+ }
+
+ {
+ int i = request_irq(dev->irq, &fepci_interrupt,
+ IRQF_SHARED, dev->name, dev);
+ if (i) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate IRQ %d, error 0x%x",
+ fepci_NAME, dev->irq, i);
+ return i;
+ }
+ }
+
+ fp->in_eth_mode = 1;
+
+ fepci_init_ring(dev);
+ set_rx_mode(dev);
+
+ fp->cur_rx = 0;
+ fp->cur_tx = 0;
+
+ netif_carrier_off(dev);
+
+ /* Enable interrupts by setting the interrupt mask. */
+
+ set_int_mask(fp->channel_number,
+ MaskFrameReceived | MaskFrameTransmitted |
+ MaskRxFifoError | MaskRxFrameDroppedError |
+ MaskTxFifoError, fp->this_card_priv);
+
+ {
+ void *ioaddr = (void *)dev->base_addr;
+
+ /* Start Rx and Tx channels. */
+ writel(Receive_enable |
+ (Rx_fifo_threshold & RX_FIFO_THRESHOLD_PACKET_MODE),
+ (void *)(ioaddr + fp->reg_rxctrl));
+
+ writel((Transmit_enable |
+ (Tx_desc_threshold &
+ (TX_DESC_THRESHOLD_PACKET_MODE << 8)) |
+ (Tx_fifo_threshold & TX_FIFO_THRESHOLD_PACKET_MODE)),
+ (void *)(ioaddr + fp->reg_txctrl));
+ }
+
+ netif_start_queue(dev);
+
+ /* Set timer */
+ init_timer(&fp->timer);
+ fp->timer.expires = jiffies + HZ;
+ fp->timer.data = (unsigned long)dev;
+ fp->timer.function = &fepci_timer; /* timer handler */
+ add_timer(&fp->timer);
+
+ up_write(&fp->this_card_priv->semaphore);
+
+ return 0;
+}
+
+static void fepci_timer(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct fepci_ch_private *fp = dev->priv;
+
+ if ((get_common_reg_word(fp->this_card_priv->ioaddr, 0x72) >> fp->
+ channel_number) & 1)
+ netif_carrier_off(dev);
+ else
+ netif_carrier_on(dev);
+
+ if (fp->in_eth_mode)
+ mod_timer(&fp->timer, jiffies + 5 * HZ);
+}
+
+/* Just to make it absolutely sure the sending starts again
+ * if the system jams: free already sent skbuffs. */
+
+static void fepci_tx_timeout(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+
+ if (netif_tx_trylock(dev)) {
+ if (!(fp->tx_interrupts_since_last_timer)) {
+ unsigned i = TX_RING_SIZE - 1;
+ do {
+ unsigned desc_b;
+ if (fp->tx_skbuff[i] == NULL)
+ continue;
+ desc_b = readl(&fp->tx_desc[i].desc_b);
+ if ((desc_b & transfer_not_done) == 0) {
+ /* has been sent */
+ pci_unmap_single(fp->
+ this_card_priv->
+ pci_dev,
+ readl(&fp->
+ tx_desc[i].
+ desc_a),
+ desc_b &
+ frame_length,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb(fp->tx_skbuff[i]);
+
+ fp->tx_skbuff[i] = NULL;
+
+ if (desc_b & fifo_error)
+ fp->stats.tx_fifo_errors++;
+ else
+ fp->stats.tx_packets++;
+ }
+ }
+ while (i--);
+ }
+ /* if the next descriptor is free, continue taking new ones */
+ if (!
+ (readl(&fp->tx_desc[fp->cur_tx].desc_b) &
+ transfer_not_done))
+ netif_wake_queue(dev);
+ netif_tx_unlock(dev);
+ }
+
+ fp->tx_interrupts_since_last_timer = 0;
+}
+
+/* Initialize the rx and tx rings */
+static void fepci_init_ring(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned i;
+
+ fp->rx_buf_sz = 2000;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb =
+ __dev_alloc_skb(fp->rx_buf_sz, GFP_KERNEL);
+
+ if (unlikely(skb == NULL)) {
+ZERO:
+ writel(0, &fp->rx_desc[i].desc_a);
+ writel(0, &fp->rx_desc[i].desc_b);
+ continue;
+ } else {
+ dma_addr_t bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ skb->data, fp->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+ if (likely(!pci_dma_mapping_error(bus_address))) {
+ /* Mark as being used by this device */
+ skb->dev = dev;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ fp->rx_skbuff[i] = skb;
+ writel(bus_address, &fp->rx_desc[i].desc_a);
+ writel(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else {
+ dev_kfree_skb(skb);
+ goto ZERO;
+ }
+ }
+ }
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ fp->tx_skbuff[i] = NULL;
+ writel(0, &fp->tx_desc[i].desc_a); /* no skbuff */
+ /* no transfer enable, no int enable */
+ writel(0, &fp->tx_desc[i].desc_b);
+ }
+
+ return;
+}
+
+static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ const unsigned cur_tx = fp->cur_tx;
+ unsigned next;
+ unsigned tx_length = skb->len;
+ dma_addr_t bus_address;
+ struct sk_buff *old;
+
+ if (unlikely(tx_length < ETH_ZLEN)) {
+ struct sk_buff *bigger =
+ skb_copy_expand(skb, 0, ETH_ZLEN - tx_length,
+ GFP_ATOMIC);
+ if (unlikely(!bigger))
+ return NET_XMIT_CN;
+ tx_length = ETH_ZLEN;
+ old = skb;
+ skb = bigger;
+ } else
+ old = NULL;
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev, skb->data,
+ tx_length, PCI_DMA_TODEVICE);
+ if (likely(!pci_dma_mapping_error(bus_address))) {
+ struct fepci_desc *descriptor;
+ if (old)
+ dev_kfree_skb(old);
+ descriptor = &fp->tx_desc[cur_tx];
+ writel(bus_address, &descriptor->desc_a);
+ writel((tx_length & frame_length) | enable_transfer,
+ &descriptor->desc_b);
+ } else {
+ if (old)
+ dev_kfree_skb(skb);
+ return NET_XMIT_CN;
+ }
+ fp->stats.tx_bytes += tx_length;
+
+ fp->tx_skbuff[cur_tx] = skb;
+
+ /* Calculate the next Tx descriptor entry */
+ next = (cur_tx + 1) & (TX_RING_SIZE - 1);
+ fp->cur_tx = next;
+ /* if the next descriptor is busy, discontinue taking new ones */
+ if (fp->tx_skbuff[next] != NULL)
+ netif_stop_queue(dev);
+ dev->trans_start = jiffies;
+
+ return NET_XMIT_SUCCESS;
+}
+
+static irqreturn_t fepci_interrupt(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ void *ioaddr = (void *)dev->base_addr;
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned intr_status = get_int_status(fp->channel_number, ioaddr);
+
+ if (unlikely(!intr_status))
+ return IRQ_NONE;
+ clear_int(fp->channel_number, intr_status, ioaddr);
+
+ if (intr_status &
+ (IntrFrameReceived | IntrRxFifoError | IntrRxFrameDroppedError))
+ fepci_rx(dev);
+ if (intr_status & IntrFrameTransmitted) {
+ fp->tx_interrupts_since_last_timer++;
+ if (netif_tx_trylock(dev)) {
+ unsigned i = TX_RING_SIZE - 1;
+ unsigned next;
+ do {
+ unsigned desc_b;
+ if (fp->tx_skbuff[i] == NULL)
+ continue;
+ desc_b = readl(&fp->tx_desc[i].desc_b);
+ if ((desc_b & transfer_not_done) == 0) {
+ /* has been sent */
+ pci_unmap_single(fp->
+ this_card_priv->
+ pci_dev,
+ readl(&fp->
+ tx_desc[i].
+ desc_a),
+ desc_b &
+ frame_length,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb_irq(fp->tx_skbuff[i]);
+ fp->tx_skbuff[i] = NULL;
+ if (desc_b & fifo_error)
+ fp->stats.tx_fifo_errors++;
+ else
+ fp->stats.tx_packets++;
+ }
+ }
+ while (i--);
+ next = fp->cur_tx;
+ /* if next tx descriptor is free,
+ * continue taking new ones */
+ if (!(readl(&fp->tx_desc[next].desc_b) &
+ transfer_not_done))
+ netif_wake_queue(dev);
+ netif_tx_unlock(dev);
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static int fepci_rx(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+
+ unsigned int i, old_cur_rx = fp->cur_rx;
+ for (i = old_cur_rx;
+ i != ((old_cur_rx + RX_RING_SIZE - 1) & (RX_RING_SIZE - 1));
+ i = (i + 1) & (RX_RING_SIZE - 1)) {
+ unsigned desc_b;
+ struct sk_buff *skb;
+ /* transfer done */
+ bool condition = (skb = fp->rx_skbuff[i]) &&
+ ((desc_b =
+ readl(&fp->rx_desc[i].desc_b)) & transfer_not_done) == 0;
+ if (condition) {
+ bool fifo = (desc_b & fifo_error) != 0;
+ bool size = (desc_b & size_error) != 0;
+ bool crc = (desc_b & crc_error) != 0;
+ bool octet = (desc_b & octet_error) != 0;
+ bool line = (desc_b & line_error) != 0;
+ unsigned length = desc_b & frame_length;
+ pci_unmap_single(fp->this_card_priv->pci_dev,
+ readl(&fp->rx_desc[i].desc_a),
+ fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ fp->cur_rx = (i + 1) & (RX_RING_SIZE - 1);
+ if (fifo) {
+ fp->stats.rx_errors++;
+ fp->stats.rx_frame_errors++;
+ writel(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (size) {
+ fp->stats.rx_errors++;
+ fp->stats.rx_over_errors++;
+ writel(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (crc) {
+ fp->stats.rx_errors++;
+ fp->stats.rx_crc_errors++;
+ writel(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (octet)
+ writel(enable_transfer, &fp->rx_desc[i].desc_b);
+ else if (line)
+ writel(enable_transfer, &fp->rx_desc[i].desc_b);
+ else {
+ skb_put(skb, length - 4);
+
+ skb->protocol = eth_type_trans(skb, dev);
+#ifdef FEPCI_POINT_TO_POINT
+ if (dev->flags & IFF_POINTOPOINT) {
+ /* everything received is for us. */
+
+ if (dev->flags & IFF_NOARP) {
+ /* NOARP applied ->
+ * destination MAC addresses
+ * are bogus */
+ if (skb->
+ pkt_type ==
+ PACKET_OTHERHOST)
+ skb->
+ pkt_type =
+ PACKET_HOST;
+ } else {
+ /* NOARP not applied ->
+ * destination MAC addresses are
+ * broadcast */
+ if (skb->
+ pkt_type ==
+ PACKET_BROADCAST)
+ skb->
+ pkt_type =
+ PACKET_HOST;
+
+ } /* IFF_NOARP */
+ } /* IFF_POINTOPOINT */
+#endif
+ skb_reset_mac_header(skb);
+ netif_rx(skb);
+ /* statistics -4==crc */
+ fp->stats.rx_bytes += length - 4;
+ fp->stats.rx_packets++;
+
+ fp->rx_skbuff[i] = NULL;
+ dev->last_rx = jiffies;
+ }
+ }
+ /* reserve a new one */
+ if (fp->rx_skbuff[i] == NULL) {
+ struct sk_buff *skb = dev_alloc_skb(fp->rx_buf_sz);
+
+ if (skb == NULL)
+ continue; /* Better luck next round. */
+ else {
+ dma_addr_t address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ skb->data,
+ fp->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+ if (likely(!pci_dma_mapping_error(address))) {
+ struct fepci_desc *descriptor;
+ fp->rx_skbuff[i] = skb;
+ /* Mark as being used by this device. */
+ skb->dev = dev;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ descriptor = &fp->rx_desc[i];
+ writel(address, &descriptor->desc_a);
+ writel(enable_transfer,
+ &descriptor->desc_b);
+ } else {
+ dev_kfree_skb(skb);
+ printk(KERN_WARNING
+ "%s: failed to map DMA.\n",
+ dev->name);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int fepci_close(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned i;
+ void *ioaddr = (void *)dev->base_addr;
+
+ down_write(&fp->this_card_priv->semaphore);
+
+ netif_stop_queue(dev);
+
+ /* Disable interrupts by clearing the interrupt mask */
+ set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
+
+ /* Stop the Tx and Rx processes */
+ writel(0x0, ioaddr + fp->reg_rxctrl);
+ writel(0x0, ioaddr + fp->reg_txctrl);
+ fp->in_eth_mode = 0;
+
+ del_timer_sync(&fp->timer);
+
+ free_irq(dev->irq, dev);
+
+ /* Free all the rx skbuffs */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (fp->rx_skbuff[i] != NULL) {
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev,
+ readl(&fp->rx_desc[i].
+ desc_a),
+ fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(fp->rx_skbuff[i]);
+ fp->rx_skbuff[i] = NULL;
+ }
+ }
+ /* and tx */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (fp->tx_skbuff[i] != NULL) {
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev,
+ readl(&fp->tx_desc[i].
+ desc_a),
+ readl(&fp->tx_desc[i].
+ desc_b) & frame_length,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb(fp->tx_skbuff[i]);
+ fp->tx_skbuff[i] = NULL;
+ }
+ }
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static struct net_device_stats *fepci_get_stats(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ return &fp->stats;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+ dev_dbg(&dev->dev, "set_rx_mode\n");
+}
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ char data = ((unsigned)rq->ifr_data) & 0xff;
+ int ret = 0;
+
+ dev_dbg(&dev->dev, "netdev_ioctl called (command_nmbr:0x%x).\n",
+ cmd);
+
+ switch (cmd) {
+ case FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE:
+ dev_dbg
+ (&dev->dev,
+ " ioctl stream bufsize commanded. (bufsize:0x%x)\n",
+ data);
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -EBUSY;
+ }
+ fp->bufsize_order = data;
+ up_write(&fp->this_card_priv->semaphore);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_UNITSIZE:
+ dev_dbg
+ (&dev->dev,
+ " ioctl stream unitsize commanded. (unitsize:0x%x)\n",
+ data);
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -EBUSY;
+ }
+ fp->fake_unit_sz_order = data;
+ up_write(&fp->this_card_priv->semaphore);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_OPEN:
+ dev_dbg(&dev->dev, " ioctl stream open commanded.\n");
+ ret = fepci_stream_open(dev);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_START:
+ dev_dbg(&dev->dev, " ioctl stream start commanded.\n");
+ ret = fepci_stream_start(dev);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_CLOSE:
+ dev_dbg(&dev->dev, " ioctl stream close commanded.\n");
+ ret = fepci_stream_close(dev);
+ break;
+ default:
+ dev_dbg(&dev->dev, " unknown ioctl command 0x%x.\n", cmd);
+ return -ENOTTY;
+ }
+ return ret;
+}
+
+static void fepci_remove_one(struct pci_dev *pdev)
+{
+ /* from first dev, same in all */
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct fepci_ch_private *fp = dev->priv;
+ struct fepci_card_private *cardp = fp->this_card_priv;
+ unsigned int i;
+
+ writel(0, (void *)(cardp->ioaddr + reg_custom));
+ /* alarm manager int off */
+
+ for (i = 0; i < CHANNELS; i++) {
+ dev = cardp->ch_privates[i]->this_dev;
+ fp = dev->priv;
+ unregister_netdev(dev);
+ fepci_stream_close(dev);
+ free_netdev(dev);
+ }
+
+ pdev = cardp->pci_dev;
+ free_irq(pdev->irq, (void *)cardp);
+
+ down_write(&cardp->semaphore);
+
+ fepci_proc_cleanup_card(cardp->card_number);
+
+ pci_set_drvdata(pdev, NULL);
+
+ if (cardp->card_number + 1 == find_cnt)
+ find_cnt--;
+ cardp->pci_dev = NULL;
+
+ iounmap(cardp->ioaddr);
+
+ up_write(&cardp->semaphore);
+
+ pci_disable_device(pdev);
+ pci_release_regions(pdev);
+}
+
+static struct pci_driver fepci_driver = {
+name: DRV_NAME,
+id_table: fepci_pci_tbl,
+probe: fepci_init_one,
+remove: fepci_remove_one,
+};
+
+static int __init fepci_init(void)
+{
+ unsigned card = MAX_DEVICES - 1;
+ do
+ init_rwsem(&card_privates[card].semaphore);
+ while (card--);
+ major = fepci_register_char_device();
+ if (major < 0)
+ return major;
+ stream_pointers =
+ (unsigned long)get_zeroed_page(GFP_KERNEL | __GFP_DMA);
+ pr_debug(" %lx.\n", stream_pointers);
+ if (stream_pointers == 0) {
+ fepci_unregister_char_device();
+ return -ENOMEM;
+ }
+ pr_debug("SetPageReserved %lu.\n", stream_pointers);
+ SetPageReserved(virt_to_page(stream_pointers));
+ pr_debug("fepci_proc_init_driver.\n");
+ fepci_proc_init_driver();
+ pr_debug("pci_register_driver %p.\n", &fepci_driver);
+ {
+ int ret = pci_register_driver(&fepci_driver);
+ if (ret) {
+ fepci_unregister_char_device();
+ ClearPageReserved(virt_to_page(stream_pointers));
+ free_page(stream_pointers);
+ pr_debug
+ ("pci_register_driver %p failed with %d.\n",
+ &fepci_driver, ret);
+ fepci_proc_cleanup_driver();
+ return ret;
+ }
+ pr_debug("fepci_init %d.\n", ret);
+ return ret;
+ }
+}
+
+static void __exit fepci_cleanup(void)
+{
+ pr_debug("fepci_cleanup\n");
+ pci_unregister_driver(&fepci_driver);
+ fepci_proc_cleanup_driver();
+ fepci_unregister_char_device();
+ ClearPageReserved(virt_to_page(stream_pointers));
+ free_page(stream_pointers);
+}
+
+module_init(fepci_init);
+module_exit(fepci_cleanup);
+
+static int get_line_data_rate_value(unsigned char line_rate)
+{
+ switch (line_rate) {
+ case 0x00:
+ return 0;
+ case 0x05:
+ case 0x28:
+ return 8;
+ case 0x14:
+ return 256;
+ case 0x15:
+ return 300;
+ case 0x29:
+ case 0x06:
+ return 10;
+ case 0x30:
+ return 32;
+ case 0x34:
+ return 56;
+ case 0x36:
+ return 64;
+ case 0x01:
+ case 0x60:
+ case 0xa0:
+ case 0x20:
+ return 1;
+ default:
+ return -1;
+ }
+}
+
+static char get_line_data_rate_unit(unsigned char line_rate)
+{
+ switch (line_rate) {
+ case 0x00:
+ case 0x01:
+ case 0x05:
+ case 0x06:
+ case 0x14:
+ case 0x15:
+ return 0;
+ case 0x20:
+ case 0x28:
+ case 0x29:
+ case 0x30:
+ case 0x34:
+ case 0x36:
+ return 'k';
+ case 0x60:
+ return 'M';
+ case 0xa0:
+ return 'G';
+ default:
+ return 0;
+ }
+}
+
+static int print_line_type(unsigned char type, char *buf, int pos)
+{
+ pr_debug("print_line_type %c\n", type);
+ switch (type) {
+ case 0:
+ pos += sprintf(buf + pos, "NONE");
+ break;
+ case 1:
+ pos += sprintf(buf + pos, "DCombus management bus");
+ break;
+ case 2:
+ pos += sprintf(buf + pos, "V.24");
+ break;
+ case 3:
+ pos += sprintf(buf + pos, "X.21");
+ break;
+ case 4:
+ pos += sprintf(buf + pos, "V.35");
+ break;
+ case 5:
+ pos += sprintf(buf + pos, "V.11");
+ break;
+ case 6:
+ pos += sprintf(buf + pos, "IDSL (ISDN Basic Rate");
+ break;
+ case 7:
+ pos += sprintf(buf + pos, "E1 nonframed/framed");
+ break;
+ case 8:
+ pos += sprintf(buf + pos, "E2 nonframed/framed");
+ break;
+ case 9:
+ pos += sprintf(buf + pos, "E3 nonframed/framed");
+ break;
+ case 10:
+ pos += sprintf(buf + pos, "T1 nonframed/framed");
+ break;
+ case 11:
+ pos += sprintf(buf + pos, "T2 nonframed/framed");
+ break;
+ case 12:
+ pos += sprintf(buf + pos, "T3 nonframed/framed");
+ break;
+ case 13:
+ pos += sprintf(buf + pos, "HDSL");
+ break;
+ case 14:
+ pos += sprintf(buf + pos, "ADSL");
+ break;
+ case 15:
+ pos += sprintf(buf + pos, "SDSL");
+ break;
+ case 16:
+ pos += sprintf(buf + pos, "HDSL2");
+ break;
+ case 17:
+ pos += sprintf(buf + pos, "VDSL");
+ break;
+ case 18:
+ pos += sprintf(buf + pos, "G.shdsl");
+ break;
+ case 19:
+ pos += sprintf(buf + pos, "SDH");
+ break;
+ case 255:
+ pos +=
+ sprintf(buf + pos, "COMBINATION, get list using mailbox");
+ break;
+ default:
+ pos += sprintf(buf + pos, "reserved");
+ }
+ return pos;
+}
+
+static int print_card_type(unsigned char type, char *buf, int pos)
+{
+ pr_debug("print_card_type\n");
+ switch (type) {
+ case 0x00:
+ pos += sprintf(buf + pos, "illegal ");
+ break;
+ case 0x01:
+ case 0x02:
+ pos += sprintf(buf + pos, "Retina ");
+ break;
+ default:
+ pos += sprintf(buf + pos, "reserved ");
+ }
+
+ return pos;
+}
+
+static int print_card_model(unsigned char model, char *buf, int pos)
+{
+ pr_debug("print_card_model\n");
+ switch (model) {
+ case 0x00:
+ pos += sprintf(buf + pos, "illegal ");
+ break;
+ case 0x01:
+ pos += sprintf(buf + pos, "E2200 ");
+ break;
+ case 0x02:
+ pos += sprintf(buf + pos, "C5400 ");
+ break;
+ default:
+ pos += sprintf(buf + pos, "reserved ");
+ }
+
+ return pos;
+}
+
+static inline u8 get_common_reg(void *ioaddr, unsigned long offsett)
+{
+ unsigned long byte = __test_and_clear_bit(0, &offsett);
+ u8 reg;
+ pr_debug("get_common_reg %p, old %p\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+ byte,
+ &((u32 *) (ioaddr +
+ FEPCI_IDENTIFICATION_OFFSETT))[offsett / 2]);
+ reg =
+ readb(ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+ byte);
+ pr_debug("get_common_reg %p: %u\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+ byte, reg);
+ return reg;
+}
+
+static int fepci_proc_read_features(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+ int tab = 0;
+ struct fepci_card_private *cardp;
+ void *ioaddr;
+
+ cardp = (struct fepci_card_private *)data;
+
+ down_read(&cardp->semaphore);
+
+ if (unlikely(cardp->pci_dev == NULL))
+ goto EOF;
+
+ ioaddr = cardp->ioaddr;
+
+ switch (offset) {
+ case 0:
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "card type: ");
+ pos = print_card_type(get_common_reg(ioaddr, 0x00), buf, pos);
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "model: ");
+ pos = print_card_model(get_common_reg(ioaddr, 0x00), buf, pos);
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "serial number: ");
+ pos +=
+ sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x07));
+ pos +=
+ sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x06));
+ pos +=
+ sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x05));
+ pos +=
+ sprintf(buf + pos, "%2.2x\n", get_common_reg(ioaddr, 0x04));
+
+ pos += sprintf(buf + pos, "version: ");
+ pos +=
+ sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x08));
+ pos +=
+ sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x09));
+ pos +=
+ sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x0a));
+ pos +=
+ sprintf(buf + pos, "%2.2x\n", get_common_reg(ioaddr, 0x0b));
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "max. pipe count: ");
+ pos += sprintf(buf + pos, "%d\n", get_common_reg(ioaddr, 0x03));
+
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ case 1:
+ pos += sprintf(buf + pos, "line count: ");
+ pos += sprintf(buf + pos, "%d\n", get_common_reg(ioaddr, 0x02));
+
+ pos += sprintf(buf + pos, "line type: ");
+ pos = print_line_type(get_common_reg(ioaddr, 0x0c), buf, pos);
+
+ /* JT: Secondary interface is also printed also,
+ * if there is one. */
+ if (get_common_reg(ioaddr, 0x10) > 0) {
+ pos += sprintf(buf + pos, " / ");
+ pos =
+ print_line_type(get_common_reg(ioaddr, 0x10),
+ buf, pos);
+ }
+
+ pos += sprintf(buf + pos, "\n");
+
+ /* JT: Line modes are printed only if
+ * the card has NO secondary interface. */
+ if (get_common_reg(ioaddr, 0x10) == 0) {
+ pos += sprintf(buf + pos, "line modes: ");
+ tab = 0;
+ if (get_common_reg(ioaddr, 0x21) & 0x80) {
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "framed mode supported\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x40) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "unframed mode supported\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x20) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line code setting available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x10) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line attenuation available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x08) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line quality available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x04) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "timing setting available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x02) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos += sprintf(buf + pos, "reserved\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x01) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line shutdown setting "
+ "available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) == 0) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos += sprintf(buf + pos, "n/a\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ case 2:
+ pos += sprintf(buf + pos, "line rate: ");
+ if (get_common_reg(ioaddr, 0x28) ==
+ get_common_reg(ioaddr, 0x2a)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos += sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))
+ * get_common_reg_word(ioaddr,
+ 0x28));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos, "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list using mailbox");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos += sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))
+ * get_common_reg_word(ioaddr,
+ 0x28));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos, "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))
+ * get_common_reg_word(ioaddr,
+ 0x2a));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos, "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list using mailbox");
+ }
+
+ /* JT: Line rate of a secondary interface is printed,
+ * if there is one. */
+ if (get_common_reg(ioaddr, 0x10) > 0) {
+ pos += sprintf(buf + pos, " / ");
+
+ if (get_common_reg(ioaddr, 0x34) ==
+ get_common_reg(ioaddr, 0x36)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos +=
+ sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr, 0x34));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or "
+ "list using mailbox");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos +=
+ sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr, 0x34));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr, 0x36));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list "
+ "using mailbox");
+ }
+ }
+
+ pos += sprintf(buf + pos, "\n");
+
+ /* JT: Primary line special info is printed only
+ * if the card has NO secondary interface. */
+ if (get_common_reg(ioaddr, 0x10) == 0) {
+ pos += sprintf(buf + pos, "rate sel. methods: ");
+ if (get_common_reg(ioaddr, 0x28) ==
+ get_common_reg(ioaddr, 0x2a))
+ pos += sprintf(buf + pos, "n/a\n");
+ else if ((get_common_reg(ioaddr, 0x22) & 4)
+ && (get_common_reg(ioaddr, 0x23) & 4))
+ pos += sprintf(buf + pos, "adjust\n");
+ else if (((!get_common_reg(ioaddr, 0x22)) & 4)
+ && (get_common_reg(ioaddr, 0x23) & 4))
+ pos += sprintf(buf + pos, "adjust(framed)\n");
+ else if ((get_common_reg(ioaddr, 0x22) & 4)
+ && ((!get_common_reg(ioaddr, 0x23)) & 4))
+ pos += sprintf(buf + pos, "adjust(unframed)\n");
+ else
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "unframed data rate: ");
+ if (get_common_reg(ioaddr, 0x30) ==
+ get_common_reg(ioaddr, 0x32)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x30));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list using "
+ "mailbox\n");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x30));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x32));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+
+ case 3:
+ /* JT: Line modes are printed only
+ * if the card has NO secondary interface. */
+ if (get_common_reg(ioaddr, 0x10) == 0) {
+ pos += sprintf(buf + pos, "rate sel. methods: ");
+ if (get_common_reg(ioaddr, 0x30) ==
+ get_common_reg(ioaddr, 0x32))
+ pos += sprintf(buf + pos, "n/a\n");
+ else {
+ if (get_common_reg(ioaddr, 0x23) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "timeslot map\n");
+ if (get_common_reg(ioaddr, 0x23) & 2)
+ pos += sprintf(buf + pos, "adjust\n");
+ }
+
+ pos += sprintf(buf + pos, "framed data rate: ");
+ if (get_common_reg(ioaddr, 0x2c) ==
+ get_common_reg(ioaddr, 0x2e)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x2c));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x2c));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x2e));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ }
+
+ pos += sprintf(buf + pos, "rate sel. methods: ");
+ if (get_common_reg_word(ioaddr, 0x2c) ==
+ get_common_reg_word(ioaddr, 0x2e))
+ pos += sprintf(buf + pos, "n/a\n");
+ else {
+ if (get_common_reg(ioaddr, 0x22) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "timeslot map\n");
+ if (get_common_reg(ioaddr, 0x22) & 2)
+ pos += sprintf(buf + pos, "adjust\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ default:
+EOF:
+ up_read(&cardp->semaphore);
+ *start = (char *)0;
+ *eof = 1;
+ return 0;
+ }
+}
+
+static int fepci_proc_read_settings(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+ int i, j;
+
+ struct fepci_card_private *cardp;
+ void *ioaddr;
+
+ cardp = (struct fepci_card_private *)data;
+
+ down_read(&cardp->semaphore);
+
+ if (unlikely(cardp->pci_dev == NULL))
+ goto EOF;
+
+ ioaddr = cardp->ioaddr;
+
+ switch (offset) {
+ case 0:
+ j = get_common_reg(ioaddr, 0x02);
+
+ for (i = 0; i < j; i++) {
+ if ((get_common_reg_word(ioaddr, 0x44) >> i) & 1) {
+ pos += sprintf(buf + pos, "\nline %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+ pos +=
+ sprintf(buf + pos, "mode: ");
+ if ((get_common_reg_word(ioaddr, 0x46) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "framed\n");
+ else
+ pos += sprintf(buf + pos, "unframed\n");
+
+ pos +=
+ sprintf(buf + pos, "scrambler: ");
+ if ((get_common_reg_word(ioaddr, 0x48) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "enabled\n");
+ else if ((get_common_reg_word(ioaddr, 0x46)
+ >> i) & 1)
+ pos += sprintf(buf + pos, "n/a\n");
+ else
+ pos += sprintf(buf + pos, "disabled\n");
+
+ pos +=
+ sprintf(buf + pos, "line crc: ");
+ if ((get_common_reg_word(ioaddr, 0x46) >>
+ i) & 1) {
+ if ((get_common_reg_word
+ (ioaddr, 0x4a) >> i) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "enabled\n");
+ else if (get_common_reg
+ (ioaddr, 0x22) & 0x20)
+ pos +=
+ sprintf(buf + pos,
+ "disabled\n");
+ else
+ pos +=
+ sprintf(buf + pos, "n/a\n");
+ } else {
+ if ((get_common_reg_word
+ (ioaddr, 0x4a) >> i) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "enabled\n");
+ else if (get_common_reg
+ (ioaddr, 0x23) & 0x20)
+ pos +=
+ sprintf(buf + pos,
+ "disabled\n");
+ else
+ pos +=
+ sprintf(buf + pos, "n/a\n");
+ }
+ }
+ }
+
+ /* If there are no enabled lines, "No enabled lines!" printed.
+ * The settings file would remain empty otherwise. */
+ if ((get_common_reg_word(ioaddr, 0x44)) == 0)
+ pos += sprintf(buf + pos, "No enabled lines!\n");
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ case 1:
+ j = get_common_reg(ioaddr, 0x03);
+
+ if ((get_common_reg_word(ioaddr, 0x44)) != 0) {
+ for (i = 0; i < j; i++) {
+ pos += sprintf(buf + pos, "\npipe %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+
+ pos +=
+ sprintf(buf + pos, "access mode: ");
+ if ((get_common_reg_word(ioaddr, 0x4e) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "packet\n");
+ else
+ pos += sprintf(buf + pos, "stream\n");
+
+ pos +=
+ sprintf(buf + pos, "data rate: ");
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x42)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x42))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x42)) *
+ get_common_reg_word
+ (ioaddr, 0x50 + 2 * i));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x42))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x42));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ default:
+EOF:
+ up_read(&cardp->semaphore);
+ *start = (char *)0;
+ *eof = 1;
+ return 0;
+ }
+}
+
+static void proc_cpy(char *buf_from, char *buf_to, int len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ *(buf_to + i) = *(buf_from + i);
+}
+
+static int fepci_proc_read_status(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ unsigned int pos = 0;
+ unsigned int i, j;
+
+ struct fepci_card_private *cardp;
+
+ void *ioaddr;
+
+ struct fepci_ch_private *fp;
+
+ cardp = (struct fepci_card_private *)data;
+
+ down_read(&cardp->semaphore);
+
+ if (unlikely(cardp->pci_dev == NULL))
+ goto EOF;
+
+ ioaddr = cardp->ioaddr;
+
+ if ((offset & 0xff000) == 0) {
+ j = get_common_reg(ioaddr, 0x02);
+ for (i = 0; i < j; i++) {
+ pos += sprintf(buf + pos, "\nline %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+ pos += sprintf(buf + pos, "status: ");
+ if ((get_common_reg_word(ioaddr, 0x72) >> i) & 1) {
+ pos += sprintf(buf + pos, "DOWN\n");
+ netif_carrier_off(cardp->ch_privates[i]->
+ this_dev);
+ } else {
+ netif_carrier_on(cardp->ch_privates[i]->
+ this_dev);
+ if ((get_common_reg_word(ioaddr, 0x74) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "UP/Alarm\n");
+ else
+ pos += sprintf(buf + pos, "UP\n");
+ }
+ pos += sprintf(buf + pos, "primary clock: ");
+ if ((get_common_reg_word(ioaddr, 0x76) >> i) & 1)
+ pos += sprintf(buf + pos, "OK\n");
+ else
+ pos += sprintf(buf + pos, "FAIL\n");
+ }
+ up_read(&cardp->semaphore);
+ if (pos - (0xfff & offset) > (unsigned)len)
+ *start = (char *)len;
+ else
+ *start = (char *)(0x1000 - (offset & 0xfff));
+ proc_cpy(buf + (offset & 0xfff), buf, len);
+ return min((int)(pos - (0xfff & offset)), len);
+ }
+
+ j = get_common_reg(ioaddr, 0x03);
+ if ((j <= 0) || (j > 16))
+ j = 16;
+ if ((((((unsigned)offset) & 0xff000) >> 12) > 0) &&
+ (((((unsigned)offset) & 0xff000) >> 12) <= j)) {
+ i = ((int)((offset & 0xff000) >> 12)) - 1;
+ if (i < CHANNELS) {
+ pos += sprintf(buf + pos, "\npipe %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+ pos += sprintf(buf + pos, "status: ");
+
+ if (!((get_common_reg_word(ioaddr, 0x78) >> i) & 1))
+ pos += sprintf(buf + pos, "DOWN\n");
+ else if ((get_common_reg_word(ioaddr, 0x7a) >> i) & 1)
+ pos += sprintf(buf + pos, "UP/Degraded\n");
+ else
+ pos += sprintf(buf + pos, "UP\n");
+ if ((get_common_reg_word(ioaddr, 0x4e) >> i) & 1) {
+ pos +=
+ sprintf(buf + pos, "packet counters:\n\n");
+ pos +=
+ sprintf(buf + pos, "rx count: %lu \n",
+ cardp->ch_privates[i]->stats.
+ rx_packets);
+ pos +=
+ sprintf(buf + pos, "rx data: %lu \n",
+ cardp->ch_privates[i]->stats.
+ rx_bytes);
+ pos +=
+ sprintf(buf + pos, "rx errors:%lu \n",
+ cardp->ch_privates[i]->stats.
+ rx_errors);
+ pos +=
+ sprintf(buf + pos, "tx count: %lu \n",
+ cardp->ch_privates[i]->stats.
+ tx_packets);
+ pos +=
+ sprintf(buf + pos, "tx data: %lu \n",
+ cardp->ch_privates[i]->stats.
+ tx_bytes);
+ pos +=
+ sprintf(buf + pos, "tx errors:%lu \n",
+ cardp->ch_privates[i]->stats.
+ tx_errors);
+ } else {
+ pos +=
+ sprintf(buf + pos, "stream counters:\n\n");
+ fp = cardp->ch_privates[i];
+ pos += sprintf(buf + pos,
+ "rx_desc_fifo_err_stream : %10u"
+ " \n",
+ fp->rx_desc_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_desc_size_err_stream : %10u"
+ " \n",
+ fp->rx_desc_size_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_desc_octet_err_stream : %9u"
+ " \n",
+ fp->rx_desc_octet_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_desc_line_err_stream : %10u"
+ " \n",
+ fp->rx_desc_line_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "tx_desc_fifo_err_stream : %10u"
+ " \n",
+ fp->tx_desc_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_int_fifo_err_stream : %11u \n",
+ fp->rx_int_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_int_frame_dropped_err_stream :"
+ " %2u \n",
+ fp->
+ rx_int_frame_dropped_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "tx_int_fifo_err_stream : %11u \n",
+ fp->tx_int_fifo_err_stream);
+ }
+ if (pos - (0xfff & offset) > (unsigned)len)
+ *start = (char *)len;
+ else
+ *start = (char *)(0x1000 - (offset & 0xfff));
+ proc_cpy(buf + (offset & 0xfff), buf, len);
+ }
+ up_read(&cardp->semaphore);
+ return min((int)(pos - (0xfff & offset)), len);
+ } else {
+EOF:
+ up_read(&cardp->semaphore);
+ *start = (char *)0;
+ *eof = 1;
+ return 0;
+ }
+}
+
+static int fepci_proc_read_devices(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ void *ioaddr;
+ unsigned int pos = 0;
+ unsigned int i;
+
+ pos +=
+ sprintf(buf + pos,
+ "bus:card.function cardtype model lines, max rate\n");
+ for (i = 0; i < find_cnt; i++) {
+ down_read(&card_privates[i].semaphore);
+ if (likely(card_privates[i].pci_dev)) { /* the card exists */
+ pos +=
+ sprintf(buf + pos,
+ "%02x:%s ",
+ card_privates[i].pci_dev->bus->
+ number,
+ card_privates[i].pci_dev->procent->name);
+ ioaddr = card_privates[i].ioaddr;
+ pos =
+ print_card_type(get_common_reg
+ (ioaddr, 0x00), buf, pos);
+ pos =
+ print_card_model(get_common_reg
+ (ioaddr, 0x00), buf, pos);
+ pos +=
+ sprintf(buf + pos, "%d",
+ get_common_reg(ioaddr, 0x02));
+ pos += sprintf(buf + pos, " x ");
+ pos =
+ print_line_type(get_common_reg
+ (ioaddr, 0x0c), buf, pos);
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos += sprintf(buf + pos, "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos += sprintf(buf + pos, ", total ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr,
+ 0x2a) *
+ get_common_reg(ioaddr, 0x02));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value or list "
+ "using mailbox\n");
+
+ }
+ up_read(&card_privates[i].semaphore);
+ }
+ pr_debug("fepci_proc_read_devices pos %u < %lu?\n", pos, PAGE_SIZE);
+ *eof = 1;
+ return pos;
+}
+
+static int fepci_proc_read_id_list(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ unsigned int pos = 0;
+ unsigned int i;
+
+ pos += sprintf(buf + pos, "bus:card.function major minor interfaces\n");
+ for (i = 0; i < find_cnt; i++) {
+ down_read(&card_privates[i].semaphore);
+ if (likely(card_privates[i].pci_dev)) /* the card exists */
+ pos +=
+ sprintf(buf + pos,
+ "%02x:%s %03i %03i %s..%s\n",
+ card_privates[i].pci_dev->bus->
+ number,
+ card_privates[i].pci_dev->
+ procent->name, major,
+ card_privates[i].
+ ch_privates[0]->minor,
+ card_privates[i].
+ ch_privates[0]->this_dev->name,
+ card_privates[i].
+ ch_privates[CHANNELS - 1]->this_dev->name);
+ up_read(&card_privates[i].semaphore);
+ }
+
+ pos += sprintf(buf + pos, "\n");
+ pr_debug("fepci_proc_read_id_list pos %u < %lu?\n", pos, PAGE_SIZE);
+ *eof = 1;
+ return pos;
+}
+
+const static char DEVICES[] = "devices";
+const static char ID_LIST[] = "id_list";
+
+static int fepci_proc_init_driver(void)
+{
+ pr_debug("fepci_proc_init_driver\n");
+ proc_root_entry = create_proc_entry(fepci_proc_entry_name, S_IFDIR, 0);
+ if (!proc_root_entry)
+ printk(KERN_WARNING
+ "%s: creating proc root dir entry failed.\n",
+ fepci_NAME);
+
+ create_proc_read_entry(DEVICES, 0, proc_root_entry,
+ fepci_proc_read_devices, NULL);
+ create_proc_read_entry(ID_LIST, 0, proc_root_entry,
+ fepci_proc_read_id_list, NULL);
+
+ return 0;
+}
+
+static void fepci_proc_cleanup_driver(void)
+{
+ char name[sizeof(fepci_proc_entry_name) + sizeof(DEVICES)];
+ strcpy(name, fepci_proc_entry_name);
+ name[sizeof(fepci_proc_entry_name) - 1] = '/';
+ strcpy(name + sizeof(fepci_proc_entry_name), DEVICES);
+ remove_proc_entry(name, 0);
+ strcpy(name + sizeof(fepci_proc_entry_name), ID_LIST);
+ remove_proc_entry(name, 0);
+ remove_proc_entry(fepci_proc_entry_name, 0);
+}
+
+static void fepci_proc_init_card(int card_number, void *card_data)
+{
+ char buf[50];
+ struct proc_dir_entry *ent;
+ sprintf(buf, "%02x:%02x.%02x",
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+ ent = create_proc_entry(buf, S_IFDIR, proc_root_entry);
+ if (!ent)
+ printk("%s: creating proc dir entry failed.\n", fepci_NAME);
+
+ sprintf(buf, fepci_features_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_features,
+ card_data);
+
+ sprintf(buf, fepci_settings_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_settings,
+ card_data);
+
+ sprintf(buf, fepci_status_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_status, card_data);
+}
+
+static void fepci_proc_cleanup_card(int card_number)
+{
+ char buf[50];
+
+ sprintf(buf, fepci_features_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ remove_proc_entry(buf, 0);
+ sprintf(buf, fepci_settings_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ remove_proc_entry(buf, 0);
+ sprintf(buf, fepci_status_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ remove_proc_entry(buf, 0);
+
+ sprintf(buf, "%02x:%02x.%02x",
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+ remove_proc_entry(buf, proc_root_entry);
+}
diff -Napur linux-2.6.23/drivers/net/wan/retina.h linux-2.6.24/drivers/net/wan/retina.h
--- linux-2.6.23/drivers/net/wan/retina.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.24/drivers/net/wan/retina.h 2007-10-25 09:22:15.579158599 +0300
@@ -0,0 +1,164 @@
+/* V1.0.0 */
+
+/*
+ Copyright (C) 2002-2003 Jouni Kujala, Flexibilis Oy.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ All the drivers derived from or based on this code fall under the
+ GPL and must retain the copyright and license notice.
+*/
+
+#ifndef RETINA_H
+#define RETINA_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* net device related stuff: */
+#define FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE 0x89F1
+#define FEPCI_NETDEV_IOCTL_STREAM_UNITSIZE 0x89F2
+#define FEPCI_NETDEV_IOCTL_STREAM_OPEN 0x89F3
+#define FEPCI_NETDEV_IOCTL_STREAM_START 0x89F4
+#define FEPCI_NETDEV_IOCTL_STREAM_CLOSE 0x89F6
+
+/* char device related stuff: */
+
+#define FEPCI_SHARED_MEM_OFFSETT 0x8000
+#define FEPCI_IDENTIFICATION_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x0)
+#define FEPCI_FEATURES_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x40)
+#define FEPCI_SETTINGS_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x80)
+#define FEPCI_STATUS_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0xE0)
+#define FEPCI_MAILBOX_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x100)
+
+/* structures for ioctl calls: */
+struct fepci_ioctl_identification {
+ unsigned char data[0x20];
+};
+struct fepci_real_identification {
+ unsigned long int data[0x10];
+};
+
+struct fepci_ioctl_features {
+ unsigned char data[0x20];
+};
+struct fepci_real_features {
+ unsigned long int data[0x10];
+};
+
+struct fepci_ioctl_settings {
+ unsigned char data[0x30];
+};
+struct fepci_real_settings {
+ unsigned long int data[0x15];
+};
+
+struct fepci_ioctl_status {
+ unsigned char data[0x10];
+};
+struct fepci_real_status {
+ unsigned long int data[0x5];
+};
+
+struct fepci_ioctl_shared_mem {
+ unsigned long int data[0x80];
+};
+
+#define FEPCI_IOCTL_MAGIC 0xAA
+
+#define FEPCI_IOCTL_R_SHARED_MEM _IOR(FEPCI_IOCTL_MAGIC, 1, \
+struct fepci_ioctl_shared_mem)
+#define FEPCI_IOCTL_W_SHARED_MEM _IOW(FEPCI_IOCTL_MAGIC, 2, \
+struct fepci_ioctl_shared_mem)
+
+#define FEPCI_IOCTL_G_IDENTIFICATION _IOR(FEPCI_IOCTL_MAGIC, 0x81, \
+struct fepci_ioctl_identification)
+#define FEPCI_IOCTL_G_FEATURES _IOR(FEPCI_IOCTL_MAGIC, 0x82, \
+struct fepci_ioctl_features)
+#define FEPCI_IOCTL_G_SETTINGS _IOR(FEPCI_IOCTL_MAGIC, 0x83, \
+struct fepci_ioctl_settings)
+#define FEPCI_IOCTL_G_STATUS _IOR(FEPCI_IOCTL_MAGIC, 0x84, \
+struct fepci_ioctl_status)
+
+/* mailbox: */
+
+struct fepci_ioctl_mailbox {
+ unsigned char Semafore;
+ unsigned char Mail_number;
+ unsigned char Size;
+ unsigned char Command;
+ unsigned char Data[112];
+};
+
+struct fepci_real_mailbox {
+ __u32 Semafore_Mail_number;
+ __u32 Size_Command;
+ __u32 Data[112 / 2];
+};
+
+#define FEPCI_IOCTL_B_POLL _IO(FEPCI_IOCTL_MAGIC, 0x85)
+#define FEPCI_IOCTL_B_GRAB _IO(FEPCI_IOCTL_MAGIC, 0x86)
+#define FEPCI_IOCTL_B_RELEASE _IO(FEPCI_IOCTL_MAGIC, 0x87)
+#define FEPCI_IOCTL_B_S_CMAIL _IOW(FEPCI_IOCTL_MAGIC, 0x88, \
+struct fepci_ioctl_mailbox)
+#define FEPCI_IOCTL_B_S_QMAIL _IOW(FEPCI_IOCTL_MAGIC, 0x89, \
+struct fepci_ioctl_mailbox)
+#define FEPCI_IOCTL_B_G_MAIL _IOR(FEPCI_IOCTL_MAGIC, 0x90, \
+struct fepci_ioctl_mailbox)
+
+#define FEPCI_IOCTL_ALARM_MANAGER _IO(FEPCI_IOCTL_MAGIC, 0x91)
+#define FEPCI_IOCTL_STREAM_TRANSMIT_POLL _IO(FEPCI_IOCTL_MAGIC, 0x92)
+#define FEPCI_IOCTL_STREAM_RECEIVE_POLL _IO(FEPCI_IOCTL_MAGIC, 0x93)
+#define FEPCI_IOCTL_STREAM_BOTH_POLL _IO(FEPCI_IOCTL_MAGIC, 0x94)
+
+/* stream related stuff: */
+
+/* stream buffer address space:
+ * address: 0x 7 6 5 4 3 2 1 0
+ * ^ ^ ^
+ * | | |
+ * card | area(rx/tx,0==rx,1==tx)
+ * channel */
+
+#define CARD_ADDRESS_SHIFT 24u
+#define CHANNEL_ADDRESS_SHIFT 20u
+#define AREA_ADDRESS_SHIFT 16u
+
+#define STREAM_BUFFER_POINTER_AREA 0x7fff0000 /* one page reserved */
+
+/* stream buffer pointers (at pointer area):
+ * address: 0x 7 6 5 4 3 2 1 0
+ * ^ ^ ^
+ * | | |
+ * card | area(rx/tx,0==rx,4==tx)
+ * channel */
+
+#define CARD_POINTER_SHIFT 8u
+#define CHANNEL_POINTER_SHIFT 4u
+#define AREA_POINTER_SHIFT 2u
+
+/* fake pointers are for faking larger unit sizes to the user than
+ * what is the maximum internal unit size in FEPCI */
+#define USER_RX_S_FAKE_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x0) + __offset))
+#define USER_TX_S_FAKE_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x4) + __offset))
+
+#define USER_RX_S_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x8) + __offset))
+#define USER_TX_S_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0xC) + __offset))
+
+#endif
diff -Napur linux-2.6.23/MAINTAINERS linux-2.6.24/MAINTAINERS
--- linux-2.6.23/MAINTAINERS 2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/MAINTAINERS 2007-10-23 12:27:47.056221363 +0300
@@ -3149,6 +3149,12 @@ L: reiserfs-devel@vger.kernel.org
W: http://www.namesys.com
S: Supported
+RETINA DRIVER
+P: Matti Linnanvuori
+M: mattilinnanvuori@yahoo.com
+L: netdev@vger.kernel.org
+S: Supported
+
ROCKETPORT DRIVER
P: Comtrol Corp.
W: http://www.comtrol.com
Machen Sie Yahoo! zu Ihrer Startseite. Los geht's:
http://de.yahoo.com/set
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH] wan: new driver retina
2007-10-25 6:49 [PATCH] wan: new driver retina Matti Linnanvuori
@ 2007-10-25 9:18 ` Jeff Garzik
0 siblings, 0 replies; 8+ messages in thread
From: Jeff Garzik @ 2007-10-25 9:18 UTC (permalink / raw)
To: Matti Linnanvuori; +Cc: akpm, info, netdev
Not sure what's going on, but your patch looks scrambled, as scripts
look at it:
> From: Matti Linnanvuori <mattilinnanvuori@yahoo.com>=0A=0AAdding Retina G.7=
> 03 and G.SHDSL driver.=0A=0ASigned-off-by: Matti Linnanvuori <mattilinnanvu=
> ori@yahoo.com>=0A---=0A=0AFixing bugs and style according to netdev mailing=
> list comments.=0A=0Adiff -Napur linux-2.6.23/drivers/net/wan/Kconfig linux=
> -2.6.24/drivers/net/wan/Kconfig=0A--- linux-2.6.23/drivers/net/wan/Kconfig=
> =092007-10-09 23:31:38.000000000 +0300=0A+++ linux-2.6.24/drivers/net/wan/K=
> config=092007-10-25 09:23:19.933170522 +0300=0A@@ -494,4 +494,15 @@ config =
> SBNI_MULTILINE=0A =0A =09 If unsure, say N.=0A =0A+config RETINA=0A+=09tri=
> state "Retina support"=0A+=09depends on PCI=0A+=09help=0A+=09 Driver for R=
Its been mangled into "Content-Transfer-Encoding: quoted-printable" by
your mailer.
Also, when you revise a patch, please note somehow that this is an
updated version. One convention is changing the subject line:
[PATCH v3] wan: new driver retina
Otherwise I have an mbox full of seemingly-similar patches, all with the
subject "wan: new driver retina"
Jeff
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH] wan: new driver retina
@ 2007-10-23 9:58 Matti Linnanvuori
2007-10-23 16:31 ` Stephen Hemminger
0 siblings, 1 reply; 8+ messages in thread
From: Matti Linnanvuori @ 2007-10-23 9:58 UTC (permalink / raw)
To: akpm, jgarzik; +Cc: netdev
From: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
Retina G.703 and G.SHDSL driver.
Signed-off-by: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
---
Fixed bugs according to linux-netdev comments.
diff -Napur linux-2.6.23/drivers/net/wan/Kconfig linux-2.6.24/drivers/net/wan/Kconfig
--- linux-2.6.23/drivers/net/wan/Kconfig 2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/drivers/net/wan/Kconfig 2007-10-23 12:30:45.853384514 +0300
@@ -494,4 +494,14 @@ config SBNI_MULTILINE
If unsure, say N.
+config RETINA
+ tristate "Retina support"
+ depends on PCI
+ help
+ Driver for Retina C5400 and E2200 network PCI cards, which
+ support G.703, G.SHDSL with Ethernet encapsulation.
+
+ To compile this driver as a module, choose M here: the
+ module will be called retina.
+
endif # WAN
diff -Napur linux-2.6.23/drivers/net/wan/Makefile linux-2.6.24/drivers/net/wan/Makefile
--- linux-2.6.23/drivers/net/wan/Makefile 2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/drivers/net/wan/Makefile 2007-10-23 12:31:17.598640178 +0300
@@ -42,6 +42,7 @@ obj-$(CONFIG_C101) += c101.o
obj-$(CONFIG_WANXL) += wanxl.o
obj-$(CONFIG_PCI200SYN) += pci200syn.o
obj-$(CONFIG_PC300TOO) += pc300too.o
+obj-$(CONFIG_RETINA) += retina.o
clean-files := wanxlfw.inc
$(obj)/wanxl.o: $(obj)/wanxlfw.inc
diff -Napur linux-2.6.23/drivers/net/wan/retina.c linux-2.6.24/drivers/net/wan/retina.c
--- linux-2.6.23/drivers/net/wan/retina.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.24/drivers/net/wan/retina.c 2007-10-23 12:52:17.122802714 +0300
@@ -0,0 +1,4428 @@
+/* retina.c: */
+
+/*
+ This driver is based on:
+
+ /drivers/net/fepci.c
+ FEPCI (Frame Engine for PCI) driver for Linux operating system
+
+ Copyright (C) 2002-2003 Jouni Kujala, Flexibilis Oy.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ All the drivers derived from or based on this code fall under the
+ GPL and must retain the copyright and license notice.
+*/
+
+#define DRV_NAME "retina"
+#define DRV_VERSION "1.2.6"
+
+#define DBG_PRINT(xyz...) /* printk(KERN_DEBUG xyz) */
+
+/* uncomment this if you want to have debug files in /proc
+ * #define DEBUG_PROC_FILES */
+
+/* Keep this if you want to have point-to-point links
+ ! Only interfaces listed in retina_ptp_interfaces will
+ be created in PtP mode
+ see retina_ptp_interfaces */
+#define FEPCI_POINT_TO_POINT
+
+/* need to update MODULE_PARM also */
+#define MAX_DEVICES 32u
+
+#define MAX_TX_UNITS 256u
+#define MAX_RX_UNITS 256u
+
+#define MAX_UNIT_SZ_ORDER 10u
+
+#define TX_RING_SIZE 8u
+#define RX_RING_SIZE 8u
+
+/* need to update MODULE_PARM also */
+#define CHANNELS 4u
+
+#define RX_FIFO_THRESHOLD_PACKET_MODE 0x4
+#define TX_FIFO_THRESHOLD_PACKET_MODE 0x4
+#define TX_DESC_THRESHOLD_PACKET_MODE 0x4
+
+#define RX_FIFO_THRESHOLD_STREAM_MODE 0x4
+#define TX_FIFO_THRESHOLD_STREAM_MODE 0x7
+#define TX_DESC_THRESHOLD_STREAM_MODE 0x1
+
+/* need to update MODULE_PARM also */
+#define MAX_INTERFACES (CHANNELS*MAX_DEVICES)
+
+const char fepci_name[] = "retina";
+const char fepci_alarm_manager_name[] = "retina alarm manager";
+const char fepci_NAME[] = "RETINA";
+const char fepci_netdev_name[] = "dcpxx";
+const char fepci_proc_entry_name[] = "driver/retina";
+
+static unsigned int find_cnt;
+
+#ifdef FEPCI_POINT_TO_POINT
+static char *retina_ptp_interfaces[MAX_INTERFACES];
+static int retina_noarp_with_ptp = 1;
+#endif /* FEPCI_POINT_TO_POINT */
+
+#define fepci_features_proc_entry_name "driver/retina/%02x:%02x.%02x/features"
+#define fepci_settings_proc_entry_name "driver/retina/%02x:%02x.%02x/settings"
+#define fepci_status_proc_entry_name "driver/retina/%02x:%02x.%02x/status"
+
+#ifdef DEBUG_PROC_FILES
+#define fepci_counters_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/counters_ch%d"
+#define fepci_descriptors_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/descriptors_ch%d"
+#define fepci_stream_counters_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/stream_counters_ch%d"
+#define fepci_registers_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/registers_ch%d"
+#endif /* DEBUG_PROC_FILES */
+
+/* Time in jiffies before concluding that the transmitter is hung */
+#define TX_TIMEOUT (20*HZ)
+
+#include "retina.h"
+#include <linux/mm.h>
+#include <linux/random.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/version.h>
+#include <linux/pfn.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/unaligned.h>
+#include <asm/pgtable.h>
+
+MODULE_VERSION(DRV_VERSION);
+
+/* PCI I/O space extent */
+#define FEPCI_SIZE 0x20000
+#define PCI_IOTYPE (PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY)
+
+struct pci_id_info {
+ const char *name;
+ struct match_info {
+ int pci, pci_mask, subsystem, subsystem_mask;
+ int revision, revision_mask; /* Only 8 bits. */
+ } id;
+ unsigned pci_flags;
+ int io_size; /* Needed for I/O region check or ioremap */
+ int drv_flags; /* Driver use, intended as capability flags */
+};
+
+static struct pci_id_info pci_id_tbl[] = {
+ {"Frame Engine for PCI (FEPCI)",
+ {0x1FC00300, 0x1FC00301, 0xffffffff},
+ PCI_IOTYPE, FEPCI_SIZE},
+ {0,},
+};
+
+static struct pci_device_id fepci_pci_tbl[] __devinitdata = {
+ {0x1FC0, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x1FC0, 0x0301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+MODULE_DESCRIPTION("Frame Engine for PCI (FEPCI)");
+MODULE_AUTHOR("Jouni Kujala");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, fepci_pci_tbl);
+
+/* Linux appears to drop POINTOPOINT,BROADCAST and NOARP flags in SIOCSFLAGS
+ * This workaround allows load time per interface ptp mode configuration.
+ * Runtime ptp mode changes would either require changes to Linux or
+ * use of proprietary ioctls, which ifconfig knows nothing about anyway
+ */
+
+static unsigned interfaces = MAX_INTERFACES;
+#ifdef FEPCI_POINT_TO_POINT
+module_param_array(retina_ptp_interfaces, charp, &interfaces, S_IRUGO);
+module_param(retina_noarp_with_ptp, bool, S_IRUGO);
+MODULE_PARM_DESC(retina_noarp_with_ptp,
+ "0 to disable NOARP, "
+ "1 to enable NOARP on pointopoint interfaces");
+#endif
+
+struct fepci_ch_private {
+ unsigned int channel_number;
+ struct net_device *this_dev;
+
+ struct fepci_card_private *this_card_priv;
+
+ unsigned int reg_rxctrl;
+ unsigned int reg_txctrl;
+
+ struct fepci_desc *rx_desc; /* rx_ring start */
+ struct fepci_desc *tx_desc; /* tx_ring start */
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+
+ struct timer_list timer;
+ struct net_device_stats stats;
+
+ unsigned drv_flags;
+
+ unsigned int rx_buf_sz; /* MTU+slack */
+ unsigned int cur_tx; /* the next filled tx_descriptor */
+ /* in stream mode the desc which is being transmitted */
+ /* rx_descriptor where next packet transferred */
+ unsigned int cur_rx;
+ /* in stream mode the desc which is being received */
+
+/* stream mode: */
+ bool in_eth_mode;
+ bool in_stream_mode;
+ bool stream_on;
+ u32 *rx_buffer;
+ u32 *tx_buffer;
+ unsigned bufsize_order; /* 10=1kB,11=2kB,12=4kB...16=64kB */
+ unsigned bufsize;
+ unsigned unit_sz_order; /* 8=256B...14=16kB */
+ unsigned unit_sz;
+ unsigned units; /* 2,4,8,16,...,256 */
+ /* fake units (and pointers) are for faking larger unit sizes to
+ * the user than what is the maximum internal unit size in FEPCI */
+ unsigned fake_unit_sz_order;
+ unsigned fake_unit_sz;
+ unsigned fake_units;
+ u32 *tx_unit[MAX_TX_UNITS];
+ u32 *rx_unit[MAX_RX_UNITS];
+ unsigned cur_tx_unit; /* last sent tx_unit */
+ /* rx_unit where to next packet is transferred */
+ unsigned cur_rx_unit;
+/* char device: */
+ unsigned minor; /* currently the same as card_nuber */
+/* debugging stuff for packet mode: */
+ /* rx-errors in descriptors */
+ unsigned int rx_desc_fifo_err;
+ unsigned int rx_desc_size_err;
+ unsigned int rx_desc_crc_err;
+ unsigned int rx_desc_octet_err;
+ unsigned rx_desc_line_err;
+ /* tx-errors in descriptors */
+ unsigned tx_desc_fifo_err;
+ /* rx-errors in interrupts */
+ unsigned rx_int_fifo_err;
+ unsigned rx_int_frame_dropped_err;
+ /* tx-errors in interrupts */
+ unsigned tx_int_fifo_err;
+ /* ints */
+ unsigned interrupts;
+ unsigned rx_interrupts;
+ unsigned tx_interrupts;
+ /* error combination tables */
+ /* fifo,size,crc,octet,line */
+ unsigned rx_desc_err_table[2][2][2][2][2];
+ unsigned int_err_table[2][2]; /* fifo,frame_dropped */
+ /* skbuff counters */
+ unsigned rx_skbuffs_in;
+ unsigned rx_skbuffs_out;
+ unsigned tx_skbuffs_in;
+ unsigned tx_skbuffs_out;
+/* debugging stuff for stream mode: */
+ /* rx-errors in descriptors */
+ unsigned rx_desc_fifo_err_stream;
+ unsigned rx_desc_size_err_stream;
+ unsigned rx_desc_crc_err_stream;
+ unsigned rx_desc_octet_err_stream;
+ unsigned rx_desc_line_err_stream;
+ /* tx-errors in descriptors */
+ unsigned tx_desc_fifo_err_stream;
+ /* rx-errors in interrupts */
+ unsigned rx_int_fifo_err_stream;
+ unsigned rx_int_frame_dropped_err_stream;
+ /* tx-errors in interrupts */
+ unsigned tx_int_fifo_err_stream;
+ /* ints */
+ unsigned interrupts_stream;
+ unsigned rx_interrupts_stream;
+ unsigned tx_interrupts_stream;
+ /* error combination tables */
+ /* fifo,size,crc,octet,line */
+ unsigned rx_desc_err_table_stream[2][2][2][2][2];
+ unsigned int_err_table_stream[2][2]; /* fifo,frame_dropped */
+/* other: */
+ /* tx interrupts since last timer interrupt */
+ unsigned tx_interrupts_since_last_timer;
+/* small packet counters: */
+ unsigned rx_packets_of_size_0;
+ unsigned rx_packets_of_size_1;
+ unsigned rx_packets_of_size_2;
+ unsigned rx_packets_of_size_3;
+ unsigned rx_packets_of_size_4_7;
+ unsigned rx_packets_of_size_8_15;
+ unsigned rx_packets_of_size_16_31;
+/* small packet counters for stream: */
+ unsigned rx_packets_of_size_0_stream;
+ unsigned rx_packets_of_size_1_stream;
+ unsigned rx_packets_of_size_2_stream;
+ unsigned rx_packets_of_size_3_stream;
+ unsigned rx_packets_of_size_4_7_stream;
+ unsigned rx_packets_of_size_8_15_stream;
+ unsigned rx_packets_of_size_16_31_stream;
+};
+
+struct fepci_card_private {
+ unsigned int card_number;
+ u8 *ioaddr;
+ /* Process ID of the current mailbox user
+ * (for whom it is reserved for) */
+ unsigned int ioctl_saved_pid;
+ struct pci_dev *pci_dev;
+ struct fepci_ch_private *ch_privates[CHANNELS];
+
+ wait_queue_head_t alarm_manager_wait_q;
+ struct timer_list mailbox_timer;
+
+ wait_queue_head_t stream_receive_q;
+ wait_queue_head_t stream_transmit_q;
+ wait_queue_head_t stream_both_q;
+
+ struct rw_semaphore semaphore;
+};
+
+/* Offsets to the FEPCI registers */
+enum fepci_offsets {
+ reg_custom = 0x40,
+
+ reg_first_int_mask = 0x80,
+ reg_first_int_status = 0xc0,
+
+ reg_first_rxctrl = 0x4000,
+ to_next_rxctrl = 0x80,
+
+ reg_first_txctrl = 0x6000,
+ to_next_txctrl = 0x80,
+
+ first_rx_desc = 0x10000,
+ to_next_ch_rx_desc = 0x200,
+
+ first_tx_desc = 0x18000,
+ to_next_ch_tx_desc = 0x200,
+};
+
+enum reg_custom_bits {
+ AM_interrupt_mask = 0x1,
+ AM_interrupt_status = 0x100,
+};
+
+enum reg_receive_control {
+ Rx_fifo_threshold = 0x7,
+ Receive_enable = 0x80000000,
+};
+
+enum reg_transmit_control {
+ Tx_fifo_threshold = 0x7,
+ Tx_desc_threshold = 0x700,
+ Transmit_enable = 0x80000000,
+};
+
+enum int_bits {
+ MaskFrameReceived = 0x01, MaskRxFifoError =
+ 0x02, MaskRxFrameDroppedError = 0x04,
+ MaskFrameTransmitted = 0x40, MaskTxFifoError = 0x80,
+ MaskAllInts = 0xc7,
+ IntrFrameReceived = 0x01, IntrRxFifoError =
+ 0x02, IntrRxFrameDroppedError = 0x04,
+ IntrFrameTransmitted = 0x40, IntrTxFifoError = 0x80,
+ IntrAllInts = 0xc7,
+};
+
+/* The FEPCI Rx and Tx buffer descriptors
+ * Elements are written as 32 bit for endian portability */
+
+struct fepci_desc {
+ u32 desc_a;
+ u32 desc_b;
+};
+
+enum desc_b_bits {
+ frame_length = 0xFFF,
+ fifo_error = 0x10000,
+ size_error = 0x20000,
+ crc_error = 0x40000,
+ octet_error = 0x80000,
+ line_error = 0x100000,
+ enable_transfer = 0x80000000,
+ transfer_not_done = 0x80000000,
+};
+
+/* global variables (common to whole driver, all the cards): */
+int major; /* char device major number */
+struct fepci_card_private card_privates[MAX_DEVICES];
+unsigned long stream_pointers;
+struct proc_dir_entry *proc_root_entry;
+
+void set_int_mask(int channel, u_char value, struct fepci_card_private *cp)
+{
+ void *address;
+ unsigned shift, oldvalue;
+ DBG_PRINT("set_int_mask\n");
+ address = (cp->ioaddr) + reg_first_int_mask + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ oldvalue = readl((void *)address);
+ oldvalue &= ~(0xff << shift); /* clear bits */
+ oldvalue |= value << shift; /* set bits */
+ writel(oldvalue, (void *)address);
+}
+
+u_char get_int_mask(int channel, void *ioaddr)
+{
+ void *address;
+ unsigned shift, oldvalue;
+ DBG_PRINT("get_int_mask\n");
+ address = ioaddr + reg_first_int_mask + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ oldvalue = readl((void *)address);
+ oldvalue &= (0xff << shift); /* clear other bits */
+ return (oldvalue >> shift);
+}
+
+void clear_int(int channel, u_char value, void *ioaddr)
+{
+ void *address;
+ unsigned shift, longvalue;
+ DBG_PRINT("clear_int\n");
+ address = ioaddr + reg_first_int_status + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ longvalue = value << shift;
+ writel(~longvalue, (void *)address);
+}
+
+u_char get_int_status(int channel, void *ioaddr)
+{
+ void *address;
+ unsigned shift, oldvalue;
+ DBG_PRINT("get_int_status\n");
+ address = ioaddr + reg_first_int_status + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ oldvalue = readl((void *)address);
+ oldvalue &= (0xff << shift); /* clear other bits */
+ return (oldvalue >> shift);
+}
+
+void fillregisterswith_00(void *ioaddr)
+{
+ DBG_PRINT("fillregisterswith_00\n");
+ writel(0x0, (void *)(ioaddr + reg_first_rxctrl));
+ writel(0x0, (void *)(ioaddr + reg_first_txctrl));
+ writel(0x0, (void *)(ioaddr + reg_first_int_mask));
+ writel(0x0, (void *)(ioaddr + reg_first_int_status));
+ writel(0x0, (void *)(ioaddr + first_rx_desc));
+ writel(0x0, (void *)(ioaddr + first_tx_desc));
+}
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int fepci_open(struct net_device *dev);
+static void fepci_timer(unsigned long data);
+static void fepci_tx_timeout(struct net_device *dev);
+static void fepci_init_ring(struct net_device *dev);
+static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t fepci_interrupt(int irq, void *dev_instance);
+static int fepci_rx(struct net_device *dev);
+static int fepci_close(struct net_device *dev);
+static struct net_device_stats *fepci_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static void fepci_remove_one(struct pci_dev *pdev);
+
+/* proc filesystem functions introduced: */
+
+static int fepci_proc_init_driver(void);
+static void fepci_proc_cleanup_driver(void);
+static void fepci_proc_init_card(int card_number, void *card_data);
+static void fepci_proc_cleanup_card(int card_number);
+#ifdef DEBUG_PROC_FILES
+static void fepci_proc_init_channel(int card_number, int channel_number,
+ void *channel_data);
+static void fepci_proc_cleanup_channel(int card_number, int channel_number);
+#endif /* DEBUG_PROC_FILES */
+
+/* char device operations: */
+
+ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+ loff_t *f_pos);
+int fepci_char_open(struct inode *inode, struct file *filp);
+int fepci_char_release(struct inode *inode, struct file *filp);
+int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma);
+int fepci_char_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+struct file_operations fepci_char_fops = {
+read: fepci_char_read,
+ioctl: fepci_char_ioctl,
+open: fepci_char_open,
+release: fepci_char_release,
+mmap: fepci_char_mmap,
+};
+
+int fepci_char_open(struct inode *inode, struct file *filp)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ DBG_PRINT("fepci_char_open\n");
+ if (unlikely(minor >= find_cnt || card_privates[minor].pci_dev == NULL))
+ return -ENXIO;
+ filp->f_op = &fepci_char_fops;
+ if (unlikely(!try_module_get(THIS_MODULE)))
+ return -EBUSY;
+ return 0;
+}
+
+int fepci_char_release(struct inode *inode, struct file *filp)
+{
+ DBG_PRINT("fepci_char_release\n");
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+void fepci_vma_open(struct vm_area_struct *vma)
+{
+ DBG_PRINT("fepci_vma_open\n");
+}
+
+void fepci_vma_close(struct vm_area_struct *vma)
+{
+ DBG_PRINT("fepci_vma_close\n");
+ module_put(THIS_MODULE);
+}
+
+static struct vm_operations_struct fepci_vm_ops = {
+open: fepci_vma_open,
+close: fepci_vma_close,
+};
+
+int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long size = vma->vm_end - vma->vm_start;
+
+ unsigned long virtual_address = 0;
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_ops = &fepci_vm_ops;
+ vma->vm_file = filp;
+
+ if (offset == STREAM_BUFFER_POINTER_AREA) {
+ virtual_address = stream_pointers;
+ if (virtual_address == 0) {
+ printk(KERN_WARNING "%s: mmap: internal error.\n",
+ fepci_name);
+ return -ENOMEM;
+ }
+ if (size > (1 << PAGE_SHIFT)) {
+ printk(KERN_WARNING
+ "%s: mmap: area size over range.\n", fepci_name);
+ return -EINVAL;
+ }
+ } else {
+ unsigned int page;
+
+ unsigned int card;
+ unsigned int channel;
+ unsigned int area; /* 0=rx, 1=tx */
+
+ card = (offset >> CARD_ADDRESS_SHIFT) & 0xf;
+ channel = (offset >> CHANNEL_ADDRESS_SHIFT) & 0xf;
+ area = (offset >> AREA_ADDRESS_SHIFT) & 0xf;
+ page = (offset & 0xffff); /* >> PAGE_SHIFT; */
+
+ if (area == 0) {
+ /* if there really is such card */
+ if (card < find_cnt && card_privates[card].pci_dev)
+ virtual_address =
+ (unsigned long)card_privates[card].
+ ch_privates[channel]->rx_buffer;
+ else
+ goto INVALID;
+ } else if (area == 1) {
+ /* if there really is such card */
+ if (card < find_cnt && card_privates[card].pci_dev)
+ virtual_address =
+ (unsigned long)card_privates[card].
+ ch_privates[channel]->tx_buffer;
+ else
+ goto INVALID;
+ } else {
+INVALID:
+ DBG_PRINT("%s: mmap: invalid address 0x%lx\n",
+ fepci_NAME, virtual_address);
+ return -EINVAL;
+ }
+ if (virtual_address == 0)
+ goto INVALID;
+ }
+
+ if (!try_module_get(THIS_MODULE))
+ return -EBUSY;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ {
+ unsigned pfn = PFN_DOWN(virt_to_phys((void *)virtual_address));
+ int error = io_remap_pfn_range(vma, vma->vm_start, pfn,
+ size, vma->vm_page_prot);
+ if (unlikely(error))
+ return error;
+ }
+ fepci_vma_open(vma);
+ return 0;
+}
+
+/* mmap operations end */
+
+/* char operations start */
+
+void fepci_copy_to_user(unsigned long to, void *from, unsigned long len,
+ int shrink)
+{
+ unsigned int i;
+ DBG_PRINT("fepci_copy_to_user\n");
+ if (shrink) {
+ for (i = 0; i < len; i += 2) {
+ put_user((((unsigned long *)from)[i / 2]) & 0xff,
+ (unsigned char *)(to + i));
+ put_user((((unsigned long *)from)[i / 2]) >> 8,
+ (unsigned char *)(to + i + 1));
+ }
+ } else {
+ for (i = 0; i < len; i += 4)
+ put_user(((unsigned long *)from)[i / 4],
+ (unsigned long *)(to + i));
+ }
+}
+
+void fepci_copy_from_user(void *to, unsigned long from, unsigned long len,
+ int enlarge)
+{
+ unsigned int i;
+ if (enlarge) {
+ for (i = 0; i < len; i += 2) {
+ unsigned char temp1;
+ unsigned char temp2;
+ get_user(temp1, (unsigned char *)(from + i));
+ get_user(temp2, (unsigned char *)(from + i + 1));
+ *((unsigned long *)(to + i * 2)) = temp1 + (temp2 << 8);
+ }
+ } else {
+ for (i = 0; i < len; i += 4)
+ get_user(((unsigned long *)to)[i / 4],
+ (unsigned long *)(from + i));
+ }
+}
+
+unsigned get_semafore(struct fepci_real_mailbox *mailbox)
+{
+ unsigned semafore = readb(&mailbox->Semafore_Mail_number);
+ DBG_PRINT("get_semafore = %x\n", semafore);
+ return semafore;
+}
+
+int set_semafore(struct fepci_real_mailbox *mailbox, unsigned semafore)
+{
+ unsigned number = readl(&mailbox->Semafore_Mail_number);
+ DBG_PRINT("got number %u at %p.\n", number,
+ &mailbox->Semafore_Mail_number);
+ number = ((number & ~0xFF) | semafore) + (1 << 8);
+ DBG_PRINT
+ ("increases the mail number to %u at the same time at %p.\n",
+ number, &mailbox->Semafore_Mail_number);
+ writel(number, &mailbox->Semafore_Mail_number);
+ DBG_PRINT("set_semafore %p, %u returns 0.\n", mailbox, semafore);
+ return 0;
+}
+
+static void fepci_mailbox_timer(unsigned long data)
+{
+ int card_number = data;
+ unsigned int *saved_pid = &card_privates[card_number].ioctl_saved_pid;
+ void *ioaddr = card_privates[card_number].ioaddr;
+ struct fepci_real_mailbox *real_mailbox =
+ (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
+
+ set_semafore(real_mailbox, 0x0);
+ *saved_pid = 0;
+}
+
+int fepci_char_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ void *ioaddr;
+ struct fepci_real_mailbox *real_mailbox;
+ int retval = 0;
+ unsigned int *saved_pid;
+ unsigned int my_pid;
+
+ if (minor >= find_cnt || card_privates[minor].pci_dev == NULL) {
+ printk(KERN_WARNING
+ "%s: trying to access a card that does not exist.\n",
+ fepci_NAME);
+ /* if trying to access a card that does not exist */
+ return -ENXIO;
+ }
+
+ DBG_PRINT("fepci_char_ioctl minor %u.\n", minor);
+
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ if (!access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ if (!access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+
+ ioaddr = card_privates[minor].ioaddr;
+ real_mailbox =
+ (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
+ saved_pid = &card_privates[minor].ioctl_saved_pid;
+ my_pid = current->pid;
+
+ switch (cmd) {
+ case FEPCI_IOCTL_STREAM_TRANSMIT_POLL:
+ /* here: arg == channel number */
+ if (arg < 0 || arg >= CHANNELS
+ || !(card_privates[minor].ch_privates[arg]->stream_on))
+ return 0x2;
+ {
+ u32 pointer = *USER_TX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+ wait_event_interruptible((card_privates[minor].
+ stream_transmit_q)
+ ,
+ (pointer !=
+ *USER_TX_S_FAKE_POINTER
+ (minor, arg,
+ stream_pointers)));
+ return 0x1;
+ }
+ return retval;
+ case FEPCI_IOCTL_STREAM_RECEIVE_POLL:
+ /* here: arg == channel number */
+ if (arg < 0 || arg >= CHANNELS
+ || !(card_privates[minor].ch_privates[arg]->stream_on))
+ return 0x2;
+ {
+ u32 pointer = *USER_RX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+ wait_event_interruptible((card_privates[minor].
+ stream_receive_q)
+ ,
+ (pointer !=
+ *USER_RX_S_FAKE_POINTER
+ (minor, arg,
+ stream_pointers)));
+ retval = 0x1;
+ }
+ return retval;
+ case FEPCI_IOCTL_STREAM_BOTH_POLL:
+ /* here: arg == channel number */
+ if (arg < 0 || arg >= CHANNELS
+ || !(card_privates[minor].ch_privates[arg]->stream_on))
+ return 0x2;
+ {
+ u32 temp_tx_pointer =
+ *USER_TX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+ u32 temp_rx_pointer =
+ *USER_RX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+
+ wait_event_interruptible((card_privates[minor].
+ stream_both_q)
+ ,
+ (temp_tx_pointer !=
+ *USER_TX_S_FAKE_POINTER
+ (minor, arg, stream_pointers))
+ || (temp_rx_pointer !=
+ *USER_RX_S_FAKE_POINTER
+ (minor, arg,
+ stream_pointers)));
+ retval = 0x1;
+ }
+ return retval;
+ case FEPCI_IOCTL_R_SHARED_MEM:
+ DBG_PRINT(" %s: ioctl read shared mem commanded.\n",
+ fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_SHARED_MEM_OFFSETT,
+ _IOC_SIZE(cmd), 0);
+ break;
+ case FEPCI_IOCTL_W_SHARED_MEM:
+ DBG_PRINT(" %s: ioctl write shared mem commanded.\n",
+ fepci_NAME);
+ fepci_copy_from_user(ioaddr + FEPCI_SHARED_MEM_OFFSETT,
+ arg, _IOC_SIZE(cmd), 0);
+ break;
+ case FEPCI_IOCTL_G_IDENTIFICATION:
+ DBG_PRINT(" %s: IOCTL_G_IDENTIFICATION commanded.\n",
+ fepci_NAME);
+ fepci_copy_to_user(arg,
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_G_FEATURES:
+ DBG_PRINT(" %s: IOCTL_G_FEATURES commanded.\n", fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_FEATURES_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_G_SETTINGS:
+ DBG_PRINT(" %s: IOCTL_G_SETTINGS commanded.\n", fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_SETTINGS_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_G_STATUS:
+ DBG_PRINT(" %s: IOCTL_G_STATUS commanded.\n", fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_STATUS_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_B_POLL:
+ DBG_PRINT(" %s: IOCTL_B_POLL commanded.\n", fepci_NAME);
+ retval = get_semafore(real_mailbox);
+ if ((retval == 0x20 || retval == 0x21 || retval == 0x40)
+ && *saved_pid != my_pid)
+ retval = 0x7;
+ del_timer_sync(&card_privates[minor].mailbox_timer);
+ card_privates[minor].mailbox_timer.expires = jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.function =
+ &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+ break;
+ case FEPCI_IOCTL_B_GRAB:
+ DBG_PRINT("%s: IOCTL_B_GRAB commanded.\n", fepci_NAME);
+ if ((my_pid != *saved_pid) && (*saved_pid != 0)) {
+ retval = 0x2;
+ break;
+ }
+ DBG_PRINT("%s: IOCTL_B_GRAB getting semaphore.\n", fepci_NAME);
+ if (get_semafore(real_mailbox) == 0x0) {
+ DBG_PRINT("%s: IOCTL_B_GRAB setting semaphore.\n",
+ fepci_NAME);
+ set_semafore(real_mailbox, 0x40);
+ DBG_PRINT("%s: IOCTL_B_GRAB sleeping.\n", fepci_NAME);
+ msleep(1); /* delay at least 1 millisecond */
+ DBG_PRINT
+ ("%s: IOCTL_B_GRAB getting semaphore again.\n",
+ fepci_NAME);
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ retval = 0x0;
+ DBG_PRINT
+ ("%s: IOCTL_B_GRAB saving pid to %p.\n",
+ fepci_NAME, saved_pid);
+ *saved_pid = my_pid;
+ DBG_PRINT
+ ("%s: IOCTL_B_GRAB deleting timer at %p.\n",
+ fepci_NAME,
+ &card_privates[minor].mailbox_timer);
+ del_timer_sync(&card_privates[minor].
+ mailbox_timer);
+ card_privates[minor].mailbox_timer.
+ expires = jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.
+ function = &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+ break;
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ default:
+ retval = 0xff;
+ }
+ } else {
+ switch (get_semafore(real_mailbox)) {
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ default:
+ retval = 0xff;
+ }
+ }
+ break;
+ case FEPCI_IOCTL_B_RELEASE:
+ DBG_PRINT(" %s: IOCTL_B_RELEASE commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ break;
+ }
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ case 0x20:
+ retval = 0x0;
+ set_semafore(real_mailbox, 0x0);
+ *saved_pid = 0;
+ del_timer(&card_privates[minor].mailbox_timer);
+ break;
+ case 0x21:
+ retval = 0x04;
+ break;
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ default:
+ retval = 0xff;
+ }
+ break;
+ case FEPCI_IOCTL_B_S_CMAIL:
+ DBG_PRINT(" %s: IOCTL_B_S_CMAIL commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ break;
+ }
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ case 0x20:
+ case 0x21:
+ /* copy the mailbox */
+ fepci_copy_from_user(ioaddr +
+ FEPCI_MAILBOX_OFFSETT + 4,
+ arg + 2, _IOC_SIZE(cmd) - 2, 1);
+ /* semafore -> 10 */
+ set_semafore(real_mailbox, 0x10);
+ retval = 0x0;
+
+ del_timer_sync(&card_privates[minor].mailbox_timer);
+ card_privates[minor].mailbox_timer.expires =
+ jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.function =
+ &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+
+ break;
+
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ case 0x0:
+ retval = 0x3;
+ break;
+ default:
+ retval = 0xff;
+ }
+ break;
+ case FEPCI_IOCTL_B_S_QMAIL:
+ DBG_PRINT(" %s: IOCTL_B_S_QMAIL commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ break;
+ }
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ case 0x20:
+ case 0x21:
+ /* copy the mailbox; */
+ fepci_copy_from_user(ioaddr +
+ FEPCI_MAILBOX_OFFSETT + 4,
+ arg + 2, _IOC_SIZE(cmd) - 2, 1);
+ /* semafore -> 11 */
+ set_semafore(real_mailbox, 0x11);
+ retval = 0x0;
+
+ del_timer_sync(&card_privates[minor].mailbox_timer);
+ card_privates[minor].mailbox_timer.expires =
+ jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.function =
+ &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+
+ break;
+
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ case 0x0:
+ retval = 0x3;
+ break;
+ default:
+ retval = 0xff;
+ }
+ break;
+ case FEPCI_IOCTL_B_G_MAIL:
+ DBG_PRINT(" %s: IOCTL_B_G_MAIL commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ } else {
+ switch (get_semafore(real_mailbox)) {
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ case 0x40:
+ case 0x20:
+ case 0x21:
+ retval = 0x0;
+ fepci_copy_to_user(arg,
+ ioaddr +
+ FEPCI_MAILBOX_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+
+ del_timer_sync(&card_privates[minor].
+ mailbox_timer);
+ card_privates[minor].mailbox_timer.
+ expires = jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.
+ function = &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+
+ break;
+ case 0x0:
+ retval = 0x3;
+ break;
+ default:
+ retval = 0xff;
+ }
+ }
+ if (retval != 0) {
+ static unsigned char seven = 7;
+ /* copy four lowest bytes from the mailbox */
+ fepci_copy_to_user(arg,
+ ioaddr + FEPCI_MAILBOX_OFFSETT,
+ 4, 1);
+ /* lowest byte = 0x7 */
+ __put_user(arg, &seven);
+ }
+ break;
+ case FEPCI_IOCTL_ALARM_MANAGER:
+ DBG_PRINT(" %s: IOCTL_ALARM_MANAGER commanded.\n", fepci_NAME);
+ interruptible_sleep_on(&(card_privates[minor].
+ alarm_manager_wait_q));
+ return retval;
+ default:
+ DBG_PRINT(" %s: Unknown ioctl command 0x%x.\n", fepci_NAME,
+ cmd);
+ return -ENOTTY;
+ }
+ return retval;
+}
+
+ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+ loff_t *f_pos)
+{
+ DBG_PRINT("fepci_char_read\n");
+ if (count > 1)
+ count = 1;
+ if (unlikely(copy_to_user(buf, "\n", count)))
+ return -EFAULT;
+ return count;
+}
+
+static int fepci_register_char_device(void)
+{
+ int error =
+ register_chrdev(0 /* dynamic */ , fepci_name, &fepci_char_fops);
+ if (unlikely(error < 0))
+ printk(KERN_WARNING
+ "%s: unable to register char device.\n", fepci_NAME);
+ else
+ DBG_PRINT("%s: registered char device, major:0x%x.\n",
+ fepci_NAME, error);
+ return error;
+}
+
+void fepci_unregister_char_device(void)
+{
+ unregister_chrdev(major, fepci_name);
+}
+
+/* char operations end */
+
+/* stream operations start */
+
+static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance);
+static int fepci_stream_close(struct net_device *dev);
+
+static int fepci_stream_open(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned tx_pages, rx_pages, tx_order, rx_order;
+ unsigned page_number;
+ unsigned int i;
+
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_eth_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Interface is in Ethernet mode, "
+ "cannot open stream interface.\n", fepci_NAME);
+BUSY:
+ return -EBUSY;
+ }
+ if (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Interface is already in stream mode, "
+ "cannot open stream interface.\n", fepci_NAME);
+ goto BUSY;
+ }
+
+ if (unlikely(fp->this_card_priv->pci_dev == NULL)) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -ENXIO;
+ }
+
+ fp->bufsize = 1 << fp->bufsize_order;
+
+ if (fp->fake_unit_sz_order < 5) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Unit size has to be at least 32 Bytes.\n",
+ fepci_NAME);
+INVALID:
+ return (-EINVAL);
+ }
+
+ if (fp->fake_unit_sz_order >= fp->bufsize_order) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Bufsize has to be greater than unit size.\n",
+ fepci_NAME);
+ goto INVALID;
+ }
+
+ if (fp->fake_unit_sz_order >= MAX_UNIT_SZ_ORDER) {
+ fp->unit_sz_order = MAX_UNIT_SZ_ORDER;
+ } else {
+ fp->unit_sz_order = fp->fake_unit_sz_order;
+ }
+
+ fp->fake_unit_sz = 1 << fp->fake_unit_sz_order;
+ fp->unit_sz = 1 << fp->unit_sz_order;
+ fp->units = 1 << (fp->bufsize_order - fp->unit_sz_order);
+ fp->fake_units = 1 << (fp->bufsize_order - fp->fake_unit_sz_order);
+
+ /* reserve memory */
+ if (fp->bufsize_order < PAGE_SHIFT) {
+ rx_order = 0;
+ tx_order = 0;
+ rx_pages = 1;
+ tx_pages = 1;
+ } else {
+ tx_order = fp->bufsize_order - PAGE_SHIFT;
+ tx_pages = 1 << tx_order;
+ rx_order = tx_order + 1;
+ rx_pages = 1 << rx_order;
+ }
+ fp->in_stream_mode = 1;
+ fp->tx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, tx_order);
+ if (!fp->tx_buffer)
+ goto NO_MEMORY;
+ fp->rx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, rx_order);
+ if (!fp->rx_buffer) {
+NO_MEMORY:
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate memory for buffers.\n",
+ fepci_NAME);
+ fepci_stream_close(dev);
+ return (-ENOMEM);
+ }
+
+ for (page_number = 0; page_number < rx_pages; page_number++)
+ /* make pages reserved to allow remappping pages
+ with io_remap_pfn_range */
+ SetPageReserved(virt_to_page
+ ((unsigned long)fp->rx_buffer +
+ (page_number << PAGE_SHIFT)));
+ for (page_number = 0; page_number < tx_pages; page_number++)
+ /* make pages reserved to allow remappping pages
+ with io_remap_pfn_range */
+ SetPageReserved(virt_to_page
+ ((unsigned long)fp->tx_buffer +
+ (page_number << PAGE_SHIFT)));
+
+ for (i = 0; i < (fp->bufsize) / 4; i++)
+ fp->tx_buffer[i] = 0xffffffff;
+
+ /* + fp->channel_number; */
+ *USER_RX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
+ stream_pointers) = 0;
+ /* + fp->channel_number; */
+ *USER_TX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
+ stream_pointers) = 0;
+ /* + fp->channel_number; */
+ *USER_RX_S_FAKE_POINTER(fp->this_card_priv->card_number,
+ fp->channel_number, stream_pointers) = 0;
+ /* + fp->channel_number; */
+ *USER_TX_S_FAKE_POINTER(fp->this_card_priv->card_number,
+ fp->channel_number, stream_pointers) = 0;
+
+ DBG_PRINT("%s: Bufsize is 0x%x.\n", fepci_NAME, fp->bufsize);
+ DBG_PRINT("%s: Unit_size is 0x%x.\n", fepci_NAME, fp->unit_sz);
+ DBG_PRINT("%s: Number of units is 0x%x.\n", fepci_NAME, fp->units);
+
+ DBG_PRINT("%s: Fake_unit_size is 0x%x.\n", fepci_NAME,
+ fp->fake_unit_sz);
+ DBG_PRINT("%s: Number of fake units is 0x%x.\n", fepci_NAME,
+ fp->fake_units);
+
+ /* init ring buffers */
+ for (i = 0; i < MAX_RX_UNITS; i++)
+ fp->rx_unit[i] =
+ (u32 *) ((u32) (fp->rx_buffer) + (fp->unit_sz * i));
+ for (i = 0; i < MAX_TX_UNITS; i++)
+ fp->tx_unit[i] =
+ (u32 *) ((u32) (fp->tx_buffer) + (fp->unit_sz * i));
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ writel(0, &fp->rx_desc[i].desc_a);
+ writel(0, &fp->rx_desc[i].desc_b);
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ writel(0, &fp->tx_desc[i].desc_a);
+ writel(0, &fp->tx_desc[i].desc_b);
+ }
+
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static int fepci_stream_start(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned i;
+ down_write(&fp->this_card_priv->semaphore);
+
+ if (fp->in_stream_mode == 0) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Interface is not in stream mode, "
+ "streaming cannot be started.\n", fepci_NAME);
+ return (-EBUSY);
+ }
+ if (fp->stream_on) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Streaming is already on, "
+ "streaming cannot be started.\n", fepci_NAME);
+ return (-EBUSY);
+ }
+
+ {
+ /* reserve irq */
+ int error = request_irq(dev->irq, &fepci_stream_interrupt,
+ IRQF_SHARED, dev->name, dev);
+ if (error) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate IRQ %d, error 0x%x\n",
+ fepci_NAME, dev->irq, error);
+ return -ENOMEM;
+ }
+ }
+
+ fp->stream_on = 1;
+
+ /* sending &receiving on, start from the beginning of the buffer */
+ fp->cur_tx_unit = 0;
+ fp->cur_rx_unit = 0;
+ fp->cur_tx = 0;
+ fp->cur_rx = 0;
+
+ /* all the descriptors ready to go: */
+ for (i = 0; i < min(RX_RING_SIZE, TX_RING_SIZE); i++) {
+ dma_addr_t address = pci_map_single(fp->this_card_priv->pci_dev,
+ fp->
+ rx_unit[(fp->cur_rx_unit +
+ i) % fp->units],
+ fp->unit_sz,
+ PCI_DMA_FROMDEVICE);
+ if (unlikely(pci_dma_mapping_error(address)))
+ printk(KERN_WARNING
+ "%s: failed to map DMA buffer.\n", fepci_NAME);
+ else {
+ writel(address,
+ &fp->rx_desc[(fp->cur_rx + i) &
+ (RX_RING_SIZE - 1)].desc_a);
+ if (!
+ (readl
+ (&fp->
+ rx_desc[(fp->cur_rx + i) & (RX_RING_SIZE -
+ 1)].
+ desc_b) & enable_transfer))
+ writel(enable_transfer,
+ &fp->rx_desc[(fp->cur_rx + i) %
+ RX_RING_SIZE].desc_b);
+ }
+ address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ fp->tx_unit[(fp->cur_tx_unit + i) %
+ fp->units], fp->unit_sz,
+ PCI_DMA_TODEVICE);
+ writel(address,
+ &fp->
+ tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE - 1)].desc_a);
+ if (unlikely(pci_dma_mapping_error(address)))
+ printk(KERN_WARNING
+ "%s: failed to map DMA buffer.\n", fepci_NAME);
+ else {
+ if (!
+ (readl
+ (&fp->
+ tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE -
+ 1)].
+ desc_b) & enable_transfer))
+ writel(enable_transfer |
+ (fp->unit_sz & frame_length),
+ &fp->
+ tx_desc[(fp->cur_tx +
+ i) & (TX_RING_SIZE -
+ 1)].desc_b);
+ }
+ }
+
+ /* irq on */
+ set_int_mask(fp->channel_number,
+ MaskFrameReceived | MaskFrameTransmitted |
+ MaskRxFifoError | MaskRxFrameDroppedError |
+ MaskTxFifoError, fp->this_card_priv);
+ {
+ void *ioaddr = (void *)dev->base_addr;
+ /* Start Rx and Tx channels */
+ writel(Receive_enable |
+ (Rx_fifo_threshold & RX_FIFO_THRESHOLD_STREAM_MODE),
+ (void *)(ioaddr + fp->reg_rxctrl));
+ writel((Transmit_enable |
+ (Tx_desc_threshold &
+ (TX_DESC_THRESHOLD_STREAM_MODE << 8)) |
+ (Tx_fifo_threshold & TX_FIFO_THRESHOLD_STREAM_MODE)),
+ (void *)(ioaddr + fp->reg_txctrl));
+ }
+ up_write(&fp->this_card_priv->semaphore);
+
+ return 0;
+}
+
+static int fepci_stream_stop(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ void *ioaddr = (void *)dev->base_addr;
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_stream_mode == 0) {
+ up_write(&fp->this_card_priv->semaphore);
+ return (1);
+ }
+ fp->stream_on = 0;
+ /* Stop Rx and Tx channels. */
+ writel(0x0, (void *)(ioaddr + fp->reg_rxctrl));
+ writel(0x0, (void *)(ioaddr + fp->reg_txctrl));
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
+
+ /* unregister irq */
+ free_irq(dev->irq, dev);
+
+ {
+ unsigned i = min(RX_RING_SIZE, TX_RING_SIZE) - 1;
+ do {
+ dma_addr_t bus_address = readl(&fp->rx_desc[i].desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz,
+ PCI_DMA_FROMDEVICE);
+ bus_address = readl(&fp->tx_desc[i].desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz, PCI_DMA_TODEVICE);
+ }
+ while (i--);
+ }
+
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static int fepci_stream_close(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned rx_pages, tx_pages, rx_order, tx_order;
+
+ if (fepci_stream_stop(dev))
+ return -ENODEV;
+ down_write(&fp->this_card_priv->semaphore);
+ if (!(fp->in_stream_mode)) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -ENODEV;
+ }
+ /* release memory */
+ if (fp->bufsize_order < PAGE_SHIFT) {
+ rx_order = 0;
+ tx_order = 0;
+ rx_pages = 1;
+ tx_pages = 1;
+ } else {
+ rx_order = (int)((fp->bufsize_order) - PAGE_SHIFT + 1);
+ rx_pages = 1 << rx_order;
+ tx_order = (int)((fp->bufsize_order) - PAGE_SHIFT);
+ tx_pages = 1 << tx_order;
+ }
+ if (fp->rx_buffer) {
+ unsigned page_number;
+ for (page_number = 0; page_number < rx_pages; page_number++)
+ /* turn pages back to non-reserved */
+ ClearPageReserved(virt_to_page
+ ((unsigned long)fp->rx_buffer +
+ (page_number << PAGE_SHIFT)));
+ free_pages((unsigned long)fp->rx_buffer, rx_order);
+ fp->rx_buffer = NULL;
+ }
+ if (fp->tx_buffer) {
+ unsigned page_number;
+ for (page_number = 0; page_number < tx_pages; page_number++)
+ /* turn pages back to non-reserved */
+ ClearPageReserved(virt_to_page
+ ((unsigned long)fp->tx_buffer +
+ (page_number << PAGE_SHIFT)));
+ free_pages((unsigned long)fp->tx_buffer, tx_order);
+ fp->tx_buffer = NULL;
+ }
+
+ fp->in_stream_mode = 0;
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ struct fepci_ch_private *fp = dev->priv;
+ void *ioaddr = (void *)dev->base_addr;
+ unsigned intr_status = get_int_status(fp->channel_number, ioaddr);
+ bool fifo, dropped;
+ unsigned int temp_rx;
+ unsigned int temp_rx_unit;
+ unsigned int temp_tx;
+ unsigned int temp_tx_unit;
+
+ clear_int(fp->channel_number, intr_status, ioaddr);
+
+ /* debugging */
+ fp->interrupts_stream++;
+ fifo = (intr_status & IntrRxFifoError) != 0;
+ dropped = (intr_status & IntrRxFrameDroppedError) != 0;
+ fp->int_err_table_stream[fifo][dropped]++;
+ if (intr_status & IntrFrameReceived)
+ fp->rx_interrupts_stream++;
+ if (intr_status & IntrFrameTransmitted)
+ fp->tx_interrupts_stream++;
+ if (fifo)
+ fp->rx_int_fifo_err_stream++;
+ if (dropped)
+ fp->rx_int_frame_dropped_err_stream++;
+ if (intr_status & IntrTxFifoError)
+ fp->tx_int_fifo_err_stream++;
+ /* first update cur_rx, and do stuff if it has moved
+ (+ packets have been received) */
+ {
+ temp_rx = fp->cur_rx;
+ /* has been received */
+ while ((readl(&fp->rx_desc[fp->cur_rx].desc_b) &
+ transfer_not_done) == 0
+ /* stop if made one round */
+ && temp_rx != ((fp->cur_rx + 1) & (RX_RING_SIZE - 1))) {
+ dma_addr_t bus_address = readl(&fp->rx_desc[fp->cur_rx].
+ desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz,
+ PCI_DMA_FROMDEVICE);
+ fp->cur_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
+ fp->cur_rx_unit = (fp->cur_rx_unit + 1);
+ fp->cur_rx_unit *= fp->cur_rx_unit < fp->units;
+
+ *USER_RX_S_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) = fp->cur_rx_unit;
+ *USER_RX_S_FAKE_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) =
+ fp->cur_rx_unit * fp->unit_sz / fp->fake_unit_sz;
+ wake_up_interruptible(&(fp->this_card_priv->
+ stream_receive_q));
+ wake_up_interruptible(&(fp->this_card_priv->
+ stream_both_q));
+ }
+ }
+ /* from the first uninitialized descriptor to cur_rx */
+ temp_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
+ temp_rx_unit = (fp->cur_rx_unit + 1);
+ temp_rx_unit *= temp_rx_unit < fp->units;
+
+ while (temp_rx != fp->cur_rx) {
+ unsigned desc_b = readl(&fp->rx_desc[temp_rx].desc_b);
+ if ((desc_b & transfer_not_done) == 0) {
+ bool fifo = (desc_b & fifo_error) != 0;
+ bool size = (desc_b & size_error) != 0;
+ bool crc = (desc_b & crc_error) != 0;
+ bool octet = (desc_b & octet_error) != 0;
+ bool line = (desc_b & line_error) != 0;
+ unsigned length = desc_b & frame_length;
+ dma_addr_t bus_address;
+ /* update debug counters */
+ fp->rx_desc_err_table_stream[fifo]
+ [size]
+ [crc]
+ [octet]
+ [line]++;
+ if (length == 0)
+ fp->rx_packets_of_size_0_stream++;
+ else if (length == 1)
+ fp->rx_packets_of_size_1_stream++;
+ else if (length == 2)
+ fp->rx_packets_of_size_2_stream++;
+ else if (length == 3)
+ fp->rx_packets_of_size_3_stream++;
+ else if (length < 8)
+ fp->rx_packets_of_size_4_7_stream++;
+ else if (length < 16)
+ fp->rx_packets_of_size_8_15_stream++;
+ else if (length < 32)
+ fp->rx_packets_of_size_16_31_stream++;
+ if (fifo)
+ fp->rx_desc_fifo_err_stream++;
+ else if (size)
+ fp->rx_desc_size_err_stream++;
+ else if (crc)
+ fp->rx_desc_crc_err_stream++;
+ else if (octet)
+ fp->rx_desc_octet_err_stream++;
+ else if (line)
+ fp->rx_desc_line_err_stream++;
+ /* initialize the descriptor for transfer */
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ fp->rx_unit[temp_rx_unit],
+ fp->unit_sz, PCI_DMA_FROMDEVICE);
+ if (likely(!pci_dma_mapping_error(bus_address))) {
+ writel(bus_address,
+ &fp->rx_desc[temp_rx].desc_a);
+ writel(enable_transfer,
+ &fp->rx_desc[temp_rx].desc_b);
+ } else
+ printk(KERN_WARNING
+ "%s: failed to map DMA for reception.\n",
+ fepci_NAME);
+ }
+ temp_rx = (temp_rx + 1) & (RX_RING_SIZE - 1);
+ temp_rx_unit = (temp_rx_unit + 1);
+ temp_rx_unit *= temp_rx_unit < fp->units;
+ }
+
+ /* first update cur_tx, and do stuff if it has moved
+ (+ packets have been transmitted) */
+ {
+ temp_tx = fp->cur_tx;
+ /* has been transmitted */
+ while ((readl(&fp->tx_desc[fp->cur_tx].desc_b) &
+ transfer_not_done) == 0
+ /* stop if made one round */
+ && temp_tx != ((fp->cur_tx + 1) & (TX_RING_SIZE - 1))) {
+ dma_addr_t bus_address = readl(&fp->tx_desc[fp->cur_tx].
+ desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz, PCI_DMA_TODEVICE);
+ fp->cur_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
+ fp->cur_tx_unit = (fp->cur_tx_unit + 1);
+ fp->cur_tx_unit *= fp->cur_tx_unit < fp->units;
+
+ *USER_TX_S_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) = fp->cur_tx_unit;
+ *USER_TX_S_FAKE_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) =
+ fp->cur_tx_unit * fp->unit_sz / fp->fake_unit_sz;
+ wake_up_interruptible(&(fp->this_card_priv->
+ stream_transmit_q));
+ wake_up_interruptible(&(fp->this_card_priv->
+ stream_both_q));
+ }
+ }
+ /* from the first uninitialized descriptor to cur_tx */
+ temp_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
+ temp_tx_unit = (fp->cur_tx_unit + 1);
+ temp_tx_unit *= temp_tx_unit < fp->units;
+
+ while (temp_tx != fp->cur_tx) {
+ unsigned desc_b = readl(&fp->tx_desc[temp_tx].desc_b);
+ if ((desc_b & transfer_not_done) == 0) {
+ dma_addr_t bus_address;
+ /* update debug counters */
+ if (desc_b & fifo_error)
+ fp->tx_desc_fifo_err_stream++;
+ /* initialize the desctiptor for transfer */
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ fp->tx_unit[temp_tx_unit],
+ fp->unit_sz, PCI_DMA_TODEVICE);
+ writel(bus_address, &fp->tx_desc[temp_tx].desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ writel(enable_transfer |
+ (fp->unit_sz & frame_length),
+ &fp->tx_desc[temp_tx].desc_b);
+ else
+ printk(KERN_WARNING
+ "%s: failed to map tranmission DMA.\n",
+ fepci_NAME);
+ }
+ temp_tx = (temp_tx + 1) & (TX_RING_SIZE - 1);
+ temp_tx_unit = (temp_tx_unit + 1);
+ temp_tx_unit *= temp_tx_unit < fp->units;
+ }
+
+ return intr_status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/* stream operations end */
+
+int fepci_rebuild_header(struct sk_buff *skb)
+{
+ DBG_PRINT("fepci_rebuild_header\n");
+ return 0;
+}
+
+static inline u16 get_common_reg_word(void *ioaddr, unsigned long offsett)
+{
+ u16 word;
+ DBG_PRINT("get_common_reg_word ioaddr %p, offsett %lu\n", ioaddr,
+ offsett);
+ __clear_bit(0, &offsett);
+ DBG_PRINT("get_common_reg_word %p\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1));
+ word = le16_to_cpu(readw
+ (ioaddr + FEPCI_IDENTIFICATION_OFFSETT +
+ (offsett << 1)));
+ DBG_PRINT("get_common_reg_word %p: %hu\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1), word);
+ return word;
+}
+
+static irqreturn_t alarm_manager_interrupt(int irq, void *pointer)
+{
+ struct fepci_card_private *card_private = pointer;
+ void *ioaddr = card_private->ioaddr;
+ /* check int status */
+ if (readl((void *)(ioaddr + reg_custom)) & AM_interrupt_status) {
+ /* clear int (zero everything, but the mask bit) */
+ writel(readl((void *)(ioaddr + reg_custom)) &
+ AM_interrupt_mask, (void *)(ioaddr + reg_custom));
+ /* wake queue */
+ wake_up(&(card_private->alarm_manager_wait_q));
+ }
+
+ return IRQ_HANDLED;
+}
+
+#ifdef FEPCI_POINT_TO_POINT
+static int is_ptp_interface(struct net_device *dev)
+{
+ char **p_ptp_if_name = retina_ptp_interfaces;
+ unsigned int i = interfaces;
+ while (i > 0 && *p_ptp_if_name != NULL) {
+ if (!strncmp(dev->name, *p_ptp_if_name, sizeof(dev->name)))
+ return 1;
+ p_ptp_if_name++;
+ i--;
+ }
+ return 0;
+}
+#endif /* FEPCI_POINT_TO_POINT */
+
+static int __devinit fepci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev = NULL;
+ struct fepci_ch_private *fp = NULL;
+ int chip_idx = ent->driver_data;
+ int drv_flags = pci_id_tbl[chip_idx].drv_flags;
+ int i;
+ unsigned j;
+ resource_size_t real_ioaddr;
+ void *ioaddr;
+ unsigned position;
+
+ i = pci_enable_device(pdev);
+ if (i) {
+ printk(KERN_WARNING "%s: pci_enable_device returned %x.\n",
+ fepci_NAME, i);
+ return i;
+ }
+
+ pci_set_master(pdev);
+
+ i = pci_request_regions(pdev, (char *)fepci_name);
+ if (i) {
+ printk(KERN_WARNING
+ "%s: pci_request_regions returned %x.\n", fepci_NAME, i);
+ pci_disable_device(pdev);
+ return i;
+ }
+
+ /* make sure above region is MMIO */
+ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+ printk(KERN_WARNING "%s: region not MMIO region\n", fepci_NAME);
+ goto ERR_1;
+ }
+
+ j = pci_resource_len(pdev, 0);
+ if (j < FEPCI_SIZE) {
+ printk(KERN_WARNING
+ "%s: resource length %u less than required %u.\n",
+ fepci_NAME, j, FEPCI_SIZE);
+ goto ERR_1;
+ }
+
+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+ printk(KERN_WARNING "%s: no suitable DMA available.\n",
+ fepci_NAME);
+ goto ERR_1;
+ }
+
+ real_ioaddr = pci_resource_start(pdev, 0);
+ DBG_PRINT("pci_resource_start %lu.\n", real_ioaddr);
+ ioaddr = ioremap_nocache(real_ioaddr, FEPCI_SIZE);
+ DBG_PRINT("ioremap_nocache = %p.\n", ioaddr);
+ if (!ioaddr) {
+ printk(KERN_WARNING "%s: remapping failed.\n", fepci_NAME);
+ goto ERR_1_5;
+ }
+ position = 0;
+ for (; position < MAX_DEVICES; position++) {
+ down_write(&card_privates[position].semaphore);
+ if (card_privates[position].pci_dev == NULL) {
+ card_privates[position].pci_dev = pdev;
+ if (position == find_cnt)
+ find_cnt++;
+ goto FOUND;
+ }
+ up_write(&card_privates[position].semaphore);
+ }
+ printk(KERN_WARNING
+ "%s: no space to inialize device #%u.\n",
+ fepci_NAME, MAX_DEVICES + 1);
+ goto ERR_2;
+FOUND:
+ card_privates[position].card_number = position;
+ card_privates[position].ioaddr = ioaddr;
+ card_privates[position].pci_dev = pdev;
+ DBG_PRINT("fillregisterswith_00 %p.\n", ioaddr);
+ fillregisterswith_00(ioaddr);
+
+ fepci_proc_init_card(position, (void *)&(card_privates[position]));
+
+ init_waitqueue_head(&(card_privates[position].alarm_manager_wait_q));
+ init_waitqueue_head(&(card_privates[position].stream_transmit_q));
+ init_waitqueue_head(&(card_privates[position].stream_receive_q));
+ init_waitqueue_head(&(card_privates[position].stream_both_q));
+
+ init_timer(&card_privates[position].mailbox_timer);
+
+ DBG_PRINT("request_irq %d, %s.\n", pdev->irq, fepci_alarm_manager_name);
+ i = request_irq(pdev->irq, &alarm_manager_interrupt,
+ IRQF_SHARED,
+ fepci_alarm_manager_name, &card_privates[position]);
+ if (i) {
+ up_write(&card_privates[position].semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate IRQ %d alarm manager: 0x%x\n",
+ fepci_NAME, pdev->irq, i);
+ goto ERR_2;
+ }
+ DBG_PRINT("alarm manager int on %p.\n", (void *)(ioaddr + reg_custom));
+ writel(AM_interrupt_mask, (void *)(ioaddr + reg_custom));
+ /* alarm manager int on */
+
+ for (j = 0; j < CHANNELS; j++) {
+ DBG_PRINT("alloc_etherdev %u.\n", j);
+ dev = alloc_etherdev(sizeof(struct fepci_ch_private));
+ if (!dev) {
+ printk(KERN_WARNING
+ "%s: cannot allocate ethernet device\n",
+ fepci_NAME);
+ continue;
+ }
+
+ fp = dev->priv;
+ fp->minor = position; /* * CHANNELS + j; */
+ /* name := xxx01..xxxnn */
+ memcpy(dev->name, fepci_netdev_name, 6);
+ /* dev->name[3]= j+'0'; channel number -> ascii */
+ /* minor number -> ascii */
+ dev->name[4] = ((fp->minor * CHANNELS + j) % 10) + '0';
+ /* minor number -> ascii */
+ dev->name[3] = ((fp->minor * CHANNELS + j) / 10) + '0';
+
+ SET_MODULE_OWNER(dev);
+ DBG_PRINT("clear_int %u, %x, %p.\n", j, IntrAllInts, ioaddr);
+ clear_int(j, IntrAllInts, ioaddr);
+ DBG_PRINT("ether_setup %p.\n", dev);
+ ether_setup(dev);
+
+ random_ether_addr(dev->dev_addr);
+ /* HW_ADDR is got using the mailbox: */
+ {
+ struct fepci_real_mailbox *real_mailbox =
+ (struct fepci_real_mailbox *)
+ (ioaddr + FEPCI_MAILBOX_OFFSETT);
+ unsigned long waituntil;
+
+ set_semafore(real_mailbox, 0x40);
+ writel(0x1 /*size */ +
+ (0x8 << 8) /* get mac command */ ,
+ &real_mailbox->Size_Command);
+ set_semafore(real_mailbox, 0x11);
+
+ waituntil = jiffies + HZ;
+ while (time_before(jiffies, waituntil) &&
+ get_semafore(real_mailbox) != 0x20) {
+ DBG_PRINT("jiffies %lu < waituntil %lu.\n",
+ jiffies, waituntil);
+ msleep(0);
+ }
+
+ /* 14.5.2004 JT: Made this safer. */
+ if (get_semafore(real_mailbox) == 0x20) {
+ dev->dev_addr[5] =
+ readb(&real_mailbox->Data[0 + 3 * j]);
+ dev->dev_addr[4] =
+ readb(((u8 *) & real_mailbox->
+ Data[0 + 3 * j]) + 1);
+ dev->dev_addr[3] =
+ readb(&real_mailbox->Data[1 + 3 * j]);
+ dev->dev_addr[2] =
+ readb(((u8 *) & real_mailbox->
+ Data[1 + 3 * j]) + 1);
+ dev->dev_addr[1] =
+ readb(&real_mailbox->Data[2 + 3 * j]);
+ dev->dev_addr[0] =
+ readb(((u8 *) & real_mailbox->
+ Data[2 + 3 * j]) + 1);
+ }
+
+ set_semafore(real_mailbox, 0x0);
+ }
+ dev->addr_len = 6;
+
+ dev->base_addr = (unsigned long)ioaddr;
+ dev->irq = pdev->irq;
+ DBG_PRINT("alarm pci_set_drvdata %p, %p.\n", pdev, dev);
+ if (j == 0)
+ pci_set_drvdata(pdev, dev);
+
+ fp->drv_flags = drv_flags;
+
+ fp->rx_desc =
+ (struct fepci_desc *)(dev->base_addr + first_rx_desc +
+ j * to_next_ch_rx_desc);
+ fp->tx_desc =
+ (struct fepci_desc *)(dev->base_addr + first_tx_desc +
+ j * to_next_ch_tx_desc);
+
+ fp->channel_number = j; /*channel in this device */
+ fp->this_dev = dev;
+
+ fp->this_card_priv = &card_privates[position];
+ fp->this_card_priv->ch_privates[j] = fp;
+
+ fp->cur_tx = 0;
+
+ fp->in_stream_mode = 0;
+ fp->in_eth_mode = 0;
+
+ fp->reg_rxctrl = reg_first_rxctrl + j * to_next_rxctrl;
+ fp->reg_txctrl = reg_first_txctrl + j * to_next_txctrl;
+
+ /* The FEPCI specific entries in the device structure */
+ dev->open = &fepci_open;
+ dev->hard_start_xmit = &fepci_start_xmit;
+ dev->stop = &fepci_close;
+ dev->get_stats = &fepci_get_stats;
+ dev->set_multicast_list = &set_rx_mode;
+ dev->do_ioctl = &netdev_ioctl;
+ dev->tx_timeout = fepci_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+#ifdef FEPCI_POINT_TO_POINT
+ if (is_ptp_interface(dev)) {
+ dev->flags |= (IFF_POINTOPOINT);
+ dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
+ if (retina_noarp_with_ptp) {
+ dev->rebuild_header = fepci_rebuild_header;
+ dev->flags |= (IFF_NOARP);
+ }
+ }
+#endif
+ DBG_PRINT("register_netdev %p.\n", dev);
+ i = register_netdev(dev);
+ if (i) {
+ printk(KERN_WARNING
+ "%s: register_netdev failed 0x%x.\n",
+ fepci_NAME, i);
+ continue;
+ }
+
+ printk("%s: %s type %x at %p, ",
+ dev->name, pci_id_tbl[chip_idx].name, 0x0, ioaddr);
+ for (i = 0; i < 5; i++)
+ printk("%2.2x:", dev->dev_addr[i]);
+ printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], pdev->irq);
+#ifdef DEBUG_PROC_FILES
+ fepci_proc_init_channel(position, j, fp);
+#endif /* DEBUG_PROC_FILES */
+ }
+ up_write(&card_privates[position].semaphore);
+ DBG_PRINT("fepci_init_one %u.\n", position);
+ return 0;
+
+ERR_2:
+ iounmap(ioaddr);
+ERR_1_5:
+ pci_set_drvdata(pdev, NULL);
+ERR_1:
+ pci_disable_device(pdev);
+ pci_release_regions(pdev);
+ return -ENODEV;
+}
+
+static int fepci_open(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+
+ down_write(&fp->this_card_priv->semaphore);
+
+ if (fp->this_card_priv->pci_dev == NULL) {
+ up_write(&fp->this_card_priv->semaphore);
+ fepci_close(dev);
+ return -ENXIO;
+ }
+
+ while (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ fepci_stream_close(dev);
+ down_write(&fp->this_card_priv->semaphore);
+ }
+
+ {
+ int i = request_irq(dev->irq, &fepci_interrupt,
+ IRQF_SHARED, dev->name, dev);
+ if (i) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate IRQ %d, error 0x%x",
+ fepci_NAME, dev->irq, i);
+ return i;
+ }
+ }
+
+ fp->in_eth_mode = 1;
+
+ fepci_init_ring(dev);
+ set_rx_mode(dev);
+
+ fp->cur_rx = 0;
+ fp->cur_tx = 0;
+
+ netif_carrier_off(dev);
+
+ /* Enable interrupts by setting the interrupt mask. */
+
+ set_int_mask(fp->channel_number,
+ MaskFrameReceived | MaskFrameTransmitted |
+ MaskRxFifoError | MaskRxFrameDroppedError |
+ MaskTxFifoError, fp->this_card_priv);
+
+ {
+ void *ioaddr = (void *)dev->base_addr;
+
+ /* Start Rx and Tx channels. */
+ writel(Receive_enable |
+ (Rx_fifo_threshold & RX_FIFO_THRESHOLD_PACKET_MODE),
+ (void *)(ioaddr + fp->reg_rxctrl));
+
+ writel((Transmit_enable |
+ (Tx_desc_threshold &
+ (TX_DESC_THRESHOLD_PACKET_MODE << 8)) |
+ (Tx_fifo_threshold & TX_FIFO_THRESHOLD_PACKET_MODE)),
+ (void *)(ioaddr + fp->reg_txctrl));
+ }
+
+ netif_start_queue(dev);
+
+ /* Set timer */
+ init_timer(&fp->timer);
+ fp->timer.expires = jiffies + HZ;
+ fp->timer.data = (unsigned long)dev;
+ fp->timer.function = &fepci_timer; /* timer handler */
+ add_timer(&fp->timer);
+
+ up_write(&fp->this_card_priv->semaphore);
+
+ return 0;
+}
+
+static void fepci_timer(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct fepci_ch_private *fp = dev->priv;
+
+ /* just to make it absolutely sure the sending starts again
+ * if the system jams: free already sent skbuffs */
+
+ if (netif_tx_trylock(dev)) {
+ if (!(fp->tx_interrupts_since_last_timer)) {
+ unsigned i = TX_RING_SIZE - 1;
+ do {
+ unsigned desc_b;
+ if ((fp->tx_skbuff[i] != NULL)
+ &&
+ (((desc_b =
+ readl(&fp->tx_desc[i].
+ desc_b)) & transfer_not_done) ==
+ 0)) {
+ /* has been sent */
+ pci_unmap_single(fp->
+ this_card_priv->
+ pci_dev,
+ readl(&fp->
+ tx_desc[i].
+ desc_a),
+ desc_b &
+ frame_length,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb(fp->tx_skbuff[i]);
+ fp->tx_skbuffs_out++;
+
+ fp->tx_skbuff[i] = NULL;
+
+ if (desc_b & fifo_error) {
+ fp->stats.tx_fifo_errors++;
+ fp->tx_desc_fifo_err++;
+ } else
+ fp->stats.tx_packets++;
+ }
+ }
+ while (i--);
+ }
+ /* if the next descriptor is free, continue taking new ones */
+ if (!
+ (readl(&fp->tx_desc[fp->cur_tx].desc_b) &
+ transfer_not_done))
+ netif_wake_queue(dev);
+ netif_tx_unlock(dev);
+ }
+
+ fp->tx_interrupts_since_last_timer = 0;
+
+ if ((get_common_reg_word(fp->this_card_priv->ioaddr, 0x72) >> fp->
+ channel_number) & 1) {
+ netif_carrier_off(dev);
+ } else {
+ netif_carrier_on(dev);
+ }
+
+ if (fp->in_eth_mode)
+ mod_timer(&fp->timer, jiffies + 5 * HZ);
+}
+
+static void fepci_tx_timeout(struct net_device *dev)
+{
+ DBG_PRINT("%s: transmit timed out!\n", dev->name);
+}
+
+/* Initialize the rx and tx rings */
+static void fepci_init_ring(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned i;
+
+ fp->rx_buf_sz = 2000;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb =
+ __dev_alloc_skb(fp->rx_buf_sz, GFP_KERNEL);
+
+ if (unlikely(skb == NULL)) {
+ZERO:
+ writel(0, &fp->rx_desc[i].desc_a);
+ writel(0, &fp->rx_desc[i].desc_b);
+ continue;
+ } else {
+ dma_addr_t bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ skb->data, fp->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+ if (likely(!pci_dma_mapping_error(bus_address))) {
+ fp->rx_skbuffs_in++;
+ /* Mark as being used by this device */
+ skb->dev = dev;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ fp->rx_skbuff[i] = skb;
+ writel(bus_address, &fp->rx_desc[i].desc_a);
+ writel(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else {
+ dev_kfree_skb(skb);
+ goto ZERO;
+ }
+ }
+ }
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ fp->tx_skbuff[i] = NULL;
+ writel(0, &fp->tx_desc[i].desc_a); /* no skbuff */
+ /* no transfer enable, no int enable */
+ writel(0, &fp->tx_desc[i].desc_b);
+ }
+
+ return;
+}
+
+static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ const unsigned cur_tx = fp->cur_tx;
+ unsigned next;
+ unsigned tx_length = skb->len;
+ dma_addr_t bus_address;
+ struct sk_buff *old;
+
+ fp->tx_skbuffs_in++;
+
+ if (unlikely(tx_length < ETH_ZLEN)) {
+ struct sk_buff *bigger =
+ skb_copy_expand(skb, 0, ETH_ZLEN - tx_length,
+ GFP_ATOMIC);
+ if (unlikely(!bigger))
+ return NET_XMIT_CN;
+ tx_length = ETH_ZLEN;
+ old = skb;
+ skb = bigger;
+ } else
+ old = NULL;
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev, skb->data,
+ tx_length, PCI_DMA_TODEVICE);
+ if (likely(!pci_dma_mapping_error(bus_address))) {
+ struct fepci_desc *descriptor;
+ if (old)
+ dev_kfree_skb(old);
+ descriptor = &fp->tx_desc[cur_tx];
+ writel(bus_address, &descriptor->desc_a);
+ writel((tx_length & frame_length) | enable_transfer,
+ &descriptor->desc_b);
+ } else {
+ if (old)
+ dev_kfree_skb(skb);
+ return NET_XMIT_CN;
+ }
+ fp->stats.tx_bytes += tx_length;
+
+ fp->tx_skbuff[cur_tx] = skb;
+
+ /* Calculate the next Tx descriptor entry */
+ next = (cur_tx + 1) & (TX_RING_SIZE - 1);
+ fp->cur_tx = next;
+ /* if the next descriptor is busy, discontinue taking new ones */
+ if (fp->tx_skbuff[next] != NULL)
+ netif_stop_queue(dev);
+ dev->trans_start = jiffies;
+
+ return NET_XMIT_SUCCESS;
+}
+
+static irqreturn_t fepci_interrupt(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ void *ioaddr = (void *)dev->base_addr;
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned intr_status = get_int_status(fp->channel_number, ioaddr);
+ bool RxFifoError;
+ bool RxFrameDroppedError;
+
+ clear_int(fp->channel_number, intr_status, ioaddr);
+
+ RxFifoError = (intr_status & IntrRxFifoError) != 0;
+ RxFrameDroppedError = (intr_status & IntrRxFrameDroppedError) != 0;
+ /* first update interrupt error table */
+ fp->int_err_table[RxFifoError][RxFrameDroppedError]++;
+
+ fp->interrupts++;
+
+ if (intr_status & IntrFrameReceived) {
+ fepci_rx(dev);
+ fp->rx_interrupts++;
+ }
+ if (intr_status & IntrFrameTransmitted) {
+ fp->tx_interrupts_since_last_timer++;
+ if (netif_tx_trylock(dev)) {
+ unsigned i = TX_RING_SIZE - 1;
+ unsigned next;
+ do {
+ unsigned desc_b;
+ if ((fp->tx_skbuff[i] != NULL)
+ &&
+ (((desc_b =
+ readl(&fp->tx_desc[i].
+ desc_b)) & transfer_not_done) ==
+ 0)) {
+ /* has been sent */
+ pci_unmap_single(fp->
+ this_card_priv->
+ pci_dev,
+ readl(&fp->
+ tx_desc[i].
+ desc_a),
+ desc_b &
+ frame_length,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb_irq(fp->tx_skbuff[i]);
+ fp->tx_skbuffs_out++;
+ fp->tx_skbuff[i] = NULL;
+ if (desc_b & fifo_error) {
+ fp->stats.tx_fifo_errors++;
+ fp->tx_desc_fifo_err++;
+ } else
+ fp->stats.tx_packets++;
+ }
+ }
+ while (i--);
+ next = fp->cur_tx;
+ /* if next tx descriptor is free,
+ * continue taking new ones */
+ if (!(readl(&fp->tx_desc[next].desc_b) &
+ transfer_not_done))
+ netif_wake_queue(dev);
+ netif_tx_unlock(dev);
+ }
+ fp->tx_interrupts++;
+ }
+ if (RxFifoError) {
+ fp->rx_int_fifo_err++;
+ fepci_rx(dev);
+ }
+ if (RxFrameDroppedError) {
+ fp->rx_int_frame_dropped_err++;
+ fepci_rx(dev);
+ }
+ if (intr_status & IntrTxFifoError)
+ fp->tx_int_fifo_err++;
+ return intr_status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int fepci_rx(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+
+ unsigned int i, old_cur_rx = fp->cur_rx;
+ for (i = old_cur_rx;
+ i != ((old_cur_rx + RX_RING_SIZE - 1) & (RX_RING_SIZE - 1));
+ i = (i + 1) & (RX_RING_SIZE - 1)) {
+ unsigned desc_b;
+ struct sk_buff *skb;
+ /* transfer done */
+ bool condition = (skb = fp->rx_skbuff[i]) &&
+ ((desc_b =
+ readl(&fp->rx_desc[i].desc_b)) & transfer_not_done) == 0;
+ if (condition) {
+ bool fifo = (desc_b & fifo_error) != 0;
+ bool size = (desc_b & size_error) != 0;
+ bool crc = (desc_b & crc_error) != 0;
+ bool octet = (desc_b & octet_error) != 0;
+ bool line = (desc_b & line_error) != 0;
+ unsigned length = desc_b & frame_length;
+ pci_unmap_single(fp->this_card_priv->pci_dev,
+ readl(&fp->rx_desc[i].desc_a),
+ fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ fp->cur_rx = (i + 1) & (RX_RING_SIZE - 1);
+ /* first update error table */
+ fp->rx_desc_err_table[fifo]
+ [size]
+ [crc]
+ [octet]
+ [line]++;
+ /* small packet counters */
+ if (length == 0)
+ fp->rx_packets_of_size_0++;
+ else if (length == 1)
+ fp->rx_packets_of_size_1++;
+ else if (length == 2)
+ fp->rx_packets_of_size_2++;
+ else if (length == 3)
+ fp->rx_packets_of_size_3++;
+ else if (length < 8)
+ fp->rx_packets_of_size_4_7++;
+ else if (length < 16)
+ fp->rx_packets_of_size_8_15++;
+ else if (length < 32)
+ fp->rx_packets_of_size_16_31++;
+
+ if (fifo) {
+ fp->stats.rx_errors++;
+ fp->stats.rx_frame_errors++;
+ fp->rx_desc_fifo_err++;
+ writel(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (size) {
+ fp->stats.rx_errors++;
+ fp->stats.rx_over_errors++;
+ fp->rx_desc_size_err++;
+ writel(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (crc) {
+ fp->stats.rx_errors++;
+ fp->stats.rx_crc_errors++;
+ fp->rx_desc_crc_err++;
+ writel(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (octet) {
+ fp->rx_desc_octet_err++;
+ writel(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (line) {
+ fp->rx_desc_line_err++;
+ writel(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else {
+ skb_put(skb, length - 4);
+
+ skb->protocol = eth_type_trans(skb, dev);
+#ifdef FEPCI_POINT_TO_POINT
+ if (dev->flags & IFF_POINTOPOINT) {
+ /* everything received is for us. */
+
+ if (dev->flags & IFF_NOARP) {
+ /* NOARP applied ->
+ * destination MAC addresses
+ * are bogus */
+ if (skb->
+ pkt_type ==
+ PACKET_OTHERHOST)
+ skb->
+ pkt_type =
+ PACKET_HOST;
+ } else {
+ /* NOARP not applied ->
+ * destination MAC addresses are
+ * broadcast */
+ if (skb->
+ pkt_type ==
+ PACKET_BROADCAST)
+ skb->
+ pkt_type =
+ PACKET_HOST;
+
+ } /* IFF_NOARP */
+ } /* IFF_POINTOPOINT */
+#endif
+ skb_reset_mac_header(skb);
+ netif_rx(skb);
+ fp->rx_skbuffs_out++;
+ /* statistics -4==crc */
+ fp->stats.rx_bytes += length - 4;
+ fp->stats.rx_packets++;
+
+ fp->rx_skbuff[i] = NULL;
+ dev->last_rx = jiffies;
+ }
+ }
+ /* reserve a new one */
+ if (fp->rx_skbuff[i] == NULL) {
+ struct sk_buff *skb = dev_alloc_skb(fp->rx_buf_sz);
+
+ if (skb == NULL)
+ continue; /* Better luck next round. */
+ else {
+ dma_addr_t address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ skb->data,
+ fp->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+ if (likely(!pci_dma_mapping_error(address))) {
+ struct fepci_desc *descriptor;
+ fp->rx_skbuffs_in++;
+ fp->rx_skbuff[i] = skb;
+ /* Mark as being used by this device. */
+ skb->dev = dev;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ descriptor = &fp->rx_desc[i];
+ writel(address, &descriptor->desc_a);
+ writel(enable_transfer,
+ &descriptor->desc_b);
+ } else {
+ dev_kfree_skb(skb);
+ printk(KERN_WARNING
+ "%s: failed to map DMA.\n",
+ dev->name);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int fepci_close(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned i;
+ void *ioaddr = (void *)dev->base_addr;
+
+ down_write(&fp->this_card_priv->semaphore);
+
+ netif_stop_queue(dev);
+
+ /* Disable interrupts by clearing the interrupt mask */
+ set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
+
+ /* Stop the Tx and Rx processes */
+ writel(0x0, ioaddr + fp->reg_rxctrl);
+ writel(0x0, ioaddr + fp->reg_txctrl);
+ fp->in_eth_mode = 0;
+
+ del_timer_sync(&fp->timer);
+
+ free_irq(dev->irq, dev);
+
+ /* Free all the rx skbuffs */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (fp->rx_skbuff[i] != NULL) {
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev,
+ readl(&fp->rx_desc[i].
+ desc_a),
+ fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(fp->rx_skbuff[i]);
+ fp->rx_skbuffs_out++;
+ fp->rx_skbuff[i] = NULL;
+ }
+ }
+ /* and tx */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (fp->tx_skbuff[i] != NULL) {
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev,
+ readl(&fp->tx_desc[i].
+ desc_a),
+ readl(&fp->tx_desc[i].
+ desc_b) & frame_length,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb(fp->tx_skbuff[i]);
+ fp->tx_skbuffs_out++;
+ fp->tx_skbuff[i] = NULL;
+ }
+ }
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static struct net_device_stats *fepci_get_stats(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ return &fp->stats;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+ DBG_PRINT("set_rx_mode\n");
+}
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ char data = ((unsigned)rq->ifr_data) & 0xff;
+ int ret = 0;
+
+ DBG_PRINT("%s: netdev_ioctl called (command_nmbr:0x%x).\n",
+ dev->name, cmd);
+
+ switch (cmd) {
+ case FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE:
+ DBG_PRINT
+ (" ioctl stream bufsize commanded. (bufsize:0x%x)\n", data);
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -EBUSY;
+ }
+ fp->bufsize_order = data;
+ up_write(&fp->this_card_priv->semaphore);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_UNITSIZE:
+ DBG_PRINT
+ (" ioctl stream unitsize commanded. (unitsize:0x%x)\n",
+ data);
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -EBUSY;
+ }
+ fp->fake_unit_sz_order = data;
+ up_write(&fp->this_card_priv->semaphore);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_OPEN:
+ DBG_PRINT(" ioctl stream open commanded.\n");
+ ret = fepci_stream_open(dev);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_START:
+ DBG_PRINT(" ioctl stream start commanded.\n");
+ ret = fepci_stream_start(dev);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_CLOSE:
+ DBG_PRINT(" ioctl stream close commanded.\n");
+ ret = fepci_stream_close(dev);
+ break;
+ default:
+ DBG_PRINT(" unknown ioctl command 0x%x.\n", cmd);
+ return -ENOTTY;
+ }
+ return ret;
+}
+
+static void fepci_remove_one(struct pci_dev *pdev)
+{
+ /* from first dev, same in all */
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct fepci_ch_private *fp = dev->priv;
+ struct fepci_card_private *cardp = fp->this_card_priv;
+ unsigned int i;
+
+ writel(0, (void *)(cardp->ioaddr + reg_custom));
+ /* alarm manager int off */
+
+ for (i = 0; i < CHANNELS; i++) {
+ dev = cardp->ch_privates[i]->this_dev;
+ fp = dev->priv;
+#ifdef DEBUG_PROC_FILES
+ fepci_proc_cleanup_channel(cardp->card_number,
+ fp->channel_number);
+#endif /* DEBUG_PROC_FILES */
+ unregister_netdev(dev);
+ fepci_stream_close(dev);
+ free_netdev(dev);
+ }
+
+ pdev = cardp->pci_dev;
+ free_irq(pdev->irq, (void *)cardp);
+
+ down_write(&cardp->semaphore);
+
+ fepci_proc_cleanup_card(cardp->card_number);
+
+ pci_set_drvdata(pdev, NULL);
+
+ if (cardp->card_number + 1 == find_cnt)
+ find_cnt--;
+ cardp->pci_dev = NULL;
+
+ iounmap(cardp->ioaddr);
+
+ up_write(&cardp->semaphore);
+
+ pci_disable_device(pdev);
+ pci_release_regions(pdev);
+}
+
+static struct pci_driver fepci_driver = {
+name: DRV_NAME,
+id_table: fepci_pci_tbl,
+probe: fepci_init_one,
+remove: fepci_remove_one,
+};
+
+static int __init fepci_init(void)
+{
+ unsigned card = MAX_DEVICES - 1;
+ do
+ init_rwsem(&card_privates[card].semaphore);
+ while (card--);
+ major = fepci_register_char_device();
+ if (major < 0)
+ return major;
+ stream_pointers =
+ (unsigned long)get_zeroed_page(GFP_KERNEL | __GFP_DMA);
+ DBG_PRINT(" %x.\n", stream_pointers);
+ if (stream_pointers == 0) {
+ fepci_unregister_char_device();
+ return -ENOMEM;
+ }
+ DBG_PRINT("SetPageReserved %u.\n", stream_pointers);
+ SetPageReserved(virt_to_page(stream_pointers));
+ DBG_PRINT("fepci_proc_init_driver.\n");
+ fepci_proc_init_driver();
+ DBG_PRINT("pci_register_driver %p.\n", &fepci_driver);
+ {
+ int ret = pci_register_driver(&fepci_driver);
+ if (ret) {
+ fepci_unregister_char_device();
+ ClearPageReserved(virt_to_page(stream_pointers));
+ free_page(stream_pointers);
+ DBG_PRINT
+ ("pci_register_driver %p failed with %d.\n",
+ &fepci_driver, ret);
+ fepci_proc_cleanup_driver();
+ return ret;
+ }
+ DBG_PRINT("fepci_init %d.\n", ret);
+ return ret;
+ }
+}
+
+static void __exit fepci_cleanup(void)
+{
+ DBG_PRINT("fepci_cleanup\n");
+ pci_unregister_driver(&fepci_driver);
+ fepci_proc_cleanup_driver();
+ fepci_unregister_char_device();
+ ClearPageReserved(virt_to_page(stream_pointers));
+ free_page(stream_pointers);
+}
+
+module_init(fepci_init);
+module_exit(fepci_cleanup);
+
+/* proc-filesystem specific stuff: */
+
+#ifdef DEBUG_PROC_FILES
+
+static char string_true[] = "<true> ";
+static char string_false[] = " - ";
+
+/* convert boolean value to more clarifying string format */
+static char *b(int b)
+{
+ if (b)
+ return string_true;
+ return string_false;
+}
+
+static int fepci_proc_read_descriptors(char *buf, char **start,
+ off_t offset, int len, int *eof,
+ void *data)
+{
+ int pos = 0;
+
+ int d; /* descriptor */
+
+ struct fepci_ch_private *fp;
+ struct fepci_card_private *cardp;
+ fp = (struct fepci_ch_private *)data;
+ cardp = (struct fepci_card_private *)fp->this_card_priv;
+
+ pos +=
+ sprintf(buf + pos,
+ "Rx descriptors of channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, "\n");
+
+ for (d = 0; d < RX_RING_SIZE; d++) {
+ pos += sprintf(buf + pos, " rx descriptor number %u : \n", d);
+ pos +=
+ sprintf(buf + pos, " desc_a value : 0x%8x \n",
+ readl(&fp->rx_desc[d].desc_a));
+ pos +=
+ sprintf(buf + pos, " desc_b value : 0x%8x \n",
+ readl(&fp->rx_desc[d].desc_b));
+ }
+
+ pos += sprintf(buf + pos, "\n");
+ pos +=
+ sprintf(buf + pos,
+ "Tx descriptors of channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, "\n");
+
+ for (d = 0; d < TX_RING_SIZE; d++) {
+ pos += sprintf(buf + pos, " tx descriptor number %u : \n", d);
+ pos +=
+ sprintf(buf + pos, " desc_a value : 0x%8x \n",
+ readl(&fp->tx_desc[d].desc_a));
+ pos +=
+ sprintf(buf + pos, " desc_b value : 0x%8x \n",
+ readl(&fp->tx_desc[d].desc_b));
+ }
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, "last line\n");
+
+ *eof = 1;
+ return pos;
+}
+
+static int fepci_proc_read_stream_counters(char *buf, char **start,
+ off_t offset, int len, int *eof,
+ void *data)
+{
+ int pos = 0;
+
+ int fifo;
+ int size;
+ int crc;
+ int octet;
+ int line;
+ int frame_dropped;
+ unsigned *rx_desc_err_table[2][2][2][2] = fp->rx_desc_err_table_stream;
+ struct fepci_ch_private *fp = (struct fepci_ch_private *)data;
+ struct fepci_card_private *cardp =
+ (struct fepci_card_private *)fp->this_card_priv;
+
+ pos +=
+ sprintf(buf + pos,
+ "Stream status for channel %u of card %u : \n",
+ fp->channel_number, cardp->card_number);
+ if (fp->in_stream_mode) {
+ pos += sprintf(buf + pos, " Channel is in stream mode \n");
+ if (fp->stream_on)
+ pos += sprintf(buf + pos, " Stream is on (started) \n");
+ else
+ pos +=
+ sprintf(buf + pos,
+ " Stream is off (not started)\n");
+ } else
+ pos += sprintf(buf + pos, " Channel is NOT in stream mode \n");
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos,
+ "Error counters for channel %u of card %u "
+ "(in stream mode) : \n",
+ fp->channel_number, cardp->card_number);
+
+ pos +=
+ sprintf(buf + pos, " rx_desc_fifo_err_stream : %10u \n",
+ fp->rx_desc_fifo_err_stream);
+
+ pos +=
+ sprintf(buf + pos, " rx_desc_size_err_stream : %10u \n",
+ fp->rx_desc_size_err_stream);
+ pos +=
+ sprintf(buf + pos, " rx_desc_crc_err_stream : %11u \n",
+ fp->rx_desc_crc_err_stream);
+ pos +=
+ sprintf(buf + pos, " rx_desc_octet_err_stream : %9u \n",
+ fp->rx_desc_octet_err_stream);
+ pos +=
+ sprintf(buf + pos, " rx_desc_line_err_stream : %10u \n",
+ fp->rx_desc_line_err_stream);
+ pos +=
+ sprintf(buf + pos, " tx_desc_fifo_err_stream : %10u \n",
+ fp->tx_desc_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos, " rx_int_fifo_err_stream : %11u \n",
+ fp->rx_int_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ " rx_int_frame_dropped_err_stream : %2u \n",
+ fp->rx_int_frame_dropped_err_stream);
+ pos +=
+ sprintf(buf + pos, " tx_int_fifo_err_stream : %11u \n",
+ fp->tx_int_fifo_err_stream);
+
+ pos += sprintf(buf + pos, "\n");
+ pos +=
+ sprintf(buf + pos,
+ "Other counters for channel %u of card %u "
+ "(in stream mode) : \n",
+ fp->channel_number, cardp->card_number);
+ pos +=
+ sprintf(buf + pos, " interrupts : %10u \n",
+ fp->interrupts_stream);
+ pos +=
+ sprintf(buf + pos, " rx_interrupts : %10u \n",
+ fp->rx_interrupts_stream);
+ pos +=
+ sprintf(buf + pos, " tx_interrupts : %10u \n",
+ fp->tx_interrupts_stream);
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos,
+ "Error counter tables for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, " rx_desc_error_table_stream:\n");
+ pos +=
+ sprintf(buf + pos,
+ " [fifo] [size] [crc] [octet] [line] : [count]\n");
+
+ for (fifo = 0; fifo < 2; fifo++)
+ for (size = 0; size < 2; size++)
+ for (crc = 0; crc < 2; crc++)
+ for (octet = 0; octet < 2; octet++)
+ for (line = 0; line < 2; line++)
+ pos +=
+ sprintf(buf + pos,
+ " %s %s %s %s %s "
+ ": %7u \n",
+ b(fifo),
+ b(size),
+ b(crc),
+ b(octet),
+ b(line),
+ rx_desc_err_table
+ [fifo][size]
+ [crc][octet]
+ [line]);
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, " int_error_table:\n");
+ pos += sprintf(buf + pos, " [rx_fifo] [frame_dropped] : [count]\n");
+ for (fifo = 0; fifo < 2; fifo++)
+ for (frame_dropped = 0; frame_dropped < 2; frame_dropped++)
+ pos +=
+ sprintf(buf + pos,
+ " %s %s : %7u\n", b(fifo),
+ b(frame_dropped),
+ fp->int_err_table_stream[fifo]
+ [frame_dropped]);
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos,
+ "Small packet counters for channel %u of card %u "
+ "(stream mode): \n",
+ fp->channel_number, cardp->card_number);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 0 : %10u \n",
+ fp->rx_packets_of_size_0_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 1 : %10u \n",
+ fp->rx_packets_of_size_1_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 2 : %10u \n",
+ fp->rx_packets_of_size_2_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 3 : %10u \n",
+ fp->rx_packets_of_size_3_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 4..7 : %10u \n",
+ fp->rx_packets_of_size_4_7_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 8..15 : %10u \n",
+ fp->rx_packets_of_size_8_15_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 16..31 : %10u \n",
+ fp->rx_packets_of_size_16_31_stream);
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, "last line\n");
+
+ *eof = 1;
+ return pos;
+}
+
+static int fepci_proc_read_counters(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+
+ int fifo;
+ int size;
+ int crc;
+ int octet;
+ int line;
+ int frame_dropped;
+
+ struct fepci_ch_private *fp;
+ struct fepci_card_private *cardp;
+
+ fp = (struct fepci_ch_private *)data;
+ cardp = (struct fepci_card_private *)fp->this_card_priv;
+
+ pos +=
+ sprintf(buf + pos,
+ "Error counters for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+
+ pos +=
+ sprintf(buf + pos, " rx_desc_fifo_err : %10u \n",
+ fp->rx_desc_fifo_err);
+ pos +=
+ sprintf(buf + pos, " rx_desc_size_err : %10u \n",
+ fp->rx_desc_size_err);
+ pos +=
+ sprintf(buf + pos, " rx_desc_crc_err : %11u \n",
+ fp->rx_desc_crc_err);
+ pos +=
+ sprintf(buf + pos, " rx_desc_octet_err : %9u \n",
+ fp->rx_desc_octet_err);
+ pos +=
+ sprintf(buf + pos, " rx_desc_line_err : %10u \n",
+ fp->rx_desc_line_err);
+ pos +=
+ sprintf(buf + pos, " tx_desc_fifo_err : %10u \n",
+ fp->tx_desc_fifo_err);
+ pos +=
+ sprintf(buf + pos, " rx_int_fifo_err : %11u \n",
+ fp->rx_int_fifo_err);
+ pos +=
+ sprintf(buf + pos, " rx_int_frame_dropped_err : %2u \n",
+ fp->rx_int_frame_dropped_err);
+ pos +=
+ sprintf(buf + pos, " tx_int_fifo_err : %11u \n",
+ fp->tx_int_fifo_err);
+
+ pos += sprintf(buf + pos, "\n");
+ pos +=
+ sprintf(buf + pos,
+ "Other counters for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, " interrupts : %10u \n", fp->interrupts);
+ pos +=
+ sprintf(buf + pos, " rx_interrupts : %10u \n", fp->rx_interrupts);
+ pos +=
+ sprintf(buf + pos, " tx_interrupts : %10u \n", fp->tx_interrupts);
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos, " rx_skbuffs_in : %10u \n", fp->rx_skbuffs_in);
+ pos +=
+ sprintf(buf + pos, " rx_skbuffs_out : %9u \n", fp->rx_skbuffs_out);
+ pos +=
+ sprintf(buf + pos, " difference : %13i \n",
+ fp->rx_skbuffs_in - fp->rx_skbuffs_out);
+ pos +=
+ sprintf(buf + pos, " tx_skbuffs_in : %10u \n", fp->tx_skbuffs_in);
+ pos +=
+ sprintf(buf + pos, " tx_skbuffs_out : %9u \n", fp->tx_skbuffs_out);
+ pos +=
+ sprintf(buf + pos, " difference : %13i \n",
+ fp->tx_skbuffs_in - fp->tx_skbuffs_out);
+
+ pos += sprintf(buf + pos, "\n");
+ pos +=
+ sprintf(buf + pos,
+ "Error counter tables for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, " rx_desc_error_table:\n");
+ pos +=
+ sprintf(buf + pos,
+ " [fifo] [size] [crc] [octet] [line] : [count]\n");
+
+ for (fifo = 0; fifo < 2; fifo++)
+ for (size = 0; size < 2; size++)
+ for (crc = 0; crc < 2; crc++)
+ for (octet = 0; octet < 2; octet++)
+ for (line = 0; line < 2; line++)
+ pos +=
+ sprintf(buf + pos,
+ " %s %s %s %s %s "
+ ": %7u \n",
+ b(fifo),
+ b(size),
+ b(crc),
+ b(octet),
+ b(line),
+ fp->
+ rx_desc_err_table
+ [fifo][size]
+ [crc][octet]
+ [line]);
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, " int_error_table:\n");
+ pos += sprintf(buf + pos, " [rx_fifo] [frame_dropped] : [count]\n");
+ for (fifo = 0; fifo < 2; fifo++)
+ for (frame_dropped = 0; frame_dropped < 2; frame_dropped++)
+ pos +=
+ sprintf(buf + pos,
+ " %s %s : %7u\n", b(fifo),
+ b(frame_dropped),
+ fp->int_err_table[fifo][frame_dropped]);
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos,
+ "Small packet counters for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 0 : %10u \n",
+ fp->rx_packets_of_size_0);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 1 : %10u \n",
+ fp->rx_packets_of_size_1);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 2 : %10u \n",
+ fp->rx_packets_of_size_2);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 3 : %10u \n",
+ fp->rx_packets_of_size_3);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 4..7 : %10u \n",
+ fp->rx_packets_of_size_4_7);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 8..15 : %10u \n",
+ fp->rx_packets_of_size_8_15);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 16..31 : %10u \n",
+ fp->rx_packets_of_size_16_31);
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, "last line\n");
+
+ *eof = 1;
+ return pos;
+}
+
+static int fepci_proc_read_registers(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+
+ struct fepci_ch_private *fp;
+ struct fepci_card_private *cardp;
+ void *ioaddr;
+
+ fp = (struct fepci_ch_private *)data;
+ cardp = (struct fepci_card_private *)fp->this_card_priv;
+ ioaddr = cardp->ioaddr;
+
+ pos +=
+ sprintf(buf + pos, "Registers for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+
+ pos +=
+ sprintf(buf + pos, " interrupt status : 0x%8x \n",
+ get_int_status(fp->channel_number, ioaddr));
+ pos +=
+ sprintf(buf + pos, " interrupt mask : 0x%8x \n",
+ get_int_mask(fp->channel_number, ioaddr));
+ pos +=
+ sprintf(buf + pos, " rxctrl : 0x%8x \n",
+ readl(ioaddr + fp->reg_rxctrl));
+ pos +=
+ sprintf(buf + pos, " txctrl : 0x%8x \n",
+ readl(ioaddr + fp->reg_txctrl));
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "last line\n");
+
+ *eof = 1;
+ return pos;
+}
+
+#endif /* DEBUG_PROC_FILES */
+
+int get_line_data_rate_value(unsigned char line_rate)
+{
+ switch (line_rate) {
+ case 0x00:
+ return 0;
+ case 0x01:
+ return 1;
+ case 0x05:
+ return 8;
+ case 0x06:
+ return 10;
+ case 0x14:
+ return 256;
+ case 0x15:
+ return 300;
+ case 0x20:
+ return 1;
+ case 0x28:
+ return 8;
+ case 0x29:
+ return 10;
+ case 0x30:
+ return 32;
+ case 0x34:
+ return 56;
+ case 0x36:
+ return 64;
+ case 0x60:
+ return 1;
+ case 0xa0:
+ return 1;
+ default:
+ return -1;
+ }
+}
+
+char get_line_data_rate_unit(unsigned char line_rate)
+{
+ switch (line_rate) {
+ case 0x00:
+ return 0;
+ case 0x01:
+ return 0;
+ case 0x05:
+ return 0;
+ case 0x06:
+ return 0;
+ case 0x14:
+ return 0;
+ case 0x15:
+ return 0;
+ case 0x20:
+ return 'k';
+ case 0x28:
+ return 'k';
+ case 0x29:
+ return 'k';
+ case 0x30:
+ return 'k';
+ case 0x34:
+ return 'k';
+ case 0x36:
+ return 'k';
+ case 0x60:
+ return 'M';
+ case 0xa0:
+ return 'G';
+ default:
+ return 0;
+ }
+}
+
+int print_line_type(unsigned char type, char *buf, int pos)
+{
+ DBG_PRINT("print_line_type %c\n", type);
+ switch (type) {
+ case 0:
+ pos += sprintf(buf + pos, "NONE");
+ break;
+ case 1:
+ pos += sprintf(buf + pos, "DCombus management bus");
+ break;
+ case 2:
+ pos += sprintf(buf + pos, "V.24");
+ break;
+ case 3:
+ pos += sprintf(buf + pos, "X.21");
+ break;
+ case 4:
+ pos += sprintf(buf + pos, "V.35");
+ break;
+ case 5:
+ pos += sprintf(buf + pos, "V.11");
+ break;
+ case 6:
+ pos += sprintf(buf + pos, "IDSL (ISDN Basic Rate");
+ break;
+ case 7:
+ pos += sprintf(buf + pos, "E1 nonframed/framed");
+ break;
+ case 8:
+ pos += sprintf(buf + pos, "E2 nonframed/framed");
+ break;
+ case 9:
+ pos += sprintf(buf + pos, "E3 nonframed/framed");
+ break;
+ case 10:
+ pos += sprintf(buf + pos, "T1 nonframed/framed");
+ break;
+ case 11:
+ pos += sprintf(buf + pos, "T2 nonframed/framed");
+ break;
+ case 12:
+ pos += sprintf(buf + pos, "T3 nonframed/framed");
+ break;
+ case 13:
+ pos += sprintf(buf + pos, "HDSL");
+ break;
+ case 14:
+ pos += sprintf(buf + pos, "ADSL");
+ break;
+ case 15:
+ pos += sprintf(buf + pos, "SDSL");
+ break;
+ case 16:
+ pos += sprintf(buf + pos, "HDSL2");
+ break;
+ case 17:
+ pos += sprintf(buf + pos, "VDSL");
+ break;
+ case 18:
+ pos += sprintf(buf + pos, "G.shdsl");
+ break;
+ case 19:
+ pos += sprintf(buf + pos, "SDH");
+ break;
+ case 255:
+ pos +=
+ sprintf(buf + pos, "COMBINATION, get list using mailbox");
+ break;
+ default:
+ pos += sprintf(buf + pos, "reserved");
+ }
+ return pos;
+}
+
+int print_card_type(unsigned char type, char *buf, int pos)
+{
+ DBG_PRINT("print_card_type\n");
+ switch (type) {
+ case 0x00:
+ pos += sprintf(buf + pos, "illegal ");
+ break;
+ case 0x01:
+ case 0x02:
+ pos += sprintf(buf + pos, "Retina ");
+ break;
+ default:
+ pos += sprintf(buf + pos, "reserved ");
+ }
+
+ return pos;
+}
+
+int print_card_model(unsigned char model, char *buf, int pos)
+{
+ DBG_PRINT("print_card_model\n");
+ switch (model) {
+ case 0x00:
+ pos += sprintf(buf + pos, "illegal ");
+ break;
+ case 0x01:
+ pos += sprintf(buf + pos, "E2200 ");
+ break;
+ case 0x02:
+ pos += sprintf(buf + pos, "C5400 ");
+ break;
+ default:
+ pos += sprintf(buf + pos, "reserved ");
+ }
+
+ return pos;
+}
+
+static inline u8 get_common_reg(void *ioaddr, unsigned long offsett)
+{
+ unsigned long byte = __test_and_clear_bit(0, &offsett);
+ u8 reg;
+ DBG_PRINT("get_common_reg %p, old %p\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+ byte,
+ &((u32 *) (ioaddr +
+ FEPCI_IDENTIFICATION_OFFSETT))[offsett / 2]);
+ reg =
+ readb(ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+ byte);
+ DBG_PRINT("get_common_reg %p: %u\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+ byte, reg);
+ return reg;
+}
+
+static int fepci_proc_read_features(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+ int tab = 0;
+ struct fepci_card_private *cardp;
+ void *ioaddr;
+
+ cardp = (struct fepci_card_private *)data;
+
+ down_read(&cardp->semaphore);
+
+ if (unlikely(cardp->pci_dev == NULL))
+ goto EOF;
+
+ ioaddr = cardp->ioaddr;
+
+ switch (offset) {
+ case 0:
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "card type: ");
+ pos = print_card_type(get_common_reg(ioaddr, 0x00), buf, pos);
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "model: ");
+ pos = print_card_model(get_common_reg(ioaddr, 0x00), buf, pos);
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "serial number: ");
+ pos +=
+ sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x07));
+ pos +=
+ sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x06));
+ pos +=
+ sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x05));
+ pos +=
+ sprintf(buf + pos, "%2.2x\n", get_common_reg(ioaddr, 0x04));
+
+ pos += sprintf(buf + pos, "version: ");
+ pos +=
+ sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x08));
+ pos +=
+ sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x09));
+ pos +=
+ sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x0a));
+ pos +=
+ sprintf(buf + pos, "%2.2x\n", get_common_reg(ioaddr, 0x0b));
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "max. pipe count: ");
+ pos += sprintf(buf + pos, "%d\n", get_common_reg(ioaddr, 0x03));
+
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ case 1:
+ pos += sprintf(buf + pos, "line count: ");
+ pos += sprintf(buf + pos, "%d\n", get_common_reg(ioaddr, 0x02));
+
+ pos += sprintf(buf + pos, "line type: ");
+ pos = print_line_type(get_common_reg(ioaddr, 0x0c), buf, pos);
+
+ /* JT: Secondary interface is also printed also,
+ * if there is one. */
+ if (get_common_reg(ioaddr, 0x10) > 0) {
+ pos += sprintf(buf + pos, " / ");
+ pos =
+ print_line_type(get_common_reg(ioaddr, 0x10),
+ buf, pos);
+ }
+
+ pos += sprintf(buf + pos, "\n");
+
+ /* JT: Line modes are printed only if
+ * the card has NO secondary interface. */
+ if (get_common_reg(ioaddr, 0x10) == 0) {
+ pos += sprintf(buf + pos, "line modes: ");
+ tab = 0;
+ if (get_common_reg(ioaddr, 0x21) & 0x80) {
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "framed mode supported\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x40) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "unframed mode supported\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x20) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line code setting available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x10) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line attenuation available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x08) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line quality available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x04) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "timing setting available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x02) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos += sprintf(buf + pos, "reserved\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x01) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line shutdown setting "
+ "available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) == 0) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos += sprintf(buf + pos, "n/a\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ case 2:
+ pos += sprintf(buf + pos, "line rate: ");
+ if (get_common_reg(ioaddr, 0x28) ==
+ get_common_reg(ioaddr, 0x2a)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos += sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))
+ * get_common_reg_word(ioaddr,
+ 0x28));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos, "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list using mailbox");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos += sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))
+ * get_common_reg_word(ioaddr,
+ 0x28));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos, "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))
+ * get_common_reg_word(ioaddr,
+ 0x2a));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos, "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list using mailbox");
+ }
+
+ /* JT: Line rate of a secondary interface is printed,
+ * if there is one. */
+ if (get_common_reg(ioaddr, 0x10) > 0) {
+ pos += sprintf(buf + pos, " / ");
+
+ if (get_common_reg(ioaddr, 0x34) ==
+ get_common_reg(ioaddr, 0x36)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos +=
+ sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr, 0x34));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or "
+ "list using mailbox");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos +=
+ sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr, 0x34));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr, 0x36));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list "
+ "using mailbox");
+ }
+ }
+
+ pos += sprintf(buf + pos, "\n");
+
+ /* JT: Primary line special info is printed only
+ * if the card has NO secondary interface. */
+ if (get_common_reg(ioaddr, 0x10) == 0) {
+ pos += sprintf(buf + pos, "rate sel. methods: ");
+ if (get_common_reg(ioaddr, 0x28) ==
+ get_common_reg(ioaddr, 0x2a))
+ pos += sprintf(buf + pos, "n/a\n");
+ else if ((get_common_reg(ioaddr, 0x22) & 4)
+ && (get_common_reg(ioaddr, 0x23) & 4))
+ pos += sprintf(buf + pos, "adjust\n");
+ else if (((!get_common_reg(ioaddr, 0x22)) & 4)
+ && (get_common_reg(ioaddr, 0x23) & 4))
+ pos += sprintf(buf + pos, "adjust(framed)\n");
+ else if ((get_common_reg(ioaddr, 0x22) & 4)
+ && ((!get_common_reg(ioaddr, 0x23)) & 4))
+ pos += sprintf(buf + pos, "adjust(unframed)\n");
+ else
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "unframed data rate: ");
+ if (get_common_reg(ioaddr, 0x30) ==
+ get_common_reg(ioaddr, 0x32)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x30));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list using "
+ "mailbox\n");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x30));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x32));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+
+ case 3:
+ /* JT: Line modes are printed only
+ * if the card has NO secondary interface. */
+ if (get_common_reg(ioaddr, 0x10) == 0) {
+ pos += sprintf(buf + pos, "rate sel. methods: ");
+ if (get_common_reg(ioaddr, 0x30) ==
+ get_common_reg(ioaddr, 0x32))
+ pos += sprintf(buf + pos, "n/a\n");
+ else {
+ if (get_common_reg(ioaddr, 0x23) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "timeslot map\n");
+ if (get_common_reg(ioaddr, 0x23) & 2)
+ pos += sprintf(buf + pos, "adjust\n");
+ }
+
+ pos += sprintf(buf + pos, "framed data rate: ");
+ if (get_common_reg(ioaddr, 0x2c) ==
+ get_common_reg(ioaddr, 0x2e)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x2c));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x2c));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x2e));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ }
+
+ pos += sprintf(buf + pos, "rate sel. methods: ");
+ if (get_common_reg_word(ioaddr, 0x2c) ==
+ get_common_reg_word(ioaddr, 0x2e))
+ pos += sprintf(buf + pos, "n/a\n");
+ else {
+ if (get_common_reg(ioaddr, 0x22) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "timeslot map\n");
+ if (get_common_reg(ioaddr, 0x22) & 2)
+ pos += sprintf(buf + pos, "adjust\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ default:
+EOF:
+ up_read(&cardp->semaphore);
+ *start = (char *)0;
+ *eof = 1;
+ return 0;
+ }
+}
+
+static int fepci_proc_read_settings(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+ int i, j;
+
+ struct fepci_card_private *cardp;
+ void *ioaddr;
+
+ cardp = (struct fepci_card_private *)data;
+
+ down_read(&cardp->semaphore);
+
+ if (unlikely(cardp->pci_dev == NULL))
+ goto EOF;
+
+ ioaddr = cardp->ioaddr;
+
+ switch (offset) {
+ case 0:
+ j = get_common_reg(ioaddr, 0x02);
+
+ for (i = 0; i < j; i++) {
+ if ((get_common_reg_word(ioaddr, 0x44) >> i) & 1) {
+ pos += sprintf(buf + pos, "\nline %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+ pos +=
+ sprintf(buf + pos, "mode: ");
+ if ((get_common_reg_word(ioaddr, 0x46) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "framed\n");
+ else
+ pos += sprintf(buf + pos, "unframed\n");
+
+ pos +=
+ sprintf(buf + pos, "scrambler: ");
+ if ((get_common_reg_word(ioaddr, 0x48) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "enabled\n");
+ else if ((get_common_reg_word(ioaddr, 0x46)
+ >> i) & 1)
+ pos += sprintf(buf + pos, "n/a\n");
+ else
+ pos += sprintf(buf + pos, "disabled\n");
+
+ pos +=
+ sprintf(buf + pos, "line crc: ");
+ if ((get_common_reg_word(ioaddr, 0x46) >>
+ i) & 1) {
+ if ((get_common_reg_word
+ (ioaddr, 0x4a) >> i) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "enabled\n");
+ else if (get_common_reg
+ (ioaddr, 0x22) & 0x20)
+ pos +=
+ sprintf(buf + pos,
+ "disabled\n");
+ else
+ pos +=
+ sprintf(buf + pos, "n/a\n");
+ } else {
+ if ((get_common_reg_word
+ (ioaddr, 0x4a) >> i) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "enabled\n");
+ else if (get_common_reg
+ (ioaddr, 0x23) & 0x20)
+ pos +=
+ sprintf(buf + pos,
+ "disabled\n");
+ else
+ pos +=
+ ÀGø\x02+ }
+ }
+ }
+
+ /* If there are no enabled lines, "No enabled lines!" printed.
+ * The settings file would remain empty otherwise. */
+ if ((get_common_reg_word(ioaddr, 0x44)) == 0)
+ pos += sprintf(buf + pos, "No enabled lines!\n");
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ case 1:
+ j = get_common_reg(ioaddr, 0x03);
+
+ if ((get_common_reg_word(ioaddr, 0x44)) != 0) {
+ for (i = 0; i < j; i++) {
+ pos += sprintf(buf + pos, "\npipe %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+
+ pos +=
+ sprintf(buf + pos, "access mode: ");
+ if ((get_common_reg_word(ioaddr, 0x4e) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "packet\n");
+ else
+ pos += sprintf(buf + pos, "stream\n");
+
+ pos +=
+ sprintf(buf + pos, "data rate: ");
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x42)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x42))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x42)) *
+ get_common_reg_word
+ (ioaddr, 0x50 + 2 * i));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x42))) {
+ char unit =
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x42));
+ pos +=
+ sprintf(buf + pos,
+ "%c", unit);
+ }
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ default:
+EOF:
+ up_read(&cardp->semaphore);
+ *start = (char *)0;
+ *eof = 1;
+ return 0;
+ }
+}
+
+void proc_cpy(char *buf_from, char *buf_to, int len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ *(buf_to + i) = *(buf_from + i);
+}
+
+static int fepci_proc_read_status(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ unsigned int pos = 0;
+ unsigned int i, j;
+
+ struct fepci_card_private *cardp;
+
+ void *ioaddr;
+
+ struct fepci_ch_private *fp;
+
+ cardp = (struct fepci_card_private *)data;
+
+ down_read(&cardp->semaphore);
+
+ if (unlikely(cardp->pci_dev == NULL))
+ goto EOF;
+
+ ioaddr = cardp->ioaddr;
+
+ if ((offset & 0xff000) == 0) {
+ j = get_common_reg(ioaddr, 0x02);
+ for (i = 0; i < j; i++) {
+ pos += sprintf(buf + pos, "\nline %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+ pos += sprintf(buf + pos, "status: ");
+ if ((get_common_reg_word(ioaddr, 0x72) >> i) & 1) {
+ pos += sprintf(buf + pos, "DOWN\n");
+ netif_carrier_off(cardp->ch_privates[i]->
+ this_dev);
+ } else {
+ netif_carrier_on(cardp->ch_privates[i]->
+ this_dev);
+ if ((get_common_reg_word(ioaddr, 0x74) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "UP/Alarm\n");
+ else
+ pos += sprintf(buf + pos, "UP\n");
+ }
+ pos += sprintf(buf + pos, "primary clock: ");
+ if ((get_common_reg_word(ioaddr, 0x76) >> i) & 1)
+ pos += sprintf(buf + pos, "OK\n");
+ else
+ pos += sprintf(buf + pos, "FAIL\n");
+ }
+ up_read(&cardp->semaphore);
+ if (pos - (0xfff & offset) > (unsigned)len)
+ *start = (char *)len;
+ else
+ *start = (char *)(0x1000 - (offset & 0xfff));
+ proc_cpy(buf + (offset & 0xfff), buf, len);
+ return min((int)(pos - (0xfff & offset)), len);
+ }
+
+ j = get_common_reg(ioaddr, 0x03);
+ if ((j <= 0) || (j > 16))
+ j = 16;
+ if ((((((unsigned)offset) & 0xff000) >> 12) > 0) &&
+ (((((unsigned)offset) & 0xff000) >> 12) <= j)) {
+ i = ((int)((offset & 0xff000) >> 12)) - 1;
+ if (i < CHANNELS) {
+ pos += sprintf(buf + pos, "\npipe %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+ pos += sprintf(buf + pos, "status: ");
+
+ if (!((get_common_reg_word(ioaddr, 0x78) >> i) & 1))
+ pos += sprintf(buf + pos, "DOWN\n");
+ else if ((get_common_reg_word(ioaddr, 0x7a) >> i) & 1)
+ pos += sprintf(buf + pos, "UP/Degraded\n");
+ else
+ pos += sprintf(buf + pos, "UP\n");
+ if ((get_common_reg_word(ioaddr, 0x4e) >> i) & 1) {
+ pos +=
+ sprintf(buf + pos, "packet counters:\n\n");
+ pos +=
+ sprintf(buf + pos, "rx count: %lu \n",
+ cardp->ch_privates[i]->stats.
+ rx_packets);
+ pos +=
+ sprintf(buf + pos, "rx data: %lu \n",
+ cardp->ch_privates[i]->stats.
+ rx_bytes);
+ pos +=
+ sprintf(buf + pos, "rx errors:%lu \n",
+ cardp->ch_privates[i]->stats.
+ rx_errors);
+ pos +=
+ sprintf(buf + pos, "tx count: %lu \n",
+ cardp->ch_privates[i]->stats.
+ tx_packets);
+ pos +=
+ sprintf(buf + pos, "tx data: %lu \n",
+ cardp->ch_privates[i]->stats.
+ tx_bytes);
+ pos +=
+ sprintf(buf + pos, "tx errors:%lu \n",
+ cardp->ch_privates[i]->stats.
+ tx_errors);
+ } else {
+ pos +=
+ sprintf(buf + pos, "stream counters:\n\n");
+ fp = cardp->ch_privates[i];
+ pos += sprintf(buf + pos,
+ "rx_desc_fifo_err_stream : %10u"
+ " \n",
+ fp->rx_desc_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_desc_size_err_stream : %10u"
+ " \n",
+ fp->rx_desc_size_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_desc_octet_err_stream : %9u"
+ " \n",
+ fp->rx_desc_octet_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_desc_line_err_stream : %10u"
+ " \n",
+ fp->rx_desc_line_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "tx_desc_fifo_err_stream : %10u"
+ " \n",
+ fp->tx_desc_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_int_fifo_err_stream : %11u \n",
+ fp->rx_int_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_int_frame_dropped_err_stream :"
+ " %2u \n",
+ fp->
+ rx_int_frame_dropped_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "tx_int_fifo_err_stream : %11u \n",
+ fp->tx_int_fifo_err_stream);
+ }
+ if (pos - (0xfff & offset) > (unsigned)len)
+ *start = (char *)len;
+ else
+ *start = (char *)(0x1000 - (offset & 0xfff));
+ proc_cpy(buf + (offset & 0xfff), buf, len);
+ }
+ up_read(&cardp->semaphore);
+ return min((int)(pos - (0xfff & offset)), len);
+ } else {
+EOF:
+ up_read(&cardp->semaphore);
+ *start = (char *)0;
+ *eof = 1;
+ return 0;
+ }
+}
+
+static int fepci_proc_read_devices(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ void *ioaddr;
+ unsigned int pos = 0;
+ unsigned int i;
+
+ pos +=
+ sprintf(buf + pos,
+ "bus:card.function cardtype model lines, max rate\n");
+ for (i = 0; i < find_cnt; i++) {
+ down_read(&card_privates[i].semaphore);
+ if (likely(card_privates[i].pci_dev)) { /* the card exists */
+ pos +=
+ sprintf(buf + pos,
+ "%02x:%s ",
+ card_privates[i].pci_dev->bus->
+ number,
+ card_privates[i].pci_dev->procent->name);
+ ioaddr = card_privates[i].ioaddr;
+ pos =
+ print_card_type(get_common_reg
+ (ioaddr, 0x00), buf, pos);
+ pos =
+ print_card_model(get_common_reg
+ (ioaddr, 0x00), buf, pos);
+ pos +=
+ sprintf(buf + pos, "%d",
+ get_common_reg(ioaddr, 0x02));
+ pos += sprintf(buf + pos, " x ");
+ pos =
+ print_line_type(get_common_reg
+ (ioaddr, 0x0c), buf, pos);
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos += sprintf(buf + pos, "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos += sprintf(buf + pos, ", total ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr,
+ 0x2a) *
+ get_common_reg(ioaddr, 0x02));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value or list "
+ "using mailbox\n");
+
+ }
+ up_read(&card_privates[i].semaphore);
+ }
+ DBG_PRINT("fepci_proc_read_devices pos %u < %lu?\n", pos, PAGE_SIZE);
+ *eof = 1;
+ return pos;
+}
+
+static int fepci_proc_read_id_list(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ unsigned int pos = 0;
+ unsigned int i;
+
+ pos += sprintf(buf + pos, "bus:card.function major minor interfaces\n");
+ for (i = 0; i < find_cnt; i++) {
+ down_read(&card_privates[i].semaphore);
+ if (likely(card_privates[i].pci_dev)) /* the card exists */
+ pos +=
+ sprintf(buf + pos,
+ "%02x:%s %03i %03i %s..%s\n",
+ card_privates[i].pci_dev->bus->
+ number,
+ card_privates[i].pci_dev->
+ procent->name, major,
+ card_privates[i].
+ ch_privates[0]->minor,
+ card_privates[i].
+ ch_privates[0]->this_dev->name,
+ card_privates[i].
+ ch_privates[CHANNELS - 1]->this_dev->name);
+ up_read(&card_privates[i].semaphore);
+ }
+
+ pos += sprintf(buf + pos, "\n");
+ DBG_PRINT("fepci_proc_read_id_list pos %u < %lu?\n", pos, PAGE_SIZE);
+ *eof = 1;
+ return pos;
+}
+
+const static char DEVICES[] = "devices";
+const static char ID_LIST[] = "id_list";
+
+static int fepci_proc_init_driver(void)
+{
+ DBG_PRINT("fepci_proc_init_driver\n");
+ proc_root_entry = create_proc_entry(fepci_proc_entry_name, S_IFDIR, 0);
+ if (!proc_root_entry)
+ printk(KERN_WARNING
+ "%s: creating proc root dir entry failed.\n",
+ fepci_NAME);
+
+ create_proc_read_entry(DEVICES, 0, proc_root_entry,
+ fepci_proc_read_devices, NULL);
+ create_proc_read_entry(ID_LIST, 0, proc_root_entry,
+ fepci_proc_read_id_list, NULL);
+
+ return 0;
+}
+
+static void fepci_proc_cleanup_driver(void)
+{
+ char name[sizeof(fepci_proc_entry_name) + sizeof(DEVICES)];
+ strcpy(name, fepci_proc_entry_name);
+ name[sizeof(fepci_proc_entry_name) - 1] = '/';
+ strcpy(name + sizeof(fepci_proc_entry_name), DEVICES);
+ remove_proc_entry(name, 0);
+ strcpy(name + sizeof(fepci_proc_entry_name), ID_LIST);
+ remove_proc_entry(name, 0);
+ remove_proc_entry(fepci_proc_entry_name, 0);
+}
+
+static void fepci_proc_init_card(int card_number, void *card_data)
+{
+ char buf[50];
+ struct proc_dir_entry *ent;
+ sprintf(buf, "%02x:%02x.%02x",
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+ ent = create_proc_entry(buf, S_IFDIR, proc_root_entry);
+ if (!ent)
+ printk("%s: creating proc dir entry failed.\n", fepci_NAME);
+
+ sprintf(buf, fepci_features_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_features,
+ card_data);
+
+ sprintf(buf, fepci_settings_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_settings,
+ card_data);
+
+ sprintf(buf, fepci_status_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_status, card_data);
+}
+
+static void fepci_proc_cleanup_card(int card_number)
+{
+ char buf[50];
+
+ sprintf(buf, fepci_features_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ remove_proc_entry(buf, 0);
+ sprintf(buf, fepci_settings_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ remove_proc_entry(buf, 0);
+ sprintf(buf, fepci_status_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ remove_proc_entry(buf, 0);
+
+ sprintf(buf, "%02x:%02x.%02x",
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+ remove_proc_entry(buf, proc_root_entry);
+}
+
+#ifdef DEBUG_PROC_FILES
+static void fepci_proc_init_channel(int card_number, int channel_number,
+ void *channel_data)
+{
+ DBG_PRINT("fepci_proc_init_channel\n");
+ char buf[50];
+
+ sprintf(buf, fepci_counters_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_counters,
+ channel_data);
+
+ sprintf(buf, fepci_descriptors_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_descriptors,
+ channel_data);
+
+ sprintf(buf, fepci_stream_counters_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+
+ create_proc_read_entry(buf, 0, NULL,
+ fepci_proc_read_stream_counters, channel_data);
+
+ sprintf(buf, fepci_registers_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_registers,
+ channel_data);
+}
+
+static void fepci_proc_cleanup_channel(int card_number, int channel_number)
+{
+ char buf[50];
+ sprintf(buf, fepci_counters_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ remove_proc_entry(buf, 0);
+
+ sprintf(buf, fepci_descriptors_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ remove_proc_entry(buf, 0);
+
+ sprintf(buf, fepci_stream_counters_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ remove_proc_entry(buf, 0);
+
+ sprintf(buf, fepci_registers_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ remove_proc_entry(buf, 0);
+}
+#endif /* DEBUG_PROC_FILES */
diff -Napur linux-2.6.23/drivers/net/wan/retina.h linux-2.6.24/drivers/net/wan/retina.h
--- linux-2.6.23/drivers/net/wan/retina.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.24/drivers/net/wan/retina.h 2007-10-23 12:31:44.539117387 +0300
@@ -0,0 +1,164 @@
+/* V1.0.0 */
+
+/*
+ Copyright (C) 2002-2003 Jouni Kujala, Flexibilis Oy.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ All the drivers derived from or based on this code fall under the
+ GPL and must retain the copyright and license notice.
+*/
+
+#ifndef RETINA_H
+#define RETINA_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* net device related stuff: */
+#define FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE 0x89F1
+#define FEPCI_NETDEV_IOCTL_STREAM_UNITSIZE 0x89F2
+#define FEPCI_NETDEV_IOCTL_STREAM_OPEN 0x89F3
+#define FEPCI_NETDEV_IOCTL_STREAM_START 0x89F4
+#define FEPCI_NETDEV_IOCTL_STREAM_CLOSE 0x89F6
+
+/* char device related stuff: */
+
+#define FEPCI_SHARED_MEM_OFFSETT 0x8000
+#define FEPCI_IDENTIFICATION_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x0)
+#define FEPCI_FEATURES_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x40)
+#define FEPCI_SETTINGS_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x80)
+#define FEPCI_STATUS_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0xE0)
+#define FEPCI_MAILBOX_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x100)
+
+/* structures for ioctl calls: */
+struct fepci_ioctl_identification {
+ unsigned char data[0x20];
+};
+struct fepci_real_identification {
+ unsigned long int data[0x10];
+};
+
+struct fepci_ioctl_features {
+ unsigned char data[0x20];
+};
+struct fepci_real_features {
+ unsigned long int data[0x10];
+};
+
+struct fepci_ioctl_settings {
+ unsigned char data[0x30];
+};
+struct fepci_real_settings {
+ unsigned long int data[0x15];
+};
+
+struct fepci_ioctl_status {
+ unsigned char data[0x10];
+};
+struct fepci_real_status {
+ unsigned long int data[0x5];
+};
+
+struct fepci_ioctl_shared_mem {
+ unsigned long int data[0x80];
+};
+
+#define FEPCI_IOCTL_MAGIC 0xAA
+
+#define FEPCI_IOCTL_R_SHARED_MEM _IOR(FEPCI_IOCTL_MAGIC, 1, \
+struct fepci_ioctl_shared_mem)
+#define FEPCI_IOCTL_W_SHARED_MEM _IOW(FEPCI_IOCTL_MAGIC, 2, \
+struct fepci_ioctl_shared_mem)
+
+#define FEPCI_IOCTL_G_IDENTIFICATION _IOR(FEPCI_IOCTL_MAGIC, 0x81, \
+struct fepci_ioctl_identification)
+#define FEPCI_IOCTL_G_FEATURES _IOR(FEPCI_IOCTL_MAGIC, 0x82, \
+struct fepci_ioctl_features)
+#define FEPCI_IOCTL_G_SETTINGS _IOR(FEPCI_IOCTL_MAGIC, 0x83, \
+struct fepci_ioctl_settings)
+#define FEPCI_IOCTL_G_STATUS _IOR(FEPCI_IOCTL_MAGIC, 0x84, \
+struct fepci_ioctl_status)
+
+/* mailbox: */
+
+struct fepci_ioctl_mailbox {
+ unsigned char Semafore;
+ unsigned char Mail_number;
+ unsigned char Size;
+ unsigned char Command;
+ unsigned char Data[112];
+};
+
+struct fepci_real_mailbox {
+ __u32 Semafore_Mail_number;
+ __u32 Size_Command;
+ __u32 Data[112 / 2];
+};
+
+#define FEPCI_IOCTL_B_POLL _IO(FEPCI_IOCTL_MAGIC, 0x85)
+#define FEPCI_IOCTL_B_GRAB _IO(FEPCI_IOCTL_MAGIC, 0x86)
+#define FEPCI_IOCTL_B_RELEASE _IO(FEPCI_IOCTL_MAGIC, 0x87)
+#define FEPCI_IOCTL_B_S_CMAIL _IOW(FEPCI_IOCTL_MAGIC, 0x88, \
+struct fepci_ioctl_mailbox)
+#define FEPCI_IOCTL_B_S_QMAIL _IOW(FEPCI_IOCTL_MAGIC, 0x89, \
+struct fepci_ioctl_mailbox)
+#define FEPCI_IOCTL_B_G_MAIL _IOR(FEPCI_IOCTL_MAGIC, 0x90, \
+struct fepci_ioctl_mailbox)
+
+#define FEPCI_IOCTL_ALARM_MANAGER _IO(FEPCI_IOCTL_MAGIC, 0x91)
+#define FEPCI_IOCTL_STREAM_TRANSMIT_POLL _IO(FEPCI_IOCTL_MAGIC, 0x92)
+#define FEPCI_IOCTL_STREAM_RECEIVE_POLL _IO(FEPCI_IOCTL_MAGIC, 0x93)
+#define FEPCI_IOCTL_STREAM_BOTH_POLL _IO(FEPCI_IOCTL_MAGIC, 0x94)
+
+/* stream related stuff: */
+
+/* stream buffer address space:
+ * address: 0x 7 6 5 4 3 2 1 0
+ * ^ ^ ^
+ * | | |
+ * card | area(rx/tx,0==rx,1==tx)
+ * channel */
+
+#define CARD_ADDRESS_SHIFT 24u
+#define CHANNEL_ADDRESS_SHIFT 20u
+#define AREA_ADDRESS_SHIFT 16u
+
+#define STREAM_BUFFER_POINTER_AREA 0x7fff0000 /* one page reserved */
+
+/* stream buffer pointers (at pointer area):
+ * address: 0x 7 6 5 4 3 2 1 0
+ * ^ ^ ^
+ * | | |
+ * card | area(rx/tx,0==rx,4==tx)
+ * channel */
+
+#define CARD_POINTER_SHIFT 8u
+#define CHANNEL_POINTER_SHIFT 4u
+#define AREA_POINTER_SHIFT 2u
+
+/* fake pointers are for faking larger unit sizes to the user than
+ * what is the maximum internal unit size in FEPCI */
+#define USER_RX_S_FAKE_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x0) + __offset))
+#define USER_TX_S_FAKE_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x4) + __offset))
+
+#define USER_RX_S_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x8) + __offset))
+#define USER_TX_S_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0xC) + __offset))
+
+#endif
diff -Napur linux-2.6.23/MAINTAINERS linux-2.6.24/MAINTAINERS
--- linux-2.6.23/MAINTAINERS 2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/MAINTAINERS 2007-10-23 12:27:47.056221363 +0300
@@ -3149,6 +3149,12 @@ L: reiserfs-devel@vger.kernel.org
W: http://www.namesys.com
S: Supported
+RETINA DRIVER
+P: Matti Linnanvuori
+M: mattilinnanvuori@yahoo.com
+L: netdev@vger.kernel.org
+S: Supported
+
ROCKETPORT DRIVER
P: Comtrol Corp.
W: http://www.comtrol.com
Heute schon einen Blick in die Zukunft von E-Mails wagen? Versuchen Sie´s mit dem neuen Yahoo! Mail. www.yahoo.de/mail
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH] wan: new driver retina
2007-10-23 9:58 Matti Linnanvuori
@ 2007-10-23 16:31 ` Stephen Hemminger
0 siblings, 0 replies; 8+ messages in thread
From: Stephen Hemminger @ 2007-10-23 16:31 UTC (permalink / raw)
To: Matti Linnanvuori; +Cc: akpm, jgarzik, netdev
On Tue, 23 Oct 2007 02:58:29 -0700 (PDT)
Matti Linnanvuori <mattilinnanvuori@yahoo.com> wrote:
> From: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
>
> Retina G.703 and G.SHDSL driver.
>
> Signed-off-by: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
> ---
>
> Fixed bugs according to linux-netdev comments.
>
> diff -Napur linux-2.6.23/drivers/net/wan/Kconfig linux-2.6.24/drivers/net/wan/Kconfig
> --- linux-2.6.23/drivers/net/wan/Kconfig 2007-10-09 23:31:38.000000000 +0300
> +++ linux-2.6.24/drivers/net/wan/Kconfig 2007-10-23 12:30:45.853384514 +0300
> @@ -494,4 +494,14 @@ config SBNI_MULTILINE
>
> If unsure, say N.
>
> +config RETINA
> + tristate "Retina support"
> + depends on PCI
> + help
> + Driver for Retina C5400 and E2200 network PCI cards, which
> + support G.703, G.SHDSL with Ethernet encapsulation.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called retina.
> +
> endif # WAN
> diff -Napur linux-2.6.23/drivers/net/wan/Makefile linux-2.6.24/drivers/net/wan/Makefile
> --- linux-2.6.23/drivers/net/wan/Makefile 2007-10-09 23:31:38.000000000 +0300
> +++ linux-2.6.24/drivers/net/wan/Makefile 2007-10-23 12:31:17.598640178 +0300
> @@ -42,6 +42,7 @@ obj-$(CONFIG_C101) += c101.o
> obj-$(CONFIG_WANXL) += wanxl.o
> obj-$(CONFIG_PCI200SYN) += pci200syn.o
> obj-$(CONFIG_PC300TOO) += pc300too.o
> +obj-$(CONFIG_RETINA) += retina.o
>
> clean-files := wanxlfw.inc
> $(obj)/wanxl.o: $(obj)/wanxlfw.inc
> diff -Napur linux-2.6.23/drivers/net/wan/retina.c linux-2.6.24/drivers/net/wan/retina.c
> --- linux-2.6.23/drivers/net/wan/retina.c 1970-01-01 02:00:00.000000000 +0200
> +++ linux-2.6.24/drivers/net/wan/retina.c 2007-10-23 12:52:17.122802714 +0300
> @@ -0,0 +1,4428 @@
> +/* retina.c: */
> +
> +/*
> + This driver is based on:
> +
> + /drivers/net/fepci.c
> + FEPCI (Frame Engine for PCI) driver for Linux operating system
> +
> + Copyright (C) 2002-2003 Jouni Kujala, Flexibilis Oy.
> +
> + This program is free software; you can redistribute it and/or
> + modify it under the terms of the GNU General Public License
> + as published by the Free Software Foundation; either version 2
> + of the License, or (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + All the drivers derived from or based on this code fall under the
> + GPL and must retain the copyright and license notice.
> +*/
> +
> +#define DRV_NAME "retina"
> +#define DRV_VERSION "1.2.6"
> +
> +#define DBG_PRINT(xyz...) /* printk(KERN_DEBUG xyz) */
pr_debug()
> +/* uncomment this if you want to have debug files in /proc
> + * #define DEBUG_PROC_FILES */
> +
> +/* Keep this if you want to have point-to-point links
> + ! Only interfaces listed in retina_ptp_interfaces will
> + be created in PtP mode
> + see retina_ptp_interfaces */
> +#define FEPCI_POINT_TO_POINT
> +
> +/* need to update MODULE_PARM also */
> +#define MAX_DEVICES 32u
> +
> +#define MAX_TX_UNITS 256u
> +#define MAX_RX_UNITS 256u
> +
> +#define MAX_UNIT_SZ_ORDER 10u
> +
> +#define TX_RING_SIZE 8u
> +#define RX_RING_SIZE 8u
> +
> +/* need to update MODULE_PARM also */
> +#define CHANNELS 4u
> +
> +#define RX_FIFO_THRESHOLD_PACKET_MODE 0x4
> +#define TX_FIFO_THRESHOLD_PACKET_MODE 0x4
> +#define TX_DESC_THRESHOLD_PACKET_MODE 0x4
> +
> +#define RX_FIFO_THRESHOLD_STREAM_MODE 0x4
> +#define TX_FIFO_THRESHOLD_STREAM_MODE 0x7
> +#define TX_DESC_THRESHOLD_STREAM_MODE 0x1
> +
> +/* need to update MODULE_PARM also */
> +#define MAX_INTERFACES (CHANNELS*MAX_DEVICES)
> +
> +const char fepci_name[] = "retina";
> +const char fepci_alarm_manager_name[] = "retina alarm manager";
> +const char fepci_NAME[] = "RETINA";
> +const char fepci_netdev_name[] = "dcpxx";
> +const char fepci_proc_entry_name[] = "driver/retina";
> +
> +static unsigned int find_cnt;
> +
> +#ifdef FEPCI_POINT_TO_POINT
> +static char *retina_ptp_interfaces[MAX_INTERFACES];
> +static int retina_noarp_with_ptp = 1;
> +#endif /* FEPCI_POINT_TO_POINT */
> +
> +#define fepci_features_proc_entry_name "driver/retina/%02x:%02x.%02x/features"
> +#define fepci_settings_proc_entry_name "driver/retina/%02x:%02x.%02x/settings"
> +#define fepci_status_proc_entry_name "driver/retina/%02x:%02x.%02x/status"
> +
> +#ifdef DEBUG_PROC_FILES
> +#define fepci_counters_proc_entry_name \
> +"driver/retina/%02x:%02x.%02x/counters_ch%d"
> +#define fepci_descriptors_proc_entry_name \
> +"driver/retina/%02x:%02x.%02x/descriptors_ch%d"
> +#define fepci_stream_counters_proc_entry_name \
> +"driver/retina/%02x:%02x.%02x/stream_counters_ch%d"
> +#define fepci_registers_proc_entry_name \
> +"driver/retina/%02x:%02x.%02x/registers_ch%d"
> +#endif /* DEBUG_PROC_FILES */
You can't use /proc for this in new drivers. Either:
1. Remove the code since it doesn't do anything useful
2. Convert it to use debugfs (see skge and sky2)
3. Convert it to use sysfs attributes.
> +/* Time in jiffies before concluding that the transmitter is hung */
> +#define TX_TIMEOUT (20*HZ)
Pretty long.
> +#include "retina.h"
> +#include <linux/mm.h>
> +#include <linux/random.h>
> +#include <linux/proc_fs.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/string.h>
> +#include <linux/timer.h>
> +#include <linux/errno.h>
> +#include <linux/ioport.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/pci.h>
> +#include <linux/init.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/ethtool.h>
> +#include <linux/delay.h>
> +#include <linux/bitops.h>
> +#include <linux/version.h>
> +#include <linux/pfn.h>
> +#include <linux/uaccess.h>
> +#include <linux/io.h>
> +
> +#include <asm/unaligned.h>
> +#include <asm/pgtable.h>
> +
> +MODULE_VERSION(DRV_VERSION);
> +
> +/* PCI I/O space extent */
> +#define FEPCI_SIZE 0x20000
> +#define PCI_IOTYPE (PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY)
> +
> +struct pci_id_info {
> + const char *name;
> + struct match_info {
> + int pci, pci_mask, subsystem, subsystem_mask;
> + int revision, revision_mask; /* Only 8 bits. */
> + } id;
> + unsigned pci_flags;
> + int io_size; /* Needed for I/O region check or ioremap */
> + int drv_flags; /* Driver use, intended as capability flags */
> +};
> +
> +static struct pci_id_info pci_id_tbl[] = {
> + {"Frame Engine for PCI (FEPCI)",
> + {0x1FC00300, 0x1FC00301, 0xffffffff},
> + PCI_IOTYPE, FEPCI_SIZE},
> + {0,},
> +};
You shouldn't need this if you use current pci device discovery
methods.
> +static struct pci_device_id fepci_pci_tbl[] __devinitdata = {
> + {0x1FC0, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
> + {0x1FC0, 0x0301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
> + {0,}
> +};
You might want to add PCI_VENDOR_ID_FEPCI to pci_ids.h
> +MODULE_DESCRIPTION("Frame Engine for PCI (FEPCI)");
> +MODULE_AUTHOR("Jouni Kujala");
> +MODULE_LICENSE("GPL");
> +MODULE_DEVICE_TABLE(pci, fepci_pci_tbl);
> +
> +/* Linux appears to drop POINTOPOINT,BROADCAST and NOARP flags in SIOCSFLAGS
> + * This workaround allows load time per interface ptp mode configuration.
> + * Runtime ptp mode changes would either require changes to Linux or
> + * use of proprietary ioctls, which ifconfig knows nothing about anyway
> + */
> +
> +static unsigned interfaces = MAX_INTERFACES;
> +#ifdef FEPCI_POINT_TO_POINT
> +module_param_array(retina_ptp_interfaces, charp, &interfaces, S_IRUGO);
> +module_param(retina_noarp_with_ptp, bool, S_IRUGO);
> +MODULE_PARM_DESC(retina_noarp_with_ptp,
> + "0 to disable NOARP, "
> + "1 to enable NOARP on pointopoint interfaces");
> +#endif
> +
> +struct fepci_ch_private {
> + unsigned int channel_number;
> + struct net_device *this_dev;
> +
> + struct fepci_card_private *this_card_priv;
> +
> + unsigned int reg_rxctrl;
> + unsigned int reg_txctrl;
> +
> + struct fepci_desc *rx_desc; /* rx_ring start */
> + struct fepci_desc *tx_desc; /* tx_ring start */
> + struct sk_buff *rx_skbuff[RX_RING_SIZE];
> + struct sk_buff *tx_skbuff[TX_RING_SIZE];
> +
> + struct timer_list timer;
> + struct net_device_stats stats;
> +
> + unsigned drv_flags;
> +
> + unsigned int rx_buf_sz; /* MTU+slack */
> + unsigned int cur_tx; /* the next filled tx_descriptor */
> + /* in stream mode the desc which is being transmitted */
> + /* rx_descriptor where next packet transferred */
> + unsigned int cur_rx;
> + /* in stream mode the desc which is being received */
> +
> +/* stream mode: */
> + bool in_eth_mode;
> + bool in_stream_mode;
> + bool stream_on;
> + u32 *rx_buffer;
> + u32 *tx_buffer;
> + unsigned bufsize_order; /* 10=1kB,11=2kB,12=4kB...16=64kB */
> + unsigned bufsize;
> + unsigned unit_sz_order; /* 8=256B...14=16kB */
> + unsigned unit_sz;
> + unsigned units; /* 2,4,8,16,...,256 */
> + /* fake units (and pointers) are for faking larger unit sizes to
> + * the user than what is the maximum internal unit size in FEPCI */
> + unsigned fake_unit_sz_order;
> + unsigned fake_unit_sz;
> + unsigned fake_units;
> + u32 *tx_unit[MAX_TX_UNITS];
> + u32 *rx_unit[MAX_RX_UNITS];
> + unsigned cur_tx_unit; /* last sent tx_unit */
> + /* rx_unit where to next packet is transferred */
> + unsigned cur_rx_unit;
> +/* char device: */
> + unsigned minor; /* currently the same as card_nuber */
> +/* debugging stuff for packet mode: */
> + /* rx-errors in descriptors */
> + unsigned int rx_desc_fifo_err;
> + unsigned int rx_desc_size_err;
> + unsigned int rx_desc_crc_err;
> + unsigned int rx_desc_octet_err;
> + unsigned rx_desc_line_err;
> + /* tx-errors in descriptors */
> + unsigned tx_desc_fifo_err;
> + /* rx-errors in interrupts */
> + unsigned rx_int_fifo_err;
> + unsigned rx_int_frame_dropped_err;
> + /* tx-errors in interrupts */
> + unsigned tx_int_fifo_err;
> + /* ints */
> + unsigned interrupts;
> + unsigned rx_interrupts;
> + unsigned tx_interrupts;
> + /* error combination tables */
> + /* fifo,size,crc,octet,line */
> + unsigned rx_desc_err_table[2][2][2][2][2];
> + unsigned int_err_table[2][2]; /* fifo,frame_dropped */
> + /* skbuff counters */
> + unsigned rx_skbuffs_in;
> + unsigned rx_skbuffs_out;
> + unsigned tx_skbuffs_in;
> + unsigned tx_skbuffs_out;
> +/* debugging stuff for stream mode: */
> + /* rx-errors in descriptors */
> + unsigned rx_desc_fifo_err_stream;
> + unsigned rx_desc_size_err_stream;
> + unsigned rx_desc_crc_err_stream;
> + unsigned rx_desc_octet_err_stream;
> + unsigned rx_desc_line_err_stream;
> + /* tx-errors in descriptors */
> + unsigned tx_desc_fifo_err_stream;
> + /* rx-errors in interrupts */
> + unsigned rx_int_fifo_err_stream;
> + unsigned rx_int_frame_dropped_err_stream;
> + /* tx-errors in interrupts */
> + unsigned tx_int_fifo_err_stream;
> + /* ints */
> + unsigned interrupts_stream;
> + unsigned rx_interrupts_stream;
> + unsigned tx_interrupts_stream;
> + /* error combination tables */
> + /* fifo,size,crc,octet,line */
> + unsigned rx_desc_err_table_stream[2][2][2][2][2];
> + unsigned int_err_table_stream[2][2]; /* fifo,frame_dropped */
> +/* other: */
> + /* tx interrupts since last timer interrupt */
> + unsigned tx_interrupts_since_last_timer;
> +/* small packet counters: */
> + unsigned rx_packets_of_size_0;
> + unsigned rx_packets_of_size_1;
> + unsigned rx_packets_of_size_2;
> + unsigned rx_packets_of_size_3;
> + unsigned rx_packets_of_size_4_7;
> + unsigned rx_packets_of_size_8_15;
> + unsigned rx_packets_of_size_16_31;
> +/* small packet counters for stream: */
> + unsigned rx_packets_of_size_0_stream;
> + unsigned rx_packets_of_size_1_stream;
> + unsigned rx_packets_of_size_2_stream;
> + unsigned rx_packets_of_size_3_stream;
> + unsigned rx_packets_of_size_4_7_stream;
> + unsigned rx_packets_of_size_8_15_stream;
> + unsigned rx_packets_of_size_16_31_stream;
> +};
> +
> +struct fepci_card_private {
> + unsigned int card_number;
> + u8 *ioaddr;
> + /* Process ID of the current mailbox user
> + * (for whom it is reserved for) */
> + unsigned int ioctl_saved_pid;
> + struct pci_dev *pci_dev;
> + struct fepci_ch_private *ch_privates[CHANNELS];
> +
> + wait_queue_head_t alarm_manager_wait_q;
> + struct timer_list mailbox_timer;
> +
> + wait_queue_head_t stream_receive_q;
> + wait_queue_head_t stream_transmit_q;
> + wait_queue_head_t stream_both_q;
> +
> + struct rw_semaphore semaphore;
> +};
> +
> +/* Offsets to the FEPCI registers */
> +enum fepci_offsets {
> + reg_custom = 0x40,
> +
> + reg_first_int_mask = 0x80,
> + reg_first_int_status = 0xc0,
> +
> + reg_first_rxctrl = 0x4000,
> + to_next_rxctrl = 0x80,
> +
> + reg_first_txctrl = 0x6000,
> + to_next_txctrl = 0x80,
> +
> + first_rx_desc = 0x10000,
> + to_next_ch_rx_desc = 0x200,
> +
> + first_tx_desc = 0x18000,
> + to_next_ch_tx_desc = 0x200,
> +};
> +
> +enum reg_custom_bits {
> + AM_interrupt_mask = 0x1,
> + AM_interrupt_status = 0x100,
> +};
> +
> +enum reg_receive_control {
> + Rx_fifo_threshold = 0x7,
> + Receive_enable = 0x80000000,
> +};
> +
> +enum reg_transmit_control {
> + Tx_fifo_threshold = 0x7,
> + Tx_desc_threshold = 0x700,
> + Transmit_enable = 0x80000000,
> +};
> +
> +enum int_bits {
> + MaskFrameReceived = 0x01, MaskRxFifoError =
> + 0x02, MaskRxFrameDroppedError = 0x04,
> + MaskFrameTransmitted = 0x40, MaskTxFifoError = 0x80,
> + MaskAllInts = 0xc7,
> + IntrFrameReceived = 0x01, IntrRxFifoError =
> + 0x02, IntrRxFrameDroppedError = 0x04,
> + IntrFrameTransmitted = 0x40, IntrTxFifoError = 0x80,
> + IntrAllInts = 0xc7,
> +};
> +
> +/* The FEPCI Rx and Tx buffer descriptors
> + * Elements are written as 32 bit for endian portability */
> +
> +struct fepci_desc {
> + u32 desc_a;
> + u32 desc_b;
> +};
> +
> +enum desc_b_bits {
> + frame_length = 0xFFF,
> + fifo_error = 0x10000,
> + size_error = 0x20000,
> + crc_error = 0x40000,
> + octet_error = 0x80000,
> + line_error = 0x100000,
> + enable_transfer = 0x80000000,
> + transfer_not_done = 0x80000000,
> +};
> +
> +/* global variables (common to whole driver, all the cards): */
> +int major; /* char device major number */
Why are you using regular major/minor for a network device?
Any local variable like this has to be declared static so
as not to pollute the namespace of the whole kernel.
> +struct fepci_card_private card_privates[MAX_DEVICES];
> +unsigned long stream_pointers;
> +struct proc_dir_entry *proc_root_entry;
More name space pollution
> +void set_int_mask(int channel, u_char value, struct fepci_card_private *cp)
> +{
> + void *address;
> + unsigned shift, oldvalue;
> + DBG_PRINT("set_int_mask\n");
> + address = (cp->ioaddr) + reg_first_int_mask + (channel / 4L) * 4L;
> + shift = 8L * (channel % 4L);
> + oldvalue = readl((void *)address);
> + oldvalue &= ~(0xff << shift); /* clear bits */
> + oldvalue |= value << shift; /* set bits */
> + writel(oldvalue, (void *)address);
> +}
All these have to be declared static.
> +u_char get_int_mask(int channel, void *ioaddr)
> +{
> + void *address;
> + unsigned shift, oldvalue;
> + DBG_PRINT("get_int_mask\n");
> + address = ioaddr + reg_first_int_mask + (channel / 4L) * 4L;
> + shift = 8L * (channel % 4L);
> + oldvalue = readl((void *)address);
> + oldvalue &= (0xff << shift); /* clear other bits */
> + return (oldvalue >> shift);
> +}
> +
> +void clear_int(int channel, u_char value, void *ioaddr)
> +{
> + void *address;
> + unsigned shift, longvalue;
> + DBG_PRINT("clear_int\n");
> + address = ioaddr + reg_first_int_status + (channel / 4L) * 4L;
> + shift = 8L * (channel % 4L);
> + longvalue = value << shift;
> + writel(~longvalue, (void *)address);
> +}
> +
> +u_char get_int_status(int channel, void *ioaddr)
> +{
> + void *address;
> + unsigned shift, oldvalue;
> + DBG_PRINT("get_int_status\n");
> + address = ioaddr + reg_first_int_status + (channel / 4L) * 4L;
> + shift = 8L * (channel % 4L);
> + oldvalue = readl((void *)address);
> + oldvalue &= (0xff << shift); /* clear other bits */
> + return (oldvalue >> shift);
> +}
> +
> +void fillregisterswith_00(void *ioaddr)
> +{
> + DBG_PRINT("fillregisterswith_00\n");
> + writel(0x0, (void *)(ioaddr + reg_first_rxctrl));
> + writel(0x0, (void *)(ioaddr + reg_first_txctrl));
> + writel(0x0, (void *)(ioaddr + reg_first_int_mask));
> + writel(0x0, (void *)(ioaddr + reg_first_int_status));
> + writel(0x0, (void *)(ioaddr + first_rx_desc));
> + writel(0x0, (void *)(ioaddr + first_tx_desc));
> +}
> +
> +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
> +static int fepci_open(struct net_device *dev);
> +static void fepci_timer(unsigned long data);
> +static void fepci_tx_timeout(struct net_device *dev);
> +static void fepci_init_ring(struct net_device *dev);
> +static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev);
> +static irqreturn_t fepci_interrupt(int irq, void *dev_instance);
> +static int fepci_rx(struct net_device *dev);
> +static int fepci_close(struct net_device *dev);
> +static struct net_device_stats *fepci_get_stats(struct net_device *dev);
> +static void set_rx_mode(struct net_device *dev);
> +static void fepci_remove_one(struct pci_dev *pdev);
> +
> +/* proc filesystem functions introduced: */
> +
> +static int fepci_proc_init_driver(void);
> +static void fepci_proc_cleanup_driver(void);
> +static void fepci_proc_init_card(int card_number, void *card_data);
> +static void fepci_proc_cleanup_card(int card_number);
> +#ifdef DEBUG_PROC_FILES
> +static void fepci_proc_init_channel(int card_number, int channel_number,
> + void *channel_data);
> +static void fepci_proc_cleanup_channel(int card_number, int channel_number);
> +#endif /* DEBUG_PROC_FILES */
> +
> +/* char device operations: */
> +
> +ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
> + loff_t *f_pos);
> +int fepci_char_open(struct inode *inode, struct file *filp);
> +int fepci_char_release(struct inode *inode, struct file *filp);
> +int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma);
> +int fepci_char_ioctl(struct inode *inode, struct file *filp,
> + unsigned int cmd, unsigned long arg);
> +
> +struct file_operations fepci_char_fops = {
> +read: fepci_char_read,
> +ioctl: fepci_char_ioctl,
> +open: fepci_char_open,
> +release: fepci_char_release,
> +mmap: fepci_char_mmap,
> +};
Please use C99 style initializers not old Gcc style.
> +int fepci_char_open(struct inode *inode, struct file *filp)
> +{
> + unsigned int minor = MINOR(inode->i_rdev);
> + DBG_PRINT("fepci_char_open\n");
> + if (unlikely(minor >= find_cnt || card_privates[minor].pci_dev == NULL))
> + return -ENXIO;
> + filp->f_op = &fepci_char_fops;
> + if (unlikely(!try_module_get(THIS_MODULE)))
> + return -EBUSY;
> + return 0;
> +}
If you use owner properly on proc files, you won't need to be
dorking with module ref counts.
> +
> +int fepci_char_release(struct inode *inode, struct file *filp)
> +{
> + DBG_PRINT("fepci_char_release\n");
> + module_put(THIS_MODULE);
> + return 0;
> +}
> +
> +void fepci_vma_open(struct vm_area_struct *vma)
> +{
> + DBG_PRINT("fepci_vma_open\n");
> +}
> +
> +void fepci_vma_close(struct vm_area_struct *vma)
> +{
> + DBG_PRINT("fepci_vma_close\n");
> + module_put(THIS_MODULE);
> +}
> +
> +static struct vm_operations_struct fepci_vm_ops = {
> +open: fepci_vma_open,
> +close: fepci_vma_close,
> +};
> +
> +int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
> + unsigned long size = vma->vm_end - vma->vm_start;
> +
> + unsigned long virtual_address = 0;
> +
> + vma->vm_flags |= VM_IO | VM_RESERVED;
> + vma->vm_ops = &fepci_vm_ops;
> + vma->vm_file = filp;
> +
> + if (offset == STREAM_BUFFER_POINTER_AREA) {
> + virtual_address = stream_pointers;
> + if (virtual_address == 0) {
> + printk(KERN_WARNING "%s: mmap: internal error.\n",
> + fepci_name);
> + return -ENOMEM;
> + }
> + if (size > (1 << PAGE_SHIFT)) {
> + printk(KERN_WARNING
> + "%s: mmap: area size over range.\n", fepci_name);
> + return -EINVAL;
> + }
> + } else {
> + unsigned int page;
> +
> + unsigned int card;
> + unsigned int channel;
> + unsigned int area; /* 0=rx, 1=tx */
> +
> + card = (offset >> CARD_ADDRESS_SHIFT) & 0xf;
> + channel = (offset >> CHANNEL_ADDRESS_SHIFT) & 0xf;
> + area = (offset >> AREA_ADDRESS_SHIFT) & 0xf;
> + page = (offset & 0xffff); /* >> PAGE_SHIFT; */
> +
> + if (area == 0) {
> + /* if there really is such card */
> + if (card < find_cnt && card_privates[card].pci_dev)
> + virtual_address =
> + (unsigned long)card_privates[card].
> + ch_privates[channel]->rx_buffer;
> + else
> + goto INVALID;
> + } else if (area == 1) {
> + /* if there really is such card */
> + if (card < find_cnt && card_privates[card].pci_dev)
> + virtual_address =
> + (unsigned long)card_privates[card].
> + ch_privates[channel]->tx_buffer;
> + else
> + goto INVALID;
> + } else {
> +INVALID:
> + DBG_PRINT("%s: mmap: invalid address 0x%lx\n",
> + fepci_NAME, virtual_address);
> + return -EINVAL;
> + }
> + if (virtual_address == 0)
> + goto INVALID;
> + }
> +
> + if (!try_module_get(THIS_MODULE))
> + return -EBUSY;
> +
> + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> + {
> + unsigned pfn = PFN_DOWN(virt_to_phys((void *)virtual_address));
> + int error = io_remap_pfn_range(vma, vma->vm_start, pfn,
> + size, vma->vm_page_prot);
> + if (unlikely(error))
> + return error;
> + }
> + fepci_vma_open(vma);
> + return 0;
> +}
> +
> +/* mmap operations end */
> +
> +/* char operations start */
> +
> +void fepci_copy_to_user(unsigned long to, void *from, unsigned long len,
> + int shrink)
> +{
> + unsigned int i;
> + DBG_PRINT("fepci_copy_to_user\n");
> + if (shrink) {
> + for (i = 0; i < len; i += 2) {
> + put_user((((unsigned long *)from)[i / 2]) & 0xff,
> + (unsigned char *)(to + i));
> + put_user((((unsigned long *)from)[i / 2]) >> 8,
> + (unsigned char *)(to + i + 1));
> + }
> + } else {
> + for (i = 0; i < len; i += 4)
> + put_user(((unsigned long *)from)[i / 4],
> + (unsigned long *)(to + i));
> + }
> +}
> +
> +void fepci_copy_from_user(void *to, unsigned long from, unsigned long len,
> + int enlarge)
> +{
> + unsigned int i;
> + if (enlarge) {
> + for (i = 0; i < len; i += 2) {
> + unsigned char temp1;
> + unsigned char temp2;
> + get_user(temp1, (unsigned char *)(from + i));
> + get_user(temp2, (unsigned char *)(from + i + 1));
> + *((unsigned long *)(to + i * 2)) = temp1 + (temp2 << 8);
> + }
> + } else {
> + for (i = 0; i < len; i += 4)
> + get_user(((unsigned long *)to)[i / 4],
> + (unsigned long *)(from + i));
> + }
> +}
> +
> +unsigned get_semafore(struct fepci_real_mailbox *mailbox)
> +{
> + unsigned semafore = readb(&mailbox->Semafore_Mail_number);
> + DBG_PRINT("get_semafore = %x\n", semafore);
> + return semafore;
> +}
> +
> +int set_semafore(struct fepci_real_mailbox *mailbox, unsigned semafore)
> +{
> + unsigned number = readl(&mailbox->Semafore_Mail_number);
> + DBG_PRINT("got number %u at %p.\n", number,
> + &mailbox->Semafore_Mail_number);
> + number = ((number & ~0xFF) | semafore) + (1 << 8);
> + DBG_PRINT
> + ("increases the mail number to %u at the same time at %p.\n",
> + number, &mailbox->Semafore_Mail_number);
> + writel(number, &mailbox->Semafore_Mail_number);
> + DBG_PRINT("set_semafore %p, %u returns 0.\n", mailbox, semafore);
> + return 0;
> +}
> +
> +static void fepci_mailbox_timer(unsigned long data)
> +{
> + int card_number = data;
> + unsigned int *saved_pid = &card_privates[card_number].ioctl_saved_pid;
> + void *ioaddr = card_privates[card_number].ioaddr;
> + struct fepci_real_mailbox *real_mailbox =
> + (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
> +
> + set_semafore(real_mailbox, 0x0);
> + *saved_pid = 0;
> +}
> +
> +int fepci_char_ioctl(struct inode *inode, struct file *filp,
> + unsigned int cmd, unsigned long arg)
> +{
> + unsigned int minor = MINOR(inode->i_rdev);
> + void *ioaddr;
> + struct fepci_real_mailbox *real_mailbox;
> + int retval = 0;
> + unsigned int *saved_pid;
> + unsigned int my_pid;
> +
> + if (minor >= find_cnt || card_privates[minor].pci_dev == NULL) {
> + printk(KERN_WARNING
> + "%s: trying to access a card that does not exist.\n",
> + fepci_NAME);
> + /* if trying to access a card that does not exist */
> + return -ENXIO;
> + }
> +
> + DBG_PRINT("fepci_char_ioctl minor %u.\n", minor);
> +
> + if (_IOC_DIR(cmd) & _IOC_READ)
> + if (!access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)))
> + return -EFAULT;
> + if (_IOC_DIR(cmd) & _IOC_WRITE)
> + if (!access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)))
> + return -EFAULT;
> +
> + ioaddr = card_privates[minor].ioaddr;
> + real_mailbox =
> + (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
> + saved_pid = &card_privates[minor].ioctl_saved_pid;
> + my_pid = current->pid;
> +
> + switch (cmd) {
> + case FEPCI_IOCTL_STREAM_TRANSMIT_POLL:
> + /* here: arg == channel number */
> + if (arg < 0 || arg >= CHANNELS
> + || !(card_privates[minor].ch_privates[arg]->stream_on))
> + return 0x2;
> + {
> + u32 pointer = *USER_TX_S_FAKE_POINTER(minor, arg,
> + stream_pointers);
> + wait_event_interruptible((card_privates[minor].
> + stream_transmit_q)
> + ,
> + (pointer !=
> + *USER_TX_S_FAKE_POINTER
> + (minor, arg,
> + stream_pointers)));
> + return 0x1;
> + }
> + return retval;
> + case FEPCI_IOCTL_STREAM_RECEIVE_POLL:
> + /* here: arg == channel number */
> + if (arg < 0 || arg >= CHANNELS
> + || !(card_privates[minor].ch_privates[arg]->stream_on))
> + return 0x2;
> + {
> + u32 pointer = *USER_RX_S_FAKE_POINTER(minor, arg,
> + stream_pointers);
> + wait_event_interruptible((card_privates[minor].
> + stream_receive_q)
> + ,
> + (pointer !=
> + *USER_RX_S_FAKE_POINTER
> + (minor, arg,
> + stream_pointers)));
> + retval = 0x1;
> + }
> + return retval;
> + case FEPCI_IOCTL_STREAM_BOTH_POLL:
> + /* here: arg == channel number */
> + if (arg < 0 || arg >= CHANNELS
> + || !(card_privates[minor].ch_privates[arg]->stream_on))
> + return 0x2;
> + {
> + u32 temp_tx_pointer =
> + *USER_TX_S_FAKE_POINTER(minor, arg,
> + stream_pointers);
> + u32 temp_rx_pointer =
> + *USER_RX_S_FAKE_POINTER(minor, arg,
> + stream_pointers);
> +
> + wait_event_interruptible((card_privates[minor].
> + stream_both_q)
> + ,
> + (temp_tx_pointer !=
> + *USER_TX_S_FAKE_POINTER
> + (minor, arg, stream_pointers))
> + || (temp_rx_pointer !=
> + *USER_RX_S_FAKE_POINTER
> + (minor, arg,
> + stream_pointers)));
> + retval = 0x1;
> + }
> + return retval;
> + case FEPCI_IOCTL_R_SHARED_MEM:
> + DBG_PRINT(" %s: ioctl read shared mem commanded.\n",
> + fepci_NAME);
> + fepci_copy_to_user(arg, ioaddr + FEPCI_SHARED_MEM_OFFSETT,
> + _IOC_SIZE(cmd), 0);
> + break;
> + case FEPCI_IOCTL_W_SHARED_MEM:
> + DBG_PRINT(" %s: ioctl write shared mem commanded.\n",
> + fepci_NAME);
> + fepci_copy_from_user(ioaddr + FEPCI_SHARED_MEM_OFFSETT,
> + arg, _IOC_SIZE(cmd), 0);
> + break;
> + case FEPCI_IOCTL_G_IDENTIFICATION:
> + DBG_PRINT(" %s: IOCTL_G_IDENTIFICATION commanded.\n",
> + fepci_NAME);
> + fepci_copy_to_user(arg,
> + ioaddr + FEPCI_IDENTIFICATION_OFFSETT,
> + _IOC_SIZE(cmd), 1);
> + break;
> + case FEPCI_IOCTL_G_FEATURES:
> + DBG_PRINT(" %s: IOCTL_G_FEATURES commanded.\n", fepci_NAME);
> + fepci_copy_to_user(arg, ioaddr + FEPCI_FEATURES_OFFSETT,
> + _IOC_SIZE(cmd), 1);
> + break;
> + case FEPCI_IOCTL_G_SETTINGS:
> + DBG_PRINT(" %s: IOCTL_G_SETTINGS commanded.\n", fepci_NAME);
> + fepci_copy_to_user(arg, ioaddr + FEPCI_SETTINGS_OFFSETT,
> + _IOC_SIZE(cmd), 1);
> + break;
> + case FEPCI_IOCTL_G_STATUS:
> + DBG_PRINT(" %s: IOCTL_G_STATUS commanded.\n", fepci_NAME);
> + fepci_copy_to_user(arg, ioaddr + FEPCI_STATUS_OFFSETT,
> + _IOC_SIZE(cmd), 1);
> + break;
> + case FEPCI_IOCTL_B_POLL:
> + DBG_PRINT(" %s: IOCTL_B_POLL commanded.\n", fepci_NAME);
> + retval = get_semafore(real_mailbox);
> + if ((retval == 0x20 || retval == 0x21 || retval == 0x40)
> + && *saved_pid != my_pid)
> + retval = 0x7;
> + del_timer_sync(&card_privates[minor].mailbox_timer);
> + card_privates[minor].mailbox_timer.expires = jiffies + 20 * HZ;
> + card_privates[minor].mailbox_timer.data = minor;
> + card_privates[minor].mailbox_timer.function =
> + &fepci_mailbox_timer;
> + add_timer(&card_privates[minor].mailbox_timer);
> + break;
> + case FEPCI_IOCTL_B_GRAB:
> + DBG_PRINT("%s: IOCTL_B_GRAB commanded.\n", fepci_NAME);
> + if ((my_pid != *saved_pid) && (*saved_pid != 0)) {
> + retval = 0x2;
> + break;
> + }
> + DBG_PRINT("%s: IOCTL_B_GRAB getting semaphore.\n", fepci_NAME);
> + if (get_semafore(real_mailbox) == 0x0) {
> + DBG_PRINT("%s: IOCTL_B_GRAB setting semaphore.\n",
> + fepci_NAME);
> + set_semafore(real_mailbox, 0x40);
> + DBG_PRINT("%s: IOCTL_B_GRAB sleeping.\n", fepci_NAME);
> + msleep(1); /* delay at least 1 millisecond */
> + DBG_PRINT
> + ("%s: IOCTL_B_GRAB getting semaphore again.\n",
> + fepci_NAME);
> + switch (get_semafore(real_mailbox)) {
> + case 0x40:
> + retval = 0x0;
> + DBG_PRINT
> + ("%s: IOCTL_B_GRAB saving pid to %p.\n",
> + fepci_NAME, saved_pid);
> + *saved_pid = my_pid;
> + DBG_PRINT
> + ("%s: IOCTL_B_GRAB deleting timer at %p.\n",
> + fepci_NAME,
> + &card_privates[minor].mailbox_timer);
> + del_timer_sync(&card_privates[minor].
> + mailbox_timer);
> + card_privates[minor].mailbox_timer.
> + expires = jiffies + 20 * HZ;
> + card_privates[minor].mailbox_timer.data = minor;
> + card_privates[minor].mailbox_timer.
> + function = &fepci_mailbox_timer;
> + add_timer(&card_privates[minor].mailbox_timer);
> + break;
> + case 0x10:
> + case 0x11:
> + case 0x80:
> + retval = 0x1;
> + break;
> + default:
> + retval = 0xff;
> + }
> + } else {
> + switch (get_semafore(real_mailbox)) {
> + case 0x10:
> + case 0x11:
> + case 0x80:
> + retval = 0x1;
> + break;
> + default:
> + retval = 0xff;
> + }
> + }
> + break;
> + case FEPCI_IOCTL_B_RELEASE:
> + DBG_PRINT(" %s: IOCTL_B_RELEASE commanded.\n", fepci_NAME);
> + if (my_pid != *saved_pid) {
> + retval = 0x2;
> + break;
> + }
> + switch (get_semafore(real_mailbox)) {
> + case 0x40:
> + case 0x20:
> + retval = 0x0;
> + set_semafore(real_mailbox, 0x0);
> + *saved_pid = 0;
> + del_timer(&card_privates[minor].mailbox_timer);
> + break;
> + case 0x21:
> + retval = 0x04;
> + break;
> + case 0x10:
> + case 0x11:
> + case 0x80:
> + retval = 0x1;
> + break;
> + default:
> + retval = 0xff;
> + }
> + break;
> + case FEPCI_IOCTL_B_S_CMAIL:
> + DBG_PRINT(" %s: IOCTL_B_S_CMAIL commanded.\n", fepci_NAME);
> + if (my_pid != *saved_pid) {
> + retval = 0x2;
> + break;
> + }
> + switch (get_semafore(real_mailbox)) {
> + case 0x40:
> + case 0x20:
> + case 0x21:
> + /* copy the mailbox */
> + fepci_copy_from_user(ioaddr +
> + FEPCI_MAILBOX_OFFSETT + 4,
> + arg + 2, _IOC_SIZE(cmd) - 2, 1);
> + /* semafore -> 10 */
> + set_semafore(real_mailbox, 0x10);
> + retval = 0x0;
> +
> + del_timer_sync(&card_privates[minor].mailbox_timer);
> + card_privates[minor].mailbox_timer.expires =
> + jiffies + 20 * HZ;
> + card_privates[minor].mailbox_timer.data = minor;
> + card_privates[minor].mailbox_timer.function =
> + &fepci_mailbox_timer;
> + add_timer(&card_privates[minor].mailbox_timer);
> +
> + break;
> +
> + case 0x10:
> + case 0x11:
> + case 0x80:
> + retval = 0x1;
> + break;
> + case 0x0:
> + retval = 0x3;
> + break;
> + default:
> + retval = 0xff;
> + }
> + break;
> + case FEPCI_IOCTL_B_S_QMAIL:
> + DBG_PRINT(" %s: IOCTL_B_S_QMAIL commanded.\n", fepci_NAME);
> + if (my_pid != *saved_pid) {
> + retval = 0x2;
> + break;
> + }
> + switch (get_semafore(real_mailbox)) {
> + case 0x40:
> + case 0x20:
> + case 0x21:
> + /* copy the mailbox; */
> + fepci_copy_from_user(ioaddr +
> + FEPCI_MAILBOX_OFFSETT + 4,
> + arg + 2, _IOC_SIZE(cmd) - 2, 1);
> + /* semafore -> 11 */
> + set_semafore(real_mailbox, 0x11);
> + retval = 0x0;
> +
> + del_timer_sync(&card_privates[minor].mailbox_timer);
> + card_privates[minor].mailbox_timer.expires =
> + jiffies + 20 * HZ;
> + card_privates[minor].mailbox_timer.data = minor;
> + card_privates[minor].mailbox_timer.function =
> + &fepci_mailbox_timer;
> + add_timer(&card_privates[minor].mailbox_timer);
> +
> + break;
> +
> + case 0x10:
> + case 0x11:
> + case 0x80:
> + retval = 0x1;
> + break;
> + case 0x0:
> + retval = 0x3;
> + break;
> + default:
> + retval = 0xff;
> + }
> + break;
> + case FEPCI_IOCTL_B_G_MAIL:
> + DBG_PRINT(" %s: IOCTL_B_G_MAIL commanded.\n", fepci_NAME);
> + if (my_pid != *saved_pid) {
> + retval = 0x2;
> + } else {
> + switch (get_semafore(real_mailbox)) {
> + case 0x10:
> + case 0x11:
> + case 0x80:
> + retval = 0x1;
> + break;
> + case 0x40:
> + case 0x20:
> + case 0x21:
> + retval = 0x0;
> + fepci_copy_to_user(arg,
> + ioaddr +
> + FEPCI_MAILBOX_OFFSETT,
> + _IOC_SIZE(cmd), 1);
> +
> + del_timer_sync(&card_privates[minor].
> + mailbox_timer);
> + card_privates[minor].mailbox_timer.
> + expires = jiffies + 20 * HZ;
> + card_privates[minor].mailbox_timer.data = minor;
> + card_privates[minor].mailbox_timer.
> + function = &fepci_mailbox_timer;
> + add_timer(&card_privates[minor].mailbox_timer);
> +
> + break;
> + case 0x0:
> + retval = 0x3;
> + break;
> + default:
> + retval = 0xff;
> + }
> + }
> + if (retval != 0) {
> + static unsigned char seven = 7;
> + /* copy four lowest bytes from the mailbox */
> + fepci_copy_to_user(arg,
> + ioaddr + FEPCI_MAILBOX_OFFSETT,
> + 4, 1);
> + /* lowest byte = 0x7 */
> + __put_user(arg, &seven);
> + }
> + break;
> + case FEPCI_IOCTL_ALARM_MANAGER:
> + DBG_PRINT(" %s: IOCTL_ALARM_MANAGER commanded.\n", fepci_NAME);
> + interruptible_sleep_on(&(card_privates[minor].
> + alarm_manager_wait_q));
> + return retval;
> + default:
> + DBG_PRINT(" %s: Unknown ioctl command 0x%x.\n", fepci_NAME,
> + cmd);
> + return -ENOTTY;
> + }
> + return retval;
> +}
> +
> +ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
> + loff_t *f_pos)
> +{
> + DBG_PRINT("fepci_char_read\n");
> + if (count > 1)
> + count = 1;
> + if (unlikely(copy_to_user(buf, "\n", count)))
> + return -EFAULT;
> + return count;
> +}
> +
> +static int fepci_register_char_device(void)
> +{
> + int error =
> + register_chrdev(0 /* dynamic */ , fepci_name, &fepci_char_fops);
> + if (unlikely(error < 0))
> + printk(KERN_WARNING
> + "%s: unable to register char device.\n", fepci_NAME);
> + else
> + DBG_PRINT("%s: registered char device, major:0x%x.\n",
> + fepci_NAME, error);
> + return error;
> +}
> +
> +void fepci_unregister_char_device(void)
> +{
> + unregister_chrdev(major, fepci_name);
> +}
> +
> +/* char operations end */
> +
> +/* stream operations start */
> +
> +static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance);
> +static int fepci_stream_close(struct net_device *dev);
> +
> +static int fepci_stream_open(struct net_device *dev)
> +{
> + struct fepci_ch_private *fp = dev->priv;
> + unsigned tx_pages, rx_pages, tx_order, rx_order;
> + unsigned page_number;
> + unsigned int i;
> +
> + down_write(&fp->this_card_priv->semaphore);
> + if (fp->in_eth_mode) {
> + up_write(&fp->this_card_priv->semaphore);
> + printk(KERN_WARNING
> + "%s: Interface is in Ethernet mode, "
> + "cannot open stream interface.\n", fepci_NAME);
> +BUSY:
> + return -EBUSY;
> + }
> + if (fp->in_stream_mode) {
> + up_write(&fp->this_card_priv->semaphore);
> + printk(KERN_WARNING
> + "%s: Interface is already in stream mode, "
> + "cannot open stream interface.\n", fepci_NAME);
> + goto BUSY;
> + }
> +
> + if (unlikely(fp->this_card_priv->pci_dev == NULL)) {
> + up_write(&fp->this_card_priv->semaphore);
> + return -ENXIO;
> + }
> +
> + fp->bufsize = 1 << fp->bufsize_order;
> +
> + if (fp->fake_unit_sz_order < 5) {
> + up_write(&fp->this_card_priv->semaphore);
> + printk(KERN_WARNING
> + "%s: Unit size has to be at least 32 Bytes.\n",
> + fepci_NAME);
> +INVALID:
> + return (-EINVAL);
> + }
> +
> + if (fp->fake_unit_sz_order >= fp->bufsize_order) {
> + up_write(&fp->this_card_priv->semaphore);
> + printk(KERN_WARNING
> + "%s: Bufsize has to be greater than unit size.\n",
> + fepci_NAME);
> + goto INVALID;
> + }
> +
> + if (fp->fake_unit_sz_order >= MAX_UNIT_SZ_ORDER) {
> + fp->unit_sz_order = MAX_UNIT_SZ_ORDER;
> + } else {
> + fp->unit_sz_order = fp->fake_unit_sz_order;
> + }
> +
> + fp->fake_unit_sz = 1 << fp->fake_unit_sz_order;
> + fp->unit_sz = 1 << fp->unit_sz_order;
> + fp->units = 1 << (fp->bufsize_order - fp->unit_sz_order);
> + fp->fake_units = 1 << (fp->bufsize_order - fp->fake_unit_sz_order);
> +
> + /* reserve memory */
> + if (fp->bufsize_order < PAGE_SHIFT) {
> + rx_order = 0;
> + tx_order = 0;
> + rx_pages = 1;
> + tx_pages = 1;
> + } else {
> + tx_order = fp->bufsize_order - PAGE_SHIFT;
> + tx_pages = 1 << tx_order;
> + rx_order = tx_order + 1;
> + rx_pages = 1 << rx_order;
> + }
> + fp->in_stream_mode = 1;
> + fp->tx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, tx_order);
> + if (!fp->tx_buffer)
> + goto NO_MEMORY;
> + fp->rx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, rx_order);
> + if (!fp->rx_buffer) {
> +NO_MEMORY:
> + up_write(&fp->this_card_priv->semaphore);
> + printk(KERN_WARNING
> + "%s: unable to allocate memory for buffers.\n",
> + fepci_NAME);
> + fepci_stream_close(dev);
> + return (-ENOMEM);
> + }
> +
> + for (page_number = 0; page_number < rx_pages; page_number++)
> + /* make pages reserved to allow remappping pages
> + with io_remap_pfn_range */
> + SetPageReserved(virt_to_page
> + ((unsigned long)fp->rx_buffer +
> + (page_number << PAGE_SHIFT)));
> + for (page_number = 0; page_number < tx_pages; page_number++)
> + /* make pages reserved to allow remappping pages
> + with io_remap_pfn_range */
> + SetPageReserved(virt_to_page
> + ((unsigned long)fp->tx_buffer +
> + (page_number << PAGE_SHIFT)));
> +
> + for (i = 0; i < (fp->bufsize) / 4; i++)
> + fp->tx_buffer[i] = 0xffffffff;
> +
> + /* + fp->channel_number; */
> + *USER_RX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
> + stream_pointers) = 0;
> + /* + fp->channel_number; */
> + *USER_TX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
> + stream_pointers) = 0;
> + /* + fp->channel_number; */
> + *USER_RX_S_FAKE_POINTER(fp->this_card_priv->card_number,
> + fp->channel_number, stream_pointers) = 0;
> + /* + fp->channel_number; */
> + *USER_TX_S_FAKE_POINTER(fp->this_card_priv->card_number,
> + fp->channel_number, stream_pointers) = 0;
> +
> + DBG_PRINT("%s: Bufsize is 0x%x.\n", fepci_NAME, fp->bufsize);
> + DBG_PRINT("%s: Unit_size is 0x%x.\n", fepci_NAME, fp->unit_sz);
> + DBG_PRINT("%s: Number of units is 0x%x.\n", fepci_NAME, fp->units);
> +
> + DBG_PRINT("%s: Fake_unit_size is 0x%x.\n", fepci_NAME,
> + fp->fake_unit_sz);
> + DBG_PRINT("%s: Number of fake units is 0x%x.\n", fepci_NAME,
> + fp->fake_units);
> +
> + /* init ring buffers */
> + for (i = 0; i < MAX_RX_UNITS; i++)
> + fp->rx_unit[i] =
> + (u32 *) ((u32) (fp->rx_buffer) + (fp->unit_sz * i));
> + for (i = 0; i < MAX_TX_UNITS; i++)
> + fp->tx_unit[i] =
> + (u32 *) ((u32) (fp->tx_buffer) + (fp->unit_sz * i));
> +
> + for (i = 0; i < RX_RING_SIZE; i++) {
> + writel(0, &fp->rx_desc[i].desc_a);
> + writel(0, &fp->rx_desc[i].desc_b);
> + }
> + for (i = 0; i < TX_RING_SIZE; i++) {
> + writel(0, &fp->tx_desc[i].desc_a);
> + writel(0, &fp->tx_desc[i].desc_b);
> + }
> +
> + up_write(&fp->this_card_priv->semaphore);
> + return 0;
> +}
> +
> +static int fepci_stream_start(struct net_device *dev)
> +{
> + struct fepci_ch_private *fp = dev->priv;
> + unsigned i;
> + down_write(&fp->this_card_priv->semaphore);
> +
> + if (fp->in_stream_mode == 0) {
> + up_write(&fp->this_card_priv->semaphore);
> + printk(KERN_WARNING
> + "%s: Interface is not in stream mode, "
> + "streaming cannot be started.\n", fepci_NAME);
> + return (-EBUSY);
> + }
> + if (fp->stream_on) {
> + up_write(&fp->this_card_priv->semaphore);
> + printk(KERN_WARNING
> + "%s: Streaming is already on, "
> + "streaming cannot be started.\n", fepci_NAME);
> + return (-EBUSY);
> + }
> +
> + {
> + /* reserve irq */
> + int error = request_irq(dev->irq, &fepci_stream_interrupt,
> + IRQF_SHARED, dev->name, dev);
> + if (error) {
> + up_write(&fp->this_card_priv->semaphore);
> + printk(KERN_WARNING
> + "%s: unable to allocate IRQ %d, error 0x%x\n",
> + fepci_NAME, dev->irq, error);
> + return -ENOMEM;
> + }
> + }
> +
> + fp->stream_on = 1;
> +
> + /* sending &receiving on, start from the beginning of the buffer */
> + fp->cur_tx_unit = 0;
> + fp->cur_rx_unit = 0;
> + fp->cur_tx = 0;
> + fp->cur_rx = 0;
> +
> + /* all the descriptors ready to go: */
> + for (i = 0; i < min(RX_RING_SIZE, TX_RING_SIZE); i++) {
> + dma_addr_t address = pci_map_single(fp->this_card_priv->pci_dev,
> + fp->
> + rx_unit[(fp->cur_rx_unit +
> + i) % fp->units],
> + fp->unit_sz,
> + PCI_DMA_FROMDEVICE);
> + if (unlikely(pci_dma_mapping_error(address)))
> + printk(KERN_WARNING
> + "%s: failed to map DMA buffer.\n", fepci_NAME);
> + else {
> + writel(address,
> + &fp->rx_desc[(fp->cur_rx + i) &
> + (RX_RING_SIZE - 1)].desc_a);
> + if (!
> + (readl
> + (&fp->
> + rx_desc[(fp->cur_rx + i) & (RX_RING_SIZE -
> + 1)].
> + desc_b) & enable_transfer))
> + writel(enable_transfer,
> + &fp->rx_desc[(fp->cur_rx + i) %
> + RX_RING_SIZE].desc_b);
> + }
> + address =
> + pci_map_single(fp->this_card_priv->pci_dev,
> + fp->tx_unit[(fp->cur_tx_unit + i) %
> + fp->units], fp->unit_sz,
> + PCI_DMA_TODEVICE);
> + writel(address,
> + &fp->
> + tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE - 1)].desc_a);
> + if (unlikely(pci_dma_mapping_error(address)))
> + printk(KERN_WARNING
> + "%s: failed to map DMA buffer.\n", fepci_NAME);
> + else {
> + if (!
> + (readl
> + (&fp->
> + tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE -
> + 1)].
> + desc_b) & enable_transfer))
> + writel(enable_transfer |
> + (fp->unit_sz & frame_length),
> + &fp->
> + tx_desc[(fp->cur_tx +
> + i) & (TX_RING_SIZE -
> + 1)].desc_b);
> + }
> + }
> +
> + /* irq on */
> + set_int_mask(fp->channel_number,
> + MaskFrameReceived | MaskFrameTransmitted |
> + MaskRxFifoError | MaskRxFrameDroppedError |
> + MaskTxFifoError, fp->this_card_priv);
> + {
> + void *ioaddr = (void *)dev->base_addr;
> + /* Start Rx and Tx channels */
> + writel(Receive_enable |
> + (Rx_fifo_threshold & RX_FIFO_THRESHOLD_STREAM_MODE),
> + (void *)(ioaddr + fp->reg_rxctrl));
> + writel((Transmit_enable |
> + (Tx_desc_threshold &
> + (TX_DESC_THRESHOLD_STREAM_MODE << 8)) |
> + (Tx_fifo_threshold & TX_FIFO_THRESHOLD_STREAM_MODE)),
> + (void *)(ioaddr + fp->reg_txctrl));
> + }
> + up_write(&fp->this_card_priv->semaphore);
> +
> + return 0;
> +}
> +
> +static int fepci_stream_stop(struct net_device *dev)
> +{
> + struct fepci_ch_private *fp = dev->priv;
> + void *ioaddr = (void *)dev->base_addr;
> + down_write(&fp->this_card_priv->semaphore);
> + if (fp->in_stream_mode == 0) {
> + up_write(&fp->this_card_priv->semaphore);
> + return (1);
> + }
> + fp->stream_on = 0;
> + /* Stop Rx and Tx channels. */
> + writel(0x0, (void *)(ioaddr + fp->reg_rxctrl));
> + writel(0x0, (void *)(ioaddr + fp->reg_txctrl));
> +
> + /* Disable interrupts by clearing the interrupt mask. */
> + set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
> +
> + /* unregister irq */
> + free_irq(dev->irq, dev);
> +
> + {
> + unsigned i = min(RX_RING_SIZE, TX_RING_SIZE) - 1;
> + do {
> + dma_addr_t bus_address = readl(&fp->rx_desc[i].desc_a);
> + if (likely(!pci_dma_mapping_error(bus_address)))
> + pci_unmap_single(fp->this_card_priv->
> + pci_dev, bus_address,
> + fp->unit_sz,
> + PCI_DMA_FROMDEVICE);
> + bus_address = readl(&fp->tx_desc[i].desc_a);
> + if (likely(!pci_dma_mapping_error(bus_address)))
> + pci_unmap_single(fp->this_card_priv->
> + pci_dev, bus_address,
> + fp->unit_sz, PCI_DMA_TODEVICE);
> + }
> + while (i--);
> + }
> +
> + up_write(&fp->this_card_priv->semaphore);
> + return 0;
> +}
> +
> +static int fepci_stream_close(struct net_device *dev)
> +{
> + struct fepci_ch_private *fp = dev->priv;
> + unsigned rx_pages, tx_pages, rx_order, tx_order;
> +
> + if (fepci_stream_stop(dev))
> + return -ENODEV;
> + down_write(&fp->this_card_priv->semaphore);
> + if (!(fp->in_stream_mode)) {
> + up_write(&fp->this_card_priv->semaphore);
> + return -ENODEV;
> + }
> + /* release memory */
> + if (fp->bufsize_order < PAGE_SHIFT) {
> + rx_order = 0;
> + tx_order = 0;
> + rx_pages = 1;
> + tx_pages = 1;
> + } else {
> + rx_order = (int)((fp->bufsize_order) - PAGE_SHIFT + 1);
> + rx_pages = 1 << rx_order;
> + tx_order = (int)((fp->bufsize_order) - PAGE_SHIFT);
> + tx_pages = 1 << tx_order;
> + }
> + if (fp->rx_buffer) {
> + unsigned page_number;
> + for (page_number = 0; page_number < rx_pages; page_number++)
> + /* turn pages back to non-reserved */
> + ClearPageReserved(virt_to_page
> + ((unsigned long)fp->rx_buffer +
> + (page_number << PAGE_SHIFT)));
> + free_pages((unsigned long)fp->rx_buffer, rx_order);
> + fp->rx_buffer = NULL;
> + }
> + if (fp->tx_buffer) {
> + unsigned page_number;
> + for (page_number = 0; page_number < tx_pages; page_number++)
> + /* turn pages back to non-reserved */
> + ClearPageReserved(virt_to_page
> + ((unsigned long)fp->tx_buffer +
> + (page_number << PAGE_SHIFT)));
> + free_pages((unsigned long)fp->tx_buffer, tx_order);
> + fp->tx_buffer = NULL;
> + }
> +
> + fp->in_stream_mode = 0;
> + up_write(&fp->this_card_priv->semaphore);
> + return 0;
> +}
> +
> +static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance)
> +{
> + struct net_device *dev = dev_instance;
> + struct fepci_ch_private *fp = dev->priv;
> + void *ioaddr = (void *)dev->base_addr;
> + unsigned intr_status = get_int_status(fp->channel_number, ioaddr);
> + bool fifo, dropped;
> + unsigned int temp_rx;
> + unsigned int temp_rx_unit;
> + unsigned int temp_tx;
> + unsigned int temp_tx_unit;
> +
> + clear_int(fp->channel_number, intr_status, ioaddr);
> +
> + /* debugging */
> + fp->interrupts_stream++;
> + fifo = (intr_status & IntrRxFifoError) != 0;
> + dropped = (intr_status & IntrRxFrameDroppedError) != 0;
> + fp->int_err_table_stream[fifo][dropped]++;
> + if (intr_status & IntrFrameReceived)
> + fp->rx_interrupts_stream++;
> + if (intr_status & IntrFrameTransmitted)
> + fp->tx_interrupts_stream++;
> + if (fifo)
> + fp->rx_int_fifo_err_stream++;
> + if (dropped)
> + fp->rx_int_frame_dropped_err_stream++;
> + if (intr_status & IntrTxFifoError)
> + fp->tx_int_fifo_err_stream++;
> + /* first update cur_rx, and do stuff if it has moved
> + (+ packets have been received) */
> + {
> + temp_rx = fp->cur_rx;
> + /* has been received */
> + while ((readl(&fp->rx_desc[fp->cur_rx].desc_b) &
> + transfer_not_done) == 0
> + /* stop if made one round */
> + && temp_rx != ((fp->cur_rx + 1) & (RX_RING_SIZE - 1))) {
> + dma_addr_t bus_address = readl(&fp->rx_desc[fp->cur_rx].
> + desc_a);
> + if (likely(!pci_dma_mapping_error(bus_address)))
> + pci_unmap_single(fp->this_card_priv->
> + pci_dev, bus_address,
> + fp->unit_sz,
> + PCI_DMA_FROMDEVICE);
> + fp->cur_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
> + fp->cur_rx_unit = (fp->cur_rx_unit + 1);
> + fp->cur_rx_unit *= fp->cur_rx_unit < fp->units;
> +
> + *USER_RX_S_POINTER(fp->this_card_priv->
> + card_number,
> + fp->channel_number,
> + stream_pointers) = fp->cur_rx_unit;
> + *USER_RX_S_FAKE_POINTER(fp->this_card_priv->
> + card_number,
> + fp->channel_number,
> + stream_pointers) =
> + fp->cur_rx_unit * fp->unit_sz / fp->fake_unit_sz;
> + wake_up_interruptible(&(fp->this_card_priv->
> + stream_receive_q));
> + wake_up_interruptible(&(fp->this_card_priv->
> + stream_both_q));
> + }
> + }
> + /* from the first uninitialized descriptor to cur_rx */
> + temp_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
> + temp_rx_unit = (fp->cur_rx_unit + 1);
> + temp_rx_unit *= temp_rx_unit < fp->units;
> +
> + while (temp_rx != fp->cur_rx) {
> + unsigned desc_b = readl(&fp->rx_desc[temp_rx].desc_b);
> + if ((desc_b & transfer_not_done) == 0) {
> + bool fifo = (desc_b & fifo_error) != 0;
> + bool size = (desc_b & size_error) != 0;
> + bool crc = (desc_b & crc_error) != 0;
> + bool octet = (desc_b & octet_error) != 0;
> + bool line = (desc_b & line_error) != 0;
> + unsigned length = desc_b & frame_length;
> + dma_addr_t bus_address;
> + /* update debug counters */
> + fp->rx_desc_err_table_stream[fifo]
> + [size]
> + [crc]
> + [octet]
> + [line]++;
> + if (length == 0)
> + fp->rx_packets_of_size_0_stream++;
> + else if (length == 1)
> + fp->rx_packets_of_size_1_stream++;
> + else if (length == 2)
> + fp->rx_packets_of_size_2_stream++;
> + else if (length == 3)
> + fp->rx_packets_of_size_3_stream++;
> + else if (length < 8)
> + fp->rx_packets_of_size_4_7_stream++;
> + else if (length < 16)
> + fp->rx_packets_of_size_8_15_stream++;
> + else if (length < 32)
> + fp->rx_packets_of_size_16_31_stream++;
> + if (fifo)
> + fp->rx_desc_fifo_err_stream++;
> + else if (size)
> + fp->rx_desc_size_err_stream++;
> + else if (crc)
> + fp->rx_desc_crc_err_stream++;
> + else if (octet)
> + fp->rx_desc_octet_err_stream++;
> + else if (line)
> + fp->rx_desc_line_err_stream++;
> + /* initialize the descriptor for transfer */
> + bus_address =
> + pci_map_single(fp->this_card_priv->pci_dev,
> + fp->rx_unit[temp_rx_unit],
> + fp->unit_sz, PCI_DMA_FROMDEVICE);
> + if (likely(!pci_dma_mapping_error(bus_address))) {
> + writel(bus_address,
> + &fp->rx_desc[temp_rx].desc_a);
> + writel(enable_transfer,
> + &fp->rx_desc[temp_rx].desc_b);
> + } else
> + printk(KERN_WARNING
> + "%s: failed to map DMA for reception.\n",
> + fepci_NAME);
> + }
> + temp_rx = (temp_rx + 1) & (RX_RING_SIZE - 1);
> + temp_rx_unit = (temp_rx_unit + 1);
> + temp_rx_unit *= temp_rx_unit < fp->units;
> + }
> +
> + /* first update cur_tx, and do stuff if it has moved
> + (+ packets have been transmitted) */
> + {
> + temp_tx = fp->cur_tx;
> + /* has been transmitted */
> + while ((readl(&fp->tx_desc[fp->cur_tx].desc_b) &
> + transfer_not_done) == 0
> + /* stop if made one round */
> + && temp_tx != ((fp->cur_tx + 1) & (TX_RING_SIZE - 1))) {
> + dma_addr_t bus_address = readl(&fp->tx_desc[fp->cur_tx].
> + desc_a);
> + if (likely(!pci_dma_mapping_error(bus_address)))
> + pci_unmap_single(fp->this_card_priv->
> + pci_dev, bus_address,
> + fp->unit_sz, PCI_DMA_TODEVICE);
> + fp->cur_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
> + fp->cur_tx_unit = (fp->cur_tx_unit + 1);
> + fp->cur_tx_unit *= fp->cur_tx_unit < fp->units;
> +
> + *USER_TX_S_POINTER(fp->this_card_priv->
> + card_number,
> + fp->channel_number,
> + stream_pointers) = fp->cur_tx_unit;
> + *USER_TX_S_FAKE_POINTER(fp->this_card_priv->
> + card_number,
> + fp->channel_number,
> + stream_pointers) =
> + fp->cur_tx_unit * fp->unit_sz / fp->fake_unit_sz;
> + wake_up_interruptible(&(fp->this_card_priv->
> + stream_transmit_q));
> + wake_up_interruptible(&(fp->this_card_priv->
> + stream_both_q));
> + }
> + }
> + /* from the first uninitialized descriptor to cur_tx */
> + temp_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
> + temp_tx_unit = (fp->cur_tx_unit + 1);
> + temp_tx_unit *= temp_tx_unit < fp->units;
> +
> + while (temp_tx != fp->cur_tx) {
> + unsigned desc_b = readl(&fp->tx_desc[temp_tx].desc_b);
> + if ((desc_b & transfer_not_done) == 0) {
> + dma_addr_t bus_address;
> + /* update debug counters */
> + if (desc_b & fifo_error)
> + fp->tx_desc_fifo_err_stream++;
> + /* initialize the desctiptor for transfer */
> + bus_address =
> + pci_map_single(fp->this_card_priv->pci_dev,
> + fp->tx_unit[temp_tx_unit],
> + fp->unit_sz, PCI_DMA_TODEVICE);
> + writel(bus_address, &fp->tx_desc[temp_tx].desc_a);
> + if (likely(!pci_dma_mapping_error(bus_address)))
> + writel(enable_transfer |
> + (fp->unit_sz & frame_length),
> + &fp->tx_desc[temp_tx].desc_b);
> + else
> + printk(KERN_WARNING
> + "%s: failed to map tranmission DMA.\n",
> + fepci_NAME);
> + }
> + temp_tx = (temp_tx + 1) & (TX_RING_SIZE - 1);
> + temp_tx_unit = (temp_tx_unit + 1);
> + temp_tx_unit *= temp_tx_unit < fp->units;
> + }
> +
> + return intr_status ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +/* stream operations end */
> +
> +int fepci_rebuild_header(struct sk_buff *skb)
> +{
> + DBG_PRINT("fepci_rebuild_header\n");
> + return 0;
> +}
> +
> +static inline u16 get_common_reg_word(void *ioaddr, unsigned long offsett)
> +{
> + u16 word;
> + DBG_PRINT("get_common_reg_word ioaddr %p, offsett %lu\n", ioaddr,
> + offsett);
> + __clear_bit(0, &offsett);
> + DBG_PRINT("get_common_reg_word %p\n",
> + ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1));
> + word = le16_to_cpu(readw
> + (ioaddr + FEPCI_IDENTIFICATION_OFFSETT +
> + (offsett << 1)));
> + DBG_PRINT("get_common_reg_word %p: %hu\n",
> + ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1), word);
> + return word;
> +}
> +
> +static irqreturn_t alarm_manager_interrupt(int irq, void *pointer)
> +{
> + struct fepci_card_private *card_private = pointer;
> + void *ioaddr = card_private->ioaddr;
> + /* check int status */
> + if (readl((void *)(ioaddr + reg_custom)) & AM_interrupt_status) {
> + /* clear int (zero everything, but the mask bit) */
> + writel(readl((void *)(ioaddr + reg_custom)) &
> + AM_interrupt_mask, (void *)(ioaddr + reg_custom));
> + /* wake queue */
> + wake_up(&(card_private->alarm_manager_wait_q));
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +#ifdef FEPCI_POINT_TO_POINT
> +static int is_ptp_interface(struct net_device *dev)
> +{
> + char **p_ptp_if_name = retina_ptp_interfaces;
> + unsigned int i = interfaces;
> + while (i > 0 && *p_ptp_if_name != NULL) {
> + if (!strncmp(dev->name, *p_ptp_if_name, sizeof(dev->name)))
> + return 1;
> + p_ptp_if_name++;
> + i--;
> + }
> + return 0;
> +}
> +#endif /* FEPCI_POINT_TO_POINT */
> +
> +static int __devinit fepci_init_one(struct pci_dev *pdev,
> + const struct pci_device_id *ent)
> +{
> + struct net_device *dev = NULL;
> + struct fepci_ch_private *fp = NULL;
> + int chip_idx = ent->driver_data;
> + int drv_flags = pci_id_tbl[chip_idx].drv_flags;
> + int i;
> + unsigned j;
> + resource_size_t real_ioaddr;
> + void *ioaddr;
> + unsigned position;
> +
> + i = pci_enable_device(pdev);
> + if (i) {
> + printk(KERN_WARNING "%s: pci_enable_device returned %x.\n",
> + fepci_NAME, i);
> + return i;
> + }
> +
> + pci_set_master(pdev);
> +
> + i = pci_request_regions(pdev, (char *)fepci_name);
> + if (i) {
> + printk(KERN_WARNING
> + "%s: pci_request_regions returned %x.\n", fepci_NAME, i);
> + pci_disable_device(pdev);
> + return i;
> + }
> +
> + /* make sure above region is MMIO */
> + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
> + printk(KERN_WARNING "%s: region not MMIO region\n", fepci_NAME);
> + goto ERR_1;
> + }
> +
> + j = pci_resource_len(pdev, 0);
> + if (j < FEPCI_SIZE) {
> + printk(KERN_WARNING
> + "%s: resource length %u less than required %u.\n",
> + fepci_NAME, j, FEPCI_SIZE);
> + goto ERR_1;
> + }
> +
> + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
> + printk(KERN_WARNING "%s: no suitable DMA available.\n",
> + fepci_NAME);
> + goto ERR_1;
> + }
> +
> + real_ioaddr = pci_resource_start(pdev, 0);
> + DBG_PRINT("pci_resource_start %lu.\n", real_ioaddr);
> + ioaddr = ioremap_nocache(real_ioaddr, FEPCI_SIZE);
> + DBG_PRINT("ioremap_nocache = %p.\n", ioaddr);
> + if (!ioaddr) {
> + printk(KERN_WARNING "%s: remapping failed.\n", fepci_NAME);
> + goto ERR_1_5;
> + }
> + position = 0;
> + for (; position < MAX_DEVICES; position++) {
> + down_write(&card_privates[position].semaphore);
> + if (card_privates[position].pci_dev == NULL) {
> + card_privates[position].pci_dev = pdev;
> + if (position == find_cnt)
> + find_cnt++;
> + goto FOUND;
> + }
> + up_write(&card_privates[position].semaphore);
> + }
> + printk(KERN_WARNING
> + "%s: no space to inialize device #%u.\n",
> + fepci_NAME, MAX_DEVICES + 1);
> + goto ERR_2;
> +FOUND:
> + card_privates[position].card_number = position;
> + card_privates[position].ioaddr = ioaddr;
> + card_privates[position].pci_dev = pdev;
> + DBG_PRINT("fillregisterswith_00 %p.\n", ioaddr);
> + fillregisterswith_00(ioaddr);
> +
> + fepci_proc_init_card(position, (void *)&(card_privates[position]));
> +
> + init_waitqueue_head(&(card_privates[position].alarm_manager_wait_q));
> + init_waitqueue_head(&(card_privates[position].stream_transmit_q));
> + init_waitqueue_head(&(card_privates[position].stream_receive_q));
> + init_waitqueue_head(&(card_privates[position].stream_both_q));
> +
> + init_timer(&card_privates[position].mailbox_timer);
> +
> + DBG_PRINT("request_irq %d, %s.\n", pdev->irq, fepci_alarm_manager_name);
> + i = request_irq(pdev->irq, &alarm_manager_interrupt,
> + IRQF_SHARED,
> + fepci_alarm_manager_name, &card_privates[position]);
> + if (i) {
> + up_write(&card_privates[position].semaphore);
> + printk(KERN_WARNING
> + "%s: unable to allocate IRQ %d alarm manager: 0x%x\n",
> + fepci_NAME, pdev->irq, i);
> + goto ERR_2;
> + }
> + DBG_PRINT("alarm manager int on %p.\n", (void *)(ioaddr + reg_custom));
> + writel(AM_interrupt_mask, (void *)(ioaddr + reg_custom));
> + /* alarm manager int on */
> +
> + for (j = 0; j < CHANNELS; j++) {
> + DBG_PRINT("alloc_etherdev %u.\n", j);
> + dev = alloc_etherdev(sizeof(struct fepci_ch_private));
> + if (!dev) {
> + printk(KERN_WARNING
> + "%s: cannot allocate ethernet device\n",
> + fepci_NAME);
> + continue;
> + }
> +
> + fp = dev->priv;
> + fp->minor = position; /* * CHANNELS + j; */
> + /* name := xxx01..xxxnn */
> + memcpy(dev->name, fepci_netdev_name, 6);
> + /* dev->name[3]= j+'0'; channel number -> ascii */
> + /* minor number -> ascii */
> + dev->name[4] = ((fp->minor * CHANNELS + j) % 10) + '0';
> + /* minor number -> ascii */
> + dev->name[3] = ((fp->minor * CHANNELS + j) / 10) + '0';
> +
> + SET_MODULE_OWNER(dev);
> + DBG_PRINT("clear_int %u, %x, %p.\n", j, IntrAllInts, ioaddr);
> + clear_int(j, IntrAllInts, ioaddr);
> + DBG_PRINT("ether_setup %p.\n", dev);
> + ether_setup(dev);
> +
> + random_ether_addr(dev->dev_addr);
> + /* HW_ADDR is got using the mailbox: */
> + {
> + struct fepci_real_mailbox *real_mailbox =
> + (struct fepci_real_mailbox *)
> + (ioaddr + FEPCI_MAILBOX_OFFSETT);
> + unsigned long waituntil;
> +
> + set_semafore(real_mailbox, 0x40);
> + writel(0x1 /*size */ +
> + (0x8 << 8) /* get mac command */ ,
> + &real_mailbox->Size_Command);
> + set_semafore(real_mailbox, 0x11);
> +
> + waituntil = jiffies + HZ;
> + while (time_before(jiffies, waituntil) &&
> + get_semafore(real_mailbox) != 0x20) {
> + DBG_PRINT("jiffies %lu < waituntil %lu.\n",
> + jiffies, waituntil);
> + msleep(0);
> + }
> +
> + /* 14.5.2004 JT: Made this safer. */
> + if (get_semafore(real_mailbox) == 0x20) {
> + dev->dev_addr[5] =
> + readb(&real_mailbox->Data[0 + 3 * j]);
> + dev->dev_addr[4] =
> + readb(((u8 *) & real_mailbox->
> + Data[0 + 3 * j]) + 1);
> + dev->dev_addr[3] =
> + readb(&real_mailbox->Data[1 + 3 * j]);
> + dev->dev_addr[2] =
> + readb(((u8 *) & real_mailbox->
> + Data[1 + 3 * j]) + 1);
> + dev->dev_addr[1] =
> + readb(&real_mailbox->Data[2 + 3 * j]);
> + dev->dev_addr[0] =
> + readb(((u8 *) & real_mailbox->
> + Data[2 + 3 * j]) + 1);
> + }
> +
> + set_semafore(real_mailbox, 0x0);
> + }
> + dev->addr_len = 6;
> +
> + dev->base_addr = (unsigned long)ioaddr;
> + dev->irq = pdev->irq;
> + DBG_PRINT("alarm pci_set_drvdata %p, %p.\n", pdev, dev);
> + if (j == 0)
> + pci_set_drvdata(pdev, dev);
> +
> + fp->drv_flags = drv_flags;
> +
> + fp->rx_desc =
> + (struct fepci_desc *)(dev->base_addr + first_rx_desc +
> + j * to_next_ch_rx_desc);
> + fp->tx_desc =
> + (struct fepci_desc *)(dev->base_addr + first_tx_desc +
> + j * to_next_ch_tx_desc);
> +
> + fp->channel_number = j; /*channel in this device */
> + fp->this_dev = dev;
> +
> + fp->this_card_priv = &card_privates[position];
> + fp->this_card_priv->ch_privates[j] = fp;
> +
> + fp->cur_tx = 0;
> +
> + fp->in_stream_mode = 0;
> + fp->in_eth_mode = 0;
> +
> + fp->reg_rxctrl = reg_first_rxctrl + j * to_next_rxctrl;
> + fp->reg_txctrl = reg_first_txctrl + j * to_next_txctrl;
> +
> + /* The FEPCI specific entries in the device structure */
> + dev->open = &fepci_open;
> + dev->hard_start_xmit = &fepci_start_xmit;
> + dev->stop = &fepci_close;
> + dev->get_stats = &fepci_get_stats;
> + dev->set_multicast_list = &set_rx_mode;
> + dev->do_ioctl = &netdev_ioctl;
> + dev->tx_timeout = fepci_tx_timeout;
> + dev->watchdog_timeo = TX_TIMEOUT;
> +
> +#ifdef FEPCI_POINT_TO_POINT
> + if (is_ptp_interface(dev)) {
> + dev->flags |= (IFF_POINTOPOINT);
> + dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
> + if (retina_noarp_with_ptp) {
> + dev->rebuild_header = fepci_rebuild_header;
> + dev->flags |= (IFF_NOARP);
> + }
> + }
> +#endif
> + DBG_PRINT("register_netdev %p.\n", dev);
> + i = register_netdev(dev);
> + if (i) {
> + printk(KERN_WARNING
> + "%s: register_netdev failed 0x%x.\n",
> + fepci_NAME, i);
> + continue;
> + }
> +
> + printk("%s: %s type %x at %p, ",
> + dev->name, pci_id_tbl[chip_idx].name, 0x0, ioaddr);
> + for (i = 0; i < 5; i++)
> + printk("%2.2x:", dev->dev_addr[i]);
> + printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], pdev->irq);
> +#ifdef DEBUG_PROC_FILES
> + fepci_proc_init_channel(position, j, fp);
> +#endif /* DEBUG_PROC_FILES */
> + }
> + up_write(&card_privates[position].semaphore);
> + DBG_PRINT("fepci_init_one %u.\n", position);
> + return 0;
> +
> +ERR_2:
> + iounmap(ioaddr);
> +ERR_1_5:
> + pci_set_drvdata(pdev, NULL);
> +ERR_1:
> + pci_disable_device(pdev);
> + pci_release_regions(pdev);
> + return -ENODEV;
> +}
> +
> +static int fepci_open(struct net_device *dev)
> +{
> + struct fepci_ch_private *fp = dev->priv;
> +
> + down_write(&fp->this_card_priv->semaphore);
> +
> + if (fp->this_card_priv->pci_dev == NULL) {
> + up_write(&fp->this_card_priv->semaphore);
> + fepci_close(dev);
> + return -ENXIO;
> + }
> +
> + while (fp->in_stream_mode) {
> + up_write(&fp->this_card_priv->semaphore);
> + fepci_stream_close(dev);
> + down_write(&fp->this_card_priv->semaphore);
> + }
> +
> + {
> + int i = request_irq(dev->irq, &fepci_interrupt,
> + IRQF_SHARED, dev->name, dev);
> + if (i) {
> + up_write(&fp->this_card_priv->semaphore);
> + printk(KERN_WARNING
> + "%s: unable to allocate IRQ %d, error 0x%x",
> + fepci_NAME, dev->irq, i);
> + return i;
> + }
> + }
> +
> + fp->in_eth_mode = 1;
> +
> + fepci_init_ring(dev);
> + set_rx_mode(dev);
> +
> + fp->cur_rx = 0;
> + fp->cur_tx = 0;
> +
> + netif_carrier_off(dev);
> +
> + /* Enable interrupts by setting the interrupt mask. */
> +
> + set_int_mask(fp->channel_number,
> + MaskFrameReceived | MaskFrameTransmitted |
> + MaskRxFifoError | MaskRxFrameDroppedError |
> + MaskTxFifoError, fp->this_card_priv);
> +
> + {
> + void *ioaddr = (void *)dev->base_addr;
> +
> + /* Start Rx and Tx channels. */
> + writel(Receive_enable |
> + (Rx_fifo_threshold & RX_FIFO_THRESHOLD_PACKET_MODE),
> + (void *)(ioaddr + fp->reg_rxctrl));
> +
> + writel((Transmit_enable |
> + (Tx_desc_threshold &
> + (TX_DESC_THRESHOLD_PACKET_MODE << 8)) |
> + (Tx_fifo_threshold & TX_FIFO_THRESHOLD_PACKET_MODE)),
> + (void *)(ioaddr + fp->reg_txctrl));
> + }
> +
> + netif_start_queue(dev);
> +
> + /* Set timer */
> + init_timer(&fp->timer);
> + fp->timer.expires = jiffies + HZ;
> + fp->timer.data = (unsigned long)dev;
> + fp->timer.function = &fepci_timer; /* timer handler */
> + add_timer(&fp->timer);
> +
> + up_write(&fp->this_card_priv->semaphore);
> +
> + return 0;
> +}
> +
> +static void fepci_timer(unsigned long data)
> +{
> + struct net_device *dev = (struct net_device *)data;
> + struct fepci_ch_private *fp = dev->priv;
> +
> + /* just to make it absolutely sure the sending starts again
> + * if the system jams: free already sent skbuffs */
> +
> + if (netif_tx_trylock(dev)) {
> + if (!(fp->tx_interrupts_since_last_timer)) {
> + unsigned i = TX_RING_SIZE - 1;
> + do {
> + unsigned desc_b;
> + if ((fp->tx_skbuff[i] != NULL)
> + &&
> + (((desc_b =
> + readl(&fp->tx_desc[i].
> + desc_b)) & transfer_not_done) ==
> + 0)) {
> + /* has been sent */
> + pci_unmap_single(fp->
> + this_card_priv->
> + pci_dev,
> + readl(&fp->
> + tx_desc[i].
> + desc_a),
> + desc_b &
> + frame_length,
> + PCI_DMA_TODEVICE);
> + dev_kfree_skb(fp->tx_skbuff[i]);
> + fp->tx_skbuffs_out++;
> +
> + fp->tx_skbuff[i] = NULL;
> +
> + if (desc_b & fifo_error) {
> + fp->stats.tx_fifo_errors++;
> + fp->tx_desc_fifo_err++;
> + } else
> + fp->stats.tx_packets++;
> + }
> + }
> + while (i--);
> + }
> + /* if the next descriptor is free, continue taking new ones */
> + if (!
> + (readl(&fp->tx_desc[fp->cur_tx].desc_b) &
> + transfer_not_done))
> + netif_wake_queue(dev);
> + netif_tx_unlock(dev);
> + }
> +
> + fp->tx_interrupts_since_last_timer = 0;
> +
> + if ((get_common_reg_word(fp->this_card_priv->ioaddr, 0x72) >> fp->
> + channel_number) & 1) {
> + netif_carrier_off(dev);
> + } else {
> + netif_carrier_on(dev);
> + }
> +
> + if (fp->in_eth_mode)
> + mod_timer(&fp->timer, jiffies + 5 * HZ);
> +}
> +
> +static void fepci_tx_timeout(struct net_device *dev)
> +{
> + DBG_PRINT("%s: transmit timed out!\n", dev->name);
> +}
> +
> +/* Initialize the rx and tx rings */
> +static void fepci_init_ring(struct net_device *dev)
> +{
> + struct fepci_ch_private *fp = dev->priv;
> + unsigned i;
> +
> + fp->rx_buf_sz = 2000;
> +
> + for (i = 0; i < RX_RING_SIZE; i++) {
> + struct sk_buff *skb =
> + __dev_alloc_skb(fp->rx_buf_sz, GFP_KERNEL);
> +
> + if (unlikely(skb == NULL)) {
> +ZERO:
> + writel(0, &fp->rx_desc[i].desc_a);
> + writel(0, &fp->rx_desc[i].desc_b);
> + continue;
> + } else {
> + dma_addr_t bus_address =
> + pci_map_single(fp->this_card_priv->pci_dev,
> + skb->data, fp->rx_buf_sz,
> + PCI_DMA_FROMDEVICE);
> + if (likely(!pci_dma_mapping_error(bus_address))) {
> + fp->rx_skbuffs_in++;
> + /* Mark as being used by this device */
> + skb->dev = dev;
> + skb->ip_summed = CHECKSUM_UNNECESSARY;
> + fp->rx_skbuff[i] = skb;
> + writel(bus_address, &fp->rx_desc[i].desc_a);
> + writel(enable_transfer, &fp->rx_desc[i].desc_b);
> + } else {
> + dev_kfree_skb(skb);
> + goto ZERO;
> + }
> + }
> + }
> +
> + for (i = 0; i < TX_RING_SIZE; i++) {
> + fp->tx_skbuff[i] = NULL;
> + writel(0, &fp->tx_desc[i].desc_a); /* no skbuff */
> + /* no transfer enable, no int enable */
> + writel(0, &fp->tx_desc[i].desc_b);
> + }
> +
> + return;
> +}
> +
> +static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> + struct fepci_ch_private *fp = dev->priv;
> + const unsigned cur_tx = fp->cur_tx;
> + unsigned next;
> + unsigned tx_length = skb->len;
> + dma_addr_t bus_address;
> + struct sk_buff *old;
> +
> + fp->tx_skbuffs_in++;
> +
> + if (unlikely(tx_length < ETH_ZLEN)) {
> + struct sk_buff *bigger =
> + skb_copy_expand(skb, 0, ETH_ZLEN - tx_length,
> + GFP_ATOMIC);
> + if (unlikely(!bigger))
> + return NET_XMIT_CN;
> + tx_length = ETH_ZLEN;
> + old = skb;
> + skb = bigger;
> + } else
> + old = NULL;
> + bus_address =
> + pci_map_single(fp->this_card_priv->pci_dev, skb->data,
> + tx_length, PCI_DMA_TODEVICE);
> + if (likely(!pci_dma_mapping_error(bus_address))) {
> + struct fepci_desc *descriptor;
> + if (old)
> + dev_kfree_skb(old);
> + descriptor = &fp->tx_desc[cur_tx];
> + writel(bus_address, &descriptor->desc_a);
> + writel((tx_length & frame_length) | enable_transfer,
> + &descriptor->desc_b);
> + } else {
> + if (old)
> + dev_kfree_skb(skb);
> + return NET_XMIT_CN;
> + }
> + fp->stats.tx_bytes += tx_length;
> +
> + fp->tx_skbuff[cur_tx] = skb;
> +
> + /* Calculate the next Tx descriptor entry */
> + next = (cur_tx + 1) & (TX_RING_SIZE - 1);
> + fp->cur_tx = next;
> + /* if the next descriptor is busy, discontinue taking new ones */
> + if (fp->tx_skbuff[next] != NULL)
> + netif_stop_queue(dev);
> + dev->trans_start = jiffies;
> +
> + return NET_XMIT_SUCCESS;
> +}
> +
> +static irqreturn_t fepci_interrupt(int irq, void *dev_instance)
> +{
> + struct net_device *dev = dev_instance;
> + void *ioaddr = (void *)dev->base_addr;
> + struct fepci_ch_private *fp = dev->priv;
> + unsigned intr_status = get_int_status(fp->channel_number, ioaddr);
> + bool RxFifoError;
> + bool RxFrameDroppedError;
> +
> + clear_int(fp->channel_number, intr_status, ioaddr);
> +
> + RxFifoError = (intr_status & IntrRxFifoError) != 0;
> + RxFrameDroppedError = (intr_status & IntrRxFrameDroppedError) != 0;
> + /* first update interrupt error table */
> + fp->int_err_table[RxFifoError][RxFrameDroppedError]++;
> +
> + fp->interrupts++;
> +
> + if (intr_status & IntrFrameReceived) {
> + fepci_rx(dev);
> + fp->rx_interrupts++;
> + }
> + if (intr_status & IntrFrameTransmitted) {
> + fp->tx_interrupts_since_last_timer++;
> + if (netif_tx_trylock(dev)) {
> + unsigned i = TX_RING_SIZE - 1;
> + unsigned next;
> + do {
> + unsigned desc_b;
> + if ((fp->tx_skbuff[i] != NULL)
> + &&
> + (((desc_b =
> + readl(&fp->tx_desc[i].
> + desc_b)) & transfer_not_done) ==
> + 0)) {
> + /* has been sent */
> + pci_unmap_single(fp->
> + this_card_priv->
> + pci_dev,
> + readl(&fp->
> + tx_desc[i].
> + desc_a),
> + desc_b &
> + frame_length,
> + PCI_DMA_TODEVICE);
> + dev_kfree_skb_irq(fp->tx_skbuff[i]);
> + fp->tx_skbuffs_out++;
> + fp->tx_skbuff[i] = NULL;
> + if (desc_b & fifo_error) {
> + fp->stats.tx_fifo_errors++;
> + fp->tx_desc_fifo_err++;
> + } else
> + fp->stats.tx_packets++;
> + }
> + }
> + while (i--);
> + next = fp->cur_tx;
> + /* if next tx descriptor is free,
> + * continue taking new ones */
> + if (!(readl(&fp->tx_desc[next].desc_b) &
> + transfer_not_done))
> + netif_wake_queue(dev);
> + netif_tx_unlock(dev);
> + }
> + fp->tx_interrupts++;
> + }
> + if (RxFifoError) {
> + fp->rx_int_fifo_err++;
> + fepci_rx(dev);
> + }
> + if (RxFrameDroppedError) {
> + fp->rx_int_frame_dropped_err++;
> + fepci_rx(dev);
> + }
> + if (intr_status & IntrTxFifoError)
> + fp->tx_int_fifo_err++;
> + return intr_status ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +static int fepci_rx(struct net_device *dev)
> +{
> + struct fepci_ch_private *fp = dev->priv;
> +
> + unsigned int i, old_cur_rx = fp->cur_rx;
> + for (i = old_cur_rx;
> + i != ((old_cur_rx + RX_RING_SIZE - 1) & (RX_RING_SIZE - 1));
> + i = (i + 1) & (RX_RING_SIZE - 1)) {
> + unsigned desc_b;
> + struct sk_buff *skb;
> + /* transfer done */
> + bool condition = (skb = fp->rx_skbuff[i]) &&
> + ((desc_b =
> + readl(&fp->rx_desc[i].desc_b)) & transfer_not_done) == 0;
> + if (condition) {
> + bool fifo = (desc_b & fifo_error) != 0;
> + bool size = (desc_b & size_error) != 0;
> + bool crc = (desc_b & crc_error) != 0;
> + bool octet = (desc_b & octet_error) != 0;
> + bool line = (desc_b & line_error) != 0;
> + unsigned length = desc_b & frame_length;
> + pci_unmap_single(fp->this_card_priv->pci_dev,
> + readl(&fp->rx_desc[i].desc_a),
> + fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
> + fp->cur_rx = (i + 1) & (RX_RING_SIZE - 1);
> + /* first update error table */
> + fp->rx_desc_err_table[fifo]
> + [size]
> + [crc]
> + [octet]
> + [line]++;
> + /* small packet counters */
> + if (length == 0)
> + fp->rx_packets_of_size_0++;
> + else if (length == 1)
> + fp->rx_packets_of_size_1++;
> + else if (length == 2)
> + fp->rx_packets_of_size_2++;
> + else if (length == 3)
> + fp->rx_packets_of_size_3++;
> + else if (length < 8)
> + fp->rx_packets_of_size_4_7++;
> + else if (length < 16)
> + fp->rx_packets_of_size_8_15++;
> + else if (length < 32)
> + fp->rx_packets_of_size_16_31++;
> +
> + if (fifo) {
> + fp->stats.rx_errors++;
> + fp->stats.rx_frame_errors++;
> + fp->rx_desc_fifo_err++;
> + writel(enable_transfer, &fp->rx_desc[i].desc_b);
> + } else if (size) {
> + fp->stats.rx_errors++;
> + fp->stats.rx_over_errors++;
> + fp->rx_desc_size_err++;
> + writel(enable_transfer, &fp->rx_desc[i].desc_b);
> + } else if (crc) {
> + fp->stats.rx_errors++;
> + fp->stats.rx_crc_errors++;
> + fp->rx_desc_crc_err++;
> + writel(enable_transfer, &fp->rx_desc[i].desc_b);
> + } else if (octet) {
> + fp->rx_desc_octet_err++;
> + writel(enable_transfer, &fp->rx_desc[i].desc_b);
> + } else if (line) {
> + fp->rx_desc_line_err++;
> + writel(enable_transfer, &fp->rx_desc[i].desc_b);
> + } else {
> + skb_put(skb, length - 4);
> +
> + skb->protocol = eth_type_trans(skb, dev);
> +#ifdef FEPCI_POINT_TO_POINT
> + if (dev->flags & IFF_POINTOPOINT) {
> + /* everything received is for us. */
> +
> + if (dev->flags & IFF_NOARP) {
> + /* NOARP applied ->
> + * destination MAC addresses
> + * are bogus */
> + if (skb->
> + pkt_type ==
> + PACKET_OTHERHOST)
> + skb->
> + pkt_type =
> + PACKET_HOST;
> + } else {
> + /* NOARP not applied ->
> + * destination MAC addresses are
> + * broadcast */
> + if (skb->
> + pkt_type ==
> + PACKET_BROADCAST)
> + skb->
> + pkt_type =
> + PACKET_HOST;
> +
> + } /* IFF_NOARP */
> + } /* IFF_POINTOPOINT */
> +#endif
> + skb_reset_mac_header(skb);
> + netif_rx(skb);
> + fp->rx_skbuffs_out++;
> + /* statistics -4==crc */
> + fp->stats.rx_bytes += length - 4;
> + fp->stats.rx_packets++;
> +
> + fp->rx_skbuff[i] = NULL;
> + dev->last_rx = jiffies;
> + }
> + }
> + /* reserve a new one */
> + if (fp->rx_skbuff[i] == NULL) {
> + struct sk_buff *skb = dev_alloc_skb(fp->rx_buf_sz);
> +
> + if (skb == NULL)
> + continue; /* Better luck next round. */
> + else {
> + dma_addr_t address =
> + pci_map_single(fp->this_card_priv->pci_dev,
> + skb->data,
> + fp->rx_buf_sz,
> + PCI_DMA_FROMDEVICE);
> + if (likely(!pci_dma_mapping_error(address))) {
> + struct fepci_desc *descriptor;
> + fp->rx_skbuffs_in++;
> + fp->rx_skbuff[i] = skb;
> + /* Mark as being used by this device. */
> + skb->dev = dev;
> + skb->ip_summed = CHECKSUM_UNNECESSARY;
> + descriptor = &fp->rx_desc[i];
> + writel(address, &descriptor->desc_a);
> + writel(enable_transfer,
> + &descriptor->desc_b);
> + } else {
> + dev_kfree_skb(skb);
> + printk(KERN_WARNING
> + "%s: failed to map DMA.\n",
> + dev->name);
> + }
> + }
> + }
> + }
> + return 0;
> +}
> +
> +static int fepci_close(struct net_device *dev)
> +{
> + struct fepci_ch_private *fp = dev->priv;
> + unsigned i;
> + void *ioaddr = (void *)dev->base_addr;
> +
> + down_write(&fp->this_card_priv->semaphore);
> +
> + netif_stop_queue(dev);
> +
> + /* Disable interrupts by clearing the interrupt mask */
> + set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
> +
> + /* Stop the Tx and Rx processes */
> + writel(0x0, ioaddr + fp->reg_rxctrl);
> + writel(0x0, ioaddr + fp->reg_txctrl);
> + fp->in_eth_mode = 0;
> +
> + del_timer_sync(&fp->timer);
> +
> + free_irq(dev->irq, dev);
> +
> + /* Free all the rx skbuffs */
> + for (i = 0; i < RX_RING_SIZE; i++) {
> + if (fp->rx_skbuff[i] != NULL) {
> + pci_unmap_single(fp->this_card_priv->
> + pci_dev,
> + readl(&fp->rx_desc[i].
> + desc_a),
> + fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
> + dev_kfree_skb(fp->rx_skbuff[i]);
> + fp->rx_skbuffs_out++;
> + fp->rx_skbuff[i] = NULL;
> + }
> + }
> + /* and tx */
> + for (i = 0; i < TX_RING_SIZE; i++) {
> + if (fp->tx_skbuff[i] != NULL) {
> + pci_unmap_single(fp->this_card_priv->
> + pci_dev,
> + readl(&fp->tx_desc[i].
> + desc_a),
> + readl(&fp->tx_desc[i].
> + desc_b) & frame_length,
> + PCI_DMA_TODEVICE);
> + dev_kfree_skb(fp->tx_skbuff[i]);
> + fp->tx_skbuffs_out++;
> + fp->tx_skbuff[i] = NULL;
> + }
> + }
> + up_write(&fp->this_card_priv->semaphore);
> + return 0;
> +}
> +
> +static struct net_device_stats *fepci_get_stats(struct net_device *dev)
> +{
> + struct fepci_ch_private *fp = dev->priv;
> + return &fp->stats;
> +}
> +
> +static void set_rx_mode(struct net_device *dev)
> +{
> + DBG_PRINT("set_rx_mode\n");
> +}
> +
> +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
> +{
> + struct fepci_ch_private *fp = dev->priv;
> + char data = ((unsigned)rq->ifr_data) & 0xff;
> + int ret = 0;
> +
> + DBG_PRINT("%s: netdev_ioctl called (command_nmbr:0x%x).\n",
> + dev->name, cmd);
> +
> + switch (cmd) {
> + case FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE:
> + DBG_PRINT
> + (" ioctl stream bufsize commanded. (bufsize:0x%x)\n", data);
> + down_write(&fp->this_card_priv->semaphore);
> + if (fp->in_stream_mode) {
> + up_write(&fp->this_card_priv->semaphore);
> + return -EBUSY;
> + }
> + fp->bufsize_order = data;
> + up_write(&fp->this_card_priv->semaphore);
> + break;
> + case FEPCI_NETDEV_IOCTL_STREAM_UNITSIZE:
> + DBG_PRINT
> + (" ioctl stream unitsize commanded. (unitsize:0x%x)\n",
> + data);
> + down_write(&fp->this_card_priv->semaphore);
> + if (fp->in_stream_mode) {
> + up_write(&fp->this_card_priv->semaphore);
> + return -EBUSY;
> + }
> + fp->fake_unit_sz_order = data;
> + up_write(&fp->this_card_priv->semaphore);
> + break;
> + case FEPCI_NETDEV_IOCTL_STREAM_OPEN:
> + DBG_PRINT(" ioctl stream open commanded.\n");
> + ret = fepci_stream_open(dev);
> + break;
> + case FEPCI_NETDEV_IOCTL_STREAM_START:
> + DBG_PRINT(" ioctl stream start commanded.\n");
> + ret = fepci_stream_start(dev);
> + break;
> + case FEPCI_NETDEV_IOCTL_STREAM_CLOSE:
> + DBG_PRINT(" ioctl stream close commanded.\n");
> + ret = fepci_stream_close(dev);
> + break;
> + default:
> + DBG_PRINT(" unknown ioctl command 0x%x.\n", cmd);
> + return -ENOTTY;
> + }
> + return ret;
> +}
> +
> +static void fepci_remove_one(struct pci_dev *pdev)
> +{
> + /* from first dev, same in all */
> + struct net_device *dev = pci_get_drvdata(pdev);
> + struct fepci_ch_private *fp = dev->priv;
> + struct fepci_card_private *cardp = fp->this_card_priv;
> + unsigned int i;
> +
> + writel(0, (void *)(cardp->ioaddr + reg_custom));
> + /* alarm manager int off */
> +
> + for (i = 0; i < CHANNELS; i++) {
> + dev = cardp->ch_privates[i]->this_dev;
> + fp = dev->priv;
> +#ifdef DEBUG_PROC_FILES
> + fepci_proc_cleanup_channel(cardp->card_number,
> + fp->channel_number);
> +#endif /* DEBUG_PROC_FILES */
> + unregister_netdev(dev);
> + fepci_stream_close(dev);
> + free_netdev(dev);
> + }
> +
> + pdev = cardp->pci_dev;
> + free_irq(pdev->irq, (void *)cardp);
> +
> + down_write(&cardp->semaphore);
> +
> + fepci_proc_cleanup_card(cardp->card_number);
> +
> + pci_set_drvdata(pdev, NULL);
> +
> + if (cardp->card_number + 1 == find_cnt)
> + find_cnt--;
> + cardp->pci_dev = NULL;
> +
> + iounmap(cardp->ioaddr);
> +
> + up_write(&cardp->semaphore);
> +
> + pci_disable_device(pdev);
> + pci_release_regions(pdev);
> +}
> +
> +static struct pci_driver fepci_driver = {
> +name: DRV_NAME,
> +id_table: fepci_pci_tbl,
> +probe: fepci_init_one,
> +remove: fepci_remove_one,
> +};
> +
> +static int __init fepci_init(void)
> +{
> + unsigned card = MAX_DEVICES - 1;
> + do
> + init_rwsem(&card_privates[card].semaphore);
> + while (card--);
> + major = fepci_register_char_device();
> + if (major < 0)
> + return major;
> + stream_pointers =
> + (unsigned long)get_zeroed_page(GFP_KERNEL | __GFP_DMA);
> + DBG_PRINT(" %x.\n", stream_pointers);
> + if (stream_pointers == 0) {
> + fepci_unregister_char_device();
> + return -ENOMEM;
> + }
> + DBG_PRINT("SetPageReserved %u.\n", stream_pointers);
> + SetPageReserved(virt_to_page(stream_pointers));
> + DBG_PRINT("fepci_proc_init_driver.\n");
> + fepci_proc_init_driver();
> + DBG_PRINT("pci_register_driver %p.\n", &fepci_driver);
> + {
> + int ret = pci_register_driver(&fepci_driver);
> + if (ret) {
> + fepci_unregister_char_device();
> + ClearPageReserved(virt_to_page(stream_pointers));
> + free_page(stream_pointers);
> + DBG_PRINT
> + ("pci_register_driver %p failed with %d.\n",
> + &fepci_driver, ret);
> + fepci_proc_cleanup_driver();
> + return ret;
> + }
> + DBG_PRINT("fepci_init %d.\n", ret);
> + return ret;
> + }
> +}
> +
> +static void __exit fepci_cleanup(void)
> +{
> + DBG_PRINT("fepci_cleanup\n");
> + pci_unregister_driver(&fepci_driver);
> + fepci_proc_cleanup_driver();
> + fepci_unregister_char_device();
> + ClearPageReserved(virt_to_page(stream_pointers));
> + free_page(stream_pointers);
> +}
> +
> +module_init(fepci_init);
> +module_exit(fepci_cleanup);
> +
>
--
Stephen Hemminger <shemminger@linux-foundation.org>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH] wan: new driver retina
@ 2007-10-22 11:14 Matti Linnanvuori
2007-10-22 15:02 ` Randy Dunlap
2007-10-23 0:41 ` Krzysztof Halasa
0 siblings, 2 replies; 8+ messages in thread
From: Matti Linnanvuori @ 2007-10-22 11:14 UTC (permalink / raw)
To: akpm, jgarzik; +Cc: netdev
From: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
Retina G.703 and G.SHDSL driver.
Signed-off-by: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
---
Fixed Kconfig and the body of the explanation.
diff -Napur
linux-2.6.23/drivers/net/wan/Kconfig
linux-2.6.24/drivers/net/wan/Kconfig
--- linux-2.6.23/drivers/net/wan/Kconfig 2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/drivers/net/wan/Kconfig 2007-10-22 13:54:55.383644167 +0300
@@ -494,4 +494,15 @@ config SBNI_MULTILINE
If unsure, say N.
+config RETINA
+ tristate "Retina support"
+ depends on m && PCI
+ help
+ Driver for Retina C5400 and E2200 network cards, which
+ support G.703, G.SHDSL, Ethernet encapsulation and PCI.
+
+ The driver will be compiled as a module: the
+ module will be called retina.
+
+
endif # WAN
diff -Napur linux-2.6.23/drivers/net/wan/Makefile linux-2.6.24/drivers/net/wan/Makefile
--- linux-2.6.23/drivers/net/wan/Makefile 2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/drivers/net/wan/Makefile 2007-10-22 12:05:57.638172823 +0300
@@ -42,6 +42,7 @@ obj-$(CONFIG_C101) += c101.o
obj-$(CONFIG_WANXL) += wanxl.o
obj-$(CONFIG_PCI200SYN) += pci200syn.o
obj-$(CONFIG_PC300TOO) += pc300too.o
+obj-$(CONFIG_RETINA) += retina.o
clean-files := wanxlfw.inc
$(obj)/wanxl.o: $(obj)/wanxlfw.inc
diff -Napur linux-2.6.23/drivers/net/wan/retina.c linux-2.6.24/drivers/net/wan/retina.c
--- linux-2.6.23/drivers/net/wan/retina.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.24/drivers/net/wan/retina.c 2007-10-22 12:01:44.856033562 +0300
@@ -0,0 +1,4503 @@
+/* V1.2.4 */
+
+/* retina.c: */
+
+/*
+ This driver is based on:
+
+ /drivers/net/fepci.c
+ FEPCI (Frame Engine for PCI) driver for Linux operating system
+
+ Copyright (C) 2002-2003 Jouni Kujala, Flexibilis Oy.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ All the drivers derived from or based on this code fall under the
+ GPL and must retain the copyright and license notice.
+
+ CHANGES
+ -------
+
+ v1.0.0 (JK) - May 27, 2003:
+ * Original driver.
+
+ v1.1.0 (JK) - June, 2003:
+ * final Flexibilis driver
+
+ v1.2.0: NO_ARP option back again
+
+ v1.2.1: (JT) - Aug 21, 2003:
+ * Added support for Retina C5400 card including PROC stuff.
+
+ v1.2.2: (Petri Ahonen) - Sep 19, 2003:
+ * PtP changes:
+ * Added retina_ptp_interfaces module parameter for initializing
+ * interfaces in PtP mode.
+ * for example:
+ * insmod ./retina.o retina_ptp_interfaces=dcp00,dcp01
+ * Added retina_noarp_with_ptp module parameter to set/clear
+ * NOARP flag on PtP interfaces
+ * for example:
+ * insmod ./retina.o retina_ptp_interfaces=dcp00 retina_noarp_with_ptp=0
+
+ v1.2.3: (JT) - Nov 14, 2003:
+ * Fixed PROC packet counters.
+
+ v1.2.4: (JT) - May 14, 2004:
+ * Made initializing MACs safer.
+*/
+
+#define DRV_NAME "retina"
+#define DRV_VERSION "1.2.5"
+#define DRV_RELDATE "November 14, 2003"
+
+#define DBG_PRINT(xyz...)
+/* printk(KERN_DEBUG xyz) */
+
+/* uncomment this if you want to have debug files in /proc */
+/* #define DEBUG_PROC_FILES */
+
+/* obsolete
+ see retina_noarp_with_ptp
+define FEPCI_NO_ARP */
+
+/* uncomment this if you want to have point-to-point links
+ ! Only interfaces listed in retina_ptp_interfaces will
+ be created in PtP mode
+ see retina_ptp_interfaces */
+#define FEPCI_POINT_TO_POINT
+
+/* need to update MODULE_PARM also */
+#define MAX_DEVICES 32u
+
+#define MAX_TX_UNITS 256u
+#define MAX_RX_UNITS 256u
+
+#define MAX_UNIT_SZ_ORDER 10u
+
+#define TX_RING_SIZE 8u
+#define RX_RING_SIZE 8u
+
+/* need to update MODULE_PARM also */
+#define CHANNELS 4u
+
+#define RX_FIFO_THRESHOLD_PACKET_MODE 0x4
+#define TX_FIFO_THRESHOLD_PACKET_MODE 0x4
+#define TX_DESC_THRESHOLD_PACKET_MODE 0x4
+
+#define RX_FIFO_THRESHOLD_STREAM_MODE 0x4
+#define TX_FIFO_THRESHOLD_STREAM_MODE 0x7
+#define TX_DESC_THRESHOLD_STREAM_MODE 0x1
+
+/* need to update MODULE_PARM also */
+#define MAX_INTERFACES (CHANNELS*MAX_DEVICES)
+
+const char fepci_name[] = "retina";
+const char fepci_alarm_manager_name[] = "retina alarm manager";
+const char fepci_NAME[] = "RETINA";
+const char fepci_netdev_name[] = "dcpxx";
+const char fepci_proc_entry_name[] = "driver/retina";
+
+static unsigned int find_cnt;
+
+#ifdef FEPCI_POINT_TO_POINT
+static char *retina_ptp_interfaces[MAX_INTERFACES];
+static int retina_noarp_with_ptp = 1;
+#endif /* FEPCI_POINT_TO_POINT */
+
+#define fepci_features_proc_entry_name "driver/retina/%02x:%02x.%02x/features"
+#define fepci_settings_proc_entry_name "driver/retina/%02x:%02x.%02x/settings"
+#define fepci_status_proc_entry_name "driver/retina/%02x:%02x.%02x/status"
+
+#ifdef DEBUG_PROC_FILES
+#define fepci_counters_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/counters_ch%d"
+#define fepci_descriptors_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/descriptors_ch%d"
+#define fepci_stream_counters_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/stream_counters_ch%d"
+#define fepci_registers_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/registers_ch%d"
+#endif /* DEBUG_PROC_FILES */
+
+/* Time in jiffies before concluding that the transmitter is hung */
+#define TX_TIMEOUT (20*HZ)
+
+#if !defined(__OPTIMIZE__)
+#warning You must compile this file with correct options.
+#warning See the last lines of the source file.
+#error You have to compile this driver with "-O".
+#endif
+#include "retina.h"
+
+#include <linux/mm.h>
+#include <linux/random.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/version.h>
+#include <linux/pfn.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/unaligned.h>
+#include <asm/pgtable.h>
+
+MODULE_VERSION(DRV_VERSION);
+
+#undef inb
+#undef inw
+#undef inl
+#undef outb
+#undef outw
+#undef outl
+#define inb nonexistent /* force using only 32bit access */
+#define inw nonexistent /* force using only 32bit access */
+#define inl(x) le32_to_cpu(readl(x))
+#define outb nonexistent /* force using only 32bit access */
+#define outw nonexistent /* force using only 32bit access */
+#define outl(value, address) writel(cpu_to_le32(value), address)
+
+#define VMA_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT)
+
+enum pci_id_flags_bits {
+ /* Set PCI command register bits before calling probe */
+ PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4,
+ /* Read and map the single following PCI BAR */
+ PCI_ADDR0 = 0 << 4, PCI_ADDR1 = 1 << 4, PCI_ADDR2 =
+ 2 << 4, PCI_ADDR3 = 3 << 4,
+ PCI_ADDR_64BITS = 0x100, PCI_NO_ACPI_WAKE =
+ 0x200, PCI_NO_MIN_LATENCY = 0x400,
+ PCI_UNUSED_IRQ = 0x800,
+};
+
+/* PCI I/O space extent */
+#define FEPCI_SIZE 0x20000
+#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR0)
+
+struct pci_id_info {
+ const char *name;
+ struct match_info {
+ int pci, pci_mask, subsystem, subsystem_mask;
+ int revision, revision_mask; /* Only 8 bits. */
+ } id;
+ enum pci_id_flags_bits pci_flags;
+ int io_size; /* Needed for I/O region check or ioremap */
+ int drv_flags; /* Driver use, intended as capability flags */
+};
+
+static struct pci_id_info pci_id_tbl[] = {
+ {"Frame Engine for PCI (FEPCI)",
+ {0x1FC00300, 0x1FC00301, 0xffffffff},
+ PCI_IOTYPE, FEPCI_SIZE},
+ {0,},
+};
+
+static struct pci_device_id fepci_pci_tbl[] __devinitdata = {
+ {0x1FC0, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x1FC0, 0x0301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+MODULE_DESCRIPTION("Frame Engine for PCI (FEPCI)");
+MODULE_AUTHOR("Jouni Kujala");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, fepci_pci_tbl);
+
+/* Linux 2.4 appears to drop POINTOPOINT,BROADCAST and NOARP flags in SIOCSFLAGS
+ * This workaround allows load time per interface ptp mode configuration.
+ * Runtime ptp mode changes would either require changes to 2.4 or
+ * use of proprietary ioctls, which ifconfig knows nothing about anyway
+ */
+
+static unsigned interfaces = MAX_INTERFACES;
+#ifdef FEPCI_POINT_TO_POINT
+module_param_array(retina_ptp_interfaces, charp, &interfaces, S_IRUGO);
+module_param(retina_noarp_with_ptp, bool, S_IRUGO);
+MODULE_PARM_DESC(retina_noarp_with_ptp,
+ "0 to disable NOARP, "
+ "1 to enable NOARP on pointopoint interfaces");
+#endif
+
+struct fepci_ch_private {
+ unsigned int channel_number;
+ struct net_device *this_dev;
+
+ struct fepci_card_private *this_card_priv;
+
+ unsigned int reg_rxctrl;
+ unsigned int reg_txctrl;
+
+ struct fepci_desc *rx_desc; /* rx_ring start */
+ struct fepci_desc *tx_desc; /* tx_ring start */
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+
+ struct timer_list timer;
+ struct net_device_stats stats;
+
+ unsigned drv_flags;
+
+ unsigned int rx_buf_sz; /* MTU+slack */
+ unsigned int cur_tx; /* the next filled tx_descriptor */
+ /* in stream mode the desc which is being transmitted */
+ /* rx_descriptor where next packet transferred */
+ unsigned int cur_rx;
+ /* in stream mode the desc which is being received */
+
+/* stream mode: */
+ bool in_eth_mode;
+ bool in_stream_mode;
+ bool stream_on;
+ u32 *rx_buffer;
+ u32 *tx_buffer;
+ unsigned bufsize_order; /* 10=1kB,11=2kB,12=4kB...16=64kB */
+ u32 bufsize;
+ unsigned unit_sz_order; /* 8=256B...14=16kB */
+ u32 unit_sz;
+ unsigned units; /* 2,4,8,16,...,256 */
+ /* fake units (and pointers) are for faking larger unit sizes to
+ * the user than what is the maximum internal unit size in FEPCI */
+ unsigned fake_unit_sz_order;
+ u32 fake_unit_sz;
+ unsigned fake_units;
+ u32 *tx_unit[MAX_TX_UNITS];
+ u32 *rx_unit[MAX_RX_UNITS];
+ unsigned cur_tx_unit; /* last sent tx_unit */
+ /* rx_unit where to next packet is transferred */
+ unsigned cur_rx_unit;
+/* char device: */
+ unsigned minor; /* currently the same as card_nuber */
+/* debugging stuff for packet mode: */
+ /* rx-errors in descriptors */
+ unsigned int rx_desc_fifo_err;
+ unsigned int rx_desc_size_err;
+ unsigned int rx_desc_crc_err;
+ unsigned int rx_desc_octet_err;
+ unsigned rx_desc_line_err;
+ /* tx-errors in descriptors */
+ unsigned tx_desc_fifo_err;
+ /* rx-errors in interrupts */
+ unsigned rx_int_fifo_err;
+ unsigned rx_int_frame_dropped_err;
+ /* tx-errors in interrupts */
+ unsigned tx_int_fifo_err;
+ /* ints */
+ unsigned interrupts;
+ unsigned rx_interrupts;
+ unsigned tx_interrupts;
+ /* error combination tables */
+ /* fifo,size,crc,octet,line */
+ unsigned rx_desc_err_table[2][2][2][2][2];
+ unsigned int_err_table[2][2]; /* fifo,frame_dropped */
+ /* skbuff counters */
+ unsigned rx_skbuffs_in;
+ unsigned rx_skbuffs_out;
+ unsigned tx_skbuffs_in;
+ unsigned tx_skbuffs_out;
+/* debugging stuff for stream mode: */
+ /* rx-errors in descriptors */
+ unsigned rx_desc_fifo_err_stream;
+ unsigned rx_desc_size_err_stream;
+ unsigned rx_desc_crc_err_stream;
+ unsigned rx_desc_octet_err_stream;
+ unsigned rx_desc_line_err_stream;
+ /* tx-errors in descriptors */
+ unsigned tx_desc_fifo_err_stream;
+ /* rx-errors in interrupts */
+ unsigned rx_int_fifo_err_stream;
+ unsigned rx_int_frame_dropped_err_stream;
+ /* tx-errors in interrupts */
+ unsigned tx_int_fifo_err_stream;
+ /* ints */
+ unsigned interrupts_stream;
+ unsigned rx_interrupts_stream;
+ unsigned tx_interrupts_stream;
+ /* error combination tables */
+ /* fifo,size,crc,octet,line */
+ unsigned rx_desc_err_table_stream[2][2][2][2][2];
+ unsigned int_err_table_stream[2][2]; /* fifo,frame_dropped */
+/* other: */
+ /* tx interrupts since last timer interrupt */
+ unsigned tx_interrupts_since_last_timer;
+/* small packet counters: */
+ unsigned rx_packets_of_size_0;
+ unsigned rx_packets_of_size_1;
+ unsigned rx_packets_of_size_2;
+ unsigned rx_packets_of_size_3;
+ unsigned rx_packets_of_size_4_7;
+ unsigned rx_packets_of_size_8_15;
+ unsigned rx_packets_of_size_16_31;
+/* small packet counters for stream: */
+ unsigned rx_packets_of_size_0_stream;
+ unsigned rx_packets_of_size_1_stream;
+ unsigned rx_packets_of_size_2_stream;
+ unsigned rx_packets_of_size_3_stream;
+ unsigned rx_packets_of_size_4_7_stream;
+ unsigned rx_packets_of_size_8_15_stream;
+ unsigned rx_packets_of_size_16_31_stream;
+};
+
+struct fepci_card_private {
+ unsigned int card_number;
+ u8 *ioaddr;
+ /* Process ID of the current mailbox user
+ * (for whom it is reserved for) */
+ unsigned int ioctl_saved_pid;
+ struct pci_dev *pci_dev;
+ struct fepci_ch_private *ch_privates[CHANNELS];
+
+ wait_queue_head_t alarm_manager_wait_q;
+ struct timer_list mailbox_timer;
+
+ wait_queue_head_t stream_receive_q;
+ wait_queue_head_t stream_transmit_q;
+ wait_queue_head_t stream_both_q;
+
+ struct rw_semaphore semaphore;
+};
+
+/* Offsets to the FEPCI registers */
+enum fepci_offsets {
+ reg_custom = 0x40,
+
+ reg_first_int_mask = 0x80,
+ reg_first_int_status = 0xc0,
+
+ reg_first_rxctrl = 0x4000,
+ to_next_rxctrl = 0x80,
+
+ reg_first_txctrl = 0x6000,
+ to_next_txctrl = 0x80,
+
+ first_rx_desc = 0x10000,
+ to_next_ch_rx_desc = 0x200,
+
+ first_tx_desc = 0x18000,
+ to_next_ch_tx_desc = 0x200,
+};
+
+enum reg_custom_bits {
+ AM_interrupt_mask = 0x1,
+ AM_interrupt_status = 0x100,
+};
+
+enum reg_receive_control {
+ Rx_fifo_threshold = 0x7,
+ Receive_enable = 0x80000000,
+};
+
+enum reg_transmit_control {
+ Tx_fifo_threshold = 0x7,
+ Tx_desc_threshold = 0x700,
+ Transmit_enable = 0x80000000,
+};
+
+enum int_bits {
+ MaskFrameReceived = 0x01, MaskRxFifoError =
+ 0x02, MaskRxFrameDroppedError = 0x04,
+ MaskFrameTransmitted = 0x40, MaskTxFifoError = 0x80,
+ MaskAllInts = 0xc7,
+ IntrFrameReceived = 0x01, IntrRxFifoError =
+ 0x02, IntrRxFrameDroppedError = 0x04,
+ IntrFrameTransmitted = 0x40, IntrTxFifoError = 0x80,
+ IntrAllInts = 0xc7,
+};
+
+/* The FEPCI Rx and Tx buffer descriptors
+ * Elements are written as 32 bit for endian portability */
+
+struct fepci_desc {
+ u32 desc_a;
+ u32 desc_b;
+};
+
+enum desc_b_bits {
+ frame_length = 0xFFF,
+ fifo_error = 0x10000,
+ size_error = 0x20000,
+ crc_error = 0x40000,
+ octet_error = 0x80000,
+ line_error = 0x100000,
+ enable_transfer = 0x80000000,
+ transfer_not_done = 0x80000000,
+};
+
+/* global variables (common to whole driver, all the cards): */
+int major; /* char device major number */
+struct fepci_card_private card_privates[MAX_DEVICES];
+u32 stream_pointers;
+struct proc_dir_entry *proc_root_entry;
+
+void set_int_mask(int channel, u_char value, struct fepci_card_private *cp)
+{
+ void *address;
+ u32 shift, oldvalue;
+ DBG_PRINT("set_int_mask\n");
+ address = (cp->ioaddr) + reg_first_int_mask + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ oldvalue = inl((void *)address);
+ oldvalue &= ~(u32) (0xff << shift); /* clear bits */
+ oldvalue |= (value << shift); /* set bits */
+ outl(oldvalue, (void *)address);
+}
+
+u_char get_int_mask(int channel, void *ioaddr)
+{
+ void *address;
+ u32 shift, oldvalue;
+ DBG_PRINT("get_int_mask\n");
+ address = ioaddr + reg_first_int_mask + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ oldvalue = inl((void *)address);
+ oldvalue &= (0xff << shift); /* clear other bits */
+ return (oldvalue >> shift);
+}
+
+void clear_int(int channel, u_char value, void *ioaddr)
+{
+ void *address;
+ u32 shift, longvalue;
+ DBG_PRINT("clear_int\n");
+ address = ioaddr + reg_first_int_status + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ longvalue = value << shift;
+ outl(~longvalue, (void *)address);
+}
+
+u_char get_int_status(int channel, void *ioaddr)
+{
+ void *address;
+ u32 shift, oldvalue;
+ DBG_PRINT("get_int_status\n");
+ address = ioaddr + reg_first_int_status + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ oldvalue = inl((void *)address);
+ oldvalue &= (0xff << shift); /* clear other bits */
+ return (oldvalue >> shift);
+}
+
+void fillregisterswith_00(void *ioaddr)
+{
+ DBG_PRINT("fillregisterswith_00\n");
+ outl(0x0, (void *)(ioaddr + reg_first_rxctrl));
+ outl(0x0, (void *)(ioaddr + reg_first_txctrl));
+ outl(0x0, (void *)(ioaddr + reg_first_int_mask));
+ outl(0x0, (void *)(ioaddr + reg_first_int_status));
+ outl(0x0, (void *)(ioaddr + first_rx_desc));
+ outl(0x0, (void *)(ioaddr + first_tx_desc));
+}
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int fepci_open(struct net_device *dev);
+static void fepci_timer(unsigned long data);
+static void fepci_tx_timeout(struct net_device *dev);
+static void fepci_init_ring(struct net_device *dev);
+static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t fepci_interrupt(int irq, void *dev_instance);
+static int fepci_rx(struct net_device *dev);
+static int fepci_close(struct net_device *dev);
+static struct net_device_stats *fepci_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static void fepci_remove_one(struct pci_dev *pdev);
+
+/* proc filesystem functions introduced: */
+
+static int fepci_proc_init_driver(void);
+static void fepci_proc_cleanup_driver(void);
+static void fepci_proc_init_card(int card_number, void *card_data);
+static void fepci_proc_cleanup_card(int card_number);
+#ifdef DEBUG_PROC_FILES
+static void fepci_proc_init_channel(int card_number, int channel_number,
+ void *channel_data);
+static void fepci_proc_cleanup_channel(int card_number, int channel_number);
+#endif /* DEBUG_PROC_FILES */
+
+/* char device operations: */
+
+ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+ loff_t *f_pos);
+int fepci_char_open(struct inode *inode, struct file *filp);
+int fepci_char_release(struct inode *inode, struct file *filp);
+int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma);
+int fepci_char_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+struct file_operations fepci_char_fops = {
+ read:fepci_char_read,
+ ioctl:fepci_char_ioctl,
+ open:fepci_char_open,
+ release:fepci_char_release,
+ mmap:fepci_char_mmap,
+};
+
+int fepci_char_open(struct inode *inode, struct file *filp)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ DBG_PRINT("fepci_char_open\n");
+ if (unlikely(minor >= find_cnt || card_privates[minor].pci_dev == NULL))
+ return -ENXIO;
+ filp->f_op = &fepci_char_fops;
+ if (unlikely(!try_module_get(THIS_MODULE)))
+ return -EBUSY;
+ return 0;
+}
+
+int fepci_char_release(struct inode *inode, struct file *filp)
+{
+ DBG_PRINT("fepci_char_release\n");
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+void fepci_vma_open(struct vm_area_struct *vma)
+{
+ DBG_PRINT("fepci_vma_open\n");
+}
+
+void fepci_vma_close(struct vm_area_struct *vma)
+{
+ DBG_PRINT("fepci_vma_close\n");
+ module_put(THIS_MODULE);
+}
+
+static struct vm_operations_struct fepci_vm_ops = {
+ open:fepci_vma_open,
+ close:fepci_vma_close,
+};
+
+int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ unsigned long offset = VMA_OFFSET(vma);
+ unsigned long size = vma->vm_end - vma->vm_start;
+
+ u32 virtual_address = 0;
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_ops = &fepci_vm_ops;
+ vma->vm_file = filp;
+
+ if (offset == STREAM_BUFFER_POINTER_AREA) {
+ virtual_address = stream_pointers;
+ if (virtual_address == 0) {
+ printk(KERN_WARNING "%s: mmap: internal error.\n",
+ fepci_name);
+ return -ENOMEM;
+ }
+ if (size > (1 << PAGE_SHIFT)) {
+ printk(KERN_WARNING
+ "%s: mmap: area size over range.\n", fepci_name);
+ return -EINVAL;
+ }
+ } else {
+ unsigned int page;
+
+ unsigned int card;
+ unsigned int channel;
+ unsigned int area; /* 0=rx, 1=tx */
+
+ card = (offset >> CARD_ADDRESS_SHIFT) & 0xf;
+ channel = (offset >> CHANNEL_ADDRESS_SHIFT) & 0xf;
+ area = (offset >> AREA_ADDRESS_SHIFT) & 0xf;
+ page = (offset & 0xffff); /* >> PAGE_SHIFT; */
+
+ if (area == 0) {
+ /* if there really is such card */
+ if (card < find_cnt && card_privates[card].pci_dev)
+ virtual_address =
+ (u32) card_privates[card].
+ ch_privates[channel]->rx_buffer;
+ else
+ goto INVALID;
+ } else if (area == 1) {
+ /* if there really is such card */
+ if (card < find_cnt && card_privates[card].pci_dev)
+ virtual_address =
+ (u32) card_privates[card].
+ ch_privates[channel]->tx_buffer;
+ else
+ goto INVALID;
+ } else {
+ INVALID:
+ DBG_PRINT("%s: mmap: invalid address 0x%x\n",
+ fepci_NAME, virtual_address);
+ return -EINVAL;
+ }
+ if (virtual_address == 0)
+ goto INVALID;
+ }
+
+ if (!try_module_get(THIS_MODULE))
+ return -EBUSY;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ {
+ unsigned pfn = PFN_DOWN(virt_to_phys((void *)virtual_address));
+ int error = io_remap_pfn_range(vma, vma->vm_start, pfn,
+ size, vma->vm_page_prot);
+ if (unlikely(error))
+ return error;
+ }
+ fepci_vma_open(vma);
+ return 0;
+}
+
+/* mmap operations end */
+
+/* char operations start */
+
+void fepci_copy_to_user(unsigned long to, void *from, unsigned long len,
+ int shrink)
+{
+ unsigned int i;
+ DBG_PRINT("fepci_copy_to_user\n");
+ if (shrink) {
+ for (i = 0; i < len; i += 2) {
+ put_user((((unsigned long *)from)[i / 2]) & 0xff,
+ (unsigned char *)(to + i));
+ put_user((((unsigned long *)from)[i / 2]) >> 8,
+ (unsigned char *)(to + i + 1));
+ }
+ } else {
+ for (i = 0; i < len; i += 4)
+ put_user(((unsigned long *)from)[i / 4],
+ (unsigned long *)(to + i));
+ }
+}
+
+void fepci_copy_from_user(void *to, unsigned long from, unsigned long len,
+ int enlarge)
+{
+ unsigned int i;
+ if (enlarge) {
+ for (i = 0; i < len; i += 2) {
+ unsigned char temp1;
+ unsigned char temp2;
+ get_user(temp1, (unsigned char *)(from + i));
+ get_user(temp2, (unsigned char *)(from + i + 1));
+ *((unsigned long *)(to + i * 2)) = temp1 + (temp2 << 8);
+ }
+ } else {
+ for (i = 0; i < len; i += 4)
+ get_user(((unsigned long *)to)[i / 4],
+ (unsigned long *)(from + i));
+ }
+}
+
+unsigned get_semafore(struct fepci_real_mailbox *mailbox)
+{
+ unsigned semafore = readb(&mailbox->Semafore_Mail_number);
+ DBG_PRINT("get_semafore = %x\n", semafore);
+ return semafore;
+}
+
+int set_semafore(struct fepci_real_mailbox *mailbox, unsigned semafore)
+{
+ u32 number = inl(&mailbox->Semafore_Mail_number);
+ DBG_PRINT("got number %u at %p.\n", number,
+ &mailbox->Semafore_Mail_number);
+ number = ((number & ~0xFF) | semafore) + (1 << 8);
+ DBG_PRINT
+ ("increases the mail number to %u at the same time at %p.\n",
+ number, &mailbox->Semafore_Mail_number);
+ outl(number, &mailbox->Semafore_Mail_number);
+ DBG_PRINT("set_semafore %p, %u returns 0.\n", mailbox, semafore);
+ return 0;
+}
+
+static void fepci_mailbox_timer(unsigned long data)
+{
+ int card_number = data;
+ unsigned int *saved_pid = &card_privates[card_number].ioctl_saved_pid;
+ void *ioaddr = card_privates[card_number].ioaddr;
+ struct fepci_real_mailbox *real_mailbox =
+ (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
+
+ set_semafore(real_mailbox, 0x0);
+ *saved_pid = 0;
+}
+
+int fepci_char_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ void *ioaddr;
+ struct fepci_real_mailbox *real_mailbox;
+ int retval = 0;
+ unsigned int *saved_pid;
+ unsigned int my_pid;
+
+ if (minor >= find_cnt || card_privates[minor].pci_dev == NULL) {
+ printk(KERN_WARNING
+ "%s: trying to access a card that does not exist.\n",
+ fepci_NAME);
+ /* if trying to access a card that does not exist */
+ return -ENXIO;
+ }
+
+ DBG_PRINT("fepci_char_ioctl minor %u.\n", minor);
+
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ if (!access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ if (!access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+
+ ioaddr = card_privates[minor].ioaddr;
+ real_mailbox =
+ (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
+ saved_pid = &card_privates[minor].ioctl_saved_pid;
+ my_pid = current->pid;
+
+ switch (cmd) {
+ case FEPCI_IOCTL_STREAM_TRANSMIT_POLL:
+ /* here: arg == channel number */
+ if (arg < 0 || arg >= CHANNELS
+ || !(card_privates[minor].ch_privates[arg]->stream_on))
+ return 0x2;
+ {
+ u32 pointer = *USER_TX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+ wait_event_interruptible((card_privates[minor].
+ stream_transmit_q)
+ ,
+ (pointer !=
+ *USER_TX_S_FAKE_POINTER
+ (minor, arg,
+ stream_pointers)));
+ return 0x1;
+ }
+ return retval;
+ case FEPCI_IOCTL_STREAM_RECEIVE_POLL:
+ /* here: arg == channel number */
+ if (arg < 0 || arg >= CHANNELS
+ || !(card_privates[minor].ch_privates[arg]->stream_on))
+ return 0x2;
+ {
+ u32 pointer = *USER_RX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+ wait_event_interruptible((card_privates[minor].
+ stream_receive_q)
+ ,
+ (pointer !=
+ *USER_RX_S_FAKE_POINTER
+ (minor, arg,
+ stream_pointers)));
+ retval = 0x1;
+ }
+ return retval;
+ case FEPCI_IOCTL_STREAM_BOTH_POLL:
+ /* here: arg == channel number */
+ if (arg < 0 || arg >= CHANNELS
+ || !(card_privates[minor].ch_privates[arg]->stream_on))
+ return 0x2;
+ {
+ u32 temp_tx_pointer =
+ *USER_TX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+ u32 temp_rx_pointer =
+ *USER_RX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+
+ wait_event_interruptible((card_privates[minor].
+ stream_both_q)
+ ,
+ (temp_tx_pointer !=
+ *USER_TX_S_FAKE_POINTER
+ (minor, arg, stream_pointers))
+ || (temp_rx_pointer !=
+ *USER_RX_S_FAKE_POINTER
+ (minor, arg,
+ stream_pointers)));
+ retval = 0x1;
+ }
+ return retval;
+ case FEPCI_IOCTL_R_SHARED_MEM:
+ DBG_PRINT(" %s: ioctl read shared mem commanded.\n",
+ fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_SHARED_MEM_OFFSETT,
+ _IOC_SIZE(cmd), 0);
+ break;
+ case FEPCI_IOCTL_W_SHARED_MEM:
+ DBG_PRINT(" %s: ioctl write shared mem commanded.\n",
+ fepci_NAME);
+ fepci_copy_from_user(ioaddr + FEPCI_SHARED_MEM_OFFSETT,
+ arg, _IOC_SIZE(cmd), 0);
+ break;
+ case FEPCI_IOCTL_G_IDENTIFICATION:
+ DBG_PRINT(" %s: IOCTL_G_IDENTIFICATION commanded.\n",
+ fepci_NAME);
+ fepci_copy_to_user(arg,
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_G_FEATURES:
+ DBG_PRINT(" %s: IOCTL_G_FEATURES commanded.\n", fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_FEATURES_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_G_SETTINGS:
+ DBG_PRINT(" %s: IOCTL_G_SETTINGS commanded.\n", fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_SETTINGS_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_G_STATUS:
+ DBG_PRINT(" %s: IOCTL_G_STATUS commanded.\n", fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_STATUS_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_B_POLL:
+ DBG_PRINT(" %s: IOCTL_B_POLL commanded.\n", fepci_NAME);
+ retval = get_semafore(real_mailbox);
+ if ((retval == 0x20 || retval == 0x21 || retval == 0x40)
+ && *saved_pid != my_pid)
+ retval = 0x7;
+ del_timer_sync(&card_privates[minor].mailbox_timer);
+ card_privates[minor].mailbox_timer.expires = jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.function =
+ &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+ break;
+ case FEPCI_IOCTL_B_GRAB:
+ DBG_PRINT("%s: IOCTL_B_GRAB commanded.\n", fepci_NAME);
+ if ((my_pid != *saved_pid) && (*saved_pid != 0)) {
+ retval = 0x2;
+ break;
+ }
+ DBG_PRINT("%s: IOCTL_B_GRAB getting semaphore.\n", fepci_NAME);
+ if (get_semafore(real_mailbox) == 0x0) {
+ DBG_PRINT("%s: IOCTL_B_GRAB setting semaphore.\n",
+ fepci_NAME);
+ set_semafore(real_mailbox, 0x40);
+ DBG_PRINT("%s: IOCTL_B_GRAB sleeping.\n", fepci_NAME);
+ msleep(1); /* delay at least 1 millisecond */
+ DBG_PRINT
+ ("%s: IOCTL_B_GRAB getting semaphore again.\n",
+ fepci_NAME);
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ retval = 0x0;
+ DBG_PRINT
+ ("%s: IOCTL_B_GRAB saving pid to %p.\n",
+ fepci_NAME, saved_pid);
+ *saved_pid = my_pid;
+ DBG_PRINT
+ ("%s: IOCTL_B_GRAB deleting timer at %p.\n",
+ fepci_NAME,
+ &card_privates[minor].mailbox_timer);
+ del_timer_sync(&card_privates[minor].
+ mailbox_timer);
+ card_privates[minor].mailbox_timer.
+ expires = jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.
+ function = &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+ break;
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ default:
+ retval = 0xff;
+ }
+ } else {
+ switch (get_semafore(real_mailbox)) {
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ default:
+ retval = 0xff;
+ }
+ }
+ break;
+ case FEPCI_IOCTL_B_RELEASE:
+ DBG_PRINT(" %s: IOCTL_B_RELEASE commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ break;
+ }
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ case 0x20:
+ retval = 0x0;
+ set_semafore(real_mailbox, 0x0);
+ *saved_pid = 0;
+ del_timer(&card_privates[minor].mailbox_timer);
+ break;
+ case 0x21:
+ retval = 0x04;
+ break;
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ default:
+ retval = 0xff;
+ }
+ break;
+ case FEPCI_IOCTL_B_S_CMAIL:
+ DBG_PRINT(" %s: IOCTL_B_S_CMAIL commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ break;
+ }
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ case 0x20:
+ case 0x21:
+ /* copy the mailbox */
+ fepci_copy_from_user(ioaddr +
+ FEPCI_MAILBOX_OFFSETT + 4,
+ arg + 2, _IOC_SIZE(cmd) - 2, 1);
+ /* semafore -> 10 */
+ set_semafore(real_mailbox, 0x10);
+ retval = 0x0;
+
+ del_timer_sync(&card_privates[minor].mailbox_timer);
+ card_privates[minor].mailbox_timer.expires =
+ jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.function =
+ &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+
+ break;
+
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ case 0x0:
+ retval = 0x3;
+ break;
+ default:
+ retval = 0xff;
+ }
+ break;
+ case FEPCI_IOCTL_B_S_QMAIL:
+ DBG_PRINT(" %s: IOCTL_B_S_QMAIL commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ break;
+ }
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ case 0x20:
+ case 0x21:
+ /* copy the mailbox; */
+ fepci_copy_from_user(ioaddr +
+ FEPCI_MAILBOX_OFFSETT + 4,
+ arg + 2, _IOC_SIZE(cmd) - 2, 1);
+ /* semafore -> 11 */
+ set_semafore(real_mailbox, 0x11);
+ retval = 0x0;
+
+ del_timer_sync(&card_privates[minor].mailbox_timer);
+ card_privates[minor].mailbox_timer.expires =
+ jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.function =
+ &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+
+ break;
+
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ case 0x0:
+ retval = 0x3;
+ break;
+ default:
+ retval = 0xff;
+ }
+ break;
+ case FEPCI_IOCTL_B_G_MAIL:
+ DBG_PRINT(" %s: IOCTL_B_G_MAIL commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ } else {
+ switch (get_semafore(real_mailbox)) {
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ case 0x40:
+ case 0x20:
+ case 0x21:
+ retval = 0x0;
+ fepci_copy_to_user(arg,
+ ioaddr +
+ FEPCI_MAILBOX_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+
+ del_timer_sync(&card_privates[minor].
+ mailbox_timer);
+ card_privates[minor].mailbox_timer.
+ expires = jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.
+ function = &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+
+ break;
+ case 0x0:
+ retval = 0x3;
+ break;
+ default:
+ retval = 0xff;
+ }
+ }
+ if (retval != 0) {
+ static unsigned char seven = 7;
+ /* copy four lowest bytes from the mailbox */
+ fepci_copy_to_user(arg,
+ ioaddr + FEPCI_MAILBOX_OFFSETT,
+ 4, 1);
+ /* lowest byte = 0x7 */
+ __put_user(arg, &seven);
+ }
+ break;
+ case FEPCI_IOCTL_ALARM_MANAGER:
+ DBG_PRINT(" %s: IOCTL_ALARM_MANAGER commanded.\n", fepci_NAME);
+ interruptible_sleep_on(&
+ (card_privates[minor].
+ alarm_manager_wait_q));
+ return retval;
+ default:
+ DBG_PRINT(" %s: Unknown ioctl command 0x%x.\n", fepci_NAME,
+ cmd);
+ return -ENOTTY;
+ }
+ return retval;
+}
+
+ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+ loff_t *f_pos)
+{
+ DBG_PRINT("fepci_char_read\n");
+ if (count > 1)
+ count = 1;
+ if (copy_to_user(buf, "\n", count))
+ return -EFAULT;
+ return count;
+}
+
+static int fepci_register_char_device(void)
+{
+ int error =
+ register_chrdev(0 /* dynamic */ , fepci_name, &fepci_char_fops);
+ if (error < 0)
+ printk(KERN_WARNING
+ "%s: unable to register char device.\n", fepci_NAME);
+ else
+ DBG_PRINT("%s: registered char device, major:0x%x.\n",
+ fepci_NAME, error);
+ return error;
+}
+
+void fepci_unregister_char_device(void)
+{
+ unregister_chrdev(major, fepci_name);
+}
+
+/* char operations end */
+
+/* stream operations start */
+
+static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance);
+static int fepci_stream_close(struct net_device *dev);
+
+static int fepci_stream_open(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned tx_pages, rx_pages, tx_order, rx_order;
+ unsigned page_number;
+ unsigned int i;
+
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_eth_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Interface is in Ethernet mode, "
+ "cannot open stream interface.\n", fepci_NAME);
+ BUSY:
+ return -EBUSY;
+ }
+ if (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Interface is already in stream mode, "
+ "cannot open stream interface.\n", fepci_NAME);
+ goto BUSY;
+ }
+
+ if (fp->this_card_priv->pci_dev == NULL) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -ENXIO;
+ }
+
+ fp->bufsize = 1 << fp->bufsize_order;
+
+ if (fp->fake_unit_sz_order < 5) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Unit size has to be at least 32 Bytes.\n",
+ fepci_NAME);
+ INVALID:
+ return (-EINVAL);
+ }
+
+ if (fp->fake_unit_sz_order >= fp->bufsize_order) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Bufsize has to be greater than unit size.\n",
+ fepci_NAME);
+ goto INVALID;
+ }
+
+ if (fp->fake_unit_sz_order >= MAX_UNIT_SZ_ORDER) {
+ fp->unit_sz_order = MAX_UNIT_SZ_ORDER;
+ } else {
+ fp->unit_sz_order = fp->fake_unit_sz_order;
+ }
+
+ fp->fake_unit_sz = 1 << fp->fake_unit_sz_order;
+ fp->unit_sz = 1 << fp->unit_sz_order;
+ fp->units = 1 << (fp->bufsize_order - fp->unit_sz_order);
+ fp->fake_units = 1 << (fp->bufsize_order - fp->fake_unit_sz_order);
+
+ /* reserve memory */
+ if (fp->bufsize_order < PAGE_SHIFT) {
+ tx_order = rx_order = 0;
+ tx_pages = rx_pages = 1;
+ } else {
+ tx_order = fp->bufsize_order - PAGE_SHIFT;
+ tx_pages = 1 << tx_order;
+ rx_order = tx_order + 1;
+ rx_pages = 1 << rx_order;
+ }
+ fp->in_stream_mode = 1;
+ fp->tx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, tx_order);
+ if (!fp->tx_buffer)
+ goto NO_MEMORY;
+ fp->rx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, rx_order);
+ if (!fp->rx_buffer) {
+ NO_MEMORY:
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate memory for buffers.\n",
+ fepci_NAME);
+ fepci_stream_close(dev);
+ return (-ENOMEM);
+ }
+
+ for (page_number = 0; page_number < rx_pages; page_number++)
+ /* make pages reserved to allow remappping pages
+ with io_remap_pfn_range */
+ SetPageReserved(virt_to_page
+ ((unsigned long)fp->rx_buffer +
+ (page_number << PAGE_SHIFT)));
+ for (page_number = 0; page_number < tx_pages; page_number++)
+ /* make pages reserved to allow remappping pages
+ with io_remap_pfn_range */
+ SetPageReserved(virt_to_page
+ ((unsigned long)fp->tx_buffer +
+ (page_number << PAGE_SHIFT)));
+
+ for (i = 0; i < (fp->bufsize) / 4; i++)
+ fp->tx_buffer[i] = 0xffffffff;
+
+ /* + fp->channel_number; */
+ *USER_RX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
+ stream_pointers) = 0;
+ /* + fp->channel_number; */
+ *USER_TX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
+ stream_pointers) = 0;
+ /* + fp->channel_number; */
+ *USER_RX_S_FAKE_POINTER(fp->this_card_priv->card_number,
+ fp->channel_number, stream_pointers) = 0;
+ /* + fp->channel_number; */
+ *USER_TX_S_FAKE_POINTER(fp->this_card_priv->card_number,
+ fp->channel_number, stream_pointers) = 0;
+
+ DBG_PRINT("%s: Bufsize is 0x%x.\n", fepci_NAME, fp->bufsize);
+ DBG_PRINT("%s: Unit_size is 0x%x.\n", fepci_NAME, fp->unit_sz);
+ DBG_PRINT("%s: Number of units is 0x%x.\n", fepci_NAME, fp->units);
+
+ DBG_PRINT("%s: Fake_unit_size is 0x%x.\n", fepci_NAME,
+ fp->fake_unit_sz);
+ DBG_PRINT("%s: Number of fake units is 0x%x.\n", fepci_NAME,
+ fp->fake_units);
+
+ /* init ring buffers */
+ for (i = 0; i < MAX_RX_UNITS; i++)
+ fp->rx_unit[i] =
+ (u32 *) ((u32) (fp->rx_buffer) + (fp->unit_sz * i));
+ for (i = 0; i < MAX_TX_UNITS; i++)
+ fp->tx_unit[i] =
+ (u32 *) ((u32) (fp->tx_buffer) + (fp->unit_sz * i));
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ outl(0, &fp->rx_desc[i].desc_a);
+ outl(0, &fp->rx_desc[i].desc_b);
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ outl(0, &fp->tx_desc[i].desc_a);
+ outl(0, &fp->tx_desc[i].desc_b);
+ }
+
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static int fepci_stream_start(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned i;
+ down_write(&fp->this_card_priv->semaphore);
+
+ if (fp->in_stream_mode == 0) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Interface is not in stream mode, "
+ "streaming cannot be started.\n", fepci_NAME);
+ return (-EBUSY);
+ }
+ if (fp->stream_on) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Streaming is already on, "
+ "streaming cannot be started.\n", fepci_NAME);
+ return (-EBUSY);
+ }
+
+ {
+ /* reserve irq */
+ int error = request_irq(dev->irq, &fepci_stream_interrupt,
+ IRQF_SHARED, dev->name, dev);
+ if (error) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate IRQ %d, error 0x%x\n",
+ fepci_NAME, dev->irq, error);
+ return -ENOMEM;
+ }
+ }
+
+ fp->stream_on = 1;
+
+ /* sending &receiving on, start from the beginning of the buffer */
+ fp->cur_tx_unit = 0;
+ fp->cur_rx_unit = 0;
+ fp->cur_tx = 0;
+ fp->cur_rx = 0;
+
+ /* all the descriptors ready to go: */
+ for (i = 0; i < min(RX_RING_SIZE, TX_RING_SIZE); i++) {
+ u32 bus_address = pci_map_single(fp->this_card_priv->pci_dev,
+ fp->
+ rx_unit[(fp->cur_rx_unit +
+ i) % fp->units],
+ fp->unit_sz,
+ PCI_DMA_FROMDEVICE);
+ outl(bus_address,
+ &fp->
+ rx_desc[(fp->cur_rx + i) & (RX_RING_SIZE - 1)].desc_a);
+ if (unlikely(pci_dma_mapping_error(bus_address)))
+ printk(KERN_WARNING
+ "%s: failed to map DMA buffer.\n", fepci_NAME);
+ else {
+ if (!
+ (inl
+ (&fp->
+ rx_desc[(fp->cur_rx + i) & (RX_RING_SIZE -
+ 1)].
+ desc_b) & enable_transfer))
+ outl(enable_transfer,
+ &fp->rx_desc[(fp->cur_rx + i) %
+ RX_RING_SIZE].desc_b);
+ }
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ fp->tx_unit[(fp->cur_tx_unit + i) %
+ fp->units], fp->unit_sz,
+ PCI_DMA_TODEVICE);
+ outl(bus_address,
+ &fp->
+ tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE - 1)].desc_a);
+ if (unlikely(pci_dma_mapping_error(bus_address)))
+ printk(KERN_WARNING
+ "%s: failed to map DMA buffer.\n", fepci_NAME);
+ else {
+ if (!
+ (inl
+ (&fp->
+ tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE -
+ 1)].
+ desc_b) & enable_transfer))
+ outl(enable_transfer |
+ (fp->unit_sz & frame_length),
+ &fp->
+ tx_desc[(fp->cur_tx +
+ i) & (TX_RING_SIZE - 1)].desc_b);
+ }
+ }
+
+ /* irq on */
+ set_int_mask(fp->channel_number,
+ MaskFrameReceived | MaskFrameTransmitted |
+ MaskRxFifoError | MaskRxFrameDroppedError |
+ MaskTxFifoError, fp->this_card_priv);
+ {
+ void *ioaddr = (void *)dev->base_addr;
+ /* Start Rx and Tx channels */
+ outl(Receive_enable |
+ (Rx_fifo_threshold & RX_FIFO_THRESHOLD_STREAM_MODE),
+ (void *)(ioaddr + fp->reg_rxctrl));
+ outl((Transmit_enable |
+ (Tx_desc_threshold &
+ (TX_DESC_THRESHOLD_STREAM_MODE << 8)) |
+ (Tx_fifo_threshold & TX_FIFO_THRESHOLD_STREAM_MODE)),
+ (void *)(ioaddr + fp->reg_txctrl));
+ }
+ up_write(&fp->this_card_priv->semaphore);
+
+ return 0;
+}
+
+static int fepci_stream_stop(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ void *ioaddr = (void *)dev->base_addr;
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_stream_mode == 0) {
+ up_write(&fp->this_card_priv->semaphore);
+ return (1);
+ }
+ fp->stream_on = 0;
+ /* Stop Rx and Tx channels. */
+ outl(0x0, (void *)(ioaddr + fp->reg_rxctrl));
+ outl(0x0, (void *)(ioaddr + fp->reg_txctrl));
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
+
+ /* unregister irq */
+ free_irq(dev->irq, dev);
+
+ {
+ unsigned i = min(RX_RING_SIZE, TX_RING_SIZE) - 1;
+ do {
+ u32 bus_address;
+ if (likely
+ (!pci_dma_mapping_error
+ (bus_address = inl(&fp->rx_desc[i].desc_a))))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz,
+ PCI_DMA_FROMDEVICE);
+ if (likely
+ (!pci_dma_mapping_error
+ (bus_address = inl(&fp->tx_desc[i].desc_a))))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz, PCI_DMA_TODEVICE);
+ }
+ while (i--);
+ }
+
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static int fepci_stream_close(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned rx_pages, tx_pages, rx_order, tx_order;
+
+ if (fepci_stream_stop(dev))
+ return -ENODEV;
+ down_write(&fp->this_card_priv->semaphore);
+ if (!(fp->in_stream_mode)) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -ENODEV;
+ }
+ /* release memory */
+ if (fp->bufsize_order < PAGE_SHIFT) {
+ tx_order = rx_order = 0;
+ tx_pages = rx_pages = 1;
+ } else {
+ rx_order = (int)((fp->bufsize_order) - PAGE_SHIFT + 1);
+ rx_pages = 1 << rx_order;
+ tx_order = (int)((fp->bufsize_order) - PAGE_SHIFT);
+ tx_pages = 1 << tx_order;
+ }
+ if (fp->rx_buffer) {
+ unsigned page_number;
+ for (page_number = 0; page_number < rx_pages; page_number++)
+ /* turn pages back to non-reserved */
+ ClearPageReserved(virt_to_page
+ ((unsigned long)fp->rx_buffer +
+ (page_number << PAGE_SHIFT)));
+ free_pages((u32) fp->rx_buffer, rx_order);
+ fp->rx_buffer = NULL;
+ }
+ if (fp->tx_buffer) {
+ unsigned page_number;
+ for (page_number = 0; page_number < tx_pages; page_number++)
+ /* turn pages back to non-reserved */
+ ClearPageReserved(virt_to_page
+ ((unsigned long)fp->tx_buffer +
+ (page_number << PAGE_SHIFT)));
+ free_pages((u32) fp->tx_buffer, tx_order);
+ fp->tx_buffer = NULL;
+ }
+
+ fp->in_stream_mode = 0;
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ struct fepci_ch_private *fp = dev->priv;
+ void *ioaddr = (void *)dev->base_addr;
+ u32 intr_status = get_int_status(fp->channel_number, ioaddr);
+ bool fifo, dropped;
+ unsigned int temp_rx;
+ unsigned int temp_rx_unit;
+ unsigned int temp_tx;
+ unsigned int temp_tx_unit;
+
+ clear_int(fp->channel_number, intr_status, ioaddr);
+
+ /* debugging */
+ fp->interrupts_stream++;
+ fifo = (intr_status & IntrRxFifoError) != 0;
+ dropped = (intr_status & IntrRxFrameDroppedError) != 0;
+ fp->int_err_table_stream[fifo][dropped]++;
+ if (intr_status & IntrFrameReceived)
+ fp->rx_interrupts_stream++;
+ if (intr_status & IntrFrameTransmitted)
+ fp->tx_interrupts_stream++;
+ if (fifo)
+ fp->rx_int_fifo_err_stream++;
+ if (dropped)
+ fp->rx_int_frame_dropped_err_stream++;
+ if (intr_status & IntrTxFifoError)
+ fp->tx_int_fifo_err_stream++;
+ /* first update cur_rx, and do stuff if it has moved
+ (+ packets have been received) */
+ {
+ temp_rx = fp->cur_rx;
+ /* has been received */
+ while ((inl(&fp->rx_desc[fp->cur_rx].desc_b) &
+ transfer_not_done) == 0
+ /* stop if made one round */
+ && temp_rx != ((fp->cur_rx + 1) & (RX_RING_SIZE - 1))) {
+ u32 bus_address;
+ if (likely(!pci_dma_mapping_error(bus_address =
+ inl(&fp->
+ rx_desc[fp->
+ cur_rx].
+ desc_a))))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz,
+ PCI_DMA_FROMDEVICE);
+ fp->cur_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
+ fp->cur_rx_unit = (fp->cur_rx_unit + 1);
+ fp->cur_rx_unit *= fp->cur_rx_unit < fp->units;
+
+ *USER_RX_S_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) = fp->cur_rx_unit;
+ *USER_RX_S_FAKE_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) =
+ fp->cur_rx_unit * fp->unit_sz / fp->fake_unit_sz;
+ wake_up_interruptible(&
+ (fp->this_card_priv->
+ stream_receive_q));
+ wake_up_interruptible(&
+ (fp->this_card_priv->
+ stream_both_q));
+ }
+ }
+ /* from the first uninitialized descriptor to cur_rx */
+ temp_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
+ temp_rx_unit = (fp->cur_rx_unit + 1);
+ temp_rx_unit *= temp_rx_unit < fp->units;
+
+ while (temp_rx != fp->cur_rx) {
+ u32 desc_b = inl(&fp->rx_desc[temp_rx].desc_b);
+ if ((desc_b & transfer_not_done) == 0) {
+ bool fifo = (desc_b & fifo_error) != 0;
+ bool size = (desc_b & size_error) != 0;
+ bool crc = (desc_b & crc_error) != 0;
+ bool octet = (desc_b & octet_error) != 0;
+ bool line = (desc_b & line_error) != 0;
+ u32 length = desc_b & frame_length;
+ u32 bus_address;
+ /* update debug counters */
+ fp->rx_desc_err_table_stream[fifo]
+ [size]
+ [crc]
+ [octet]
+ [line]++;
+ if (length == 0) {
+ fp->rx_packets_of_size_0_stream++;
+ } else if (length == 1) {
+ fp->rx_packets_of_size_1_stream++;
+ } else if (length == 2) {
+ fp->rx_packets_of_size_2_stream++;
+ } else if (length == 3) {
+ fp->rx_packets_of_size_3_stream++;
+ } else if (length < 8) {
+ fp->rx_packets_of_size_4_7_stream++;
+ } else if (length < 16) {
+ fp->rx_packets_of_size_8_15_stream++;
+ } else if (length < 32) {
+ fp->rx_packets_of_size_16_31_stream++;
+ }
+ if (fifo) {
+ fp->rx_desc_fifo_err_stream++;
+ } else if (size) {
+ fp->rx_desc_size_err_stream++;
+ } else if (crc) {
+ fp->rx_desc_crc_err_stream++;
+ } else if (octet) {
+ fp->rx_desc_octet_err_stream++;
+ } else if (line) {
+ fp->rx_desc_line_err_stream++;
+ }
+ /* initialize the descriptor for transfer */
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ fp->rx_unit[temp_rx_unit],
+ fp->unit_sz, PCI_DMA_FROMDEVICE);
+ outl(bus_address, &fp->rx_desc[temp_rx].desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ outl(enable_transfer,
+ &fp->rx_desc[temp_rx].desc_b);
+ else
+ printk(KERN_WARNING
+ "%s: failed to map DMA for reception.\n",
+ fepci_NAME);
+ }
+ temp_rx = (temp_rx + 1) & (RX_RING_SIZE - 1);
+ temp_rx_unit = (temp_rx_unit + 1);
+ temp_rx_unit *= temp_rx_unit < fp->units;
+ }
+
+ /* first update cur_tx, and do stuff if it has moved
+ (+ packets have been transmitted) */
+ {
+ temp_tx = fp->cur_tx;
+ /* has been transmitted */
+ while ((inl(&fp->tx_desc[fp->cur_tx].desc_b) &
+ transfer_not_done) == 0
+ /* stop if made one round */
+ && temp_tx != ((fp->cur_tx + 1) & (TX_RING_SIZE - 1))) {
+ u32 bus_address;
+ if (likely(!pci_dma_mapping_error(bus_address =
+ inl(&fp->
+ tx_desc[fp->
+ cur_tx].
+ desc_a))))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz, PCI_DMA_TODEVICE);
+ fp->cur_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
+ fp->cur_tx_unit = (fp->cur_tx_unit + 1);
+ fp->cur_tx_unit *= fp->cur_tx_unit < fp->units;
+
+ *USER_TX_S_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) = fp->cur_tx_unit;
+ *USER_TX_S_FAKE_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) =
+ fp->cur_tx_unit * fp->unit_sz / fp->fake_unit_sz;
+ wake_up_interruptible(&
+ (fp->this_card_priv->
+ stream_transmit_q));
+ wake_up_interruptible(&
+ (fp->this_card_priv->
+ stream_both_q));
+ }
+ }
+ /* from the first uninitialized descriptor to cur_tx */
+ temp_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
+ temp_tx_unit = (fp->cur_tx_unit + 1);
+ temp_tx_unit *= temp_tx_unit < fp->units;
+
+ while (temp_tx != fp->cur_tx) {
+ u32 desc_b = inl(&fp->tx_desc[temp_tx].desc_b);
+ if ((desc_b & transfer_not_done) == 0) {
+ u32 bus_address;
+ /* update debug counters */
+ if (desc_b & fifo_error)
+ fp->tx_desc_fifo_err_stream++;
+ /* initialize the desctiptor for transfer */
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ fp->tx_unit[temp_tx_unit],
+ fp->unit_sz, PCI_DMA_TODEVICE);
+ outl(bus_address, &fp->tx_desc[temp_tx].desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ outl(enable_transfer |
+ (fp->unit_sz & frame_length),
+ &fp->tx_desc[temp_tx].desc_b);
+ else
+ printk(KERN_WARNING
+ "%s: failed to map tranmission DMA.\n",
+ fepci_NAME);
+ }
+ temp_tx = (temp_tx + 1) & (TX_RING_SIZE - 1);
+ temp_tx_unit = (temp_tx_unit + 1);
+ temp_tx_unit *= temp_tx_unit < fp->units;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* stream operations end */
+
+int fepci_rebuild_header(struct sk_buff *skb)
+{
+ DBG_PRINT("fepci_rebuild_header\n");
+ return 0;
+}
+
+static inline u16 get_common_reg_word(void *ioaddr, unsigned long offsett)
+{
+ u16 word;
+ DBG_PRINT("get_common_reg_word ioaddr %p, offsett %lu\n", ioaddr,
+ offsett);
+ __clear_bit(0, &offsett);
+ DBG_PRINT("get_common_reg_word %p\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1));
+ word = le16_to_cpu(readw
+ (ioaddr + FEPCI_IDENTIFICATION_OFFSETT +
+ (offsett << 1)));
+ DBG_PRINT("get_common_reg_word %p: %hu\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1), word);
+ return word;
+}
+
+static irqreturn_t alarm_manager_interrupt(int irq, void *pointer)
+{
+ struct fepci_card_private *card_private = pointer;
+ void *ioaddr = card_private->ioaddr;
+ /* check int status */
+ if (inl((void *)(ioaddr + reg_custom)) & AM_interrupt_status) {
+ /* clear int (zero everything, but the mask bit) */
+ outl(inl((void *)(ioaddr + reg_custom)) &
+ AM_interrupt_mask, (void *)(ioaddr + reg_custom));
+ /* wake queue */
+ wake_up(&(card_private->alarm_manager_wait_q));
+ }
+
+ return IRQ_HANDLED;
+}
+
+#ifdef FEPCI_POINT_TO_POINT
+static int is_ptp_interface(struct net_device *dev)
+{
+ char **p_ptp_if_name = retina_ptp_interfaces;
+ unsigned int i = interfaces;
+ while (i > 0 && *p_ptp_if_name != NULL) {
+ if (!strncmp(dev->name, *p_ptp_if_name, sizeof(dev->name))) {
+ return 1;
+ } else {
+ }
+ p_ptp_if_name++;
+ i--;
+ }
+ return 0;
+}
+#endif /* FEPCI_POINT_TO_POINT */
+
+static int __devinit fepci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev = NULL;
+ struct fepci_ch_private *fp = NULL;
+ int chip_idx = ent->driver_data;
+ int drv_flags = pci_id_tbl[chip_idx].drv_flags;
+ int i = pci_enable_device(pdev);
+ u32 j;
+ resource_size_t real_ioaddr;
+ void *ioaddr;
+ unsigned position;
+
+ if (i) {
+ printk(KERN_WARNING "%s: pci_enable_device returned %x.\n",
+ fepci_NAME, i);
+ return i;
+ }
+
+ pci_set_master(pdev);
+
+ i = pci_request_regions(pdev, (char *)fepci_name);
+ if (i) {
+ printk(KERN_WARNING
+ "%s: pci_request_regions returned %x.\n", fepci_NAME, i);
+ pci_disable_device(pdev);
+ return i;
+ }
+
+ /* make sure above region is MMIO */
+ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+ printk(KERN_WARNING "%s: region not MMIO region\n", fepci_NAME);
+ goto err_1;
+ }
+
+ j = pci_resource_len(pdev, 0);
+ if (j < FEPCI_SIZE) {
+ printk(KERN_WARNING
+ "%s: resource length %u less than required %u.\n",
+ fepci_NAME, j, FEPCI_SIZE);
+ goto err_1;
+ }
+
+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+ printk(KERN_WARNING "%s: no suitable DMA available.\n",
+ fepci_NAME);
+ goto err_1;
+ }
+
+ real_ioaddr = pci_resource_start(pdev, 0);
+ DBG_PRINT("pci_resource_start %lu.\n", real_ioaddr);
+ ioaddr = ioremap_nocache(real_ioaddr, FEPCI_SIZE);
+ DBG_PRINT("ioremap_nocache = %p.\n", ioaddr);
+ if (!ioaddr) {
+ printk(KERN_WARNING "%s: remapping failed.\n", fepci_NAME);
+ goto err_1_5;
+ }
+ position = 0;
+ for (; position < MAX_DEVICES; position++) {
+ down_write(&card_privates[position].semaphore);
+ if (card_privates[position].pci_dev == NULL) {
+ card_privates[position].pci_dev = pdev;
+ if (position == find_cnt)
+ find_cnt++;
+ goto FOUND;
+ }
+ up_write(&card_privates[position].semaphore);
+ }
+ printk(KERN_WARNING
+ "%s: no space to inialize device #%u.\n",
+ fepci_NAME, MAX_DEVICES + 1);
+ goto err_2;
+ FOUND:
+ card_privates[position].card_number = position;
+ card_privates[position].ioaddr = ioaddr;
+ card_privates[position].pci_dev = pdev;
+ DBG_PRINT("fillregisterswith_00 %p.\n", ioaddr);
+ fillregisterswith_00(ioaddr);
+
+ fepci_proc_init_card(position, (void *)&(card_privates[position]));
+
+ init_waitqueue_head(&(card_privates[position].alarm_manager_wait_q));
+ init_waitqueue_head(&(card_privates[position].stream_transmit_q));
+ init_waitqueue_head(&(card_privates[position].stream_receive_q));
+ init_waitqueue_head(&(card_privates[position].stream_both_q));
+
+ init_timer(&card_privates[position].mailbox_timer);
+
+ DBG_PRINT("request_irq %d, %s.\n", pdev->irq, fepci_alarm_manager_name);
+ i = request_irq(pdev->irq, &alarm_manager_interrupt,
+ IRQF_SHARED,
+ fepci_alarm_manager_name, &card_privates[position]);
+ if (i) {
+ up_write(&card_privates[position].semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate IRQ %d alarm manager: 0x%x\n",
+ fepci_NAME, pdev->irq, i);
+ goto err_2;
+ }
+ DBG_PRINT("alarm manager int on %p.\n", (void *)(ioaddr + reg_custom));
+ outl(AM_interrupt_mask, (void *)(ioaddr + reg_custom));
+ /* alarm manager int on */
+
+ for (j = 0; j < CHANNELS; j++) {
+ DBG_PRINT("alloc_etherdev %u.\n", j);
+ dev = alloc_etherdev(sizeof(struct fepci_ch_private));
+ if (!dev) {
+ printk(KERN_WARNING
+ "%s: cannot allocate ethernet device\n",
+ fepci_NAME);
+ continue;
+ }
+
+ fp = dev->priv;
+ fp->minor = position; /* * CHANNELS + j; */
+ /* name := xxx01..xxxnn */
+ memcpy(dev->name, fepci_netdev_name, 6);
+ /* dev->name[3]= j+0x30; channel number -> ascii */
+ /* minor number -> ascii */
+ dev->name[4] = ((fp->minor * CHANNELS + j) % 10) + 0x30;
+ /* minor number -> ascii */
+ dev->name[3] = ((fp->minor * CHANNELS + j) / 10) + 0x30;
+
+ SET_MODULE_OWNER(dev);
+ DBG_PRINT("clear_int %u, %x, %p.\n", j, IntrAllInts, ioaddr);
+ clear_int(j, IntrAllInts, ioaddr);
+ DBG_PRINT("ether_setup %p.\n", dev);
+ ether_setup(dev);
+
+ /* HW_ADDR: 00:rnd:rnd:rnd:rnd:05 */
+ dev->dev_addr[0] = 0;
+ get_random_bytes(&(dev->dev_addr[1]), 4);
+ dev->dev_addr[5] = 5;
+ /* HW_ADDR is got using the mailbox: */
+ {
+ struct fepci_real_mailbox *real_mailbox =
+ (struct fepci_real_mailbox *)
+ (ioaddr + FEPCI_MAILBOX_OFFSETT);
+ unsigned long waituntil;
+
+ set_semafore(real_mailbox, 0x40);
+ outl(0x1 /*size */ +
+ (0x8 << 8) /* get mac command */ ,
+ &real_mailbox->Size_Command);
+ set_semafore(real_mailbox, 0x11);
+
+ waituntil = jiffies + HZ;
+ while (time_before(jiffies, waituntil) &&
+ get_semafore(real_mailbox) != 0x20) {
+ DBG_PRINT("jiffies %lu < waituntil %lu.\n",
+ jiffies, waituntil);
+ msleep(0);
+ }
+
+ /* 14.5.2004 JT: Made this safer. */
+ if (get_semafore(real_mailbox) == 0x20) {
+ dev->dev_addr[5] =
+ readb(&real_mailbox->Data[0 + 3 * j]);
+ dev->dev_addr[4] =
+ readb(((u8 *) & real_mailbox->
+ Data[0 + 3 * j]) + 1);
+ dev->dev_addr[3] =
+ readb(&real_mailbox->Data[1 + 3 * j]);
+ dev->dev_addr[2] =
+ readb(((u8 *) & real_mailbox->
+ Data[1 + 3 * j]) + 1);
+ dev->dev_addr[1] =
+ readb(&real_mailbox->Data[2 + 3 * j]);
+ dev->dev_addr[0] =
+ readb(((u8 *) & real_mailbox->
+ Data[2 + 3 * j]) + 1);
+ }
+
+ set_semafore(real_mailbox, 0x0);
+ }
+ dev->addr_len = 6;
+
+ dev->base_addr = (unsigned long)ioaddr;
+ dev->irq = pdev->irq;
+ DBG_PRINT("alarm pci_set_drvdata %p, %p.\n", pdev, dev);
+ if (j == 0)
+ pci_set_drvdata(pdev, dev);
+
+ fp->drv_flags = drv_flags;
+
+ fp->rx_desc =
+ (struct fepci_desc *)(dev->base_addr + first_rx_desc +
+ j * to_next_ch_rx_desc);
+ fp->tx_desc =
+ (struct fepci_desc *)(dev->base_addr + first_tx_desc +
+ j * to_next_ch_tx_desc);
+
+ fp->channel_number = j; /*channel in this device */
+ fp->this_dev = dev;
+
+ fp->this_card_priv = &card_privates[position];
+ fp->this_card_priv->ch_privates[j] = fp;
+
+ fp->cur_tx = 0;
+
+ fp->in_stream_mode = 0;
+ fp->in_eth_mode = 0;
+
+ fp->reg_rxctrl = reg_first_rxctrl + j * to_next_rxctrl;
+ fp->reg_txctrl = reg_first_txctrl + j * to_next_txctrl;
+
+ /* The FEPCI specific entries in the device structure */
+ dev->open = &fepci_open;
+ dev->hard_start_xmit = &fepci_start_xmit;
+ dev->stop = &fepci_close;
+ dev->get_stats = &fepci_get_stats;
+ dev->set_multicast_list = &set_rx_mode;
+ dev->do_ioctl = &netdev_ioctl;
+ dev->tx_timeout = fepci_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+#ifdef FEPCI_POINT_TO_POINT
+ if (is_ptp_interface(dev)) {
+ dev->flags |= (IFF_POINTOPOINT);
+ dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
+ if (retina_noarp_with_ptp) {
+ dev->rebuild_header = fepci_rebuild_header;
+ dev->flags |= (IFF_NOARP);
+ }
+ }
+#endif
+ DBG_PRINT("register_netdev %p.\n", dev);
+ i = register_netdev(dev);
+ if (i) {
+ printk(KERN_WARNING
+ "%s: register_netdev failed 0x%x.\n",
+ fepci_NAME, i);
+ continue;
+ }
+
+ printk("%s: %s type %x at %p, ",
+ dev->name, pci_id_tbl[chip_idx].name,
+ 0x0 /* inl(ioaddr + ChipRev) */ , ioaddr);
+ for (i = 0; i < 5; i++)
+ printk("%2.2x:", dev->dev_addr[i]);
+ printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], pdev->irq);
+#ifdef DEBUG_PROC_FILES
+ fepci_proc_init_channel(position, j, fp);
+#endif /* DEBUG_PROC_FILES */
+ }
+ up_write(&card_privates[position].semaphore);
+ DBG_PRINT("fepci_init_one %u.\n", position);
+ return 0;
+
+ err_2:
+ iounmap(ioaddr);
+ err_1_5:
+ pci_set_drvdata(pdev, NULL);
+ err_1:
+ pci_disable_device(pdev);
+ pci_release_regions(pdev);
+ return -ENODEV;
+}
+
+static int fepci_open(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+
+ down_write(&fp->this_card_priv->semaphore);
+
+ if (fp->this_card_priv->pci_dev == NULL) {
+ up_write(&fp->this_card_priv->semaphore);
+ fepci_close(dev);
+ return -ENXIO;
+ }
+
+ while (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ fepci_stream_close(dev);
+ down_write(&fp->this_card_priv->semaphore);
+ }
+
+ {
+ int i = request_irq(dev->irq, &fepci_interrupt,
+ IRQF_SHARED, dev->name, dev);
+ if (i) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate IRQ %d, error 0x%x",
+ fepci_NAME, dev->irq, i);
+ return i;
+ }
+ }
+
+ fp->in_eth_mode = 1;
+
+ fepci_init_ring(dev);
+ set_rx_mode(dev);
+
+ fp->cur_rx = 0;
+ fp->cur_tx = 0;
+
+ netif_carrier_off(dev);
+
+ /* Enable interrupts by setting the interrupt mask. */
+
+ set_int_mask(fp->channel_number,
+ MaskFrameReceived | MaskFrameTransmitted |
+ MaskRxFifoError | MaskRxFrameDroppedError |
+ MaskTxFifoError, fp->this_card_priv);
+
+ {
+ void *ioaddr = (void *)dev->base_addr;
+
+ /* Start Rx and Tx channels. */
+ outl(Receive_enable |
+ (Rx_fifo_threshold & RX_FIFO_THRESHOLD_PACKET_MODE),
+ (void *)(ioaddr + fp->reg_rxctrl));
+
+ outl((Transmit_enable |
+ (Tx_desc_threshold &
+ (TX_DESC_THRESHOLD_PACKET_MODE << 8)) |
+ (Tx_fifo_threshold & TX_FIFO_THRESHOLD_PACKET_MODE)),
+ (void *)(ioaddr + fp->reg_txctrl));
+ }
+
+ netif_start_queue(dev);
+
+ /* Set timer */
+ init_timer(&fp->timer);
+ fp->timer.expires = jiffies + HZ;
+ fp->timer.data = (unsigned long)dev;
+ fp->timer.function = &fepci_timer; /* timer handler */
+ add_timer(&fp->timer);
+
+ up_write(&fp->this_card_priv->semaphore);
+
+ return 0;
+}
+
+static void fepci_timer(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct fepci_ch_private *fp = dev->priv;
+
+ /* just to make it absolutely sure the sending starts again
+ * if the system jams: free already sent skbuffs */
+
+ if (netif_tx_trylock(dev)) {
+ if (!(fp->tx_interrupts_since_last_timer)) {
+ unsigned i = TX_RING_SIZE - 1;
+ do {
+ u32 desc_b;
+ if ((fp->tx_skbuff[i] != NULL)
+ &&
+ (((desc_b =
+ inl(&fp->tx_desc[i].
+ desc_b)) & transfer_not_done) ==
+ 0)) {
+ /* has been sent */
+ pci_unmap_single(fp->
+ this_card_priv->
+ pci_dev,
+ inl(&fp->
+ tx_desc[i].
+ desc_a),
+ desc_b &
+ frame_length,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb(fp->tx_skbuff[i]);
+ fp->tx_skbuffs_out++;
+
+ fp->tx_skbuff[i] = NULL;
+
+ if (desc_b & fifo_error) {
+ fp->stats.tx_fifo_errors++;
+ fp->tx_desc_fifo_err++;
+ } else
+ fp->stats.tx_packets++;
+ }
+ }
+ while (i--);
+ }
+ /* if the next descriptor is free, continue taking new ones */
+ if (!(inl(&fp->tx_desc[fp->cur_tx].desc_b) & transfer_not_done))
+ netif_wake_queue(dev);
+ netif_tx_unlock(dev);
+ }
+
+ fp->tx_interrupts_since_last_timer = 0;
+
+ if ((get_common_reg_word(fp->this_card_priv->ioaddr, 0x72) >> fp->
+ channel_number) & 1) {
+ netif_carrier_off(dev);
+ } else {
+ netif_carrier_on(dev);
+ }
+
+ if (fp->in_eth_mode)
+ mod_timer(&fp->timer, jiffies + 5 * HZ);
+}
+
+static void fepci_tx_timeout(struct net_device *dev)
+{
+ DBG_PRINT("%s: transmit timed out!\n", dev->name);
+}
+
+/* Initialize the rx and tx rings */
+static void fepci_init_ring(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned i;
+
+ fp->rx_buf_sz = 2000;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb =
+ __dev_alloc_skb(fp->rx_buf_sz, GFP_KERNEL);
+
+ if (skb == NULL) {
+ ZERO:
+ outl(0, &fp->rx_desc[i].desc_a);
+ outl(0, &fp->rx_desc[i].desc_b);
+ continue;
+ } else {
+ u32 bus_address;
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ skb->data, fp->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+ if (likely(!pci_dma_mapping_error(bus_address))) {
+ fp->rx_skbuffs_in++;
+ /* Mark as being used by this device */
+ skb->dev = dev;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ fp->rx_skbuff[i] = skb;
+ outl(bus_address, &fp->rx_desc[i].desc_a);
+ outl(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else {
+ dev_kfree_skb(skb);
+ goto ZERO;
+ }
+ }
+ }
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ fp->tx_skbuff[i] = NULL;
+ outl(0, &fp->tx_desc[i].desc_a); /* no skbuff */
+ /* no transfer enable, no int enable */
+ outl(0, &fp->tx_desc[i].desc_b);
+ }
+
+ return;
+}
+
+static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ const unsigned cur_tx = fp->cur_tx;
+
+ fp->tx_skbuffs_in++;
+
+ {
+ u32 tx_length = skb->len;
+ u32 bus_address;
+ struct sk_buff *old;
+ if (unlikely(tx_length < ETH_ZLEN)) {
+ struct sk_buff *bigger =
+ skb_copy_expand(skb, 0, ETH_ZLEN - tx_length,
+ GFP_ATOMIC);
+ if (unlikely(!bigger))
+ return NET_XMIT_CN;
+ tx_length = ETH_ZLEN;
+ old = skb;
+ skb = bigger;
+ } else
+ old = NULL;
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev, skb->data,
+ tx_length, PCI_DMA_TODEVICE);
+ if (likely(!pci_dma_mapping_error(bus_address))) {
+ struct fepci_desc *descriptor;
+ if (old)
+ dev_kfree_skb(old);
+ descriptor = &fp->tx_desc[cur_tx];
+ outl(bus_address, &descriptor->desc_a);
+ outl((tx_length & frame_length) | enable_transfer,
+ &descriptor->desc_b);
+ } else {
+ if (old)
+ dev_kfree_skb(skb);
+ return NET_XMIT_CN;
+ }
+ fp->stats.tx_bytes += tx_length;
+ }
+
+ fp->tx_skbuff[cur_tx] = skb;
+
+ {
+ /* Calculate the next Tx descriptor entry */
+ unsigned next = (cur_tx + 1) & (TX_RING_SIZE - 1);
+ fp->cur_tx = next;
+ /* if the next descriptor is busy,
+ * discontinue taking new ones */
+ if (fp->tx_skbuff[next] != NULL)
+ netif_stop_queue(dev);
+ }
+ dev->trans_start = jiffies;
+
+ return NET_XMIT_SUCCESS;
+}
+
+static irqreturn_t fepci_interrupt(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ void *ioaddr = (void *)dev->base_addr;
+ struct fepci_ch_private *fp = dev->priv;
+ u32 intr_status = get_int_status(fp->channel_number, ioaddr);
+ bool RxFifoError;
+ bool RxFrameDroppedError;
+
+ clear_int(fp->channel_number, intr_status, ioaddr);
+
+ RxFifoError = (intr_status & IntrRxFifoError) != 0;
+ RxFrameDroppedError = (intr_status & IntrRxFrameDroppedError) != 0;
+ /* first update interrupt error table */
+ fp->int_err_table[RxFifoError][RxFrameDroppedError]++;
+
+ fp->interrupts++;
+
+ if (intr_status & IntrFrameReceived) {
+ fepci_rx(dev);
+ fp->rx_interrupts++;
+ }
+ if (intr_status & IntrFrameTransmitted) {
+ fp->tx_interrupts_since_last_timer++;
+ if (netif_tx_trylock(dev)) {
+ unsigned i = TX_RING_SIZE - 1;
+ do {
+ u32 desc_b;
+ if ((fp->tx_skbuff[i] != NULL)
+ &&
+ (((desc_b =
+ inl(&fp->tx_desc[i].
+ desc_b)) & transfer_not_done) ==
+ 0)) {
+ /* has been sent */
+ pci_unmap_single(fp->
+ this_card_priv->
+ pci_dev,
+ inl(&fp->
+ tx_desc[i].
+ desc_a),
+ desc_b &
+ frame_length,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb_irq(fp->tx_skbuff[i]);
+ fp->tx_skbuffs_out++;
+ fp->tx_skbuff[i] = NULL;
+ if (desc_b & fifo_error) {
+ fp->stats.tx_fifo_errors++;
+ fp->tx_desc_fifo_err++;
+ } else {
+ fp->stats.tx_packets++;
+ }
+ }
+ }
+ while (i--);
+ {
+ unsigned next = fp->cur_tx;
+ /* if next tx descriptor is free,
+ * continue taking new ones */
+ if (!(inl(&fp->tx_desc[next].desc_b) &
+ transfer_not_done))
+ netif_wake_queue(dev);
+ }
+ netif_tx_unlock(dev);
+ }
+ fp->tx_interrupts++;
+ }
+ if (RxFifoError) {
+ fp->rx_int_fifo_err++;
+ fepci_rx(dev);
+ }
+ if (RxFrameDroppedError) {
+ fp->rx_int_frame_dropped_err++;
+ fepci_rx(dev);
+ }
+ if (intr_status & IntrTxFifoError)
+ fp->tx_int_fifo_err++;
+ return intr_status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int fepci_rx(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+
+ unsigned int i, old_cur_rx = fp->cur_rx;
+ for (i = old_cur_rx;
+ i != ((old_cur_rx + RX_RING_SIZE - 1) & (RX_RING_SIZE - 1));
+ i = (i + 1) & (RX_RING_SIZE - 1)) {
+ u32 desc_b;
+ struct sk_buff *skb;
+ /* transfer done */
+ bool condition = (skb = fp->rx_skbuff[i]) &&
+ ((desc_b =
+ inl(&fp->rx_desc[i].desc_b)) & transfer_not_done) == 0;
+ if (condition) {
+ bool fifo = (desc_b & fifo_error) != 0;
+ bool size = (desc_b & size_error) != 0;
+ bool crc = (desc_b & crc_error) != 0;
+ bool octet = (desc_b & octet_error) != 0;
+ bool line = (desc_b & line_error) != 0;
+ u32 length = desc_b & frame_length;
+ pci_unmap_single(fp->this_card_priv->pci_dev,
+ inl(&fp->rx_desc[i].desc_a),
+ fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ fp->cur_rx = (i + 1) & (RX_RING_SIZE - 1);
+ /* first update error table */
+ fp->rx_desc_err_table[fifo]
+ [size]
+ [crc]
+ [octet]
+ [line]++;
+ /* small packet counters */
+ if (length == 0)
+ fp->rx_packets_of_size_0++;
+ else if (length == 1)
+ fp->rx_packets_of_size_1++;
+ else if (length == 2)
+ fp->rx_packets_of_size_2++;
+ else if (length == 3)
+ fp->rx_packets_of_size_3++;
+ else if (length < 8)
+ fp->rx_packets_of_size_4_7++;
+ else if (length < 16)
+ fp->rx_packets_of_size_8_15++;
+ else if (length < 32)
+ fp->rx_packets_of_size_16_31++;
+
+ if (fifo) {
+ fp->stats.rx_errors++;
+ fp->stats.rx_frame_errors++;
+ fp->rx_desc_fifo_err++;
+ outl(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (size) {
+ fp->stats.rx_errors++;
+ fp->stats.rx_over_errors++;
+ fp->rx_desc_size_err++;
+ outl(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (crc) {
+ fp->stats.rx_errors++;
+ fp->stats.rx_crc_errors++;
+ fp->rx_desc_crc_err++;
+ outl(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (octet) {
+ fp->rx_desc_octet_err++;
+ outl(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (line) {
+ fp->rx_desc_line_err++;
+ outl(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else {
+ skb_put(skb, length - 4);
+
+ skb->protocol = eth_type_trans(skb, dev);
+#ifdef FEPCI_POINT_TO_POINT
+ if (dev->flags & IFF_POINTOPOINT) {
+ /* everything received is for us. */
+
+ if (dev->flags & IFF_NOARP) {
+ /* NOARP applied ->
+ * destination MAC addresses
+ * are bogus */
+ if (skb->
+ pkt_type ==
+ PACKET_OTHERHOST)
+ skb->
+ pkt_type =
+ PACKET_HOST;
+ } else {
+ /* NOARP not applied ->
+ * destination MAC addresses are
+ * broadcast */
+ if (skb->
+ pkt_type ==
+ PACKET_BROADCAST)
+ skb->
+ pkt_type =
+ PACKET_HOST;
+
+ } /* IFF_NOARP */
+ } /* IFF_POINTOPOINT */
+#endif
+ skb_reset_mac_header(skb);
+ netif_rx(skb);
+ fp->rx_skbuffs_out++;
+ /* statistics -4==crc */
+ fp->stats.rx_bytes += length - 4;
+ fp->stats.rx_packets++;
+
+ fp->rx_skbuff[i] = NULL;
+ dev->last_rx = jiffies;
+ }
+ }
+ /* reserve a new one */
+ if (fp->rx_skbuff[i] == NULL) {
+ struct sk_buff *skb = dev_alloc_skb(fp->rx_buf_sz);
+
+ if (skb == NULL)
+ continue; /* Better luck next round. */
+ else {
+ u32 address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ skb->data,
+ fp->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+ if (likely(!pci_dma_mapping_error(address))) {
+ struct fepci_desc *descriptor;
+ fp->rx_skbuffs_in++;
+ fp->rx_skbuff[i] = skb;
+ /* Mark as being used by this device. */
+ skb->dev = dev;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ descriptor = &fp->rx_desc[i];
+ outl(address, &descriptor->desc_a);
+ outl(enable_transfer,
+ &descriptor->desc_b);
+ } else {
+ dev_kfree_skb(skb);
+ printk(KERN_WARNING
+ "%s: failed to map DMA.\n",
+ dev->name);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int fepci_close(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned i;
+ void *ioaddr = (void *)dev->base_addr;
+
+ down_write(&fp->this_card_priv->semaphore);
+
+ netif_stop_queue(dev);
+
+ /* Disable interrupts by clearing the interrupt mask */
+ set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
+
+ /* Stop the Tx and Rx processes */
+ outl(0x0, ioaddr + fp->reg_rxctrl);
+ outl(0x0, ioaddr + fp->reg_txctrl);
+ fp->in_eth_mode = 0;
+
+ del_timer_sync(&fp->timer);
+
+ free_irq(dev->irq, dev);
+
+ /* Free all the rx skbuffs */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (fp->rx_skbuff[i] != NULL) {
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev,
+ inl(&fp->rx_desc[i].
+ desc_a),
+ fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(fp->rx_skbuff[i]);
+ fp->rx_skbuffs_out++;
+ fp->rx_skbuff[i] = NULL;
+ }
+ }
+ /* and tx */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (fp->tx_skbuff[i] != NULL) {
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev,
+ inl(&fp->tx_desc[i].
+ desc_a),
+ inl(&fp->tx_desc[i].
+ desc_b) & frame_length,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb(fp->tx_skbuff[i]);
+ fp->tx_skbuffs_out++;
+ fp->tx_skbuff[i] = NULL;
+ }
+ }
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static struct net_device_stats *fepci_get_stats(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ return &fp->stats;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+ DBG_PRINT("set_rx_mode\n");
+}
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ char data = ((u32) rq->ifr_data) & 0xff;
+ int ret = 0;
+
+ DBG_PRINT("%s: netdev_ioctl called (command_nmbr:0x%x).\n",
+ dev->name, cmd);
+
+ switch (cmd) {
+ case FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE:
+ DBG_PRINT
+ (" ioctl stream bufsize commanded. (bufsize:0x%x)\n", data);
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -EBUSY;
+ }
+ fp->bufsize_order = data;
+ up_write(&fp->this_card_priv->semaphore);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_UNITSIZE:
+ DBG_PRINT
+ (" ioctl stream unitsize commanded. (unitsize:0x%x)\n",
+ data);
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -EBUSY;
+ }
+ fp->fake_unit_sz_order = data;
+ up_write(&fp->this_card_priv->semaphore);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_OPEN:
+ DBG_PRINT(" ioctl stream open commanded.\n");
+ ret = fepci_stream_open(dev);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_START:
+ DBG_PRINT(" ioctl stream start commanded.\n");
+ ret = fepci_stream_start(dev);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_CLOSE:
+ DBG_PRINT(" ioctl stream close commanded.\n");
+ ret = fepci_stream_close(dev);
+ break;
+ default:
+ DBG_PRINT(" unknown ioctl command 0x%x.\n", cmd);
+ return -ENOTTY;
+ }
+ return ret;
+}
+
+static void fepci_remove_one(struct pci_dev *pdev)
+{
+ /* from first dev, same in all */
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct fepci_ch_private *fp = dev->priv;
+ struct fepci_card_private *cardp = fp->this_card_priv;
+ unsigned int i;
+
+ outl(0, (void *)(cardp->ioaddr + reg_custom));
+ /* alarm manager int off */
+
+ for (i = 0; i < CHANNELS; i++) {
+ dev = cardp->ch_privates[i]->this_dev;
+ fp = dev->priv;
+#ifdef DEBUG_PROC_FILES
+ fepci_proc_cleanup_channel(cardp->card_number,
+ fp->channel_number);
+#endif /* DEBUG_PROC_FILES */
+ unregister_netdev(dev);
+ fepci_stream_close(dev);
+ free_netdev(dev);
+ }
+
+ pdev = cardp->pci_dev;
+ free_irq(pdev->irq, (void *)cardp);
+
+ down_write(&cardp->semaphore);
+
+ fepci_proc_cleanup_card(cardp->card_number);
+
+ pci_set_drvdata(pdev, NULL);
+
+ if (cardp->card_number + 1 == find_cnt)
+ find_cnt--;
+ cardp->pci_dev = NULL;
+
+ iounmap(cardp->ioaddr);
+
+ up_write(&cardp->semaphore);
+
+ pci_disable_device(pdev);
+ pci_release_regions(pdev);
+}
+
+static struct pci_driver fepci_driver = {
+ name:DRV_NAME,
+ id_table:fepci_pci_tbl,
+ probe:fepci_init_one,
+ remove:fepci_remove_one,
+};
+
+static int __init fepci_init(void)
+{
+ unsigned card = MAX_DEVICES - 1;
+ do
+ init_rwsem(&card_privates[card].semaphore);
+ while (card--);
+ major = fepci_register_char_device();
+ if (major < 0)
+ return major;
+ stream_pointers = (u32) get_zeroed_page(GFP_KERNEL | __GFP_DMA);
+ DBG_PRINT(" %x.\n", stream_pointers);
+ if (stream_pointers == 0) {
+ fepci_unregister_char_device();
+ return -ENOMEM;
+ }
+ DBG_PRINT("SetPageReserved %u.\n", stream_pointers);
+ SetPageReserved(virt_to_page(stream_pointers));
+ DBG_PRINT("fepci_proc_init_driver.\n");
+ fepci_proc_init_driver();
+ DBG_PRINT("pci_register_driver %p.\n", &fepci_driver);
+ {
+ int ret = pci_register_driver(&fepci_driver);
+ if (ret) {
+ fepci_unregister_char_device();
+ ClearPageReserved(virt_to_page(stream_pointers));
+ free_page(stream_pointers);
+ DBG_PRINT
+ ("pci_register_driver %p failed with %d.\n",
+ &fepci_driver, ret);
+ fepci_proc_cleanup_driver();
+ return ret;
+ }
+ DBG_PRINT("fepci_init %d.\n", ret);
+ return ret;
+ }
+}
+
+static void __exit fepci_cleanup(void)
+{
+ DBG_PRINT("fepci_cleanup\n");
+ pci_unregister_driver(&fepci_driver);
+ fepci_proc_cleanup_driver();
+ fepci_unregister_char_device();
+ ClearPageReserved(virt_to_page(stream_pointers));
+ free_page(stream_pointers);
+}
+
+module_init(fepci_init);
+module_exit(fepci_cleanup);
+
+/* proc-filesystem specific stuff: */
+
+#ifdef DEBUG_PROC_FILES
+
+static char string_true[] = "<true> ";
+static char string_false[] = " - ";
+
+/* convert boolean value to more clarifying string format */
+static char *b(int b)
+{
+ if (b)
+ return string_true;
+ return string_false;
+}
+
+static int fepci_proc_read_descriptors(char *buf, char **start,
+ off_t offset, int len, int *eof,
+ void *data)
+{
+ int pos = 0;
+
+ int d; /* descriptor */
+
+ struct fepci_ch_private *fp;
+ struct fepci_card_private *cardp;
+ fp = (struct fepci_ch_private *)data;
+ cardp = (struct fepci_card_private *)fp->this_card_priv;
+
+ pos +=
+ sprintf(buf + pos,
+ "Rx descriptors of channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, "\n");
+
+ for (d = 0; d < RX_RING_SIZE; d++) {
+ pos += sprintf(buf + pos, " rx descriptor number %u : \n", d);
+ pos +=
+ sprintf(buf + pos, " desc_a value : 0x%8x \n",
+ inl(&fp->rx_desc[d].desc_a));
+ pos +=
+ sprintf(buf + pos, " desc_b value : 0x%8x \n",
+ inl(&fp->rx_desc[d].desc_b));
+ }
+
+ pos += sprintf(buf + pos, "\n");
+ pos +=
+ sprintf(buf + pos,
+ "Tx descriptors of channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, "\n");
+
+ for (d = 0; d < TX_RING_SIZE; d++) {
+ pos += sprintf(buf + pos, " tx descriptor number %u : \n", d);
+ pos +=
+ sprintf(buf + pos, " desc_a value : 0x%8x \n",
+ inl(&fp->tx_desc[d].desc_a));
+ pos +=
+ sprintf(buf + pos, " desc_b value : 0x%8x \n",
+ inl(&fp->tx_desc[d].desc_b));
+ }
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, "last line\n");
+
+ *eof = 1;
+ return pos;
+}
+
+static int fepci_proc_read_stream_counters(char *buf, char **start,
+ off_t offset, int len, int *eof,
+ void *data)
+{
+ int pos = 0;
+
+ int fifo;
+ int size;
+ int crc;
+ int octet;
+ int line;
+ int frame_dropped;
+
+ struct fepci_ch_private *fp;
+ struct fepci_card_private *cardp;
+
+ fp = (struct fepci_ch_private *)data;
+ cardp = (struct fepci_card_private *)fp->this_card_priv;
+
+ pos +=
+ sprintf(buf + pos,
+ "Stream status for channel %u of card %u : \n",
+ fp->channel_number, cardp->card_number);
+ if (fp->in_stream_mode) {
+ pos += sprintf(buf + pos, " Channel is in stream mode \n");
+ if (fp->stream_on) {
+ pos += sprintf(buf + pos, " Stream is on (started) \n");
+ } else {
+ pos +=
+ sprintf(buf + pos,
+ " Stream is off (not started)\n");
+ }
+ } else {
+ pos += sprintf(buf + pos, " Channel is NOT in stream mode \n");
+ }
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos,
+ "Error counters for channel %u of card %u "
+ "(in stream mode) : \n",
+ fp->channel_number, cardp->card_number);
+
+ pos +=
+ sprintf(buf + pos, " rx_desc_fifo_err_stream : %10u \n",
+ fp->rx_desc_fifo_err_stream);
+
+ pos +=
+ sprintf(buf + pos, " rx_desc_size_err_stream : %10u \n",
+ fp->rx_desc_size_err_stream);
+ pos +=
+ sprintf(buf + pos, " rx_desc_crc_err_stream : %11u \n",
+ fp->rx_desc_crc_err_stream);
+ pos +=
+ sprintf(buf + pos, " rx_desc_octet_err_stream : %9u \n",
+ fp->rx_desc_octet_err_stream);
+ pos +=
+ sprintf(buf + pos, " rx_desc_line_err_stream : %10u \n",
+ fp->rx_desc_line_err_stream);
+ pos +=
+ sprintf(buf + pos, " tx_desc_fifo_err_stream : %10u \n",
+ fp->tx_desc_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos, " rx_int_fifo_err_stream : %11u \n",
+ fp->rx_int_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ " rx_int_frame_dropped_err_stream : %2u \n",
+ fp->rx_int_frame_dropped_err_stream);
+ pos +=
+ sprintf(buf + pos, " tx_int_fifo_err_stream : %11u \n",
+ fp->tx_int_fifo_err_stream);
+
+ pos += sprintf(buf + pos, "\n");
+ pos +=
+ sprintf(buf + pos,
+ "Other counters for channel %u of card %u "
+ "(in stream mode) : \n",
+ fp->channel_number, cardp->card_number);
+ pos +=
+ sprintf(buf + pos, " interrupts : %10u \n",
+ fp->interrupts_stream);
+ pos +=
+ sprintf(buf + pos, " rx_interrupts : %10u \n",
+ fp->rx_interrupts_stream);
+ pos +=
+ sprintf(buf + pos, " tx_interrupts : %10u \n",
+ fp->tx_interrupts_stream);
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos,
+ "Error counter tables for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, " rx_desc_error_table_stream:\n");
+ pos +=
+ sprintf(buf + pos,
+ " [fifo] [size] [crc] [octet] [line] : [count]\n");
+
+ for (fifo = 0; fifo < 2; fifo++)
+ for (size = 0; size < 2; size++)
+ for (crc = 0; crc < 2; crc++)
+ for (octet = 0; octet < 2; octet++)
+ for (line = 0; line < 2; line++)
+ pos +=
+ sprintf(buf + pos,
+ " %s %s %s %s %s "
+ ": %7u \n",
+ b(fifo),
+ b(size),
+ b(crc),
+ b(octet),
+ b(line),
+ fp->
+ rx_desc_err_table_stream
+ [fifo][size]
+ [crc][octet]
+ [line]);
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, " int_error_table:\n");
+ pos += sprintf(buf + pos, " [rx_fifo] [frame_dropped] : [count]\n");
+ for (fifo = 0; fifo < 2; fifo++)
+ for (frame_dropped = 0; frame_dropped < 2; frame_dropped++)
+ pos +=
+ sprintf(buf + pos,
+ " %s %s : %7u\n", b(fifo),
+ b(frame_dropped),
+ fp->int_err_table_stream[fifo]
+ [frame_dropped]);
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos,
+ "Small packet counters for channel %u of card %u "
+ "(stream mode): \n",
+ fp->channel_number, cardp->card_number);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 0 : %10u \n",
+ fp->rx_packets_of_size_0_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 1 : %10u \n",
+ fp->rx_packets_of_size_1_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 2 : %10u \n",
+ fp->rx_packets_of_size_2_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 3 : %10u \n",
+ fp->rx_packets_of_size_3_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 4..7 : %10u \n",
+ fp->rx_packets_of_size_4_7_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 8..15 : %10u \n",
+ fp->rx_packets_of_size_8_15_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 16..31 : %10u \n",
+ fp->rx_packets_of_size_16_31_stream);
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, "last line\n");
+
+ *eof = 1;
+ return pos;
+}
+
+static int fepci_proc_read_counters(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+
+ int fifo;
+ int size;
+ int crc;
+ int octet;
+ int line;
+ int frame_dropped;
+
+ struct fepci_ch_private *fp;
+ struct fepci_card_private *cardp;
+
+ fp = (struct fepci_ch_private *)data;
+ cardp = (struct fepci_card_private *)fp->this_card_priv;
+
+ pos +=
+ sprintf(buf + pos,
+ "Error counters for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+
+ pos +=
+ sprintf(buf + pos, " rx_desc_fifo_err : %10u \n",
+ fp->rx_desc_fifo_err);
+ pos +=
+ sprintf(buf + pos, " rx_desc_size_err : %10u \n",
+ fp->rx_desc_size_err);
+ pos +=
+ sprintf(buf + pos, " rx_desc_crc_err : %11u \n",
+ fp->rx_desc_crc_err);
+ pos +=
+ sprintf(buf + pos, " rx_desc_octet_err : %9u \n",
+ fp->rx_desc_octet_err);
+ pos +=
+ sprintf(buf + pos, " rx_desc_line_err : %10u \n",
+ fp->rx_desc_line_err);
+ pos +=
+ sprintf(buf + pos, " tx_desc_fifo_err : %10u \n",
+ fp->tx_desc_fifo_err);
+ pos +=
+ sprintf(buf + pos, " rx_int_fifo_err : %11u \n",
+ fp->rx_int_fifo_err);
+ pos +=
+ sprintf(buf + pos, " rx_int_frame_dropped_err : %2u \n",
+ fp->rx_int_frame_dropped_err);
+ pos +=
+ sprintf(buf + pos, " tx_int_fifo_err : %11u \n",
+ fp->tx_int_fifo_err);
+
+ pos += sprintf(buf + pos, "\n");
+ pos +=
+ sprintf(buf + pos,
+ "Other counters for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, " interrupts : %10u \n", fp->interrupts);
+ pos +=
+ sprintf(buf + pos, " rx_interrupts : %10u \n", fp->rx_interrupts);
+ pos +=
+ sprintf(buf + pos, " tx_interrupts : %10u \n", fp->tx_interrupts);
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos, " rx_skbuffs_in : %10u \n", fp->rx_skbuffs_in);
+ pos +=
+ sprintf(buf + pos, " rx_skbuffs_out : %9u \n", fp->rx_skbuffs_out);
+ pos +=
+ sprintf(buf + pos, " difference : %13i \n",
+ fp->rx_skbuffs_in - fp->rx_skbuffs_out);
+ pos +=
+ sprintf(buf + pos, " tx_skbuffs_in : %10u \n", fp->tx_skbuffs_in);
+ pos +=
+ sprintf(buf + pos, " tx_skbuffs_out : %9u \n", fp->tx_skbuffs_out);
+ pos +=
+ sprintf(buf + pos, " difference : %13i \n",
+ fp->tx_skbuffs_in - fp->tx_skbuffs_out);
+
+ pos += sprintf(buf + pos, "\n");
+ pos +=
+ sprintf(buf + pos,
+ "Error counter tables for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, " rx_desc_error_table:\n");
+ pos +=
+ sprintf(buf + pos,
+ " [fifo] [size] [crc] [octet] [line] : [count]\n");
+
+ for (fifo = 0; fifo < 2; fifo++)
+ for (size = 0; size < 2; size++)
+ for (crc = 0; crc < 2; crc++)
+ for (octet = 0; octet < 2; octet++)
+ for (line = 0; line < 2; line++)
+ pos +=
+ sprintf(buf + pos,
+ " %s %s %s %s %s "
+ ": %7u \n",
+ b(fifo),
+ b(size),
+ b(crc),
+ b(octet),
+ b(line),
+ fp->
+ rx_desc_err_table
+ [fifo][size]
+ [crc][octet]
+ [line]);
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, " int_error_table:\n");
+ pos += sprintf(buf + pos, " [rx_fifo] [frame_dropped] : [count]\n");
+ for (fifo = 0; fifo < 2; fifo++)
+ for (frame_dropped = 0; frame_dropped < 2; frame_dropped++)
+ pos +=
+ sprintf(buf + pos,
+ " %s %s : %7u\n", b(fifo),
+ b(frame_dropped),
+ fp->int_err_table[fifo][frame_dropped]);
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos,
+ "Small packet counters for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 0 : %10u \n",
+ fp->rx_packets_of_size_0);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 1 : %10u \n",
+ fp->rx_packets_of_size_1);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 2 : %10u \n",
+ fp->rx_packets_of_size_2);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 3 : %10u \n",
+ fp->rx_packets_of_size_3);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 4..7 : %10u \n",
+ fp->rx_packets_of_size_4_7);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 8..15 : %10u \n",
+ fp->rx_packets_of_size_8_15);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 16..31 : %10u \n",
+ fp->rx_packets_of_size_16_31);
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, "last line\n");
+
+ *eof = 1;
+ return pos;
+}
+
+static int fepci_proc_read_registers(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+
+ struct fepci_ch_private *fp;
+ struct fepci_card_private *cardp;
+ void *ioaddr;
+
+ fp = (struct fepci_ch_private *)data;
+ cardp = (struct fepci_card_private *)fp->this_card_priv;
+ ioaddr = cardp->ioaddr;
+
+ pos +=
+ sprintf(buf + pos, "Registers for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+
+ pos +=
+ sprintf(buf + pos, " interrupt status : 0x%8x \n",
+ get_int_status(fp->channel_number, ioaddr));
+ pos +=
+ sprintf(buf + pos, " interrupt mask : 0x%8x \n",
+ get_int_mask(fp->channel_number, ioaddr));
+ pos +=
+ sprintf(buf + pos, " rxctrl : 0x%8x \n",
+ inl(ioaddr + fp->reg_rxctrl));
+ pos +=
+ sprintf(buf + pos, " txctrl : 0x%8x \n",
+ inl(ioaddr + fp->reg_txctrl));
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "last line\n");
+
+ *eof = 1;
+ return pos;
+}
+
+#endif /* DEBUG_PROC_FILES */
+
+int get_line_data_rate_value(unsigned char line_rate)
+{
+ switch (line_rate) {
+ case 0x00:
+ return 0;
+ case 0x01:
+ return 1;
+ case 0x05:
+ return 8;
+ case 0x06:
+ return 10;
+ case 0x14:
+ return 256;
+ case 0x15:
+ return 300;
+ case 0x20:
+ return 1;
+ case 0x28:
+ return 8;
+ case 0x29:
+ return 10;
+ case 0x30:
+ return 32;
+ case 0x34:
+ return 56;
+ case 0x36:
+ return 64;
+ case 0x60:
+ return 1;
+ case 0xa0:
+ return 1;
+ default:
+ return -1;
+ }
+}
+
+char get_line_data_rate_unit(unsigned char line_rate)
+{
+ switch (line_rate) {
+ case 0x00:
+ return 0;
+ case 0x01:
+ return 0;
+ case 0x05:
+ return 0;
+ case 0x06:
+ return 0;
+ case 0x14:
+ return 0;
+ case 0x15:
+ return 0;
+ case 0x20:
+ return 'k';
+ case 0x28:
+ return 'k';
+ case 0x29:
+ return 'k';
+ case 0x30:
+ return 'k';
+ case 0x34:
+ return 'k';
+ case 0x36:
+ return 'k';
+ case 0x60:
+ return 'M';
+ case 0xa0:
+ return 'G';
+ default:
+ return 0;
+ }
+}
+
+int print_line_type(unsigned char type, char *buf, int pos)
+{
+ DBG_PRINT("print_line_type %c\n", type);
+ switch (type) {
+ case 0:
+ pos += sprintf(buf + pos, "NONE");
+ break;
+ case 1:
+ pos += sprintf(buf + pos, "DCombus management bus");
+ break;
+ case 2:
+ pos += sprintf(buf + pos, "V.24");
+ break;
+ case 3:
+ pos += sprintf(buf + pos, "X.21");
+ break;
+ case 4:
+ pos += sprintf(buf + pos, "V.35");
+ break;
+ case 5:
+ pos += sprintf(buf + pos, "V.11");
+ break;
+ case 6:
+ pos += sprintf(buf + pos, "IDSL (ISDN Basic Rate");
+ break;
+ case 7:
+ pos += sprintf(buf + pos, "E1 nonframed/framed");
+ break;
+ case 8:
+ pos += sprintf(buf + pos, "E2 nonframed/framed");
+ break;
+ case 9:
+ pos += sprintf(buf + pos, "E3 nonframed/framed");
+ break;
+ case 10:
+ pos += sprintf(buf + pos, "T1 nonframed/framed");
+ break;
+ case 11:
+ pos += sprintf(buf + pos, "T2 nonframed/framed");
+ break;
+ case 12:
+ pos += sprintf(buf + pos, "T3 nonframed/framed");
+ break;
+ case 13:
+ pos += sprintf(buf + pos, "HDSL");
+ break;
+ case 14:
+ pos += sprintf(buf + pos, "ADSL");
+ break;
+ case 15:
+ pos += sprintf(buf + pos, "SDSL");
+ break;
+ case 16:
+ pos += sprintf(buf + pos, "HDSL2");
+ break;
+ case 17:
+ pos += sprintf(buf + pos, "VDSL");
+ break;
+ case 18:
+ pos += sprintf(buf + pos, "G.shdsl");
+ break;
+ case 19:
+ pos += sprintf(buf + pos, "SDH");
+ break;
+ case 255:
+ pos +=
+ sprintf(buf + pos, "COMBINATION, get list using mailbox");
+ break;
+ default:
+ pos += sprintf(buf + pos, "reserved");
+ }
+ return pos;
+}
+
+int print_card_type(unsigned char type, char *buf, int pos)
+{
+ DBG_PRINT("print_card_type\n");
+ switch (type) {
+ case 0x00:
+ pos += sprintf(buf + pos, "illegal ");
+ break;
+ case 0x01:
+ case 0x02:
+ pos += sprintf(buf + pos, "Retina ");
+ break;
+ default:
+ pos += sprintf(buf + pos, "reserved ");
+ }
+
+ return pos;
+}
+
+int print_card_model(unsigned char model, char *buf, int pos)
+{
+ DBG_PRINT("print_card_model\n");
+ switch (model) {
+ case 0x00:
+ pos += sprintf(buf + pos, "illegal ");
+ break;
+ case 0x01:
+ pos += sprintf(buf + pos, "E2200 ");
+ break;
+ case 0x02:
+ pos += sprintf(buf + pos, "C5400 ");
+ break;
+ default:
+ pos += sprintf(buf + pos, "reserved ");
+ }
+
+ return pos;
+}
+
+static inline u8 get_common_reg(void *ioaddr, unsigned long offsett)
+{
+ unsigned long byte = __test_and_clear_bit(0, &offsett);
+ u8 reg;
+ DBG_PRINT("get_common_reg %p, old %p\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+ byte,
+ &((u32 *) (ioaddr +
+ FEPCI_IDENTIFICATION_OFFSETT))[offsett / 2]);
+ reg =
+ readb(ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+ byte);
+ DBG_PRINT("get_common_reg %p: %u\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+ byte, reg);
+ return reg;
+}
+
+static int fepci_proc_read_features(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+ int tab = 0;
+ struct fepci_card_private *cardp;
+ void *ioaddr;
+
+ cardp = (struct fepci_card_private *)data;
+
+ down_read(&cardp->semaphore);
+
+ if (unlikely(cardp->pci_dev == NULL))
+ goto EOF;
+
+ ioaddr = cardp->ioaddr;
+
+ switch (offset) {
+ case 0:
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "card type: ");
+ pos = print_card_type(get_common_reg(ioaddr, 0x00), buf, pos);
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "model: ");
+ pos = print_card_model(get_common_reg(ioaddr, 0x00), buf, pos);
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "serial number: ");
+ pos +=
+ sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x07));
+ pos +=
+ sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x06));
+ pos +=
+ sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x05));
+ pos +=
+ sprintf(buf + pos, "%2.2x\n", get_common_reg(ioaddr, 0x04));
+
+ pos += sprintf(buf + pos, "version: ");
+ pos +=
+ sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x08));
+ pos +=
+ sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x09));
+ pos +=
+ sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x0a));
+ pos +=
+ sprintf(buf + pos, "%2.2x\n", get_common_reg(ioaddr, 0x0b));
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "max. pipe count: ");
+ pos += sprintf(buf + pos, "%d\n", get_common_reg(ioaddr, 0x03));
+
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ case 1:
+ pos += sprintf(buf + pos, "line count: ");
+ pos += sprintf(buf + pos, "%d\n", get_common_reg(ioaddr, 0x02));
+
+ pos += sprintf(buf + pos, "line type: ");
+ pos = print_line_type(get_common_reg(ioaddr, 0x0c), buf, pos);
+
+ /* JT: Secondary interface is also printed also,
+ * if there is one. */
+ if (get_common_reg(ioaddr, 0x10) > 0) {
+ pos += sprintf(buf + pos, " / ");
+ pos =
+ print_line_type(get_common_reg(ioaddr, 0x10),
+ buf, pos);
+ }
+
+ pos += sprintf(buf + pos, "\n");
+
+ /* JT: Line modes are printed only if
+ * the card has NO secondary interface. */
+ if (get_common_reg(ioaddr, 0x10) == 0) {
+ pos += sprintf(buf + pos, "line modes: ");
+ tab = 0;
+ if (get_common_reg(ioaddr, 0x21) & 0x80) {
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "framed mode supported\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x40) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "unframed mode supported\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x20) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line code setting available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x10) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line attenuation available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x08) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line quality available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x04) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "timing setting available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x02) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos += sprintf(buf + pos, "reserved\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x01) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line shutdown setting "
+ "available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) == 0) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos += sprintf(buf + pos, "n/a\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ case 2:
+ pos += sprintf(buf + pos, "line rate: ");
+ if (get_common_reg(ioaddr, 0x28) ==
+ get_common_reg(ioaddr, 0x2a)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos += sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))
+ * get_common_reg_word(ioaddr,
+ 0x28));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos, "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list using mailbox");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos += sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))
+ * get_common_reg_word(ioaddr,
+ 0x28));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos, "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))
+ * get_common_reg_word(ioaddr,
+ 0x2a));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos, "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list using mailbox");
+ }
+
+ /* JT: Line rate of a secondary interface is printed,
+ * if there is one. */
+ if (get_common_reg(ioaddr, 0x10) > 0) {
+ pos += sprintf(buf + pos, " / ");
+
+ if (get_common_reg(ioaddr, 0x34) ==
+ get_common_reg(ioaddr, 0x36)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos +=
+ sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr, 0x34));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or "
+ "list using mailbox");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos +=
+ sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr, 0x34));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr, 0x36));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list "
+ "using mailbox");
+ }
+ }
+
+ pos += sprintf(buf + pos, "\n");
+
+ /* JT: Primary line special info is printed only
+ * if the card has NO secondary interface. */
+ if (get_common_reg(ioaddr, 0x10) == 0) {
+ pos += sprintf(buf + pos, "rate sel. methods: ");
+ if (get_common_reg(ioaddr, 0x28) ==
+ get_common_reg(ioaddr, 0x2a))
+ pos += sprintf(buf + pos, "n/a\n");
+ else if ((get_common_reg(ioaddr, 0x22) & 4)
+ && (get_common_reg(ioaddr, 0x23) & 4))
+ pos += sprintf(buf + pos, "adjust\n");
+ else if (((!get_common_reg(ioaddr, 0x22)) & 4)
+ && (get_common_reg(ioaddr, 0x23) & 4))
+ pos += sprintf(buf + pos, "adjust(framed)\n");
+ else if ((get_common_reg(ioaddr, 0x22) & 4)
+ && ((!get_common_reg(ioaddr, 0x23)) & 4))
+ pos += sprintf(buf + pos, "adjust(unframed)\n");
+ else
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "unframed data rate: ");
+ if (get_common_reg(ioaddr, 0x30) ==
+ get_common_reg(ioaddr, 0x32)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x30));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list using "
+ "mailbox\n");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x30));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27)));
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x32));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+
+ case 3:
+ /* JT: Line modes are printed only
+ * if the card has NO secondary interface. */
+ if (get_common_reg(ioaddr, 0x10) == 0) {
+ pos += sprintf(buf + pos, "rate sel. methods: ");
+ if (get_common_reg(ioaddr, 0x30) ==
+ get_common_reg(ioaddr, 0x32))
+ pos += sprintf(buf + pos, "n/a\n");
+ else {
+ if (get_common_reg(ioaddr, 0x23) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "timeslot map\n");
+ if (get_common_reg(ioaddr, 0x23) & 2)
+ pos += sprintf(buf + pos, "adjust\n");
+ }
+
+ pos += sprintf(buf + pos, "framed data rate: ");
+ if (get_common_reg(ioaddr, 0x2c) ==
+ get_common_reg(ioaddr, 0x2e)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x2c));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x2c));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27)));
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x2e));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ }
+
+ pos += sprintf(buf + pos, "rate sel. methods: ");
+ if (get_common_reg_word(ioaddr, 0x2c) ==
+ get_common_reg_word(ioaddr, 0x2e))
+ pos += sprintf(buf + pos, "n/a\n");
+ else {
+ if (get_common_reg(ioaddr, 0x22) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "timeslot map\n");
+ if (get_common_reg(ioaddr, 0x22) & 2)
+ pos += sprintf(buf + pos, "adjust\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ default:
+ EOF:
+ up_read(&cardp->semaphore);
+ *start = (char *)0;
+ *eof = 1;
+ return 0;
+ }
+}
+
+static int fepci_proc_read_settings(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+ int i, j;
+
+ struct fepci_card_private *cardp;
+ void *ioaddr;
+
+ cardp = (struct fepci_card_private *)data;
+
+ down_read(&cardp->semaphore);
+
+ if (unlikely(cardp->pci_dev == NULL))
+ goto EOF;
+
+ ioaddr = cardp->ioaddr;
+
+ switch (offset) {
+ case 0:
+ j = get_common_reg(ioaddr, 0x02);
+
+ for (i = 0; i < j; i++) {
+ if ((get_common_reg_word(ioaddr, 0x44) >> i) & 1) {
+ pos += sprintf(buf + pos, "\nline %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+ pos +=
+ sprintf(buf + pos, "mode: ");
+ if ((get_common_reg_word(ioaddr, 0x46) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "framed\n");
+ else
+ pos += sprintf(buf + pos, "unframed\n");
+
+ pos +=
+ sprintf(buf + pos, "scrambler: ");
+ if ((get_common_reg_word(ioaddr, 0x48) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "enabled\n");
+ else if ((get_common_reg_word(ioaddr, 0x46)
+ >> i) & 1)
+ pos += sprintf(buf + pos, "n/a\n");
+ else
+ pos += sprintf(buf + pos, "disabled\n");
+
+ pos +=
+ sprintf(buf + pos, "line crc: ");
+ if ((get_common_reg_word(ioaddr, 0x46) >>
+ i) & 1) {
+ if ((get_common_reg_word
+ (ioaddr, 0x4a) >> i) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "enabled\n");
+ else if (get_common_reg
+ (ioaddr, 0x22) & 0x20)
+ pos +=
+ sprintf(buf + pos,
+ "disabled\n");
+ else
+ pos +=
+ sprintf(buf + pos, "n/a\n");
+ } else {
+ if ((get_common_reg_word
+ (ioaddr, 0x4a) >> i) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "enabled\n");
+ else if (get_common_reg
+ (ioaddr, 0x23) & 0x20)
+ pos +=
+ sprintf(buf + pos,
+ "disabled\n");
+ else
+ pos +=
+ sprintf(buf + pos, "n/a\n");
+ }
+ }
+ }
+
+ /* If there are no enabled lines, "No enabled lines!" printed.
+ * The settings file would remain empty otherwise. */
+ if ((get_common_reg_word(ioaddr, 0x44)) == 0)
+ pos += sprintf(buf + pos, "No enabled lines!\n");
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ case 1:
+ j = get_common_reg(ioaddr, 0x03);
+
+ if ((get_common_reg_word(ioaddr, 0x44)) != 0) {
+ for (i = 0; i < j; i++) {
+ pos += sprintf(buf + pos, "\npipe %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+
+ pos +=
+ sprintf(buf + pos, "access mode: ");
+ if ((get_common_reg_word(ioaddr, 0x4e) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "packet\n");
+ else
+ pos += sprintf(buf + pos, "stream\n");
+
+ pos +=
+ sprintf(buf + pos, "data rate: ");
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x42)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x42))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x42)) *
+ get_common_reg_word
+ (ioaddr, 0x50 + 2 * i));
+ if (get_line_data_rate_unit
+ Àg#\x03+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x42)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ default:
+ EOF:
+ up_read(&cardp->semaphore);
+ *start = (char *)0;
+ *eof = 1;
+ return 0;
+ }
+}
+
+void proc_cpy(char *buf_from, char *buf_to, int len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ *(buf_to + i) = *(buf_from + i);
+}
+
+static int fepci_proc_read_status(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ unsigned int pos = 0;
+ unsigned int i, j;
+
+ struct fepci_card_private *cardp;
+
+ void *ioaddr;
+
+ struct fepci_ch_private *fp;
+
+ cardp = (struct fepci_card_private *)data;
+
+ down_read(&cardp->semaphore);
+
+ if (unlikely(cardp->pci_dev == NULL))
+ goto EOF;
+
+ ioaddr = cardp->ioaddr;
+
+ if ((offset & 0xff000) == 0) {
+ j = get_common_reg(ioaddr, 0x02);
+ for (i = 0; i < j; i++) {
+ pos += sprintf(buf + pos, "\nline %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+ pos += sprintf(buf + pos, "status: ");
+ if ((get_common_reg_word(ioaddr, 0x72) >> i) & 1) {
+ pos += sprintf(buf + pos, "DOWN\n");
+ netif_carrier_off(cardp->ch_privates[i]->
+ this_dev);
+ } else {
+ netif_carrier_on(cardp->ch_privates[i]->
+ this_dev);
+ if ((get_common_reg_word(ioaddr, 0x74) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "UP/Alarm\n");
+ else
+ pos += sprintf(buf + pos, "UP\n");
+ }
+ pos += sprintf(buf + pos, "primary clock: ");
+ if ((get_common_reg_word(ioaddr, 0x76) >> i) & 1)
+ pos += sprintf(buf + pos, "OK\n");
+ else
+ pos += sprintf(buf + pos, "FAIL\n");
+ }
+ up_read(&cardp->semaphore);
+ if (pos - (0xfff & offset) > (unsigned)len)
+ *start = (char *)len;
+ else
+ *start = (char *)(0x1000 - (offset & 0xfff));
+ proc_cpy(buf + (offset & 0xfff), buf, len);
+ return min((int)(pos - (0xfff & offset)), len);
+ }
+
+ j = get_common_reg(ioaddr, 0x03);
+ if ((j <= 0) || (j > 16))
+ j = 16;
+ if ((((((unsigned)offset) & 0xff000) >> 12) > 0) &&
+ (((((unsigned)offset) & 0xff000) >> 12) <= j)) {
+ i = ((int)((offset & 0xff000) >> 12)) - 1;
+ if (i < CHANNELS) {
+ pos += sprintf(buf + pos, "\npipe %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+ pos += sprintf(buf + pos, "status: ");
+
+ if (!((get_common_reg_word(ioaddr, 0x78) >> i) & 1))
+ pos += sprintf(buf + pos, "DOWN\n");
+ else if ((get_common_reg_word(ioaddr, 0x7a) >> i) & 1)
+ pos += sprintf(buf + pos, "UP/Degraded\n");
+ else
+ pos += sprintf(buf + pos, "UP\n");
+ if ((get_common_reg_word(ioaddr, 0x4e) >> i) & 1) {
+ pos +=
+ sprintf(buf + pos, "packet counters:\n\n");
+ pos +=
+ sprintf(buf + pos, "rx count: %lu \n",
+ cardp->ch_privates[i]->stats.
+ rx_packets);
+ pos +=
+ sprintf(buf + pos, "rx data: %lu \n",
+ cardp->ch_privates[i]->stats.
+ rx_bytes);
+ pos +=
+ sprintf(buf + pos, "rx errors:%lu \n",
+ cardp->ch_privates[i]->stats.
+ rx_errors);
+ pos +=
+ sprintf(buf + pos, "tx count: %lu \n",
+ cardp->ch_privates[i]->stats.
+ tx_packets);
+ pos +=
+ sprintf(buf + pos, "tx data: %lu \n",
+ cardp->ch_privates[i]->stats.
+ tx_bytes);
+ pos +=
+ sprintf(buf + pos, "tx errors:%lu \n",
+ cardp->ch_privates[i]->stats.
+ tx_errors);
+ } else {
+ pos +=
+ sprintf(buf + pos, "stream counters:\n\n");
+ fp = cardp->ch_privates[i];
+ pos += sprintf(buf + pos,
+ "rx_desc_fifo_err_stream : %10u"
+ " \n",
+ fp->rx_desc_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_desc_size_err_stream : %10u"
+ " \n",
+ fp->rx_desc_size_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_desc_octet_err_stream : %9u"
+ " \n",
+ fp->rx_desc_octet_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_desc_line_err_stream : %10u"
+ " \n",
+ fp->rx_desc_line_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "tx_desc_fifo_err_stream : %10u"
+ " \n",
+ fp->tx_desc_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_int_fifo_err_stream : %11u \n",
+ fp->rx_int_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_int_frame_dropped_err_stream :"
+ " %2u \n",
+ fp->
+ rx_int_frame_dropped_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "tx_int_fifo_err_stream : %11u \n",
+ fp->tx_int_fifo_err_stream);
+ }
+ if (pos - (0xfff & offset) > (unsigned)len)
+ *start = (char *)len;
+ else
+ *start = (char *)(0x1000 - (offset & 0xfff));
+ proc_cpy(buf + (offset & 0xfff), buf, len);
+ }
+ up_read(&cardp->semaphore);
+ return min((int)(pos - (0xfff & offset)), len);
+ } else {
+ EOF:
+ up_read(&cardp->semaphore);
+ *start = (char *)0;
+ *eof = 1;
+ return 0;
+ }
+}
+
+static int fepci_proc_read_devices(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ void *ioaddr;
+ unsigned int pos = 0;
+ unsigned int i;
+
+ pos +=
+ sprintf(buf + pos,
+ "bus:card.function cardtype model lines, max rate\n");
+ for (i = 0; i < find_cnt; i++) {
+ down_read(&card_privates[i].semaphore);
+ if (likely(card_privates[i].pci_dev)) { /* the card exists */
+ pos +=
+ sprintf(buf + pos,
+ "%02x:%s ",
+ card_privates[i].pci_dev->bus->
+ number,
+ card_privates[i].pci_dev->procent->name);
+ ioaddr = card_privates[i].ioaddr;
+ pos =
+ print_card_type(get_common_reg
+ (ioaddr, 0x00), buf, pos);
+ pos =
+ print_card_model(get_common_reg
+ (ioaddr, 0x00), buf, pos);
+ pos +=
+ sprintf(buf + pos, "%d",
+ get_common_reg(ioaddr, 0x02));
+ pos += sprintf(buf + pos, " x ");
+ pos =
+ print_line_type(get_common_reg
+ (ioaddr, 0x0c), buf, pos);
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos += sprintf(buf + pos, "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos += sprintf(buf + pos, ", total ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr,
+ 0x2a) *
+ get_common_reg(ioaddr, 0x02));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value or list "
+ "using mailbox\n");
+
+ }
+ up_read(&card_privates[i].semaphore);
+ }
+ DBG_PRINT("fepci_proc_read_devices pos %u < %lu?\n", pos, PAGE_SIZE);
+ *eof = 1;
+ return pos;
+}
+
+static int fepci_proc_read_id_list(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ unsigned int pos = 0;
+ unsigned int i;
+
+ pos += sprintf(buf + pos, "bus:card.function major minor interfaces\n");
+ for (i = 0; i < find_cnt; i++) {
+ down_read(&card_privates[i].semaphore);
+ if (likely(card_privates[i].pci_dev)) /* the card exists */
+ pos +=
+ sprintf(buf + pos,
+ "%02x:%s %03i %03i %s..%s\n",
+ card_privates[i].pci_dev->bus->
+ number,
+ card_privates[i].pci_dev->
+ procent->name, major,
+ card_privates[i].
+ ch_privates[0]->minor,
+ card_privates[i].
+ ch_privates[0]->this_dev->name,
+ card_privates[i].
+ ch_privates[CHANNELS - 1]->this_dev->name);
+ up_read(&card_privates[i].semaphore);
+ }
+
+ pos += sprintf(buf + pos, "\n");
+ DBG_PRINT("fepci_proc_read_id_list pos %u < %lu?\n", pos, PAGE_SIZE);
+ *eof = 1;
+ return pos;
+}
+
+const static char DEVICES[] = "devices";
+const static char ID_LIST[] = "id_list";
+
+static int fepci_proc_init_driver(void)
+{
+ DBG_PRINT("fepci_proc_init_driver\n");
+ proc_root_entry = create_proc_entry(fepci_proc_entry_name, S_IFDIR, 0);
+ if (!proc_root_entry)
+ printk(KERN_WARNING
+ "%s: creating proc root dir entry failed.\n",
+ fepci_NAME);
+
+ create_proc_read_entry(DEVICES, 0, proc_root_entry,
+ fepci_proc_read_devices, NULL);
+ create_proc_read_entry(ID_LIST, 0, proc_root_entry,
+ fepci_proc_read_id_list, NULL);
+
+ return 0;
+}
+
+static void fepci_proc_cleanup_driver(void)
+{
+ char name[sizeof(fepci_proc_entry_name) + sizeof(DEVICES)];
+ strcpy(name, fepci_proc_entry_name);
+ name[sizeof(fepci_proc_entry_name) - 1] = '/';
+ strcpy(name + sizeof(fepci_proc_entry_name), DEVICES);
+ remove_proc_entry(name, 0);
+ strcpy(name + sizeof(fepci_proc_entry_name), ID_LIST);
+ remove_proc_entry(name, 0);
+ remove_proc_entry(fepci_proc_entry_name, 0);
+}
+
+static void fepci_proc_init_card(int card_number, void *card_data)
+{
+ char buf[50];
+ struct proc_dir_entry *ent;
+ sprintf(buf, "%02x:%02x.%02x",
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+ ent = create_proc_entry(buf, S_IFDIR, proc_root_entry);
+ if (!ent)
+ printk("%s: creating proc dir entry failed.\n", fepci_NAME);
+
+ sprintf(buf, fepci_features_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_features,
+ card_data);
+
+ sprintf(buf, fepci_settings_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_settings,
+ card_data);
+
+ sprintf(buf, fepci_status_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_status, card_data);
+}
+
+static void fepci_proc_cleanup_card(int card_number)
+{
+ char buf[50];
+
+ sprintf(buf, fepci_features_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ remove_proc_entry(buf, 0);
+ sprintf(buf, fepci_settings_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ remove_proc_entry(buf, 0);
+ sprintf(buf, fepci_status_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ remove_proc_entry(buf, 0);
+
+ sprintf(buf, "%02x:%02x.%02x",
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+ remove_proc_entry(buf, proc_root_entry);
+}
+
+#ifdef DEBUG_PROC_FILES
+static void fepci_proc_init_channel(int card_number, int channel_number,
+ void *channel_data)
+{
+ DBG_PRINT("fepci_proc_init_channel\n");
+ char buf[50];
+
+ sprintf(buf, fepci_counters_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_counters,
+ channel_data);
+
+ sprintf(buf, fepci_descriptors_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_descriptors,
+ channel_data);
+
+ sprintf(buf, fepci_stream_counters_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+
+ create_proc_read_entry(buf, 0, NULL,
+ fepci_proc_read_stream_counters, channel_data);
+
+ sprintf(buf, fepci_registers_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_registers,
+ channel_data);
+}
+
+static void fepci_proc_cleanup_channel(int card_number, int channel_number)
+{
+ char buf[50];
+ sprintf(buf, fepci_counters_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ remove_proc_entry(buf, 0);
+
+ sprintf(buf, fepci_descriptors_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ remove_proc_entry(buf, 0);
+
+ sprintf(buf, fepci_stream_counters_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ remove_proc_entry(buf, 0);
+
+ sprintf(buf, fepci_registers_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ remove_proc_entry(buf, 0);
+}
+#endif /* DEBUG_PROC_FILES */
diff -Napur linux-2.6.23/drivers/net/wan/retina.h linux-2.6.24/drivers/net/wan/retina.h
--- linux-2.6.23/drivers/net/wan/retina.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.24/drivers/net/wan/retina.h 2007-10-22 12:01:44.856033562 +0300
@@ -0,0 +1,164 @@
+/* V1.0.0 */
+
+/*
+ Copyright (C) 2002-2003 Jouni Kujala, Flexibilis Oy.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ All the drivers derived from or based on this code fall under the
+ GPL and must retain the copyright and license notice.
+*/
+
+#ifndef RETINA_H
+#define RETINA_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* net device related stuff: */
+#define FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE 0x89F1
+#define FEPCI_NETDEV_IOCTL_STREAM_UNITSIZE 0x89F2
+#define FEPCI_NETDEV_IOCTL_STREAM_OPEN 0x89F3
+#define FEPCI_NETDEV_IOCTL_STREAM_START 0x89F4
+#define FEPCI_NETDEV_IOCTL_STREAM_CLOSE 0x89F6
+
+/* char device related stuff: */
+
+#define FEPCI_SHARED_MEM_OFFSETT 0x8000
+#define FEPCI_IDENTIFICATION_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x0)
+#define FEPCI_FEATURES_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x40)
+#define FEPCI_SETTINGS_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x80)
+#define FEPCI_STATUS_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0xE0)
+#define FEPCI_MAILBOX_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x100)
+
+/* structures for ioctl calls: */
+struct fepci_ioctl_identification {
+ unsigned char data[0x20];
+};
+struct fepci_real_identification {
+ unsigned long int data[0x10];
+};
+
+struct fepci_ioctl_features {
+ unsigned char data[0x20];
+};
+struct fepci_real_features {
+ unsigned long int data[0x10];
+};
+
+struct fepci_ioctl_settings {
+ unsigned char data[0x30];
+};
+struct fepci_real_settings {
+ unsigned long int data[0x15];
+};
+
+struct fepci_ioctl_status {
+ unsigned char data[0x10];
+};
+struct fepci_real_status {
+ unsigned long int data[0x5];
+};
+
+struct fepci_ioctl_shared_mem {
+ unsigned long int data[0x80];
+};
+
+#define FEPCI_IOCTL_MAGIC 0xAA
+
+#define FEPCI_IOCTL_R_SHARED_MEM _IOR(FEPCI_IOCTL_MAGIC, 1, \
+struct fepci_ioctl_shared_mem)
+#define FEPCI_IOCTL_W_SHARED_MEM _IOW(FEPCI_IOCTL_MAGIC, 2, \
+struct fepci_ioctl_shared_mem)
+
+#define FEPCI_IOCTL_G_IDENTIFICATION _IOR(FEPCI_IOCTL_MAGIC, 0x81, \
+struct fepci_ioctl_identification)
+#define FEPCI_IOCTL_G_FEATURES _IOR(FEPCI_IOCTL_MAGIC, 0x82, \
+struct fepci_ioctl_features)
+#define FEPCI_IOCTL_G_SETTINGS _IOR(FEPCI_IOCTL_MAGIC, 0x83, \
+struct fepci_ioctl_settings)
+#define FEPCI_IOCTL_G_STATUS _IOR(FEPCI_IOCTL_MAGIC, 0x84, \
+struct fepci_ioctl_status)
+
+/* mailbox: */
+
+struct fepci_ioctl_mailbox {
+ unsigned char Semafore;
+ unsigned char Mail_number;
+ unsigned char Size;
+ unsigned char Command;
+ unsigned char Data[112];
+};
+
+struct fepci_real_mailbox {
+ __u32 Semafore_Mail_number;
+ __u32 Size_Command;
+ __u32 Data[112 / 2];
+};
+
+#define FEPCI_IOCTL_B_POLL _IO(FEPCI_IOCTL_MAGIC, 0x85)
+#define FEPCI_IOCTL_B_GRAB _IO(FEPCI_IOCTL_MAGIC, 0x86)
+#define FEPCI_IOCTL_B_RELEASE _IO(FEPCI_IOCTL_MAGIC, 0x87)
+#define FEPCI_IOCTL_B_S_CMAIL _IOW(FEPCI_IOCTL_MAGIC, 0x88, \
+struct fepci_ioctl_mailbox)
+#define FEPCI_IOCTL_B_S_QMAIL _IOW(FEPCI_IOCTL_MAGIC, 0x89, \
+struct fepci_ioctl_mailbox)
+#define FEPCI_IOCTL_B_G_MAIL _IOR(FEPCI_IOCTL_MAGIC, 0x90, \
+struct fepci_ioctl_mailbox)
+
+#define FEPCI_IOCTL_ALARM_MANAGER _IO(FEPCI_IOCTL_MAGIC, 0x91)
+#define FEPCI_IOCTL_STREAM_TRANSMIT_POLL _IO(FEPCI_IOCTL_MAGIC, 0x92)
+#define FEPCI_IOCTL_STREAM_RECEIVE_POLL _IO(FEPCI_IOCTL_MAGIC, 0x93)
+#define FEPCI_IOCTL_STREAM_BOTH_POLL _IO(FEPCI_IOCTL_MAGIC, 0x94)
+
+/* stream related stuff: */
+
+/* stream buffer address space:
+ * address: 0x 7 6 5 4 3 2 1 0
+ * ^ ^ ^
+ * | | |
+ * card | area(rx/tx,0==rx,1==tx)
+ * channel */
+
+#define CARD_ADDRESS_SHIFT 24u
+#define CHANNEL_ADDRESS_SHIFT 20u
+#define AREA_ADDRESS_SHIFT 16u
+
+#define STREAM_BUFFER_POINTER_AREA 0x7fff0000 /* one page reserved */
+
+/* stream buffer pointers (at pointer area):
+ * address: 0x 7 6 5 4 3 2 1 0
+ * ^ ^ ^
+ * | | |
+ * card | area(rx/tx,0==rx,4==tx)
+ * channel */
+
+#define CARD_POINTER_SHIFT 8u
+#define CHANNEL_POINTER_SHIFT 4u
+#define AREA_POINTER_SHIFT 2u
+
+/* fake pointers are for faking larger unit sizes to the user than
+ * what is the maximum internal unit size in FEPCI */
+#define USER_RX_S_FAKE_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x0) + __offset))
+#define USER_TX_S_FAKE_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x4) + __offset))
+
+#define USER_RX_S_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x8) + __offset))
+#define USER_TX_S_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0xC) + __offset))
+
+#endif
diff -Napur linux-2.6.23/MAINTAINERS linux-2.6.24/MAINTAINERS
--- linux-2.6.23/MAINTAINERS 2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/MAINTAINERS 2007-10-22 11:50:13.190983316 +0300
@@ -3149,6 +3149,11 @@ L: reiserfs-devel@vger.kernel.org
W: http://www.namesys.com
S: Supported
+RETINA DRIVER
+P: Matti Linnanvuori
+M: mattilinnanvuori@yahoo.com
+S: Supported
+
ROCKETPORT DRIVER
P: Comtrol Corp.
W: http://www.comtrol.com
__________________________________ Ihr erstes Baby? Holen Sie sich Tipps von anderen Eltern. www.yahoo.de/clever
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH] wan: new driver retina
2007-10-22 11:14 Matti Linnanvuori
@ 2007-10-22 15:02 ` Randy Dunlap
2007-10-23 0:41 ` Krzysztof Halasa
1 sibling, 0 replies; 8+ messages in thread
From: Randy Dunlap @ 2007-10-22 15:02 UTC (permalink / raw)
To: Matti Linnanvuori; +Cc: akpm, jgarzik, netdev
On Mon, 22 Oct 2007 04:14:57 -0700 (PDT) Matti Linnanvuori wrote:
> From: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
>
> Retina G.703 and G.SHDSL driver.
>
> Signed-off-by: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
> ---
Hi,
Just a few basic questions... I'm not reviewing any of the code parts.
> Fixed Kconfig and the body of the explanation.
>
> diff -Napur
> linux-2.6.23/drivers/net/wan/Kconfig
> linux-2.6.24/drivers/net/wan/Kconfig
> --- linux-2.6.23/drivers/net/wan/Kconfig 2007-10-09 23:31:38.000000000 +0300
> +++ linux-2.6.24/drivers/net/wan/Kconfig 2007-10-22 13:54:55.383644167 +0300
> @@ -494,4 +494,15 @@ config SBNI_MULTILINE
>
> If unsure, say N.
>
> +config RETINA
> + tristate "Retina support"
> + depends on m && PCI
> + help
> + Driver for Retina C5400 and E2200 network cards, which
> + support G.703, G.SHDSL, Ethernet encapsulation and PCI.
> +
> + The driver will be compiled as a module: the
> + module will be called retina.
Why is the driver restricted/limited to module only?
> +
> endif # WAN
> diff -Napur linux-2.6.23/drivers/net/wan/retina.c linux-2.6.24/drivers/net/wan/retina.c
> --- linux-2.6.23/drivers/net/wan/retina.c 1970-01-01 02:00:00.000000000 +0200
> +++ linux-2.6.24/drivers/net/wan/retina.c 2007-10-22 12:01:44.856033562 +0300
> @@ -0,0 +1,4503 @@
> +/* V1.2.4 */
> +
> +/* retina.c: */
> +
> +
> +#define DRV_NAME "retina"
> +#define DRV_VERSION "1.2.5"
> +#define DRV_RELDATE "November 14, 2003"
huh?
> +#if !defined(__OPTIMIZE__)
> +#warning You must compile this file with correct options.
> +#warning See the last lines of the source file.
> +#error You have to compile this driver with "-O".
> +#endif
What defines __OPTIMIZE__ in the kernel build environment?
> diff -Napur linux-2.6.23/drivers/net/wan/retina.h linux-2.6.24/drivers/net/wan/retina.h
> --- linux-2.6.23/drivers/net/wan/retina.h 1970-01-01 02:00:00.000000000 +0200
> +++ linux-2.6.24/drivers/net/wan/retina.h 2007-10-22 12:01:44.856033562 +0300
> @@ -0,0 +1,164 @@
> diff -Napur linux-2.6.23/MAINTAINERS linux-2.6.24/MAINTAINERS
> --- linux-2.6.23/MAINTAINERS 2007-10-09 23:31:38.000000000 +0300
> +++ linux-2.6.24/MAINTAINERS 2007-10-22 11:50:13.190983316 +0300
> @@ -3149,6 +3149,11 @@ L: reiserfs-devel@vger.kernel.org
> W: http://www.namesys.com
> S: Supported
>
> +RETINA DRIVER
> +P: Matti Linnanvuori
> +M: mattilinnanvuori@yahoo.com
and what mailing list?
> +S: Supported
> +
> ROCKETPORT DRIVER
> P: Comtrol Corp.
> W: http://www.comtrol.com
---
~Randy
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] wan: new driver retina
2007-10-22 11:14 Matti Linnanvuori
2007-10-22 15:02 ` Randy Dunlap
@ 2007-10-23 0:41 ` Krzysztof Halasa
1 sibling, 0 replies; 8+ messages in thread
From: Krzysztof Halasa @ 2007-10-23 0:41 UTC (permalink / raw)
To: Matti Linnanvuori; +Cc: akpm, jgarzik, netdev
A quick look only:
Matti Linnanvuori <mattilinnanvuori@yahoo.com> writes:
> +++ linux-2.6.24/drivers/net/wan/retina.c
> + CHANGES
> + -------
> +
> + v1.0.0 (JK) - May 27, 2003:
> + * Original driver.
> +
> + v1.1.0 (JK) - June, 2003:
> + * final Flexibilis driver
> +
> + v1.2.0: NO_ARP option back again
> +
> + v1.2.1: (JT) - Aug 21, 2003:
> + * Added support for Retina C5400 card including PROC stuff.
> +
> + v1.2.2: (Petri Ahonen) - Sep 19, 2003:
And so on - I'm not sure such logs belong here.
> +#define DRV_NAME "retina"
> +#define DRV_VERSION "1.2.5"
> +#define DRV_RELDATE "November 14, 2003"
Hmm...
> +/* obsolete
> + see retina_noarp_with_ptp
> +define FEPCI_NO_ARP */
If it's obsolete (or rather unused), just drop it.
> +#undef inb
> +#undef inw
> +#undef inl
> +#undef outb
> +#undef outw
> +#undef outl
> +#define inb nonexistent /* force using only 32bit access */
> +#define inw nonexistent /* force using only 32bit access */
> +#define inl(x) le32_to_cpu(readl(x))
> +#define outb nonexistent /* force using only 32bit access */
> +#define outw nonexistent /* force using only 32bit access */
> +#define outl(value, address) writel(cpu_to_le32(value), address)
Any code like that is write-only, why don't just use readl()/
writel() in the actual code?
Are you sure about this cpu_to_le32? readl()/writel() already
preserve the value.
> +#define VMA_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT)
Not sure about such things in a driver.
> +enum pci_id_flags_bits {
> + /* Set PCI command register bits before calling probe */
> + PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4,
> + /* Read and map the single following PCI BAR */
> + PCI_ADDR0 = 0 << 4, PCI_ADDR1 = 1 << 4, PCI_ADDR2 =
> + 2 << 4, PCI_ADDR3 = 3 << 4,
> + PCI_ADDR_64BITS = 0x100, PCI_NO_ACPI_WAKE =
> + 0x200, PCI_NO_MIN_LATENCY = 0x400,
> + PCI_UNUSED_IRQ = 0x800,
> +};
We already have such things in PCI headers, don't we?
> +/* Linux 2.4 appears to drop POINTOPOINT,BROADCAST and NOARP flags
Linux 2.4?
> +/* proc filesystem functions introduced: */
I'm not sure we're adding new /proc files.
Perhaps you should investigate sysfs and friends?
> + case FEPCI_IOCTL_R_SHARED_MEM:
> + DBG_PRINT(" %s: ioctl read shared mem commanded.\n",
> + fepci_NAME);
> + fepci_copy_to_user(arg, ioaddr + FEPCI_SHARED_MEM_OFFSETT,
> + _IOC_SIZE(cmd), 0);
> + break;
> + case FEPCI_IOCTL_W_SHARED_MEM:
> + DBG_PRINT(" %s: ioctl write shared mem commanded.\n",
> + fepci_NAME);
> + fepci_copy_from_user(ioaddr + FEPCI_SHARED_MEM_OFFSETT,
> + arg, _IOC_SIZE(cmd), 0);
> + break;
> + case FEPCI_IOCTL_G_IDENTIFICATION:
> + DBG_PRINT(" %s: IOCTL_G_IDENTIFICATION commanded.\n",
> + fepci_NAME);
> + fepci_copy_to_user(arg,
> + ioaddr + FEPCI_IDENTIFICATION_OFFSETT,
> + _IOC_SIZE(cmd), 1);
> + break;
> + case FEPCI_IOCTL_G_FEATURES:
> + DBG_PRINT(" %s: IOCTL_G_FEATURES commanded.\n", fepci_NAME);
> + fepci_copy_to_user(arg, ioaddr + FEPCI_FEATURES_OFFSETT,
> + _IOC_SIZE(cmd), 1);
> + break;
Are you sure these ioctls are a good idea? Perhaps sysfs attributes
would be much better?
> + if (length == 0) {
> + fp->rx_packets_of_size_0_stream++;
> + } else if (length == 1) {
> + fp->rx_packets_of_size_1_stream++;
> + } else if (length == 2) {
> + fp->rx_packets_of_size_2_stream++;
> + } else if (length == 3) {
> + fp->rx_packets_of_size_3_stream++;
> + } else if (length < 8) {
> + fp->rx_packets_of_size_4_7_stream++;
> + } else if (length < 16) {
...
I think style details are really a personal thing but this would
look much better without the braces.
> + }
> + temp_tx = (temp_tx + 1) & (TX_RING_SIZE - 1);
> + temp_tx_unit = (temp_tx_unit + 1);
> + temp_tx_unit *= temp_tx_unit < fp->units;
> + }
> +
> + return IRQ_HANDLED;
No unhandled IRQ protection anymore?
> +#ifdef FEPCI_POINT_TO_POINT
> +static int is_ptp_interface(struct net_device *dev)
> +{
> + char **p_ptp_if_name = retina_ptp_interfaces;
> + unsigned int i = interfaces;
> + while (i > 0 && *p_ptp_if_name != NULL) {
> + if (!strncmp(dev->name, *p_ptp_if_name, sizeof(dev->name))) {
> + return 1;
> + } else {
> + }
> + p_ptp_if_name++;
> + i--;
> + }
> + return 0;
> +}
A bit weird, isn't it?
> +static int __devinit fepci_init_one(struct pci_dev *pdev,
> + const struct pci_device_id *ent)
> +{
> + struct net_device *dev = NULL;
> + struct fepci_ch_private *fp = NULL;
> + int chip_idx = ent->driver_data;
> + int drv_flags = pci_id_tbl[chip_idx].drv_flags;
> + int i = pci_enable_device(pdev);
> + u32 j;
> + resource_size_t real_ioaddr;
> + void *ioaddr;
> + unsigned position;
> +
> + if (i) {
> + printk(KERN_WARNING "%s: pci_enable_device returned %x.\n",
> + fepci_NAME, i);
> + return i;
> + }
Didn't spot that pci_enable_device() at first. I think we shouldn't
put state-changing functions in variable declarations, especially
when there are many variables.
> + goto err_2;
> + FOUND:
The labels can be hard to spot, too.
> + /* dev->name[3]= j+0x30; channel number -> ascii */
> + /* minor number -> ascii */
> + dev->name[4] = ((fp->minor * CHANNELS + j) % 10) + 0x30;
> + /* minor number -> ascii */
> + dev->name[3] = ((fp->minor * CHANNELS + j) / 10) + 0x30;
That 0x30 could be written as plain '0'.
> + /* HW_ADDR: 00:rnd:rnd:rnd:rnd:05 */
> + dev->dev_addr[0] = 0;
> + get_random_bytes(&(dev->dev_addr[1]), 4);
> + dev->dev_addr[5] = 5;
I think we already have a function for this.
> +
> +static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> + struct fepci_ch_private *fp = dev->priv;
> + const unsigned cur_tx = fp->cur_tx;
> +
> + fp->tx_skbuffs_in++;
> +
> + {
> + u32 tx_length = skb->len;
> + u32 bus_address;
> + struct sk_buff *old;
> + if (unlikely(tx_length < ETH_ZLEN)) {
> + struct sk_buff *bigger =
Why don't you just move the variables to the beginning of this function?
The extra indentation isn't good for readability.
> + if (intr_status & IntrFrameTransmitted) {
> + fp->tx_interrupts_since_last_timer++;
> + if (netif_tx_trylock(dev)) {
> + unsigned i = TX_RING_SIZE - 1;
> + do {
> + u32 desc_b;
> + if ((fp->tx_skbuff[i] != NULL)
> + &&
> + (((desc_b =
> + inl(&fp->tx_desc[i].
> + desc_b)) & transfer_not_done) ==
> + 0)) {
> + /* has been sent */
> + pci_unmap_single(fp->
> + this_card_priv->
> + pci_dev,
> + inl(&fp->
> + tx_desc[i].
> + desc_a),
> + desc_b &
> + frame_length,
> + PCI_DMA_TODEVICE);
The above looks like a hardcopy printout with missing CRs (carriage
returns) :-)
Why not split it?
> + [line]++;
> + /* small packet counters */
> + if (length == 0)
> + fp->rx_packets_of_size_0++;
> + else if (length == 1)
> + fp->rx_packets_of_size_1++;
> + else if (length == 2)
> + fp->rx_packets_of_size_2++;
> + else if (length == 3)
> + fp->rx_packets_of_size_3++;
> + else if (length < 8)
> + fp->rx_packets_of_size_4_7++;
> + else if (length < 16)
> + fp->rx_packets_of_size_8_15++;
> + else if (length < 32)
> + fp->rx_packets_of_size_16_31++;
Is there a specific reason to have such detailed counters?
> +int get_line_data_rate_value(unsigned char line_rate)
> +{
> + switch (line_rate) {
> + case 0x00:
> + return 0;
> + case 0x01:
> + return 1;
> + case 0x05:
> + return 8;
> + case 0x06:
> + return 10;
> + case 0x14:
> + return 256;
> + case 0x15:
> + return 300;
> + case 0x20:
> + return 1;
> + case 0x28:
> + return 8;
> + case 0x29:
> + return 10;
> + case 0x30:
> + return 32;
> + case 0x34:
> + return 56;
> + case 0x36:
> + return 64;
> + case 0x60:
> + return 1;
> + case 0xa0:
> + return 1;
> + default:
> + return -1;
> + }
> +}
> +
> +char get_line_data_rate_unit(unsigned char line_rate)
> +{
> + switch (line_rate) {
> + case 0x00:
> + return 0;
> + case 0x01:
> + return 0;
> + case 0x05:
> + return 0;
> + case 0x06:
> + return 0;
> + case 0x14:
> + return 0;
> + case 0x15:
> + return 0;
> + case 0x20:
> + return 'k';
> + case 0x28:
> + return 'k';
> + case 0x29:
> + return 'k';
> + case 0x30:
> + return 'k';
> + case 0x34:
> + return 'k';
> + case 0x36:
> + return 'k';
> + case 0x60:
> + return 'M';
> + case 0xa0:
> + return 'G';
> + default:
> + return 0;
> + }
> +}
Are you sure you really want this?
> +int print_line_type(unsigned char type, char *buf, int pos)
> +{
> + DBG_PRINT("print_line_type %c\n", type);
> + switch (type) {
> + case 0:
> + pos += sprintf(buf + pos, "NONE");
> + break;
> + case 1:
> + pos += sprintf(buf + pos, "DCombus management bus");
> + break;
> + case 2:
> + pos += sprintf(buf + pos, "V.24");
> + break;
> + case 3:
> + pos += sprintf(buf + pos, "X.21");
> + break;
> + case 4:
> + pos += sprintf(buf + pos, "V.35");
> + break;
> + case 5:
> + pos += sprintf(buf + pos, "V.11");
> + break;
> + case 6:
> + pos += sprintf(buf + pos, "IDSL (ISDN Basic Rate");
> + break;
> + case 7:
> + pos += sprintf(buf + pos, "E1 nonframed/framed");
> + break;
> + case 8:
> + pos += sprintf(buf + pos, "E2 nonframed/framed");
> + break;
> + case 9:
A table would be more readable I think.
> +++ linux-2.6.24/drivers/net/wan/retina.h
> @@ -0,0 +1,164 @@
Short header file included by single .c - is it worth it?
Wow, really long.
--
Krzysztof Halasa
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH] wan: new driver retina
@ 2007-10-22 9:15 Matti Linnanvuori
0 siblings, 0 replies; 8+ messages in thread
From: Matti Linnanvuori @ 2007-10-22 9:15 UTC (permalink / raw)
To: akpm, jgarzik, netdev
Retina G.703 and G.SHDSL drivers.
Signed-off-by: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
---
diff -Napur linux-2.6.23/drivers/net/wan/Kconfig linux-2.6.24/drivers/net/wan/Kconfig
--- linux-2.6.23/drivers/net/wan/Kconfig 2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/drivers/net/wan/Kconfig 2007-10-22 11:58:36.776467534 +0300
@@ -494,4 +494,15 @@ config SBNI_MULTILINE
If unsure, say N.
+config RETINA
+ tristate "Retina support"
+ depends on m && HDLC_PPP
+ help
+ Driver for Retina C5400 and E2200 network cards, which
+ support G.703, G.SHDSL, Ethernet and PPP.
+
+ The driver will be compiled as a module: the
+ module will be called retina.
+
+
endif # WAN
diff -Napur linux-2.6.23/drivers/net/wan/Makefile linux-2.6.24/drivers/net/wan/Makefile
--- linux-2.6.23/drivers/net/wan/Makefile 2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/drivers/net/wan/Makefile 2007-10-22 12:05:57.638172823 +0300
@@ -42,6 +42,7 @@ obj-$(CONFIG_C101) += c101.o
obj-$(CONFIG_WANXL) += wanxl.o
obj-$(CONFIG_PCI200SYN) += pci200syn.o
obj-$(CONFIG_PC300TOO) += pc300too.o
+obj-$(CONFIG_RETINA) += retina.o
clean-files := wanxlfw.inc
$(obj)/wanxl.o: $(obj)/wanxlfw.inc
diff -Napur linux-2.6.23/drivers/net/wan/retina.c linux-2.6.24/drivers/net/wan/retina.c
--- linux-2.6.23/drivers/net/wan/retina.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.24/drivers/net/wan/retina.c 2007-10-22 12:01:44.856033562 +0300
@@ -0,0 +1,4503 @@
+/* V1.2.4 */
+
+/* retina.c: */
+
+/*
+ This driver is based on:
+
+ /drivers/net/fepci.c
+ FEPCI (Frame Engine for PCI) driver for Linux operating system
+
+ Copyright (C) 2002-2003 Jouni Kujala, Flexibilis Oy.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ All the drivers derived from or based on this code fall under the
+ GPL and must retain the copyright and license notice.
+
+ CHANGES
+ -------
+
+ v1.0.0 (JK) - May 27, 2003:
+ * Original driver.
+
+ v1.1.0 (JK) - June, 2003:
+ * final Flexibilis driver
+
+ v1.2.0: NO_ARP option back again
+
+ v1.2.1: (JT) - Aug 21, 2003:
+ * Added support for Retina C5400 card including PROC stuff.
+
+ v1.2.2: (Petri Ahonen) - Sep 19, 2003:
+ * PtP changes:
+ * Added retina_ptp_interfaces module parameter for initializing
+ * interfaces in PtP mode.
+ * for example:
+ * insmod ./retina.o retina_ptp_interfaces=dcp00,dcp01
+ * Added retina_noarp_with_ptp module parameter to set/clear
+ * NOARP flag on PtP interfaces
+ * for example:
+ * insmod ./retina.o retina_ptp_interfaces=dcp00 retina_noarp_with_ptp=0
+
+ v1.2.3: (JT) - Nov 14, 2003:
+ * Fixed PROC packet counters.
+
+ v1.2.4: (JT) - May 14, 2004:
+ * Made initializing MACs safer.
+*/
+
+#define DRV_NAME "retina"
+#define DRV_VERSION "1.2.5"
+#define DRV_RELDATE "November 14, 2003"
+
+#define DBG_PRINT(xyz...)
+/* printk(KERN_DEBUG xyz) */
+
+/* uncomment this if you want to have debug files in /proc */
+/* #define DEBUG_PROC_FILES */
+
+/* obsolete
+ see retina_noarp_with_ptp
+define FEPCI_NO_ARP */
+
+/* uncomment this if you want to have point-to-point links
+ ! Only interfaces listed in retina_ptp_interfaces will
+ be created in PtP mode
+ see retina_ptp_interfaces */
+#define FEPCI_POINT_TO_POINT
+
+/* need to update MODULE_PARM also */
+#define MAX_DEVICES 32u
+
+#define MAX_TX_UNITS 256u
+#define MAX_RX_UNITS 256u
+
+#define MAX_UNIT_SZ_ORDER 10u
+
+#define TX_RING_SIZE 8u
+#define RX_RING_SIZE 8u
+
+/* need to update MODULE_PARM also */
+#define CHANNELS 4u
+
+#define RX_FIFO_THRESHOLD_PACKET_MODE 0x4
+#define TX_FIFO_THRESHOLD_PACKET_MODE 0x4
+#define TX_DESC_THRESHOLD_PACKET_MODE 0x4
+
+#define RX_FIFO_THRESHOLD_STREAM_MODE 0x4
+#define TX_FIFO_THRESHOLD_STREAM_MODE 0x7
+#define TX_DESC_THRESHOLD_STREAM_MODE 0x1
+
+/* need to update MODULE_PARM also */
+#define MAX_INTERFACES (CHANNELS*MAX_DEVICES)
+
+const char fepci_name[] = "retina";
+const char fepci_alarm_manager_name[] = "retina alarm manager";
+const char fepci_NAME[] = "RETINA";
+const char fepci_netdev_name[] = "dcpxx";
+const char fepci_proc_entry_name[] = "driver/retina";
+
+static unsigned int find_cnt;
+
+#ifdef FEPCI_POINT_TO_POINT
+static char *retina_ptp_interfaces[MAX_INTERFACES];
+static int retina_noarp_with_ptp = 1;
+#endif /* FEPCI_POINT_TO_POINT */
+
+#define fepci_features_proc_entry_name "driver/retina/%02x:%02x.%02x/features"
+#define fepci_settings_proc_entry_name "driver/retina/%02x:%02x.%02x/settings"
+#define fepci_status_proc_entry_name "driver/retina/%02x:%02x.%02x/status"
+
+#ifdef DEBUG_PROC_FILES
+#define fepci_counters_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/counters_ch%d"
+#define fepci_descriptors_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/descriptors_ch%d"
+#define fepci_stream_counters_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/stream_counters_ch%d"
+#define fepci_registers_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/registers_ch%d"
+#endif /* DEBUG_PROC_FILES */
+
+/* Time in jiffies before concluding that the transmitter is hung */
+#define TX_TIMEOUT (20*HZ)
+
+#if !defined(__OPTIMIZE__)
+#warning You must compile this file with correct options.
+#warning See the last lines of the source file.
+#error You have to compile this driver with "-O".
+#endif
+#include "retina.h"
+
+#include <linux/mm.h>
+#include <linux/random.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/version.h>
+#include <linux/pfn.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/unaligned.h>
+#include <asm/pgtable.h>
+
+MODULE_VERSION(DRV_VERSION);
+
+#undef inb
+#undef inw
+#undef inl
+#undef outb
+#undef outw
+#undef outl
+#define inb nonexistent /* force using only 32bit access */
+#define inw nonexistent /* force using only 32bit access */
+#define inl(x) le32_to_cpu(readl(x))
+#define outb nonexistent /* force using only 32bit access */
+#define outw nonexistent /* force using only 32bit access */
+#define outl(value, address) writel(cpu_to_le32(value), address)
+
+#define VMA_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT)
+
+enum pci_id_flags_bits {
+ /* Set PCI command register bits before calling probe */
+ PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4,
+ /* Read and map the single following PCI BAR */
+ PCI_ADDR0 = 0 << 4, PCI_ADDR1 = 1 << 4, PCI_ADDR2 =
+ 2 << 4, PCI_ADDR3 = 3 << 4,
+ PCI_ADDR_64BITS = 0x100, PCI_NO_ACPI_WAKE =
+ 0x200, PCI_NO_MIN_LATENCY = 0x400,
+ PCI_UNUSED_IRQ = 0x800,
+};
+
+/* PCI I/O space extent */
+#define FEPCI_SIZE 0x20000
+#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR0)
+
+struct pci_id_info {
+ const char *name;
+ struct match_info {
+ int pci, pci_mask, subsystem, subsystem_mask;
+ int revision, revision_mask; /* Only 8 bits. */
+ } id;
+ enum pci_id_flags_bits pci_flags;
+ int io_size; /* Needed for I/O region check or ioremap */
+ int drv_flags; /* Driver use, intended as capability flags */
+};
+
+static struct pci_id_info pci_id_tbl[] = {
+ {"Frame Engine for PCI (FEPCI)",
+ {0x1FC00300, 0x1FC00301, 0xffffffff},
+ PCI_IOTYPE, FEPCI_SIZE},
+ {0,},
+};
+
+static struct pci_device_id fepci_pci_tbl[] __devinitdata = {
+ {0x1FC0, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x1FC0, 0x0301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+MODULE_DESCRIPTION("Frame Engine for PCI (FEPCI)");
+MODULE_AUTHOR("Jouni Kujala");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, fepci_pci_tbl);
+
+/* Linux 2.4 appears to drop POINTOPOINT,BROADCAST and NOARP flags in SIOCSFLAGS
+ * This workaround allows load time per interface ptp mode configuration.
+ * Runtime ptp mode changes would either require changes to 2.4 or
+ * use of proprietary ioctls, which ifconfig knows nothing about anyway
+ */
+
+static unsigned interfaces = MAX_INTERFACES;
+#ifdef FEPCI_POINT_TO_POINT
+module_param_array(retina_ptp_interfaces, charp, &interfaces, S_IRUGO);
+module_param(retina_noarp_with_ptp, bool, S_IRUGO);
+MODULE_PARM_DESC(retina_noarp_with_ptp,
+ "0 to disable NOARP, "
+ "1 to enable NOARP on pointopoint interfaces");
+#endif
+
+struct fepci_ch_private {
+ unsigned int channel_number;
+ struct net_device *this_dev;
+
+ struct fepci_card_private *this_card_priv;
+
+ unsigned int reg_rxctrl;
+ unsigned int reg_txctrl;
+
+ struct fepci_desc *rx_desc; /* rx_ring start */
+ struct fepci_desc *tx_desc; /* tx_ring start */
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+
+ struct timer_list timer;
+ struct net_device_stats stats;
+
+ unsigned drv_flags;
+
+ unsigned int rx_buf_sz; /* MTU+slack */
+ unsigned int cur_tx; /* the next filled tx_descriptor */
+ /* in stream mode the desc which is being transmitted */
+ /* rx_descriptor where next packet transferred */
+ unsigned int cur_rx;
+ /* in stream mode the desc which is being received */
+
+/* stream mode: */
+ bool in_eth_mode;
+ bool in_stream_mode;
+ bool stream_on;
+ u32 *rx_buffer;
+ u32 *tx_buffer;
+ unsigned bufsize_order; /* 10=1kB,11=2kB,12=4kB...16=64kB */
+ u32 bufsize;
+ unsigned unit_sz_order; /* 8=256B...14=16kB */
+ u32 unit_sz;
+ unsigned units; /* 2,4,8,16,...,256 */
+ /* fake units (and pointers) are for faking larger unit sizes to
+ * the user than what is the maximum internal unit size in FEPCI */
+ unsigned fake_unit_sz_order;
+ u32 fake_unit_sz;
+ unsigned fake_units;
+ u32 *tx_unit[MAX_TX_UNITS];
+ u32 *rx_unit[MAX_RX_UNITS];
+ unsigned cur_tx_unit; /* last sent tx_unit */
+ /* rx_unit where to next packet is transferred */
+ unsigned cur_rx_unit;
+/* char device: */
+ unsigned minor; /* currently the same as card_nuber */
+/* debugging stuff for packet mode: */
+ /* rx-errors in descriptors */
+ unsigned int rx_desc_fifo_err;
+ unsigned int rx_desc_size_err;
+ unsigned int rx_desc_crc_err;
+ unsigned int rx_desc_octet_err;
+ unsigned rx_desc_line_err;
+ /* tx-errors in descriptors */
+ unsigned tx_desc_fifo_err;
+ /* rx-errors in interrupts */
+ unsigned rx_int_fifo_err;
+ unsigned rx_int_frame_dropped_err;
+ /* tx-errors in interrupts */
+ unsigned tx_int_fifo_err;
+ /* ints */
+ unsigned interrupts;
+ unsigned rx_interrupts;
+ unsigned tx_interrupts;
+ /* error combination tables */
+ /* fifo,size,crc,octet,line */
+ unsigned rx_desc_err_table[2][2][2][2][2];
+ unsigned int_err_table[2][2]; /* fifo,frame_dropped */
+ /* skbuff counters */
+ unsigned rx_skbuffs_in;
+ unsigned rx_skbuffs_out;
+ unsigned tx_skbuffs_in;
+ unsigned tx_skbuffs_out;
+/* debugging stuff for stream mode: */
+ /* rx-errors in descriptors */
+ unsigned rx_desc_fifo_err_stream;
+ unsigned rx_desc_size_err_stream;
+ unsigned rx_desc_crc_err_stream;
+ unsigned rx_desc_octet_err_stream;
+ unsigned rx_desc_line_err_stream;
+ /* tx-errors in descriptors */
+ unsigned tx_desc_fifo_err_stream;
+ /* rx-errors in interrupts */
+ unsigned rx_int_fifo_err_stream;
+ unsigned rx_int_frame_dropped_err_stream;
+ /* tx-errors in interrupts */
+ unsigned tx_int_fifo_err_stream;
+ /* ints */
+ unsigned interrupts_stream;
+ unsigned rx_interrupts_stream;
+ unsigned tx_interrupts_stream;
+ /* error combination tables */
+ /* fifo,size,crc,octet,line */
+ unsigned rx_desc_err_table_stream[2][2][2][2][2];
+ unsigned int_err_table_stream[2][2]; /* fifo,frame_dropped */
+/* other: */
+ /* tx interrupts since last timer interrupt */
+ unsigned tx_interrupts_since_last_timer;
+/* small packet counters: */
+ unsigned rx_packets_of_size_0;
+ unsigned rx_packets_of_size_1;
+ unsigned rx_packets_of_size_2;
+ unsigned rx_packets_of_size_3;
+ unsigned rx_packets_of_size_4_7;
+ unsigned rx_packets_of_size_8_15;
+ unsigned rx_packets_of_size_16_31;
+/* small packet counters for stream: */
+ unsigned rx_packets_of_size_0_stream;
+ unsigned rx_packets_of_size_1_stream;
+ unsigned rx_packets_of_size_2_stream;
+ unsigned rx_packets_of_size_3_stream;
+ unsigned rx_packets_of_size_4_7_stream;
+ unsigned rx_packets_of_size_8_15_stream;
+ unsigned rx_packets_of_size_16_31_stream;
+};
+
+struct fepci_card_private {
+ unsigned int card_number;
+ u8 *ioaddr;
+ /* Process ID of the current mailbox user
+ * (for whom it is reserved for) */
+ unsigned int ioctl_saved_pid;
+ struct pci_dev *pci_dev;
+ struct fepci_ch_private *ch_privates[CHANNELS];
+
+ wait_queue_head_t alarm_manager_wait_q;
+ struct timer_list mailbox_timer;
+
+ wait_queue_head_t stream_receive_q;
+ wait_queue_head_t stream_transmit_q;
+ wait_queue_head_t stream_both_q;
+
+ struct rw_semaphore semaphore;
+};
+
+/* Offsets to the FEPCI registers */
+enum fepci_offsets {
+ reg_custom = 0x40,
+
+ reg_first_int_mask = 0x80,
+ reg_first_int_status = 0xc0,
+
+ reg_first_rxctrl = 0x4000,
+ to_next_rxctrl = 0x80,
+
+ reg_first_txctrl = 0x6000,
+ to_next_txctrl = 0x80,
+
+ first_rx_desc = 0x10000,
+ to_next_ch_rx_desc = 0x200,
+
+ first_tx_desc = 0x18000,
+ to_next_ch_tx_desc = 0x200,
+};
+
+enum reg_custom_bits {
+ AM_interrupt_mask = 0x1,
+ AM_interrupt_status = 0x100,
+};
+
+enum reg_receive_control {
+ Rx_fifo_threshold = 0x7,
+ Receive_enable = 0x80000000,
+};
+
+enum reg_transmit_control {
+ Tx_fifo_threshold = 0x7,
+ Tx_desc_threshold = 0x700,
+ Transmit_enable = 0x80000000,
+};
+
+enum int_bits {
+ MaskFrameReceived = 0x01, MaskRxFifoError =
+ 0x02, MaskRxFrameDroppedError = 0x04,
+ MaskFrameTransmitted = 0x40, MaskTxFifoError = 0x80,
+ MaskAllInts = 0xc7,
+ IntrFrameReceived = 0x01, IntrRxFifoError =
+ 0x02, IntrRxFrameDroppedError = 0x04,
+ IntrFrameTransmitted = 0x40, IntrTxFifoError = 0x80,
+ IntrAllInts = 0xc7,
+};
+
+/* The FEPCI Rx and Tx buffer descriptors
+ * Elements are written as 32 bit for endian portability */
+
+struct fepci_desc {
+ u32 desc_a;
+ u32 desc_b;
+};
+
+enum desc_b_bits {
+ frame_length = 0xFFF,
+ fifo_error = 0x10000,
+ size_error = 0x20000,
+ crc_error = 0x40000,
+ octet_error = 0x80000,
+ line_error = 0x100000,
+ enable_transfer = 0x80000000,
+ transfer_not_done = 0x80000000,
+};
+
+/* global variables (common to whole driver, all the cards): */
+int major; /* char device major number */
+struct fepci_card_private card_privates[MAX_DEVICES];
+u32 stream_pointers;
+struct proc_dir_entry *proc_root_entry;
+
+void set_int_mask(int channel, u_char value, struct fepci_card_private *cp)
+{
+ void *address;
+ u32 shift, oldvalue;
+ DBG_PRINT("set_int_mask\n");
+ address = (cp->ioaddr) + reg_first_int_mask + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ oldvalue = inl((void *)address);
+ oldvalue &= ~(u32) (0xff << shift); /* clear bits */
+ oldvalue |= (value << shift); /* set bits */
+ outl(oldvalue, (void *)address);
+}
+
+u_char get_int_mask(int channel, void *ioaddr)
+{
+ void *address;
+ u32 shift, oldvalue;
+ DBG_PRINT("get_int_mask\n");
+ address = ioaddr + reg_first_int_mask + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ oldvalue = inl((void *)address);
+ oldvalue &= (0xff << shift); /* clear other bits */
+ return (oldvalue >> shift);
+}
+
+void clear_int(int channel, u_char value, void *ioaddr)
+{
+ void *address;
+ u32 shift, longvalue;
+ DBG_PRINT("clear_int\n");
+ address = ioaddr + reg_first_int_status + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ longvalue = value << shift;
+ outl(~longvalue, (void *)address);
+}
+
+u_char get_int_status(int channel, void *ioaddr)
+{
+ void *address;
+ u32 shift, oldvalue;
+ DBG_PRINT("get_int_status\n");
+ address = ioaddr + reg_first_int_status + (channel / 4L) * 4L;
+ shift = 8L * (channel % 4L);
+ oldvalue = inl((void *)address);
+ oldvalue &= (0xff << shift); /* clear other bits */
+ return (oldvalue >> shift);
+}
+
+void fillregisterswith_00(void *ioaddr)
+{
+ DBG_PRINT("fillregisterswith_00\n");
+ outl(0x0, (void *)(ioaddr + reg_first_rxctrl));
+ outl(0x0, (void *)(ioaddr + reg_first_txctrl));
+ outl(0x0, (void *)(ioaddr + reg_first_int_mask));
+ outl(0x0, (void *)(ioaddr + reg_first_int_status));
+ outl(0x0, (void *)(ioaddr + first_rx_desc));
+ outl(0x0, (void *)(ioaddr + first_tx_desc));
+}
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int fepci_open(struct net_device *dev);
+static void fepci_timer(unsigned long data);
+static void fepci_tx_timeout(struct net_device *dev);
+static void fepci_init_ring(struct net_device *dev);
+static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t fepci_interrupt(int irq, void *dev_instance);
+static int fepci_rx(struct net_device *dev);
+static int fepci_close(struct net_device *dev);
+static struct net_device_stats *fepci_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static void fepci_remove_one(struct pci_dev *pdev);
+
+/* proc filesystem functions introduced: */
+
+static int fepci_proc_init_driver(void);
+static void fepci_proc_cleanup_driver(void);
+static void fepci_proc_init_card(int card_number, void *card_data);
+static void fepci_proc_cleanup_card(int card_number);
+#ifdef DEBUG_PROC_FILES
+static void fepci_proc_init_channel(int card_number, int channel_number,
+ void *channel_data);
+static void fepci_proc_cleanup_channel(int card_number, int channel_number);
+#endif /* DEBUG_PROC_FILES */
+
+/* char device operations: */
+
+ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+ loff_t *f_pos);
+int fepci_char_open(struct inode *inode, struct file *filp);
+int fepci_char_release(struct inode *inode, struct file *filp);
+int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma);
+int fepci_char_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+struct file_operations fepci_char_fops = {
+ read:fepci_char_read,
+ ioctl:fepci_char_ioctl,
+ open:fepci_char_open,
+ release:fepci_char_release,
+ mmap:fepci_char_mmap,
+};
+
+int fepci_char_open(struct inode *inode, struct file *filp)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ DBG_PRINT("fepci_char_open\n");
+ if (unlikely(minor >= find_cnt || card_privates[minor].pci_dev == NULL))
+ return -ENXIO;
+ filp->f_op = &fepci_char_fops;
+ if (unlikely(!try_module_get(THIS_MODULE)))
+ return -EBUSY;
+ return 0;
+}
+
+int fepci_char_release(struct inode *inode, struct file *filp)
+{
+ DBG_PRINT("fepci_char_release\n");
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+void fepci_vma_open(struct vm_area_struct *vma)
+{
+ DBG_PRINT("fepci_vma_open\n");
+}
+
+void fepci_vma_close(struct vm_area_struct *vma)
+{
+ DBG_PRINT("fepci_vma_close\n");
+ module_put(THIS_MODULE);
+}
+
+static struct vm_operations_struct fepci_vm_ops = {
+ open:fepci_vma_open,
+ close:fepci_vma_close,
+};
+
+int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ unsigned long offset = VMA_OFFSET(vma);
+ unsigned long size = vma->vm_end - vma->vm_start;
+
+ u32 virtual_address = 0;
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_ops = &fepci_vm_ops;
+ vma->vm_file = filp;
+
+ if (offset == STREAM_BUFFER_POINTER_AREA) {
+ virtual_address = stream_pointers;
+ if (virtual_address == 0) {
+ printk(KERN_WARNING "%s: mmap: internal error.\n",
+ fepci_name);
+ return -ENOMEM;
+ }
+ if (size > (1 << PAGE_SHIFT)) {
+ printk(KERN_WARNING
+ "%s: mmap: area size over range.\n", fepci_name);
+ return -EINVAL;
+ }
+ } else {
+ unsigned int page;
+
+ unsigned int card;
+ unsigned int channel;
+ unsigned int area; /* 0=rx, 1=tx */
+
+ card = (offset >> CARD_ADDRESS_SHIFT) & 0xf;
+ channel = (offset >> CHANNEL_ADDRESS_SHIFT) & 0xf;
+ area = (offset >> AREA_ADDRESS_SHIFT) & 0xf;
+ page = (offset & 0xffff); /* >> PAGE_SHIFT; */
+
+ if (area == 0) {
+ /* if there really is such card */
+ if (card < find_cnt && card_privates[card].pci_dev)
+ virtual_address =
+ (u32) card_privates[card].
+ ch_privates[channel]->rx_buffer;
+ else
+ goto INVALID;
+ } else if (area == 1) {
+ /* if there really is such card */
+ if (card < find_cnt && card_privates[card].pci_dev)
+ virtual_address =
+ (u32) card_privates[card].
+ ch_privates[channel]->tx_buffer;
+ else
+ goto INVALID;
+ } else {
+ INVALID:
+ DBG_PRINT("%s: mmap: invalid address 0x%x\n",
+ fepci_NAME, virtual_address);
+ return -EINVAL;
+ }
+ if (virtual_address == 0)
+ goto INVALID;
+ }
+
+ if (!try_module_get(THIS_MODULE))
+ return -EBUSY;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ {
+ unsigned pfn = PFN_DOWN(virt_to_phys((void *)virtual_address));
+ int error = io_remap_pfn_range(vma, vma->vm_start, pfn,
+ size, vma->vm_page_prot);
+ if (unlikely(error))
+ return error;
+ }
+ fepci_vma_open(vma);
+ return 0;
+}
+
+/* mmap operations end */
+
+/* char operations start */
+
+void fepci_copy_to_user(unsigned long to, void *from, unsigned long len,
+ int shrink)
+{
+ unsigned int i;
+ DBG_PRINT("fepci_copy_to_user\n");
+ if (shrink) {
+ for (i = 0; i < len; i += 2) {
+ put_user((((unsigned long *)from)[i / 2]) & 0xff,
+ (unsigned char *)(to + i));
+ put_user((((unsigned long *)from)[i / 2]) >> 8,
+ (unsigned char *)(to + i + 1));
+ }
+ } else {
+ for (i = 0; i < len; i += 4)
+ put_user(((unsigned long *)from)[i / 4],
+ (unsigned long *)(to + i));
+ }
+}
+
+void fepci_copy_from_user(void *to, unsigned long from, unsigned long len,
+ int enlarge)
+{
+ unsigned int i;
+ if (enlarge) {
+ for (i = 0; i < len; i += 2) {
+ unsigned char temp1;
+ unsigned char temp2;
+ get_user(temp1, (unsigned char *)(from + i));
+ get_user(temp2, (unsigned char *)(from + i + 1));
+ *((unsigned long *)(to + i * 2)) = temp1 + (temp2 << 8);
+ }
+ } else {
+ for (i = 0; i < len; i += 4)
+ get_user(((unsigned long *)to)[i / 4],
+ (unsigned long *)(from + i));
+ }
+}
+
+unsigned get_semafore(struct fepci_real_mailbox *mailbox)
+{
+ unsigned semafore = readb(&mailbox->Semafore_Mail_number);
+ DBG_PRINT("get_semafore = %x\n", semafore);
+ return semafore;
+}
+
+int set_semafore(struct fepci_real_mailbox *mailbox, unsigned semafore)
+{
+ u32 number = inl(&mailbox->Semafore_Mail_number);
+ DBG_PRINT("got number %u at %p.\n", number,
+ &mailbox->Semafore_Mail_number);
+ number = ((number & ~0xFF) | semafore) + (1 << 8);
+ DBG_PRINT
+ ("increases the mail number to %u at the same time at %p.\n",
+ number, &mailbox->Semafore_Mail_number);
+ outl(number, &mailbox->Semafore_Mail_number);
+ DBG_PRINT("set_semafore %p, %u returns 0.\n", mailbox, semafore);
+ return 0;
+}
+
+static void fepci_mailbox_timer(unsigned long data)
+{
+ int card_number = data;
+ unsigned int *saved_pid = &card_privates[card_number].ioctl_saved_pid;
+ void *ioaddr = card_privates[card_number].ioaddr;
+ struct fepci_real_mailbox *real_mailbox =
+ (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
+
+ set_semafore(real_mailbox, 0x0);
+ *saved_pid = 0;
+}
+
+int fepci_char_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ void *ioaddr;
+ struct fepci_real_mailbox *real_mailbox;
+ int retval = 0;
+ unsigned int *saved_pid;
+ unsigned int my_pid;
+
+ if (minor >= find_cnt || card_privates[minor].pci_dev == NULL) {
+ printk(KERN_WARNING
+ "%s: trying to access a card that does not exist.\n",
+ fepci_NAME);
+ /* if trying to access a card that does not exist */
+ return -ENXIO;
+ }
+
+ DBG_PRINT("fepci_char_ioctl minor %u.\n", minor);
+
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ if (!access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ if (!access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+
+ ioaddr = card_privates[minor].ioaddr;
+ real_mailbox =
+ (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
+ saved_pid = &card_privates[minor].ioctl_saved_pid;
+ my_pid = current->pid;
+
+ switch (cmd) {
+ case FEPCI_IOCTL_STREAM_TRANSMIT_POLL:
+ /* here: arg == channel number */
+ if (arg < 0 || arg >= CHANNELS
+ || !(card_privates[minor].ch_privates[arg]->stream_on))
+ return 0x2;
+ {
+ u32 pointer = *USER_TX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+ wait_event_interruptible((card_privates[minor].
+ stream_transmit_q)
+ ,
+ (pointer !=
+ *USER_TX_S_FAKE_POINTER
+ (minor, arg,
+ stream_pointers)));
+ return 0x1;
+ }
+ return retval;
+ case FEPCI_IOCTL_STREAM_RECEIVE_POLL:
+ /* here: arg == channel number */
+ if (arg < 0 || arg >= CHANNELS
+ || !(card_privates[minor].ch_privates[arg]->stream_on))
+ return 0x2;
+ {
+ u32 pointer = *USER_RX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+ wait_event_interruptible((card_privates[minor].
+ stream_receive_q)
+ ,
+ (pointer !=
+ *USER_RX_S_FAKE_POINTER
+ (minor, arg,
+ stream_pointers)));
+ retval = 0x1;
+ }
+ return retval;
+ case FEPCI_IOCTL_STREAM_BOTH_POLL:
+ /* here: arg == channel number */
+ if (arg < 0 || arg >= CHANNELS
+ || !(card_privates[minor].ch_privates[arg]->stream_on))
+ return 0x2;
+ {
+ u32 temp_tx_pointer =
+ *USER_TX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+ u32 temp_rx_pointer =
+ *USER_RX_S_FAKE_POINTER(minor, arg,
+ stream_pointers);
+
+ wait_event_interruptible((card_privates[minor].
+ stream_both_q)
+ ,
+ (temp_tx_pointer !=
+ *USER_TX_S_FAKE_POINTER
+ (minor, arg, stream_pointers))
+ || (temp_rx_pointer !=
+ *USER_RX_S_FAKE_POINTER
+ (minor, arg,
+ stream_pointers)));
+ retval = 0x1;
+ }
+ return retval;
+ case FEPCI_IOCTL_R_SHARED_MEM:
+ DBG_PRINT(" %s: ioctl read shared mem commanded.\n",
+ fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_SHARED_MEM_OFFSETT,
+ _IOC_SIZE(cmd), 0);
+ break;
+ case FEPCI_IOCTL_W_SHARED_MEM:
+ DBG_PRINT(" %s: ioctl write shared mem commanded.\n",
+ fepci_NAME);
+ fepci_copy_from_user(ioaddr + FEPCI_SHARED_MEM_OFFSETT,
+ arg, _IOC_SIZE(cmd), 0);
+ break;
+ case FEPCI_IOCTL_G_IDENTIFICATION:
+ DBG_PRINT(" %s: IOCTL_G_IDENTIFICATION commanded.\n",
+ fepci_NAME);
+ fepci_copy_to_user(arg,
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_G_FEATURES:
+ DBG_PRINT(" %s: IOCTL_G_FEATURES commanded.\n", fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_FEATURES_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_G_SETTINGS:
+ DBG_PRINT(" %s: IOCTL_G_SETTINGS commanded.\n", fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_SETTINGS_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_G_STATUS:
+ DBG_PRINT(" %s: IOCTL_G_STATUS commanded.\n", fepci_NAME);
+ fepci_copy_to_user(arg, ioaddr + FEPCI_STATUS_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+ break;
+ case FEPCI_IOCTL_B_POLL:
+ DBG_PRINT(" %s: IOCTL_B_POLL commanded.\n", fepci_NAME);
+ retval = get_semafore(real_mailbox);
+ if ((retval == 0x20 || retval == 0x21 || retval == 0x40)
+ && *saved_pid != my_pid)
+ retval = 0x7;
+ del_timer_sync(&card_privates[minor].mailbox_timer);
+ card_privates[minor].mailbox_timer.expires = jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.function =
+ &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+ break;
+ case FEPCI_IOCTL_B_GRAB:
+ DBG_PRINT("%s: IOCTL_B_GRAB commanded.\n", fepci_NAME);
+ if ((my_pid != *saved_pid) && (*saved_pid != 0)) {
+ retval = 0x2;
+ break;
+ }
+ DBG_PRINT("%s: IOCTL_B_GRAB getting semaphore.\n", fepci_NAME);
+ if (get_semafore(real_mailbox) == 0x0) {
+ DBG_PRINT("%s: IOCTL_B_GRAB setting semaphore.\n",
+ fepci_NAME);
+ set_semafore(real_mailbox, 0x40);
+ DBG_PRINT("%s: IOCTL_B_GRAB sleeping.\n", fepci_NAME);
+ msleep(1); /* delay at least 1 millisecond */
+ DBG_PRINT
+ ("%s: IOCTL_B_GRAB getting semaphore again.\n",
+ fepci_NAME);
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ retval = 0x0;
+ DBG_PRINT
+ ("%s: IOCTL_B_GRAB saving pid to %p.\n",
+ fepci_NAME, saved_pid);
+ *saved_pid = my_pid;
+ DBG_PRINT
+ ("%s: IOCTL_B_GRAB deleting timer at %p.\n",
+ fepci_NAME,
+ &card_privates[minor].mailbox_timer);
+ del_timer_sync(&card_privates[minor].
+ mailbox_timer);
+ card_privates[minor].mailbox_timer.
+ expires = jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.
+ function = &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+ break;
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ default:
+ retval = 0xff;
+ }
+ } else {
+ switch (get_semafore(real_mailbox)) {
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ default:
+ retval = 0xff;
+ }
+ }
+ break;
+ case FEPCI_IOCTL_B_RELEASE:
+ DBG_PRINT(" %s: IOCTL_B_RELEASE commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ break;
+ }
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ case 0x20:
+ retval = 0x0;
+ set_semafore(real_mailbox, 0x0);
+ *saved_pid = 0;
+ del_timer(&card_privates[minor].mailbox_timer);
+ break;
+ case 0x21:
+ retval = 0x04;
+ break;
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ default:
+ retval = 0xff;
+ }
+ break;
+ case FEPCI_IOCTL_B_S_CMAIL:
+ DBG_PRINT(" %s: IOCTL_B_S_CMAIL commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ break;
+ }
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ case 0x20:
+ case 0x21:
+ /* copy the mailbox */
+ fepci_copy_from_user(ioaddr +
+ FEPCI_MAILBOX_OFFSETT + 4,
+ arg + 2, _IOC_SIZE(cmd) - 2, 1);
+ /* semafore -> 10 */
+ set_semafore(real_mailbox, 0x10);
+ retval = 0x0;
+
+ del_timer_sync(&card_privates[minor].mailbox_timer);
+ card_privates[minor].mailbox_timer.expires =
+ jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.function =
+ &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+
+ break;
+
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ case 0x0:
+ retval = 0x3;
+ break;
+ default:
+ retval = 0xff;
+ }
+ break;
+ case FEPCI_IOCTL_B_S_QMAIL:
+ DBG_PRINT(" %s: IOCTL_B_S_QMAIL commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ break;
+ }
+ switch (get_semafore(real_mailbox)) {
+ case 0x40:
+ case 0x20:
+ case 0x21:
+ /* copy the mailbox; */
+ fepci_copy_from_user(ioaddr +
+ FEPCI_MAILBOX_OFFSETT + 4,
+ arg + 2, _IOC_SIZE(cmd) - 2, 1);
+ /* semafore -> 11 */
+ set_semafore(real_mailbox, 0x11);
+ retval = 0x0;
+
+ del_timer_sync(&card_privates[minor].mailbox_timer);
+ card_privates[minor].mailbox_timer.expires =
+ jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.function =
+ &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+
+ break;
+
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ case 0x0:
+ retval = 0x3;
+ break;
+ default:
+ retval = 0xff;
+ }
+ break;
+ case FEPCI_IOCTL_B_G_MAIL:
+ DBG_PRINT(" %s: IOCTL_B_G_MAIL commanded.\n", fepci_NAME);
+ if (my_pid != *saved_pid) {
+ retval = 0x2;
+ } else {
+ switch (get_semafore(real_mailbox)) {
+ case 0x10:
+ case 0x11:
+ case 0x80:
+ retval = 0x1;
+ break;
+ case 0x40:
+ case 0x20:
+ case 0x21:
+ retval = 0x0;
+ fepci_copy_to_user(arg,
+ ioaddr +
+ FEPCI_MAILBOX_OFFSETT,
+ _IOC_SIZE(cmd), 1);
+
+ del_timer_sync(&card_privates[minor].
+ mailbox_timer);
+ card_privates[minor].mailbox_timer.
+ expires = jiffies + 20 * HZ;
+ card_privates[minor].mailbox_timer.data = minor;
+ card_privates[minor].mailbox_timer.
+ function = &fepci_mailbox_timer;
+ add_timer(&card_privates[minor].mailbox_timer);
+
+ break;
+ case 0x0:
+ retval = 0x3;
+ break;
+ default:
+ retval = 0xff;
+ }
+ }
+ if (retval != 0) {
+ static unsigned char seven = 7;
+ /* copy four lowest bytes from the mailbox */
+ fepci_copy_to_user(arg,
+ ioaddr + FEPCI_MAILBOX_OFFSETT,
+ 4, 1);
+ /* lowest byte = 0x7 */
+ __put_user(arg, &seven);
+ }
+ break;
+ case FEPCI_IOCTL_ALARM_MANAGER:
+ DBG_PRINT(" %s: IOCTL_ALARM_MANAGER commanded.\n", fepci_NAME);
+ interruptible_sleep_on(&
+ (card_privates[minor].
+ alarm_manager_wait_q));
+ return retval;
+ default:
+ DBG_PRINT(" %s: Unknown ioctl command 0x%x.\n", fepci_NAME,
+ cmd);
+ return -ENOTTY;
+ }
+ return retval;
+}
+
+ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+ loff_t *f_pos)
+{
+ DBG_PRINT("fepci_char_read\n");
+ if (count > 1)
+ count = 1;
+ if (copy_to_user(buf, "\n", count))
+ return -EFAULT;
+ return count;
+}
+
+static int fepci_register_char_device(void)
+{
+ int error =
+ register_chrdev(0 /* dynamic */ , fepci_name, &fepci_char_fops);
+ if (error < 0)
+ printk(KERN_WARNING
+ "%s: unable to register char device.\n", fepci_NAME);
+ else
+ DBG_PRINT("%s: registered char device, major:0x%x.\n",
+ fepci_NAME, error);
+ return error;
+}
+
+void fepci_unregister_char_device(void)
+{
+ unregister_chrdev(major, fepci_name);
+}
+
+/* char operations end */
+
+/* stream operations start */
+
+static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance);
+static int fepci_stream_close(struct net_device *dev);
+
+static int fepci_stream_open(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned tx_pages, rx_pages, tx_order, rx_order;
+ unsigned page_number;
+ unsigned int i;
+
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_eth_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Interface is in Ethernet mode, "
+ "cannot open stream interface.\n", fepci_NAME);
+ BUSY:
+ return -EBUSY;
+ }
+ if (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Interface is already in stream mode, "
+ "cannot open stream interface.\n", fepci_NAME);
+ goto BUSY;
+ }
+
+ if (fp->this_card_priv->pci_dev == NULL) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -ENXIO;
+ }
+
+ fp->bufsize = 1 << fp->bufsize_order;
+
+ if (fp->fake_unit_sz_order < 5) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Unit size has to be at least 32 Bytes.\n",
+ fepci_NAME);
+ INVALID:
+ return (-EINVAL);
+ }
+
+ if (fp->fake_unit_sz_order >= fp->bufsize_order) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Bufsize has to be greater than unit size.\n",
+ fepci_NAME);
+ goto INVALID;
+ }
+
+ if (fp->fake_unit_sz_order >= MAX_UNIT_SZ_ORDER) {
+ fp->unit_sz_order = MAX_UNIT_SZ_ORDER;
+ } else {
+ fp->unit_sz_order = fp->fake_unit_sz_order;
+ }
+
+ fp->fake_unit_sz = 1 << fp->fake_unit_sz_order;
+ fp->unit_sz = 1 << fp->unit_sz_order;
+ fp->units = 1 << (fp->bufsize_order - fp->unit_sz_order);
+ fp->fake_units = 1 << (fp->bufsize_order - fp->fake_unit_sz_order);
+
+ /* reserve memory */
+ if (fp->bufsize_order < PAGE_SHIFT) {
+ tx_order = rx_order = 0;
+ tx_pages = rx_pages = 1;
+ } else {
+ tx_order = fp->bufsize_order - PAGE_SHIFT;
+ tx_pages = 1 << tx_order;
+ rx_order = tx_order + 1;
+ rx_pages = 1 << rx_order;
+ }
+ fp->in_stream_mode = 1;
+ fp->tx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, tx_order);
+ if (!fp->tx_buffer)
+ goto NO_MEMORY;
+ fp->rx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, rx_order);
+ if (!fp->rx_buffer) {
+ NO_MEMORY:
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate memory for buffers.\n",
+ fepci_NAME);
+ fepci_stream_close(dev);
+ return (-ENOMEM);
+ }
+
+ for (page_number = 0; page_number < rx_pages; page_number++)
+ /* make pages reserved to allow remappping pages
+ with io_remap_pfn_range */
+ SetPageReserved(virt_to_page
+ ((unsigned long)fp->rx_buffer +
+ (page_number << PAGE_SHIFT)));
+ for (page_number = 0; page_number < tx_pages; page_number++)
+ /* make pages reserved to allow remappping pages
+ with io_remap_pfn_range */
+ SetPageReserved(virt_to_page
+ ((unsigned long)fp->tx_buffer +
+ (page_number << PAGE_SHIFT)));
+
+ for (i = 0; i < (fp->bufsize) / 4; i++)
+ fp->tx_buffer[i] = 0xffffffff;
+
+ /* + fp->channel_number; */
+ *USER_RX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
+ stream_pointers) = 0;
+ /* + fp->channel_number; */
+ *USER_TX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
+ stream_pointers) = 0;
+ /* + fp->channel_number; */
+ *USER_RX_S_FAKE_POINTER(fp->this_card_priv->card_number,
+ fp->channel_number, stream_pointers) = 0;
+ /* + fp->channel_number; */
+ *USER_TX_S_FAKE_POINTER(fp->this_card_priv->card_number,
+ fp->channel_number, stream_pointers) = 0;
+
+ DBG_PRINT("%s: Bufsize is 0x%x.\n", fepci_NAME, fp->bufsize);
+ DBG_PRINT("%s: Unit_size is 0x%x.\n", fepci_NAME, fp->unit_sz);
+ DBG_PRINT("%s: Number of units is 0x%x.\n", fepci_NAME, fp->units);
+
+ DBG_PRINT("%s: Fake_unit_size is 0x%x.\n", fepci_NAME,
+ fp->fake_unit_sz);
+ DBG_PRINT("%s: Number of fake units is 0x%x.\n", fepci_NAME,
+ fp->fake_units);
+
+ /* init ring buffers */
+ for (i = 0; i < MAX_RX_UNITS; i++)
+ fp->rx_unit[i] =
+ (u32 *) ((u32) (fp->rx_buffer) + (fp->unit_sz * i));
+ for (i = 0; i < MAX_TX_UNITS; i++)
+ fp->tx_unit[i] =
+ (u32 *) ((u32) (fp->tx_buffer) + (fp->unit_sz * i));
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ outl(0, &fp->rx_desc[i].desc_a);
+ outl(0, &fp->rx_desc[i].desc_b);
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ outl(0, &fp->tx_desc[i].desc_a);
+ outl(0, &fp->tx_desc[i].desc_b);
+ }
+
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static int fepci_stream_start(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned i;
+ down_write(&fp->this_card_priv->semaphore);
+
+ if (fp->in_stream_mode == 0) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Interface is not in stream mode, "
+ "streaming cannot be started.\n", fepci_NAME);
+ return (-EBUSY);
+ }
+ if (fp->stream_on) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: Streaming is already on, "
+ "streaming cannot be started.\n", fepci_NAME);
+ return (-EBUSY);
+ }
+
+ {
+ /* reserve irq */
+ int error = request_irq(dev->irq, &fepci_stream_interrupt,
+ IRQF_SHARED, dev->name, dev);
+ if (error) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate IRQ %d, error 0x%x\n",
+ fepci_NAME, dev->irq, error);
+ return -ENOMEM;
+ }
+ }
+
+ fp->stream_on = 1;
+
+ /* sending &receiving on, start from the beginning of the buffer */
+ fp->cur_tx_unit = 0;
+ fp->cur_rx_unit = 0;
+ fp->cur_tx = 0;
+ fp->cur_rx = 0;
+
+ /* all the descriptors ready to go: */
+ for (i = 0; i < min(RX_RING_SIZE, TX_RING_SIZE); i++) {
+ u32 bus_address = pci_map_single(fp->this_card_priv->pci_dev,
+ fp->
+ rx_unit[(fp->cur_rx_unit +
+ i) % fp->units],
+ fp->unit_sz,
+ PCI_DMA_FROMDEVICE);
+ outl(bus_address,
+ &fp->
+ rx_desc[(fp->cur_rx + i) & (RX_RING_SIZE - 1)].desc_a);
+ if (unlikely(pci_dma_mapping_error(bus_address)))
+ printk(KERN_WARNING
+ "%s: failed to map DMA buffer.\n", fepci_NAME);
+ else {
+ if (!
+ (inl
+ (&fp->
+ rx_desc[(fp->cur_rx + i) & (RX_RING_SIZE -
+ 1)].
+ desc_b) & enable_transfer))
+ outl(enable_transfer,
+ &fp->rx_desc[(fp->cur_rx + i) %
+ RX_RING_SIZE].desc_b);
+ }
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ fp->tx_unit[(fp->cur_tx_unit + i) %
+ fp->units], fp->unit_sz,
+ PCI_DMA_TODEVICE);
+ outl(bus_address,
+ &fp->
+ tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE - 1)].desc_a);
+ if (unlikely(pci_dma_mapping_error(bus_address)))
+ printk(KERN_WARNING
+ "%s: failed to map DMA buffer.\n", fepci_NAME);
+ else {
+ if (!
+ (inl
+ (&fp->
+ tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE -
+ 1)].
+ desc_b) & enable_transfer))
+ outl(enable_transfer |
+ (fp->unit_sz & frame_length),
+ &fp->
+ tx_desc[(fp->cur_tx +
+ i) & (TX_RING_SIZE - 1)].desc_b);
+ }
+ }
+
+ /* irq on */
+ set_int_mask(fp->channel_number,
+ MaskFrameReceived | MaskFrameTransmitted |
+ MaskRxFifoError | MaskRxFrameDroppedError |
+ MaskTxFifoError, fp->this_card_priv);
+ {
+ void *ioaddr = (void *)dev->base_addr;
+ /* Start Rx and Tx channels */
+ outl(Receive_enable |
+ (Rx_fifo_threshold & RX_FIFO_THRESHOLD_STREAM_MODE),
+ (void *)(ioaddr + fp->reg_rxctrl));
+ outl((Transmit_enable |
+ (Tx_desc_threshold &
+ (TX_DESC_THRESHOLD_STREAM_MODE << 8)) |
+ (Tx_fifo_threshold & TX_FIFO_THRESHOLD_STREAM_MODE)),
+ (void *)(ioaddr + fp->reg_txctrl));
+ }
+ up_write(&fp->this_card_priv->semaphore);
+
+ return 0;
+}
+
+static int fepci_stream_stop(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ void *ioaddr = (void *)dev->base_addr;
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_stream_mode == 0) {
+ up_write(&fp->this_card_priv->semaphore);
+ return (1);
+ }
+ fp->stream_on = 0;
+ /* Stop Rx and Tx channels. */
+ outl(0x0, (void *)(ioaddr + fp->reg_rxctrl));
+ outl(0x0, (void *)(ioaddr + fp->reg_txctrl));
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
+
+ /* unregister irq */
+ free_irq(dev->irq, dev);
+
+ {
+ unsigned i = min(RX_RING_SIZE, TX_RING_SIZE) - 1;
+ do {
+ u32 bus_address;
+ if (likely
+ (!pci_dma_mapping_error
+ (bus_address = inl(&fp->rx_desc[i].desc_a))))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz,
+ PCI_DMA_FROMDEVICE);
+ if (likely
+ (!pci_dma_mapping_error
+ (bus_address = inl(&fp->tx_desc[i].desc_a))))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz, PCI_DMA_TODEVICE);
+ }
+ while (i--);
+ }
+
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static int fepci_stream_close(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned rx_pages, tx_pages, rx_order, tx_order;
+
+ if (fepci_stream_stop(dev))
+ return -ENODEV;
+ down_write(&fp->this_card_priv->semaphore);
+ if (!(fp->in_stream_mode)) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -ENODEV;
+ }
+ /* release memory */
+ if (fp->bufsize_order < PAGE_SHIFT) {
+ tx_order = rx_order = 0;
+ tx_pages = rx_pages = 1;
+ } else {
+ rx_order = (int)((fp->bufsize_order) - PAGE_SHIFT + 1);
+ rx_pages = 1 << rx_order;
+ tx_order = (int)((fp->bufsize_order) - PAGE_SHIFT);
+ tx_pages = 1 << tx_order;
+ }
+ if (fp->rx_buffer) {
+ unsigned page_number;
+ for (page_number = 0; page_number < rx_pages; page_number++)
+ /* turn pages back to non-reserved */
+ ClearPageReserved(virt_to_page
+ ((unsigned long)fp->rx_buffer +
+ (page_number << PAGE_SHIFT)));
+ free_pages((u32) fp->rx_buffer, rx_order);
+ fp->rx_buffer = NULL;
+ }
+ if (fp->tx_buffer) {
+ unsigned page_number;
+ for (page_number = 0; page_number < tx_pages; page_number++)
+ /* turn pages back to non-reserved */
+ ClearPageReserved(virt_to_page
+ ((unsigned long)fp->tx_buffer +
+ (page_number << PAGE_SHIFT)));
+ free_pages((u32) fp->tx_buffer, tx_order);
+ fp->tx_buffer = NULL;
+ }
+
+ fp->in_stream_mode = 0;
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ struct fepci_ch_private *fp = dev->priv;
+ void *ioaddr = (void *)dev->base_addr;
+ u32 intr_status = get_int_status(fp->channel_number, ioaddr);
+ bool fifo, dropped;
+ unsigned int temp_rx;
+ unsigned int temp_rx_unit;
+ unsigned int temp_tx;
+ unsigned int temp_tx_unit;
+
+ clear_int(fp->channel_number, intr_status, ioaddr);
+
+ /* debugging */
+ fp->interrupts_stream++;
+ fifo = (intr_status & IntrRxFifoError) != 0;
+ dropped = (intr_status & IntrRxFrameDroppedError) != 0;
+ fp->int_err_table_stream[fifo][dropped]++;
+ if (intr_status & IntrFrameReceived)
+ fp->rx_interrupts_stream++;
+ if (intr_status & IntrFrameTransmitted)
+ fp->tx_interrupts_stream++;
+ if (fifo)
+ fp->rx_int_fifo_err_stream++;
+ if (dropped)
+ fp->rx_int_frame_dropped_err_stream++;
+ if (intr_status & IntrTxFifoError)
+ fp->tx_int_fifo_err_stream++;
+ /* first update cur_rx, and do stuff if it has moved
+ (+ packets have been received) */
+ {
+ temp_rx = fp->cur_rx;
+ /* has been received */
+ while ((inl(&fp->rx_desc[fp->cur_rx].desc_b) &
+ transfer_not_done) == 0
+ /* stop if made one round */
+ && temp_rx != ((fp->cur_rx + 1) & (RX_RING_SIZE - 1))) {
+ u32 bus_address;
+ if (likely(!pci_dma_mapping_error(bus_address =
+ inl(&fp->
+ rx_desc[fp->
+ cur_rx].
+ desc_a))))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz,
+ PCI_DMA_FROMDEVICE);
+ fp->cur_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
+ fp->cur_rx_unit = (fp->cur_rx_unit + 1);
+ fp->cur_rx_unit *= fp->cur_rx_unit < fp->units;
+
+ *USER_RX_S_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) = fp->cur_rx_unit;
+ *USER_RX_S_FAKE_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) =
+ fp->cur_rx_unit * fp->unit_sz / fp->fake_unit_sz;
+ wake_up_interruptible(&
+ (fp->this_card_priv->
+ stream_receive_q));
+ wake_up_interruptible(&
+ (fp->this_card_priv->
+ stream_both_q));
+ }
+ }
+ /* from the first uninitialized descriptor to cur_rx */
+ temp_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
+ temp_rx_unit = (fp->cur_rx_unit + 1);
+ temp_rx_unit *= temp_rx_unit < fp->units;
+
+ while (temp_rx != fp->cur_rx) {
+ u32 desc_b = inl(&fp->rx_desc[temp_rx].desc_b);
+ if ((desc_b & transfer_not_done) == 0) {
+ bool fifo = (desc_b & fifo_error) != 0;
+ bool size = (desc_b & size_error) != 0;
+ bool crc = (desc_b & crc_error) != 0;
+ bool octet = (desc_b & octet_error) != 0;
+ bool line = (desc_b & line_error) != 0;
+ u32 length = desc_b & frame_length;
+ u32 bus_address;
+ /* update debug counters */
+ fp->rx_desc_err_table_stream[fifo]
+ [size]
+ [crc]
+ [octet]
+ [line]++;
+ if (length == 0) {
+ fp->rx_packets_of_size_0_stream++;
+ } else if (length == 1) {
+ fp->rx_packets_of_size_1_stream++;
+ } else if (length == 2) {
+ fp->rx_packets_of_size_2_stream++;
+ } else if (length == 3) {
+ fp->rx_packets_of_size_3_stream++;
+ } else if (length < 8) {
+ fp->rx_packets_of_size_4_7_stream++;
+ } else if (length < 16) {
+ fp->rx_packets_of_size_8_15_stream++;
+ } else if (length < 32) {
+ fp->rx_packets_of_size_16_31_stream++;
+ }
+ if (fifo) {
+ fp->rx_desc_fifo_err_stream++;
+ } else if (size) {
+ fp->rx_desc_size_err_stream++;
+ } else if (crc) {
+ fp->rx_desc_crc_err_stream++;
+ } else if (octet) {
+ fp->rx_desc_octet_err_stream++;
+ } else if (line) {
+ fp->rx_desc_line_err_stream++;
+ }
+ /* initialize the descriptor for transfer */
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ fp->rx_unit[temp_rx_unit],
+ fp->unit_sz, PCI_DMA_FROMDEVICE);
+ outl(bus_address, &fp->rx_desc[temp_rx].desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ outl(enable_transfer,
+ &fp->rx_desc[temp_rx].desc_b);
+ else
+ printk(KERN_WARNING
+ "%s: failed to map DMA for reception.\n",
+ fepci_NAME);
+ }
+ temp_rx = (temp_rx + 1) & (RX_RING_SIZE - 1);
+ temp_rx_unit = (temp_rx_unit + 1);
+ temp_rx_unit *= temp_rx_unit < fp->units;
+ }
+
+ /* first update cur_tx, and do stuff if it has moved
+ (+ packets have been transmitted) */
+ {
+ temp_tx = fp->cur_tx;
+ /* has been transmitted */
+ while ((inl(&fp->tx_desc[fp->cur_tx].desc_b) &
+ transfer_not_done) == 0
+ /* stop if made one round */
+ && temp_tx != ((fp->cur_tx + 1) & (TX_RING_SIZE - 1))) {
+ u32 bus_address;
+ if (likely(!pci_dma_mapping_error(bus_address =
+ inl(&fp->
+ tx_desc[fp->
+ cur_tx].
+ desc_a))))
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev, bus_address,
+ fp->unit_sz, PCI_DMA_TODEVICE);
+ fp->cur_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
+ fp->cur_tx_unit = (fp->cur_tx_unit + 1);
+ fp->cur_tx_unit *= fp->cur_tx_unit < fp->units;
+
+ *USER_TX_S_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) = fp->cur_tx_unit;
+ *USER_TX_S_FAKE_POINTER(fp->this_card_priv->
+ card_number,
+ fp->channel_number,
+ stream_pointers) =
+ fp->cur_tx_unit * fp->unit_sz / fp->fake_unit_sz;
+ wake_up_interruptible(&
+ (fp->this_card_priv->
+ stream_transmit_q));
+ wake_up_interruptible(&
+ (fp->this_card_priv->
+ stream_both_q));
+ }
+ }
+ /* from the first uninitialized descriptor to cur_tx */
+ temp_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
+ temp_tx_unit = (fp->cur_tx_unit + 1);
+ temp_tx_unit *= temp_tx_unit < fp->units;
+
+ while (temp_tx != fp->cur_tx) {
+ u32 desc_b = inl(&fp->tx_desc[temp_tx].desc_b);
+ if ((desc_b & transfer_not_done) == 0) {
+ u32 bus_address;
+ /* update debug counters */
+ if (desc_b & fifo_error)
+ fp->tx_desc_fifo_err_stream++;
+ /* initialize the desctiptor for transfer */
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ fp->tx_unit[temp_tx_unit],
+ fp->unit_sz, PCI_DMA_TODEVICE);
+ outl(bus_address, &fp->tx_desc[temp_tx].desc_a);
+ if (likely(!pci_dma_mapping_error(bus_address)))
+ outl(enable_transfer |
+ (fp->unit_sz & frame_length),
+ &fp->tx_desc[temp_tx].desc_b);
+ else
+ printk(KERN_WARNING
+ "%s: failed to map tranmission DMA.\n",
+ fepci_NAME);
+ }
+ temp_tx = (temp_tx + 1) & (TX_RING_SIZE - 1);
+ temp_tx_unit = (temp_tx_unit + 1);
+ temp_tx_unit *= temp_tx_unit < fp->units;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* stream operations end */
+
+int fepci_rebuild_header(struct sk_buff *skb)
+{
+ DBG_PRINT("fepci_rebuild_header\n");
+ return 0;
+}
+
+static inline u16 get_common_reg_word(void *ioaddr, unsigned long offsett)
+{
+ u16 word;
+ DBG_PRINT("get_common_reg_word ioaddr %p, offsett %lu\n", ioaddr,
+ offsett);
+ __clear_bit(0, &offsett);
+ DBG_PRINT("get_common_reg_word %p\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1));
+ word = le16_to_cpu(readw
+ (ioaddr + FEPCI_IDENTIFICATION_OFFSETT +
+ (offsett << 1)));
+ DBG_PRINT("get_common_reg_word %p: %hu\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1), word);
+ return word;
+}
+
+static irqreturn_t alarm_manager_interrupt(int irq, void *pointer)
+{
+ struct fepci_card_private *card_private = pointer;
+ void *ioaddr = card_private->ioaddr;
+ /* check int status */
+ if (inl((void *)(ioaddr + reg_custom)) & AM_interrupt_status) {
+ /* clear int (zero everything, but the mask bit) */
+ outl(inl((void *)(ioaddr + reg_custom)) &
+ AM_interrupt_mask, (void *)(ioaddr + reg_custom));
+ /* wake queue */
+ wake_up(&(card_private->alarm_manager_wait_q));
+ }
+
+ return IRQ_HANDLED;
+}
+
+#ifdef FEPCI_POINT_TO_POINT
+static int is_ptp_interface(struct net_device *dev)
+{
+ char **p_ptp_if_name = retina_ptp_interfaces;
+ unsigned int i = interfaces;
+ while (i > 0 && *p_ptp_if_name != NULL) {
+ if (!strncmp(dev->name, *p_ptp_if_name, sizeof(dev->name))) {
+ return 1;
+ } else {
+ }
+ p_ptp_if_name++;
+ i--;
+ }
+ return 0;
+}
+#endif /* FEPCI_POINT_TO_POINT */
+
+static int __devinit fepci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev = NULL;
+ struct fepci_ch_private *fp = NULL;
+ int chip_idx = ent->driver_data;
+ int drv_flags = pci_id_tbl[chip_idx].drv_flags;
+ int i = pci_enable_device(pdev);
+ u32 j;
+ resource_size_t real_ioaddr;
+ void *ioaddr;
+ unsigned position;
+
+ if (i) {
+ printk(KERN_WARNING "%s: pci_enable_device returned %x.\n",
+ fepci_NAME, i);
+ return i;
+ }
+
+ pci_set_master(pdev);
+
+ i = pci_request_regions(pdev, (char *)fepci_name);
+ if (i) {
+ printk(KERN_WARNING
+ "%s: pci_request_regions returned %x.\n", fepci_NAME, i);
+ pci_disable_device(pdev);
+ return i;
+ }
+
+ /* make sure above region is MMIO */
+ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+ printk(KERN_WARNING "%s: region not MMIO region\n", fepci_NAME);
+ goto err_1;
+ }
+
+ j = pci_resource_len(pdev, 0);
+ if (j < FEPCI_SIZE) {
+ printk(KERN_WARNING
+ "%s: resource length %u less than required %u.\n",
+ fepci_NAME, j, FEPCI_SIZE);
+ goto err_1;
+ }
+
+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+ printk(KERN_WARNING "%s: no suitable DMA available.\n",
+ fepci_NAME);
+ goto err_1;
+ }
+
+ real_ioaddr = pci_resource_start(pdev, 0);
+ DBG_PRINT("pci_resource_start %lu.\n", real_ioaddr);
+ ioaddr = ioremap_nocache(real_ioaddr, FEPCI_SIZE);
+ DBG_PRINT("ioremap_nocache = %p.\n", ioaddr);
+ if (!ioaddr) {
+ printk(KERN_WARNING "%s: remapping failed.\n", fepci_NAME);
+ goto err_1_5;
+ }
+ position = 0;
+ for (; position < MAX_DEVICES; position++) {
+ down_write(&card_privates[position].semaphore);
+ if (card_privates[position].pci_dev == NULL) {
+ card_privates[position].pci_dev = pdev;
+ if (position == find_cnt)
+ find_cnt++;
+ goto FOUND;
+ }
+ up_write(&card_privates[position].semaphore);
+ }
+ printk(KERN_WARNING
+ "%s: no space to inialize device #%u.\n",
+ fepci_NAME, MAX_DEVICES + 1);
+ goto err_2;
+ FOUND:
+ card_privates[position].card_number = position;
+ card_privates[position].ioaddr = ioaddr;
+ card_privates[position].pci_dev = pdev;
+ DBG_PRINT("fillregisterswith_00 %p.\n", ioaddr);
+ fillregisterswith_00(ioaddr);
+
+ fepci_proc_init_card(position, (void *)&(card_privates[position]));
+
+ init_waitqueue_head(&(card_privates[position].alarm_manager_wait_q));
+ init_waitqueue_head(&(card_privates[position].stream_transmit_q));
+ init_waitqueue_head(&(card_privates[position].stream_receive_q));
+ init_waitqueue_head(&(card_privates[position].stream_both_q));
+
+ init_timer(&card_privates[position].mailbox_timer);
+
+ DBG_PRINT("request_irq %d, %s.\n", pdev->irq, fepci_alarm_manager_name);
+ i = request_irq(pdev->irq, &alarm_manager_interrupt,
+ IRQF_SHARED,
+ fepci_alarm_manager_name, &card_privates[position]);
+ if (i) {
+ up_write(&card_privates[position].semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate IRQ %d alarm manager: 0x%x\n",
+ fepci_NAME, pdev->irq, i);
+ goto err_2;
+ }
+ DBG_PRINT("alarm manager int on %p.\n", (void *)(ioaddr + reg_custom));
+ outl(AM_interrupt_mask, (void *)(ioaddr + reg_custom));
+ /* alarm manager int on */
+
+ for (j = 0; j < CHANNELS; j++) {
+ DBG_PRINT("alloc_etherdev %u.\n", j);
+ dev = alloc_etherdev(sizeof(struct fepci_ch_private));
+ if (!dev) {
+ printk(KERN_WARNING
+ "%s: cannot allocate ethernet device\n",
+ fepci_NAME);
+ continue;
+ }
+
+ fp = dev->priv;
+ fp->minor = position; /* * CHANNELS + j; */
+ /* name := xxx01..xxxnn */
+ memcpy(dev->name, fepci_netdev_name, 6);
+ /* dev->name[3]= j+0x30; channel number -> ascii */
+ /* minor number -> ascii */
+ dev->name[4] = ((fp->minor * CHANNELS + j) % 10) + 0x30;
+ /* minor number -> ascii */
+ dev->name[3] = ((fp->minor * CHANNELS + j) / 10) + 0x30;
+
+ SET_MODULE_OWNER(dev);
+ DBG_PRINT("clear_int %u, %x, %p.\n", j, IntrAllInts, ioaddr);
+ clear_int(j, IntrAllInts, ioaddr);
+ DBG_PRINT("ether_setup %p.\n", dev);
+ ether_setup(dev);
+
+ /* HW_ADDR: 00:rnd:rnd:rnd:rnd:05 */
+ dev->dev_addr[0] = 0;
+ get_random_bytes(&(dev->dev_addr[1]), 4);
+ dev->dev_addr[5] = 5;
+ /* HW_ADDR is got using the mailbox: */
+ {
+ struct fepci_real_mailbox *real_mailbox =
+ (struct fepci_real_mailbox *)
+ (ioaddr + FEPCI_MAILBOX_OFFSETT);
+ unsigned long waituntil;
+
+ set_semafore(real_mailbox, 0x40);
+ outl(0x1 /*size */ +
+ (0x8 << 8) /* get mac command */ ,
+ &real_mailbox->Size_Command);
+ set_semafore(real_mailbox, 0x11);
+
+ waituntil = jiffies + HZ;
+ while (time_before(jiffies, waituntil) &&
+ get_semafore(real_mailbox) != 0x20) {
+ DBG_PRINT("jiffies %lu < waituntil %lu.\n",
+ jiffies, waituntil);
+ msleep(0);
+ }
+
+ /* 14.5.2004 JT: Made this safer. */
+ if (get_semafore(real_mailbox) == 0x20) {
+ dev->dev_addr[5] =
+ readb(&real_mailbox->Data[0 + 3 * j]);
+ dev->dev_addr[4] =
+ readb(((u8 *) & real_mailbox->
+ Data[0 + 3 * j]) + 1);
+ dev->dev_addr[3] =
+ readb(&real_mailbox->Data[1 + 3 * j]);
+ dev->dev_addr[2] =
+ readb(((u8 *) & real_mailbox->
+ Data[1 + 3 * j]) + 1);
+ dev->dev_addr[1] =
+ readb(&real_mailbox->Data[2 + 3 * j]);
+ dev->dev_addr[0] =
+ readb(((u8 *) & real_mailbox->
+ Data[2 + 3 * j]) + 1);
+ }
+
+ set_semafore(real_mailbox, 0x0);
+ }
+ dev->addr_len = 6;
+
+ dev->base_addr = (unsigned long)ioaddr;
+ dev->irq = pdev->irq;
+ DBG_PRINT("alarm pci_set_drvdata %p, %p.\n", pdev, dev);
+ if (j == 0)
+ pci_set_drvdata(pdev, dev);
+
+ fp->drv_flags = drv_flags;
+
+ fp->rx_desc =
+ (struct fepci_desc *)(dev->base_addr + first_rx_desc +
+ j * to_next_ch_rx_desc);
+ fp->tx_desc =
+ (struct fepci_desc *)(dev->base_addr + first_tx_desc +
+ j * to_next_ch_tx_desc);
+
+ fp->channel_number = j; /*channel in this device */
+ fp->this_dev = dev;
+
+ fp->this_card_priv = &card_privates[position];
+ fp->this_card_priv->ch_privates[j] = fp;
+
+ fp->cur_tx = 0;
+
+ fp->in_stream_mode = 0;
+ fp->in_eth_mode = 0;
+
+ fp->reg_rxctrl = reg_first_rxctrl + j * to_next_rxctrl;
+ fp->reg_txctrl = reg_first_txctrl + j * to_next_txctrl;
+
+ /* The FEPCI specific entries in the device structure */
+ dev->open = &fepci_open;
+ dev->hard_start_xmit = &fepci_start_xmit;
+ dev->stop = &fepci_close;
+ dev->get_stats = &fepci_get_stats;
+ dev->set_multicast_list = &set_rx_mode;
+ dev->do_ioctl = &netdev_ioctl;
+ dev->tx_timeout = fepci_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+#ifdef FEPCI_POINT_TO_POINT
+ if (is_ptp_interface(dev)) {
+ dev->flags |= (IFF_POINTOPOINT);
+ dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
+ if (retina_noarp_with_ptp) {
+ dev->rebuild_header = fepci_rebuild_header;
+ dev->flags |= (IFF_NOARP);
+ }
+ }
+#endif
+ DBG_PRINT("register_netdev %p.\n", dev);
+ i = register_netdev(dev);
+ if (i) {
+ printk(KERN_WARNING
+ "%s: register_netdev failed 0x%x.\n",
+ fepci_NAME, i);
+ continue;
+ }
+
+ printk("%s: %s type %x at %p, ",
+ dev->name, pci_id_tbl[chip_idx].name,
+ 0x0 /* inl(ioaddr + ChipRev) */ , ioaddr);
+ for (i = 0; i < 5; i++)
+ printk("%2.2x:", dev->dev_addr[i]);
+ printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], pdev->irq);
+#ifdef DEBUG_PROC_FILES
+ fepci_proc_init_channel(position, j, fp);
+#endif /* DEBUG_PROC_FILES */
+ }
+ up_write(&card_privates[position].semaphore);
+ DBG_PRINT("fepci_init_one %u.\n", position);
+ return 0;
+
+ err_2:
+ iounmap(ioaddr);
+ err_1_5:
+ pci_set_drvdata(pdev, NULL);
+ err_1:
+ pci_disable_device(pdev);
+ pci_release_regions(pdev);
+ return -ENODEV;
+}
+
+static int fepci_open(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+
+ down_write(&fp->this_card_priv->semaphore);
+
+ if (fp->this_card_priv->pci_dev == NULL) {
+ up_write(&fp->this_card_priv->semaphore);
+ fepci_close(dev);
+ return -ENXIO;
+ }
+
+ while (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ fepci_stream_close(dev);
+ down_write(&fp->this_card_priv->semaphore);
+ }
+
+ {
+ int i = request_irq(dev->irq, &fepci_interrupt,
+ IRQF_SHARED, dev->name, dev);
+ if (i) {
+ up_write(&fp->this_card_priv->semaphore);
+ printk(KERN_WARNING
+ "%s: unable to allocate IRQ %d, error 0x%x",
+ fepci_NAME, dev->irq, i);
+ return i;
+ }
+ }
+
+ fp->in_eth_mode = 1;
+
+ fepci_init_ring(dev);
+ set_rx_mode(dev);
+
+ fp->cur_rx = 0;
+ fp->cur_tx = 0;
+
+ netif_carrier_off(dev);
+
+ /* Enable interrupts by setting the interrupt mask. */
+
+ set_int_mask(fp->channel_number,
+ MaskFrameReceived | MaskFrameTransmitted |
+ MaskRxFifoError | MaskRxFrameDroppedError |
+ MaskTxFifoError, fp->this_card_priv);
+
+ {
+ void *ioaddr = (void *)dev->base_addr;
+
+ /* Start Rx and Tx channels. */
+ outl(Receive_enable |
+ (Rx_fifo_threshold & RX_FIFO_THRESHOLD_PACKET_MODE),
+ (void *)(ioaddr + fp->reg_rxctrl));
+
+ outl((Transmit_enable |
+ (Tx_desc_threshold &
+ (TX_DESC_THRESHOLD_PACKET_MODE << 8)) |
+ (Tx_fifo_threshold & TX_FIFO_THRESHOLD_PACKET_MODE)),
+ (void *)(ioaddr + fp->reg_txctrl));
+ }
+
+ netif_start_queue(dev);
+
+ /* Set timer */
+ init_timer(&fp->timer);
+ fp->timer.expires = jiffies + HZ;
+ fp->timer.data = (unsigned long)dev;
+ fp->timer.function = &fepci_timer; /* timer handler */
+ add_timer(&fp->timer);
+
+ up_write(&fp->this_card_priv->semaphore);
+
+ return 0;
+}
+
+static void fepci_timer(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct fepci_ch_private *fp = dev->priv;
+
+ /* just to make it absolutely sure the sending starts again
+ * if the system jams: free already sent skbuffs */
+
+ if (netif_tx_trylock(dev)) {
+ if (!(fp->tx_interrupts_since_last_timer)) {
+ unsigned i = TX_RING_SIZE - 1;
+ do {
+ u32 desc_b;
+ if ((fp->tx_skbuff[i] != NULL)
+ &&
+ (((desc_b =
+ inl(&fp->tx_desc[i].
+ desc_b)) & transfer_not_done) ==
+ 0)) {
+ /* has been sent */
+ pci_unmap_single(fp->
+ this_card_priv->
+ pci_dev,
+ inl(&fp->
+ tx_desc[i].
+ desc_a),
+ desc_b &
+ frame_length,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb(fp->tx_skbuff[i]);
+ fp->tx_skbuffs_out++;
+
+ fp->tx_skbuff[i] = NULL;
+
+ if (desc_b & fifo_error) {
+ fp->stats.tx_fifo_errors++;
+ fp->tx_desc_fifo_err++;
+ } else
+ fp->stats.tx_packets++;
+ }
+ }
+ while (i--);
+ }
+ /* if the next descriptor is free, continue taking new ones */
+ if (!(inl(&fp->tx_desc[fp->cur_tx].desc_b) & transfer_not_done))
+ netif_wake_queue(dev);
+ netif_tx_unlock(dev);
+ }
+
+ fp->tx_interrupts_since_last_timer = 0;
+
+ if ((get_common_reg_word(fp->this_card_priv->ioaddr, 0x72) >> fp->
+ channel_number) & 1) {
+ netif_carrier_off(dev);
+ } else {
+ netif_carrier_on(dev);
+ }
+
+ if (fp->in_eth_mode)
+ mod_timer(&fp->timer, jiffies + 5 * HZ);
+}
+
+static void fepci_tx_timeout(struct net_device *dev)
+{
+ DBG_PRINT("%s: transmit timed out!\n", dev->name);
+}
+
+/* Initialize the rx and tx rings */
+static void fepci_init_ring(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned i;
+
+ fp->rx_buf_sz = 2000;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb =
+ __dev_alloc_skb(fp->rx_buf_sz, GFP_KERNEL);
+
+ if (skb == NULL) {
+ ZERO:
+ outl(0, &fp->rx_desc[i].desc_a);
+ outl(0, &fp->rx_desc[i].desc_b);
+ continue;
+ } else {
+ u32 bus_address;
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ skb->data, fp->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+ if (likely(!pci_dma_mapping_error(bus_address))) {
+ fp->rx_skbuffs_in++;
+ /* Mark as being used by this device */
+ skb->dev = dev;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ fp->rx_skbuff[i] = skb;
+ outl(bus_address, &fp->rx_desc[i].desc_a);
+ outl(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else {
+ dev_kfree_skb(skb);
+ goto ZERO;
+ }
+ }
+ }
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ fp->tx_skbuff[i] = NULL;
+ outl(0, &fp->tx_desc[i].desc_a); /* no skbuff */
+ /* no transfer enable, no int enable */
+ outl(0, &fp->tx_desc[i].desc_b);
+ }
+
+ return;
+}
+
+static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ const unsigned cur_tx = fp->cur_tx;
+
+ fp->tx_skbuffs_in++;
+
+ {
+ u32 tx_length = skb->len;
+ u32 bus_address;
+ struct sk_buff *old;
+ if (unlikely(tx_length < ETH_ZLEN)) {
+ struct sk_buff *bigger =
+ skb_copy_expand(skb, 0, ETH_ZLEN - tx_length,
+ GFP_ATOMIC);
+ if (unlikely(!bigger))
+ return NET_XMIT_CN;
+ tx_length = ETH_ZLEN;
+ old = skb;
+ skb = bigger;
+ } else
+ old = NULL;
+ bus_address =
+ pci_map_single(fp->this_card_priv->pci_dev, skb->data,
+ tx_length, PCI_DMA_TODEVICE);
+ if (likely(!pci_dma_mapping_error(bus_address))) {
+ struct fepci_desc *descriptor;
+ if (old)
+ dev_kfree_skb(old);
+ descriptor = &fp->tx_desc[cur_tx];
+ outl(bus_address, &descriptor->desc_a);
+ outl((tx_length & frame_length) | enable_transfer,
+ &descriptor->desc_b);
+ } else {
+ if (old)
+ dev_kfree_skb(skb);
+ return NET_XMIT_CN;
+ }
+ fp->stats.tx_bytes += tx_length;
+ }
+
+ fp->tx_skbuff[cur_tx] = skb;
+
+ {
+ /* Calculate the next Tx descriptor entry */
+ unsigned next = (cur_tx + 1) & (TX_RING_SIZE - 1);
+ fp->cur_tx = next;
+ /* if the next descriptor is busy,
+ * discontinue taking new ones */
+ if (fp->tx_skbuff[next] != NULL)
+ netif_stop_queue(dev);
+ }
+ dev->trans_start = jiffies;
+
+ return NET_XMIT_SUCCESS;
+}
+
+static irqreturn_t fepci_interrupt(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ void *ioaddr = (void *)dev->base_addr;
+ struct fepci_ch_private *fp = dev->priv;
+ u32 intr_status = get_int_status(fp->channel_number, ioaddr);
+ bool RxFifoError;
+ bool RxFrameDroppedError;
+
+ clear_int(fp->channel_number, intr_status, ioaddr);
+
+ RxFifoError = (intr_status & IntrRxFifoError) != 0;
+ RxFrameDroppedError = (intr_status & IntrRxFrameDroppedError) != 0;
+ /* first update interrupt error table */
+ fp->int_err_table[RxFifoError][RxFrameDroppedError]++;
+
+ fp->interrupts++;
+
+ if (intr_status & IntrFrameReceived) {
+ fepci_rx(dev);
+ fp->rx_interrupts++;
+ }
+ if (intr_status & IntrFrameTransmitted) {
+ fp->tx_interrupts_since_last_timer++;
+ if (netif_tx_trylock(dev)) {
+ unsigned i = TX_RING_SIZE - 1;
+ do {
+ u32 desc_b;
+ if ((fp->tx_skbuff[i] != NULL)
+ &&
+ (((desc_b =
+ inl(&fp->tx_desc[i].
+ desc_b)) & transfer_not_done) ==
+ 0)) {
+ /* has been sent */
+ pci_unmap_single(fp->
+ this_card_priv->
+ pci_dev,
+ inl(&fp->
+ tx_desc[i].
+ desc_a),
+ desc_b &
+ frame_length,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb_irq(fp->tx_skbuff[i]);
+ fp->tx_skbuffs_out++;
+ fp->tx_skbuff[i] = NULL;
+ if (desc_b & fifo_error) {
+ fp->stats.tx_fifo_errors++;
+ fp->tx_desc_fifo_err++;
+ } else {
+ fp->stats.tx_packets++;
+ }
+ }
+ }
+ while (i--);
+ {
+ unsigned next = fp->cur_tx;
+ /* if next tx descriptor is free,
+ * continue taking new ones */
+ if (!(inl(&fp->tx_desc[next].desc_b) &
+ transfer_not_done))
+ netif_wake_queue(dev);
+ }
+ netif_tx_unlock(dev);
+ }
+ fp->tx_interrupts++;
+ }
+ if (RxFifoError) {
+ fp->rx_int_fifo_err++;
+ fepci_rx(dev);
+ }
+ if (RxFrameDroppedError) {
+ fp->rx_int_frame_dropped_err++;
+ fepci_rx(dev);
+ }
+ if (intr_status & IntrTxFifoError)
+ fp->tx_int_fifo_err++;
+ return intr_status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int fepci_rx(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+
+ unsigned int i, old_cur_rx = fp->cur_rx;
+ for (i = old_cur_rx;
+ i != ((old_cur_rx + RX_RING_SIZE - 1) & (RX_RING_SIZE - 1));
+ i = (i + 1) & (RX_RING_SIZE - 1)) {
+ u32 desc_b;
+ struct sk_buff *skb;
+ /* transfer done */
+ bool condition = (skb = fp->rx_skbuff[i]) &&
+ ((desc_b =
+ inl(&fp->rx_desc[i].desc_b)) & transfer_not_done) == 0;
+ if (condition) {
+ bool fifo = (desc_b & fifo_error) != 0;
+ bool size = (desc_b & size_error) != 0;
+ bool crc = (desc_b & crc_error) != 0;
+ bool octet = (desc_b & octet_error) != 0;
+ bool line = (desc_b & line_error) != 0;
+ u32 length = desc_b & frame_length;
+ pci_unmap_single(fp->this_card_priv->pci_dev,
+ inl(&fp->rx_desc[i].desc_a),
+ fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ fp->cur_rx = (i + 1) & (RX_RING_SIZE - 1);
+ /* first update error table */
+ fp->rx_desc_err_table[fifo]
+ [size]
+ [crc]
+ [octet]
+ [line]++;
+ /* small packet counters */
+ if (length == 0)
+ fp->rx_packets_of_size_0++;
+ else if (length == 1)
+ fp->rx_packets_of_size_1++;
+ else if (length == 2)
+ fp->rx_packets_of_size_2++;
+ else if (length == 3)
+ fp->rx_packets_of_size_3++;
+ else if (length < 8)
+ fp->rx_packets_of_size_4_7++;
+ else if (length < 16)
+ fp->rx_packets_of_size_8_15++;
+ else if (length < 32)
+ fp->rx_packets_of_size_16_31++;
+
+ if (fifo) {
+ fp->stats.rx_errors++;
+ fp->stats.rx_frame_errors++;
+ fp->rx_desc_fifo_err++;
+ outl(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (size) {
+ fp->stats.rx_errors++;
+ fp->stats.rx_over_errors++;
+ fp->rx_desc_size_err++;
+ outl(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (crc) {
+ fp->stats.rx_errors++;
+ fp->stats.rx_crc_errors++;
+ fp->rx_desc_crc_err++;
+ outl(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (octet) {
+ fp->rx_desc_octet_err++;
+ outl(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else if (line) {
+ fp->rx_desc_line_err++;
+ outl(enable_transfer, &fp->rx_desc[i].desc_b);
+ } else {
+ skb_put(skb, length - 4);
+
+ skb->protocol = eth_type_trans(skb, dev);
+#ifdef FEPCI_POINT_TO_POINT
+ if (dev->flags & IFF_POINTOPOINT) {
+ /* everything received is for us. */
+
+ if (dev->flags & IFF_NOARP) {
+ /* NOARP applied ->
+ * destination MAC addresses
+ * are bogus */
+ if (skb->
+ pkt_type ==
+ PACKET_OTHERHOST)
+ skb->
+ pkt_type =
+ PACKET_HOST;
+ } else {
+ /* NOARP not applied ->
+ * destination MAC addresses are
+ * broadcast */
+ if (skb->
+ pkt_type ==
+ PACKET_BROADCAST)
+ skb->
+ pkt_type =
+ PACKET_HOST;
+
+ } /* IFF_NOARP */
+ } /* IFF_POINTOPOINT */
+#endif
+ skb_reset_mac_header(skb);
+ netif_rx(skb);
+ fp->rx_skbuffs_out++;
+ /* statistics -4==crc */
+ fp->stats.rx_bytes += length - 4;
+ fp->stats.rx_packets++;
+
+ fp->rx_skbuff[i] = NULL;
+ dev->last_rx = jiffies;
+ }
+ }
+ /* reserve a new one */
+ if (fp->rx_skbuff[i] == NULL) {
+ struct sk_buff *skb = dev_alloc_skb(fp->rx_buf_sz);
+
+ if (skb == NULL)
+ continue; /* Better luck next round. */
+ else {
+ u32 address =
+ pci_map_single(fp->this_card_priv->pci_dev,
+ skb->data,
+ fp->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+ if (likely(!pci_dma_mapping_error(address))) {
+ struct fepci_desc *descriptor;
+ fp->rx_skbuffs_in++;
+ fp->rx_skbuff[i] = skb;
+ /* Mark as being used by this device. */
+ skb->dev = dev;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ descriptor = &fp->rx_desc[i];
+ outl(address, &descriptor->desc_a);
+ outl(enable_transfer,
+ &descriptor->desc_b);
+ } else {
+ dev_kfree_skb(skb);
+ printk(KERN_WARNING
+ "%s: failed to map DMA.\n",
+ dev->name);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int fepci_close(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ unsigned i;
+ void *ioaddr = (void *)dev->base_addr;
+
+ down_write(&fp->this_card_priv->semaphore);
+
+ netif_stop_queue(dev);
+
+ /* Disable interrupts by clearing the interrupt mask */
+ set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
+
+ /* Stop the Tx and Rx processes */
+ outl(0x0, ioaddr + fp->reg_rxctrl);
+ outl(0x0, ioaddr + fp->reg_txctrl);
+ fp->in_eth_mode = 0;
+
+ del_timer_sync(&fp->timer);
+
+ free_irq(dev->irq, dev);
+
+ /* Free all the rx skbuffs */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (fp->rx_skbuff[i] != NULL) {
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev,
+ inl(&fp->rx_desc[i].
+ desc_a),
+ fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(fp->rx_skbuff[i]);
+ fp->rx_skbuffs_out++;
+ fp->rx_skbuff[i] = NULL;
+ }
+ }
+ /* and tx */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (fp->tx_skbuff[i] != NULL) {
+ pci_unmap_single(fp->this_card_priv->
+ pci_dev,
+ inl(&fp->tx_desc[i].
+ desc_a),
+ inl(&fp->tx_desc[i].
+ desc_b) & frame_length,
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb(fp->tx_skbuff[i]);
+ fp->tx_skbuffs_out++;
+ fp->tx_skbuff[i] = NULL;
+ }
+ }
+ up_write(&fp->this_card_priv->semaphore);
+ return 0;
+}
+
+static struct net_device_stats *fepci_get_stats(struct net_device *dev)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ return &fp->stats;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+ DBG_PRINT("set_rx_mode\n");
+}
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct fepci_ch_private *fp = dev->priv;
+ char data = ((u32) rq->ifr_data) & 0xff;
+ int ret = 0;
+
+ DBG_PRINT("%s: netdev_ioctl called (command_nmbr:0x%x).\n",
+ dev->name, cmd);
+
+ switch (cmd) {
+ case FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE:
+ DBG_PRINT
+ (" ioctl stream bufsize commanded. (bufsize:0x%x)\n", data);
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -EBUSY;
+ }
+ fp->bufsize_order = data;
+ up_write(&fp->this_card_priv->semaphore);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_UNITSIZE:
+ DBG_PRINT
+ (" ioctl stream unitsize commanded. (unitsize:0x%x)\n",
+ data);
+ down_write(&fp->this_card_priv->semaphore);
+ if (fp->in_stream_mode) {
+ up_write(&fp->this_card_priv->semaphore);
+ return -EBUSY;
+ }
+ fp->fake_unit_sz_order = data;
+ up_write(&fp->this_card_priv->semaphore);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_OPEN:
+ DBG_PRINT(" ioctl stream open commanded.\n");
+ ret = fepci_stream_open(dev);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_START:
+ DBG_PRINT(" ioctl stream start commanded.\n");
+ ret = fepci_stream_start(dev);
+ break;
+ case FEPCI_NETDEV_IOCTL_STREAM_CLOSE:
+ DBG_PRINT(" ioctl stream close commanded.\n");
+ ret = fepci_stream_close(dev);
+ break;
+ default:
+ DBG_PRINT(" unknown ioctl command 0x%x.\n", cmd);
+ return -ENOTTY;
+ }
+ return ret;
+}
+
+static void fepci_remove_one(struct pci_dev *pdev)
+{
+ /* from first dev, same in all */
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct fepci_ch_private *fp = dev->priv;
+ struct fepci_card_private *cardp = fp->this_card_priv;
+ unsigned int i;
+
+ outl(0, (void *)(cardp->ioaddr + reg_custom));
+ /* alarm manager int off */
+
+ for (i = 0; i < CHANNELS; i++) {
+ dev = cardp->ch_privates[i]->this_dev;
+ fp = dev->priv;
+#ifdef DEBUG_PROC_FILES
+ fepci_proc_cleanup_channel(cardp->card_number,
+ fp->channel_number);
+#endif /* DEBUG_PROC_FILES */
+ unregister_netdev(dev);
+ fepci_stream_close(dev);
+ free_netdev(dev);
+ }
+
+ pdev = cardp->pci_dev;
+ free_irq(pdev->irq, (void *)cardp);
+
+ down_write(&cardp->semaphore);
+
+ fepci_proc_cleanup_card(cardp->card_number);
+
+ pci_set_drvdata(pdev, NULL);
+
+ if (cardp->card_number + 1 == find_cnt)
+ find_cnt--;
+ cardp->pci_dev = NULL;
+
+ iounmap(cardp->ioaddr);
+
+ up_write(&cardp->semaphore);
+
+ pci_disable_device(pdev);
+ pci_release_regions(pdev);
+}
+
+static struct pci_driver fepci_driver = {
+ name:DRV_NAME,
+ id_table:fepci_pci_tbl,
+ probe:fepci_init_one,
+ remove:fepci_remove_one,
+};
+
+static int __init fepci_init(void)
+{
+ unsigned card = MAX_DEVICES - 1;
+ do
+ init_rwsem(&card_privates[card].semaphore);
+ while (card--);
+ major = fepci_register_char_device();
+ if (major < 0)
+ return major;
+ stream_pointers = (u32) get_zeroed_page(GFP_KERNEL | __GFP_DMA);
+ DBG_PRINT(" %x.\n", stream_pointers);
+ if (stream_pointers == 0) {
+ fepci_unregister_char_device();
+ return -ENOMEM;
+ }
+ DBG_PRINT("SetPageReserved %u.\n", stream_pointers);
+ SetPageReserved(virt_to_page(stream_pointers));
+ DBG_PRINT("fepci_proc_init_driver.\n");
+ fepci_proc_init_driver();
+ DBG_PRINT("pci_register_driver %p.\n", &fepci_driver);
+ {
+ int ret = pci_register_driver(&fepci_driver);
+ if (ret) {
+ fepci_unregister_char_device();
+ ClearPageReserved(virt_to_page(stream_pointers));
+ free_page(stream_pointers);
+ DBG_PRINT
+ ("pci_register_driver %p failed with %d.\n",
+ &fepci_driver, ret);
+ fepci_proc_cleanup_driver();
+ return ret;
+ }
+ DBG_PRINT("fepci_init %d.\n", ret);
+ return ret;
+ }
+}
+
+static void __exit fepci_cleanup(void)
+{
+ DBG_PRINT("fepci_cleanup\n");
+ pci_unregister_driver(&fepci_driver);
+ fepci_proc_cleanup_driver();
+ fepci_unregister_char_device();
+ ClearPageReserved(virt_to_page(stream_pointers));
+ free_page(stream_pointers);
+}
+
+module_init(fepci_init);
+module_exit(fepci_cleanup);
+
+/* proc-filesystem specific stuff: */
+
+#ifdef DEBUG_PROC_FILES
+
+static char string_true[] = "<true> ";
+static char string_false[] = " - ";
+
+/* convert boolean value to more clarifying string format */
+static char *b(int b)
+{
+ if (b)
+ return string_true;
+ return string_false;
+}
+
+static int fepci_proc_read_descriptors(char *buf, char **start,
+ off_t offset, int len, int *eof,
+ void *data)
+{
+ int pos = 0;
+
+ int d; /* descriptor */
+
+ struct fepci_ch_private *fp;
+ struct fepci_card_private *cardp;
+ fp = (struct fepci_ch_private *)data;
+ cardp = (struct fepci_card_private *)fp->this_card_priv;
+
+ pos +=
+ sprintf(buf + pos,
+ "Rx descriptors of channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, "\n");
+
+ for (d = 0; d < RX_RING_SIZE; d++) {
+ pos += sprintf(buf + pos, " rx descriptor number %u : \n", d);
+ pos +=
+ sprintf(buf + pos, " desc_a value : 0x%8x \n",
+ inl(&fp->rx_desc[d].desc_a));
+ pos +=
+ sprintf(buf + pos, " desc_b value : 0x%8x \n",
+ inl(&fp->rx_desc[d].desc_b));
+ }
+
+ pos += sprintf(buf + pos, "\n");
+ pos +=
+ sprintf(buf + pos,
+ "Tx descriptors of channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, "\n");
+
+ for (d = 0; d < TX_RING_SIZE; d++) {
+ pos += sprintf(buf + pos, " tx descriptor number %u : \n", d);
+ pos +=
+ sprintf(buf + pos, " desc_a value : 0x%8x \n",
+ inl(&fp->tx_desc[d].desc_a));
+ pos +=
+ sprintf(buf + pos, " desc_b value : 0x%8x \n",
+ inl(&fp->tx_desc[d].desc_b));
+ }
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, "last line\n");
+
+ *eof = 1;
+ return pos;
+}
+
+static int fepci_proc_read_stream_counters(char *buf, char **start,
+ off_t offset, int len, int *eof,
+ void *data)
+{
+ int pos = 0;
+
+ int fifo;
+ int size;
+ int crc;
+ int octet;
+ int line;
+ int frame_dropped;
+
+ struct fepci_ch_private *fp;
+ struct fepci_card_private *cardp;
+
+ fp = (struct fepci_ch_private *)data;
+ cardp = (struct fepci_card_private *)fp->this_card_priv;
+
+ pos +=
+ sprintf(buf + pos,
+ "Stream status for channel %u of card %u : \n",
+ fp->channel_number, cardp->card_number);
+ if (fp->in_stream_mode) {
+ pos += sprintf(buf + pos, " Channel is in stream mode \n");
+ if (fp->stream_on) {
+ pos += sprintf(buf + pos, " Stream is on (started) \n");
+ } else {
+ pos +=
+ sprintf(buf + pos,
+ " Stream is off (not started)\n");
+ }
+ } else {
+ pos += sprintf(buf + pos, " Channel is NOT in stream mode \n");
+ }
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos,
+ "Error counters for channel %u of card %u "
+ "(in stream mode) : \n",
+ fp->channel_number, cardp->card_number);
+
+ pos +=
+ sprintf(buf + pos, " rx_desc_fifo_err_stream : %10u \n",
+ fp->rx_desc_fifo_err_stream);
+
+ pos +=
+ sprintf(buf + pos, " rx_desc_size_err_stream : %10u \n",
+ fp->rx_desc_size_err_stream);
+ pos +=
+ sprintf(buf + pos, " rx_desc_crc_err_stream : %11u \n",
+ fp->rx_desc_crc_err_stream);
+ pos +=
+ sprintf(buf + pos, " rx_desc_octet_err_stream : %9u \n",
+ fp->rx_desc_octet_err_stream);
+ pos +=
+ sprintf(buf + pos, " rx_desc_line_err_stream : %10u \n",
+ fp->rx_desc_line_err_stream);
+ pos +=
+ sprintf(buf + pos, " tx_desc_fifo_err_stream : %10u \n",
+ fp->tx_desc_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos, " rx_int_fifo_err_stream : %11u \n",
+ fp->rx_int_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ " rx_int_frame_dropped_err_stream : %2u \n",
+ fp->rx_int_frame_dropped_err_stream);
+ pos +=
+ sprintf(buf + pos, " tx_int_fifo_err_stream : %11u \n",
+ fp->tx_int_fifo_err_stream);
+
+ pos += sprintf(buf + pos, "\n");
+ pos +=
+ sprintf(buf + pos,
+ "Other counters for channel %u of card %u "
+ "(in stream mode) : \n",
+ fp->channel_number, cardp->card_number);
+ pos +=
+ sprintf(buf + pos, " interrupts : %10u \n",
+ fp->interrupts_stream);
+ pos +=
+ sprintf(buf + pos, " rx_interrupts : %10u \n",
+ fp->rx_interrupts_stream);
+ pos +=
+ sprintf(buf + pos, " tx_interrupts : %10u \n",
+ fp->tx_interrupts_stream);
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos,
+ "Error counter tables for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, " rx_desc_error_table_stream:\n");
+ pos +=
+ sprintf(buf + pos,
+ " [fifo] [size] [crc] [octet] [line] : [count]\n");
+
+ for (fifo = 0; fifo < 2; fifo++)
+ for (size = 0; size < 2; size++)
+ for (crc = 0; crc < 2; crc++)
+ for (octet = 0; octet < 2; octet++)
+ for (line = 0; line < 2; line++)
+ pos +=
+ sprintf(buf + pos,
+ " %s %s %s %s %s "
+ ": %7u \n",
+ b(fifo),
+ b(size),
+ b(crc),
+ b(octet),
+ b(line),
+ fp->
+ rx_desc_err_table_stream
+ [fifo][size]
+ [crc][octet]
+ [line]);
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, " int_error_table:\n");
+ pos += sprintf(buf + pos, " [rx_fifo] [frame_dropped] : [count]\n");
+ for (fifo = 0; fifo < 2; fifo++)
+ for (frame_dropped = 0; frame_dropped < 2; frame_dropped++)
+ pos +=
+ sprintf(buf + pos,
+ " %s %s : %7u\n", b(fifo),
+ b(frame_dropped),
+ fp->int_err_table_stream[fifo]
+ [frame_dropped]);
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos,
+ "Small packet counters for channel %u of card %u "
+ "(stream mode): \n",
+ fp->channel_number, cardp->card_number);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 0 : %10u \n",
+ fp->rx_packets_of_size_0_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 1 : %10u \n",
+ fp->rx_packets_of_size_1_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 2 : %10u \n",
+ fp->rx_packets_of_size_2_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 3 : %10u \n",
+ fp->rx_packets_of_size_3_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 4..7 : %10u \n",
+ fp->rx_packets_of_size_4_7_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 8..15 : %10u \n",
+ fp->rx_packets_of_size_8_15_stream);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 16..31 : %10u \n",
+ fp->rx_packets_of_size_16_31_stream);
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, "last line\n");
+
+ *eof = 1;
+ return pos;
+}
+
+static int fepci_proc_read_counters(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+
+ int fifo;
+ int size;
+ int crc;
+ int octet;
+ int line;
+ int frame_dropped;
+
+ struct fepci_ch_private *fp;
+ struct fepci_card_private *cardp;
+
+ fp = (struct fepci_ch_private *)data;
+ cardp = (struct fepci_card_private *)fp->this_card_priv;
+
+ pos +=
+ sprintf(buf + pos,
+ "Error counters for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+
+ pos +=
+ sprintf(buf + pos, " rx_desc_fifo_err : %10u \n",
+ fp->rx_desc_fifo_err);
+ pos +=
+ sprintf(buf + pos, " rx_desc_size_err : %10u \n",
+ fp->rx_desc_size_err);
+ pos +=
+ sprintf(buf + pos, " rx_desc_crc_err : %11u \n",
+ fp->rx_desc_crc_err);
+ pos +=
+ sprintf(buf + pos, " rx_desc_octet_err : %9u \n",
+ fp->rx_desc_octet_err);
+ pos +=
+ sprintf(buf + pos, " rx_desc_line_err : %10u \n",
+ fp->rx_desc_line_err);
+ pos +=
+ sprintf(buf + pos, " tx_desc_fifo_err : %10u \n",
+ fp->tx_desc_fifo_err);
+ pos +=
+ sprintf(buf + pos, " rx_int_fifo_err : %11u \n",
+ fp->rx_int_fifo_err);
+ pos +=
+ sprintf(buf + pos, " rx_int_frame_dropped_err : %2u \n",
+ fp->rx_int_frame_dropped_err);
+ pos +=
+ sprintf(buf + pos, " tx_int_fifo_err : %11u \n",
+ fp->tx_int_fifo_err);
+
+ pos += sprintf(buf + pos, "\n");
+ pos +=
+ sprintf(buf + pos,
+ "Other counters for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, " interrupts : %10u \n", fp->interrupts);
+ pos +=
+ sprintf(buf + pos, " rx_interrupts : %10u \n", fp->rx_interrupts);
+ pos +=
+ sprintf(buf + pos, " tx_interrupts : %10u \n", fp->tx_interrupts);
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos, " rx_skbuffs_in : %10u \n", fp->rx_skbuffs_in);
+ pos +=
+ sprintf(buf + pos, " rx_skbuffs_out : %9u \n", fp->rx_skbuffs_out);
+ pos +=
+ sprintf(buf + pos, " difference : %13i \n",
+ fp->rx_skbuffs_in - fp->rx_skbuffs_out);
+ pos +=
+ sprintf(buf + pos, " tx_skbuffs_in : %10u \n", fp->tx_skbuffs_in);
+ pos +=
+ sprintf(buf + pos, " tx_skbuffs_out : %9u \n", fp->tx_skbuffs_out);
+ pos +=
+ sprintf(buf + pos, " difference : %13i \n",
+ fp->tx_skbuffs_in - fp->tx_skbuffs_out);
+
+ pos += sprintf(buf + pos, "\n");
+ pos +=
+ sprintf(buf + pos,
+ "Error counter tables for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos += sprintf(buf + pos, " rx_desc_error_table:\n");
+ pos +=
+ sprintf(buf + pos,
+ " [fifo] [size] [crc] [octet] [line] : [count]\n");
+
+ for (fifo = 0; fifo < 2; fifo++)
+ for (size = 0; size < 2; size++)
+ for (crc = 0; crc < 2; crc++)
+ for (octet = 0; octet < 2; octet++)
+ for (line = 0; line < 2; line++)
+ pos +=
+ sprintf(buf + pos,
+ " %s %s %s %s %s "
+ ": %7u \n",
+ b(fifo),
+ b(size),
+ b(crc),
+ b(octet),
+ b(line),
+ fp->
+ rx_desc_err_table
+ [fifo][size]
+ [crc][octet]
+ [line]);
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, " int_error_table:\n");
+ pos += sprintf(buf + pos, " [rx_fifo] [frame_dropped] : [count]\n");
+ for (fifo = 0; fifo < 2; fifo++)
+ for (frame_dropped = 0; frame_dropped < 2; frame_dropped++)
+ pos +=
+ sprintf(buf + pos,
+ " %s %s : %7u\n", b(fifo),
+ b(frame_dropped),
+ fp->int_err_table[fifo][frame_dropped]);
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos +=
+ sprintf(buf + pos,
+ "Small packet counters for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 0 : %10u \n",
+ fp->rx_packets_of_size_0);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 1 : %10u \n",
+ fp->rx_packets_of_size_1);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 2 : %10u \n",
+ fp->rx_packets_of_size_2);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 3 : %10u \n",
+ fp->rx_packets_of_size_3);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 4..7 : %10u \n",
+ fp->rx_packets_of_size_4_7);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 8..15 : %10u \n",
+ fp->rx_packets_of_size_8_15);
+ pos +=
+ sprintf(buf + pos, " rx_packets of size 16..31 : %10u \n",
+ fp->rx_packets_of_size_16_31);
+
+ pos += sprintf(buf + pos, "\n");
+ pos += sprintf(buf + pos, "last line\n");
+
+ *eof = 1;
+ return pos;
+}
+
+static int fepci_proc_read_registers(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+
+ struct fepci_ch_private *fp;
+ struct fepci_card_private *cardp;
+ void *ioaddr;
+
+ fp = (struct fepci_ch_private *)data;
+ cardp = (struct fepci_card_private *)fp->this_card_priv;
+ ioaddr = cardp->ioaddr;
+
+ pos +=
+ sprintf(buf + pos, "Registers for channel %u of card %u: \n",
+ fp->channel_number, cardp->card_number);
+
+ pos +=
+ sprintf(buf + pos, " interrupt status : 0x%8x \n",
+ get_int_status(fp->channel_number, ioaddr));
+ pos +=
+ sprintf(buf + pos, " interrupt mask : 0x%8x \n",
+ get_int_mask(fp->channel_number, ioaddr));
+ pos +=
+ sprintf(buf + pos, " rxctrl : 0x%8x \n",
+ inl(ioaddr + fp->reg_rxctrl));
+ pos +=
+ sprintf(buf + pos, " txctrl : 0x%8x \n",
+ inl(ioaddr + fp->reg_txctrl));
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "last line\n");
+
+ *eof = 1;
+ return pos;
+}
+
+#endif /* DEBUG_PROC_FILES */
+
+int get_line_data_rate_value(unsigned char line_rate)
+{
+ switch (line_rate) {
+ case 0x00:
+ return 0;
+ case 0x01:
+ return 1;
+ case 0x05:
+ return 8;
+ case 0x06:
+ return 10;
+ case 0x14:
+ return 256;
+ case 0x15:
+ return 300;
+ case 0x20:
+ return 1;
+ case 0x28:
+ return 8;
+ case 0x29:
+ return 10;
+ case 0x30:
+ return 32;
+ case 0x34:
+ return 56;
+ case 0x36:
+ return 64;
+ case 0x60:
+ return 1;
+ case 0xa0:
+ return 1;
+ default:
+ return -1;
+ }
+}
+
+char get_line_data_rate_unit(unsigned char line_rate)
+{
+ switch (line_rate) {
+ case 0x00:
+ return 0;
+ case 0x01:
+ return 0;
+ case 0x05:
+ return 0;
+ case 0x06:
+ return 0;
+ case 0x14:
+ return 0;
+ case 0x15:
+ return 0;
+ case 0x20:
+ return 'k';
+ case 0x28:
+ return 'k';
+ case 0x29:
+ return 'k';
+ case 0x30:
+ return 'k';
+ case 0x34:
+ return 'k';
+ case 0x36:
+ return 'k';
+ case 0x60:
+ return 'M';
+ case 0xa0:
+ return 'G';
+ default:
+ return 0;
+ }
+}
+
+int print_line_type(unsigned char type, char *buf, int pos)
+{
+ DBG_PRINT("print_line_type %c\n", type);
+ switch (type) {
+ case 0:
+ pos += sprintf(buf + pos, "NONE");
+ break;
+ case 1:
+ pos += sprintf(buf + pos, "DCombus management bus");
+ break;
+ case 2:
+ pos += sprintf(buf + pos, "V.24");
+ break;
+ case 3:
+ pos += sprintf(buf + pos, "X.21");
+ break;
+ case 4:
+ pos += sprintf(buf + pos, "V.35");
+ break;
+ case 5:
+ pos += sprintf(buf + pos, "V.11");
+ break;
+ case 6:
+ pos += sprintf(buf + pos, "IDSL (ISDN Basic Rate");
+ break;
+ case 7:
+ pos += sprintf(buf + pos, "E1 nonframed/framed");
+ break;
+ case 8:
+ pos += sprintf(buf + pos, "E2 nonframed/framed");
+ break;
+ case 9:
+ pos += sprintf(buf + pos, "E3 nonframed/framed");
+ break;
+ case 10:
+ pos += sprintf(buf + pos, "T1 nonframed/framed");
+ break;
+ case 11:
+ pos += sprintf(buf + pos, "T2 nonframed/framed");
+ break;
+ case 12:
+ pos += sprintf(buf + pos, "T3 nonframed/framed");
+ break;
+ case 13:
+ pos += sprintf(buf + pos, "HDSL");
+ break;
+ case 14:
+ pos += sprintf(buf + pos, "ADSL");
+ break;
+ case 15:
+ pos += sprintf(buf + pos, "SDSL");
+ break;
+ case 16:
+ pos += sprintf(buf + pos, "HDSL2");
+ break;
+ case 17:
+ pos += sprintf(buf + pos, "VDSL");
+ break;
+ case 18:
+ pos += sprintf(buf + pos, "G.shdsl");
+ break;
+ case 19:
+ pos += sprintf(buf + pos, "SDH");
+ break;
+ case 255:
+ pos +=
+ sprintf(buf + pos, "COMBINATION, get list using mailbox");
+ break;
+ default:
+ pos += sprintf(buf + pos, "reserved");
+ }
+ return pos;
+}
+
+int print_card_type(unsigned char type, char *buf, int pos)
+{
+ DBG_PRINT("print_card_type\n");
+ switch (type) {
+ case 0x00:
+ pos += sprintf(buf + pos, "illegal ");
+ break;
+ case 0x01:
+ case 0x02:
+ pos += sprintf(buf + pos, "Retina ");
+ break;
+ default:
+ pos += sprintf(buf + pos, "reserved ");
+ }
+
+ return pos;
+}
+
+int print_card_model(unsigned char model, char *buf, int pos)
+{
+ DBG_PRINT("print_card_model\n");
+ switch (model) {
+ case 0x00:
+ pos += sprintf(buf + pos, "illegal ");
+ break;
+ case 0x01:
+ pos += sprintf(buf + pos, "E2200 ");
+ break;
+ case 0x02:
+ pos += sprintf(buf + pos, "C5400 ");
+ break;
+ default:
+ pos += sprintf(buf + pos, "reserved ");
+ }
+
+ return pos;
+}
+
+static inline u8 get_common_reg(void *ioaddr, unsigned long offsett)
+{
+ unsigned long byte = __test_and_clear_bit(0, &offsett);
+ u8 reg;
+ DBG_PRINT("get_common_reg %p, old %p\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+ byte,
+ &((u32 *) (ioaddr +
+ FEPCI_IDENTIFICATION_OFFSETT))[offsett / 2]);
+ reg =
+ readb(ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+ byte);
+ DBG_PRINT("get_common_reg %p: %u\n",
+ ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+ byte, reg);
+ return reg;
+}
+
+static int fepci_proc_read_features(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+ int tab = 0;
+ struct fepci_card_private *cardp;
+ void *ioaddr;
+
+ cardp = (struct fepci_card_private *)data;
+
+ down_read(&cardp->semaphore);
+
+ if (unlikely(cardp->pci_dev == NULL))
+ goto EOF;
+
+ ioaddr = cardp->ioaddr;
+
+ switch (offset) {
+ case 0:
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "card type: ");
+ pos = print_card_type(get_common_reg(ioaddr, 0x00), buf, pos);
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "model: ");
+ pos = print_card_model(get_common_reg(ioaddr, 0x00), buf, pos);
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "serial number: ");
+ pos +=
+ sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x07));
+ pos +=
+ sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x06));
+ pos +=
+ sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x05));
+ pos +=
+ sprintf(buf + pos, "%2.2x\n", get_common_reg(ioaddr, 0x04));
+
+ pos += sprintf(buf + pos, "version: ");
+ pos +=
+ sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x08));
+ pos +=
+ sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x09));
+ pos +=
+ sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x0a));
+ pos +=
+ sprintf(buf + pos, "%2.2x\n", get_common_reg(ioaddr, 0x0b));
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "max. pipe count: ");
+ pos += sprintf(buf + pos, "%d\n", get_common_reg(ioaddr, 0x03));
+
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ case 1:
+ pos += sprintf(buf + pos, "line count: ");
+ pos += sprintf(buf + pos, "%d\n", get_common_reg(ioaddr, 0x02));
+
+ pos += sprintf(buf + pos, "line type: ");
+ pos = print_line_type(get_common_reg(ioaddr, 0x0c), buf, pos);
+
+ /* JT: Secondary interface is also printed also,
+ * if there is one. */
+ if (get_common_reg(ioaddr, 0x10) > 0) {
+ pos += sprintf(buf + pos, " / ");
+ pos =
+ print_line_type(get_common_reg(ioaddr, 0x10),
+ buf, pos);
+ }
+
+ pos += sprintf(buf + pos, "\n");
+
+ /* JT: Line modes are printed only if
+ * the card has NO secondary interface. */
+ if (get_common_reg(ioaddr, 0x10) == 0) {
+ pos += sprintf(buf + pos, "line modes: ");
+ tab = 0;
+ if (get_common_reg(ioaddr, 0x21) & 0x80) {
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "framed mode supported\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x40) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "unframed mode supported\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x20) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line code setting available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x10) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line attenuation available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x08) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line quality available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x04) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "timing setting available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x02) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos += sprintf(buf + pos, "reserved\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) & 0x01) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos +=
+ sprintf(buf + pos,
+ "line shutdown setting "
+ "available\n");
+ }
+ if (get_common_reg(ioaddr, 0x21) == 0) {
+ if (tab)
+ pos +=
+ sprintf(buf + pos,
+ " ");
+ else
+ tab = 1;
+ pos += sprintf(buf + pos, "n/a\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ case 2:
+ pos += sprintf(buf + pos, "line rate: ");
+ if (get_common_reg(ioaddr, 0x28) ==
+ get_common_reg(ioaddr, 0x2a)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos += sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))
+ * get_common_reg_word(ioaddr,
+ 0x28));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos, "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list using mailbox");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos += sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))
+ * get_common_reg_word(ioaddr,
+ 0x28));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos, "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))
+ * get_common_reg_word(ioaddr,
+ 0x2a));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos, "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list using mailbox");
+ }
+
+ /* JT: Line rate of a secondary interface is printed,
+ * if there is one. */
+ if (get_common_reg(ioaddr, 0x10) > 0) {
+ pos += sprintf(buf + pos, " / ");
+
+ if (get_common_reg(ioaddr, 0x34) ==
+ get_common_reg(ioaddr, 0x36)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos +=
+ sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr, 0x34));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or "
+ "list using mailbox");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos +=
+ sprintf(buf + pos, "Invalid value");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr, 0x34));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr, 0x36));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list "
+ "using mailbox");
+ }
+ }
+
+ pos += sprintf(buf + pos, "\n");
+
+ /* JT: Primary line special info is printed only
+ * if the card has NO secondary interface. */
+ if (get_common_reg(ioaddr, 0x10) == 0) {
+ pos += sprintf(buf + pos, "rate sel. methods: ");
+ if (get_common_reg(ioaddr, 0x28) ==
+ get_common_reg(ioaddr, 0x2a))
+ pos += sprintf(buf + pos, "n/a\n");
+ else if ((get_common_reg(ioaddr, 0x22) & 4)
+ && (get_common_reg(ioaddr, 0x23) & 4))
+ pos += sprintf(buf + pos, "adjust\n");
+ else if (((!get_common_reg(ioaddr, 0x22)) & 4)
+ && (get_common_reg(ioaddr, 0x23) & 4))
+ pos += sprintf(buf + pos, "adjust(framed)\n");
+ else if ((get_common_reg(ioaddr, 0x22) & 4)
+ && ((!get_common_reg(ioaddr, 0x23)) & 4))
+ pos += sprintf(buf + pos, "adjust(unframed)\n");
+ else
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "\n");
+
+ pos += sprintf(buf + pos, "unframed data rate: ");
+ if (get_common_reg(ioaddr, 0x30) ==
+ get_common_reg(ioaddr, 0x32)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x30));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, "
+ "get value or list using "
+ "mailbox\n");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x30));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27)));
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x32));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+
+ case 3:
+ /* JT: Line modes are printed only
+ * if the card has NO secondary interface. */
+ if (get_common_reg(ioaddr, 0x10) == 0) {
+ pos += sprintf(buf + pos, "rate sel. methods: ");
+ if (get_common_reg(ioaddr, 0x30) ==
+ get_common_reg(ioaddr, 0x32))
+ pos += sprintf(buf + pos, "n/a\n");
+ else {
+ if (get_common_reg(ioaddr, 0x23) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "timeslot map\n");
+ if (get_common_reg(ioaddr, 0x23) & 2)
+ pos += sprintf(buf + pos, "adjust\n");
+ }
+
+ pos += sprintf(buf + pos, "framed data rate: ");
+ if (get_common_reg(ioaddr, 0x2c) ==
+ get_common_reg(ioaddr, 0x2e)) {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos += sprintf(buf + pos, "fixed ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x2c));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ } else {
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x27))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x2c));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27)));
+ pos += sprintf(buf + pos, "bps - ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x27)) *
+ get_common_reg_word
+ (ioaddr, 0x2e));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x27)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x27)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ }
+
+ pos += sprintf(buf + pos, "rate sel. methods: ");
+ if (get_common_reg_word(ioaddr, 0x2c) ==
+ get_common_reg_word(ioaddr, 0x2e))
+ pos += sprintf(buf + pos, "n/a\n");
+ else {
+ if (get_common_reg(ioaddr, 0x22) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "timeslot map\n");
+ if (get_common_reg(ioaddr, 0x22) & 2)
+ pos += sprintf(buf + pos, "adjust\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ default:
+ EOF:
+ up_read(&cardp->semaphore);
+ *start = (char *)0;
+ *eof = 1;
+ return 0;
+ }
+}
+
+static int fepci_proc_read_settings(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int pos = 0;
+ int i, j;
+
+ struct fepci_card_private *cardp;
+ void *ioaddr;
+
+ cardp = (struct fepci_card_private *)data;
+
+ down_read(&cardp->semaphore);
+
+ if (unlikely(cardp->pci_dev == NULL))
+ goto EOF;
+
+ ioaddr = cardp->ioaddr;
+
+ switch (offset) {
+ case 0:
+ j = get_common_reg(ioaddr, 0x02);
+
+ for (i = 0; i < j; i++) {
+ if ((get_common_reg_word(ioaddr, 0x44) >> i) & 1) {
+ pos += sprintf(buf + pos, "\nline %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+ pos +=
+ sprintf(buf + pos, "mode: ");
+ if ((get_common_reg_word(ioaddr, 0x46) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "framed\n");
+ else
+ pos += sprintf(buf + pos, "unframed\n");
+
+ pos +=
+ sprintf(buf + pos, "scrambler: ");
+ if ((get_common_reg_word(ioaddr, 0x48) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "enabled\n");
+ else if ((get_common_reg_word(ioaddr, 0x46)
+ >> i) & 1)
+ pos += sprintf(buf + pos, "n/a\n");
+ else
+ pos += sprintf(buf + pos, "disabled\n");
+
+ pos +=
+ sprintf(buf + pos, "line crc: ");
+ if ((get_common_reg_word(ioaddr, 0x46) >>
+ i) & 1) {
+ if ((get_common_reg_word
+ (ioaddr, 0x4a) >> i) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "enabled\n");
+ else if (get_common_reg
+ (ioaddr, 0x22) & 0x20)
+ pos +=
+ sprintf(buf + pos,
+ "disabled\n");
+ else
+ pos +=
+ sprintf(buf + pos, "n/a\n");
+ } else {
+ if ((get_common_reg_word
+ (ioaddr, 0x4a) >> i) & 1)
+ pos +=
+ sprintf(buf + pos,
+ "enabled\n");
+ else if (get_common_reg
+ (ioaddr, 0x23) & 0x20)
+ pos +=
+ sprintf(buf + pos,
+ "disabled\n");
+ else
+ pos +=
+ sprintf(buf + pos, "n/a\n");
+ }
+ }
+ }
+
+ /* If there are no enabled lines, "No enabled lines!" printed.
+ * The settings file would remain empty otherwise. */
+ if ((get_common_reg_word(ioaddr, 0x44)) == 0)
+ pos += sprintf(buf + pos, "No enabled lines!\n");
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ case 1:
+ j = get_common_reg(ioaddr, 0x03);
+
+ if ((get_common_reg_word(ioaddr, 0x44)) != 0) {
+ for (i = 0; i < j; i++) {
+ pos += sprintf(buf + pos, "\npipe %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+
+ pos +=
+ sprintf(buf + pos, "access mode: ");
+ if ((get_common_reg_word(ioaddr, 0x4e) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "packet\n");
+ else
+ pos += sprintf(buf + pos, "stream\n");
+
+ pos +=
+ sprintf(buf + pos, "data rate: ");
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x42)) < 0)
+ pos +=
+ sprintf(buf + pos,
+ "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x42))) {
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x42)) *
+ get_common_reg_word
+ (ioaddr, 0x50 + 2 * i));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x42)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x42)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value "
+ "or list using mailbox\n");
+ }
+ }
+ up_read(&cardp->semaphore);
+ *start = (char *)1;
+ *eof = 1;
+ return min(pos, len);
+ default:
+ EOF:
+ up_read(&cardp->semaphore);
+ *start = (char *)0;
+ *eof = 1;
+ return 0;
+ }
+}
+
+void proc_cpy(char *buf_from, char *buf_to, int len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ *(buf_to + i) = *(buf_from + i);
+}
+
+static int fepci_proc_read_status(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ unsigned int pos = 0;
+ unsigned int i, j;
+
+ struct fepci_card_private *cardp;
+
+ void *ioaddr;
+
+ struct fepci_ch_private *fp;
+
+ cardp = (struct fepci_card_private *)data;
+
+ down_read(&cardp->semaphore);
+
+ if (unlikely(cardp->pci_dev == NULL))
+ goto EOF;
+
+ ioaddr = cardp->ioaddr;
+
+ if ((offset & 0xff000) == 0) {
+ j = get_common_reg(ioaddr, 0x02);
+ for (i = 0; i < j; i++) {
+ pos += sprintf(buf + pos, "\nline %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+ pos += sprintf(buf + pos, "status: ");
+ if ((get_common_reg_word(ioaddr, 0x72) >> i) & 1) {
+ pos += sprintf(buf + pos, "DOWN\n");
+ netif_carrier_off(cardp->ch_privates[i]->
+ this_dev);
+ } else {
+ netif_carrier_on(cardp->ch_privates[i]->
+ this_dev);
+ if ((get_common_reg_word(ioaddr, 0x74) >>
+ i) & 1)
+ pos += sprintf(buf + pos, "UP/Alarm\n");
+ else
+ pos += sprintf(buf + pos, "UP\n");
+ }
+ pos += sprintf(buf + pos, "primary clock: ");
+ if ((get_common_reg_word(ioaddr, 0x76) >> i) & 1)
+ pos += sprintf(buf + pos, "OK\n");
+ else
+ pos += sprintf(buf + pos, "FAIL\n");
+ }
+ up_read(&cardp->semaphore);
+ if (pos - (0xfff & offset) > (unsigned)len)
+ *start = (char *)len;
+ else
+ *start = (char *)(0x1000 - (offset & 0xfff));
+ proc_cpy(buf + (offset & 0xfff), buf, len);
+ return min((int)(pos - (0xfff & offset)), len);
+ }
+
+ j = get_common_reg(ioaddr, 0x03);
+ if ((j <= 0) || (j > 16))
+ j = 16;
+ if ((((((unsigned)offset) & 0xff000) >> 12) > 0) &&
+ (((((unsigned)offset) & 0xff000) >> 12) <= j)) {
+ i = ((int)((offset & 0xff000) >> 12)) - 1;
+ if (i < CHANNELS) {
+ pos += sprintf(buf + pos, "\npipe %2d\n", i);
+ pos += sprintf(buf + pos, "-------\n");
+ pos += sprintf(buf + pos, "status: ");
+
+ if (!((get_common_reg_word(ioaddr, 0x78) >> i) & 1))
+ pos += sprintf(buf + pos, "DOWN\n");
+ else if ((get_common_reg_word(ioaddr, 0x7a) >> i) & 1)
+ pos += sprintf(buf + pos, "UP/Degraded\n");
+ else
+ pos += sprintf(buf + pos, "UP\n");
+ if ((get_common_reg_word(ioaddr, 0x4e) >> i) & 1) {
+ pos +=
+ sprintf(buf + pos, "packet counters:\n\n");
+ pos +=
+ sprintf(buf + pos, "rx count: %lu \n",
+ cardp->ch_privates[i]->stats.
+ rx_packets);
+ pos +=
+ sprintf(buf + pos, "rx data: %lu \n",
+ cardp->ch_privates[i]->stats.
+ rx_bytes);
+ pos +=
+ sprintf(buf + pos, "rx errors:%lu \n",
+ cardp->ch_privates[i]->stats.
+ rx_errors);
+ pos +=
+ sprintf(buf + pos, "tx count: %lu \n",
+ cardp->ch_privates[i]->stats.
+ tx_packets);
+ pos +=
+ sprintf(buf + pos, "tx data: %lu \n",
+ cardp->ch_privates[i]->stats.
+ tx_bytes);
+ pos +=
+ sprintf(buf + pos, "tx errors:%lu \n",
+ cardp->ch_privates[i]->stats.
+ tx_errors);
+ } else {
+ pos +=
+ sprintf(buf + pos, "stream counters:\n\n");
+ fp = cardp->ch_privates[i];
+ pos += sprintf(buf + pos,
+ "rx_desc_fifo_err_stream : %10u"
+ " \n",
+ fp->rx_desc_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_desc_size_err_stream : %10u"
+ " \n",
+ fp->rx_desc_size_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_desc_octet_err_stream : %9u"
+ " \n",
+ fp->rx_desc_octet_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_desc_line_err_stream : %10u"
+ " \n",
+ fp->rx_desc_line_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "tx_desc_fifo_err_stream : %10u"
+ " \n",
+ fp->tx_desc_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_int_fifo_err_stream : %11u \n",
+ fp->rx_int_fifo_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "rx_int_frame_dropped_err_stream :"
+ " %2u \n",
+ fp->
+ rx_int_frame_dropped_err_stream);
+ pos +=
+ sprintf(buf + pos,
+ "tx_int_fifo_err_stream : %11u \n",
+ fp->tx_int_fifo_err_stream);
+ }
+ if (pos - (0xfff & offset) > (unsigned)len)
+ *start = (char *)len;
+ else
+ *start = (char *)(0x1000 - (offset & 0xfff));
+ proc_cpy(buf + (offset & 0xfff), buf, len);
+ }
+ up_read(&cardp->semaphore);
+ return min((int)(pos - (0xfff & offset)), len);
+ } else {
+ EOF:
+ up_read(&cardp->semaphore);
+ *start = (char *)0;
+ *eof = 1;
+ return 0;
+ }
+}
+
+static int fepci_proc_read_devices(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ void *ioaddr;
+ unsigned int pos = 0;
+ unsigned int i;
+
+ pos +=
+ sprintf(buf + pos,
+ "bus:card.function cardtype model lines, max rate\n");
+ for (i = 0; i < find_cnt; i++) {
+ down_read(&card_privates[i].semaphore);
+ if (likely(card_privates[i].pci_dev)) { /* the card exists */
+ pos +=
+ sprintf(buf + pos,
+ "%02x:%s ",
+ card_privates[i].pci_dev->bus->
+ number,
+ card_privates[i].pci_dev->procent->name);
+ ioaddr = card_privates[i].ioaddr;
+ pos =
+ print_card_type(get_common_reg
+ (ioaddr, 0x00), buf, pos);
+ pos =
+ print_card_model(get_common_reg
+ (ioaddr, 0x00), buf, pos);
+ pos +=
+ sprintf(buf + pos, "%d",
+ get_common_reg(ioaddr, 0x02));
+ pos += sprintf(buf + pos, " x ");
+ pos =
+ print_line_type(get_common_reg
+ (ioaddr, 0x0c), buf, pos);
+ if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26)) < 0)
+ pos += sprintf(buf + pos, "Invalid value\n");
+ else if (get_line_data_rate_value
+ (get_common_reg(ioaddr, 0x26))) {
+ pos += sprintf(buf + pos, ", total ");
+ pos +=
+ sprintf(buf + pos, "%d ",
+ get_line_data_rate_value
+ (get_common_reg
+ (ioaddr,
+ 0x26)) *
+ get_common_reg_word
+ (ioaddr,
+ 0x2a) *
+ get_common_reg(ioaddr, 0x02));
+ if (get_line_data_rate_unit
+ (get_common_reg(ioaddr, 0x26)))
+ pos +=
+ sprintf(buf + pos,
+ "%c",
+ get_line_data_rate_unit
+ (get_common_reg
+ (ioaddr, 0x26)));
+ pos += sprintf(buf + pos, "bps\n");
+ } else
+ pos +=
+ sprintf(buf + pos,
+ "No multiplier, get value or list "
+ "using mailbox\n");
+
+ }
+ up_read(&card_privates[i].semaphore);
+ }
+ DBG_PRINT("fepci_proc_read_devices pos %u < %lu?\n", pos, PAGE_SIZE);
+ *eof = 1;
+ return pos;
+}
+
+static int fepci_proc_read_id_list(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ unsigned int pos = 0;
+ unsigned int i;
+
+ pos += sprintf(buf + pos, "bus:card.function major minor interfaces\n");
+ for (i = 0; i < find_cnt; i++) {
+ down_read(&card_privates[i].semaphore);
+ if (likely(card_privates[i].pci_dev)) /* the card exists */
+ pos +=
+ sprintf(buf + pos,
+ "%02x:%s %03i %03i %s..%s\n",
+ card_privates[i].pci_dev->bus->
+ number,
+ card_privates[i].pci_dev->
+ procent->name, major,
+ card_privates[i].
+ ch_privates[0]->minor,
+ card_privates[i].
+ ch_privates[0]->this_dev->name,
+ card_privates[i].
+ ch_privates[CHANNELS - 1]->this_dev->name);
+ up_read(&card_privates[i].semaphore);
+ }
+
+ pos += sprintf(buf + pos, "\n");
+ DBG_PRINT("fepci_proc_read_id_list pos %u < %lu?\n", pos, PAGE_SIZE);
+ *eof = 1;
+ return pos;
+}
+
+const static char DEVICES[] = "devices";
+const static char ID_LIST[] = "id_list";
+
+static int fepci_proc_init_driver(void)
+{
+ DBG_PRINT("fepci_proc_init_driver\n");
+ proc_root_entry = create_proc_entry(fepci_proc_entry_name, S_IFDIR, 0);
+ if (!proc_root_entry)
+ printk(KERN_WARNING
+ "%s: creating proc root dir entry failed.\n",
+ fepci_NAME);
+
+ create_proc_read_entry(DEVICES, 0, proc_root_entry,
+ fepci_proc_read_devices, NULL);
+ create_proc_read_entry(ID_LIST, 0, proc_root_entry,
+ fepci_proc_read_id_list, NULL);
+
+ return 0;
+}
+
+static void fepci_proc_cleanup_driver(void)
+{
+ char name[sizeof(fepci_proc_entry_name) + sizeof(DEVICES)];
+ strcpy(name, fepci_proc_entry_name);
+ name[sizeof(fepci_proc_entry_name) - 1] = '/';
+ strcpy(name + sizeof(fepci_proc_entry_name), DEVICES);
+ remove_proc_entry(name, 0);
+ strcpy(name + sizeof(fepci_proc_entry_name), ID_LIST);
+ remove_proc_entry(name, 0);
+ remove_proc_entry(fepci_proc_entry_name, 0);
+}
+
+static void fepci_proc_init_card(int card_number, void *card_data)
+{
+ char buf[50];
+ struct proc_dir_entry *ent;
+ sprintf(buf, "%02x:%02x.%02x",
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+ ent = create_proc_entry(buf, S_IFDIR, proc_root_entry);
+ if (!ent)
+ printk("%s: creating proc dir entry failed.\n", fepci_NAME);
+
+ sprintf(buf, fepci_features_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_features,
+ card_data);
+
+ sprintf(buf, fepci_settings_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_settings,
+ card_data);
+
+ sprintf(buf, fepci_status_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_status, card_data);
+}
+
+static void fepci_proc_cleanup_card(int card_number)
+{
+ char buf[50];
+
+ sprintf(buf, fepci_features_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ remove_proc_entry(buf, 0);
+ sprintf(buf, fepci_settings_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ remove_proc_entry(buf, 0);
+ sprintf(buf, fepci_status_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+ remove_proc_entry(buf, 0);
+
+ sprintf(buf, "%02x:%02x.%02x",
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+ remove_proc_entry(buf, proc_root_entry);
+}
+
+#ifdef DEBUG_PROC_FILES
+static void fepci_proc_init_channel(int card_number, int channel_number,
+ void *channel_data)
+{
+ DBG_PRINT("fepci_proc_init_channel\n");
+ char buf[50];
+
+ sprintf(buf, fepci_counters_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_counters,
+ channel_data);
+
+ sprintf(buf, fepci_descriptors_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_descriptors,
+ channel_data);
+
+ sprintf(buf, fepci_stream_counters_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+
+ create_proc_read_entry(buf, 0, NULL,
+ fepci_proc_read_stream_counters, channel_data);
+
+ sprintf(buf, fepci_registers_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ create_proc_read_entry(buf, 0, NULL, fepci_proc_read_registers,
+ channel_data);
+}
+
+static void fepci_proc_cleanup_channel(int card_number, int channel_number)
+{
+ char buf[50];
+ sprintf(buf, fepci_counters_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ remove_proc_entry(buf, 0);
+
+ sprintf(buf, fepci_descriptors_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ remove_proc_entry(buf, 0);
+
+ sprintf(buf, fepci_stream_counters_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ remove_proc_entry(buf, 0);
+
+ sprintf(buf, fepci_registers_proc_entry_name,
+ card_privates[card_number].pci_dev->bus->number,
+ PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+ PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+ channel_number);
+ remove_proc_entry(buf, 0);
+}
+#endif /* DEBUG_PROC_FILES */
diff -Napur linux-2.6.23/drivers/net/wan/retina.h linux-2.6.24/drivers/net/wan/retina.h
--- linux-2.6.23/drivers/net/wan/retina.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.24/drivers/net/wan/retina.h 2007-10-22 12:01:44.856033562 +0300
@@ -0,0 +1,164 @@
+/* V1.0.0 */
+
+/*
+ Copyright (C) 2002-2003 Jouni Kujala, Flexibilis Oy.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ All the drivers derived from or based on this code fall under the
+ GPL and must retain the copyright and license notice.
+*/
+
+#ifndef RETINA_H
+#define RETINA_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* net device related stuff: */
+#define FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE 0x89F1
+#define FEPCI_NETDEV_IOCTL_STREAM_UNITSIZE 0x89F2
+#define FEPCI_NETDEV_IOCTL_STREAM_OPEN 0x89F3
+#define FEPCI_NETDEV_IOCTL_STREAM_START 0x89F4
+#define FEPCI_NETDEV_IOCTL_STREAM_CLOSE 0x89F6
+
+/* char device related stuff: */
+
+#define FEPCI_SHARED_MEM_OFFSETT 0x8000
+#define FEPCI_IDENTIFICATION_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x0)
+#define FEPCI_FEATURES_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x40)
+#define FEPCI_SETTINGS_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x80)
+#define FEPCI_STATUS_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0xE0)
+#define FEPCI_MAILBOX_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x100)
+
+/* structures for ioctl calls: */
+struct fepci_ioctl_identification {
+ unsigned char data[0x20];
+};
+struct fepci_real_identification {
+ unsigned long int data[0x10];
+};
+
+struct fepci_ioctl_features {
+ unsigned char data[0x20];
+};
+struct fepci_real_features {
+ unsigned long int data[0x10];
+};
+
+struct fepci_ioctl_settings {
+ unsigned char data[0x30];
+};
+struct fepci_real_settings {
+ unsigned long int data[0x15];
+};
+
+struct fepci_ioctl_status {
+ unsigned char data[0x10];
+};
+struct fepci_real_status {
+ unsigned long int data[0x5];
+};
+
+struct fepci_ioctl_shared_mem {
+ unsigned long int data[0x80];
+};
+
+#define FEPCI_IOCTL_MAGIC 0xAA
+
+#define FEPCI_IOCTL_R_SHARED_MEM _IOR(FEPCI_IOCTL_MAGIC, 1, \
+struct fepci_ioctl_shared_mem)
+#define FEPCI_IOCTL_W_SHARED_MEM _IOW(FEPCI_IOCTL_MAGIC, 2, \
+struct fepci_ioctl_shared_mem)
+
+#define FEPCI_IOCTL_G_IDENTIFICATION _IOR(FEPCI_IOCTL_MAGIC, 0x81, \
+struct fepci_ioctl_identification)
+#define FEPCI_IOCTL_G_FEATURES _IOR(FEPCI_IOCTL_MAGIC, 0x82, \
+struct fepci_ioctl_features)
+#define FEPCI_IOCTL_G_SETTINGS _IOR(FEPCI_IOCTL_MAGIC, 0x83, \
+struct fepci_ioctl_settings)
+#define FEPCI_IOCTL_G_STATUS _IOR(FEPCI_IOCTL_MAGIC, 0x84, \
+struct fepci_ioctl_status)
+
+/* mailbox: */
+
+struct fepci_ioctl_mailbox {
+ unsigned char Semafore;
+ unsigned char Mail_number;
+ unsigned char Size;
+ unsigned char Command;
+ unsigned char Data[112];
+};
+
+struct fepci_real_mailbox {
+ __u32 Semafore_Mail_number;
+ __u32 Size_Command;
+ __u32 Data[112 / 2];
+};
+
+#define FEPCI_IOCTL_B_POLL _IO(FEPCI_IOCTL_MAGIC, 0x85)
+#define FEPCI_IOCTL_B_GRAB _IO(FEPCI_IOCTL_MAGIC, 0x86)
+#define FEPCI_IOCTL_B_RELEASE _IO(FEPCI_IOCTL_MAGIC, 0x87)
+#define FEPCI_IOCTL_B_S_CMAIL _IOW(FEPCI_IOCTL_MAGIC, 0x88, \
+struct fepci_ioctl_mailbox)
+#define FEPCI_IOCTL_B_S_QMAIL _IOW(FEPCI_IOCTL_MAGIC, 0x89, \
+struct fepci_ioctl_mailbox)
+#define FEPCI_IOCTL_B_G_MAIL _IOR(FEPCI_IOCTL_MAGIC, 0x90, \
+struct fepci_ioctl_mailbox)
+
+#define FEPCI_IOCTL_ALARM_MANAGER _IO(FEPCI_IOCTL_MAGIC, 0x91)
+#define FEPCI_IOCTL_STREAM_TRANSMIT_POLL _IO(FEPCI_IOCTL_MAGIC, 0x92)
+#define FEPCI_IOCTL_STREAM_RECEIVE_POLL _IO(FEPCI_IOCTL_MAGIC, 0x93)
+#define FEPCI_IOCTL_STREAM_BOTH_POLL _IO(FEPCI_IOCTL_MAGIC, 0x94)
+
+/* stream related stuff: */
+
+/* stream buffer address space:
+ * address: 0x 7 6 5 4 3 2 1 0
+ * ^ ^ ^
+ * | | |
+ * card | area(rx/tx,0==rx,1==tx)
+ * channel */
+
+#define CARD_ADDRESS_SHIFT 24u
+#define CHANNEL_ADDRESS_SHIFT 20u
+#define AREA_ADDRESS_SHIFT 16u
+
+#define STREAM_BUFFER_POINTER_AREA 0x7fff0000 /* one page reserved */
+
+/* stream buffer pointers (at pointer area):
+ * address: 0x 7 6 5 4 3 2 1 0
+ * ^ ^ ^
+ * | | |
+ * card | area(rx/tx,0==rx,4==tx)
+ * channel */
+
+#define CARD_POINTER_SHIFT 8u
+#define CHANNEL_POINTER_SHIFT 4u
+#define AREA_POINTER_SHIFT 2u
+
+/* fake pointers are for faking larger unit sizes to the user than
+ * what is the maximum internal unit size in FEPCI */
+#define USER_RX_S_FAKE_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x0) + __offset))
+#define USER_TX_S_FAKE_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x4) + __offset))
+
+#define USER_RX_S_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x8) + __offset))
+#define USER_TX_S_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0xC) + __offset))
+
+#endif
diff -Napur linux-2.6.23/MAINTAINERS linux-2.6.24/MAINTAINERS
--- linux-2.6.23/MAINTAINERS 2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/MAINTAINERS 2007-10-22 11:50:13.190983316 +0300
@@ -3149,6 +3149,11 @@ L: reiserfs-devel@vger.kernel.org
W: http://www.namesys.com
S: Supported
+RETINA DRIVER
+P: Matti Linnanvuori
+M: mattilinnanvuori@yahoo.com
+S: Supported
+
ROCKETPORT DRIVER
P: Comtrol Corp.
W: http://www.comtrol.com
Heute schon einen Blick in die Zukunft von E-Mails wagen? www.yahoo.de/mail
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2007-10-25 9:18 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-10-25 6:49 [PATCH] wan: new driver retina Matti Linnanvuori
2007-10-25 9:18 ` Jeff Garzik
-- strict thread matches above, loose matches on Subject: below --
2007-10-23 9:58 Matti Linnanvuori
2007-10-23 16:31 ` Stephen Hemminger
2007-10-22 11:14 Matti Linnanvuori
2007-10-22 15:02 ` Randy Dunlap
2007-10-23 0:41 ` Krzysztof Halasa
2007-10-22 9:15 Matti Linnanvuori
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).