* Re: [PATCH] - trivial - Improve appletalk checksum calculation
From: Joe Perches @ 2007-10-23 3:39 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: Arnaldo Carvalho de Melo, netdev, David S. Miller
In-Reply-To: <20071022203052.23725719@freepuppy.rosehill>
On Mon, 2007-10-22 at 20:30 -0700, Stephen Hemminger wrote:
> > Corrected fast code is:
> >
> > while (len--) {
> > sum += *data++;
> > sum <<= 1;
> > sum = (((sum & 0x10000) >> 16) + sum) & 0xffff;
> > }
> >
> > At least it is correct on the standalone random data test, and the
> > new code is 30% faster for the cached memory case (13.7 clks/byte vs 18
> > clks/byte).
> Your code looks different...
Both are 16 bit rotate lefts.
Which looks clearer?
^ permalink raw reply
* Re: [PATCH 5/7] pci: Export the pci_restore_msi_state() function
From: Michael Ellerman @ 2007-10-23 4:20 UTC (permalink / raw)
To: Linas Vepstas
Cc: David Miller, mcarlson, netdev, linux-pci, mchan,
linuxppc-dev list
In-Reply-To: <20071022181336.GC4280@austin.ibm.com>
[-- Attachment #1: Type: text/plain, Size: 1962 bytes --]
On Mon, 2007-10-22 at 13:13 -0500, Linas Vepstas wrote:
> On Mon, Oct 22, 2007 at 11:49:24AM +1000, Michael Ellerman wrote:
> >
> > On pseries there's a chance it will work for PCI error recovery, but if
> > so it's just lucky that firmware has left everything configured the same
> > way.
>
> ? The papr is quite clear that i is up to the OS to restore the msi
> state after an eeh error.
Perhaps. I see R1-7.3.10.5.1-10, which says we should restore the config
space after a reset operation, but after an isolate/unisolate we must
recall RTAS. I thought EEH was doing the isolate/unisolate, but if it's
just a reset then just blatting the config space back should work.
> > Yes I think so. That way we can properly reconfigure via the firmware
> > interface. The other option would be to design some new arch hook to do
> > resume, but just doing a disable/enable seems simpler to me.
>
> Err, If you read the code for suspend/resume, it never actually calls
> disable/enable (and thus doesn't go to the firmware); it calls
> restore_msi_state() function!
I know. That was a proposed solution, to explicitly call disable/enable
instead of restore_msi_state().
> If suspend/resume needs to call firmware to restore the state, then,
> at the moment, suspend/resume is broken. As I mentioned earlier,
> I presumed that no powerpc laptops currently use msi-enabled devices,
> as otherwise, this would have been flushed out.
On _pseries_ suspend/resume with MSI is broken. The other powerpc
platforms that implement MSI should be fine, although I don't think
anyone's tested them, because they're not laptops and so suspend/resume
is not that interesting.
cheers
--
Michael Ellerman
OzLabs, IBM Australia Development Lab
wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)
We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* [PATCH] Read back MSI message in rtas_setup_msi_irqs() so restore works
From: Michael Ellerman @ 2007-10-23 4:23 UTC (permalink / raw)
To: Paul Mackerras
Cc: netdev, mcarlson, linuxppc-dev, mchan, linux-pci, David S. Miller
There are plans afoot to use pci_restore_msi_state() to restore MSI
state after a device reset. In order for this to work for the RTAS MSI
backend, we need to read back the MSI message from config space after
it has been setup by firmware.
This should be sufficient for restoring the MSI state after a device
reset, however we will need to revisit this for suspend to disk if that
is ever implemented on pseries.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
Linas, can you test this on your setup with your EEH stuff? I haven't got
any MSI supporting hardware/firmware combination.
arch/powerpc/platforms/pseries/msi.c | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c
index 2793a1b..f15222b 100644
--- a/arch/powerpc/platforms/pseries/msi.c
+++ b/arch/powerpc/platforms/pseries/msi.c
@@ -171,6 +171,7 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
struct pci_dn *pdn;
int hwirq, virq, i, rc;
struct msi_desc *entry;
+ struct msi_msg msg;
pdn = get_pdn(pdev);
if (!pdn)
@@ -213,6 +214,11 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
dev_dbg(&pdev->dev, "rtas_msi: allocated virq %d\n", virq);
set_irq_msi(virq, entry);
+
+ /* Read config space back so we can restore after reset */
+ read_msi_msg(virq, &msg);
+ entry->msg = msg;
+
unmask_msi_irq(virq);
}
--
1.5.1.3.g7a33b
^ permalink raw reply related
* Re: attach additional value to skb
From: Muli Ben-Yehuda @ 2007-10-23 5:42 UTC (permalink / raw)
To: Yakov Lerner; +Cc: netdev
In-Reply-To: <f36b08ee0710221303y5a846a27v1ebdc919db0f00ac@mail.gmail.com>
On Mon, Oct 22, 2007 at 10:03:47PM +0200, Yakov Lerner wrote:
> I want to add my custom destructor to the skb, the function pointer
> to be called at skb destruction time. Will the following work: if I
> push address of my_destructor to the
> sk-buff->destructor field, and then when my_destructor
> is called, I call the (saved) old value of sk-buff->destructor ?
> Will this work ?
It might, provided nothing else runs after you that will unilaterally
change the ->destructor field. I.e., it depends at which point in the
skb's life-time you set your hook.
Cheers,
Muli
--
SYSTOR 2007 --- 1st Annual Haifa Systems and Storage Conference 2007
http://www.haifa.il.ibm.com/Workshops/systor2007/
Virtualization workshop: Oct 29th, 2007 | Storage workshop: Oct 30th, 2007
^ permalink raw reply
* Re: [ANNOUNCE] iproute2-2.6.23
From: Herbert Xu @ 2007-10-23 6:12 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: netdev
In-Reply-To: <E1IhxQA-0004gh-00@gondolin.me.apana.org.au>
On Wed, Oct 17, 2007 at 09:10:02AM +0800, Herbert Xu wrote:
>
> Any chance you could include the NAT patch for the next release?
>
> [TC]: Add NAT action
>
> This patch adds the parser for the nat action which is used
> for stateless NAT.
>
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Hi Stephen:
Is there a problem with merging this patch?
Thanks,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* Re: [PATCH] flush_work_sync vs. flush_scheduled_work Re: [PATCH] PHYLIB: IRQ event workqueue handling fixes
From: Jarek Poplawski @ 2007-10-23 6:59 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Maciej W. Rozycki, Andy Fleming, Andrew Morton, Jeff Garzik,
netdev, linux-kernel
In-Reply-To: <20071022180259.GA967@tv-sign.ru>
On Mon, Oct 22, 2007 at 10:02:59PM +0400, Oleg Nesterov wrote:
> On 10/22, Jarek Poplawski wrote:
...
> > OK, I know I'm dumber and dumber everyday,
>
> You are not alone. I have the same feeling about myself!
Feeling is not the same, only true knowledge counts!
>
> > these all flushes are
> > rtnl lockup vulnerable wrt. other work functions, but cancel_work_sync
> > looks perfectly fine
>
> Yes,
>
> > Then, if by any chance I'm right, something like flush_work_sync
> > (or changed flush_scheduled_work, if there is no problem with such
> > a change of implementation) could be safely (if it's called without
> > locks used by flushed work only) done cancel_work_sync() way, by
> > running a work function after try_to_grab_pending() returns 1
>
> If this work doesn't rearm itself - yes. (otherwise, the same ->func
> can run twice _at the same time_)
>
> But again, in this case wait_on_work() after try_to_grab_pending() == 1
> doesn't block, so we can just do
>
> if (cancel_work_sync(w))
> w->func();
Of course, I should have written it much shorter by saying
flush_scheduled_work could be done internally just like you suggested!
My point is to make this all safer and simpler, so we don't have to
remember each time about these differences wrt. locking. Then checking
of such patches could be much easier. Unless this current behavior
of flush_scheduled_work has any real advantages, worth of this
potential unsafety. (Btw. is there any reason to use this with
rearming works, anyway?)
Thanks,
Jarek P.
^ permalink raw reply
* Re: [RFC][PATCH 0/3][XFRM]: Support packet processing error statistics.
From: Masahide NAKAMURA @ 2007-10-23 7:08 UTC (permalink / raw)
To: hadi; +Cc: Herbert Xu, David Miller, netdev
In-Reply-To: <1193056091.4422.33.camel@localhost>
Monday 22 October 2007 21:28, jamal wrote:
> On Mon, 2007-22-10 at 15:11 +0900, Masahide NAKAMURA wrote:
> > This patch introduces statistics about transformation error (or almost error)
> > factor at packet processing for developer.
> > It is not a SNMP/MIB specification from IPsec/MIPv6 but a counter
> > designed from current transformation source code.
> >
> > Comment please.
>
> very nice - these stats make IPSEC a lot more usable (I will go look and
> see if theres anything that i have used for debug before that you dont
> have and send you mail). Two comments:
Thanks. I would like you to find too much item at my patch
for the statistics, too.
> 1) Since these are not MIB stats, it sounds like a good idea not to use
> _MIB_ extender in the naming. Maybe something like _NOTMIB_ ;-> or
> totaly leave it out. One other approach is to push these to be a MIB at
> IETF since they are sensible to have.
This point is one of what I want to hear comment.
My patch uses "XFRM_MIB_XXX" because I found "LINUX_MIB_XXX" definition at
include/linux/snmp.h for TCP extended statistics at /proc/net/netstat and
it does not seem to be defined by any RFC specification. Then I feel it is not so bad to
use _MIB_ for them. Maybe we have another idea to merge them into LINUX_MIB.
Now we have the following candidates:
(1) my patch XFRM_MIB_INHDRERROR
(2) some extender XFRM_XXX_INHDRERROR (XXX is requested)
(3) not-mib extender XFRM_NOTMIB_INHDRERROR
(4) no extender XFRM_INHDRERROR
(5) merge linux-mib LINUX_MIB_XFRMINHDRERROR
Comments?
> 2) Why /proc? Are you going to make these available also via netlink?
Because /proc is easy to see it without any modified application.
If you want the netlink interface, I can do it as the next step. Do you want it?
--
Masahide NAKAMURA
^ permalink raw reply
* Re: [PATCH] flush_work_sync vs. flush_scheduled_work Re: [PATCH] PHYLIB: IRQ event workqueue handling fixes
From: Jarek Poplawski @ 2007-10-23 9:21 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Maciej W. Rozycki, Andy Fleming, Andrew Morton, Jeff Garzik,
netdev, linux-kernel
In-Reply-To: <20071022180259.GA967@tv-sign.ru>
On Mon, Oct 22, 2007 at 10:02:59PM +0400, Oleg Nesterov wrote:
...
> If this work doesn't rearm itself - yes. (otherwise, the same ->func
> can run twice _at the same time_)
>
> But again, in this case wait_on_work() after try_to_grab_pending() == 1
> doesn't block, so we can just do
>
> if (cancel_work_sync(w))
> w->func();
>
...but, if it were run just before work_clear_pending()?
Jarek P.
^ permalink raw reply
* [PATCH] wan: new driver retina
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
* Re: Regression in current git - Network Manager fails (bisected)
From: Thomas Graf @ 2007-10-23 12:11 UTC (permalink / raw)
To: Dan Williams
Cc: Denis V. Lunev, netdev, Denis V. Lunev, David S. Miller,
Alexey Kuznetsov
In-Reply-To: <1193068645.30205.22.camel@localhost.localdomain>
* Dan Williams <dcbw@redhat.com> 2007-10-22 11:57
> On Mon, 2007-10-22 at 13:22 +0400, Denis V. Lunev wrote:
> > We have spent some time with the problem with Alexey and there are no
> > guesses for now.
> >
> > Is it possible to name exact version of Network Manager and all
> > libraries related + provide us an output of strace with full buffers
> > send/received from netlink. Something like
> > strace -v -x -s 32768 <nm>
>
> NM uses netlink in two places; libnl (from Thomas Graf) and some custom
> code for listening for interface up/down events and wireless events.
>
> It looks like that code comes from libnl's lib/handlers.c where it
> thinks the received message is invalid.
>
> I'm pretty sure the code that checks carrier status of the device isn't
> libnl code; so maybe the error message (which should get fixed of
> course) isn't in the same path as the link detection.
>
> The link detection comes from src/nm-netlink-monitor.c, so maybe we
> should look at debugging there.
The patch introduced a change in semantics because it removed the
special ACK handling after a dump was started.
I will look into this.
^ permalink raw reply
* sch_prio.c vs CONFIG_NETDEVICES_MULTIQUEUE
From: Alexey Dobriyan @ 2007-10-23 12:14 UTC (permalink / raw)
To: netdev; +Cc: peter.p.waskiewicz.jr, kaber
gcc spits following warnings
net/sched/sch_prio.c:139: warning: passing argument 2 of 'netif_subqueue_stopped' makes pointer from integer without a cast
net/sched/sch_prio.c:169: warning: passing argument 2 of 'netif_subqueue_stopped' makes pointer from integer without a cast
and he is dead right -- passing integer instead of skb is not going to
work:
if (!netif_subqueue_stopped(sch->dev, (q->mq ? prio : 0))) {
skb_get_queue_mapping will ignore it if multiqueue support is off and
dereference it otherwise.
^ permalink raw reply
* Re: sch_prio.c vs CONFIG_NETDEVICES_MULTIQUEUE
From: Patrick McHardy @ 2007-10-23 12:17 UTC (permalink / raw)
To: Alexey Dobriyan; +Cc: netdev, peter.p.waskiewicz.jr, Pavel Emelyanov
In-Reply-To: <20071023121420.GB6267@localhost.sw.ru>
CCed Pavel
Alexey Dobriyan wrote:
> gcc spits following warnings
>
> net/sched/sch_prio.c:139: warning: passing argument 2 of 'netif_subqueue_stopped' makes pointer from integer without a cast
> net/sched/sch_prio.c:169: warning: passing argument 2 of 'netif_subqueue_stopped' makes pointer from integer without a cast
>
> and he is dead right -- passing integer instead of skb is not going to
> work:
>
> if (!netif_subqueue_stopped(sch->dev, (q->mq ? prio : 0))) {
>
> skb_get_queue_mapping will ignore it if multiqueue support is off and
> dereference it otherwise.
^ permalink raw reply
* Re: sch_prio.c vs CONFIG_NETDEVICES_MULTIQUEUE
From: Pavel Emelyanov @ 2007-10-23 13:01 UTC (permalink / raw)
To: Patrick McHardy, Alexey Dobriyan; +Cc: netdev, peter.p.waskiewicz.jr
In-Reply-To: <471DE660.4090004@trash.net>
Try with this patch.
Log:
Fix more users of netiff_subqueue_stopped. To check for the
queue id one must use the __netiff_subqueue_stoped call.
These run out of my sight when I made the
668f895a85b0c3a62a690425145f13dabebebd7a commit :(
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
---
diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c
index ae41973..d337bfa 100644
--- a/drivers/net/cpmac.c
+++ b/drivers/net/cpmac.c
@@ -534,7 +534,7 @@ static void cpmac_end_xmit(struct net_device *dev, int queue)
dev_kfree_skb_irq(desc->skb);
desc->skb = NULL;
#ifdef CONFIG_NETDEVICES_MULTIQUEUE
- if (netif_subqueue_stopped(dev, queue))
+ if (__netif_subqueue_stopped(dev, queue))
netif_wake_subqueue(dev, queue);
#else
if (netif_queue_stopped(dev))
@@ -545,7 +545,7 @@ static void cpmac_end_xmit(struct net_device *dev, int queue)
printk(KERN_WARNING
"%s: end_xmit: spurious interrupt\n", dev->name);
#ifdef CONFIG_NETDEVICES_MULTIQUEUE
- if (netif_subqueue_stopped(dev, queue))
+ if (__netif_subqueue_stopped(dev, queue))
netif_wake_subqueue(dev, queue);
#else
if (netif_queue_stopped(dev))
@@ -649,7 +649,7 @@ static void cpmac_clear_tx(struct net_device *dev)
for (i = 0; i < CPMAC_QUEUES; i++)
if (priv->desc_ring[i].skb) {
dev_kfree_skb_any(priv->desc_ring[i].skb);
- if (netif_subqueue_stopped(dev, i))
+ if (__netif_subqueue_stopped(dev, i))
netif_wake_subqueue(dev, i);
}
}
diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c
index abd82fc..de89409 100644
--- a/net/sched/sch_prio.c
+++ b/net/sched/sch_prio.c
@@ -136,7 +136,7 @@ prio_dequeue(struct Qdisc* sch)
* pulling an skb. This way we avoid excessive requeues
* for slower queues.
*/
- if (!netif_subqueue_stopped(sch->dev, (q->mq ? prio : 0))) {
+ if (!__netif_subqueue_stopped(sch->dev, (q->mq ? prio : 0))) {
qdisc = q->queues[prio];
skb = qdisc->dequeue(qdisc);
if (skb) {
@@ -165,7 +165,7 @@ static struct sk_buff *rr_dequeue(struct Qdisc* sch)
* for slower queues. If the queue is stopped, try the
* next queue.
*/
- if (!netif_subqueue_stopped(sch->dev,
+ if (!__netif_subqueue_stopped(sch->dev,
(q->mq ? q->curband : 0))) {
qdisc = q->queues[q->curband];
skb = qdisc->dequeue(qdisc);
^ permalink raw reply related
* Re: Regression in current git - Network Manager fails (bisected)
From: Denis V. Lunev @ 2007-10-23 13:09 UTC (permalink / raw)
To: Thomas Graf
Cc: Dan Williams, netdev, Denis V. Lunev, David S. Miller,
Alexey Kuznetsov
In-Reply-To: <20071023121123.GA8125@postel.suug.ch>
Thomas Graf wrote:
> * Dan Williams <dcbw@redhat.com> 2007-10-22 11:57
>> On Mon, 2007-10-22 at 13:22 +0400, Denis V. Lunev wrote:
>>> We have spent some time with the problem with Alexey and there are no
>>> guesses for now.
>>>
>>> Is it possible to name exact version of Network Manager and all
>>> libraries related + provide us an output of strace with full buffers
>>> send/received from netlink. Something like
>>> strace -v -x -s 32768 <nm>
>> NM uses netlink in two places; libnl (from Thomas Graf) and some custom
>> code for listening for interface up/down events and wireless events.
>>
>> It looks like that code comes from libnl's lib/handlers.c where it
>> thinks the received message is invalid.
>>
>> I'm pretty sure the code that checks carrier status of the device isn't
>> libnl code; so maybe the error message (which should get fixed of
>> course) isn't in the same path as the link detection.
>>
>> The link detection comes from src/nm-netlink-monitor.c, so maybe we
>> should look at debugging there.
>
> The patch introduced a change in semantics because it removed the
> special ACK handling after a dump was started.
>
> I will look into this.
>
I have reproduced the problem with one-line test.
./nl-route-get 192.168.1.1
The problem is with this message:
-- Debug: Sent Message:
-------------------------- BEGIN NETLINK MESSAGE
---------------------------
[HEADER] 16 octets
.nlmsg_len = 20
.nlmsg_type = 18 <route/link>
.nlmsg_flags = 773 <REQUEST,ACK,ROOT,MATCH>
.nlmsg_seq = 1193143772
.nlmsg_pid = 8233
[PAYLOAD] 16 octets
00 1d fa 20 00 00 00 00 81 0e 02 00 00 00 00 00 ... ............
--------------------------- END NETLINK MESSAGE
---------------------------
it starts dump and requests ACK.
Regards,
Den
^ permalink raw reply
* Re: Regression in current git - Network Manager fails (bisected)
From: Thomas Graf @ 2007-10-23 13:38 UTC (permalink / raw)
To: Denis V. Lunev
Cc: Dan Williams, netdev, Denis V. Lunev, David S. Miller,
Alexey Kuznetsov
In-Reply-To: <471DF28D.3030904@sw.ru>
* Denis V. Lunev <den@sw.ru> 2007-10-23 17:09
> I have reproduced the problem with one-line test.
> ./nl-route-get 192.168.1.1
> The problem is with this message:
>
> -- Debug: Sent Message:
> -------------------------- BEGIN NETLINK MESSAGE
> ---------------------------
> [HEADER] 16 octets
> .nlmsg_len = 20
> .nlmsg_type = 18 <route/link>
> .nlmsg_flags = 773 <REQUEST,ACK,ROOT,MATCH>
> .nlmsg_seq = 1193143772
> .nlmsg_pid = 8233
> [PAYLOAD] 16 octets
> 00 1d fa 20 00 00 00 00 81 0e 02 00 00 00 00 00 ... ............
> --------------------------- END NETLINK MESSAGE
> ---------------------------
> it starts dump and requests ACK.
libnl sets the ACK bit for all requests unless the application
disables this behaviour.
^ permalink raw reply
* Re: iproute2: git pull request from debian repo.
From: Andreas Henriksson @ 2007-10-23 13:56 UTC (permalink / raw)
To: Patrick McHardy; +Cc: maximilian attems, shemminger, netdev
In-Reply-To: <471B90E5.1040108@trash.net>
On Sun, Oct 21, 2007 at 07:48:21PM +0200, Patrick McHardy wrote:
> Andreas Henriksson wrote:
> >- for (;;) {
> >+ while (round < MAX_ROUNDS) {
> > if (rtnl_wilddump_request(&rth, filter.family,
> > RTM_GETADDR) < 0) {
> > perror("Cannot send dump request");
> > exit(1);
> >@@ -694,6 +696,8 @@ int ipaddr_list_or_flush(int argc, char **argv, int
> >flush)
> > fflush(stdout);
> > }
> > }
> >+ fprintf(stderr, "*** Flush remains incomplete after %d
> >rounds. ***\n", MAX_ROUNDS); fflush(stderr);
>
>
> Again, please make this optional. People might want to make
> sure the cache is flushed even if it takes a bit longer.
Something like the patch below should hopefully please everybody. Making
MAX_ROUNDS configurable is possibly even better (and 0=infinite), I'll see if
I can whip up an alternativ patch so you can decide which one you like better.
I've not added it to the patches branch yet, since Stephen said next release
will probably be a bugfix only release and I consider this a new feature.
(The previous patch simply makes ip addr flush behave the same as ip neigh
flush, and avoids an endless loop which I consider a staight bugfix.
Plus I don't want to do anything more with it until it's confirmed
I've set up the git repo correctly.)
I guess it's up to Stephen if he wants this feature and if he wants in now or
later... Please tell me.
Only compile-tested for now (this is on top of the previous patch):
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index ff9e318..356535b 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -65,7 +65,7 @@ static void usage(void)
fprintf(stderr, "Usage: ip addr {add|change|replace} IFADDR dev STRING [ LIFETIME ]\n");
fprintf(stderr, " [ CONFFLAG-LIST]\n");
fprintf(stderr, " ip addr del IFADDR dev STRING\n");
- fprintf(stderr, " ip addr {show|flush} [ dev STRING ] [ scope SCOPE-ID ]\n");
+ fprintf(stderr, " ip addr {show|flush|forceflush} [ dev STRING ] [ scope SCOPE-ID ]\n");
fprintf(stderr, " [ to PREFIX ] [ FLAG-LIST ] [ label PATTERN ]\n");
fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n");
fprintf(stderr, " [ broadcast ADDR ] [ anycast ADDR ]\n");
@@ -559,7 +559,7 @@ static int store_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n,
return 0;
}
-int ipaddr_list_or_flush(int argc, char **argv, int flush)
+int ipaddr_list_or_flush(int argc, char **argv, int flush, int force)
{
struct nlmsg_list *linfo = NULL;
struct nlmsg_list *ainfo = NULL;
@@ -669,7 +669,7 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
filter.flushp = 0;
filter.flushe = sizeof(flushb);
- while (round < MAX_ROUNDS) {
+ while (force || (round < MAX_ROUNDS)) {
if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
perror("Cannot send dump request");
exit(1);
@@ -696,7 +696,8 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
fflush(stdout);
}
}
- fprintf(stderr, "*** Flush remains incomplete after %d rounds. ***\n", MAX_ROUNDS); fflush(stderr);
+ fprintf(stderr, "*** Flush remains incomplete, bailing out after %d rounds (use forceflush to never give up).\n", MAX_ROUNDS);
+ fflush(stderr);
return 1;
}
@@ -790,7 +791,7 @@ int ipaddr_list_link(int argc, char **argv)
{
preferred_family = AF_PACKET;
do_link = 1;
- return ipaddr_list_or_flush(argc, argv, 0);
+ return ipaddr_list_or_flush(argc, argv, 0, 0);
}
void ipaddr_reset_filter(int oneline)
@@ -1006,7 +1007,7 @@ int ipaddr_modify(int cmd, int flags, int argc, char **argv)
int do_ipaddr(int argc, char **argv)
{
if (argc < 1)
- return ipaddr_list_or_flush(0, NULL, 0);
+ return ipaddr_list_or_flush(0, NULL, 0, 0);
if (matches(*argv, "add") == 0)
return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
if (matches(*argv, "change") == 0 ||
@@ -1018,9 +1019,11 @@ int do_ipaddr(int argc, char **argv)
return ipaddr_modify(RTM_DELADDR, 0, argc-1, argv+1);
if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
|| matches(*argv, "lst") == 0)
- return ipaddr_list_or_flush(argc-1, argv+1, 0);
+ return ipaddr_list_or_flush(argc-1, argv+1, 0, 0);
if (matches(*argv, "flush") == 0)
- return ipaddr_list_or_flush(argc-1, argv+1, 1);
+ return ipaddr_list_or_flush(argc-1, argv+1, 1, 0);
+ if (matches(*argv, "forceflush") == 0)
+ return ipaddr_list_or_flush(argc-1, argv+1, 1, 1);
if (matches(*argv, "help") == 0)
usage();
fprintf(stderr, "Command \"%s\" is unknown, try \"ip address help\".\n", *argv);
diff --git a/ip/ipneigh.c b/ip/ipneigh.c
index db684f5..6a99377 100644
--- a/ip/ipneigh.c
+++ b/ip/ipneigh.c
@@ -52,7 +52,7 @@ static void usage(void)
fprintf(stderr, "Usage: ip neigh { add | del | change | replace } { ADDR [ lladdr LLADDR ]\n"
" [ nud { permanent | noarp | stale | reachable } ]\n"
" | proxy ADDR } [ dev DEV ]\n");
- fprintf(stderr, " ip neigh {show|flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n");
+ fprintf(stderr, " ip neigh {show|flush|forceflush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n");
exit(-1);
}
@@ -317,7 +317,7 @@ void ipneigh_reset_filter()
filter.state = ~0;
}
-int do_show_or_flush(int argc, char **argv, int flush)
+int do_show_or_flush(int argc, char **argv, int flush, int force)
{
char *filter_dev = NULL;
int state_given = 0;
@@ -392,7 +392,7 @@ int do_show_or_flush(int argc, char **argv, int flush)
filter.flushe = sizeof(flushb);
filter.state &= ~NUD_FAILED;
- while (round < MAX_ROUNDS) {
+ while (force || (round < MAX_ROUNDS)) {
if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) {
perror("Cannot send dump request");
exit(1);
@@ -418,8 +418,9 @@ int do_show_or_flush(int argc, char **argv, int flush)
fflush(stdout);
}
}
- printf("*** Flush not complete bailing out after %d rounds\n",
- MAX_ROUNDS);
+ fprintf(stderr, "*** Flush remains incomplete, bailing out after %d rounds (use forceflush to never give up).\n", MAX_ROUNDS);
+ fflush(stderr);
+
return 1;
}
@@ -455,13 +456,15 @@ int do_ipneigh(int argc, char **argv)
if (matches(*argv, "show") == 0 ||
matches(*argv, "lst") == 0 ||
matches(*argv, "list") == 0)
- return do_show_or_flush(argc-1, argv+1, 0);
+ return do_show_or_flush(argc-1, argv+1, 0, 0);
if (matches(*argv, "flush") == 0)
- return do_show_or_flush(argc-1, argv+1, 1);
+ return do_show_or_flush(argc-1, argv+1, 1, 0);
+ if (matches(*argv, "forceflush") == 0)
+ return do_show_or_flush(argc-1, argv+1, 1, 1);
if (matches(*argv, "help") == 0)
usage();
} else
- return do_show_or_flush(0, NULL, 0);
+ return do_show_or_flush(0, NULL, 0, 0);
fprintf(stderr, "Command \"%s\" is unknown, try \"ip neigh help\".\n", *argv);
exit(-1);
--
Regards,
Andreas Henriksson
^ permalink raw reply related
* Re: Regression in current git - Network Manager fails (bisected)
From: Dan Williams @ 2007-10-23 14:10 UTC (permalink / raw)
To: Thomas Graf
Cc: Denis V. Lunev, netdev, Denis V. Lunev, David S. Miller,
Alexey Kuznetsov
In-Reply-To: <20071023133823.GB8125@postel.suug.ch>
On Tue, 2007-10-23 at 15:38 +0200, Thomas Graf wrote:
> * Denis V. Lunev <den@sw.ru> 2007-10-23 17:09
> > I have reproduced the problem with one-line test.
> > ./nl-route-get 192.168.1.1
> > The problem is with this message:
> >
> > -- Debug: Sent Message:
> > -------------------------- BEGIN NETLINK MESSAGE
> > ---------------------------
> > [HEADER] 16 octets
> > .nlmsg_len = 20
> > .nlmsg_type = 18 <route/link>
> > .nlmsg_flags = 773 <REQUEST,ACK,ROOT,MATCH>
> > .nlmsg_seq = 1193143772
> > .nlmsg_pid = 8233
> > [PAYLOAD] 16 octets
> > 00 1d fa 20 00 00 00 00 81 0e 02 00 00 00 00 00 ... ............
> > --------------------------- END NETLINK MESSAGE
> > ---------------------------
> > it starts dump and requests ACK.
>
> libnl sets the ACK bit for all requests unless the application
> disables this behaviour.
Should I make NM disable ACKs for now until it gets fixed?
Dan
^ permalink raw reply
* Re: iproute2: git pull request from debian repo.
From: Andreas Henriksson @ 2007-10-23 14:18 UTC (permalink / raw)
To: Patrick McHardy; +Cc: maximilian attems, shemminger, netdev
In-Reply-To: <471B90E5.1040108@trash.net>
On Sun, Oct 21, 2007 at 07:48:21PM +0200, Patrick McHardy wrote:
> Andreas Henriksson wrote:
> >- for (;;) {
> >+ while (round < MAX_ROUNDS) {
> > if (rtnl_wilddump_request(&rth, filter.family,
> > RTM_GETADDR) < 0) {
> > perror("Cannot send dump request");
> > exit(1);
> >@@ -694,6 +696,8 @@ int ipaddr_list_or_flush(int argc, char **argv, int
> >flush)
> > fflush(stdout);
> > }
> > }
> >+ fprintf(stderr, "*** Flush remains incomplete after %d
> >rounds. ***\n", MAX_ROUNDS); fflush(stderr);
>
>
> Again, please make this optional. People might want to make
> sure the cache is flushed even if it takes a bit longer.
And here's the alternativ with configurable max rounds (0=infinite)...
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index ff9e318..e4ba340 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -67,6 +67,7 @@ static void usage(void)
fprintf(stderr, " ip addr del IFADDR dev STRING\n");
fprintf(stderr, " ip addr {show|flush} [ dev STRING ] [ scope SCOPE-ID ]\n");
fprintf(stderr, " [ to PREFIX ] [ FLAG-LIST ] [ label PATTERN ]\n");
+ fprintf(stderr, " [ maxrounds N ]\n");
fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n");
fprintf(stderr, " [ broadcast ADDR ] [ anycast ADDR ]\n");
fprintf(stderr, " [ label STRING ] [ scope SCOPE-ID ]\n");
@@ -566,6 +567,7 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
struct nlmsg_list *l, *n;
char *filter_dev = NULL;
int no_link = 0;
+ int maxrounds = MAX_ROUNDS;
ipaddr_reset_filter(oneline);
filter.showqueue = 1;
@@ -630,6 +632,9 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
} else if (strcmp(*argv, "label") == 0) {
NEXT_ARG();
filter.label = *argv;
+ } else if (strcmp(*argv, "maxrounds") == 0) {
+ NEXT_ARG();
+ maxrounds = atoi(*argv);
} else {
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
@@ -669,7 +674,7 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
filter.flushp = 0;
filter.flushe = sizeof(flushb);
- while (round < MAX_ROUNDS) {
+ while ((maxrounds == 0) || (round < maxrounds)) {
if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
perror("Cannot send dump request");
exit(1);
@@ -696,7 +701,10 @@ int ipaddr_list_or_flush(int argc, char **argv, int flush)
fflush(stdout);
}
}
- fprintf(stderr, "*** Flush remains incomplete after %d rounds. ***\n", MAX_ROUNDS); fflush(stderr);
+ fprintf(stderr,
+ "*** Flush remains incomplete after %d rounds. ***\n",
+ maxrounds);
+ fflush(stderr);
return 1;
}
diff --git a/ip/ipneigh.c b/ip/ipneigh.c
index db684f5..5a9ce40 100644
--- a/ip/ipneigh.c
+++ b/ip/ipneigh.c
@@ -53,6 +53,7 @@ static void usage(void)
" [ nud { permanent | noarp | stale | reachable } ]\n"
" | proxy ADDR } [ dev DEV ]\n");
fprintf(stderr, " ip neigh {show|flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n");
+ fprintf(stderr, " [ maxrounds N ]\n");
exit(-1);
}
@@ -321,6 +322,7 @@ int do_show_or_flush(int argc, char **argv, int flush)
{
char *filter_dev = NULL;
int state_given = 0;
+ int maxrounds = MAX_ROUNDS;
ipneigh_reset_filter();
@@ -361,6 +363,9 @@ int do_show_or_flush(int argc, char **argv, int flush)
if (state == 0)
state = 0x100;
filter.state |= state;
+ } else if (strcmp(*argv, "maxrounds") == 0) {
+ NEXT_ARG();
+ maxrounds = atoi(*argv);
} else {
if (strcmp(*argv, "to") == 0) {
NEXT_ARG();
@@ -392,7 +397,7 @@ int do_show_or_flush(int argc, char **argv, int flush)
filter.flushe = sizeof(flushb);
filter.state &= ~NUD_FAILED;
- while (round < MAX_ROUNDS) {
+ while ((maxrounds == 0) || (round < maxrounds)) {
if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) {
perror("Cannot send dump request");
exit(1);
@@ -418,8 +423,10 @@ int do_show_or_flush(int argc, char **argv, int flush)
fflush(stdout);
}
}
- printf("*** Flush not complete bailing out after %d rounds\n",
- MAX_ROUNDS);
+ fprintf(stderr,
+ "*** Flush remains incomplete after %d rounds. ***\n",
+ maxrounds);
+ fflush(stderr);
return 1;
}
--
Regards,
Andreas Henriksson
^ permalink raw reply related
* [PATCH RESEND] ip_gre: sendto/recvfrom NBMA address
From: Timo Teräs @ 2007-10-23 14:31 UTC (permalink / raw)
To: davem, netdev; +Cc: kuznet
When GRE tunnel is in NBMA mode, this patch allows an application to use
a PF_PACKET socket to:
- send a packet to specific NBMA address with sendto()
- use recvfrom() to receive packet and check which NBMA address it came from
Signed-off-by: Timo Teras <timo.teras@iki.fi>
---
This is useful for implementing NHRP (and other low level protocols) over
NBMA GRE tunnels.
Posted about a week ago, but got no reply. Could someone comment on this?
Or commit it? Thanks.
net/ipv4/ip_gre.c | 12 +++++++++++-
1 files changed, 11 insertions(+), 1 deletions(-)
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index f151900..b1e3816 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -1033,7 +1033,6 @@ static int ipgre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
-#ifdef CONFIG_NET_IPGRE_BROADCAST
/* Nice toy. Unfortunately, useless in real life :-)
It allows to construct virtual multiprotocol broadcast "LAN"
over the Internet, provided multicast routing is tuned.
@@ -1092,10 +1091,19 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
return -t->hlen;
}
+static int ipgre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+{
+ struct iphdr *iph = (struct iphdr *) skb_mac_header(skb);
+ memcpy(haddr, &iph->saddr, 4);
+ return 4;
+}
+
static const struct header_ops ipgre_header_ops = {
.create = ipgre_header,
+ .parse = ipgre_header_parse,
};
+#ifdef CONFIG_NET_IPGRE_BROADCAST
static int ipgre_open(struct net_device *dev)
{
struct ip_tunnel *t = netdev_priv(dev);
@@ -1197,6 +1205,8 @@ static int ipgre_tunnel_init(struct net_device *dev)
dev->stop = ipgre_close;
}
#endif
+ } else {
+ dev->header_ops = &ipgre_header_ops;
}
if (!tdev && tunnel->parms.link)
^ permalink raw reply related
* Re: [PATCH RESEND] ip_gre: sendto/recvfrom NBMA address
From: Patrick McHardy @ 2007-10-23 14:33 UTC (permalink / raw)
To: Timo Teräs; +Cc: davem, netdev, kuznet
In-Reply-To: <471E05BD.7040407@iki.fi>
Timo Teräs wrote:
> When GRE tunnel is in NBMA mode, this patch allows an application to use
> a PF_PACKET socket to:
> - send a packet to specific NBMA address with sendto()
> - use recvfrom() to receive packet and check which NBMA address it came
> from
>
> Signed-off-by: Timo Teras <timo.teras@iki.fi>
>
> ---
>
> This is useful for implementing NHRP (and other low level protocols) over
> NBMA GRE tunnels.
>
> Posted about a week ago, but got no reply. Could someone comment on this?
> Or commit it? Thanks.
Your mailer mangled tabs, it won't apply like this. Try sending to
yourself for testing, then repost when it works properly.
^ permalink raw reply
* [PATCH] fix ACK processing after netlink_dump_start
From: Denis V. Lunev @ 2007-10-23 14:40 UTC (permalink / raw)
To: dcbw, tgraf, jfannin, davem; +Cc: netdev, kuznet
Revert to original netlink behavior. Do not reply with ACK if the
netlink dump has bees successfully started.
libnl has been broken by the cd40b7d3983c708aabe3d3008ec64ffce56d33b0
The following command reproduce the problem:
/nl-route-get 192.168.1.1
Signed-off-by: Denis V. Lunev <den@openvz.org>
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 98e313e..44a8b41 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1565,7 +1565,10 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
netlink_dump(sk);
sock_put(sk);
- return 0;
+
+ /* We successfully started a dump, by returning -EINTR we
+ * signal not to send ACK even if it was requested */
+ return -EINTR;
}
void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
@@ -1619,17 +1622,21 @@ int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
/* Only requests are handled by the kernel */
if (!(nlh->nlmsg_flags & NLM_F_REQUEST))
- goto skip;
+ goto ack;
/* Skip control messages */
if (nlh->nlmsg_type < NLMSG_MIN_TYPE)
- goto skip;
+ goto ack;
err = cb(skb, nlh);
-skip:
+ if (err == -EINTR)
+ goto skip;
+
+ack:
if (nlh->nlmsg_flags & NLM_F_ACK || err)
netlink_ack(skb, nlh, err);
+skip:
msglen = NLMSG_ALIGN(nlh->nlmsg_len);
if (msglen > skb->len)
msglen = skb->len;
^ permalink raw reply related
* Re: iproute2: git pull request from debian repo.
From: Patrick McHardy @ 2007-10-23 14:38 UTC (permalink / raw)
To: Andreas Henriksson; +Cc: maximilian attems, shemminger, netdev
In-Reply-To: <20071023141838.GA25201@scream.fatal.se>
Andreas Henriksson wrote:
> On Sun, Oct 21, 2007 at 07:48:21PM +0200, Patrick McHardy wrote:
>> Andreas Henriksson wrote:
>>> - for (;;) {
>>> + while (round < MAX_ROUNDS) {
>>> if (rtnl_wilddump_request(&rth, filter.family,
>>> RTM_GETADDR) < 0) {
>>> perror("Cannot send dump request");
>>> exit(1);
>>> @@ -694,6 +696,8 @@ int ipaddr_list_or_flush(int argc, char **argv, int
>>> flush)
>>> fflush(stdout);
>>> }
>>> }
>>> + fprintf(stderr, "*** Flush remains incomplete after %d
>>> rounds. ***\n", MAX_ROUNDS); fflush(stderr);
>>
>> Again, please make this optional. People might want to make
>> sure the cache is flushed even if it takes a bit longer.
>
> And here's the alternativ with configurable max rounds (0=infinite)...
Almost - the default behaviour shouldn't change IMO. Also please
send a complete patch, not one on top of your old one.
>
> - while (round < MAX_ROUNDS) {
> + while ((maxrounds == 0) || (round < maxrounds)) {
And please remove the unnecessary parens.
^ permalink raw reply
* Re: iproute2: git pull request from debian repo.
From: Andreas Henriksson @ 2007-10-23 15:01 UTC (permalink / raw)
To: Patrick McHardy; +Cc: maximilian attems, shemminger, netdev
In-Reply-To: <471E074E.8050805@trash.net>
On Tue, Oct 23, 2007 at 04:38:06PM +0200, Patrick McHardy wrote:
> Almost - the default behaviour shouldn't change IMO. Also please
> send a complete patch, not one on top of your old one.
Which "default behaviour"? ip addr flush or ip neigh flush behaviour?
Both of those should behave the same IMHO.
Defaulting to never bail out is causing problems in a bunch of firewalling
frontends which has been reported multiple times to both debian and ubuntu
(and possibly others I don't know about), and looping forever causes the
bootup to never finish.
Thats my reason for advocating not selecting the "loop forever" as the
default choice.
--
Regards,
Andreas Henriksson
^ permalink raw reply
* Re: iproute2: git pull request from debian repo.
From: Patrick McHardy @ 2007-10-23 15:08 UTC (permalink / raw)
To: Andreas Henriksson; +Cc: maximilian attems, shemminger, netdev
In-Reply-To: <20071023150154.GA25314@scream.fatal.se>
Andreas Henriksson wrote:
> On Tue, Oct 23, 2007 at 04:38:06PM +0200, Patrick McHardy wrote:
>> Almost - the default behaviour shouldn't change IMO. Also please
>> send a complete patch, not one on top of your old one.
>
> Which "default behaviour"? ip addr flush or ip neigh flush behaviour?
> Both of those should behave the same IMHO.
In both cases your changed behaviour should be optional and it should
default to the old behaviour. Not actually flushing completely when the
user specified "flush" is counter-intuitive.
> Defaulting to never bail out is causing problems in a bunch of firewalling
> frontends which has been reported multiple times to both debian and ubuntu
> (and possibly others I don't know about), and looping forever causes the
> bootup to never finish.
> Thats my reason for advocating not selecting the "loop forever" as the
> default choice.
Then those frontends should add "maxrounds N" if they want to abort
before the flush is complete.
^ permalink raw reply
* [PATCH] LRO ack aggregation
From: Andrew Gallatin @ 2007-10-23 15:11 UTC (permalink / raw)
To: netdev; +Cc: ossthema
[-- Attachment #1: Type: text/plain, Size: 3466 bytes --]
Hi,
We recently did some performance comparisons between the new inet_lro
LRO support in the kernel, and our Myri10GE in-driver LRO.
For receive, we found they were nearly identical. However, for
transmit, we found that Myri10GE's LRO shows much lower CPU
utilization. We traced the CPU utilization difference to our driver
LRO aggregating TCP acks, and the inet_lro module not doing so.
I've attached a patch which adds support to inet_lro for aggregating
pure acks. Aggregating pure acks (segments with TCP_PAYLOAD_LENGTH ==
0) entails freeing the skb (or putting the page in the frags case).
The patch also handles trimming (typical for 54-byte pure ack frames
which have been padded to the ethernet minimum 60 byte frame size).
In the frags case, I tried to keep things simple by only doing the
trim when the entire frame fits in the first frag. To be safe, I
ensure that the padding is all 0 (or, more exactly, was some pattern
whose checksum is -0) so that it doesn't impact hardware checksums.
This patch also fixes a small bug in the skb LRO path dealing with
vlans that I found when doing my own testing. Specifically, in the
desc->active case, the existing code always fails the
lro_tcp_ip_check() for NICs without LRO_F_EXTRACT_VLAN_ID, because it
fails to subtract the vlan_hdr_len from the skb->len.
Jan-Bernd Themann (ossthema@de.ibm.com) has tested the patch using
the eHEA driver (skb codepath), and I have tested it using Myri10GE
(both frags and skb codepath).
Using a pair of identical low-end 2.0GHz Athlon 64 x2 3800+ with
Myri10GE 10GbE NICs, I ran 10 iterations of netperf TCP_SENDFILE
tests, taking the median run for comparison purposes. The receiver
was running Myri10GE + patched inet_lro:
TCP SENDFILE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to rome-my
(192.168.1.16) port 0 AF_INET : cpu bind
Recv Send Send Utilization Service
Demand
Socket Socket Message Elapsed Send Recv Send Recv
Size Size Size Time Throughput local remote local
remote
bytes bytes bytes secs. 10^6bits/s % S % S us/KB us/KB
Myri10GE driver-specific LRO:
87380 65536 65536 60.02 9442.65 16.24 69.31 0.282 1.203
Myri10GE + unpatched inet_lro:
87380 65536 65536 60.02 9442.88 20.10 69.11 0.349 1.199
Myri10GE + patched inet_lro:
87380 65536 65536 60.02 9443.30 16.95 68.97 0.294 1.197
The important bit here is the sender's CPU utilization, and service
demand (cost per byte). As you can see, without aggregating ack's,
the overhead on the sender is roughly 20% higher, even when sending to
a receiver which uses LRO. The differences are even more dramatic
when sending to a receiver which does not use LRO (and hence sends
more frequent acks).
Below is the same benchmark, run between a pair of 4-way 3.0GHz Xeon
5160 machines (Dell 2950) with Myri10GE NICs. The receiver is running
Solaris 10U4, which does not do LRO, and is acking at approximately
8:1 (or ~100K acks/sec):
Myri10GE driver-specific LRO:
196712 65536 65536 60.01 9280.09 7.14 45.37 0.252 1.602
Myri10GE + unpatched inet_lro:
196712 65536 65536 60.01 8530.80 10.51 44.60 0.404 1.713
Myri10GE + patched inet_lro:
196712 65536 65536 60.00 9249.65 7.21 45.90 0.255 1.626
Signed off by: Andrew Gallatin <gallatin@myri.com>
Andrew Gallatin
Myricom Inc.
[-- Attachment #2: ack_aggr.diff --]
[-- Type: text/plain, Size: 3789 bytes --]
diff --git a/net/ipv4/inet_lro.c b/net/ipv4/inet_lro.c
index ac3b1d3..eba145b 100644
--- a/net/ipv4/inet_lro.c
+++ b/net/ipv4/inet_lro.c
@@ -58,9 +58,6 @@ static int lro_tcp_ip_check(struct iphdr
if (ntohs(iph->tot_len) != len)
return -1;
- if (TCP_PAYLOAD_LENGTH(iph, tcph) == 0)
- return -1;
-
if (iph->ihl != IPH_LEN_WO_OPTIONS)
return -1;
@@ -223,6 +220,11 @@ static void lro_add_packet(struct net_lr
lro_add_common(lro_desc, iph, tcph, tcp_data_len);
+ if (tcp_data_len == 0) {
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
skb_pull(skb, (skb->len - tcp_data_len));
parent->truesize += skb->truesize;
@@ -244,6 +246,11 @@ static void lro_add_frags(struct net_lro
lro_add_common(lro_desc, iph, tcph, tcp_data_len);
+ if (tcp_data_len == 0) {
+ put_page(skb_frags[0].page);
+ return;
+ }
+
skb->truesize += truesize;
skb_frags[0].page_offset += hlen;
@@ -338,6 +345,8 @@ static int __lro_proc_skb(struct net_lro
struct tcphdr *tcph;
u64 flags;
int vlan_hdr_len = 0;
+ int pkt_len;
+ int trim;
if (!lro_mgr->get_skb_header
|| lro_mgr->get_skb_header(skb, (void *)&iph, (void *)&tcph,
@@ -355,6 +364,17 @@ static int __lro_proc_skb(struct net_lro
&& !test_bit(LRO_F_EXTRACT_VLAN_ID, &lro_mgr->features))
vlan_hdr_len = VLAN_HLEN;
+ /* strip padding from runts iff the padding is all zero */
+ pkt_len = vlan_hdr_len + ntohs(iph->tot_len);
+ trim = skb->len - pkt_len;
+ if (trim > 0) {
+ u8 *pad = skb_tail_pointer(skb) - trim;
+ if (unlikely(ip_compute_csum(pad, trim) != 0xffff)) {
+ goto out;
+ }
+ skb_trim(skb, skb->len - trim);
+ }
+
if (!lro_desc->active) { /* start new lro session */
if (lro_tcp_ip_check(iph, tcph, skb->len - vlan_hdr_len, NULL))
goto out;
@@ -368,7 +388,7 @@ static int __lro_proc_skb(struct net_lro
if (lro_desc->tcp_next_seq != ntohl(tcph->seq))
goto out2;
- if (lro_tcp_ip_check(iph, tcph, skb->len, lro_desc))
+ if (lro_tcp_ip_check(iph, tcph, skb->len - vlan_hdr_len, lro_desc))
goto out2;
lro_add_packet(lro_desc, skb, iph, tcph);
@@ -412,18 +432,20 @@ static struct sk_buff *lro_gen_skb(struc
memcpy(skb->data, mac_hdr, hdr_len);
- skb_frags = skb_shinfo(skb)->frags;
- while (data_len > 0) {
- *skb_frags = *frags;
- data_len -= frags->size;
- skb_frags++;
- frags++;
- skb_shinfo(skb)->nr_frags++;
+ if (skb->data_len == 0) {
+ put_page(frags[0].page);
+ } else {
+ skb_frags = skb_shinfo(skb)->frags;
+ while (data_len > 0) {
+ *skb_frags = *frags;
+ data_len -= frags->size;
+ skb_frags++;
+ frags++;
+ skb_shinfo(skb)->nr_frags++;
+ }
+ skb_shinfo(skb)->frags[0].page_offset += hdr_len;
+ skb_shinfo(skb)->frags[0].size -= hdr_len;
}
-
- skb_shinfo(skb)->frags[0].page_offset += hdr_len;
- skb_shinfo(skb)->frags[0].size -= hdr_len;
-
skb->ip_summed = ip_summed;
skb->csum = sum;
skb->protocol = eth_type_trans(skb, lro_mgr->dev);
@@ -445,6 +467,8 @@ static struct sk_buff *__lro_proc_segmen
int mac_hdr_len;
int hdr_len = LRO_MAX_PG_HLEN;
int vlan_hdr_len = 0;
+ int pkt_len;
+ int trim;
if (!lro_mgr->get_frag_header
|| lro_mgr->get_frag_header(frags, (void *)&mac_hdr, (void *)&iph,
@@ -463,6 +487,19 @@ static struct sk_buff *__lro_proc_segmen
if (!lro_desc)
goto out1;
+ /* strip padding from runts iff the padding is all zero */
+ pkt_len = mac_hdr_len + ntohs(iph->tot_len);
+ trim = len - pkt_len;
+ if (trim > 0 && pkt_len <= frags->size) {
+ u8 *pad = page_address(frags->page) + frags->page_offset +
+ pkt_len;
+ if (unlikely(ip_compute_csum(pad, trim) != 0xffff))
+ goto out1;
+ frags->size -= trim;
+ len -= trim;
+ true_size -= trim;
+ }
+
if (!lro_desc->active) { /* start new lro session */
if (lro_tcp_ip_check(iph, tcph, len - mac_hdr_len, NULL))
goto out1;
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox