diff options
Diffstat (limited to 'drivers/net')
73 files changed, 10796 insertions, 4385 deletions
diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c index dab3f99b9..a44153e31 100644 --- a/drivers/net/3c527.c +++ b/drivers/net/3c527.c @@ -2,6 +2,7 @@ * * (c) Copyright 1998 Red Hat Software Inc * Written by Alan Cox. + * Further debugging by Carl Drougge. * * Based on skeleton.c written 1993-94 by Donald Becker and ne2.c * (for the MCA stuff) written by Wim Dumon. @@ -15,7 +16,7 @@ */ static const char *version = - "3c527.c:v0.07 2000/01/18 Alan Cox (alan@redhat.com)\n"; + "3c527.c:v0.08 2000/02/22 Alan Cox (alan@redhat.com)\n"; /** * DOC: Traps for the unwary @@ -122,6 +123,7 @@ struct mc32_local u32 base; u16 rx_halted; u16 tx_halted; + u16 rx_pending; u16 exec_pending; u16 mc_reload_wait; /* a multicast load request is pending */ atomic_t tx_count; /* buffers left */ @@ -451,6 +453,9 @@ static int __init mc32_probe1(struct net_device *dev, int slot) printk("%s: %d RX buffers, %d TX buffers. Base of 0x%08X.\n", dev->name, lp->rx_len, lp->tx_len, lp->base); + + if(lp->tx_len > TX_RING_MAX) + lp->tx_len = TX_RING_MAX; dev->open = mc32_open; dev->stop = mc32_close; @@ -462,6 +467,7 @@ static int __init mc32_probe1(struct net_device *dev, int slot) lp->rx_halted = 1; lp->tx_halted = 1; + lp->rx_pending = 0; /* Fill in the fields of the device structure with ethernet values. */ ether_setup(dev); @@ -652,6 +658,7 @@ static void mc32_rx_begin(struct net_device *dev) mc32_ring_poll(dev); lp->rx_halted=0; + lp->rx_pending=0; } /** @@ -944,6 +951,7 @@ static void mc32_timeout(struct net_device *dev) static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev) { struct mc32_local *lp = (struct mc32_local *)dev->priv; + int ioaddr = dev->base_addr; unsigned long flags; u16 tx_head; @@ -967,7 +975,16 @@ static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev) lp->tx_skb[lp->tx_skb_end] = skb; lp->tx_skb_end++; lp->tx_skb_end&=(TX_RING_MAX-1); + + /* TX suspend - shouldnt be needed but apparently is. + This is a research item ... */ + + while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); + lp->tx_box->mbox=0; + outb(3, ioaddr+HOST_CMD); + /* Transmit now stopped */ + /* P is the last sending/sent buffer as a pointer */ p=(struct skb_header *)bus_to_virt(lp->base+tx_head); @@ -990,7 +1007,9 @@ static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev) p->status = 0; p->control &= ~(1<<6); + while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); lp->tx_box->mbox=0; + outb(5, ioaddr+HOST_CMD); /* Restart TX */ restore_flags(flags); netif_wake_queue(dev); @@ -1096,11 +1115,16 @@ static void mc32_rx_ring(struct net_device *dev) base = p->next; } while(x++<48); + + /* + * Restart ring processing + */ while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); lp->rx_box->mbox=0; lp->rx_box->data[0] = top; outb(1<<3, ioaddr+HOST_CMD); + lp->rx_halted=0; } @@ -1123,7 +1147,6 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs) struct net_device *dev = dev_id; struct mc32_local *lp; int ioaddr, status, boguscount = 0; - int rx_event = 0; if (dev == NULL) { printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq); @@ -1182,7 +1205,15 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs) case 0: break; case 2: /* RX */ - rx_event=1; + lp->rx_pending=1; + if(!lp->rx_halted) + { + /* + * Halt ring receive + */ + while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)); + outb(3<<3, ioaddr+HOST_CMD); + } break; case 3: case 4: @@ -1195,9 +1226,10 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs) break; case 6: /* Out of RX buffers stat */ - /* Must restart */ lp->net_stats.rx_dropped++; - rx_event = 1; /* To restart */ + lp->rx_pending=1; + /* Must restart */ + lp->rx_halted=1; break; default: printk("%s: strange rx ack %d\n", @@ -1231,11 +1263,17 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs) } /* - * Process and restart the receive ring. + * Process and restart the receive ring. This has some state + * as we must halt the ring to process it and halting the ring + * might not occur in the same IRQ handling loop as we issue + * the halt. */ - if(rx_event) + if(lp->rx_pending && lp->rx_halted) + { mc32_rx_ring(dev); + lp->rx_pending = 0; + } return; } diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 839c15ebc..31e8c79b8 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -1914,7 +1914,7 @@ static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f); return 0; case SIOCDEVPRIVATE+2: /* Write the specified MII register */ - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EPERM; EL3WINDOW(4); mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]); diff --git a/drivers/net/Config.in b/drivers/net/Config.in index b264def30..b62986f83 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -199,11 +199,13 @@ if [ "$CONFIG_FDDI" = "y" ]; then fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI - if [ "$CONFIG_HIPPI" = "y" ]; then - tristate ' Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER - if [ "$CONFIG_ROADRUNNER" != "n" ]; then - bool ' Use large TX/RX rings' CONFIG_ROADRUNNER_LARGE_RINGS + if [ "$CONFIG_INET" = "y" ]; then + bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI + if [ "$CONFIG_HIPPI" = "y" ]; then + tristate ' Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER + if [ "$CONFIG_ROADRUNNER" != "n" ]; then + bool ' Use large TX/RX rings' CONFIG_ROADRUNNER_LARGE_RINGS + fi fi fi fi diff --git a/drivers/net/aironet4500.h b/drivers/net/aironet4500.h index 45e3bc044..844f0ba03 100644 --- a/drivers/net/aironet4500.h +++ b/drivers/net/aironet4500.h @@ -454,7 +454,7 @@ struct awc_fid_queue { }; -extern void +extern __inline__ void awc_fid_queue_init(struct awc_fid_queue * queue){ unsigned long flags; diff --git a/drivers/net/appletalk/Config.in b/drivers/net/appletalk/Config.in index 951cf498d..e42231cbf 100644 --- a/drivers/net/appletalk/Config.in +++ b/drivers/net/appletalk/Config.in @@ -3,18 +3,21 @@ # if [ "$CONFIG_ATALK" != "n" ]; then - mainmenu_option next_comment - comment 'Appletalk devices' - dep_tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_ATALK - dep_tristate 'COPS LocalTalk PC support' CONFIG_COPS $CONFIG_ATALK - if [ "$CONFIG_COPS" != "n" ]; then - bool ' Dayna firmware support' CONFIG_COPS_DAYNA - bool ' Tangent firmware support' CONFIG_COPS_TANGENT - fi - dep_tristate 'Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_ATALK - if [ "$CONFIG_IPDDP" != "n" ]; then - bool ' IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP - bool ' Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP - fi - endmenu + mainmenu_option next_comment + comment 'Appletalk devices' + bool 'Appletalk interfaces support' CONFIG_APPLETALK + if [ "$CONFIG_APPLETALK" != "n" ]; then + dep_tristate ' Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_APPLETALK + dep_tristate ' COPS LocalTalk PC support' CONFIG_COPS $CONFIG_APPLETALK + if [ "$CONFIG_COPS" != "n" ]; then + bool ' Dayna firmware support' CONFIG_COPS_DAYNA + bool ' Tangent firmware support' CONFIG_COPS_TANGENT + fi + dep_tristate ' Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_APPLETALK + if [ "$CONFIG_IPDDP" != "n" ]; then + bool ' IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP + bool ' Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP + fi + fi + endmenu fi diff --git a/drivers/net/bsd_comp.c b/drivers/net/bsd_comp.c index 073a7392a..493c2f6c5 100644 --- a/drivers/net/bsd_comp.c +++ b/drivers/net/bsd_comp.c @@ -39,7 +39,7 @@ /* * This version is for use with contiguous buffers on Linux-derived systems. * - * ==FILEVERSION 970607== + * ==FILEVERSION 20000226== * * NOTE TO MAINTAINERS: * If you modify this file at all, please set the number above to the @@ -58,32 +58,9 @@ #endif #include <linux/module.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/types.h> -#include <linux/fcntl.h> -#include <linux/interrupt.h> -#include <linux/ptrace.h> -#include <linux/ioport.h> -#include <linux/in.h> +#include <linux/init.h> #include <linux/malloc.h> #include <linux/vmalloc.h> -#include <linux/tty.h> -#include <linux/errno.h> -#include <linux/string.h> /* used in new tty drivers */ -#include <linux/signal.h> /* used in new tty drivers */ - -#include <asm/system.h> -#include <asm/bitops.h> -#include <asm/byteorder.h> - -#include <linux/if.h> - -#include <linux/if_ether.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <linux/inet.h> -#include <linux/ioctl.h> #include <linux/ppp_defs.h> @@ -1177,17 +1154,18 @@ static struct compressor ppp_bsd_compress = { * Module support routines *************************************************************/ -int -init_module(void) +int bsdcomp_init(void) { - int answer = ppp_register_compressor (&ppp_bsd_compress); + int answer = ppp_register_compressor(&ppp_bsd_compress); if (answer == 0) - printk (KERN_INFO "PPP BSD Compression module registered\n"); + printk(KERN_INFO "PPP BSD Compression module registered\n"); return answer; } -void -cleanup_module(void) +void bsdcomp_cleanup(void) { - ppp_unregister_compressor (&ppp_bsd_compress); + ppp_unregister_compressor(&ppp_bsd_compress); } + +module_init(bsdcomp_init); +module_exit(bsdcomp_cleanup); diff --git a/drivers/net/dgrs.c b/drivers/net/dgrs.c index b42b8d390..5a5f3cc62 100644 --- a/drivers/net/dgrs.c +++ b/drivers/net/dgrs.c @@ -852,6 +852,8 @@ static int dgrs_ioctl(struct net_device *devN, struct ifreq *ifr, int cmd) return -EFAULT; return (0); case DGRS_SETFILTER: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; if (ioc.port > privN->bcomm->bc_nports) return -EINVAL; if (ioc.filter >= NFILTERS) @@ -1188,8 +1190,11 @@ dgrs_probe1(struct net_device *dev) priv->intrcnt = 0; for (i = jiffies + 2*HZ + HZ/2; time_after(i, jiffies); ) + { + barrier(); /* gcc 2.95 needs this */ if (priv->intrcnt >= 2) break; + } if (priv->intrcnt < 2) { printk("%s: Not interrupting on IRQ %d (%d)\n", diff --git a/drivers/net/dmfe.c b/drivers/net/dmfe.c index 01a32afd5..87e5ccd7f 100644 --- a/drivers/net/dmfe.c +++ b/drivers/net/dmfe.c @@ -1,15 +1,26 @@ /* - dmfe.c: Version 1.26 + dmfe.c: Version 1.28 01/18/2000 - A Davicom DM9102 fast ethernet driver for Linux. + A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux. + Copyright (C) 1997 Sten Wang + + 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. - 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, version 1. Compiler command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall - -Wstrict-prototypes -O6 -c dmfe.c" + -Wstrict-prototypes -O6 -c dmfe.c" + OR + "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net -Wall + -Wstrict-prototypes -O6 -c dmfe.c" The following steps teach you how to active DM9102 board: 1. Used the upper compiler command to compile dmfe.c @@ -25,7 +36,7 @@ "route add -net 172.22.3.0 eth0" 5. Well done. Your DM9102 adapter actived now. - Author: Sten Wang, E-mail: sten_wang@davicom.com.tw + Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw Date: 10/28,1998 @@ -44,6 +55,9 @@ Check and fix on 64bit and big endian boxes. Sort out the PCI latency. + (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. + + Cleaned up for kernel merge by Alan Cox (alan@redhat.com) */ #include <linux/module.h> @@ -60,38 +74,41 @@ #include <linux/pci.h> #include <linux/init.h> #include <linux/version.h> - +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> #include <linux/delay.h> + #include <asm/processor.h> #include <asm/bitops.h> #include <asm/io.h> #include <asm/dma.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> /* Board/System/Debug information/definition ---------------- */ +#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */ #define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */ #define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */ #define DMFE_SUCC 0 #define DM9102_IO_SIZE 0x80 -#define TX_FREE_DESC_CNT 0x1 /* Tx packet count */ +#define DM9102A_IO_SIZE 0x100 +#define TX_FREE_DESC_CNT 0xc /* Tx packet count */ +#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */ #define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */ #define RX_DESC_CNT 0x10 /* Allocated Rx descriptors */ #define DESC_ALL_CNT TX_DESC_CNT+RX_DESC_CNT #define TX_BUF_ALLOC 0x600 #define RX_ALLOC_SIZE 0x620 #define DM910X_RESET 1 -#define CR6_DEFAULT 0x002c0000 /* SF, MII, HD */ +#define CR6_DEFAULT 0x00280000 /* SF, HD */ #define CR7_DEFAULT 0x1a2cd #define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */ #define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */ #define MAX_PACKET_SIZE 1514 #define DMFE_MAX_MULTICAST 14 -#define RX_MAX_TRAFFIC 0x5000 +#define RX_MAX_TRAFFIC 0x14000 #define MAX_CHECK_PACKET 0x8000 #define DMFE_10MHF 0 @@ -100,8 +117,8 @@ #define DMFE_100MFD 5 #define DMFE_AUTO 8 -#define DMFE_TIMER_WUT jiffies+HZ*1 /* timer wakeup time : 1 second */ -#define DMFE_TX_TIMEOUT HZ*2 /* tx packet time-out time */ +#define DMFE_TIMER_WUT jiffies+(HZ*2)/2 /* timer wakeup time : 1 second */ +#define DMFE_TX_TIMEOUT HZ*1.5 /* tx packet time-out time 1.5 s" */ #define DMFE_DBUG(dbug_now, msg, vaule) if (dmfe_debug || dbug_now) printk("DBUG: %s %x\n", msg, vaule) @@ -109,7 +126,7 @@ #define DELAY_1US udelay(1) /* udelay scale 1 usec */ -#define SHOW_MEDIA_TYPE(mode) printk("\n<WARN> Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half"); +#define SHOW_MEDIA_TYPE(mode) printk(KERN_WARNING "dmfe: Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half"); /* CR9 definition: SROM/MII */ @@ -125,6 +142,9 @@ #define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US; +#define CHK_IO_SIZE(pci_id, dev_rev) ( (pci_id==PCI_DM9132_ID) || (dev_rev >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE + + /* Structure/enum declaration ------------------------------- */ struct tx_desc { u32 tdes0, tdes1, tdes2, tdes3; @@ -149,13 +169,14 @@ struct dmfe_board_info { struct pci_dev *net_dev; /* PCI device */ - u32 ioaddr; /* I/O base address */ + unsigned long ioaddr; /* I/O base address */ + u32 cr0_data; u32 cr5_data; u32 cr6_data; u32 cr7_data; u32 cr15_data; - -/* descriptor pointer */ + + /* descriptor pointer */ unsigned char *buf_pool_ptr; /* Tx buffer pool memory */ unsigned char *buf_pool_start; /* Tx buffer pool align dword */ unsigned char *desc_pool_ptr; /* descriptor pool memory */ @@ -166,9 +187,12 @@ struct dmfe_board_info { struct rx_desc *rx_insert_ptr; struct rx_desc *rx_ready_ptr; /* packet come pointer */ u32 tx_packet_cnt; /* transmitted packet count */ + u32 tx_queue_cnt; /* wait to send packet count */ u32 rx_avail_cnt; /* available rx descriptor count */ u32 interval_rx_cnt; /* rx packet count a callback time */ + u16 phy_id2; /* Phyxcer ID2 */ + u8 media_mode; /* user specify media mode */ u8 op_mode; /* real work media mode */ u8 phy_addr; @@ -190,14 +214,14 @@ enum dmfe_offsets { enum dmfe_CR6_bits { CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, CR6_FDM = 0x200, - CR6_TXSC = 0x2000, CR6_STI = 0x100000, CR6_SFT = 0x200000, CR6_RXA = 0x40000000 + CR6_TXSC = 0x2000, CR6_STI = 0x100000, CR6_SFT = 0x200000, CR6_RXA = 0x40000000, + CR6_NO_PURGE = 0x20000000 }; /* Global variable declaration ----------------------------- */ - static int dmfe_debug = 0; static unsigned char dmfe_media_mode = 8; -static struct net_device *dmfe_root_dev = NULL; /* First device */ +static struct net_device *dmfe_root_dev = NULL; /* First device */ static u32 dmfe_cr6_user_set = 0; /* For module input parameter */ @@ -206,7 +230,7 @@ static u32 cr6set = 0; static unsigned char mode = 8; static u8 chkmode = 1; -unsigned long CrcTable[256] = +static unsigned long CrcTable[256] = { 0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL, 0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L, @@ -275,7 +299,7 @@ unsigned long CrcTable[256] = }; /* function declaration ------------------------------------- */ -static int dmfe_reg_board(void); +static int dmfe_probe(void); static int dmfe_open(struct net_device *); static int dmfe_start_xmit(struct sk_buff *, struct net_device *); static int dmfe_stop(struct net_device *); @@ -288,11 +312,11 @@ static void dmfe_descriptor_init(struct dmfe_board_info *, u32); static void allocated_rx_buffer(struct dmfe_board_info *); static void update_cr6(u32, u32); static void send_filter_frame(struct net_device *, int); -static u16 phy_read(u32, u8, u8); -static void phy_write(u32, u8, u8, u16); +static void dm9132_id_table(struct net_device *, int); +static u16 phy_read(u32, u8, u8, u32); +static void phy_write(u32, u8, u8, u16, u32); static void phy_write_1bit(u32, u32); static u16 phy_read_1bit(u32); -static void parser_ctrl_info(struct dmfe_board_info *); static void dmfe_sense_speed(struct dmfe_board_info *); static void dmfe_process_mode(struct dmfe_board_info *); static void dmfe_timer(unsigned long); @@ -301,7 +325,7 @@ static void dmfe_reused_skb(struct dmfe_board_info *, struct sk_buff *); static void dmfe_dynamic_reset(struct net_device *); static void dmfe_free_rxbuffer(struct dmfe_board_info *); static void dmfe_init_dm910x(struct net_device *); -static unsigned long cal_CRC(unsigned char *, unsigned int); +static unsigned long cal_CRC(unsigned char *, unsigned int, u8); /* DM910X network board routine ---------------------------- */ @@ -309,9 +333,9 @@ static unsigned long cal_CRC(unsigned char *, unsigned int); * Search DM910X board, allocate space and register it */ -static int __init dmfe_reg_board(void) +static int __init dmfe_probe(void) { - u32 pci_iobase; + unsigned long pci_iobase; u16 dm9102_count = 0; u8 pci_irqline; static int index = 0; /* For multiple call */ @@ -320,7 +344,7 @@ static int __init dmfe_reg_board(void) struct pci_dev *net_dev = NULL; struct net_device *dev; - DMFE_DBUG(0, "dmfe_reg_board()", 0); + DMFE_DBUG(0, "dmfe_probe()", 0); if (!pci_present()) return -ENODEV; @@ -329,20 +353,18 @@ static int __init dmfe_reg_board(void) while ((net_dev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, net_dev))) { u32 pci_id; + u32 dev_rev; index++; if (pci_read_config_dword(net_dev, PCI_VENDOR_ID, &pci_id) != DMFE_SUCC) continue; - if (pci_id != PCI_DM9102_ID) + if ((pci_id != PCI_DM9102_ID) && (pci_id != PCI_DM9132_ID)) continue; pci_iobase = net_dev->resource[0].start; pci_irqline = net_dev->irq; - if (check_region(pci_iobase, DM9102_IO_SIZE)) /* IO range check */ - continue; - /* Enable Master/IO access, Disable memory access */ pci_enable_device (net_dev); @@ -355,7 +377,19 @@ static int __init dmfe_reg_board(void) pci_write_config_byte(net_dev, PCI_LATENCY_TIMER, 0x80); - /* IO range and interrupt check */ + /* Read Chip revesion */ + pci_read_config_dword(net_dev, PCI_REVISION_ID, &dev_rev); + + /* IO range check */ + if (check_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev))) { + printk(KERN_ERR "dmfe: I/O conflict : IO=%lx Range=%x\n", pci_iobase, CHK_IO_SIZE(pci_id, dev_rev)); + continue; + } + /* Interrupt check */ + if (pci_irqline == 0) { + printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n", pci_irqline); + continue; + } /* Found DM9102 card and PCI resource allocated OK */ dm9102_count++; /* Found a DM9102 card */ @@ -373,7 +407,7 @@ static int __init dmfe_reg_board(void) db->chip_id = pci_id; /* keep Chip vandor/Device ID */ db->ioaddr = pci_iobase; - pci_read_config_dword(net_dev, 8, &db->chip_revesion); + db->chip_revesion = dev_rev; db->net_dev = net_dev; @@ -386,7 +420,7 @@ static int __init dmfe_reg_board(void) dev->set_multicast_list = &dmfe_set_filter_mode; dev->do_ioctl = &dmfe_do_ioctl; - request_region(pci_iobase, DM9102_IO_SIZE, dev->name); + request_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev), dev->name); /* read 64 word srom data */ for (i = 0; i < 64; i++) @@ -422,20 +456,17 @@ static int dmfe_open(struct net_device *dev) db->desc_pool_ptr = kmalloc(sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, GFP_KERNEL | GFP_DMA); if (db->desc_pool_ptr == NULL) return -ENOMEM; - if ((u32) db->desc_pool_ptr & 0x1f) db->first_tx_desc = (struct tx_desc *) (((u32) db->desc_pool_ptr & ~0x1f) + 0x20); else db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr; /* Allocated Tx buffer memory */ - db->buf_pool_ptr = kmalloc(TX_BUF_ALLOC * TX_DESC_CNT + 4, GFP_KERNEL | GFP_DMA); if (db->buf_pool_ptr == NULL) { kfree(db->desc_pool_ptr); return -ENOMEM; } - if ((u32) db->buf_pool_ptr & 0x3) db->buf_pool_start = (char *) (((u32) db->buf_pool_ptr & ~0x3) + 0x4); else @@ -444,16 +475,21 @@ static int dmfe_open(struct net_device *dev) /* system variable init */ db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set; db->tx_packet_cnt = 0; + db->tx_queue_cnt = 0; db->rx_avail_cnt = 0; db->link_failed = 0; db->wait_reset = 0; db->in_reset_state = 0; db->rx_error_cnt = 0; - if (chkmode && (db->chip_revesion < 0x02000030)) { - db->dm910x_chk_mode = 1; /* Enter the check mode */ - } else { + if (!chkmode || (db->chip_id == PCI_DM9132_ID) || (db->chip_revesion >= 0x02000030)) { + //db->cr6_data &= ~CR6_SFT; /* Used Tx threshold */ + //db->cr6_data |= CR6_NO_PURGE; /* No purge if rx unavailable */ + db->cr0_data = 0xc00000; /* TX/RX desc burst mode */ db->dm910x_chk_mode = 4; /* Enter the normal mode */ + } else { + db->cr0_data = 0; + db->dm910x_chk_mode = 1; /* Enter the check mode */ } /* Initilize DM910X board */ @@ -474,14 +510,12 @@ static int dmfe_open(struct net_device *dev) return 0; } -/* - * Initialize DM910X board - * Reset DM910X board - * Initialize TX/Rx descriptor chain structure - * Send the set-up frame - * Enable Tx/Rx machine +/* Initilize DM910X board + Reset DM910X board + Initilize TX/Rx descriptor chain structure + Send the set-up frame + Enable Tx/Rx machine */ - static void dmfe_init_dm910x(struct net_device *dev) { struct dmfe_board_info *db = dev->priv; @@ -490,16 +524,18 @@ static void dmfe_init_dm910x(struct net_device *dev) DMFE_DBUG(0, "dmfe_init_dm910x()", 0); /* Reset DM910x board : need 32 PCI clock to complete */ - outl(DM910X_RESET, ioaddr + DCR0); + outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */ DELAY_5US; - outl(0, ioaddr + DCR0); + outl(db->cr0_data, ioaddr + DCR0); outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */ - outl(0x80, ioaddr + DCR12); /* Reset DM9102 phyxcer */ + outl(0x80, ioaddr + DCR12); /* RESET DM9102 phyxcer */ outl(0x0, ioaddr + DCR12); /* Clear RESET signal */ - /* Parser control information: Phy addr */ - parser_ctrl_info(db); + /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */ + db->phy_addr = 1; + + /* Media Mode Check */ db->media_mode = dmfe_media_mode; if (db->media_mode & DMFE_AUTO) dmfe_sense_speed(db); @@ -514,7 +550,10 @@ static void dmfe_init_dm910x(struct net_device *dev) update_cr6(db->cr6_data, ioaddr); /* Send setup frame */ - send_filter_frame(dev, 0); + if (db->chip_id == PCI_DM9132_ID) + dm9132_id_table(dev, dev->mc_count); /* DM9132 */ + else + send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */ /* Init CR5/CR7, interrupt active bit */ outl(0xffffffff, ioaddr + DCR5); /* clear all CR5 status */ @@ -533,10 +572,9 @@ static void dmfe_init_dm910x(struct net_device *dev) /* - * Hardware start transmission. - * Send a packet to media from the upper layer. + Hardware start transmission. + Send a packet to media from the upper layer. */ - static int dmfe_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct dmfe_board_info *db = dev->priv; @@ -561,25 +599,24 @@ static int dmfe_start_xmit(struct sk_buff *skb, struct net_device *dev) txptr = db->tx_insert_ptr; memcpy((char *) txptr->tx_buf_ptr, (char *) skb->data, skb->len); txptr->tdes1 = 0xe1000000 | skb->len; - txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */ /* Point to next transmit free descriptor */ db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc; - /* transmit counter increase 1 */ - db->tx_packet_cnt++; - db->stats.tx_packets++; - - /* issue Tx polling command */ - outl(0x1, dev->base_addr + DCR1); + /* Transmit Packet Process */ + if (db->tx_packet_cnt < TX_MAX_SEND_CNT) { + txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */ + db->tx_packet_cnt++; /* Ready to send count */ + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling comand */ + } else { + db->tx_queue_cnt++; /* queue the tx packet */ + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling comand */ + } /* Tx resource check */ if (db->tx_packet_cnt < TX_FREE_DESC_CNT) netif_wake_queue(dev); - /* Set transmit time stamp */ - dev->trans_start = jiffies; /* saved the time stamp */ - /* free this SKB */ dev_kfree_skb(skb); return 0; @@ -622,8 +659,8 @@ static int dmfe_stop(struct net_device *dev) } /* - * DM9102 insterrupt handler - * receive the packet to upper layer, free the transmitted packet + DM9102 insterrupt handler + receive the packet to upper layer, free the transmitted packet */ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs) @@ -637,7 +674,6 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs) DMFE_DBUG(1, "dmfe_interrupt() without device arg", 0); return; } - /* A real interrupt coming */ db = (struct dmfe_board_info *) dev->priv; ioaddr = dev->base_addr; @@ -667,15 +703,35 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs) /* printk("tdes0=%x\n", txptr->tdes0); */ if (txptr->tdes0 & 0x80000000) break; + db->stats.tx_packets++; + if ((txptr->tdes0 & TDES0_ERR_MASK) && (txptr->tdes0 != 0x7fffffff)) { /* printk("tdes0=%x\n", txptr->tdes0); */ db->stats.tx_errors++; } + /* Transmit statistic counter */ + if (txptr->tdes0 != 0x7fffffff) { + /* printk("tdes0=%x\n", txptr->tdes0); */ + db->stats.collisions += (txptr->tdes0 >> 3) & 0xf; + db->stats.tx_bytes += txptr->tdes1 & 0x7ff; + if (txptr->tdes0 & TDES0_ERR_MASK) + db->stats.tx_errors++; + } txptr = (struct tx_desc *) txptr->next_tx_desc; db->tx_packet_cnt--; } + /* Update TX remove pointer to next */ db->tx_remove_ptr = (struct tx_desc *) txptr; + /* Send the Tx packet in queue */ + if ((db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt) { + txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */ + db->tx_packet_cnt++; /* Ready to send count */ + outl(0x1, ioaddr + DCR1); /* Issue Tx polling command */ + dev->trans_start = jiffies; /* saved the time stamp */ + db->tx_queue_cnt--; + } + /* Resource available check */ if (db->tx_packet_cnt < TX_FREE_DESC_CNT) netif_wake_queue(dev); @@ -695,7 +751,6 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs) } /* Restore CR7 to enable interrupt mask */ - if (db->interval_rx_cnt > RX_MAX_TRAFFIC) db->cr7_data = 0x1a28d; else @@ -704,9 +759,8 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs) } /* - * Receive the come packet and pass to upper layer + Receive the come packet and pass to upper layer */ - static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db) { struct rx_desc *rxptr; @@ -727,11 +781,11 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db) /* reused this SKB */ DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0); dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr); - db->rx_error_cnt++; + /* db->rx_error_cnt++; */ } else { + /* A packet with First/Last flag */ rxlen = ((rxptr->rdes0 >> 16) & 0x3fff) - 4; /* skip CRC */ - /* A packet with First/Last flag */ if (rxptr->rdes0 & 0x8000) { /* error summary bit check */ /* This is a error packet */ /* printk("rdes0 error : %x \n", rxptr->rdes0); */ @@ -748,7 +802,7 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db) skb = (struct sk_buff *) rxptr->rx_skb_ptr; /* Received Packet CRC check need or not */ - if ((db->dm910x_chk_mode & 1) && (cal_CRC(skb->tail, rxlen) != (*(unsigned long *) (skb->tail + rxlen)))) { + if ((db->dm910x_chk_mode & 1) && (cal_CRC(skb->tail, rxlen, 1) != (*(unsigned long *) (skb->tail + rxlen)))) { /* Found a error received packet */ dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr); db->dm910x_chk_mode = 3; @@ -758,11 +812,12 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db) skb_put(skb, rxlen); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); /* Send to upper layer */ - /* skb->ip_summed = CHECKSUM_UNNECESSARY; */ dev->last_rx = jiffies; db->stats.rx_packets++; + db->stats.rx_bytes += rxlen; } } else { + /* Reuse SKB buffer when the packet is error */ DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0); dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr); } @@ -772,12 +827,12 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db) } db->rx_ready_ptr = rxptr; + } /* - * Get statistics from driver. + Get statistics from driver. */ - static struct enet_statistics *dmfe_get_stats(struct net_device *dev) { struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv; @@ -787,9 +842,8 @@ static struct enet_statistics *dmfe_get_stats(struct net_device *dev) } /* - * Set DM910X multicast address + Set DM910X multicast address */ - static void dmfe_set_filter_mode(struct net_device *dev) { struct dmfe_board_info *db = dev->priv; @@ -809,13 +863,15 @@ static void dmfe_set_filter_mode(struct net_device *dev) return; } DMFE_DBUG(0, "Set multicast address", dev->mc_count); - send_filter_frame(dev, dev->mc_count); + if (db->chip_id == PCI_DM9132_ID) + dm9132_id_table(dev, dev->mc_count); /* DM9132 */ + else + send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */ } /* - * Process the upper socket ioctl command + Process the upper socket ioctl command */ - static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { DMFE_DBUG(0, "dmfe_do_ioctl()", 0); @@ -823,10 +879,9 @@ static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) } /* - * A periodic timer routine - * Dynamic media sense, allocated Rx buffer... + A periodic timer routine + Dynamic media sense, allocated Rx buffer... */ - static void dmfe_timer(unsigned long data) { u32 tmp_cr8; @@ -861,18 +916,26 @@ static void dmfe_timer(unsigned long data) if (db->wait_reset | (db->tx_packet_cnt && ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) | (db->rx_error_cnt > 3)) { - /* printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start); */ + /* + printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start); + */ DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx moniotr step1", db->tx_packet_cnt); dmfe_dynamic_reset(dev); db->timer.expires = DMFE_TIMER_WUT; add_timer(&db->timer); return; } - db->rx_error_cnt = 0; /* Clear previous counter */ + db->rx_error_cnt = 0; /* Clear previos counter */ /* Link status check, Dynamic media type change */ - tmp_cr12 = inb(db->ioaddr + DCR12); - if (db->chip_revesion == 0x02000030) { + if (db->chip_id == PCI_DM9132_ID) + tmp_cr12 = inb(db->ioaddr + DCR9 + 3); /* DM9132 */ + else + tmp_cr12 = inb(db->ioaddr + DCR12); /* DM9102/DM9102A */ + + if (((db->chip_id == PCI_DM9102_ID) && (db->chip_revesion == 0x02000030)) || + ((db->chip_id == PCI_DM9132_ID) && (db->chip_revesion == 0x02000010))) { + /* DM9102A Chip */ if (tmp_cr12 & 2) tmp_cr12 = 0x0; /* Link failed */ else @@ -882,10 +945,26 @@ static void dmfe_timer(unsigned long data) /* Link Failed */ DMFE_DBUG(0, "Link Failed", tmp_cr12); db->link_failed = 1; - phy_write(db->ioaddr, db->phy_addr, 0, 0x8000); /* reset Phy controller */ + phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); /* reset Phy */ + + /* 10/100M link failed, used 1M Home-Net */ + db->cr6_data |= 0x00040000; /* CR6 bit18 = 1, select Home-Net */ + db->cr6_data &= ~0x00000200; /* CR6 bit9 =0, half duplex mode */ + update_cr6(db->cr6_data, db->ioaddr); + + /* For DM9801 : PHY ID1 0181h, PHY ID2 B900h */ + db->phy_id2 = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id); + if (db->phy_id2 == 0xb900) + phy_write(db->ioaddr, db->phy_addr, 25, 0x7e08, db->chip_id); } else if ((tmp_cr12 & 0x3) && db->link_failed) { DMFE_DBUG(0, "Link link OK", tmp_cr12); db->link_failed = 0; + + /* CR6 bit18=0, select 10/100M */ + db->cr6_data &= ~0x00040000; + update_cr6(db->cr6_data, db->ioaddr); + + /* Auto Sense Speed */ if (db->media_mode & DMFE_AUTO) dmfe_sense_speed(db); dmfe_process_mode(db); @@ -902,13 +981,12 @@ static void dmfe_timer(unsigned long data) } /* - * Dynamic reset the DM910X board - * Stop DM910X board - * Free Tx/Rx allocated memory - * Reset DM910X board - * Re-initilize DM910X board + Dynamic reset the DM910X board + Stop DM910X board + Free Tx/Rx allocated memory + Reset DM910X board + Re-initilize DM910X board */ - static void dmfe_dynamic_reset(struct net_device *dev) { struct dmfe_board_info *db = dev->priv; @@ -929,6 +1007,7 @@ static void dmfe_dynamic_reset(struct net_device *dev) /* system variable init */ db->tx_packet_cnt = 0; + db->tx_queue_cnt = 0; db->rx_avail_cnt = 0; db->link_failed = 0; db->wait_reset = 0; @@ -945,9 +1024,8 @@ static void dmfe_dynamic_reset(struct net_device *dev) } /* - * Free all allocated rx buffer + free all allocated rx buffer */ - static void dmfe_free_rxbuffer(struct dmfe_board_info *db) { DMFE_DBUG(0, "dmfe_free_rxbuffer()", 0); @@ -961,9 +1039,8 @@ static void dmfe_free_rxbuffer(struct dmfe_board_info *db) } /* - * Reused the SK buffer + Reused the SK buffer */ - static void dmfe_reused_skb(struct dmfe_board_info *db, struct sk_buff *skb) { struct rx_desc *rxptr = db->rx_insert_ptr; @@ -979,10 +1056,9 @@ static void dmfe_reused_skb(struct dmfe_board_info *db, struct sk_buff *skb) } /* - * Initialize transmit/Receive descriptor - * Using Chain structure, and allocated Tx/Rx buffer + Initialize transmit/Receive descriptor + Using Chain structure, and allocated Tx/Rx buffer */ - static void dmfe_descriptor_init(struct dmfe_board_info *db, u32 ioaddr) { struct tx_desc *tmp_tx; @@ -1033,10 +1109,9 @@ static void dmfe_descriptor_init(struct dmfe_board_info *db, u32 ioaddr) } /* - * Update CR6 vaule - * Firstly stop DM910X , then written value and start + Update CR6 vaule + Firstly stop DM910X , then written value and start */ - static void update_cr6(u32 cr6_data, u32 ioaddr) { u32 cr6_tmp; @@ -1049,11 +1124,50 @@ static void update_cr6(u32 cr6_data, u32 ioaddr) /* printk("CR6 update %x ", cr6_tmp); */ } -/* - * Send a setup frame - * This setup frame initilize DM910X addres filter mode +/* Send a setup frame for DM9132 + This setup frame initilize DM910X addres filter mode + */ +static void dm9132_id_table(struct net_device *dev, int mc_cnt) +{ + struct dev_mc_list *mcptr; + u16 *addrptr; + u32 ioaddr = dev->base_addr + 0xc0; /* ID Table */ + u32 hash_val; + u16 i, hash_table[4]; + + DMFE_DBUG(0, "dm9132_id_table()", 0); + + /* Node address */ + addrptr = (u16 *) dev->dev_addr; + outw(addrptr[0], ioaddr); + ioaddr += 4; + outw(addrptr[1], ioaddr); + ioaddr += 4; + outw(addrptr[2], ioaddr); + ioaddr += 4; + + /* Clear Hash Table */ + for (i = 0; i < 4; i++) + hash_table[i] = 0x0; + + /* broadcast address */ + hash_table[3] = 0x8000; + + /* the multicast address in Hash Table : 64 bits */ + for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) { + hash_val = cal_CRC((char *) mcptr->dmi_addr, 6, 0) & 0x3f; + hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16); + } + + /* Write the hash table to MAC MD table */ + for (i = 0; i < 4; i++, ioaddr += 4) { + outw(hash_table[i], ioaddr); + } +} + +/* Send a setup frame for DM9102/DM9102A + This setup frame initilize DM910X addres filter mode */ - static void send_filter_frame(struct net_device *dev, int mc_cnt) { struct dmfe_board_info *db = dev->priv; @@ -1068,17 +1182,17 @@ static void send_filter_frame(struct net_device *dev, int mc_cnt) txptr = db->tx_insert_ptr; suptr = (u32 *) txptr->tx_buf_ptr; - /* broadcast address */ - *suptr++ = 0xffff; - *suptr++ = 0xffff; - *suptr++ = 0xffff; - /* Node address */ addrptr = (u16 *) dev->dev_addr; *suptr++ = addrptr[0]; *suptr++ = addrptr[1]; *suptr++ = addrptr[2]; + /* broadcast address */ + *suptr++ = 0xffff; + *suptr++ = 0xffff; + *suptr++ = 0xffff; + /* fit the multicast address */ for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) { addrptr = (u16 *) mcptr->dmi_addr; @@ -1092,19 +1206,21 @@ static void send_filter_frame(struct net_device *dev, int mc_cnt) *suptr++ = 0xffff; *suptr++ = 0xffff; } - /* prepare the setup frame */ - db->tx_packet_cnt++; - netif_stop_queue(dev); - txptr->tdes1 = 0x890000c0; - txptr->tdes0 = 0x80000000; db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc; - - update_cr6(db->cr6_data | 0x2000, dev->base_addr); - outl(0x1, dev->base_addr + DCR1); - update_cr6(db->cr6_data, dev->base_addr); - dev->trans_start = jiffies; - + txptr->tdes1 = 0x890000c0; + /* Resource Check and Send the setup packet */ + if (!db->tx_packet_cnt) { + /* Resource Empty */ + db->tx_packet_cnt++; + txptr->tdes0 = 0x80000000; + update_cr6(db->cr6_data | 0x2000, dev->base_addr); + outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */ + update_cr6(db->cr6_data, dev->base_addr); + } else { + /* Put into TX queue */ + db->tx_queue_cnt++; + } } /* @@ -1132,9 +1248,8 @@ static void allocated_rx_buffer(struct dmfe_board_info *db) } /* - * Read one word data from the serial ROM + Read one word data from the serial ROM */ - static u16 read_srom_word(long ioaddr, int offset) { int i; @@ -1170,35 +1285,6 @@ static u16 read_srom_word(long ioaddr, int offset) } /* - * Parser Control media block to get Phy address - */ - -static void parser_ctrl_info(struct dmfe_board_info *db) -{ - int i; - char *sdata = db->srom; - unsigned char count; - - /* point to info leaf0 */ - count = *(sdata + 33); - - /* Point to First media block */ - sdata += 34; - for (i = 0; i < count; i++) { - if (*(sdata + 1) == 1) { - db->phy_addr = *(sdata + 2); - break; - } - sdata += ((unsigned char) *(sdata) & 0x7f) + 1; - } - - if (i >= count) { - printk("Can't found Control Block\n"); - db->phy_addr = 1; - } -} - -/* * Auto sense the media mode */ @@ -1209,13 +1295,16 @@ static void dmfe_sense_speed(struct dmfe_board_info *db) for (i = 1000; i; i--) { DELAY_5US; - phy_mode = phy_read(db->ioaddr, db->phy_addr, 1); + phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id); if ((phy_mode & 0x24) == 0x24) break; } if (i) { - phy_mode = phy_read(db->ioaddr, db->phy_addr, 17) & 0xf000; + if (db->chip_id == PCI_DM9132_ID) /* DM9132 */ + phy_mode = phy_read(db->ioaddr, db->phy_addr, 7, db->chip_id) & 0xf000; + else /* DM9102/DM9102A */ + phy_mode = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0xf000; /* printk("Phy_mode %x ",phy_mode); */ switch (phy_mode) { case 0x1000: @@ -1231,23 +1320,22 @@ static void dmfe_sense_speed(struct dmfe_board_info *db) db->op_mode = DMFE_100MFD; break; default: - db->op_mode = DMFE_100MHF; - DMFE_DBUG(1, "Media Type error, phy reg17", phy_mode); + db->op_mode = DMFE_10MHF; + DMFE_DBUG(0, "Media Type error, phy reg17", phy_mode); break; } } else { - db->op_mode = DMFE_100MHF; + db->op_mode = DMFE_10MHF; DMFE_DBUG(0, "Link Failed :", phy_mode); } } /* - * Process op-mode - * AUTO mode : PHY controller in Auto-negotiation Mode - * Force mode: PHY controller in force mode with HUB - * N-way force capability with SWITCH + Process op-mode + AUTO mode : PHY controller in Auto-negotiation Mode + Force mode: PHY controller in force mode with HUB + N-way force capability with SWITCH */ - static void dmfe_process_mode(struct dmfe_board_info *db) { u16 phy_reg; @@ -1259,11 +1347,11 @@ static void dmfe_process_mode(struct dmfe_board_info *db) if (!(db->media_mode & DMFE_AUTO)) { /* Force Mode Check */ /* User force the media type */ - phy_reg = phy_read(db->ioaddr, db->phy_addr, 5); + phy_reg = phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id); /* printk("Nway phy_reg5 %x ",phy_reg); */ if (phy_reg & 0x1) { /* parter own the N-Way capability */ - phy_reg = phy_read(db->ioaddr, db->phy_addr, 4) & ~0x1e0; + phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x1e0; switch (db->op_mode) { case DMFE_10MHF: phy_reg |= 0x20; @@ -1278,7 +1366,7 @@ static void dmfe_process_mode(struct dmfe_board_info *db) phy_reg |= 0x100; break; } - phy_write(db->ioaddr, db->phy_addr, 4, phy_reg); + phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id); } else { /* parter without the N-Way capability */ switch (db->op_mode) { @@ -1295,95 +1383,109 @@ static void dmfe_process_mode(struct dmfe_board_info *db) phy_reg = 0x2100; break; } - phy_write(db->ioaddr, db->phy_addr, 0, phy_reg); + phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id); } } } /* - * Write a word to Phy register + Write a word to Phy register */ - -static void phy_write(u32 iobase, u8 phy_addr, u8 offset, u16 phy_data) +static void phy_write(u32 iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id) { u16 i; - u32 ioaddr = iobase + DCR9; + u32 ioaddr; - /* Send 33 synchronization clock to Phy controller */ - for (i = 0; i < 35; i++) - phy_write_1bit(ioaddr, PHY_DATA_1); + if (chip_id == PCI_DM9132_ID) { + ioaddr = iobase + 0x80 + offset * 4; + outw(phy_data, ioaddr); + } else { + /* DM9102/DM9102A Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1); - /* Send start command(01) to Phy */ - phy_write_1bit(ioaddr, PHY_DATA_0); - phy_write_1bit(ioaddr, PHY_DATA_1); + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); - /* Send write command(01) to Phy */ - phy_write_1bit(ioaddr, PHY_DATA_0); - phy_write_1bit(ioaddr, PHY_DATA_1); + /* Send write command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); - /* Send Phy addres */ - for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); + /* Send Phy addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); - /* Send register addres */ - for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); + /* Send register addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); - /* written trasnition */ - phy_write_1bit(ioaddr, PHY_DATA_1); - phy_write_1bit(ioaddr, PHY_DATA_0); + /* written trasnition */ + phy_write_1bit(ioaddr, PHY_DATA_1); + phy_write_1bit(ioaddr, PHY_DATA_0); - /* Write a word data to PHY controller */ - for (i = 0x8000; i > 0; i >>= 1) - phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0); + /* Write a word data to PHY controller */ + for (i = 0x8000; i > 0; i >>= 1) + phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0); + } } /* - * Read a word data from phy register + Read a word data from phy register */ - -static u16 phy_read(u32 iobase, u8 phy_addr, u8 offset) +static u16 phy_read(u32 iobase, u8 phy_addr, u8 offset, u32 chip_id) { int i; u16 phy_data; - u32 ioaddr = iobase + DCR9; + u32 ioaddr; - /* Send 33 synchronization clock to Phy controller */ - for (i = 0; i < 35; i++) - phy_write_1bit(ioaddr, PHY_DATA_1); + if (chip_id == PCI_DM9132_ID) { + /* DM9132 Chip */ + ioaddr = iobase + 0x80 + offset * 4; + phy_data = inw(ioaddr); + } else { + /* DM9102/DM9102A Chip */ + + ioaddr = iobase + DCR9; + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1); - /* Send start command(01) to Phy */ - phy_write_1bit(ioaddr, PHY_DATA_0); - phy_write_1bit(ioaddr, PHY_DATA_1); + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); - /* Send read command(10) to Phy */ - phy_write_1bit(ioaddr, PHY_DATA_1); - phy_write_1bit(ioaddr, PHY_DATA_0); + /* Send read command(10) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_1); + phy_write_1bit(ioaddr, PHY_DATA_0); - /* Send Phy addres */ - for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); + /* Send Phy addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); - /* Send register addres */ - for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); + /* Send register addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); - /* Skip transition state */ - phy_read_1bit(ioaddr); + /* Skip transition state */ + phy_read_1bit(ioaddr); - /* read 16bit data */ - for (phy_data = 0, i = 0; i < 16; i++) { - phy_data <<= 1; - phy_data |= phy_read_1bit(ioaddr); + /* read 16bit data */ + for (phy_data = 0, i = 0; i < 16; i++) { + phy_data <<= 1; + phy_data |= phy_read_1bit(ioaddr); + } } return phy_data; } /* - * Write one bit data to Phy Controller + Write one bit data to Phy Controller */ - static void phy_write_1bit(u32 ioaddr, u32 phy_data) { outl(phy_data, ioaddr); /* MII Clock Low */ @@ -1395,9 +1497,8 @@ static void phy_write_1bit(u32 ioaddr, u32 phy_data) } /* - * Read one bit phy data from PHY controller + Read one bit phy data from PHY controller */ - static u16 phy_read_1bit(u32 ioaddr) { u16 phy_data; @@ -1412,10 +1513,11 @@ static u16 phy_read_1bit(u32 ioaddr) } /* - * Calculate the CRC valude of the Rx packet + Calculate the CRC valude of the Rx packet + flag = 1 : return the reverse CRC (for the received packet CRC) + 0 : return the normal CRC (for Hash Table index) */ - -static unsigned long cal_CRC(unsigned char *Data, unsigned int Len) +unsigned long cal_CRC(unsigned char *Data, unsigned int Len, u8 flag) { unsigned long Crc = 0xffffffff; @@ -1423,8 +1525,10 @@ static unsigned long cal_CRC(unsigned char *Data, unsigned int Len) Crc = CrcTable[(Crc ^ *Data++) & 0xFF] ^ (Crc >> 8); } - return ~Crc; - + if (flag) + return ~Crc; + else + return Crc; } @@ -1461,7 +1565,7 @@ static int __init dmfe_init_module(void) break; } - return dmfe_reg_board(); /* search board and register */ + return dmfe_probe(); /* search board and register */ } /* @@ -1473,14 +1577,16 @@ static int __init dmfe_init_module(void) static void __exit dmfe_cleanup_module(void) { struct net_device *next_dev; + struct dmfe_board_info *db; DMFE_DBUG(0, "clean_module()", 0); while (dmfe_root_dev) { - next_dev = ((struct dmfe_board_info *) dmfe_root_dev->priv)->next_dev; + db = dmfe_root_dev->priv; + next_dev = db->next_dev; unregister_netdev(dmfe_root_dev); - release_region(dmfe_root_dev->base_addr, DM9102_IO_SIZE); - kfree(dmfe_root_dev->priv); /* free board information */ + release_region(dmfe_root_dev->base_addr, CHK_IO_SIZE(db->chip_id, db->chip_revesion)); + kfree(db); /* free board information */ kfree(dmfe_root_dev); /* free device structure */ dmfe_root_dev = next_dev; } diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c index 03c69417a..d5230ddda 100644 --- a/drivers/net/eexpress.c +++ b/drivers/net/eexpress.c @@ -712,6 +712,7 @@ static unsigned short eexp_start_irq(struct net_device *dev, ack_cmd |= SCB_RUstart; scb_wrrfa(dev, lp->rx_buf_start); lp->rx_ptr = lp->rx_buf_start; + lp->started |= STARTED_RU; } ack_cmd |= SCB_CUstart | 0x2000; } diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index 1c546b4d1..f855d1978 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -1071,7 +1071,7 @@ static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } return 0; case SIOCDEVPRIVATE+2: /* Write the specified MII register */ - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EPERM; if (! netif_running(dev)) { outl(0x0200, ioaddr + GENCTL); diff --git a/drivers/net/eql.c b/drivers/net/eql.c index 820bc5d4f..e9f0254a7 100644 --- a/drivers/net/eql.c +++ b/drivers/net/eql.c @@ -42,7 +42,7 @@ static const char *version = * reformatted. * * Revision 3.12 1995/03/22 21:07:51 anarchy - * Added suser() checks on configuration. + * Added capable() checks on configuration. * Moved header file. * * Revision 3.11 1995/01/19 23:14:31 guru @@ -1030,6 +1030,8 @@ int init_module(void) void cleanup_module(void) { + kfree(((equalizer_t *)dev_eql.priv)->stats ); + kfree(dev_eql.priv); unregister_netdev(&dev_eql); } #endif /* MODULE */ diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c index 90c82b341..ae92696c9 100644 --- a/drivers/net/ewrk3.c +++ b/drivers/net/ewrk3.c @@ -1836,7 +1836,7 @@ static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) status = -EFAULT; break; case EWRK3_SET_TX_CUT_THRU: /* Set TX cut through mode */ - if (suser()) { + if (capable(CAP_NET_ADMIN)) { lp->txc = 1; } else { status = -EPERM; @@ -1844,7 +1844,7 @@ static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) break; case EWRK3_CLR_TX_CUT_THRU: /* Clear TX cut through mode */ - if (suser()) { + if (capable(CAP_NET_ADMIN)) { lp->txc = 0; } else { status = -EPERM; diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index 98d228b91..e770baacf 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -1254,7 +1254,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; case HDLCDRVCTL_SETCHANNELPAR: - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EACCES; bc->ch_params.tx_delay = hi.data.cp.tx_delay; bc->ch_params.tx_tail = hi.data.cp.tx_tail; @@ -1275,7 +1275,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; case HDLCDRVCTL_SETMODEMPAR: - if ((!suser()) || netif_running(dev)) + if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev)) return -EACCES; dev->base_addr = hi.data.mp.iobase; dev->irq = /*hi.data.mp.irq*/0; @@ -1299,6 +1299,8 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; case HDLCDRVCTL_CALIBRATE: + if (!capable(CAP_SYS_RAWIO)) + return -EACCES; bc->hdlctx.calibrate = hi.data.calibrate * bc->bitrate / 8; return 0; @@ -1314,7 +1316,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; case HDLCDRVCTL_SETMODE: - if (!suser() || netif_running(dev)) + if (!capable(CAP_NET_ADMIN) || netif_running(dev)) return -EACCES; hi.data.modename[sizeof(hi.data.modename)-1] = '\0'; return baycom_setmode(bc, hi.data.modename); diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c index b68063b07..63dc93cfb 100644 --- a/drivers/net/hamradio/baycom_par.c +++ b/drivers/net/hamradio/baycom_par.c @@ -445,7 +445,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, return 0; case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !suser()) + if (netif_running(dev) || !capable(CAP_NET_ADMIN)) return -EACCES; hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; return baycom_setmode(bc, hi->data.modename); diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c index cdcab2621..a90eb0f8c 100644 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ b/drivers/net/hamradio/baycom_ser_fdx.c @@ -555,7 +555,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, return 0; case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !suser()) + if (netif_running(dev) || !capable(CAP_NET_ADMIN)) return -EACCES; hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; return baycom_setmode(bc, hi->data.modename); diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c index 64b27286c..111373ca9 100644 --- a/drivers/net/hamradio/baycom_ser_hdx.c +++ b/drivers/net/hamradio/baycom_ser_hdx.c @@ -598,7 +598,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, return 0; case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !suser()) + if (netif_running(dev) || !capable(CAP_NET_ADMIN)) return -EACCES; hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; return baycom_setmode(bc, hi->data.modename); diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index 0cc9aa7c1..a48b3f6c6 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -366,7 +366,7 @@ static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) struct bpqdev *bpq = dev->priv; struct bpq_req req; - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EPERM; if (bpq == NULL) /* woops! */ diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index a3560c207..eac33513d 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -635,7 +635,7 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; case HDLCDRVCTL_SETCHANNELPAR: - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EACCES; s->ch_params.tx_delay = bi.data.cp.tx_delay; s->ch_params.tx_tail = bi.data.cp.tx_tail; @@ -656,7 +656,7 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; case HDLCDRVCTL_SETMODEMPAR: - if ((!suser()) || netif_running(dev)) + if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev)) return -EACCES; dev->base_addr = bi.data.mp.iobase; dev->irq = bi.data.mp.irq; @@ -684,6 +684,8 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; case HDLCDRVCTL_CALIBRATE: + if(!capable(CAP_SYS_RAWIO)) + return -EPERM; s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16; return 0; diff --git a/drivers/net/hamradio/pi2.c b/drivers/net/hamradio/pi2.c index b52a254c8..220c4d920 100644 --- a/drivers/net/hamradio/pi2.c +++ b/drivers/net/hamradio/pi2.c @@ -1580,7 +1580,7 @@ static int pi_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) switch (rq.cmd) { case SIOCSPIPARAM: - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EPERM; save_flags(flags); cli(); @@ -1597,7 +1597,7 @@ static int pi_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) case SIOCSPIDMA: - if (!suser()) + if (!capable(CAP_SYS_RAWIO)) return -EPERM; ret = 0; if (dev->base_addr & 2) { /* if A channel */ diff --git a/drivers/net/hamradio/pt.c b/drivers/net/hamradio/pt.c index 59762edd7..8baff63ff 100644 --- a/drivers/net/hamradio/pt.c +++ b/drivers/net/hamradio/pt.c @@ -998,7 +998,7 @@ static int pt_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) switch (rq.cmd) { case SIOCSPIPARAM: - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EPERM; save_flags(flags); cli(); @@ -1015,7 +1015,7 @@ static int pt_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) case SIOCSPIDMA: - if (!suser()) + if (!capable(CAP_SYS_RAWIO)) return -EPERM; ret = 0; if (dev->base_addr & CHANA) { /* if A channel */ diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c index 09bb422f1..c7f27414f 100644 --- a/drivers/net/hamradio/scc.c +++ b/drivers/net/hamradio/scc.c @@ -1791,7 +1791,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { int found = 1; - if (!suser()) return -EPERM; + if (!capable(CAP_SYS_RAWIO)) return -EPERM; if (!arg) return -EFAULT; if (Nchips >= SCC_MAXCHIPS) @@ -1887,7 +1887,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (cmd == SIOCSCCINI) { - if (!suser()) + if (!capable(CAP_SYS_RAWIO)) return -EPERM; if (Nchips == 0) @@ -1904,7 +1904,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { if (cmd == SIOCSCCCHANINI) { - if (!suser()) return -EPERM; + if (!capable(CAP_NET_ADMIN)) return -EPERM; if (!arg) return -EINVAL; scc->stat.bufsize = SCC_BUFSIZE; @@ -1957,7 +1957,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return -ENOIOCTLCMD; case SIOCSCCSMEM: - if (!suser()) return -EPERM; + if (!capable(CAP_SYS_RAWIO)) return -EPERM; if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg))) return -EINVAL; scc->stat.bufsize = memcfg.bufsize; @@ -1977,13 +1977,13 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return 0; case SIOCSCCSKISS: - if (!suser()) return -EPERM; + if (!capable(CAP_NET_ADMIN)) return -EPERM; if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) return -EINVAL; return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param); case SIOCSCCCAL: - if (!suser()) return -EPERM; + if (!capable(CAP_SYS_RAWIO)) return -EPERM; if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0) return -EINVAL; diff --git a/drivers/net/hamradio/soundmodem/sm.c b/drivers/net/hamradio/soundmodem/sm.c index 4e5e3e9b9..af04a2c87 100644 --- a/drivers/net/hamradio/soundmodem/sm.c +++ b/drivers/net/hamradio/soundmodem/sm.c @@ -509,7 +509,7 @@ static int sm_ioctl(struct net_device *dev, struct ifreq *ifr, return 0; case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !suser()) + if (netif_running(dev) || !capable(CAP_NET_ADMIN)) return -EACCES; hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; return sethw(dev, sm, hi->data.modename); diff --git a/drivers/net/hamradio/soundmodem/sm_sbc.c b/drivers/net/hamradio/soundmodem/sm_sbc.c index efc2a3295..772940049 100644 --- a/drivers/net/hamradio/soundmodem/sm_sbc.c +++ b/drivers/net/hamradio/soundmodem/sm_sbc.c @@ -1,4 +1,4 @@ -/*****************************************************************************/ + /* * sm_sbc.c -- soundcard radio modem driver soundblaster hardware driver @@ -576,7 +576,7 @@ static int sbc_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq * return i; case SMCTL_SETMIXER: - if (!suser()) + if (!capable(CAP_SYS_RAWIO)) return -EACCES; switch (SCSTATE->revhi) { case 2: diff --git a/drivers/net/hamradio/soundmodem/sm_wss.c b/drivers/net/hamradio/soundmodem/sm_wss.c index f1dc3744e..23abd970a 100644 --- a/drivers/net/hamradio/soundmodem/sm_wss.c +++ b/drivers/net/hamradio/soundmodem/sm_wss.c @@ -637,7 +637,7 @@ static int wss_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq * return i; case SMCTL_SETMIXER: - if (!suser()) + if (!capable(CAP_SYS_RAWIO)) return -EACCES; if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL || !SCSTATE->crystal) && diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c index 400facbf8..27929e78b 100644 --- a/drivers/net/hamradio/yam.c +++ b/drivers/net/hamradio/yam.c @@ -962,7 +962,7 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (yp == NULL || yp->magic != YAM_MAGIC) return -EINVAL; - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EPERM; if (cmd != SIOCDEVPRIVATE) @@ -977,6 +977,8 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (netif_running(dev)) return -EINVAL; /* Cannot change this parameter when up */ ym = kmalloc(sizeof(struct yamdrv_ioctl_mcs), GFP_ATOMIC); + if(ym==NULL) + return -ENOBUFS; ym->bitrate = 9600; if (copy_from_user(ym, ifr->ifr_data, sizeof(struct yamdrv_ioctl_mcs))) return -EFAULT; @@ -987,6 +989,8 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; case SIOCYAMSCFG: + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; if (copy_from_user(&yi, ifr->ifr_data, sizeof(struct yamdrv_ioctl_cfg))) return -EFAULT; diff --git a/drivers/net/lance.c b/drivers/net/lance.c index 689ffbd22..ce94c3139 100644 --- a/drivers/net/lance.c +++ b/drivers/net/lance.c @@ -14,22 +14,8 @@ Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 - Fixing alignment problem with 1.3.* kernel and some minor changes - by Andrey V. Savochkin, 1996. - - Problems or questions may be send to Donald Becker (see above) or to - Andrey Savochkin -- saw@shade.msu.ru or - Laboratory of Computation Methods, - Department of Mathematics and Mechanics, - Moscow State University, - Leninskye Gory, Moscow 119899 - - But I should to inform you that I'm not an expert in the LANCE card - and it may occurs that you will receive no answer on your mail - to Donald Becker. I didn't receive any answer on all my letters - to him. Who knows why... But may be you are more lucky? ;-> - SAW - + Andrey V. Savochkin: + - alignment problem with 1.3.* kernel and some minor changes. Thomas Bogendoerfer (tsbogend@bigbug.franken.de): - added support for Linux/Alpha, but removed most of it, because it worked only for the PCI chip. @@ -785,11 +771,19 @@ lance_open(struct net_device *dev) */ static void -lance_purge_tx_ring(struct net_device *dev) +lance_purge_ring(struct net_device *dev) { struct lance_private *lp = (struct lance_private *)dev->priv; int i; + /* Free all the skbuffs in the Rx and Tx queues. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = lp->rx_skbuff[i]; + lp->rx_skbuff[i] = 0; + lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */ + if (skb) + dev_kfree_skb(skb); + } for (i = 0; i < TX_RING_SIZE; i++) { if (lp->tx_skbuff[i]) { dev_kfree_skb(lp->tx_skbuff[i]); @@ -850,7 +844,7 @@ lance_restart(struct net_device *dev, unsigned int csr0_bits, int must_reinit) if (must_reinit || (chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) { - lance_purge_tx_ring(dev); + lance_purge_ring(dev); lance_init_ring(dev, GFP_ATOMIC); } outw(0x0000, dev->base_addr + LANCE_ADDR); @@ -869,7 +863,7 @@ static void lance_tx_timeout (struct net_device *dev) outw (0x0004, ioaddr + LANCE_DATA); lp->stats.tx_errors++; #ifndef final_version - { + if (lance_debug > 3) { int i; printk (" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.", lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "", @@ -1192,19 +1186,7 @@ lance_close(struct net_device *dev) } free_irq(dev->irq, dev); - /* Free all the skbuffs in the Rx and Tx queues. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = lp->rx_skbuff[i]; - lp->rx_skbuff[i] = 0; - lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */ - if (skb) - dev_kfree_skb(skb); - } - for (i = 0; i < TX_RING_SIZE; i++) { - if (lp->tx_skbuff[i]) - dev_kfree_skb(lp->tx_skbuff[i]); - lp->tx_skbuff[i] = 0; - } + lance_purge_ring(dev); MOD_DEC_USE_COUNT; return 0; diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c index 2b5624b29..53f841af2 100644 --- a/drivers/net/pcmcia/3c574_cs.c +++ b/drivers/net/pcmcia/3c574_cs.c @@ -920,8 +920,6 @@ static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev) "status %4.4x.\n", dev->name, (long)skb->len, inw(ioaddr + EL3_STATUS)); - netif_stop_queue (dev); - outw(skb->len, ioaddr + TX_FIFO); outw(0, ioaddr + TX_FIFO); outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2); @@ -929,13 +927,13 @@ static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev) dev->trans_start = jiffies; /* TxFree appears only in Window 1, not offset 0x1c. */ - if (inw(ioaddr + TxFree) > 1536) { - netif_start_queue (dev); - } else + if (inw(ioaddr + TxFree) <= 1536) { + netif_stop_queue (dev); /* Interrupt us when the FIFO has room for max-sized packet. The threshold is in units of dwords. */ outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); - + } + dev_kfree_skb (skb); pop_tx_status(dev); @@ -976,8 +974,6 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) /* There's room in the FIFO for a full-sized packet. */ outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); netif_wake_queue (dev); - } else { - netif_stop_queue (dev); } if (status & TxComplete) diff --git a/drivers/net/pcmcia/Config.in b/drivers/net/pcmcia/Config.in index 606d5a606..534a4bdbd 100644 --- a/drivers/net/pcmcia/Config.in +++ b/drivers/net/pcmcia/Config.in @@ -18,7 +18,6 @@ if [ "$CONFIG_NET_PCMCIA" = "y" ]; then if [ "$CONFIG_CARDBUS" = "y" ]; then dep_tristate ' 3Com 3c575 CardBus support' CONFIG_PCMCIA_3C575 m - dep_tristate ' DEC Tulip CardBus support' CONFIG_PCMCIA_TULIP m fi bool 'Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO diff --git a/drivers/net/pcmcia/Makefile b/drivers/net/pcmcia/Makefile index 11c583dac..5d0d36f4a 100644 --- a/drivers/net/pcmcia/Makefile +++ b/drivers/net/pcmcia/Makefile @@ -20,7 +20,6 @@ obj- := export-objs := ray_cs.o CFLAGS_3c575_cb.o = -DCARDBUS -DMODULE -CFLAGS_tulip_cb.o = -DCARDBUS -DMODULE # 16-bit client drivers obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o @@ -40,7 +39,6 @@ obj-$(CONFIG_AIRONET4500_CS) += aironet4500_cs.o # Cardbus client drivers obj-$(CONFIG_PCMCIA_3C575) += 3c575_cb.o -obj-$(CONFIG_PCMCIA_TULIP) += tulip_cb.o O_OBJS := $(filter-out $(export-objs), $(obj-y)) OX_OBJS := $(filter $(export-objs), $(obj-y)) diff --git a/drivers/net/pcmcia/ray_cs.c b/drivers/net/pcmcia/ray_cs.c index 7c763cf8e..3c088df36 100644 --- a/drivers/net/pcmcia/ray_cs.c +++ b/drivers/net/pcmcia/ray_cs.c @@ -1417,7 +1417,7 @@ static int ray_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) #define SIOCGIPFRAMING SIOCDEVPRIVATE + 1 /* Get framing mode */ #define SIOCGIPCOUNTRY SIOCDEVPRIVATE + 3 /* Get country code */ case SIOCSIPFRAMING: - if(!suser()) /* For private IOCTLs, we need to check permissions */ + if(!capable(CAP_NET_ADMIN)) /* For private IOCTLs, we need to check permissions */ { err = -EPERM; break; diff --git a/drivers/net/pcmcia/tulip_cb.c b/drivers/net/pcmcia/tulip_cb.c deleted file mode 100644 index 38dcdc0c1..000000000 --- a/drivers/net/pcmcia/tulip_cb.c +++ /dev/null @@ -1,3150 +0,0 @@ -/* tulip.c: A DEC 21040-family ethernet driver for Linux. */ -/* - Written/copyright 1994-1999 by Donald Becker. - - This software may be used and distributed according to the terms - of the GNU Public License, incorporated herein by reference. - - This driver is for the Digital "Tulip" Ethernet adapter interface. - It should work with most DEC 21*4*-based chips/ethercards, as well as - with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX. - - The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O - Center of Excellence in Space Data and Information Sciences - Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 - - Support and updates available at - http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html -*/ - -#define SMP_CHECK -static const char version[] = "tulip.c:v0.91 4/14/99 becker@cesdis.gsfc.nasa.gov (modified by danilo@cs.uni-magdeburg.de for XIRCOM CBE, fixed by Doug Ledford)\n"; - -/* A few user-configurable values. */ - -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 25; - -#define MAX_UNITS 8 -/* Used to pass the full-duplex flag, etc. */ -static int full_duplex[MAX_UNITS] = {0, }; -static int options[MAX_UNITS] = {0, }; -static int mtu[MAX_UNITS] = {0, }; /* Jumbo MTU for interfaces. */ - -/* The possible media types that can be set in options[] are: */ -static const char * const medianame[] = { - "10baseT", "10base2", "AUI", "100baseTx", - "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx", - "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII", - "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4", -}; - -/* Keep the ring sizes a power of two for efficiency. - Making the Tx ring too large decreases the effectiveness of channel - bonding and packet priority. - There are no ill effects from too-large receive rings. */ -#define TX_RING_SIZE 16 -#define RX_RING_SIZE 32 - -/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ -#ifdef __alpha__ -static int rx_copybreak = 1518; -#else -static int rx_copybreak = 100; -#endif - -/* - Set the bus performance register. - Typical: Set 16 longword cache alignment, no burst limit. - Cache alignment bits 15:14 Burst length 13:8 - 0000 No alignment 0x00000000 unlimited 0800 8 longwords - 4000 8 longwords 0100 1 longword 1000 16 longwords - 8000 16 longwords 0200 2 longwords 2000 32 longwords - C000 32 longwords 0400 4 longwords - Warning: many older 486 systems are broken and require setting 0x00A04800 - 8 longword cache alignment, 8 longword burst. - ToDo: Non-Intel setting could be better. -*/ - -#if defined(__alpha__) -static int csr0 = 0x01A00000 | 0xE000; -#elif defined(__powerpc__) -static int csr0 = 0x01B00000 | 0x8000; -#elif defined(__sparc__) -static int csr0 = 0x01B00080 | 0x8000; -#elif defined(__i386__) -static int csr0 = 0x01A00000 | 0x8000; -#else -#warning Processor architecture undefined! -static int csr0 = 0x00A00000 | 0x4800; -#endif - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (4*HZ) -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ -/* This is a mysterious value that can be written to CSR11 in the 21040 (only) - to support a pre-NWay full-duplex signaling mechanism using short frames. - No one knows what it should be, but if left at its default value some - 10base2(!) packets trigger a full-duplex-request interrupt. */ -#define FULL_DUPLEX_MAGIC 0x6969 - -#if !defined(__OPTIMIZE__) || !defined(__KERNEL__) -#warning You must compile this file with the correct options! -#warning See the last lines of the source file. -#error You must compile this driver with "-O". -#endif - -#include <linux/version.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/malloc.h> -#include <linux/interrupt.h> -#include <linux/pci.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/delay.h> -#include <linux/init.h> -#include <asm/processor.h> /* Processor type for cache alignment. */ -#include <asm/bitops.h> -#include <asm/io.h> -#include <asm/unaligned.h> - -/* Kernel compatibility defines, some common to David Hinds' PCMCIA package. - This is only in the support-all-kernels source code. */ - -MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>"); -MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver"); -MODULE_PARM(debug, "i"); -MODULE_PARM(max_interrupt_work, "i"); -MODULE_PARM(reverse_probe, "i"); -MODULE_PARM(rx_copybreak, "i"); -MODULE_PARM(csr0, "i"); -MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); - -#define RUN_AT(x) (jiffies + (x)) - -#define tulip_debug debug -#ifdef TULIP_DEBUG -static int tulip_debug = TULIP_DEBUG; -#else -static int tulip_debug = 1; -#endif - -/* - Theory of Operation - -I. Board Compatibility - -This device driver is designed for the DECchip "Tulip", Digital's -single-chip ethernet controllers for PCI. Supported members of the family -are the 21040, 21041, 21140, 21140A, 21142, and 21143. Similar work-alike -chips from Lite-On, Macronics, ASIX, Compex and other listed below are also -supported. - -These chips are used on at least 140 unique PCI board designs. The great -number of chips and board designs supported is the reason for the -driver size and complexity. Almost of the increasing complexity is in the -board configuration and media selection code. There is very little -increasing in the operational critical path length. - -II. Board-specific settings - -PCI bus devices are configured by the system at boot time, so no jumpers -need to be set on the board. The system BIOS preferably should assign the -PCI INTA signal to an otherwise unused system IRQ line. - -Some boards have EEPROMs tables with default media entry. The factory default -is usually "autoselect". This should only be overridden when using -transceiver connections without link beat e.g. 10base2 or AUI, or (rarely!) -for forcing full-duplex when used with old link partners that do not do -autonegotiation. - -III. Driver operation - -IIIa. Ring buffers - -The Tulip can use either ring buffers or lists of Tx and Rx descriptors. -This driver uses statically allocated rings of Rx and Tx descriptors, set at -compile time by RX/TX_RING_SIZE. This version of the driver allocates skbuffs -for the Rx ring buffers at open() time and passes the skb->data field to the -Tulip as receive data buffers. When an incoming frame is less than -RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is -copied to the new skbuff. When the incoming frame is larger, the skbuff is -passed directly up the protocol stack and replaced by a newly allocated -skbuff. - -The RX_COPYBREAK value is chosen to trade-off the memory wasted by -using a full-sized skbuff for small frames vs. the copying costs of larger -frames. For small frames the copying cost is negligible (esp. considering -that we are pre-loading the cache with immediately useful header -information). For large frames the copying cost is non-trivial, and the -larger copy might flush the cache of useful data. A subtle aspect of this -choice is that the Tulip only receives into longword aligned buffers, thus -the IP header at offset 14 isn't longword aligned for further processing. -Copied frames are put into the new skbuff at an offset of "+2", thus copying -has the beneficial effect of aligning the IP header and preloading the -cache. - -IIIC. Synchronization -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the -dev->tbusy flag. The other thread is the interrupt handler, which is single -threaded by the hardware and other software. - -The send packet thread has partial control over the Tx ring and 'dev->tbusy' -flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next -queue slot is empty, it clears the tbusy flag when finished otherwise it sets -the 'tp->tx_full' flag. - -The interrupt handler has exclusive control over the Rx ring and records stats -from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so -we can't avoid the interrupt overhead by having the Tx routine reap the Tx -stats.) After reaping the stats, it marks the queue entry as empty by setting -the 'base' to zero. Iff the 'tp->tx_full' flag is set, it clears both the -tx_full and tbusy flags. - -IV. Notes - -Thanks to Duke Kamstra of SMC for long ago providing an EtherPower board. -Greg LaPolla at Linksys provided PNIC and other Linksys boards. -Znyx provided a four-port card for testing. - -IVb. References - -http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html -http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM") -http://www.national.com/pf/DP/DP83840A.html -http://www.asix.com.tw/pmac.htm -http://www.admtek.com.tw/ - -IVc. Errata - -The old DEC databooks were light on details. -The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last -register of the set CSR12-15 written. Hmmm, now how is that possible? - -The DEC SROM format is very badly designed not precisely defined, leading to -part of the media selection junkheap below. Some boards do not have EEPROM -media tables and need to be patched up. Worse, other boards use the DEC -design kit media table when it isn't correct for their board. - -We cannot use MII interrupts because there is no defined GPIO pin to attach -them. The MII transceiver status is polled using an kernel timer. - -*/ - -/* This table use during operation for capabilities and media timer. */ - -static void tulip_timer(unsigned long data); -static void t21142_timer(unsigned long data); -static void mxic_timer(unsigned long data); -static void pnic_timer(unsigned long data); -static void comet_timer(unsigned long data); - -enum tbl_flag { - HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8, - HAS_ACPI=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */ - HAS_NWAY143=0x40, /* Uses 21143-like internal NWay. */ -}; -static struct tulip_chip_table { - char *chip_name; - int io_size; - int valid_intrs; /* CSR7 interrupt enable settings */ - int flags; - void (*media_timer)(unsigned long data); -} tulip_tbl[] = { - { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer }, - { "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer }, - { "Digital DS21140 Tulip", 128, 0x0001ebef, - HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer }, - { "Digital DS21143 Tulip", 128, 0x0801fbff, - HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY143, t21142_timer }, - { "Lite-On 82c168 PNIC", 256, 0x0001ebef, - HAS_MII, pnic_timer }, - { "Macronix 98713 PMAC", 128, 0x0001ebef, - HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer }, - { "Macronix 98715 PMAC", 256, 0x0001ebef, - HAS_MEDIA_TABLE, mxic_timer }, - { "Macronix 98725 PMAC", 256, 0x0001ebef, - HAS_MEDIA_TABLE, mxic_timer }, - { "ASIX AX88140", 128, 0x0001fbff, - HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY, tulip_timer }, - { "Lite-On PNIC-II", 256, 0x0001ebef, - HAS_MII | HAS_NWAY143, pnic_timer }, - { "ADMtek Comet", 256, 0x0001abef, - MC_HASH_ONLY, comet_timer }, - { "Compex 9881 PMAC", 128, 0x0001ebef, - HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer }, - { "Xircom Cardbus Adapter (DEC 21143 compatible mode)", 128, 0x0801fbff, - HAS_MII | HAS_ACPI, tulip_timer }, - {0}, -}; -/* This matches the table above. Note 21142 == 21143. */ -enum chips { - DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3, - LC82C168, MX98713, MX98715, MX98725, AX88140, PNIC2, COMET, COMPEX9881, - X3201_3, -}; - -/* A full-duplex map for media types. */ -enum MediaIs { - MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, - MediaIs100=16}; -static const char media_cap[] = -{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20 }; -static u8 t21040_csr13[] = {2,0x0C,8,4, 4,0,0,0, 0,0,0,0, 4,0,0,0}; -/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/ -static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; -static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, }; -static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; - -static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, }; -static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, }; -static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; - -/* Offsets to the Command and Status Registers, "CSRs". All accesses - must be longword instructions and quadword aligned. */ -enum tulip_offsets { - CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, - CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, - CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 }; - -/* The bits in the CSR5 status registers, mostly interrupt sources. */ -enum status_bits { - TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10, - NormalIntr=0x10000, AbnormalIntr=0x8000, - RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40, - TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01, -}; - -/* The Tulip Rx and Tx buffer descriptors. */ -struct tulip_rx_desc { - s32 status; - s32 length; - u32 buffer1, buffer2; -}; - -struct tulip_tx_desc { - s32 status; - s32 length; - u32 buffer1, buffer2; /* We use only buffer 1. */ -}; - -enum desc_status_bits { - DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300, -}; - -/* Ring-wrap flag in length field, use for last ring entry. - 0x01000000 means chain on buffer2 address, - 0x02000000 means use the ring start address in CSR2/3. - Note: Some work-alike chips do not function correctly in chained mode. - The ASIX chip works only in chained mode. - Thus we indicates ring mode, but always write the 'next' field for - chained mode as well. -*/ -#define DESC_RING_WRAP 0x02000000 - -#ifdef CARDBUS -#define EEPROM_ADDRLEN (chip_rev == 65 ? 8 : 6) -#else -#define EEPROM_ADDRLEN 6 -#endif -#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */ - -struct medialeaf { - u8 type; - u8 media; - unsigned char *leafdata; -}; - -struct mediatable { - u16 defaultmedia; - u8 leafcount, csr12dir; /* General purpose pin directions. */ - unsigned has_mii:1, has_nonmii:1, has_reset:6; - u32 csr15dir, csr15val; /* 21143 NWay setting. */ - struct medialeaf mleaf[0]; -}; - -struct mediainfo { - struct mediainfo *next; - int info_type; - int index; - unsigned char *info; -}; - -struct tulip_private { - char devname[8]; /* Used only for kernel debugging. */ - const char *product_name; - struct tulip_rx_desc rx_ring[RX_RING_SIZE]; - struct tulip_tx_desc tx_ring[TX_RING_SIZE]; - /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - struct sk_buff* tx_skbuff[TX_RING_SIZE]; -#ifdef CARDBUS - /* The X3201-3 requires double word aligned tx bufs */ - struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE]; -#endif - /* The addresses of receive-in-place skbuffs. */ - struct sk_buff* rx_skbuff[RX_RING_SIZE]; - char *rx_buffs; /* Address of temporary Rx buffers. */ - u8 setup_buf[96*sizeof(u16) + 7]; - u16 *setup_frame; /* Pseudo-Tx frame to init address table. */ - int chip_id; - int revision; - struct net_device_stats stats; - struct timer_list timer; /* Media selection timer. */ - int interrupt; /* In-interrupt flag. */ - unsigned int cur_rx, cur_tx; /* The next free ring entry */ - unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ - unsigned int tx_full:1; /* The Tx queue is full. */ - unsigned int full_duplex:1; /* Full-duplex operation requested. */ - unsigned int full_duplex_lock:1; - unsigned int fake_addr:1; /* Multiport board faked address. */ - unsigned int default_port:4; /* Last dev->if_port value. */ - unsigned int media2:4; /* Secondary monitored media port. */ - unsigned int medialock:1; /* Don't sense media type. */ - unsigned int mediasense:1; /* Media sensing in progress. */ - unsigned int nway:1, nwayset:1; /* 21143 internal NWay. */ - unsigned int open:1; - unsigned int csr0; /* CSR0 setting. */ - unsigned int csr6; /* Current CSR6 control settings. */ - unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */ - u16 to_advertise; /* NWay capabilities advertised. */ - u16 lpar; /* 21143 Link partner ability. */ - u16 advertising[4]; - signed char phys[4], mii_cnt; /* MII device addresses. */ - struct mediatable *mtable; - int cur_index; /* Current media index. */ - int saved_if_port; - struct pci_dev *pdev; - spinlock_t lock; - int pad0, pad1; /* Used for 8-byte alignment */ -}; - -static void parse_eeprom(struct net_device *dev); -static int read_eeprom(long ioaddr, int location, int addr_len); -static int mdio_read(struct net_device *dev, int phy_id, int location); -static void mdio_write(struct net_device *dev, int phy_id, int location, int value); -static void select_media(struct net_device *dev, int startup); -static void tulip_up(struct net_device *dev); -static void tulip_down(struct net_device *dev); -static int tulip_open(struct net_device *dev); -static void tulip_timer(unsigned long data); -static void t21142_start_nway(struct net_device *dev); -static void tulip_tx_timeout(struct net_device *dev); -static void tulip_init_ring(struct net_device *dev); -static int tulip_start_xmit(struct sk_buff *skb, struct net_device *dev); -static int tulip_rx(struct net_device *dev); -static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs); -static int tulip_close(struct net_device *dev); -static struct net_device_stats *tulip_get_stats(struct net_device *dev); -#ifdef HAVE_PRIVATE_IOCTL -static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -#endif -static void set_rx_mode(struct net_device *dev); - -/* The Xircom cards are picky about when certain bits in CSR6 can be - manipulated. Keith Owens <kaos@ocs.com.au>. */ - -static void outl_CSR6 (u32 newcsr6, long ioaddr, int chip_idx) -{ - const int strict_bits = 0x0060e202; - int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200; - long flags; - save_flags(flags); - cli(); - if (chip_idx != X3201_3) { - outl(newcsr6, ioaddr + CSR6); - restore_flags(flags); - return; - } - newcsr6 &= 0x726cfeca; /* mask out the reserved CSR6 bits that always */ - /* read 0 on the Xircom cards */ - newcsr6 |= 0x320c0000; /* or in the reserved bits that always read 1 */ - currcsr6 = inl(ioaddr + CSR6); - if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) || - ((currcsr6 & ~0x2002) == 0)) { - outl(newcsr6, ioaddr + CSR6); /* safe */ - restore_flags(flags); - return; - } - /* make sure the transmitter and receiver are stopped first */ - currcsr6 &= ~0x2002; - while (1) { - csr5 = inl(ioaddr + CSR5); - if (csr5 == 0xffffffff) - break; /* cannot read csr5, card removed? */ - csr5_22_20 = csr5 & 0x700000; - csr5_19_17 = csr5 & 0x0e0000; - if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) && - (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000)) - break; /* both are stopped or suspended */ - if (!--attempts) { - printk(KERN_INFO "tulip.c: outl_CSR6 too many attempts," - "csr5=0x%08x\n", csr5); - outl(newcsr6, ioaddr + CSR6); /* unsafe but do it anyway */ - restore_flags(flags); - return; - } - outl(currcsr6, ioaddr + CSR6); - udelay(1); - } - /* now it is safe to change csr6 */ - outl(newcsr6, ioaddr + CSR6); - restore_flags(flags); -} - -static struct net_device *tulip_probe1(struct pci_dev *pdev, - struct net_device *dev, long ioaddr, int irq, - int chip_idx, int board_idx) -{ - static int did_version = 0; /* Already printed version info. */ - struct tulip_private *tp; - /* See note below on the multiport cards. */ - static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; - static int last_irq = 0; - static int multiport_cnt = 0; /* For four-port boards w/one EEPROM */ - u8 chip_rev; - int i; - unsigned short sum; - - if (tulip_debug > 0 && did_version++ == 0) - printk(KERN_INFO "%s", version); - - dev = init_etherdev(dev, 0); - - pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev); - /* Bring the 21143 out of sleep mode. - Caution: Snooze mode does not work with some boards! */ - if (tulip_tbl[chip_idx].flags & HAS_ACPI) - pci_write_config_dword(pdev, 0x40, 0x00000000); - - printk(KERN_INFO "%s: %s rev %d at %#3lx,", - dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr); - - /* Stop the chip's Tx and Rx processes. */ - outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, chip_idx); - /* Clear the missed-packet counter. */ - (volatile int)inl(ioaddr + CSR8); - - if (chip_idx == DC21041) { - if (inl(ioaddr + CSR9) & 0x8000) { - printk(" 21040 compatible mode,"); - chip_idx = DC21040; - } else { - printk(" 21041 mode,"); - } - } - - /* The station address ROM is read byte serially. The register must - be polled, waiting for the value to be read bit serially from the - EEPROM. - */ - sum = 0; - if (chip_idx == DC21040) { - outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */ - for (i = 0; i < 6; i++) { - int value, boguscnt = 100000; - do - value = inl(ioaddr + CSR9); - while (value < 0 && --boguscnt > 0); - dev->dev_addr[i] = value; - sum += value & 0xff; - } - } else if (chip_idx == LC82C168) { - for (i = 0; i < 3; i++) { - int value, boguscnt = 100000; - outl(0x600 | i, ioaddr + 0x98); - do - value = inl(ioaddr + CSR9); - while (value < 0 && --boguscnt > 0); - put_unaligned(le16_to_cpu(value), ((u16*)dev->dev_addr) + i); - sum += value & 0xffff; - } - } else if (chip_idx == COMET) { - /* No need to read the EEPROM. */ - put_unaligned(inl(ioaddr + 0xA4), (u32 *)dev->dev_addr); - put_unaligned(inl(ioaddr + 0xA8), (u16 *)(dev->dev_addr + 4)); - for (i = 0; i < 6; i ++) - sum += dev->dev_addr[i]; - } else if (chip_idx == X3201_3) { - /* Xircom has its address stored in the CIS - * we access it through the boot rom interface for now - * this might not work, as the CIS is not parsed but I - * (danilo) use the offset I found on my card's CIS !!! - * - * Doug Ledford: I changed this routine around so that it - * walks the CIS memory space, parsing the config items, and - * finds the proper lan_node_id tuple and uses the data - * stored there. - */ - unsigned char j, tuple, link, data_id, data_count; - outl(1<<12, ioaddr + CSR9); /* enable boot rom access */ - for (i = 0x100; i < 0x1f7; i += link+2) { - outl(i, ioaddr + CSR10); - tuple = inl(ioaddr + CSR9) & 0xff; - outl(i + 1, ioaddr + CSR10); - link = inl(ioaddr + CSR9) & 0xff; - outl(i + 2, ioaddr + CSR10); - data_id = inl(ioaddr + CSR9) & 0xff; - outl(i + 3, ioaddr + CSR10); - data_count = inl(ioaddr + CSR9) & 0xff; - if ( (tuple == 0x22) && - (data_id == 0x04) && (data_count == 0x06) ) { - /* - * This is it. We have the data we want. - */ - for (j = 0; j < 6; j++) { - outl(i + j + 4, ioaddr + CSR10); - dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff; - } - break; - } else if (link == 0) { - break; - } - } - sum = 1; // to make check below fail! - } else { /* Must be a new chip, with a serial EEPROM interface. */ - /* We read the whole EEPROM, and sort it out later. DEC has a - specification _Digital Semiconductor 21X4 Serial ROM Format_ - but early vendor boards just put the address in the first six - EEPROM locations. */ - unsigned char ee_data[EEPROM_SIZE]; - int sa_offset = 0; - - for (i = 0; i < sizeof(ee_data)/2; i++) - ((u16 *)ee_data)[i] = - le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN)); - - /* Detect the simple EEPROM format by the duplicated station addr. */ - for (i = 0; i < 8; i ++) - if (ee_data[i] != ee_data[16+i]) - sa_offset = 20; - if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) { - sa_offset = 2; /* Grrr, damn Matrox boards. */ - multiport_cnt = 4; - } - for (i = 0; i < 6; i ++) { - dev->dev_addr[i] = ee_data[i + sa_offset]; - sum += ee_data[i + sa_offset]; - } - } - /* Lite-On boards have the address byte-swapped. */ - if ((dev->dev_addr[0] == 0xA0 || dev->dev_addr[0] == 0xC0) - && dev->dev_addr[1] == 0x00) - for (i = 0; i < 6; i+=2) { - char tmp = dev->dev_addr[i]; - dev->dev_addr[i] = dev->dev_addr[i+1]; - dev->dev_addr[i+1] = tmp; - } - /* On the Zynx 315 Etherarray and other multiport boards only the - first Tulip has an EEPROM. - The addresses of the subsequent ports are derived from the first. - Many PCI BIOSes also incorrectly report the IRQ line, so we correct - that here as well. */ - if (sum == 0 || sum == 6*0xff) { - printk(" EEPROM not present,"); - for (i = 0; i < 5; i++) - dev->dev_addr[i] = last_phys_addr[i]; - dev->dev_addr[i] = last_phys_addr[i] + 1; -#if defined(__i386__) /* Patch up x86 BIOS bug. */ - if (last_irq) - irq = last_irq; -#endif - } - - for (i = 0; i < 6; i++) - printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]); - printk(", IRQ %d.\n", irq); - last_irq = irq; - - /* We do a request_region() only to register /proc/ioports info. */ - /* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */ - request_region(ioaddr, tulip_tbl[chip_idx].io_size, dev->name); - - dev->base_addr = ioaddr; - dev->irq = irq; - - /* Make certain the data structures are quadword aligned. */ - tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7); - memset(tp, 0, sizeof(*tp)); - dev->priv = tp; - - tp->lock = SPIN_LOCK_UNLOCKED; - tp->pdev = pdev; - tp->chip_id = chip_idx; - tp->revision = chip_rev; - tp->csr0 = csr0; - tp->setup_frame = (u16 *)(((unsigned long)tp->setup_buf + 7) & ~7); - - /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles. - And the ASIX must have a burst limit or horrible things happen. */ - if ( (chip_idx == DC21143 && chip_rev == 65) || - (chip_idx == X3201_3) ) - tp->csr0 &= ~0x01000000; - else if (chip_idx == AX88140) - tp->csr0 |= 0x2000; - -#ifdef TULIP_FULL_DUPLEX - tp->full_duplex = 1; - tp->full_duplex_lock = 1; -#endif -#ifdef TULIP_DEFAULT_MEDIA - tp->default_port = TULIP_DEFAULT_MEDIA; -#endif -#ifdef TULIP_NO_MEDIA_SWITCH - tp->medialock = 1; -#endif - - /* The lower four bits are the media type. */ - if (board_idx >= 0 && board_idx < MAX_UNITS) { - tp->default_port = options[board_idx] & 15; - if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0) - tp->full_duplex = 1; - if (mtu[board_idx] > 0) - dev->mtu = mtu[board_idx]; - } - if (dev->mem_start) - tp->default_port = dev->mem_start; - if (tp->default_port) { - tp->medialock = 1; - if (media_cap[tp->default_port] & MediaAlwaysFD) - tp->full_duplex = 1; - } - if (tp->full_duplex) - tp->full_duplex_lock = 1; - - /* This is logically part of probe1(), but too complex to write inline. */ - if (tulip_tbl[chip_idx].flags & HAS_MEDIA_TABLE) - parse_eeprom(dev); - - if (media_cap[tp->default_port] & MediaIsMII) { - u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 }; - tp->to_advertise = media2advert[tp->default_port - 9]; - } else - tp->to_advertise = 0x03e1; - - if ((tulip_tbl[chip_idx].flags & ALWAYS_CHECK_MII) || - (tp->mtable && tp->mtable->has_mii) || - ( ! tp->mtable && (tulip_tbl[chip_idx].flags & HAS_MII))) { - int phy, phy_idx; - if (tp->mtable && tp->mtable->has_mii) { - for (i = 0; i < tp->mtable->leafcount; i++) - if (tp->mtable->mleaf[i].media == 11) { - tp->cur_index = i; - tp->saved_if_port = dev->if_port; - select_media(dev, 1); - dev->if_port = tp->saved_if_port; - break; - } - } - /* Find the connected MII xcvrs. - Doing this in open() would allow detecting external xcvrs later, - but takes much time. */ - for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); - phy++) { - int mii_status = mdio_read(dev, phy, 1); - if ((mii_status & 0x8301) == 0x8001 || - ((mii_status & 0x8000) == 0 && (mii_status & 0x7800) != 0)) { - int mii_reg0 = mdio_read(dev, phy, 0); - int mii_advert = mdio_read(dev, phy, 4); - int reg4 = ((mii_status>>6) & tp->to_advertise) | 1; - tp->phys[phy_idx] = phy; - tp->advertising[phy_idx++] = reg4; - printk(KERN_INFO "%s: MII transceiver #%d " - "config %4.4x status %4.4x advertising %4.4x.\n", - dev->name, phy, mii_reg0, mii_status, mii_advert); - /* Fixup for DLink with miswired PHY. */ - if (mii_advert != reg4) { - printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d," - " previously advertising %4.4x.\n", - dev->name, reg4, phy, mii_advert); - mdio_write(dev, phy, 4, reg4); - } - /* Enable autonegotiation: some boards default to off. */ - mdio_write(dev, phy, 0, mii_reg0 | - (tp->full_duplex ? 0x1100 : 0x1000) | - (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0)); - } - } - tp->mii_cnt = phy_idx; - if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) { - printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n", - dev->name); - tp->phys[0] = 1; - } - } - - /* The Tulip-specific entries in the device structure. */ - dev->open = &tulip_open; - dev->hard_start_xmit = &tulip_start_xmit; - dev->stop = &tulip_close; - dev->get_stats = &tulip_get_stats; -#ifdef HAVE_PRIVATE_IOCTL - dev->do_ioctl = &private_ioctl; -#endif -#ifdef HAVE_MULTICAST - dev->set_multicast_list = &set_rx_mode; -#endif - dev->tx_timeout = tulip_tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; - - /* Reset the xcvr interface and turn on heartbeat. */ - switch (chip_idx) { - case DC21041: - outl(0x00000000, ioaddr + CSR13); - outl(0xFFFFFFFF, ioaddr + CSR14); - outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */ - outl_CSR6(inl(ioaddr + CSR6) | 0x0200, ioaddr, chip_idx); - outl(0x0000EF05, ioaddr + CSR13); - break; - case DC21040: - outl(0x00000000, ioaddr + CSR13); - outl(0x00000004, ioaddr + CSR13); - break; - case DC21140: default: - if (tp->mtable) - outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); - break; - case DC21142: - case PNIC2: - if (tp->mii_cnt || media_cap[dev->if_port] & MediaIsMII) { - outl_CSR6(0x82020000, ioaddr, chip_idx); - outl(0x0000, ioaddr + CSR13); - outl(0x0000, ioaddr + CSR14); - outl_CSR6(0x820E0000, ioaddr, chip_idx); - } else { - outl_CSR6(0x82420200, ioaddr, chip_idx); - outl(0x0001, ioaddr + CSR13); - outl(0x0003FFFF, ioaddr + CSR14); - outl(0x0008, ioaddr + CSR15); - outl(0x0001, ioaddr + CSR13); - outl(0x1301, ioaddr + CSR12); /* Start NWay. */ - } - break; - case X3201_3: - outl(0x0008, ioaddr + CSR15); - udelay(5); /* The delays are Xircom recommended to give the - * chipset time to reset the actual hardware - * on the PCMCIA card - */ - outl(0xa8050000, ioaddr + CSR15); - udelay(5); - outl(0xa00f0000, ioaddr + CSR15); - udelay(5); - outl_CSR6(0x32000200, ioaddr, chip_idx); - break; - case LC82C168: - if ( ! tp->mii_cnt) { - outl_CSR6(0x00420000, ioaddr, chip_idx); - outl(0x30, ioaddr + CSR12); - outl(0x0001F078, ioaddr + 0xB8); - outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ - } - break; - case MX98713: case COMPEX9881: - outl_CSR6(0x00000000, ioaddr, chip_idx); - outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */ - outl(0x00000001, ioaddr + CSR13); - break; - case MX98715: case MX98725: - outl_CSR6(0x01a80000, ioaddr, chip_idx); - outl(0xFFFFFFFF, ioaddr + CSR14); - outl(0x00001000, ioaddr + CSR12); - break; - case COMET: - /* No initialization necessary. */ - break; - } - - return dev; -} - -/* Serial EEPROM section. */ -/* The main routine to parse the very complicated SROM structure. - Search www.digital.com for "21X4 SROM" to get details. - This code is very complex, and will require changes to support - additional cards, so I'll be verbose about what is going on. - */ - -/* Known cards that have old-style EEPROMs. */ -static struct fixups { - char *name; - unsigned char addr0, addr1, addr2; - u16 newtable[32]; /* Max length below. */ -} eeprom_fixups[] = { - {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c, - 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }}, - {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f, - 0x0000, 0x009E, /* 10baseT */ - 0x0903, 0x006D, /* 100baseTx */ }}, - {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f, - 0x0107, 0x8021, /* 100baseFx */ - 0x0108, 0x8021, /* 100baseFx-FD */ - 0x0103, 0x006D, /* 100baseTx */ }}, - {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313, - 0x1001, 0x009E, /* 10base2, CSR12 0x10*/ - 0x0000, 0x009E, /* 10baseT */ - 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }}, - {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F, - 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ - 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ - 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ - }}, - {0, 0, 0, 0, {}}}; - -static const char * block_name[] = {"21140 non-MII", "21140 MII PHY", - "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"}; - -#if defined(__i386__) /* AKA get_unaligned() */ -#define get_u16(ptr) (*(u16 *)(ptr)) -#else -#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8)) -#endif - -static void parse_eeprom(struct net_device *dev) -{ - /* The last media info list parsed, for multiport boards. */ - static struct mediatable *last_mediatable = NULL; - static unsigned char *last_ee_data = NULL; - static int controller_index = 0; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; - unsigned char *ee_data = tp->eeprom; - int i; -#ifdef CARDBUS - int chip_rev = tp->revision; -#endif - - tp->mtable = 0; - for (i = 0; i < EEPROM_SIZE/2; i++) - ((u16 *)ee_data)[i] = - le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN)); - - /* Detect an old-style (SA only) EEPROM layout: - memcmp(eedata, eedata+16, 8). */ - for (i = 0; i < 8; i ++) - if (ee_data[i] != ee_data[16+i]) - break; - if (i >= 8) { - if (ee_data[0] == 0xff) { - if (last_mediatable) { - controller_index++; - printk(KERN_INFO "%s: Controller %d of multiport board.\n", - dev->name, controller_index); - tp->mtable = last_mediatable; - ee_data = last_ee_data; - goto subsequent_board; - } else - printk(KERN_INFO "%s: Missing EEPROM, this interface may " - "not work correctly!\n", - dev->name); - return; - } - /* Do a fix-up based on the vendor half of the station address prefix. */ - for (i = 0; eeprom_fixups[i].name; i++) { - if (dev->dev_addr[0] == eeprom_fixups[i].addr0 - && dev->dev_addr[1] == eeprom_fixups[i].addr1 - && dev->dev_addr[2] == eeprom_fixups[i].addr2) { - if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55) - i++; /* An Accton EN1207, not an outlaw Maxtech. */ - memcpy(ee_data + 26, eeprom_fixups[i].newtable, - sizeof(eeprom_fixups[i].newtable)); - printk(KERN_INFO "%s: Old format EEPROM on '%s' board. Using" - " substitute media control info.\n", - dev->name, eeprom_fixups[i].name); - break; - } - } - if (eeprom_fixups[i].name == NULL) { /* No fixup found. */ - printk(KERN_INFO "%s: Old style EEPROM with no media selection " - "information.\n", - dev->name); - return; - } - } - - controller_index = 0; - if (ee_data[19] > 1) { /* Multiport board. */ - last_ee_data = ee_data; - } -subsequent_board: - - if (ee_data[27] == 0) { /* No valid media table. */ - } else if (tp->chip_id == DC21041) { - unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3]; - short media; - int count; - - media = get_u16(p); - p += 2; - count = *p++; - - printk(KERN_INFO "%s:21041 Media information at %d, default media " - "%4.4x (%s).\n", dev->name, ee_data[27], media, - media & 0x0800 ? "Autosense" : medianame[media & 15]); - for (i = 0; i < count; i++) { - unsigned char media_code = *p++; - u16 csrvals[3]; - int idx; - for (idx = 0; idx < 3; idx++) { - csrvals[idx] = get_u16(p); - p += 2; - } - if (media_code & 0x40) { - printk(KERN_INFO "%s: 21041 media %2.2x (%s)," - " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n", - dev->name, media_code & 15, medianame[media_code & 15], - csrvals[0], csrvals[1], csrvals[2]); - } else - printk(KERN_INFO "%s: 21041 media #%d, %s.\n", - dev->name, media_code & 15, medianame[media_code & 15]); - } - } else { - unsigned char *p = (void *)ee_data + ee_data[27]; - unsigned char csr12dir = 0; - int count; - struct mediatable *mtable; - u16 media = get_u16(p); - - p += 2; - if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM) - csr12dir = *p++; - count = *p++; - mtable = (struct mediatable *) - kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf), - GFP_KERNEL); - if (mtable == NULL) - return; /* Horrible, impossible failure. */ - last_mediatable = tp->mtable = mtable; - mtable->defaultmedia = media; - mtable->leafcount = count; - mtable->csr12dir = csr12dir; - mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0; - mtable->csr15dir = mtable->csr15val = 0; - - printk(KERN_INFO "%s: EEPROM default media type %s.\n", dev->name, - media & 0x0800 ? "Autosense" : medianame[media & 15]); - for (i = 0; i < count; i++) { - struct medialeaf *leaf = &mtable->mleaf[i]; - - if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */ - leaf->type = 0; - leaf->media = p[0] & 0x3f; - leaf->leafdata = p; - if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */ - mtable->has_mii = 1; - p += 4; - } else { - leaf->type = p[1]; - if (p[1] == 0x05) { - mtable->has_reset = i; - leaf->media = p[2] & 0x0f; - } else if (p[1] & 1) { - mtable->has_mii = 1; - leaf->media = 11; - } else { - mtable->has_nonmii = 1; - leaf->media = p[2] & 0x0f; - if (p[1] == 2) { - if (leaf->media == 0) { - mtable->csr15dir = get_unaligned((u16*)&p[3])<<16; - mtable->csr15val = get_unaligned((u16*)&p[5])<<16; - } else if (leaf->media == 0x40) { - u32 base15 = get_unaligned((u16*)&p[7]); - mtable->csr15dir = - (get_unaligned((u16*)&p[9])<<16) + base15; - mtable->csr15val = - (get_unaligned((u16*)&p[11])<<16) + base15; - } - } - } - leaf->leafdata = p + 2; - p += (p[0] & 0x3f) + 1; - } - if (tulip_debug > 1 && leaf->media == 11) { - unsigned char *bp = leaf->leafdata; - printk(KERN_INFO "%s: MII interface PHY %d, setup/reset " - "sequences %d/%d long, capabilities %2.2x %2.2x.\n", - dev->name, bp[0], bp[1], bp[1 + bp[1]*2], - bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]); - } - printk(KERN_INFO "%s: Index #%d - Media %s (#%d) described " - "by a %s (%d) block.\n", - dev->name, i, medianame[leaf->media], leaf->media, - block_name[leaf->type], leaf->type); - } - } -} -/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/ - -/* EEPROM_Ctrl bits. */ -#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ -#define EE_CS 0x01 /* EEPROM chip select. */ -#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ -#define EE_WRITE_0 0x01 -#define EE_WRITE_1 0x05 -#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ -#define EE_ENB (0x4800 | EE_CS) - -/* Delay between EEPROM clock transitions. - Even at 33Mhz current PCI implementations don't overrun the EEPROM clock. - We add a bus turn-around to insure that this remains true. */ -#define eeprom_delay() inl(ee_addr) - -/* The EEPROM commands include the alway-set leading bit. */ -#define EE_WRITE_CMD (5 << addr_len) -#define EE_READ_CMD (6 << addr_len) -#define EE_ERASE_CMD (7 << addr_len) - -static int read_eeprom(long ioaddr, int location, int addr_len) -{ - int i; - unsigned short retval = 0; - long ee_addr = ioaddr + CSR9; - int read_cmd = location | EE_READ_CMD; - - outl(EE_ENB & ~EE_CS, ee_addr); - outl(EE_ENB, ee_addr); - - /* Shift the read command bits out. */ - for (i = 4 + addr_len; i >= 0; i--) { - short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; - outl(EE_ENB | dataval, ee_addr); - eeprom_delay(); - outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); - eeprom_delay(); - } - outl(EE_ENB, ee_addr); - - for (i = 16; i > 0; i--) { - outl(EE_ENB | EE_SHIFT_CLK, ee_addr); - eeprom_delay(); - retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); - outl(EE_ENB, ee_addr); - eeprom_delay(); - } - - /* Terminate the EEPROM access. */ - outl(EE_ENB & ~EE_CS, ee_addr); - return retval; -} - -/* MII transceiver control section. - Read and write the MII registers using software-generated serial - MDIO protocol. See the MII specifications or DP83840A data sheet - for details. */ - -/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually - met by back-to-back PCI I/O cycles, but we insert a delay to avoid - "overclocking" issues or future 66Mhz PCI. */ -#define mdio_delay() inl(mdio_addr) - -/* Read and write the MII registers using software-generated serial - MDIO protocol. It is just different enough from the EEPROM protocol - to not share code. The maxium data clock rate is 2.5 Mhz. */ -#define MDIO_SHIFT_CLK 0x10000 -#define MDIO_DATA_WRITE0 0x00000 -#define MDIO_DATA_WRITE1 0x20000 -#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ -#define MDIO_ENB_IN 0x40000 -#define MDIO_DATA_READ 0x80000 - -static int mdio_read(struct net_device *dev, int phy_id, int location) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - int i; - int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; - int retval = 0; - long ioaddr = dev->base_addr; - long mdio_addr = ioaddr + CSR9; - - if (tp->chip_id == LC82C168) { - int i = 1000; - outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0); - inl(ioaddr + 0xA0); - inl(ioaddr + 0xA0); - while (--i > 0) - if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000)) - return retval & 0xffff; - return 0xffff; - } - - if (tp->chip_id == COMET) { - if (phy_id == 1) { - if (location < 7) - return inl(ioaddr + 0xB4 + (location<<2)); - else if (location == 17) - return inl(ioaddr + 0xD0); - else if (location >= 29 && location <= 31) - return inl(ioaddr + 0xD4 + ((location-29)<<2)); - } - return 0xffff; - } - - /* Establish sync by sending at least 32 logic ones. */ - for (i = 32; i >= 0; i--) { - outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Shift the read command bits out. */ - for (i = 15; i >= 0; i--) { - int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; - - outl(MDIO_ENB | dataval, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Read the two transition, 16 data, and wire-idle bits. */ - for (i = 19; i > 0; i--) { - outl(MDIO_ENB_IN, mdio_addr); - mdio_delay(); - retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - return (retval>>1) & 0xffff; -} - -static void mdio_write(struct net_device *dev, int phy_id, int location, int value) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - int i; - int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; - long ioaddr = dev->base_addr; - long mdio_addr = ioaddr + CSR9; - - if (tp->chip_id == LC82C168) { - int i = 1000; - outl(cmd, ioaddr + 0xA0); - do - if ( ! (inl(ioaddr + 0xA0) & 0x80000000)) - break; - while (--i > 0); - return; - } - - if (tp->chip_id == COMET) { - if (phy_id != 1) - return; - if (location < 7) - outl(value, ioaddr + 0xB4 + (location<<2)); - else if (location == 17) - outl(value, ioaddr + 0xD0); - else if (location >= 29 && location <= 31) - outl(value, ioaddr + 0xD4 + ((location-29)<<2)); - return; - } - - /* Establish sync by sending 32 logic ones. */ - for (i = 32; i >= 0; i--) { - outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { - int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; - outl(MDIO_ENB | dataval, mdio_addr); - mdio_delay(); - outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - /* Clear out extra bits. */ - for (i = 2; i > 0; i--) { - outl(MDIO_ENB_IN, mdio_addr); - mdio_delay(); - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - return; -} - -static void -tulip_up(struct net_device *dev) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; - int i; - - /* On some chip revs we must set the MII/SYM port before the reset!? */ - if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) - outl_CSR6(0x00040000, ioaddr, tp->chip_id); - - /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ - outl(0x00000001, ioaddr + CSR0); - - /* Deassert reset. */ - outl(tp->csr0, ioaddr + CSR0); - udelay(2); - - if (tulip_tbl[tp->chip_id].flags & HAS_ACPI) - pci_write_config_dword(tp->pdev, 0x40, 0x00000000); - - /* Clear the tx ring */ - for (i = 0; i < TX_RING_SIZE; i++) { - tp->tx_skbuff[i] = 0; - tp->tx_ring[i].status = 0x00000000; - } - - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq); - - if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) { - u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr)); - u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(dev->dev_addr+4))); - if (tp->chip_id == AX88140) { - outl(0, ioaddr + CSR13); - outl(addr_low, ioaddr + CSR14); - outl(1, ioaddr + CSR13); - outl(addr_high, ioaddr + CSR14); - } else if (tp->chip_id == COMET) { - outl(addr_low, ioaddr + 0xA4); - outl(addr_high, ioaddr + 0xA8); - outl(0, ioaddr + 0xAC); - outl(0, ioaddr + 0xB0); - } - } else if (tp->chip_id != X3201_3) { - /* This is set_rx_mode(), but without starting the transmitter. */ - u16 *eaddrs = (u16 *)dev->dev_addr; - u16 *setup_frm = &tp->setup_frame[15*6]; - - /* 21140 bug: you must add the broadcast address. */ - memset(tp->setup_frame, 0xff, 96*sizeof(u16)); - /* Fill the final entry of the table with our physical address. */ - *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; - /* Put the setup frame on the Tx list. */ - tp->tx_ring[0].length = 0x08000000 | 192; - tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame); - tp->tx_ring[0].status = DescOwned; - - tp->cur_tx++; - } else { /* X3201_3 */ - u16 *eaddrs = (u16 *)dev->dev_addr; - u16 *setup_frm = &tp->setup_frame[0*6]; - - /* fill the table with the broadcast address */ - memset(tp->setup_frame, 0xff, 96*sizeof(u16)); - /* re-fill the first 14 table entries with our address */ - for(i=0; i<14; i++) { - *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; - } - - /* Put the setup frame on the Tx list. */ - tp->tx_ring[0].length = 0x08000000 | 192; - /* Lie about the address of our setup frame to make the */ - /* chip happy */ - tp->tx_ring[0].buffer1 = (virt_to_bus(tp->setup_frame) + 4); - tp->tx_ring[0].status = DescOwned; - - tp->cur_tx++; - } - outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); - outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); - - tp->saved_if_port = dev->if_port; - if (dev->if_port == 0) - dev->if_port = tp->default_port; - if (tp->chip_id == DC21041 && dev->if_port > 4) - /* Invalid: Select initial TP, autosense, autonegotiate. */ - dev->if_port = 4; - - /* Allow selecting a default media. */ - i = 0; - if (tp->mtable == NULL) - goto media_picked; - if (dev->if_port) { - int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 : - (dev->if_port == 12 ? 0 : dev->if_port); - for (i = 0; i < tp->mtable->leafcount; i++) - if (tp->mtable->mleaf[i].media == looking_for) { - printk(KERN_INFO "%s: Using user-specified media %s.\n", - dev->name, medianame[dev->if_port]); - goto media_picked; - } - } - if ((tp->mtable->defaultmedia & 0x0800) == 0) { - int looking_for = tp->mtable->defaultmedia & 15; - for (i = 0; i < tp->mtable->leafcount; i++) - if (tp->mtable->mleaf[i].media == looking_for) { - printk(KERN_INFO "%s: Using EEPROM-set media %s.\n", - dev->name, medianame[looking_for]); - goto media_picked; - } - } - /* Start sensing first non-full-duplex media. */ - for (i = tp->mtable->leafcount - 1; - (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--) - ; -media_picked: - - tp->csr6 = 0; - tp->cur_index = i; - if (dev->if_port == 0 && tp->chip_id == DC21142) { - if (tp->mii_cnt) { - select_media(dev, 1); - if (tulip_debug > 1) - printk(KERN_INFO "%s: Using MII transceiver %d, status " - "%4.4x.\n", - dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1)); - outl_CSR6(0x82020000, ioaddr, tp->chip_id); - tp->csr6 = 0x820E0000; - dev->if_port = 11; - outl(0x0000, ioaddr + CSR13); - outl(0x0000, ioaddr + CSR14); - } else - t21142_start_nway(dev); - } else if ((tp->chip_id == LC82C168 || tp->chip_id == PNIC2) - && tp->mii_cnt && ! tp->medialock) { - dev->if_port = 11; - tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0); - outl(0x0001, ioaddr + CSR15); - } else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) - && ! tp->medialock) { - dev->if_port = 0; - tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0); - outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); - } else if (tp->chip_id == MX98715 || tp->chip_id == MX98725) { - /* Provided by BOLO, Macronix - 12/10/1998. */ - dev->if_port = 0; - tp->csr6 = 0x01880200; - outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); - outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0); - } else if (tp->chip_id == DC21143 && - media_cap[dev->if_port] & MediaIsMII) { - /* We must reset the media CSRs when we force-select MII mode. */ - outl(0x0000, ioaddr + CSR13); - outl(0x0000, ioaddr + CSR14); - outl(0x0008, ioaddr + CSR15); - } else if (tp->chip_id == X3201_3) { - outl(0x0008, ioaddr + CSR15); - udelay(5); - outl(0xa8050000, ioaddr + CSR15); - udelay(5); - outl(0xa00f0000, ioaddr + CSR15); - udelay(5); - tp->csr6 = 0x32400000; - } else if (tp->chip_id == COMET) { - dev->if_port = 0; - tp->csr6 = 0x00040000; - } else - select_media(dev, 1); - - /* Start the chip's Tx to process setup frame. */ - outl_CSR6(tp->csr6, ioaddr, tp->chip_id); - outl_CSR6(tp->csr6 | 0x2000, ioaddr, tp->chip_id); - - /* Enable interrupts by setting the interrupt mask. */ - outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5); - outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); - outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); - outl(0, ioaddr + CSR2); /* Rx poll demand */ - - netif_start_queue (dev); - - if (tulip_debug > 2) { - printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n", - dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5), - inl(ioaddr + CSR6)); - } - /* Set the timer to switch to check for link beat and perhaps switch - to an alternate media type. */ - init_timer(&tp->timer); - tp->timer.expires = RUN_AT(5*HZ); - tp->timer.data = (unsigned long)dev; - tp->timer.function = tulip_tbl[tp->chip_id].media_timer; - add_timer(&tp->timer); -} - -static int -tulip_open(struct net_device *dev) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - - if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) - return -EAGAIN; - - tulip_init_ring(dev); - - tulip_up(dev); - tp->open = 1; - MOD_INC_USE_COUNT; - - return 0; -} - -/* Set up the transceiver control registers for the selected media type. */ -static void select_media(struct net_device *dev, int startup) -{ - long ioaddr = dev->base_addr; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - struct mediatable *mtable = tp->mtable; - u32 new_csr6; - int i; - - if (mtable) { - struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index]; - unsigned char *p = mleaf->leafdata; - switch (mleaf->type) { - case 0: /* 21140 non-MII xcvr. */ - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver" - " with control setting %2.2x.\n", - dev->name, p[1]); - dev->if_port = p[0]; - if (startup) - outl(mtable->csr12dir | 0x100, ioaddr + CSR12); - outl(p[1], ioaddr + CSR12); - new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18); - break; - case 2: case 4: { - u16 setup[5]; - u32 csr13val, csr14val, csr15dir, csr15val; - for (i = 0; i < 5; i++) - setup[i] = get_u16(&p[i*2 + 1]); - - dev->if_port = p[0] & 15; - if (media_cap[dev->if_port] & MediaAlwaysFD) - tp->full_duplex = 1; - - if (startup && mtable->has_reset) { - struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset]; - unsigned char *rst = rleaf->leafdata; - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Resetting the transceiver.\n", - dev->name); - for (i = 0; i < rst[0]; i++) - outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15); - } - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control " - "%4.4x/%4.4x.\n", - dev->name, medianame[dev->if_port], setup[0], setup[1]); - if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */ - csr13val = setup[0]; - csr14val = setup[1]; - csr15dir = (setup[3]<<16) | setup[2]; - csr15val = (setup[4]<<16) | setup[2]; - outl(0, ioaddr + CSR13); - outl(csr14val, ioaddr + CSR14); - outl(csr15dir, ioaddr + CSR15); /* Direction */ - outl(csr15val, ioaddr + CSR15); /* Data */ - outl(csr13val, ioaddr + CSR13); - } else { - csr13val = 1; - csr14val = 0x0003FF7F; - csr15dir = (setup[0]<<16) | 0x0008; - csr15val = (setup[1]<<16) | 0x0008; - if (dev->if_port <= 4) - csr14val = t21142_csr14[dev->if_port]; - if (startup) { - outl(0, ioaddr + CSR13); - outl(csr14val, ioaddr + CSR14); - } - outl(csr15dir, ioaddr + CSR15); /* Direction */ - outl(csr15val, ioaddr + CSR15); /* Data */ - if (startup) outl(csr13val, ioaddr + CSR13); - } - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Setting CSR15 to %8.8x/%8.8x.\n", - dev->name, csr15dir, csr15val); - if (mleaf->type == 4) - new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18); - else - new_csr6 = 0x82420000; - break; - } - case 1: case 3: { - int phy_num = p[0]; - int init_length = p[1]; - u16 *misc_info; - u16 to_advertise; - - dev->if_port = 11; - new_csr6 = 0x020E0000; - if (mleaf->type == 3) { /* 21142 */ - u16 *init_sequence = (u16*)(p+2); - u16 *reset_sequence = &((u16*)(p+3))[init_length]; - int reset_length = p[2 + init_length*2]; - misc_info = reset_sequence + reset_length; - if (startup) - for (i = 0; i < reset_length; i++) - outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15); - for (i = 0; i < init_length; i++) - outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15); - } else { - u8 *init_sequence = p + 2; - u8 *reset_sequence = p + 3 + init_length; - int reset_length = p[2 + init_length]; - misc_info = (u16*)(reset_sequence + reset_length); - if (startup) { - outl(mtable->csr12dir | 0x100, ioaddr + CSR12); - for (i = 0; i < reset_length; i++) - outl(reset_sequence[i], ioaddr + CSR12); - } - for (i = 0; i < init_length; i++) - outl(init_sequence[i], ioaddr + CSR12); - } - to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1; - tp->advertising[phy_num] = to_advertise; - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d (%d).\n", - dev->name, to_advertise, phy_num, tp->phys[phy_num]); - /* Bogus: put in by a committee? */ - mdio_write(dev, tp->phys[phy_num], 4, to_advertise); - break; - } - default: - printk(KERN_DEBUG "%s: Invalid media table selection %d.\n", - dev->name, mleaf->type); - new_csr6 = 0x020E0000; - } - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n", - dev->name, medianame[dev->if_port], - inl(ioaddr + CSR12) & 0xff); - } else if (tp->chip_id == DC21041) { - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n", - dev->name, medianame[dev->if_port & 15], - inl(ioaddr + CSR12) & 0xffff); - outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ - outl(t21041_csr14[dev->if_port], ioaddr + CSR14); - outl(t21041_csr15[dev->if_port], ioaddr + CSR15); - outl(t21041_csr13[dev->if_port], ioaddr + CSR13); - new_csr6 = 0x80020000; - } else if (tp->chip_id == LC82C168 || tp->chip_id == PNIC2) { - if (startup && ! tp->medialock) - dev->if_port = tp->mii_cnt ? 11 : 0; - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x," - " media %s.\n", - dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12), - medianame[dev->if_port]); - if (tp->mii_cnt) { - new_csr6 = 0x810C0000; - outl(0x0001, ioaddr + CSR15); - outl(0x0201B07A, ioaddr + 0xB8); - } else if (startup) { - /* Start with 10mbps to do autonegotiation. */ - outl(0x32, ioaddr + CSR12); - new_csr6 = 0x00420000; - outl(0x0001B078, ioaddr + 0xB8); - outl(0x0201B078, ioaddr + 0xB8); - } else if (dev->if_port == 3 || dev->if_port == 5) { - outl(0x33, ioaddr + CSR12); - new_csr6 = 0x01860000; - if (startup) - outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */ - else - outl(0x1F868, ioaddr + 0xB8); - } else { - outl(0x32, ioaddr + CSR12); - new_csr6 = 0x00420000; - outl(0x1F078, ioaddr + 0xB8); - } - } else if (tp->chip_id == DC21040) { /* 21040 */ - /* Turn on the xcvr interface. */ - int csr12 = inl(ioaddr + CSR12); - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n", - dev->name, medianame[dev->if_port], csr12); - if (media_cap[dev->if_port] & MediaAlwaysFD) - tp->full_duplex = 1; - new_csr6 = 0x20000; - /* Set the full duplux match frame. */ - outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11); - outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ - if (t21040_csr13[dev->if_port] & 8) { - outl(0x0705, ioaddr + CSR14); - outl(0x0006, ioaddr + CSR15); - } else { - outl(0xffff, ioaddr + CSR14); - outl(0x0000, ioaddr + CSR15); - } - outl(0x8f01 | t21040_csr13[dev->if_port], ioaddr + CSR13); - } else if (tp->chip_id == X3201_3) { /* Xircom */ - if (tp->default_port == 0) - dev->if_port = tp->mii_cnt ? 11 : 3; -/* Someone is on crack, the Xircom only does MII, no Fx */ -/* if (media_cap[dev->if_port] & MediaIsMII) { - new_csr6 = 0x020E0000; - } else if (media_cap[dev->if_port] & MediaIsFx) { - new_csr6 = 0x028600000; - } else - new_csr6 = 0x038600000;*/ - new_csr6 = 0x324c0000; - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Xircom CardBus Adapter: " - "%s transceiver, CSR12 %2.2x.\n", - dev->name, medianame[dev->if_port], - inl(ioaddr + CSR12)); - } else { /* Unknown chip type with no media table. */ - if (tp->default_port == 0) - dev->if_port = tp->mii_cnt ? 11 : 3; - if (media_cap[dev->if_port] & MediaIsMII) { - new_csr6 = 0x020E0000; - } else if (media_cap[dev->if_port] & MediaIsFx) { - new_csr6 = 0x028600000; - } else - new_csr6 = 0x038600000; - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: No media description table, assuming " - "%s transceiver, CSR12 %2.2x.\n", - dev->name, medianame[dev->if_port], - inl(ioaddr + CSR12)); - } - - tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0); - return; -} - -/* - Check the MII negotiated duplex, and change the CSR6 setting if - required. - Return 0 if everything is OK. - Return < 0 if the transceiver is missing or has no link beat. - */ -static int check_duplex(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - int mii_reg1, mii_reg5, negotiated, duplex; - - if (tp->full_duplex_lock) - return 0; - mii_reg1 = mdio_read(dev, tp->phys[0], 1); - mii_reg5 = mdio_read(dev, tp->phys[0], 5); - if (tulip_debug > 1) - printk(KERN_INFO "%s: MII status %4.4x, Link partner report " - "%4.4x.\n", dev->name, mii_reg1, mii_reg5); - if (mii_reg1 == 0xffff) - return -2; - if ((mii_reg1 & 0x0004) == 0) { - int new_reg1 = mdio_read(dev, tp->phys[0], 1); - if ((new_reg1 & 0x0004) == 0) { - if (tulip_debug > 1) - printk(KERN_INFO "%s: No link beat on the MII interface," - " status %4.4x.\n", dev->name, new_reg1); - return -1; - } - } - negotiated = mii_reg5 & tp->advertising[0]; - duplex = ((negotiated & 0x0300) == 0x0100 - || (negotiated & 0x00C0) == 0x0040); - /* 100baseTx-FD or 10T-FD, but not 100-HD */ - if (tp->full_duplex != duplex) { - tp->full_duplex = duplex; - if (tp->full_duplex) tp->csr6 |= 0x0200; - else tp->csr6 &= ~0x0200; - outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); - outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); - if (tulip_debug > 0) - printk(KERN_INFO "%s: Setting %s-duplex based on MII" - "#%d link partner capability of %4.4x.\n", - dev->name, tp->full_duplex ? "full" : "half", - tp->phys[0], mii_reg5); - } - return 0; -} - -static void tulip_timer(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; - u32 csr12 = inl(ioaddr + CSR12); - int next_tick = 2*HZ; - - if (tulip_debug > 2) { - printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x " - "SIA %8.8x %8.8x %8.8x %8.8x.\n", - dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6), - csr12, inl(ioaddr + CSR13), - inl(ioaddr + CSR14), inl(ioaddr + CSR15)); - } - switch (tp->chip_id) { - case DC21040: - if (!tp->medialock && csr12 & 0x0002) { /* Network error */ - printk(KERN_INFO "%s: No link beat found.\n", - dev->name); - dev->if_port = (dev->if_port == 2 ? 0 : 2); - select_media(dev, 0); - dev->trans_start = jiffies; - } - break; - case DC21041: - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: 21041 media tick CSR12 %8.8x.\n", - dev->name, csr12); - switch (dev->if_port) { - case 0: case 3: case 4: - if (csr12 & 0x0004) { /*LnkFail */ - /* 10baseT is dead. Check for activity on alternate port. */ - tp->mediasense = 1; - if (csr12 & 0x0200) - dev->if_port = 2; - else - dev->if_port = 1; - printk(KERN_INFO "%s: No 21041 10baseT link beat, Media switched to %s.\n", - dev->name, medianame[dev->if_port]); - outl(0, ioaddr + CSR13); /* Reset */ - outl(t21041_csr14[dev->if_port], ioaddr + CSR14); - outl(t21041_csr15[dev->if_port], ioaddr + CSR15); - outl(t21041_csr13[dev->if_port], ioaddr + CSR13); - next_tick = 10*HZ; /* 2.4 sec. */ - } else - next_tick = 30*HZ; - break; - case 1: /* 10base2 */ - case 2: /* AUI */ - if (csr12 & 0x0100) { - next_tick = (30*HZ); /* 30 sec. */ - tp->mediasense = 0; - } else if ((csr12 & 0x0004) == 0) { - printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n", - dev->name); - dev->if_port = 0; - select_media(dev, 0); - next_tick = (24*HZ)/10; /* 2.4 sec. */ - } else if (tp->mediasense || (csr12 & 0x0002)) { - dev->if_port = 3 - dev->if_port; /* Swap ports. */ - select_media(dev, 0); - next_tick = 20*HZ; - } else { - next_tick = 20*HZ; - } - break; - } - break; - case DC21140: case DC21142: case MX98713: case COMPEX9881: default: { - struct medialeaf *mleaf; - unsigned char *p; - if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */ - /* Not much that can be done. - Assume this a generic MII or SYM transceiver. */ - next_tick = 60*HZ; - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x " - "CSR12 0x%2.2x.\n", - dev->name, inl(ioaddr + CSR6), csr12 & 0xff); - break; - } - mleaf = &tp->mtable->mleaf[tp->cur_index]; - p = mleaf->leafdata; - switch (mleaf->type) { - case 0: case 4: { - /* Type 0 serial or 4 SYM transceiver. Check the link beat bit. */ - int offset = mleaf->type == 4 ? 5 : 2; - s8 bitnum = p[offset]; - if (p[offset+1] & 0x80) { - if (tulip_debug > 1) - printk(KERN_DEBUG"%s: Transceiver monitor tick " - "CSR12=%#2.2x, no media sense.\n", - dev->name, csr12); - if (mleaf->type == 4) { - if (mleaf->media == 3 && (csr12 & 0x02)) - goto select_next_media; - } - break; - } - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x" - " bit %d is %d, expecting %d.\n", - dev->name, csr12, (bitnum >> 1) & 7, - (csr12 & (1 << ((bitnum >> 1) & 7))) != 0, - (bitnum >= 0)); - /* Check that the specified bit has the proper value. */ - if ((bitnum < 0) != - ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) { - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name, - medianame[mleaf->media]); - if ((p[2] & 0x61) == 0x01) /* Bogus Znyx board. */ - goto actually_mii; - break; - } - if (tp->medialock) - break; - select_next_media: - if (--tp->cur_index < 0) { - /* We start again, but should instead look for default. */ - tp->cur_index = tp->mtable->leafcount - 1; - } - dev->if_port = tp->mtable->mleaf[tp->cur_index].media; - if (media_cap[dev->if_port] & MediaIsFD) - goto select_next_media; /* Skip FD entries. */ - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: No link beat on media %s," - " trying transceiver type %s.\n", - dev->name, medianame[mleaf->media & 15], - medianame[tp->mtable->mleaf[tp->cur_index].media]); - select_media(dev, 0); - /* Restart the transmit process. */ - outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); - outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); - next_tick = (24*HZ)/10; - break; - } - case 1: case 3: /* 21140, 21142 MII */ - actually_mii: - check_duplex(dev); - next_tick = 60*HZ; - break; - case 2: /* 21142 serial block has no link beat. */ - default: - break; - } - } - break; - } - tp->timer.expires = RUN_AT(next_tick); - add_timer(&tp->timer); -} - -/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list - of available transceivers. */ -static void t21142_timer(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; - int csr12 = inl(ioaddr + CSR12); - int next_tick = 60*HZ; - int new_csr6 = 0; - - if ((tulip_debug > 2) && !(media_cap[dev->if_port] & MediaIsMII)) - printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n", - dev->name, csr12, medianame[dev->if_port]); - if (media_cap[dev->if_port] & MediaIsMII) { - check_duplex(dev); - next_tick = 60*HZ; - } else if (tp->nwayset) { - /* Don't screw up a negotiated session! */ - if (tulip_debug > 1) - printk(KERN_INFO"%s: Using NWay-set %s media, csr12 %8.8x.\n", - dev->name, medianame[dev->if_port], csr12); - } else if (tp->medialock) { - ; - } else if (dev->if_port == 3) { - if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */ - if (tulip_debug > 1) - printk(KERN_INFO"%s: No 21143 100baseTx link beat, %8.8x, " - "trying NWay.\n", dev->name, csr12); - t21142_start_nway(dev); - next_tick = 3*HZ; - } - } else if (((csr12 & 0x7000) != 0x5000) - && tp->chip_id != X3201_3) { - /* Negotiation failed. Search media types. */ - if (tulip_debug > 1) - printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n", - dev->name, csr12); - if (!(csr12 & 4)) { /* 10mbps link beat good. */ - new_csr6 = 0x82420000; - dev->if_port = 0; - outl(0, ioaddr + CSR13); - outl(0x0003FFFF, ioaddr + CSR14); - outw(t21142_csr15[dev->if_port], ioaddr + CSR15); - outl(t21142_csr13[dev->if_port], ioaddr + CSR13); - } else { - /* Select 100mbps port to check for link beat. */ - new_csr6 = 0x83860000; - dev->if_port = 3; - outl(0, ioaddr + CSR13); - outl(0x0003FF7F, ioaddr + CSR14); - outw(8, ioaddr + CSR15); - outl(1, ioaddr + CSR13); - } - if (tulip_debug > 1) - printk(KERN_INFO"%s: Testing new 21143 media %s.\n", - dev->name, medianame[dev->if_port]); - if (new_csr6 != (tp->csr6 & ~0x00D5)) { - tp->csr6 &= 0x00D5; - tp->csr6 |= new_csr6; - outl(0x0301, ioaddr + CSR12); - outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); - outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); - } - next_tick = 3*HZ; - } - if (tp->cur_tx - tp->dirty_tx > 0 && - jiffies - dev->trans_start > TX_TIMEOUT) { - printk(KERN_WARNING "%s: Tx hung, %d vs. %d.\n", - dev->name, tp->cur_tx, tp->dirty_tx); - tulip_tx_timeout(dev); - } - - tp->timer.expires = RUN_AT(next_tick); - add_timer(&tp->timer); -} - -static void t21142_start_nway(struct net_device *dev) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; - int csr14 = ((tp->to_advertise & 0x0180) << 9) | - ((tp->to_advertise&0x0020)<<1) | 0xffbf; - - dev->if_port = 0; - tp->nway = tp->mediasense = 1; - tp->nwayset = tp->lpar = 0; - if (debug > 1) - printk(KERN_DEBUG "%s: Restarting 21143 autonegotiation, %8.8x.\n", - dev->name, csr14); - outl(0x0001, ioaddr + CSR13); - outl(csr14, ioaddr + CSR14); - tp->csr6 = 0x82420000 | (tp->to_advertise & 0x0040 ? 0x0200 : 0); - outl_CSR6(tp->csr6, ioaddr, tp->chip_id); - if (tp->mtable && tp->mtable->csr15dir) { - outl(tp->mtable->csr15dir, ioaddr + CSR15); - outl(tp->mtable->csr15val, ioaddr + CSR15); - } else - outw(0x0008, ioaddr + CSR15); - outl(0x1301, ioaddr + CSR12); /* Trigger NWAY. */ -} - -static void t21142_lnk_change(struct net_device *dev, int csr5) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; - int csr12 = inl(ioaddr + CSR12); - - if (tulip_debug > 1) - printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, " - "%8.8x.\n", dev->name, csr12, csr5, inl(ioaddr + CSR14)); - - /* If NWay finished and we have a negotiated partner capability. */ - if (tp->nway && !tp->nwayset && (csr12 & 0x7000) == 0x5000) { - int setup_done = 0; - tp->lpar = csr12 >> 16; - tp->nwayset = 1; - if (csr12 & 0x01000000) dev->if_port = 5; - else if (csr12 & 0x00800000) dev->if_port = 3; - else if (csr12 & 0x00400000) dev->if_port = 4; - else if (csr12 & 0x00200000) dev->if_port = 0; - else { - tp->nwayset = 0; - if ( ! (csr12 & 2)) dev->if_port = 3; - else if ( ! (csr12 & 4)) dev->if_port = 0; - } - tp->full_duplex = (media_cap[tp->default_port] & MediaAlwaysFD) ? 1:0; - - if (tulip_debug > 1) { - if (tp->nwayset) - printk(KERN_INFO "%s: Switching to %s based on link partner " - "advertisement %4.4x.\n", - dev->name, medianame[dev->if_port], tp->lpar); - else - printk(KERN_INFO "%s: Switching to %s based on link beat " - "status of %4.4x.\n", - dev->name, medianame[dev->if_port], csr12); - } - - if (tp->mtable) { - int i; - for (i = 0; i < tp->mtable->leafcount; i++) - if (tp->mtable->mleaf[i].media == dev->if_port) { - tp->cur_index = i; - select_media(dev, 0); - setup_done = 1; - break; - } - } - if ( ! setup_done) { - tp->csr6 = dev->if_port & 1 ? 0x83860000 : 0x82420000; - if (tp->full_duplex) - tp->csr6 |= 0x0200; - outw(0x0000, ioaddr + CSR13); - outw(0x0000, ioaddr + CSR14); - } - outl_CSR6(tp->csr6 | 0x0000, ioaddr, tp->chip_id); - if (debug > 2) - printk(KERN_DEBUG "%s: Restarting Tx and Rx, CSR5 is %8.8x.\n", - dev->name, inl(ioaddr + CSR5)); - outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); - } else if ((tp->nwayset && (csr5 & 0x08000000) - && (dev->if_port == 3 || dev->if_port == 5) - && (csr12 & 2) == 2) || - (tp->nway && (csr5 & (TPLnkFail)))) { - /* Link blew? Maybe restart NWay. */ - del_timer(&tp->timer); - t21142_start_nway(dev); - tp->timer.expires = RUN_AT(3*HZ); - add_timer(&tp->timer); - } else if (dev->if_port == 3 || dev->if_port == 5) { - if (tulip_debug > 1) - printk(KERN_INFO"%s: 21143 %s link beat %s.\n", - dev->name, medianame[dev->if_port], - (csr12 & 2) ? "failed" : "good"); - if ((csr12 & 2) && ! tp->medialock) { - del_timer(&tp->timer); - t21142_start_nway(dev); - tp->timer.expires = RUN_AT(3*HZ); - add_timer(&tp->timer); - } - } else if (dev->if_port == 0 || dev->if_port == 4) { - if ((csr12 & 4) == 0) - printk(KERN_INFO"%s: 21143 10baseT link beat good.\n", - dev->name); - } else if (!(csr12 & 4)) { /* 10mbps link beat good. */ - if (tulip_debug) - printk(KERN_INFO"%s: 21143 10mbps sensed media.\n", - dev->name); - dev->if_port = 0; - } else if (tp->nwayset) { - if (tulip_debug) - printk(KERN_INFO"%s: 21143 using NWay-set %s, csr6 %8.8x.\n", - dev->name, medianame[dev->if_port], tp->csr6); - } else { /* 100mbps link beat good. */ - if (tulip_debug) - printk(KERN_INFO"%s: 21143 100baseTx sensed media.\n", - dev->name); - dev->if_port = 3; - tp->csr6 = 0x83860000; - outl(0x0003FF7F, ioaddr + CSR14); - outl(0x0301, ioaddr + CSR12); - outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); - outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); - } -} - -static void mxic_timer(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; - int next_tick = 60*HZ; - - if (tulip_debug > 3) { - printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name, - inl(ioaddr + CSR12)); - } - if (next_tick) { - tp->timer.expires = RUN_AT(next_tick); - add_timer(&tp->timer); - } -} - -static void pnic_timer(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; - int csr12 = inl(ioaddr + CSR12); - int next_tick = 60*HZ; - int new_csr6 = tp->csr6 & ~0x40C40200; - - if (media_cap[dev->if_port] & MediaIsMII) { - int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0]; - - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: PNIC negotiated capability %8.8x, " - "CSR5 %8.8x.\n", - dev->name, negotiated, inl(ioaddr + CSR5)); - - if (negotiated & 0x0380) /* 10 vs 100mbps */ - new_csr6 |= 0x810E0000; - else - new_csr6 |= 0x814E0000; - if (((negotiated & 0x0300) == 0x0100) /* Duplex */ - || (negotiated & 0x00C0) == 0x0040 - || tp->full_duplex_lock) { - tp->full_duplex = 1; - new_csr6 |= 0x0200; - } - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: PNIC MII PHY status %4.4x, Link " - "partner report %4.4x, csr6 %8.8x/%8.8x.\n", - dev->name, mdio_read(dev, tp->phys[0], 1), negotiated, - tp->csr6, inl(ioaddr + CSR6)); - } else { - int phy_reg = inl(ioaddr + 0xB8); - int csr5 = inl(ioaddr + CSR5); - - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: PNIC PHY status %8.8x, CSR5 %8.8x.\n", - dev->name, phy_reg, csr5); - - if (phy_reg & 0x04000000) { /* Remote link fault */ - /*outl(0x0201F078, ioaddr + 0xB8);*/ - next_tick = 3*HZ; - } - if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */ - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, " - "CSR5 %8.8x, PHY %3.3x.\n", - dev->name, medianame[dev->if_port], csr12, - inl(ioaddr + CSR5), inl(ioaddr + 0xB8)); - if (tp->medialock) { - } else if (dev->if_port == 0) { - dev->if_port = 3; - outl(0x33, ioaddr + CSR12); - new_csr6 = 0x01860000; - outl(0x1F868, ioaddr + 0xB8); - } else { - dev->if_port = 0; - outl(0x32, ioaddr + CSR12); - new_csr6 = 0x00420000; - outl(0x1F078, ioaddr + 0xB8); - } - new_csr6 |= (tp->csr6 & 0xfdff); - next_tick = 3*HZ; - } else - new_csr6 = tp->csr6; - if (tp->full_duplex_lock || (phy_reg & 0x30000000) != 0) { - tp->full_duplex = 1; - new_csr6 |= 0x00000200; - } - } - if (tp->csr6 != new_csr6) { - tp->csr6 = new_csr6; - outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); /* Restart Tx */ - outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); - dev->trans_start = jiffies; - if (tulip_debug > 1) - printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, " - "CSR6 %8.8x.\n", - dev->name, tp->full_duplex ? "full" : "half", new_csr6); - } - tp->timer.expires = RUN_AT(next_tick); - add_timer(&tp->timer); -} - -static void comet_timer(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; - int next_tick = 60*HZ; - - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Comet link status %4.4x partner capability " - "%4.4x.\n", - dev->name, inl(ioaddr + 0xB8), inl(ioaddr + 0xC8)); - tp->timer.expires = RUN_AT(next_tick); - add_timer(&tp->timer); -} - -static void tulip_tx_timeout(struct net_device *dev) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; - - if (media_cap[dev->if_port] & MediaIsMII) { - /* Do nothing -- the media monitor should handle this. */ - if (tulip_debug > 1) - printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", - dev->name); - } else if (tp->chip_id == DC21040) { - if ( !tp->medialock && inl(ioaddr + CSR12) & 0x0002) { - dev->if_port = (dev->if_port == 2 ? 0 : 2); - printk(KERN_INFO "%s: transmit timed out, switching to " - "%s.\n", - dev->name, medianame[dev->if_port]); - select_media(dev, 0); - } - dev->trans_start = jiffies; - return; - } else if (tp->chip_id == DC21041) { - int csr12 = inl(ioaddr + CSR12); - - printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, " - "CSR12 %8.8x, CSR13 %8.8x, CSR14 %8.8x, resetting...\n", - dev->name, inl(ioaddr + CSR5), csr12, - inl(ioaddr + CSR13), inl(ioaddr + CSR14)); - tp->mediasense = 1; - if ( ! tp->medialock) { - if (dev->if_port == 1 || dev->if_port == 2) - if (csr12 & 0x0004) { - dev->if_port = 2 - dev->if_port; - } else - dev->if_port = 0; - else - dev->if_port = 1; - select_media(dev, 0); - } - } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 - || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) { - printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " - "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", - dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), - inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); - if ( ! tp->medialock && tp->mtable) { - do - --tp->cur_index; - while (tp->cur_index >= 0 - && (media_cap[tp->mtable->mleaf[tp->cur_index].media] - & MediaIsFD)); - if (--tp->cur_index < 0) { - /* We start again, but should instead look for default. */ - tp->cur_index = tp->mtable->leafcount - 1; - } - select_media(dev, 0); - printk(KERN_WARNING "%s: transmit timed out, switching to %s " - "media.\n", dev->name, medianame[dev->if_port]); - } - } else { - printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, CSR12 " - "%8.8x, resetting...\n", - dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12)); - dev->if_port = 0; - } - -#if defined(way_too_many_messages) - if (tulip_debug > 3) { - int i; - for (i = 0; i < RX_RING_SIZE; i++) { - u8 *buf = (u8 *)(tp->rx_ring[i].buffer1); - int j; - printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x " - "%2.2x %2.2x %2.2x.\n", - i, (unsigned int)tp->rx_ring[i].status, - (unsigned int)tp->rx_ring[i].length, - (unsigned int)tp->rx_ring[i].buffer1, - (unsigned int)tp->rx_ring[i].buffer2, - buf[0], buf[1], buf[2]); - for (j = 0; buf[j] != 0xee && j < 1600; j++) - if (j < 100) printk(" %2.2x", buf[j]); - printk(" j=%d.\n", j); - } - printk(KERN_DEBUG " Rx ring %8.8x: ", (int)tp->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); - printk("\n" KERN_DEBUG " Tx ring %8.8x: ", (int)tp->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); - printk("\n"); - } -#endif - - /* Stop and restart the chip's Tx processes . */ - outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); - outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); - /* Trigger an immediate transmit demand. */ - outl(0, ioaddr + CSR1); - - dev->trans_start = jiffies; - netif_wake_queue (dev); - tp->stats.tx_errors++; -} - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void tulip_init_ring(struct net_device *dev) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - int i; - - tp->tx_full = 0; - tp->cur_rx = tp->cur_tx = 0; - tp->dirty_rx = tp->dirty_tx = 0; - - for (i = 0; i < RX_RING_SIZE; i++) { - tp->rx_ring[i].status = 0x00000000; - tp->rx_ring[i].length = PKT_BUF_SZ; - tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]); - tp->rx_skbuff[i] = NULL; - } - /* Mark the last entry as wrapping the ring. */ - tp->rx_ring[i-1].length = PKT_BUF_SZ | DESC_RING_WRAP; - tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]); - - for (i = 0; i < RX_RING_SIZE; i++) { - /* Note the receive buffer must be longword aligned. - dev_alloc_skb() provides 16 byte alignment. But do *not* - use skb_reserve() to align the IP header! */ - struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ); - tp->rx_skbuff[i] = skb; - if (skb == NULL) - break; - skb->dev = dev; /* Mark as being used by this device. */ - tp->rx_ring[i].status = DescOwned; /* Owned by Tulip chip */ - tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail); - } - tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); - - /* The Tx buffer descriptor is filled in as needed, but we - do need to clear the ownership bit. */ - for (i = 0; i < TX_RING_SIZE; i++) { - tp->tx_skbuff[i] = 0; - tp->tx_ring[i].status = 0x00000000; - tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]); -#ifdef CARDBUS - if (tp->chip_id == X3201_3) - tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ); -#endif CARDBUS - } - tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]); -} - -static int -tulip_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - int entry; - u32 flag; - - /* Caution: the write order is important here, set the base address - with the "ownership" bits last. */ - - /* Calculate the next Tx descriptor entry. */ - entry = tp->cur_tx % TX_RING_SIZE; - - tp->tx_skbuff[entry] = skb; -#ifdef CARDBUS - if (tp->chip_id == X3201_3) { - memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len); - tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data); - } else -#endif - tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); - - if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ - flag = 0x60000000; /* No interrupt */ - } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { - flag = 0xe0000000; /* Tx-done intr. */ - } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { - flag = 0x60000000; /* No Tx-done intr. */ - } else { - /* Leave room for set_rx_mode() to fill entries. */ - flag = 0xe0000000; /* Tx-done intr. */ - tp->tx_full = 1; - } - if (entry == TX_RING_SIZE-1) - flag |= 0xe0000000 | DESC_RING_WRAP; - - tp->tx_ring[entry].length = skb->len | flag; - tp->tx_ring[entry].status = DescOwned; /* Pass ownership to the chip. */ - tp->cur_tx++; - if (tp->tx_full) - netif_stop_queue (dev); - else - netif_wake_queue (dev); - - /* Trigger an immediate transmit demand. */ - outl(0, dev->base_addr + CSR1); - - dev->trans_start = jiffies; - - return 0; -} - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ -static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_instance; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; - int csr5, work_budget = max_interrupt_work; - - spin_lock (&tp->lock); - - do { - csr5 = inl(ioaddr + CSR5); - /* Acknowledge all of the current interrupt sources ASAP. */ - outl(csr5 & 0x0001ffff, ioaddr + CSR5); - - if (tulip_debug > 4) - printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", - dev->name, csr5, inl(dev->base_addr + CSR5)); - - if (csr5 == 0xffffffff) - break; /* all bits set, assume PCMCIA card removed */ - - if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) - break; - - if (csr5 & (RxIntr | RxNoBuf)) - work_budget -= tulip_rx(dev); - - if (csr5 & (TxNoBuf | TxDied | TxIntr)) { - unsigned int dirty_tx; - - for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; - dirty_tx++) { - int entry = dirty_tx % TX_RING_SIZE; - int status = tp->tx_ring[entry].status; - - if (status < 0) - break; /* It still hasn't been Txed */ - /* Check for Rx filter setup frames. */ - if (tp->tx_skbuff[entry] == NULL) - continue; - - if (status & 0x8000) { - /* There was an major error, log it. */ -#ifndef final_version - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", - dev->name, status); -#endif - tp->stats.tx_errors++; - if (status & 0x4104) tp->stats.tx_aborted_errors++; - if (status & 0x0C00) tp->stats.tx_carrier_errors++; - if (status & 0x0200) tp->stats.tx_window_errors++; - if (status & 0x0002) tp->stats.tx_fifo_errors++; - if ((status & 0x0080) && tp->full_duplex == 0) - tp->stats.tx_heartbeat_errors++; -#ifdef ETHER_STATS - if (status & 0x0100) tp->stats.collisions16++; -#endif - } else { -#ifdef ETHER_STATS - if (status & 0x0001) tp->stats.tx_deferred++; -#endif - tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff; - tp->stats.collisions += (status >> 3) & 15; - tp->stats.tx_packets++; - } - - /* Free the original skb. */ - dev_kfree_skb_irq(tp->tx_skbuff[entry]); - tp->tx_skbuff[entry] = 0; - } - -#ifndef final_version - if (tp->cur_tx - dirty_tx > TX_RING_SIZE) { - printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dev->name, dirty_tx, tp->cur_tx, tp->tx_full); - dirty_tx += TX_RING_SIZE; - } -#endif - - if (tp->tx_full && - tp->cur_tx - dirty_tx < TX_RING_SIZE - 2) - /* The ring is no longer full */ - tp->tx_full = 0; - - if (tp->tx_full) - netif_stop_queue (dev); - else - netif_wake_queue (dev); - - tp->dirty_tx = dirty_tx; - if (csr5 & TxDied) { - if (tulip_debug > 2) - printk(KERN_WARNING "%s: The transmitter stopped." - " CSR5 is %x, CSR6 %x, new CSR6 %x.\n", - dev->name, csr5, inl(ioaddr + CSR6), tp->csr6); - outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); - outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); - } - } - - /* Log errors. */ - if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ - if (csr5 == 0xffffffff) - break; - if (csr5 & TxJabber) tp->stats.tx_errors++; - if (csr5 & TxFIFOUnderflow) { - if ((tp->csr6 & 0xC000) != 0xC000) - tp->csr6 += 0x4000; /* Bump up the Tx threshold */ - else - tp->csr6 |= 0x00200000; /* Store-n-forward. */ - /* Restart the transmit process. */ - outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); - outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); - } - if (csr5 & RxDied) { /* Missed a Rx frame. */ - tp->stats.rx_errors++; - tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; - outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); - } - if (csr5 & TimerInt) { - if (tulip_debug > 2) - printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n", - dev->name, csr5); - outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); - } - if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) { - if ( tp->chip_id == DC21142) - t21142_lnk_change(dev, csr5); - } - /* Clear all error sources, included undocumented ones! */ - outl(0x0800f7ba, ioaddr + CSR5); - } - if (--work_budget < 0) { - if (tulip_debug > 1) - printk(KERN_WARNING "%s: Too much work during an interrupt, " - "csr5=0x%8.8x.\n", dev->name, csr5); - /* Acknowledge all interrupt sources. */ - outl(0x8001ffff, ioaddr + CSR5); -#ifdef notdef - /* Clear all but standard interrupt sources. */ - outl((~csr5) & 0x0001ebef, ioaddr + CSR7); -#endif - break; - } - } while (1); - - if (tulip_debug > 3) - printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", - dev->name, inl(ioaddr + CSR5)); - - spin_unlock (&tp->lock); -} - -static int -tulip_rx(struct net_device *dev) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - int entry = tp->cur_rx % RX_RING_SIZE; - int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; - int work_done = 0; - - if (tulip_debug > 4) - printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, - tp->rx_ring[entry].status); - /* If we own the next entry, it's a new packet. Send it up. */ - while (tp->rx_ring[entry].status >= 0) { - s32 status = tp->rx_ring[entry].status; - - if (tulip_debug > 5) - printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, - tp->rx_ring[entry].status); - if (--rx_work_limit < 0) - break; - if ((status & 0x38008300) != 0x0300) { - if ((status & 0x38000300) != 0x0300) { - /* Ingore earlier buffers. */ - if ((status & 0xffff) != 0x7fff) { - if (tulip_debug > 1) - printk(KERN_WARNING "%s: Oversized Ethernet frame " - "spanned multiple buffers, status %8.8x!\n", - dev->name, status); - tp->stats.rx_length_errors++; - } - } else if (status & RxDescFatalErr) { - /* There was a fatal error. */ - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", - dev->name, status); - tp->stats.rx_errors++; /* end of a packet.*/ - if (status & 0x0890) tp->stats.rx_length_errors++; - if (status & 0x0004) tp->stats.rx_frame_errors++; - if (status & 0x0002) tp->stats.rx_crc_errors++; - if (status & 0x0001) tp->stats.rx_fifo_errors++; - } - } else { - /* Omit the four octet CRC from the length. */ - short pkt_len = ((status >> 16) & 0x7ff) - 4; - struct sk_buff *skb; - -#ifndef final_version - if (pkt_len > 1518) { - printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n", - dev->name, pkt_len, pkt_len); - pkt_len = 1518; - tp->stats.rx_length_errors++; - } -#endif - /* Check if the packet is long enough to accept without copying - to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak - && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; - skb_reserve(skb, 2); /* 16 byte align the IP header */ -#if ! defined(__alpha__) - eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), - pkt_len, 0); - skb_put(skb, pkt_len); -#else - memcpy(skb_put(skb, pkt_len), - bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); -#endif - work_done++; - } else { /* Pass up the skb already on the Rx ring. */ - char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len); - tp->rx_skbuff[entry] = NULL; -#ifndef final_version - if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp) - printk(KERN_ERR "%s: Internal fault: The skbuff addresses " - "do not match in tulip_rx: %p vs. %p / %p.\n", - dev->name, bus_to_virt(tp->rx_ring[entry].buffer1), - skb->head, temp); -#endif - } - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->last_rx = jiffies; - tp->stats.rx_packets++; - tp->stats.rx_bytes += pkt_len; - } - entry = (++tp->cur_rx) % RX_RING_SIZE; - } - - /* Refill the Rx ring buffers. */ - for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { - entry = tp->dirty_rx % RX_RING_SIZE; - if (tp->rx_skbuff[entry] == NULL) { - struct sk_buff *skb; - skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ); - if (skb == NULL) - break; - skb->dev = dev; /* Mark as being used by this device. */ - tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail); - work_done++; - } - tp->rx_ring[entry].status = DescOwned; - } - - return work_done; -} - -static void -tulip_down(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - - /* Disable interrupts by clearing the interrupt mask. */ - outl(0x00000000, ioaddr + CSR7); - /* Stop the chip's Tx and Rx processes. */ - outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, tp->chip_id); - /* 21040 -- Leave the card in 10baseT state. */ - if (tp->chip_id == DC21040) - outl(0x00000004, ioaddr + CSR13); - - if (inl(ioaddr + CSR6) != 0xffffffff) - tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; - - dev->if_port = tp->saved_if_port; -} - -static int -tulip_close(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - int i; - - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n", - dev->name, inl(ioaddr + CSR5)); - - netif_stop_queue(dev); - - if (netif_device_present(dev)) - tulip_down(dev); - - del_timer(&tp->timer); - - free_irq(dev->irq, dev); - - /* Free all the skbuffs in the Rx queue. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = tp->rx_skbuff[i]; - tp->rx_skbuff[i] = 0; - tp->rx_ring[i].status = 0; /* Not owned by Tulip chip. */ - tp->rx_ring[i].length = 0; - tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */ - if (skb) { - dev_kfree_skb(skb); - } - } - for (i = 0; i < TX_RING_SIZE; i++) { - if (tp->tx_skbuff[i]) - dev_kfree_skb(tp->tx_skbuff[i]); - tp->tx_skbuff[i] = 0; - } - - MOD_DEC_USE_COUNT; - tp->open = 0; - return 0; -} - -static struct net_device_stats *tulip_get_stats(struct net_device *dev) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; - - if (netif_device_present(dev)) - tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; - - return &tp->stats; -} - -#ifdef HAVE_PRIVATE_IOCTL -/* Provide ioctl() calls to examine the MII xcvr state. */ -static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; - u16 *data = (u16 *)&rq->ifr_data; - int phy = tp->phys[0] & 0x1f; - long flags; - - switch(cmd) { - case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ - if (tp->mii_cnt) - data[0] = phy; - else if (tp->chip_id == DC21142) /* 21142 pseudo-MII */ - data[0] = 32; - else if (tp->chip_id == PNIC2) - data[0] = 32; - else if (tp->chip_id == COMET) - data[0] = 1; - else - return -ENODEV; - return 0; - case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ - if (data[0] == 32 && - (tp->chip_id == DC21142 || tp->chip_id == PNIC2)) { - int csr12 = inl(ioaddr + CSR12); - int csr14 = inl(ioaddr + CSR14); - switch (data[1]) { - case 0: { - data[3] = (csr14<<5) & 0x1000; - break; } - case 1: - data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0) - + (csr12&0x06 ? 0x04 : 0); - break; - case 4: { - data[3] = ((csr14>>9)&0x0380) + - ((inl(ioaddr + CSR6)>>3)&0x0040) +((csr14>>1)&0x20) + 1; - break; - } - case 5: data[3] = csr12 >> 16; break; - default: data[3] = 0; break; - } - } else { - save_flags(flags); - cli(); - data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); - restore_flags(flags); - } - return 0; - case SIOCDEVPRIVATE+2: /* Write the specified MII register */ -#if defined(CAP_NET_ADMIN) - if (!capable(CAP_NET_ADMIN)) - return -EPERM; -#else - if (!suser()) - return -EPERM; -#endif - if (data[0] == 32 && tp->chip_id == DC21142) { - if (data[1] == 5) - tp->to_advertise = data[2]; - } else { - save_flags(flags); - cli(); - mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); - restore_flags(flags); - } - return 0; - default: - return -EOPNOTSUPP; - } - - return -EOPNOTSUPP; -} -#endif /* HAVE_PRIVATE_IOCTL */ - -/* Set or clear the multicast filter for this adaptor. - Note that we only use exclusion around actually queueing the - new frame, not around filling tp->setup_frame. This is non-deterministic - when re-entered but still correct. */ - -/* The little-endian AUTODIN32 ethernet CRC calculation. - N.B. Do not use for bulk data, use a table-based routine instead. - This is common code and should be moved to net/core/crc.c */ -static unsigned const ethernet_polynomial_le = 0xedb88320U; -static inline u32 ether_crc_le(int length, unsigned char *data) -{ - u32 crc = 0xffffffff; /* Initial value. */ - while(--length >= 0) { - unsigned char current_octet = *data++; - int bit; - for (bit = 8; --bit >= 0; current_octet >>= 1) { - if ((crc ^ current_octet) & 1) { - crc >>= 1; - crc ^= ethernet_polynomial_le; - } else - crc >>= 1; - } - } - return crc; -} -static unsigned const ethernet_polynomial = 0x04c11db7U; -static inline u32 ether_crc(int length, unsigned char *data) -{ - int crc = -1; - - while(--length >= 0) { - unsigned char current_octet = *data++; - int bit; - for (bit = 0; bit < 8; bit++, current_octet >>= 1) - crc = (crc << 1) ^ - ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); - } - return crc; -} - -static void set_rx_mode(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - int csr6 = inl(ioaddr + CSR6) & ~0x00D5; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - - tp->csr6 &= ~0x00D5; - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - tp->csr6 |= 0x00C0; - csr6 |= 0x00C0; - /* Unconditionally log net taps. */ - printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); - } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter well -- accept all multicasts. */ - tp->csr6 |= 0x0080; - csr6 |= 0x0080; - } else if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) { - /* Some work-alikes have only a 64-entry hash filter table. */ - /* Should verify correctness on big-endian/__powerpc__ */ - struct dev_mc_list *mclist; - int i; - u32 mc_filter[2]; /* Multicast hash filter */ - if (dev->mc_count > 64) { /* Arbitrary non-effective limit. */ - tp->csr6 |= 0x0080; - csr6 |= 0x0080; - } else { - mc_filter[1] = mc_filter[0] = 0; - for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; - i++, mclist = mclist->next) - set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, mc_filter); - if (tp->chip_id == AX88140) { - outl(2, ioaddr + CSR13); - outl(mc_filter[0], ioaddr + CSR14); - outl(3, ioaddr + CSR13); - outl(mc_filter[1], ioaddr + CSR14); - } else if (tp->chip_id == COMET) { /* Has a simple hash filter. */ - outl(mc_filter[0], ioaddr + 0xAC); - outl(mc_filter[1], ioaddr + 0xB0); - } - } - } else { - u16 *eaddrs, *setup_frm = tp->setup_frame; - struct dev_mc_list *mclist; - u32 tx_flags = 0x08000000 | 192; - int i; - - /* Note that only the low-address shortword of setup_frame is valid! - The values are doubled for big-endian architectures. */ - if ((dev->mc_count > 14) || ((dev->mc_count > 6) && (tp->chip_id == X3201_3))) { /* Must use a multicast hash table. */ - u16 hash_table[32]; - tx_flags = 0x08400000 | 192; /* Use hash filter. */ - memset(hash_table, 0, sizeof(hash_table)); - set_bit(255, hash_table); /* Broadcast entry */ - /* This should work on big-endian machines as well. */ - for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; - i++, mclist = mclist->next) - set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff, - hash_table); - for (i = 0; i < 32; i++) { - *setup_frm++ = hash_table[i]; - *setup_frm++ = hash_table[i]; - } - setup_frm = &tp->setup_frame[13*6]; - } else if(tp->chip_id != X3201_3) { - /* We have <= 14 addresses so we can use the wonderful - 16 address perfect filtering of the Tulip. */ - for (i = 0, mclist = dev->mc_list; i < dev->mc_count; - i++, mclist = mclist->next) { - eaddrs = (u16 *)mclist->dmi_addr; - *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; - } - /* Fill the unused entries with the broadcast address. */ - memset(setup_frm, 0xff, (15-i)*12); - setup_frm = &tp->setup_frame[15*6]; - } else { - /* fill the first two table entries with our address */ - eaddrs = (u16 *)dev->dev_addr; - for(i=0; i<2; i++) { - *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; - } - /* Double fill each entry to accomodate chips that */ - /* don't like to parse these correctly */ - for (i=0, mclist=dev->mc_list; i<dev->mc_count; - i++, mclist=mclist->next) { - eaddrs = (u16 *)mclist->dmi_addr; - *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; - *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; - } - i=((i+1)*2); - /* Fill the unused entries with the broadcast address. */ - memset(setup_frm, 0xff, (15-i)*12); - setup_frm = &tp->setup_frame[15*6]; - } - - /* Fill the final entry with our physical address. */ - eaddrs = (u16 *)dev->dev_addr; - *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; - /* Now add this frame to the Tx list. */ - if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { - /* Same setup recently queued, we need not add it. */ - } else { - unsigned long flags; - unsigned int entry, dummy = -1; - - save_flags(flags); cli(); - entry = tp->cur_tx++ % TX_RING_SIZE; - - if (entry != 0) { - /* Avoid a chip errata by prefixing a dummy entry. */ - tp->tx_skbuff[entry] = 0; - tp->tx_ring[entry].length = - (entry == TX_RING_SIZE-1) ? DESC_RING_WRAP : 0; - tp->tx_ring[entry].buffer1 = 0; - /* race with chip, set DescOwned later */ - dummy = entry; - entry = tp->cur_tx++ % TX_RING_SIZE; - } - - tp->tx_skbuff[entry] = 0; - /* Put the setup frame on the Tx list. */ - if (entry == TX_RING_SIZE-1) - tx_flags |= DESC_RING_WRAP; /* Wrap ring. */ - tp->tx_ring[entry].length = tx_flags; - if(tp->chip_id == X3201_3) - tp->tx_ring[entry].buffer1 = (virt_to_bus(tp->setup_frame) + 4); - else - tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); - tp->tx_ring[entry].status = DescOwned; - if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) { - tp->tx_full = 1; - netif_stop_queue (dev); - } - if (dummy >= 0) - tp->tx_ring[dummy].status = DescOwned; - restore_flags(flags); - /* Trigger an immediate transmit demand. */ - outl(0, ioaddr + CSR1); - } - } - outl_CSR6(csr6 | 0x0000, ioaddr, tp->chip_id); -} - -static const struct pci_device_id tulip_pci_table[] __devinitdata = { - { 0x1011, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21040 }, - { 0x1011, 0x0014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21041 }, - { 0x1011, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 }, - { 0x1011, 0x0019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21142 }, - { 0x11AD, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, LC82C168 }, - { 0x10d9, 0x0512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98713 }, - { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 }, - { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98725 }, - { 0x125B, 0x1400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AX88140 }, - { 0x11AD, 0xc115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PNIC2 }, - { 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 }, - { 0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 }, - {0}, -}; - -MODULE_DEVICE_TABLE(pci, tulip_pci_table); - -static int __devinit tulip_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct net_device *dev; - static int board_idx = 0; - - printk(KERN_INFO "tulip_attach(%s)\n", pdev->slot_name); - - pci_enable_device (pdev); - pci_set_master (pdev); - dev = tulip_probe1(pdev, NULL, - pci_resource_start (pdev, 0), pdev->irq, - id->driver_data, board_idx++); - if (dev) { - pdev->driver_data = dev; - return 0; - } - return -ENODEV; -} - -static void tulip_suspend(struct pci_dev *pdev) -{ - struct net_device *dev = pdev->driver_data; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - printk(KERN_INFO "tulip_suspend(%s)\n", dev->name); - if (tp->open) tulip_down(dev); -} - -static void tulip_resume(struct pci_dev *pdev) -{ - struct net_device *dev = pdev->driver_data; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - printk(KERN_INFO "tulip_resume(%s)\n", dev->name); - if (tp->open) tulip_up(dev); -} - -static void __devexit tulip_remove(struct pci_dev *pdev) -{ - struct net_device *dev = pdev->driver_data; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - - printk(KERN_INFO "tulip_detach(%s)\n", dev->name); - unregister_netdev(dev); - kfree(dev); - kfree(tp); -} - -static struct pci_driver tulip_ops = { - name: "tulip_cb", - id_table: tulip_pci_table, - probe: tulip_pci_probe, - remove: tulip_remove, - suspend: tulip_suspend, - resume: tulip_resume -}; - -static int __init tulip_init(void) -{ - pci_register_driver(&tulip_ops); - return 0; -} - -static __exit void tulip_exit(void) -{ - pci_unregister_driver(&tulip_ops); -} - -module_init(tulip_init) -module_exit(tulip_exit) - - -/* - * Local variables: - * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" - * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" - * cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia-cs-3.0.9/include/" - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 4 - * End: - */ diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 7374eac66..167d3cb12 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -1470,7 +1470,7 @@ static int pcnet32_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) lp->a.write_bcr (ioaddr, 33, phyaddr); return 0; case SIOCDEVPRIVATE+2: /* Write the specified MII register */ - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EPERM; lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f)); lp->a.write_bcr (ioaddr, 34, data[2]); diff --git a/drivers/net/plip.c b/drivers/net/plip.c index d86ce1d85..64fafa17f 100644 --- a/drivers/net/plip.c +++ b/drivers/net/plip.c @@ -583,6 +583,61 @@ plip_receive(unsigned short nibble_timeout, struct net_device *dev, return OK; } +/* + * Determine the packet's protocol ID. The rule here is that we + * assume 802.3 if the type field is short enough to be a length. + * This is normal practice and works for any 'now in use' protocol. + * + * PLIP is ethernet ish but the daddr might not be valid if unicast. + * PLIP fortunately has no bus architecture (its Point-to-point). + * + * We can't fix the daddr thing as that quirk (more bug) is embedded + * in far too many old systems not all even running Linux. + */ + +static unsigned short plip_type_trans(struct sk_buff *skb, struct net_device *dev) +{ + struct ethhdr *eth; + unsigned char *rawp; + + skb->mac.raw=skb->data; + skb_pull(skb,dev->hard_header_len); + eth= skb->mac.ethernet; + + if(*eth->h_dest&1) + { + if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0) + skb->pkt_type=PACKET_BROADCAST; + else + skb->pkt_type=PACKET_MULTICAST; + } + + /* + * This ALLMULTI check should be redundant by 1.4 + * so don't forget to remove it. + */ + + if (ntohs(eth->h_proto) >= 1536) + return eth->h_proto; + + rawp = skb->data; + + /* + * This is a magic hack to spot IPX packets. Older Novell breaks + * the protocol design and runs IPX over 802.3 without an 802.2 LLC + * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This + * won't work for fault tolerant netware but does for the rest. + */ + if (*(unsigned short *)rawp == 0xFFFF) + return htons(ETH_P_802_3); + + /* + * Real 802.2 LLC + */ + return htons(ETH_P_802_2); +} + + /* PLIP_RECEIVE_PACKET --- receive a packet */ static int plip_receive_packet(struct net_device *dev, struct net_local *nl, @@ -669,7 +724,7 @@ plip_receive_packet(struct net_device *dev, struct net_local *nl, case PLIP_PK_DONE: /* Inform the upper layer for the arrival of a packet. */ - rcv->skb->protocol=eth_type_trans(rcv->skb, dev); + rcv->skb->protocol=plip_type_trans(rcv->skb, dev); netif_rx(rcv->skb); nl->enet_stats.rx_bytes += rcv->length.h; nl->enet_stats.rx_packets++; @@ -1227,6 +1282,8 @@ plip_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) pc->nibble = nl->nibble; break; case PLIP_SET_TIMEOUT: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; nl->trigger = pc->trigger; nl->nibble = pc->nibble; break; diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index 43b5508ae..3cbf0e5e4 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -17,11 +17,9 @@ * PPP driver, written by Michael Callahan and Al Longyear, and * subsequently hacked by Paul Mackerras. * - * ==FILEVERSION 990806== + * ==FILEVERSION 20000227== */ -/* $Id: ppp_async.c,v 1.3 1999/09/02 05:30:10 paulus Exp $ */ - #include <linux/module.h> #include <linux/kernel.h> #include <linux/skbuff.h> @@ -31,9 +29,18 @@ #include <linux/ppp_defs.h> #include <linux/if_ppp.h> #include <linux/ppp_channel.h> +#include <linux/spinlock.h> +#include <linux/init.h> #include <asm/uaccess.h> -#define PPP_VERSION "2.4.0" +#ifndef spin_trylock_bh +#define spin_trylock_bh(lock) ({ int __r; local_bh_disable(); \ + __r = spin_trylock(lock); \ + if (!__r) local_bh_enable(); \ + __r; }) +#endif + +#define PPP_VERSION "2.4.1" #define OBUFSIZE 256 @@ -44,7 +51,9 @@ struct asyncppp { unsigned int state; unsigned int rbits; int mru; - unsigned long busy; + spinlock_t xmit_lock; + spinlock_t recv_lock; + unsigned long xmit_flags; u32 xaccm[8]; u32 raccm; unsigned int bytes_sent; @@ -55,24 +64,18 @@ struct asyncppp { u16 tfcs; unsigned char *optr; unsigned char *olim; - struct sk_buff_head xq; unsigned long last_xmit; struct sk_buff *rpkt; - struct sk_buff_head rq; - wait_queue_head_t rwait; + int lcp_fcs; struct ppp_channel chan; /* interface to generic ppp layer */ - int connected; - int index; unsigned char obuf[OBUFSIZE]; }; -/* Bit numbers in busy */ -#define XMIT_BUSY 0 -#define RECV_BUSY 1 -#define XMIT_WAKEUP 2 -#define XMIT_FULL 3 +/* Bit numbers in xmit_flags */ +#define XMIT_WAKEUP 0 +#define XMIT_FULL 1 /* State bits */ #define SC_TOSS 0x20000000 @@ -81,8 +84,6 @@ struct asyncppp { /* Bits in rbits */ #define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) -#define PPPASYNC_MAX_RQLEN 32 /* arbitrary */ - static int flag_time = HZ; MODULE_PARM(flag_time, "i"); @@ -95,57 +96,17 @@ static int ppp_async_push(struct asyncppp *ap); static void ppp_async_flush_output(struct asyncppp *ap); static void ppp_async_input(struct asyncppp *ap, const unsigned char *buf, char *flags, int count); +static int ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, + unsigned long arg); +static void async_lcp_peek(struct asyncppp *ap, unsigned char *data, + int len, int inbound); struct ppp_channel_ops async_ops = { - ppp_async_send + ppp_async_send, + ppp_async_ioctl }; /* - * Routines for locking and unlocking the transmit and receive paths. - */ -static inline void -lock_path(struct asyncppp *ap, int bit) -{ - do { - while (test_bit(bit, &ap->busy)) - mb(); - } while (test_and_set_bit(bit, &ap->busy)); - mb(); -} - -static inline int -trylock_path(struct asyncppp *ap, int bit) -{ - if (test_and_set_bit(bit, &ap->busy)) - return 0; - mb(); - return 1; -} - -static inline void -unlock_path(struct asyncppp *ap, int bit) -{ - mb(); - clear_bit(bit, &ap->busy); -} - -#define lock_xmit_path(ap) lock_path(ap, XMIT_BUSY) -#define trylock_xmit_path(ap) trylock_path(ap, XMIT_BUSY) -#define unlock_xmit_path(ap) unlock_path(ap, XMIT_BUSY) -#define lock_recv_path(ap) lock_path(ap, RECV_BUSY) -#define trylock_recv_path(ap) trylock_path(ap, RECV_BUSY) -#define unlock_recv_path(ap) unlock_path(ap, RECV_BUSY) - -static inline void -flush_skb_queue(struct sk_buff_head *q) -{ - struct sk_buff *skb; - - while ((skb = skb_dequeue(q)) != 0) - kfree_skb(skb); -} - -/* * Routines implementing the PPP line discipline. */ @@ -153,256 +114,113 @@ flush_skb_queue(struct sk_buff_head *q) * Called when a tty is put into PPP line discipline. */ static int -ppp_async_open(struct tty_struct *tty) +ppp_asynctty_open(struct tty_struct *tty) { struct asyncppp *ap; + int err; ap = kmalloc(sizeof(*ap), GFP_KERNEL); if (ap == 0) return -ENOMEM; - MOD_INC_USE_COUNT; - /* initialize the asyncppp structure */ memset(ap, 0, sizeof(*ap)); ap->tty = tty; ap->mru = PPP_MRU; + spin_lock_init(&ap->xmit_lock); + spin_lock_init(&ap->recv_lock); ap->xaccm[0] = ~0U; ap->xaccm[3] = 0x60000000U; ap->raccm = ~0U; ap->optr = ap->obuf; ap->olim = ap->obuf; - skb_queue_head_init(&ap->xq); - skb_queue_head_init(&ap->rq); - init_waitqueue_head(&ap->rwait); + ap->lcp_fcs = -1; + + ap->chan.private = ap; + ap->chan.ops = &async_ops; + ap->chan.mtu = PPP_MRU; + err = ppp_register_channel(&ap->chan); + if (err) { + kfree(ap); + return err; + } tty->disc_data = ap; + MOD_INC_USE_COUNT; return 0; } /* * Called when the tty is put into another line discipline - * (or it hangs up). + * or it hangs up. + * We assume that while we are in this routine, the tty layer + * won't call any of the other line discipline entries for the + * same tty. */ static void -ppp_async_close(struct tty_struct *tty) +ppp_asynctty_close(struct tty_struct *tty) { struct asyncppp *ap = tty->disc_data; if (ap == 0) return; tty->disc_data = 0; - lock_xmit_path(ap); - lock_recv_path(ap); + ppp_unregister_channel(&ap->chan); if (ap->rpkt != 0) kfree_skb(ap->rpkt); - flush_skb_queue(&ap->rq); if (ap->tpkt != 0) kfree_skb(ap->tpkt); - flush_skb_queue(&ap->xq); - if (ap->connected) - ppp_unregister_channel(&ap->chan); kfree(ap); MOD_DEC_USE_COUNT; } /* - * Read a PPP frame. pppd can use this to negotiate over the - * channel before it joins it to a bundle. + * Read does nothing. */ static ssize_t -ppp_async_read(struct tty_struct *tty, struct file *file, - unsigned char *buf, size_t count) +ppp_asynctty_read(struct tty_struct *tty, struct file *file, + unsigned char *buf, size_t count) { + /* For now, do the same as the old 2.3.x code useta */ struct asyncppp *ap = tty->disc_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - struct sk_buff *skb = 0; - ret = -ENXIO; if (ap == 0) - goto out; /* should never happen */ - - add_wait_queue(&ap->rwait, &wait); - current->state = TASK_INTERRUPTIBLE; - for (;;) { - ret = -EAGAIN; - skb = skb_dequeue(&ap->rq); - if (skb) - break; - if (file->f_flags & O_NONBLOCK) - break; - ret = -ERESTARTSYS; - if (signal_pending(current)) - break; - schedule(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&ap->rwait, &wait); - - if (skb == 0) - goto out; - - ret = -EOVERFLOW; - if (skb->len > count) - goto outf; - ret = -EFAULT; - if (copy_to_user(buf, skb->data, skb->len)) - goto outf; - ret = skb->len; - - outf: - kfree_skb(skb); - out: - return ret; + return -ENXIO; + return ppp_channel_read(&ap->chan, file, buf, count); } /* - * Write a ppp frame. pppd can use this to send frames over - * this particular channel. + * Write on the tty does nothing, the packets all come in + * from the ppp generic stuff. */ static ssize_t -ppp_async_write(struct tty_struct *tty, struct file *file, - const unsigned char *buf, size_t count) +ppp_asynctty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t count) { + /* For now, do the same as the old 2.3.x code useta */ struct asyncppp *ap = tty->disc_data; - struct sk_buff *skb; - ssize_t ret; - ret = -ENXIO; if (ap == 0) - goto out; /* should never happen */ - - ret = -ENOMEM; - skb = alloc_skb(count + 2, GFP_KERNEL); - if (skb == 0) - goto out; - skb_reserve(skb, 2); - ret = -EFAULT; - if (copy_from_user(skb_put(skb, count), buf, count)) { - kfree_skb(skb); - goto out; - } - - skb_queue_tail(&ap->xq, skb); - ppp_async_push(ap); - - ret = count; - - out: - return ret; + return -ENXIO; + return ppp_channel_write(&ap->chan, buf, count); } static int -ppp_async_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) +ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { struct asyncppp *ap = tty->disc_data; int err, val; - u32 accm[8]; - struct sk_buff *skb; - - err = -ENXIO; - if (ap == 0) - goto out; /* should never happen */ - err = -EPERM; - if (!capable(CAP_NET_ADMIN)) - goto out; err = -EFAULT; switch (cmd) { - case PPPIOCGFLAGS: - val = ap->flags | ap->rbits; - if (put_user(val, (int *) arg)) - break; - err = 0; - break; - case PPPIOCSFLAGS: - if (get_user(val, (int *) arg)) - break; - ap->flags = val & ~SC_RCV_BITS; - ap->rbits = val & SC_RCV_BITS; - err = 0; - break; - - case PPPIOCGASYNCMAP: - if (put_user(ap->xaccm[0], (u32 *) arg)) - break; - err = 0; - break; - case PPPIOCSASYNCMAP: - if (get_user(ap->xaccm[0], (u32 *) arg)) - break; - err = 0; - break; - - case PPPIOCGRASYNCMAP: - if (put_user(ap->raccm, (u32 *) arg)) - break; - err = 0; - break; - case PPPIOCSRASYNCMAP: - if (get_user(ap->raccm, (u32 *) arg)) - break; - err = 0; - break; - - case PPPIOCGXASYNCMAP: - if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm))) - break; - err = 0; - break; - case PPPIOCSXASYNCMAP: - if (copy_from_user(accm, (void *) arg, sizeof(accm))) - break; - accm[2] &= ~0x40000000U; /* can't escape 0x5e */ - accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ - memcpy(ap->xaccm, accm, sizeof(ap->xaccm)); - err = 0; - break; - - case PPPIOCGMRU: - if (put_user(ap->mru, (int *) arg)) - break; - err = 0; - break; - case PPPIOCSMRU: - if (get_user(val, (int *) arg)) - break; - if (val < PPP_MRU) - val = PPP_MRU; - ap->mru = val; - err = 0; - break; - - case PPPIOCATTACH: - if (get_user(val, (int *) arg)) - break; - err = -EALREADY; - if (ap->connected) - break; - ap->chan.private = ap; - ap->chan.ops = &async_ops; - err = ppp_register_channel(&ap->chan, val); - if (err != 0) - break; - ap->connected = 1; - ap->index = val; - break; - case PPPIOCDETACH: - err = -ENXIO; - if (!ap->connected) - break; - ppp_unregister_channel(&ap->chan); - ap->connected = 0; - err = 0; - break; case PPPIOCGUNIT: err = -ENXIO; - if (!ap->connected) + if (ap == 0) break; - if (put_user(ap->index, (int *) arg)) + err = -EFAULT; + if (put_user(ppp_channel_index(&ap->chan), (int *) arg)) break; err = 0; break; @@ -414,8 +232,6 @@ ppp_async_ioctl(struct tty_struct *tty, struct file *file, case TCFLSH: /* flush our buffers and the serial port's buffer */ - if (arg == TCIFLUSH || arg == TCIOFLUSH) - flush_skb_queue(&ap->rq); if (arg == TCIOFLUSH || arg == TCOFLUSH) ppp_async_flush_output(ap); err = n_tty_ioctl(tty, file, cmd, arg); @@ -423,68 +239,86 @@ ppp_async_ioctl(struct tty_struct *tty, struct file *file, case FIONREAD: val = 0; - if ((skb = skb_peek(&ap->rq)) != 0) - val = skb->len; if (put_user(val, (int *) arg)) break; err = 0; break; +/* + * For now, do the same as the old 2.3 driver useta + */ + case PPPIOCGFLAGS: + case PPPIOCSFLAGS: + case PPPIOCGASYNCMAP: + case PPPIOCSASYNCMAP: + case PPPIOCGRASYNCMAP: + case PPPIOCSRASYNCMAP: + case PPPIOCGXASYNCMAP: + case PPPIOCSXASYNCMAP: + case PPPIOCGMRU: + case PPPIOCSMRU: + err = ppp_async_ioctl(&ap->chan, cmd, arg); + break; + + case PPPIOCATTACH: + err = ppp_channel_ioctl(&ap->chan, cmd, arg); + break; + default: err = -ENOIOCTLCMD; } - out: + return err; } static unsigned int -ppp_async_poll(struct tty_struct *tty, struct file *file, poll_table *wait) +ppp_asynctty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) { - struct asyncppp *ap = tty->disc_data; unsigned int mask; + struct asyncppp *ap = tty->disc_data; - if (ap == 0) - return 0; /* should never happen */ - poll_wait(file, &ap->rwait, wait); mask = POLLOUT | POLLWRNORM; - if (skb_peek(&ap->rq)) - mask |= POLLIN | POLLRDNORM; +/* + * For now, do the same as the old 2.3 driver useta + */ + if (ap != 0) + mask |= ppp_channel_poll(&ap->chan, file, wait); if (test_bit(TTY_OTHER_CLOSED, &tty->flags) || tty_hung_up_p(file)) mask |= POLLHUP; return mask; } static int -ppp_async_room(struct tty_struct *tty) +ppp_asynctty_room(struct tty_struct *tty) { return 65535; } static void -ppp_async_receive(struct tty_struct *tty, const unsigned char *buf, +ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, char *flags, int count) { struct asyncppp *ap = tty->disc_data; if (ap == 0) return; - trylock_recv_path(ap); + spin_lock_bh(&ap->recv_lock); ppp_async_input(ap, buf, flags, count); - unlock_recv_path(ap); + spin_unlock_bh(&ap->recv_lock); if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && tty->driver.unthrottle) tty->driver.unthrottle(tty); } static void -ppp_async_wakeup(struct tty_struct *tty) +ppp_asynctty_wakeup(struct tty_struct *tty) { struct asyncppp *ap = tty->disc_data; clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); if (ap == 0) return; - if (ppp_async_push(ap) && ap->connected) + if (ppp_async_push(ap)) ppp_output_wakeup(&ap->chan); } @@ -492,15 +326,15 @@ ppp_async_wakeup(struct tty_struct *tty) static struct tty_ldisc ppp_ldisc = { magic: TTY_LDISC_MAGIC, name: "ppp", - open: ppp_async_open, - close: ppp_async_close, - read: ppp_async_read, - write: ppp_async_write, - ioctl: ppp_async_ioctl, - poll: ppp_async_poll, - receive_room: ppp_async_room, - receive_buf: ppp_async_receive, - write_wakeup: ppp_async_wakeup, + open: ppp_asynctty_open, + close: ppp_asynctty_close, + read: ppp_asynctty_read, + write: ppp_asynctty_write, + ioctl: ppp_asynctty_ioctl, + poll: ppp_asynctty_poll, + receive_room: ppp_asynctty_room, + receive_buf: ppp_asynctty_receive, + write_wakeup: ppp_asynctty_wakeup, }; int @@ -516,6 +350,91 @@ ppp_async_init(void) } /* + * The following routines provide the PPP channel interface. + */ +static int +ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg) +{ + struct asyncppp *ap = chan->private; + int err, val; + u32 accm[8]; + + err = -EFAULT; + switch (cmd) { + case PPPIOCGFLAGS: + val = ap->flags | ap->rbits; + if (put_user(val, (int *) arg)) + break; + err = 0; + break; + case PPPIOCSFLAGS: + if (get_user(val, (int *) arg)) + break; + ap->flags = val & ~SC_RCV_BITS; + spin_lock_bh(&ap->recv_lock); + ap->rbits = val & SC_RCV_BITS; + spin_unlock_bh(&ap->recv_lock); + err = 0; + break; + + case PPPIOCGASYNCMAP: + if (put_user(ap->xaccm[0], (u32 *) arg)) + break; + err = 0; + break; + case PPPIOCSASYNCMAP: + if (get_user(ap->xaccm[0], (u32 *) arg)) + break; + err = 0; + break; + + case PPPIOCGRASYNCMAP: + if (put_user(ap->raccm, (u32 *) arg)) + break; + err = 0; + break; + case PPPIOCSRASYNCMAP: + if (get_user(ap->raccm, (u32 *) arg)) + break; + err = 0; + break; + + case PPPIOCGXASYNCMAP: + if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm))) + break; + err = 0; + break; + case PPPIOCSXASYNCMAP: + if (copy_from_user(accm, (void *) arg, sizeof(accm))) + break; + accm[2] &= ~0x40000000U; /* can't escape 0x5e */ + accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ + memcpy(ap->xaccm, accm, sizeof(ap->xaccm)); + err = 0; + break; + + case PPPIOCGMRU: + if (put_user(ap->mru, (int *) arg)) + break; + err = 0; + break; + case PPPIOCSMRU: + if (get_user(val, (int *) arg)) + break; + if (val < PPP_MRU) + val = PPP_MRU; + ap->mru = val; + err = 0; + break; + + default: + err = -ENOTTY; + } + + return err; +} + +/* * Procedures for encapsulation and framing. */ @@ -597,6 +516,9 @@ ppp_async_encode(struct asyncppp *ap) islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7; if (i == 0) { + if (islcp) + async_lcp_peek(ap, data, count, 0); + /* * Start of a new packet - insert the leading FLAG * character if necessary. @@ -675,7 +597,7 @@ ppp_async_send(struct ppp_channel *chan, struct sk_buff *skb) ppp_async_push(ap); - if (test_and_set_bit(XMIT_FULL, &ap->busy)) + if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags)) return 0; /* already full */ ap->tpkt = skb; ap->tpkt_pos = 0; @@ -694,12 +616,11 @@ ppp_async_push(struct asyncppp *ap) struct tty_struct *tty = ap->tty; int tty_stuffed = 0; - if (!trylock_xmit_path(ap)) { - set_bit(XMIT_WAKEUP, &ap->busy); + set_bit(XMIT_WAKEUP, &ap->xmit_flags); + if (!spin_trylock_bh(&ap->xmit_lock)) return 0; - } for (;;) { - if (test_and_clear_bit(XMIT_WAKEUP, &ap->busy)) + if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags)) tty_stuffed = 0; if (!tty_stuffed && ap->optr < ap->olim) { avail = ap->olim - ap->optr; @@ -715,22 +636,17 @@ ppp_async_push(struct asyncppp *ap) if (ap->optr == ap->olim && ap->tpkt != 0) { if (ppp_async_encode(ap)) { /* finished processing ap->tpkt */ - struct sk_buff *skb = skb_dequeue(&ap->xq); - if (skb != 0) { - ap->tpkt = skb; - } else { - clear_bit(XMIT_FULL, &ap->busy); - done = 1; - } + clear_bit(XMIT_FULL, &ap->xmit_flags); + done = 1; } continue; } /* haven't made any progress */ - unlock_xmit_path(ap); - if (!(test_bit(XMIT_WAKEUP, &ap->busy) + spin_unlock_bh(&ap->xmit_lock); + if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) || (!tty_stuffed && ap->tpkt != 0))) break; - if (!trylock_xmit_path(ap)) + if (!spin_trylock_bh(&ap->xmit_lock)) break; } return done; @@ -739,11 +655,11 @@ flush: if (ap->tpkt != 0) { kfree_skb(ap->tpkt); ap->tpkt = 0; - clear_bit(XMIT_FULL, &ap->busy); + clear_bit(XMIT_FULL, &ap->xmit_flags); done = 1; } ap->optr = ap->olim; - unlock_xmit_path(ap); + spin_unlock_bh(&ap->xmit_lock); return done; } @@ -756,17 +672,16 @@ ppp_async_flush_output(struct asyncppp *ap) { int done = 0; - flush_skb_queue(&ap->xq); - lock_xmit_path(ap); + spin_lock_bh(&ap->xmit_lock); ap->optr = ap->olim; if (ap->tpkt != NULL) { kfree_skb(ap->tpkt); ap->tpkt = 0; - clear_bit(XMIT_FULL, &ap->busy); + clear_bit(XMIT_FULL, &ap->xmit_flags); done = 1; } - unlock_xmit_path(ap); - if (done && ap->connected) + spin_unlock_bh(&ap->xmit_lock); + if (done) ppp_output_wakeup(&ap->chan); } @@ -795,7 +710,7 @@ process_input_packet(struct asyncppp *ap) { struct sk_buff *skb; unsigned char *p; - unsigned int len, fcs; + unsigned int len, fcs, proto; int code = 0; skb = ap->rpkt; @@ -827,37 +742,32 @@ process_input_packet(struct asyncppp *ap) goto err; p = skb_pull(skb, 2); } - if (p[0] & 1) { + proto = p[0]; + if (proto & 1) { /* protocol is compressed */ skb_push(skb, 1)[0] = 0; - } else if (skb->len < 2) - goto err; - - /* all OK, give it to the generic layer or queue it */ - if (ap->connected) { - ppp_input(&ap->chan, skb); } else { - skb_queue_tail(&ap->rq, skb); - /* drop old frames if queue too long */ - while (ap->rq.qlen > PPPASYNC_MAX_RQLEN - && (skb = skb_dequeue(&ap->rq)) != 0) - kfree(skb); - wake_up_interruptible(&ap->rwait); + if (skb->len < 2) + goto err; + proto = (proto << 8) + p[1]; + if (proto == PPP_LCP) + async_lcp_peek(ap, p, skb->len, 1); } + + /* all OK, give it to the generic layer */ + ppp_input(&ap->chan, skb); return; err: kfree_skb(skb); - if (ap->connected) - ppp_input_error(&ap->chan, code); + ppp_input_error(&ap->chan, code); } static inline void input_error(struct asyncppp *ap, int code) { ap->state |= SC_TOSS; - if (ap->connected) - ppp_input_error(&ap->chan, code); + ppp_input_error(&ap->chan, code); } /* called when the tty driver has data for us. */ @@ -950,17 +860,96 @@ ppp_async_input(struct asyncppp *ap, const unsigned char *buf, input_error(ap, 0); } -#ifdef MODULE -int -init_module(void) +/* + * We look at LCP frames going past so that we can notice + * and react to the LCP configure-ack from the peer. + * In the situation where the peer has been sent a configure-ack + * already, LCP is up once it has sent its configure-ack + * so the immediately following packet can be sent with the + * configured LCP options. This allows us to process the following + * packet correctly without pppd needing to respond quickly. + * + * We only respond to the received configure-ack if we have just + * sent a configure-request, and the configure-ack contains the + * same data (this is checked using a 16-bit crc of the data). + */ +#define CONFREQ 1 /* LCP code field values */ +#define CONFACK 2 +#define LCP_MRU 1 /* LCP option numbers */ +#define LCP_ASYNCMAP 2 + +static void async_lcp_peek(struct asyncppp *ap, unsigned char *data, + int len, int inbound) { - return ppp_async_init(); + int dlen, fcs, i, code; + u32 val; + + data += 2; /* skip protocol bytes */ + len -= 2; + if (len < 4) /* 4 = code, ID, length */ + return; + code = data[0]; + if (code != CONFACK && code != CONFREQ) + return; + dlen = (data[2] << 8) + data[3]; + if (len < dlen) + return; /* packet got truncated or length is bogus */ + + if (code == (inbound? CONFACK: CONFREQ)) { + /* + * sent confreq or received confack: + * calculate the crc of the data from the ID field on. + */ + fcs = PPP_INITFCS; + for (i = 1; i < dlen; ++i) + fcs = PPP_FCS(fcs, data[i]); + + if (!inbound) { + /* outbound confreq - remember the crc for later */ + ap->lcp_fcs = fcs; + return; + } + + /* received confack, check the crc */ + fcs ^= ap->lcp_fcs; + ap->lcp_fcs = -1; + if (fcs != 0) + return; + } else if (inbound) + return; /* not interested in received confreq */ + + /* process the options in the confack */ + data += 4; + dlen -= 4; + /* data[0] is code, data[1] is length */ + while (dlen >= 2 && dlen >= data[1]) { + switch (data[0]) { + case LCP_MRU: + val = (data[2] << 8) + data[3]; + if (inbound) + ap->mru = val; + else + ap->chan.mtu = val; + break; + case LCP_ASYNCMAP: + val = (data[2] << 24) + (data[3] << 16) + + (data[4] << 8) + data[5]; + if (inbound) + ap->raccm = val; + else + ap->xaccm[0] = val; + break; + } + dlen -= data[1]; + data += data[1]; + } } -void -cleanup_module(void) +void __exit ppp_async_cleanup(void) { if (tty_register_ldisc(N_PPP, NULL) != 0) printk(KERN_ERR "failed to unregister PPP line discipline\n"); } -#endif /* MODULE */ + +module_init(ppp_async_init); +module_exit(ppp_async_cleanup); diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c index 3ef379ab2..761d8705c 100644 --- a/drivers/net/ppp_deflate.c +++ b/drivers/net/ppp_deflate.c @@ -32,26 +32,9 @@ */ #include <linux/module.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/types.h> -#include <linux/fcntl.h> -#include <linux/interrupt.h> -#include <linux/ptrace.h> -#include <linux/ioport.h> -#include <linux/in.h> #include <linux/malloc.h> #include <linux/vmalloc.h> -#include <linux/errno.h> -#include <linux/string.h> /* used in new tty drivers */ -#include <linux/signal.h> /* used in new tty drivers */ - -#include <asm/system.h> - -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <linux/inet.h> -#include <linux/ioctl.h> +#include <linux/init.h> #include <linux/ppp_defs.h> #include <linux/ppp-comp.h> @@ -656,31 +639,21 @@ struct compressor ppp_deflate_draft = { z_comp_stats, /* decomp_stat */ }; -#ifdef MODULE -/************************************************************* - * Module support routines - *************************************************************/ - -int -init_module(void) +int deflate_init(void) { - int answer = ppp_register_compressor (&ppp_deflate); + int answer = ppp_register_compressor(&ppp_deflate); if (answer == 0) - printk (KERN_INFO - "PPP Deflate Compression module registered\n"); + printk(KERN_INFO + "PPP Deflate Compression module registered\n"); ppp_register_compressor(&ppp_deflate_draft); return answer; } -void -cleanup_module(void) +void deflate_cleanup(void) { - if (MOD_IN_USE) - printk (KERN_INFO - "Deflate Compression module busy, remove delayed\n"); - else { - ppp_unregister_compressor (&ppp_deflate); - ppp_unregister_compressor (&ppp_deflate_draft); - } + ppp_unregister_compressor(&ppp_deflate); + ppp_unregister_compressor(&ppp_deflate_draft); } -#endif + +module_init(deflate_init); +module_exit(deflate_cleanup); diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 0b3aeff4b..01a4e2f81 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -1,7 +1,7 @@ /* * Generic PPP layer for Linux. * - * Copyright 1999 Paul Mackerras. + * Copyright 1999-2000 Paul Mackerras. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,11 +19,9 @@ * PPP driver, written by Michael Callahan and Al Longyear, and * subsequently hacked by Paul Mackerras. * - * ==FILEVERSION 990915== + * ==FILEVERSION 20000313== */ -/* $Id: ppp_generic.c,v 1.5 1999/09/15 11:21:48 paulus Exp $ */ - #include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> @@ -44,16 +42,9 @@ #include <linux/tcp.h> #include <linux/spinlock.h> #include <net/slhc_vj.h> +#include <asm/atomic.h> -#define PPP_VERSION "2.4.0" - -EXPORT_SYMBOL(ppp_register_channel); -EXPORT_SYMBOL(ppp_unregister_channel); -EXPORT_SYMBOL(ppp_input); -EXPORT_SYMBOL(ppp_input_error); -EXPORT_SYMBOL(ppp_output_wakeup); -EXPORT_SYMBOL(ppp_register_compressor); -EXPORT_SYMBOL(ppp_unregister_compressor); +#define PPP_VERSION "2.4.1" /* * Network protocols we support. @@ -64,32 +55,56 @@ EXPORT_SYMBOL(ppp_unregister_compressor); #define NP_AT 3 /* Appletalk protocol */ #define NUM_NP 4 /* Number of NPs. */ +#define MPHDRLEN 4 /* multilink protocol header length */ +#define MPHDRLEN_SSN 2 /* ditto with short sequence numbers */ +#define MIN_FRAG_SIZE 64 + +/* + * An instance of /dev/ppp can be associated with either a ppp + * interface unit or a ppp channel. In both cases, file->private_data + * points to one of these. + */ +struct ppp_file { + enum { + INTERFACE=1, CHANNEL + } kind; + struct sk_buff_head xq; /* pppd transmit queue */ + struct sk_buff_head rq; /* receive queue for pppd */ + wait_queue_head_t rwait; /* for poll on reading /dev/ppp */ + atomic_t refcnt; /* # refs (incl /dev/ppp attached) */ + int hdrlen; /* space to leave for headers */ + struct list_head list; /* link in all_* list */ + int index; /* interface unit / channel number */ +}; + +#define PF_TO_X(pf, X) ((X *)((char *)(pf)-(unsigned long)(&((X *)0)->file))) + +#define PF_TO_PPP(pf) PF_TO_X(pf, struct ppp) +#define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel) + +#define ROUNDUP(n, x) (((n) + (x) - 1) / (x)) + /* * Data structure describing one ppp unit. * A ppp unit corresponds to a ppp network interface device * and represents a multilink bundle. - * It may have 0 or more ppp channels connected to it. + * It can have 0 or more ppp channels connected to it. */ struct ppp { - struct list_head list; /* link in list of ppp units */ - int index; /* interface unit number */ + struct ppp_file file; /* stuff for read/write/poll */ char name[16]; /* unit name */ - int refcnt; /* # open /dev/ppp attached */ - unsigned long busy; /* lock and other bits */ struct list_head channels; /* list of attached channels */ int n_channels; /* how many channels are attached */ + spinlock_t rlock; /* lock for receive side */ + spinlock_t wlock; /* lock for transmit side */ int mru; /* max receive unit */ unsigned int flags; /* control bits */ unsigned int xstate; /* transmit state bits */ unsigned int rstate; /* receive state bits */ int debug; /* debug flags */ struct slcompress *vj; /* state for VJ header compression */ - struct sk_buff_head xq; /* pppd transmit queue */ - struct sk_buff_head rq; /* receive queue for pppd */ - wait_queue_head_t rwait; /* for poll on reading /dev/ppp */ enum NPmode npmode[NUM_NP]; /* what to do with each net proto */ struct sk_buff *xmit_pending; /* a packet ready to go out */ - struct sk_buff_head recv_pending;/* pending input packets */ struct compressor *xcomp; /* transmit packet compressor */ void *xc_state; /* its internal state */ struct compressor *rcomp; /* receive decompressor */ @@ -97,57 +112,121 @@ struct ppp { unsigned long last_xmit; /* jiffies when last pkt sent */ unsigned long last_recv; /* jiffies when last pkt rcvd */ struct net_device *dev; /* network interface device */ +#ifdef CONFIG_PPP_MULTILINK + int nxchan; /* next channel to send something on */ + u32 nxseq; /* next sequence number to send */ + int mrru; /* MP: max reconst. receive unit */ + u32 nextseq; /* MP: seq no of next packet */ + u32 minseq; /* MP: min of most recent seqnos */ + struct sk_buff_head mrq; /* MP: receive reconstruction queue */ +#endif /* CONFIG_PPP_MULTILINK */ struct net_device_stats stats; /* statistics */ }; -static LIST_HEAD(all_ppp_units); -static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED; +/* + * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC, + * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ. + * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR. + * Bits in xstate: SC_COMP_RUN + */ +#define SC_FLAG_BITS (SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC \ + |SC_MULTILINK|SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ) /* * Private data structure for each channel. - * Ultimately this will have multilink stuff etc. in it. + * This includes the data structure used for multilink. */ struct channel { - struct list_head list; /* link in list of channels per unit */ + struct ppp_file file; /* stuff for read/write/poll */ struct ppp_channel *chan; /* public channel data structure */ - int blocked; /* if channel refused last packet */ + spinlock_t downl; /* protects `chan', file.xq dequeue */ struct ppp *ppp; /* ppp unit we're connected to */ + struct list_head clist; /* link in list of channels per unit */ + rwlock_t upl; /* protects `ppp' and `ulist' */ +#ifdef CONFIG_PPP_MULTILINK + u8 avail; /* flag used in multilink stuff */ + u8 had_frag; /* >= 1 fragments have been sent */ + u32 lastseq; /* MP: last sequence # received */ +#endif /* CONFIG_PPP_MULTILINK */ }; -/* Bit numbers in busy */ -#define XMIT_BUSY 0 -#define RECV_BUSY 1 -#define XMIT_WAKEUP 2 +/* + * SMP locking issues: + * Both the ppp.rlock and ppp.wlock locks protect the ppp.channels + * list and the ppp.n_channels field, you need to take both locks + * before you modify them. + * The lock ordering is: channel.upl -> ppp.wlock -> ppp.rlock -> + * channel.downl. + */ /* - * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC. - * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR. - * Bits in xstate: SC_COMP_RUN + * all_ppp_lock protects the all_ppp_units. + * It also ensures that finding a ppp unit in the all_ppp_units list + * and updating its file.refcnt field is atomic. + */ +static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(all_ppp_units); + +/* + * all_channels_lock protects all_channels and last_channel_index, + * and the atomicity of find a channel and updating its file.refcnt + * field. */ -#define SC_FLAG_BITS (SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC) +static spinlock_t all_channels_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(all_channels); +static int last_channel_index; /* Get the PPP protocol number from a skb */ #define PPP_PROTO(skb) (((skb)->data[0] << 8) + (skb)->data[1]) -/* We limit the length of ppp->rq to this (arbitrary) value */ +/* We limit the length of ppp->file.rq to this (arbitrary) value */ #define PPP_MAX_RQLEN 32 +/* Multilink header bits. */ +#define B 0x80 /* this fragment begins a packet */ +#define E 0x40 /* this fragment ends a packet */ + +/* Compare multilink sequence numbers (assumed to be 32 bits wide) */ +#define seq_before(a, b) ((s32)((a) - (b)) < 0) +#define seq_after(a, b) ((s32)((a) - (b)) > 0) + /* Prototypes. */ -static void ppp_xmit_unlock(struct ppp *ppp, int do_mark_bh); +static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file, + char *buf, size_t count); +static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf, + size_t count); +static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file, + unsigned int cmd, unsigned long arg); +static void ppp_xmit_process(struct ppp *ppp, int wakeup); static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb); static void ppp_push(struct ppp *ppp); -static void ppp_recv_unlock(struct ppp *ppp); -static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb); +static void ppp_channel_push(struct channel *pch); +static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, + struct channel *pch); +static void ppp_receive_error(struct ppp *ppp); +static void ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb); static struct sk_buff *ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb); +#ifdef CONFIG_PPP_MULTILINK +static void ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, + struct channel *pch); +static void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb); +static struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp); +static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb); +#endif /* CONFIG_PPP_MULTILINK */ static int ppp_set_compress(struct ppp *ppp, unsigned long arg); static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound); static void ppp_ccp_closed(struct ppp *ppp); static struct compressor *find_compressor(int type); static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st); -static struct ppp *ppp_create_unit(int unit, int *retp); -static void ppp_release_unit(struct ppp *ppp); +static struct ppp *ppp_create_interface(int unit, int *retp); +static void init_ppp_file(struct ppp_file *pf, int kind); +static void ppp_destroy_interface(struct ppp *ppp); static struct ppp *ppp_find_unit(int unit); +static struct channel *ppp_find_channel(int unit); +static int ppp_connect_channel(struct channel *pch, int unit); +static int ppp_disconnect_channel(struct channel *pch); +static void ppp_destroy_channel(struct channel *pch); /* Translates a PPP protocol number to a NP index (NP == network protocol) */ static inline int proto_to_npindex(int proto) @@ -199,69 +278,24 @@ static const int npindex_to_ethertype[NUM_NP] = { }; /* - * Routines for locking and unlocking the transmit and receive paths - * of each unit. - * - * On the transmit side, we have threads of control coming into the - * driver from (at least) three places: the core net code, write calls - * on /dev/ppp from pppd, and wakeup calls from channels. There is - * possible concurrency even on UP systems (between mainline and - * BH processing). The XMIT_BUSY bit in ppp->busy serializes the - * transmit-side processing for each ppp unit. + * Locking shorthand. */ -static inline void -lock_path(struct ppp *ppp, int bit) -{ - int timeout = 1000000; - - do { - while (test_bit(bit, &ppp->busy)) { - mb(); - if (--timeout == 0) { - printk(KERN_ERR "lock_path timeout ppp=%p bit=%x\n", ppp, bit); - return; - } - } - } while (test_and_set_bit(bit, &ppp->busy)); - mb(); -} +#define ppp_xmit_lock(ppp) spin_lock_bh(&(ppp)->wlock) +#define ppp_xmit_unlock(ppp) spin_unlock_bh(&(ppp)->wlock) +#define ppp_recv_lock(ppp) spin_lock_bh(&(ppp)->rlock) +#define ppp_recv_unlock(ppp) spin_unlock_bh(&(ppp)->rlock) +#define ppp_lock(ppp) do { ppp_xmit_lock(ppp); \ + ppp_recv_lock(ppp); } while (0) +#define ppp_unlock(ppp) do { ppp_recv_unlock(ppp); \ + ppp_xmit_unlock(ppp); } while (0) -static inline int -trylock_path(struct ppp *ppp, int bit) -{ - if (test_and_set_bit(bit, &ppp->busy)) - return 0; - mb(); - return 1; -} - -static inline void -unlock_path(struct ppp *ppp, int bit) -{ - mb(); - clear_bit(bit, &ppp->busy); -} - -#define lock_xmit_path(ppp) lock_path(ppp, XMIT_BUSY) -#define trylock_xmit_path(ppp) trylock_path(ppp, XMIT_BUSY) -#define unlock_xmit_path(ppp) unlock_path(ppp, XMIT_BUSY) -#define lock_recv_path(ppp) lock_path(ppp, RECV_BUSY) -#define trylock_recv_path(ppp) trylock_path(ppp, RECV_BUSY) -#define unlock_recv_path(ppp) unlock_path(ppp, RECV_BUSY) - -static inline void -free_skbs(struct sk_buff_head *head) -{ - struct sk_buff *skb; - - while ((skb = skb_dequeue(head)) != 0) - kfree_skb(skb); -} /* * /dev/ppp device routines. * The /dev/ppp device is used by pppd to control the ppp unit. * It supports the read, write, ioctl and poll functions. + * Open instances of /dev/ppp can be in one of three states: + * unattached, attached to a ppp unit, or attached to a ppp channel. */ static int ppp_open(struct inode *inode, struct file *file) { @@ -276,11 +310,20 @@ static int ppp_open(struct inode *inode, struct file *file) static int ppp_release(struct inode *inode, struct file *file) { - struct ppp *ppp = (struct ppp *) file->private_data; + struct ppp_file *pf = (struct ppp_file *) file->private_data; - if (ppp != 0) { + if (pf != 0) { file->private_data = 0; - ppp_release_unit(ppp); + if (atomic_dec_and_test(&pf->refcnt)) { + switch (pf->kind) { + case INTERFACE: + ppp_destroy_interface(PF_TO_PPP(pf)); + break; + case CHANNEL: + ppp_destroy_channel(PF_TO_CHANNEL(pf)); + break; + } + } } MOD_DEC_USE_COUNT; return 0; @@ -289,20 +332,27 @@ static int ppp_release(struct inode *inode, struct file *file) static ssize_t ppp_read(struct file *file, char *buf, size_t count, loff_t *ppos) { - struct ppp *ppp = (struct ppp *) file->private_data; + struct ppp_file *pf = (struct ppp_file *) file->private_data; + + return ppp_file_read(pf, file, buf, count); +} + +static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file, + char *buf, size_t count) +{ DECLARE_WAITQUEUE(wait, current); ssize_t ret; struct sk_buff *skb = 0; ret = -ENXIO; - if (ppp == 0) + if (pf == 0) goto out; /* not currently attached */ - add_wait_queue(&ppp->rwait, &wait); + add_wait_queue(&pf->rwait, &wait); current->state = TASK_INTERRUPTIBLE; for (;;) { ret = -EAGAIN; - skb = skb_dequeue(&ppp->rq); + skb = skb_dequeue(&pf->rq); if (skb) break; if (file->f_flags & O_NONBLOCK) @@ -313,7 +363,7 @@ static ssize_t ppp_read(struct file *file, char *buf, schedule(); } current->state = TASK_RUNNING; - remove_wait_queue(&ppp->rwait, &wait); + remove_wait_queue(&pf->rwait, &wait); if (skb == 0) goto out; @@ -335,32 +385,42 @@ static ssize_t ppp_read(struct file *file, char *buf, static ssize_t ppp_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { - struct ppp *ppp = (struct ppp *) file->private_data; + struct ppp_file *pf = (struct ppp_file *) file->private_data; + + return ppp_file_write(pf, buf, count); +} + +static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf, + size_t count) +{ struct sk_buff *skb; ssize_t ret; - int extra; ret = -ENXIO; - if (ppp == 0) + if (pf == 0) goto out; ret = -ENOMEM; - extra = PPP_HDRLEN - 2; - if (ppp->dev && ppp->dev->hard_header_len > PPP_HDRLEN) - extra = ppp->dev->hard_header_len - 2; - skb = alloc_skb(count + extra, GFP_KERNEL); + skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL); if (skb == 0) goto out; - skb_reserve(skb, extra); + skb_reserve(skb, pf->hdrlen); ret = -EFAULT; if (copy_from_user(skb_put(skb, count), buf, count)) { kfree_skb(skb); goto out; } - skb_queue_tail(&ppp->xq, skb); - if (trylock_xmit_path(ppp)) - ppp_xmit_unlock(ppp, 1); + skb_queue_tail(&pf->xq, skb); + + switch (pf->kind) { + case INTERFACE: + ppp_xmit_process(PF_TO_PPP(pf), 0); + break; + case CHANNEL: + ppp_channel_push(PF_TO_CHANNEL(pf)); + break; + } ret = count; @@ -370,68 +430,82 @@ static ssize_t ppp_write(struct file *file, const char *buf, static unsigned int ppp_poll(struct file *file, poll_table *wait) { - struct ppp *ppp = (struct ppp *) file->private_data; + struct ppp_file *pf = (struct ppp_file *) file->private_data; unsigned int mask; - if (ppp == 0) + if (pf == 0) return 0; - poll_wait(file, &ppp->rwait, wait); + poll_wait(file, &pf->rwait, wait); mask = POLLOUT | POLLWRNORM; - if (skb_peek(&ppp->rq) != 0) + if (skb_peek(&pf->rq) != 0) mask |= POLLIN | POLLRDNORM; + if (pf->kind == CHANNEL) { + struct channel *pch = PF_TO_CHANNEL(pf); + if (pch->chan == 0) + mask |= POLLHUP; + } return mask; } static int ppp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct ppp *ppp = (struct ppp *) file->private_data; - int err, val, val2, i; + struct ppp_file *pf = (struct ppp_file *) file->private_data; + struct ppp *ppp; + int err = -EFAULT, val, val2, i; struct ppp_idle idle; struct npioctl npi; + int unit; + struct slcompress *vj; - if (cmd == PPPIOCNEWUNIT) { - /* Create a new ppp unit */ - int unit, ret; + if (pf == 0) + return ppp_unattached_ioctl(pf, file, cmd, arg); - if (ppp != 0) - return -EINVAL; - if (get_user(unit, (int *) arg)) - return -EFAULT; - ppp = ppp_create_unit(unit, &ret); - if (ppp == 0) - return ret; - file->private_data = ppp; - if (put_user(ppp->index, (int *) arg)) - return -EFAULT; - return 0; + if (pf->kind == CHANNEL) { + struct channel *pch = PF_TO_CHANNEL(pf); + struct ppp_channel *chan; + + switch (cmd) { + case PPPIOCCONNECT: + if (get_user(unit, (int *) arg)) + break; + err = ppp_connect_channel(pch, unit); + break; + + case PPPIOCDISCONN: + err = ppp_disconnect_channel(pch); + break; + + case PPPIOCDETACH: + file->private_data = 0; + if (atomic_dec_and_test(&pf->refcnt)) + ppp_destroy_channel(pch); + err = 0; + break; + + default: + spin_lock_bh(&pch->downl); + chan = pch->chan; + err = -ENOTTY; + if (chan->ops->ioctl) + err = chan->ops->ioctl(chan, cmd, arg); + spin_unlock_bh(&pch->downl); + } + return err; } - if (cmd == PPPIOCATTACH) { - /* Attach to an existing ppp unit */ - int unit; - if (ppp != 0) - return -EINVAL; - if (get_user(unit, (int *) arg)) - return -EFAULT; - spin_lock(&all_ppp_lock); - ppp = ppp_find_unit(unit); - if (ppp != 0) - ++ppp->refcnt; - spin_unlock(&all_ppp_lock); - if (ppp == 0) - return -ENXIO; - file->private_data = ppp; - return 0; + if (pf->kind != INTERFACE) { + /* can't happen */ + printk(KERN_ERR "PPP: not interface or channel??\n"); + return -EINVAL; } - if (ppp == 0) - return -ENXIO; - err = -EFAULT; + ppp = PF_TO_PPP(pf); switch (cmd) { case PPPIOCDETACH: file->private_data = 0; - ppp_release_unit(ppp); + if (atomic_dec_and_test(&pf->refcnt)) + ppp_destroy_interface(ppp); err = 0; break; @@ -445,9 +519,11 @@ static int ppp_ioctl(struct inode *inode, struct file *file, case PPPIOCSFLAGS: if (get_user(val, (int *) arg)) break; + ppp_lock(ppp); if (ppp->flags & ~val & SC_CCP_OPEN) ppp_ccp_closed(ppp); ppp->flags = val & SC_FLAG_BITS; + ppp_unlock(ppp); err = 0; break; @@ -463,7 +539,7 @@ static int ppp_ioctl(struct inode *inode, struct file *file, break; case PPPIOCGUNIT: - if (put_user(ppp->index, (int *) arg)) + if (put_user(ppp->file.index, (int *) arg)) break; err = 0; break; @@ -497,18 +573,17 @@ static int ppp_ioctl(struct inode *inode, struct file *file, val2 = val >> 16; val &= 0xffff; } - lock_xmit_path(ppp); - lock_recv_path(ppp); - if (ppp->vj != 0) - slhc_free(ppp->vj); - ppp->vj = slhc_init(val2+1, val+1); - ppp_recv_unlock(ppp); - ppp_xmit_unlock(ppp, 1); - err = -ENOMEM; - if (ppp->vj == 0) { + vj = slhc_init(val2+1, val+1); + if (vj == 0) { printk(KERN_ERR "PPP: no memory (VJ compressor)\n"); + err = -ENOMEM; break; } + ppp_lock(ppp); + if (ppp->vj != 0) + slhc_free(ppp->vj); + ppp->vj = vj; + ppp_unlock(ppp); err = 0; break; @@ -533,6 +608,76 @@ static int ppp_ioctl(struct inode *inode, struct file *file, err = 0; break; +#ifdef CONFIG_PPP_MULTILINK + case PPPIOCSMRRU: + if (get_user(val, (int *) arg)) + break; + ppp_recv_lock(ppp); + ppp->mrru = val; + ppp_recv_unlock(ppp); + err = 0; + break; +#endif /* CONFIG_PPP_MULTILINK */ + + default: + err = -ENOTTY; + } + return err; +} + +static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int unit, err = -EFAULT; + struct ppp *ppp; + struct channel *chan; + + switch (cmd) { + case PPPIOCNEWUNIT: + /* Create a new ppp unit */ + if (get_user(unit, (int *) arg)) + break; + ppp = ppp_create_interface(unit, &err); + if (ppp == 0) + break; + file->private_data = &ppp->file; + err = -EFAULT; + if (put_user(ppp->file.index, (int *) arg)) + break; + err = 0; + break; + + case PPPIOCATTACH: + /* Attach to an existing ppp unit */ + if (get_user(unit, (int *) arg)) + break; + spin_lock(&all_ppp_lock); + ppp = ppp_find_unit(unit); + if (ppp != 0) + atomic_inc(&ppp->file.refcnt); + spin_unlock(&all_ppp_lock); + err = -ENXIO; + if (ppp == 0) + break; + file->private_data = &ppp->file; + err = 0; + break; + + case PPPIOCATTCHAN: + if (get_user(unit, (int *) arg)) + break; + spin_lock_bh(&all_channels_lock); + chan = ppp_find_channel(unit); + if (chan != 0) + atomic_inc(&chan->file.refcnt); + spin_unlock_bh(&all_channels_lock); + err = -ENXIO; + if (chan == 0) + break; + file->private_data = &chan->file; + err = 0; + break; + default: err = -ENOTTY; } @@ -557,37 +702,17 @@ static devfs_handle_t devfs_handle = NULL; int __init ppp_init(void) { int err; -#ifndef MODULE -#ifdef CONFIG_PPP_DEFLATE - extern struct compressor ppp_deflate, ppp_deflate_draft; -#endif - extern int ppp_async_init(void); - extern int ppp_sync_init(void); -#endif printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n"); err = devfs_register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops); if (err) printk(KERN_ERR "failed to register PPP device (%d)\n", err); - devfs_handle = devfs_register (NULL, "ppp", 0, DEVFS_FL_NONE, - PPP_MAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, - &ppp_device_fops, NULL); -#ifndef MODULE -#ifdef CONFIG_PPP_ASYNC - ppp_async_init(); -#endif -#ifdef CONFIG_PPP_SYNC_TTY - ppp_sync_init(); -#endif -#ifdef CONFIG_PPP_DEFLATE - if (ppp_register_compressor(&ppp_deflate) == 0) - printk(KERN_INFO "PPP Deflate compression module registered\n"); - ppp_register_compressor(&ppp_deflate_draft); -#endif -#endif /* MODULE */ + devfs_handle = devfs_register(NULL, "ppp", 0, DEVFS_FL_NONE, + PPP_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &ppp_device_fops, NULL); - return -ENODEV; + return 0; } /* @@ -636,9 +761,8 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev) pp[1] = proto; netif_stop_queue(dev); - skb_queue_tail(&ppp->xq, skb); - if (trylock_xmit_path(ppp)) - ppp_xmit_unlock(ppp, 0); + skb_queue_tail(&ppp->file.xq, skb); + ppp_xmit_process(ppp, 0); return 0; outf: @@ -719,38 +843,26 @@ ppp_net_init(struct net_device *dev) */ /* - * Called to unlock the transmit side of the ppp unit, - * making sure that any work queued up gets done. + * Called to do any work queued up on the transmit side + * that can now be done. */ static void -ppp_xmit_unlock(struct ppp *ppp, int do_mark_bh) +ppp_xmit_process(struct ppp *ppp, int wakeup) { struct sk_buff *skb; - for (;;) { - /* Do whatever work is waiting to be done. */ - if (test_and_clear_bit(XMIT_WAKEUP, &ppp->busy)) - ppp_push(ppp); - /* If there's no work left to do, tell the core net - code that we can accept some more. */ - while (ppp->xmit_pending == 0 - && (skb = skb_dequeue(&ppp->xq)) != 0) - ppp_send_frame(ppp, skb); - if (ppp->xmit_pending == 0 && skb_peek(&ppp->xq) == 0) - netif_wake_queue(ppp->dev); - - /* Now unlock the transmit path, let others in. */ - unlock_xmit_path(ppp); - /* Check whether any work was queued up - between our last check and the unlock. */ - if (!(test_bit(XMIT_WAKEUP, &ppp->busy) - || (ppp->xmit_pending == 0 && skb_peek(&ppp->xq)))) - break; - /* If so, lock again and do the work. If we can't get - the lock, someone else has it and they'll do the work. */ - if (!trylock_xmit_path(ppp)) - break; - } + ppp_xmit_lock(ppp); + if (wakeup) + ppp_push(ppp); + while (ppp->xmit_pending == 0 + && (skb = skb_dequeue(&ppp->file.xq)) != 0) + ppp_send_frame(ppp, skb); + /* If there's no work left to do, tell the core net + code that we can accept some more. */ + if (ppp->xmit_pending == 0 && skb_peek(&ppp->file.xq) == 0 + && ppp->dev != 0) + netif_wake_queue(ppp->dev); + ppp_xmit_unlock(ppp); } /* @@ -847,10 +959,10 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) * queue it up for pppd to receive. */ if (ppp->flags & SC_LOOP_TRAFFIC) { - if (ppp->rq.qlen > PPP_MAX_RQLEN) + if (ppp->file.rq.qlen > PPP_MAX_RQLEN) goto drop; - skb_queue_tail(&ppp->rq, skb); - wake_up_interruptible(&ppp->rwait); + skb_queue_tail(&ppp->file.rq, skb); + wake_up_interruptible(&ppp->file.rwait); return; } @@ -871,7 +983,7 @@ static void ppp_push(struct ppp *ppp) { struct list_head *list; - struct channel *chan; + struct channel *pch; struct sk_buff *skb = ppp->xmit_pending; if (skb == 0) @@ -885,40 +997,222 @@ ppp_push(struct ppp *ppp) return; } - /* If we are doing multilink, decide which channel gets the - packet, and/or fragment the packet over several links. */ - /* XXX for now, just take the first channel */ - list = list->next; - chan = list_entry(list, struct channel, list); + if ((ppp->flags & SC_MULTILINK) == 0) { + /* not doing multilink: send it down the first channel */ + list = list->next; + pch = list_entry(list, struct channel, clist); - if (chan->chan->ops->start_xmit(chan->chan, skb)) { - ppp->xmit_pending = 0; - chan->blocked = 0; - } else - chan->blocked = 1; + spin_lock_bh(&pch->downl); + if (skb_queue_len(&pch->file.xq) == 0 + && pch->chan->ops->start_xmit(pch->chan, skb)) + ppp->xmit_pending = 0; + spin_unlock_bh(&pch->downl); + return; + } + +#ifdef CONFIG_PPP_MULTILINK + /* Multilink: fragment the packet over as many links + as can take the packet at the moment. */ + if (!ppp_mp_explode(ppp, skb)) + return; +#endif /* CONFIG_PPP_MULTILINK */ + + ppp->xmit_pending = 0; + kfree_skb(skb); +} + +#ifdef CONFIG_PPP_MULTILINK +/* + * Divide a packet to be transmitted into fragments and + * send them out the individual links. + */ +static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) +{ + int nch, len, fragsize; + int i, bits, hdrlen; + unsigned char *p, *q; + struct list_head *list; + struct channel *pch; + struct sk_buff *frag; + struct ppp_channel *chan; + + nch = 0; + list = &ppp->channels; + while ((list = list->next) != &ppp->channels) { + pch = list_entry(list, struct channel, clist); + nch += pch->avail = (skb_queue_len(&pch->file.xq) == 0); + /* + * If a channel hasn't had a fragment yet, it has to get + * one before we send any fragments on later channels. + * If it can't take a fragment now, don't give any + * to subsequent channels. + */ + if (!pch->had_frag && !pch->avail) { + while ((list = list->next) != &ppp->channels) { + pch = list_entry(list, struct channel, clist); + pch->avail = 0; + } + break; + } + } + if (nch == 0) + return 0; /* can't take now, leave it in xmit_pending */ + + /* Do protocol field compression (XXX this should be optional) */ + p = skb->data; + len = skb->len; + if (*p == 0) { + ++p; + --len; + } + + /* decide on fragment size */ + fragsize = len; + if (nch > 1) { + int maxch = ROUNDUP(len, MIN_FRAG_SIZE); + if (nch > maxch) + nch = maxch; + fragsize = ROUNDUP(fragsize, nch); + } + + /* skip to the channel after the one we last used + and start at that one */ + for (i = 0; i < ppp->nxchan; ++i) { + list = list->next; + if (list == &ppp->channels) { + i = 0; + break; + } + } + + /* create a fragment for each channel */ + bits = B; + hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN; + /* XXX gotta do A/C and prot compression here */ + do { + list = list->next; + if (list == &ppp->channels) { + i = 0; + continue; + } + pch = list_entry(list, struct channel, clist); + ++i; + if (!pch->avail) + continue; + if (fragsize >= len) { + fragsize = len; + bits |= E; + } + frag = alloc_skb(fragsize + hdrlen, GFP_ATOMIC); + if (frag != 0) { + q = skb_put(frag, fragsize + hdrlen); + /* make the MP header */ + if (ppp->flags & SC_MP_XSHORTSEQ) { + q[0] = bits + ((ppp->nxseq >> 8) & 0xf); + q[1] = ppp->nxseq; + } else { + q[0] = bits; + q[1] = ppp->nxseq >> 16; + q[2] = ppp->nxseq >> 8; + q[3] = ppp->nxseq; + } + + /* copy the data in */ + memcpy(q + hdrlen, p, fragsize); + + /* try to send it down the channel */ + spin_lock_bh(&pch->downl); + chan = pch->chan; + if (chan != 0) { + if (!chan->ops->start_xmit(chan, frag)) + skb_queue_tail(&pch->file.xq, frag); + } else { + /* channel got unregistered, too bad */ + kfree_skb(skb); + } + spin_unlock_bh(&pch->downl); + } + p += fragsize; + len -= fragsize; + ++ppp->nxseq; + bits = 0; + } while (len > 0); + ppp->nxchan = i; + + return 1; +} +#endif /* CONFIG_PPP_MULTILINK */ + +/* + * Try to send data out on a channel. + */ +static void +ppp_channel_push(struct channel *pch) +{ + struct sk_buff *skb; + + spin_lock_bh(&pch->downl); + if (pch->chan != 0) { + while (skb_queue_len(&pch->file.xq) > 0) { + skb = skb_dequeue(&pch->file.xq); + if (!pch->chan->ops->start_xmit(pch->chan, skb)) { + /* put the packet back and try again later */ + skb_queue_head(&pch->file.xq, skb); + break; + } + } + } else { + /* channel got deregistered */ + skb_queue_purge(&pch->file.xq); + } + spin_unlock_bh(&pch->downl); } /* * Receive-side routines. */ + +/* misuse a few fields of the skb for MP reconstruction */ +#define sequence priority +#define BEbits cb[0] + static inline void -ppp_do_recv(struct ppp *ppp, struct sk_buff *skb) +ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) { - skb_queue_tail(&ppp->recv_pending, skb); - if (trylock_recv_path(ppp)) - ppp_recv_unlock(ppp); + ppp_recv_lock(ppp); + /* ppp->dev == 0 means interface is closing down */ + if (ppp->dev != 0) + ppp_receive_frame(ppp, skb, pch); + else + kfree_skb(skb); + ppp_recv_unlock(ppp); } void ppp_input(struct ppp_channel *chan, struct sk_buff *skb) { struct channel *pch = chan->ppp; + int proto; if (pch == 0 || skb->len == 0) { kfree_skb(skb); return; } - ppp_do_recv(pch->ppp, skb); + + proto = PPP_PROTO(skb); + read_lock_bh(&pch->upl); + if (pch->ppp == 0 || proto == PPP_LCP || proto == 0x80fb) { + /* put it on the channel queue */ + skb_queue_tail(&pch->file.rq, skb); + /* drop old frames if queue too long */ + while (pch->file.rq.qlen > PPP_MAX_RQLEN + && (skb = skb_dequeue(&pch->file.rq)) != 0) + kfree_skb(skb); + wake_up_interruptible(&pch->file.rwait); + } else { + ppp_do_recv(pch->ppp, skb, pch); + } + read_unlock_bh(&pch->upl); } /* Put a 0-length skb in the receive queue as an error indication */ @@ -930,47 +1224,64 @@ ppp_input_error(struct ppp_channel *chan, int code) if (pch == 0) return; - skb = alloc_skb(0, GFP_ATOMIC); - if (skb == 0) - return; - skb->len = 0; /* probably unnecessary */ - skb->cb[0] = code; - ppp_do_recv(pch->ppp, skb); + + read_lock_bh(&pch->upl); + if (pch->ppp != 0) { + skb = alloc_skb(0, GFP_ATOMIC); + if (skb != 0) { + skb->len = 0; /* probably unnecessary */ + skb->cb[0] = code; + ppp_do_recv(pch->ppp, skb, pch); + } + } + read_unlock_bh(&pch->upl); } +/* + * We come in here to process a received frame. + * The receive side of the ppp unit is locked. + */ static void -ppp_recv_unlock(struct ppp *ppp) +ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) { - struct sk_buff *skb; - - for (;;) { - while ((skb = skb_dequeue(&ppp->recv_pending)) != 0) - ppp_receive_frame(ppp, skb); - unlock_recv_path(ppp); - if (skb_peek(&ppp->recv_pending) == 0) - break; - if (!trylock_recv_path(ppp)) - break; + if (skb->len >= 2) { +#ifdef CONFIG_PPP_MULTILINK + /* XXX do channel-level decompression here */ + if (PPP_PROTO(skb) == PPP_MP) + ppp_receive_mp_frame(ppp, skb, pch); + else +#endif /* CONFIG_PPP_MULTILINK */ + ppp_receive_nonmp_frame(ppp, skb); + return; } + + if (skb->len > 0) + /* note: a 0-length skb is used as an error indication */ + ++ppp->stats.rx_length_errors; + + kfree_skb(skb); + ppp_receive_error(ppp); } static void -ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb) +ppp_receive_error(struct ppp *ppp) +{ + ++ppp->stats.rx_errors; + if (ppp->vj != 0) + slhc_toss(ppp->vj); +} + +static void +ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) { struct sk_buff *ns; int proto, len, npi; - if (skb->len == 0) { - /* XXX should do something with code in skb->cb[0] */ - goto err; /* error indication */ - } - - if (skb->len < 2) { - ++ppp->stats.rx_length_errors; - goto err; - } - - /* Decompress the frame, if compressed. */ + /* + * Decompress the frame, if compressed. + * Note that some decompressors need to see uncompressed frames + * that come in as well as compressed frames. + */ if (ppp->rc_state != 0 && (ppp->rstate & SC_DECOMP_RUN) && (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0) skb = ppp_decompress_frame(ppp, skb); @@ -995,7 +1306,12 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb) } len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2); if (len <= 0) { - printk(KERN_ERR "PPP: VJ decompression error\n"); + int i; + printk(KERN_DEBUG "PPP: VJ decompression error\n"); + printk(KERN_DEBUG "PPP: len = %d data =", skb->len); + for (i = 0; i < 16 && i < skb->len; ++i) + printk(" %.2x", skb->data[i]); + printk("\n"); goto err; } len += 2; @@ -1027,15 +1343,13 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb) npi = proto_to_npindex(proto); if (npi < 0) { /* control or unknown frame - pass it to pppd */ - skb_queue_tail(&ppp->rq, skb); + skb_queue_tail(&ppp->file.rq, skb); /* limit queue length by dropping old frames */ - while (ppp->rq.qlen > PPP_MAX_RQLEN) { - skb = skb_dequeue(&ppp->rq); - if (skb) - kfree_skb(skb); - } + while (ppp->file.rq.qlen > PPP_MAX_RQLEN + && (skb = skb_dequeue(&ppp->file.rq)) != 0) + kfree_skb(skb); /* wake up any process polling or blocking on read */ - wake_up_interruptible(&ppp->rwait); + wake_up_interruptible(&ppp->file.rwait); } else { /* network protocol frame - give it to the kernel */ @@ -1054,10 +1368,8 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb) return; err: - ++ppp->stats.rx_errors; - if (ppp->vj != 0) - slhc_toss(ppp->vj); kfree_skb(skb); + ppp_receive_error(ppp); } static struct sk_buff * @@ -1070,7 +1382,7 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb) if (proto == PPP_COMP) { ns = dev_alloc_skb(ppp->mru + PPP_HDRLEN); if (ns == 0) { - printk(KERN_ERR "ppp_receive: no memory\n"); + printk(KERN_ERR "ppp_decompress_frame: no memory\n"); goto err; } /* the decompressor still expects the A/C bytes in the hdr */ @@ -1101,70 +1413,288 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb) err: ppp->rstate |= SC_DC_ERROR; - if (ppp->vj != 0) - slhc_toss(ppp->vj); - ++ppp->stats.rx_errors; + ppp_receive_error(ppp); return skb; } +#ifdef CONFIG_PPP_MULTILINK +/* + * Receive a multilink frame. + * We put it on the reconstruction queue and then pull off + * as many completed frames as we can. + */ +static void +ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) +{ + u32 mask, seq, minseq; + struct list_head *l; + int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? 2: 4; + + if (skb->len < mphdrlen + 3) + goto err; /* no good, throw it away */ + + /* Decode sequence number and begin/end bits */ + if (ppp->flags & SC_MP_SHORTSEQ) { + seq = ((skb->data[2] & 0x0f) << 8) | skb->data[3]; + mask = 0xfff; + } else { + seq = (skb->data[3] << 16) | (skb->data[4] << 8)| skb->data[5]; + mask = 0xffffff; + } + skb->BEbits = skb->data[2]; + skb_pull(skb, mphdrlen + 2); /* pull off PPP and MP headers*/ + + /* Expand sequence number to 32 bits */ + seq |= pch->lastseq & ~mask; + if (seq_before(seq, pch->lastseq)) { + if (seq_after(seq, pch->lastseq - 100)) { + printk(KERN_DEBUG "PPP: MP fragments out of order" + " (%u, %u)\n", pch->lastseq, seq); + goto err; + } + seq += mask + 1; + } + skb->sequence = seq; + pch->lastseq = seq; + + /* + * Reevaluate minseq, the minimum over all channels of the + * last sequence number received on each channel. Because of + * the increasing sequence number rule, we know that any fragment + * before `minseq' which hasn't arrived is never going to arrive. + * The list of channels can't change because we have the receive + * side of the ppp unit locked. + */ + minseq = seq; + for (l = ppp->channels.next; l != &ppp->channels; l = l->next) { + struct channel *ch = list_entry(l, struct channel, clist); + if (seq_before(ch->lastseq, seq)) + seq = ch->lastseq; + } + ppp->minseq = minseq; + + /* Put the fragment on the reconstruction queue */ + ppp_mp_insert(ppp, skb); + + /* Pull completed packets off the queue and receive them. */ + while ((skb = ppp_mp_reconstruct(ppp)) != 0) + ppp_receive_nonmp_frame(ppp, skb); + + return; + + err: + kfree_skb(skb); + ppp_receive_error(ppp); +} + +/* + * Insert a fragment on the MP reconstruction queue. + * The queue is ordered by increasing sequence number. + */ +static void +ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb) +{ + struct sk_buff *p; + struct sk_buff_head *list = &ppp->mrq; + u32 seq = skb->sequence; + + /* N.B. we don't need to lock the list lock because we have the + ppp unit receive-side lock. */ + for (p = list->next; p != (struct sk_buff *)list; p = p->next) + if (seq_before(seq, p->sequence)) + break; + __skb_insert(skb, p->prev, p, list); +} + +/* + * Reconstruct a packet from the MP fragment queue. + * We go through increasing sequence numbers until we find a + * complete packet, or we get to the sequence number for a fragment + * which hasn't arrived but might still do so. + */ +struct sk_buff * +ppp_mp_reconstruct(struct ppp *ppp) +{ + u32 seq = ppp->nextseq; + u32 minseq = ppp->minseq; + struct sk_buff_head *list = &ppp->mrq; + struct sk_buff *p, *next; + struct sk_buff *head, *tail; + struct sk_buff *skb = NULL; + int lost = 0, len = 0; + + head = list->next; + tail = NULL; + for (p = head; p != (struct sk_buff *) list; p = next) { + next = p->next; + if (seq_before(p->sequence, seq)) { + /* this can't happen, anyway toss the skb */ + printk(KERN_ERR "ppp_mp_reconstruct bad seq %x < %x\n", + p->sequence, seq); + __skb_unlink(p, list); + kfree_skb(p); + continue; + } + if (p->sequence != seq) { + /* Fragment `seq' is missing. If it is after + minseq, it might arrive later, so stop here. */ + if (seq_after(seq, minseq)) + break; + /* Fragment `seq' is lost, keep going. */ + lost = 1; + seq = seq_before(p->sequence, minseq)? + p->sequence: minseq; + next = p; + continue; + } + + /* + * At this point we know that all the fragments from + * ppp->nextseq to seq are either present or lost. + * Also, there are no complete packets in the queue + * that have no missing fragments and end before this + * fragment. + */ + + /* B bit set indicates this fragment starts a packet */ + if (p->BEbits & B) { + head = p; + lost = 0; + /* reset len, allow for protocol ID compression */ + len = p->data[0] & 1; + } + + len += p->len; + + /* Got a complete packet yet? */ + if (lost == 0 && (p->BEbits & E) && (head->BEbits & B)) { + if (len > ppp->mrru) { + ++ppp->stats.rx_length_errors; + } else if ((skb = dev_alloc_skb(len)) == NULL) { + ++ppp->stats.rx_missed_errors; + } else { + tail = p; + break; + } + } + + /* + * If this is the ending fragment of a packet, + * and we haven't found a complete valid packet yet, + * we can discard up to and including this fragment. + */ + if (p->BEbits & E) + head = next; + + ++seq; + } + + /* If we have a complete packet, copy it all into one skb. */ + if (tail != NULL) { + /* If we have discarded any fragments, + signal a receive error. */ + if (head->sequence != ppp->nextseq) + ppp_receive_error(ppp); + + /* uncompress protocol ID */ + if (head->data[0] & 1) + *skb_put(skb, 1) = 0; + p = head; + for (;;) { + memcpy(skb_put(skb, p->len), p->data, p->len); + if (p == tail) + break; + p = p->next; + } + ppp->nextseq = tail->sequence + 1; + head = tail->next; + } + + /* Discard all the skbuffs that we have copied the data out of + or that we can't use. */ + while ((p = list->next) != head) { + __skb_unlink(p, list); + kfree_skb(p); + } + + return skb; +} +#endif /* CONFIG_PPP_MULTILINK */ + /* * Channel interface. */ /* - * Connect a channel to a given PPP unit. - * The channel MUST NOT be connected to a PPP unit already. + * Create a new, unattached ppp channel. */ int -ppp_register_channel(struct ppp_channel *chan, int unit) +ppp_register_channel(struct ppp_channel *chan) { - struct ppp *ppp; struct channel *pch; - int ret = -ENXIO; - spin_lock(&all_ppp_lock); - ppp = ppp_find_unit(unit); - if (ppp == 0) - goto out; pch = kmalloc(sizeof(struct channel), GFP_ATOMIC); - ret = -ENOMEM; if (pch == 0) - goto out; + return -ENOMEM; memset(pch, 0, sizeof(struct channel)); - pch->ppp = ppp; + pch->ppp = NULL; pch->chan = chan; - list_add(&pch->list, &ppp->channels); chan->ppp = pch; - ++ppp->n_channels; - if (ppp->dev && chan->hdrlen + PPP_HDRLEN > ppp->dev->hard_header_len) - ppp->dev->hard_header_len = chan->hdrlen + PPP_HDRLEN; - ret = 0; - out: - spin_unlock(&all_ppp_lock); - return ret; + init_ppp_file(&pch->file, CHANNEL); + pch->file.hdrlen = chan->hdrlen; + spin_lock_init(&pch->downl); + pch->upl = RW_LOCK_UNLOCKED; + spin_lock_bh(&all_channels_lock); + pch->file.index = ++last_channel_index; + list_add(&pch->file.list, &all_channels); + spin_unlock_bh(&all_channels_lock); + MOD_INC_USE_COUNT; + return 0; } /* - * Disconnect a channel from its PPP unit. + * Return the index of a channel. + */ +int ppp_channel_index(struct ppp_channel *chan) +{ + struct channel *pch = chan->ppp; + + return pch->file.index; +} + +/* + * Disconnect a channel from the generic layer. + * This can be called from mainline or BH/softirq level. */ void ppp_unregister_channel(struct ppp_channel *chan) { - struct channel *pch; + struct channel *pch = chan->ppp; - spin_lock(&all_ppp_lock); - if ((pch = chan->ppp) != 0) { - chan->ppp = 0; - list_del(&pch->list); - --pch->ppp->n_channels; - kfree(pch); - } - spin_unlock(&all_ppp_lock); + if (pch == 0) + return; /* should never happen */ + chan->ppp = 0; + + /* + * This ensures that we have returned from any calls into the + * the channel's start_xmit or ioctl routine before we proceed. + */ + spin_lock_bh(&pch->downl); + pch->chan = 0; + spin_unlock_bh(&pch->downl); + ppp_disconnect_channel(pch); + wake_up_interruptible(&pch->file.rwait); + spin_lock_bh(&all_channels_lock); + list_del(&pch->file.list); + spin_unlock_bh(&all_channels_lock); + if (atomic_dec_and_test(&pch->file.refcnt)) + ppp_destroy_channel(pch); + MOD_DEC_USE_COUNT; } /* * Callback from a channel when it can accept more to transmit. - * This should ideally be called at BH level, not interrupt level. + * This should be called at BH/softirq level, not interrupt level. */ void ppp_output_wakeup(struct ppp_channel *chan) @@ -1174,11 +1704,75 @@ ppp_output_wakeup(struct ppp_channel *chan) if (pch == 0) return; - ppp = pch->ppp; - pch->blocked = 0; - set_bit(XMIT_WAKEUP, &ppp->busy); - if (trylock_xmit_path(ppp)) - ppp_xmit_unlock(ppp, 1); + ppp_channel_push(pch); + if (skb_queue_len(&pch->file.xq) == 0) { + read_lock_bh(&pch->upl); + ppp = pch->ppp; + if (ppp != 0) + ppp_xmit_process(ppp, 1); + read_unlock_bh(&pch->upl); + } +} + +/* + * This is basically temporary compatibility stuff. + */ +ssize_t +ppp_channel_read(struct ppp_channel *chan, struct file *file, + char *buf, size_t count) +{ + struct channel *pch = chan->ppp; + + if (pch == 0) + return -ENXIO; + return ppp_file_read(&pch->file, file, buf, count); +} + +ssize_t +ppp_channel_write(struct ppp_channel *chan, const char *buf, size_t count) +{ + struct channel *pch = chan->ppp; + + if (pch == 0) + return -ENXIO; + return ppp_file_write(&pch->file, buf, count); +} + +unsigned int +ppp_channel_poll(struct ppp_channel *chan, struct file *file, poll_table *wait) +{ + unsigned int mask; + struct channel *pch = chan->ppp; + + mask = POLLOUT | POLLWRNORM; + if (pch != 0) { + poll_wait(file, &pch->file.rwait, wait); + if (skb_peek(&pch->file.rq) != 0) + mask |= POLLIN | POLLRDNORM; + } + return mask; +} + +int ppp_channel_ioctl(struct ppp_channel *chan, unsigned int cmd, + unsigned long arg) +{ + struct channel *pch = chan->ppp; + int err = -ENOTTY; + int unit; + + if (pch == 0) + return -EINVAL; + switch (cmd) { + case PPPIOCATTACH: + if (get_user(unit, (int *) arg)) + break; + err = ppp_connect_channel(pch, unit); + break; + case PPPIOCDETACH: + err = ppp_disconnect_channel(pch); + break; + } + return err; } /* @@ -1192,6 +1786,7 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg) int err; struct compressor *cp; struct ppp_option_data data; + void *state; unsigned char ccp_option[CCP_MAX_OPTION_LENGTH]; #ifdef CONFIG_KMOD char modname[32]; @@ -1220,34 +1815,41 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg) err = -ENOBUFS; if (data.transmit) { - lock_xmit_path(ppp); + ppp_xmit_lock(ppp); ppp->xstate &= ~SC_COMP_RUN; if (ppp->xc_state != 0) { ppp->xcomp->comp_free(ppp->xc_state); ppp->xc_state = 0; } - - ppp->xcomp = cp; - ppp->xc_state = cp->comp_alloc(ccp_option, data.length); - ppp_xmit_unlock(ppp, 1); - if (ppp->xc_state == 0) - goto out; + ppp_xmit_unlock(ppp); + + state = cp->comp_alloc(ccp_option, data.length); + if (state != 0) { + ppp_xmit_lock(ppp); + ppp->xcomp = cp; + ppp->xc_state = state; + ppp_xmit_unlock(ppp); + err = 0; + } } else { - lock_recv_path(ppp); + ppp_recv_lock(ppp); ppp->rstate &= ~SC_DECOMP_RUN; if (ppp->rc_state != 0) { ppp->rcomp->decomp_free(ppp->rc_state); ppp->rc_state = 0; } - - ppp->rcomp = cp; - ppp->rc_state = cp->decomp_alloc(ccp_option, data.length); ppp_recv_unlock(ppp); - if (ppp->rc_state == 0) - goto out; + + state = cp->decomp_alloc(ccp_option, data.length); + if (state != 0) { + ppp_recv_lock(ppp); + ppp->rcomp = cp; + ppp->rc_state = state; + ppp_recv_unlock(ppp); + err = 0; + } } - err = 0; out: return err; @@ -1292,7 +1894,7 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound) if (ppp->rc_state == 0) break; if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len, - ppp->index, 0, ppp->mru, ppp->debug)) { + ppp->file.index, 0, ppp->mru, ppp->debug)) { ppp->rstate |= SC_DECOMP_RUN; ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR); } @@ -1301,7 +1903,7 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound) if (ppp->xc_state == 0) break; if (ppp->xcomp->comp_init(ppp->xc_state, dp, len, - ppp->index, 0, ppp->debug)) + ppp->file.index, 0, ppp->debug)) ppp->xstate |= SC_COMP_RUN; } break; @@ -1329,21 +1931,17 @@ ppp_ccp_closed(struct ppp *ppp) { ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP); - lock_xmit_path(ppp); ppp->xstate &= ~SC_COMP_RUN; if (ppp->xc_state) { ppp->xcomp->comp_free(ppp->xc_state); ppp->xc_state = 0; } - ppp_xmit_unlock(ppp, 1); - lock_recv_path(ppp); ppp->xstate &= ~SC_DECOMP_RUN; if (ppp->rc_state) { ppp->rcomp->decomp_free(ppp->rc_state); ppp->rc_state = 0; } - ppp_recv_unlock(ppp); } /* List of compressors. */ @@ -1451,16 +2049,17 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st) } /* - * Stuff for handling the list of ppp units and for initialization. + * Stuff for handling the lists of ppp units and channels + * and for initialization. */ /* - * Create a new ppp unit. Fails if it can't allocate memory or - * if there is already a unit with the requested number. + * Create a new ppp interface unit. Fails if it can't allocate memory + * or if there is already a unit with the requested number. * unit == -1 means allocate a new number. */ static struct ppp * -ppp_create_unit(int unit, int *retp) +ppp_create_interface(int unit, int *retp) { struct ppp *ppp; struct net_device *dev; @@ -1472,13 +2071,13 @@ ppp_create_unit(int unit, int *retp) spin_lock(&all_ppp_lock); list = &all_ppp_units; while ((list = list->next) != &all_ppp_units) { - ppp = list_entry(list, struct ppp, list); - if ((unit < 0 && ppp->index > last_unit + 1) - || (unit >= 0 && unit < ppp->index)) + ppp = list_entry(list, struct ppp, file.list); + if ((unit < 0 && ppp->file.index > last_unit + 1) + || (unit >= 0 && unit < ppp->file.index)) break; - if (unit == ppp->index) + if (unit == ppp->file.index) goto out; /* unit already exists */ - last_unit = ppp->index; + last_unit = ppp->file.index; } if (unit < 0) unit = last_unit + 1; @@ -1496,17 +2095,15 @@ ppp_create_unit(int unit, int *retp) } memset(dev, 0, sizeof(struct net_device)); - ppp->index = unit; + ppp->file.index = unit; sprintf(ppp->name, "ppp%d", unit); ppp->mru = PPP_MRU; - skb_queue_head_init(&ppp->xq); - skb_queue_head_init(&ppp->rq); - init_waitqueue_head(&ppp->rwait); - ppp->refcnt = 1; + init_ppp_file(&ppp->file, INTERFACE); for (i = 0; i < NUM_NP; ++i) ppp->npmode[i] = NPMODE_PASS; INIT_LIST_HEAD(&ppp->channels); - skb_queue_head_init(&ppp->recv_pending); + spin_lock_init(&ppp->rlock); + spin_lock_init(&ppp->wlock); ppp->dev = dev; dev->init = ppp_net_init; @@ -1524,7 +2121,7 @@ ppp_create_unit(int unit, int *retp) goto out; } - list_add(&ppp->list, list->prev); + list_add(&ppp->file.list, list->prev); out: spin_unlock(&all_ppp_lock); *retp = ret; @@ -1534,56 +2131,59 @@ ppp_create_unit(int unit, int *retp) } /* - * Remove a reference to a ppp unit, and destroy it if - * the reference count goes to 0. + * Initialize a ppp_file structure. */ -static void ppp_release_unit(struct ppp *ppp) +static void +init_ppp_file(struct ppp_file *pf, int kind) { - struct list_head *list, *next; - int ref; + pf->kind = kind; + skb_queue_head_init(&pf->xq); + skb_queue_head_init(&pf->rq); + atomic_set(&pf->refcnt, 1); + init_waitqueue_head(&pf->rwait); +} + +/* + * Free up all the resources used by a ppp interface unit. + */ +static void ppp_destroy_interface(struct ppp *ppp) +{ + struct net_device *dev; spin_lock(&all_ppp_lock); - ref = --ppp->refcnt; - if (ref == 0) - list_del(&ppp->list); - spin_unlock(&all_ppp_lock); - if (ref != 0) - return; + list_del(&ppp->file.list); /* Last fd open to this ppp unit is being closed or detached: mark the interface down, free the ppp unit */ - if (ppp->dev) { - rtnl_lock(); - dev_close(ppp->dev); - rtnl_unlock(); - } - for (list = ppp->channels.next; list != &ppp->channels; list = next) { - /* forcibly detach this channel */ - struct channel *chan; - chan = list_entry(list, struct channel, list); - chan->chan->ppp = 0; - next = list->next; - kfree(chan); - } - - /* Free up resources. */ + ppp_lock(ppp); ppp_ccp_closed(ppp); - lock_xmit_path(ppp); - lock_recv_path(ppp); if (ppp->vj) { slhc_free(ppp->vj); ppp->vj = 0; } - free_skbs(&ppp->xq); - free_skbs(&ppp->rq); - free_skbs(&ppp->recv_pending); - if (ppp->dev) { + skb_queue_purge(&ppp->file.xq); + skb_queue_purge(&ppp->file.rq); + dev = ppp->dev; + ppp->dev = 0; + ppp_unlock(ppp); + + if (dev) { rtnl_lock(); - unregister_netdevice(ppp->dev); - ppp->dev = 0; + dev_close(dev); + unregister_netdevice(dev); rtnl_unlock(); } - kfree(ppp); + + /* + * We can't acquire any new channels (since we have the + * all_ppp_lock) so if n_channels is 0, we can free the + * ppp structure. Otherwise we leave it around until the + * last channel disconnects from it. + */ + if (ppp->n_channels == 0) + kfree(ppp); + + spin_unlock(&all_ppp_lock); } /* @@ -1598,32 +2198,136 @@ ppp_find_unit(int unit) list = &all_ppp_units; while ((list = list->next) != &all_ppp_units) { - ppp = list_entry(list, struct ppp, list); - if (ppp->index == unit) + ppp = list_entry(list, struct ppp, file.list); + if (ppp->file.index == unit) return ppp; } return 0; } /* - * Module stuff. + * Locate an existing ppp channel. + * The caller should have locked the all_channels_lock. */ -#ifdef MODULE -int -init_module(void) +static struct channel * +ppp_find_channel(int unit) { - ppp_init(); + struct channel *pch; + struct list_head *list; + + list = &all_channels; + while ((list = list->next) != &all_channels) { + pch = list_entry(list, struct channel, file.list); + if (pch->file.index == unit) + return pch; + } return 0; } -void -cleanup_module(void) +/* + * Connect a PPP channel to a PPP interface unit. + */ +static int +ppp_connect_channel(struct channel *pch, int unit) +{ + struct ppp *ppp; + int ret = -ENXIO; + int hdrlen; + + spin_lock(&all_ppp_lock); + ppp = ppp_find_unit(unit); + if (ppp == 0) + goto out; + write_lock_bh(&pch->upl); + ret = -EINVAL; + if (pch->ppp != 0) + goto outw; + ppp_lock(ppp); + spin_lock_bh(&pch->downl); + if (pch->chan == 0) /* need to check this?? */ + goto outr; + + hdrlen = pch->chan->hdrlen + PPP_HDRLEN; + if (ppp->dev && hdrlen > ppp->dev->hard_header_len) + ppp->dev->hard_header_len = hdrlen; + list_add(&pch->clist, &ppp->channels); + ++ppp->n_channels; + pch->ppp = ppp; + ret = 0; + + outr: + spin_unlock_bh(&pch->downl); + ppp_unlock(ppp); + outw: + write_unlock_bh(&pch->upl); + out: + spin_unlock(&all_ppp_lock); + return ret; +} + +/* + * Disconnect a channel from its ppp unit. + */ +static int +ppp_disconnect_channel(struct channel *pch) +{ + struct ppp *ppp; + int err = -EINVAL; + + write_lock_bh(&pch->upl); + ppp = pch->ppp; + if (ppp != 0) { + /* remove it from the ppp unit's list */ + pch->ppp = NULL; + ppp_lock(ppp); + list_del(&pch->clist); + --ppp->n_channels; + if (ppp->dev == 0 && ppp->n_channels == 0) + /* Last disconnect from a ppp unit + that is already dead: free it. */ + kfree(ppp); + else + ppp_unlock(ppp); + err = 0; + } + write_unlock_bh(&pch->upl); + return err; +} + +/* + * Free up the resources used by a ppp channel. + */ +static void ppp_destroy_channel(struct channel *pch) +{ + skb_queue_purge(&pch->file.xq); + skb_queue_purge(&pch->file.rq); + kfree(pch); +} + +void __exit ppp_cleanup(void) { /* should never happen */ - if (!list_empty(&all_ppp_units)) + if (!list_empty(&all_ppp_units) || !list_empty(&all_channels)) printk(KERN_ERR "PPP: removing module but units remain!\n"); if (devfs_unregister_chrdev(PPP_MAJOR, "ppp") != 0) printk(KERN_ERR "PPP: failed to unregister PPP device\n"); - devfs_unregister (devfs_handle); + devfs_unregister(devfs_handle); } -#endif /* MODULE */ + +module_init(ppp_init); +module_exit(ppp_cleanup); + +EXPORT_SYMBOL(ppp_register_channel); +EXPORT_SYMBOL(ppp_unregister_channel); +EXPORT_SYMBOL(ppp_channel_index); +EXPORT_SYMBOL(ppp_input); +EXPORT_SYMBOL(ppp_input_error); +EXPORT_SYMBOL(ppp_output_wakeup); +EXPORT_SYMBOL(ppp_register_compressor); +EXPORT_SYMBOL(ppp_unregister_compressor); +EXPORT_SYMBOL(ppp_channel_read); +EXPORT_SYMBOL(ppp_channel_write); +EXPORT_SYMBOL(ppp_channel_poll); +EXPORT_SYMBOL(ppp_channel_ioctl); +EXPORT_SYMBOL(all_ppp_units); /* for debugging */ +EXPORT_SYMBOL(all_channels); /* for debugging */ diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index 1a69f6ede..8bae76e1c 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -439,7 +439,7 @@ ppp_sync_ioctl(struct tty_struct *tty, struct file *file, break; ap->chan.private = ap; ap->chan.ops = &sync_ops; - err = ppp_register_channel(&ap->chan, val); + err = ppp_register_channel(&ap->chan); if (err != 0) break; ap->connected = 1; diff --git a/drivers/net/rcpci45.c b/drivers/net/rcpci45.c index 27b0c6474..e19f1e0fd 100644 --- a/drivers/net/rcpci45.c +++ b/drivers/net/rcpci45.c @@ -1034,6 +1034,9 @@ static int RCioctl(struct net_device *dev, struct ifreq *rq, int cmd) printk("RCioctl: cmd = 0x%x\n", cmd); #endif + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + switch (cmd) { case RCU_PROTOCOL_REV: @@ -1157,14 +1160,14 @@ static int RCioctl(struct net_device *dev, struct ifreq *rq, int cmd) RCUD_DEFAULT -> rc = 0x11223344; break; } - copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser)); + if(copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser))) + return -EFAULT; break; - } /* RCU_COMMAND */ + } /* RCU_COMMAND */ - default: - printk("RC default\n"); - rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678; - break; + default: + rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678; + return -EINVAL; } return 0; } diff --git a/drivers/net/rrunner.c b/drivers/net/rrunner.c index a3a0018cb..8e8236e92 100644 --- a/drivers/net/rrunner.c +++ b/drivers/net/rrunner.c @@ -76,8 +76,8 @@ static inline void netif_start_queue(struct net_device *dev) #else #define NET_BH 0 #define rr_mark_net_bh(foo) {do{} while(0);} -#define rr_if_busy(dev) test_bit(LINK_STATE_XOFF, &dev->state) -#define rr_if_running(dev) test_bit(LINK_STATE_START, &dev->state) +#define rr_if_busy(dev) netif_queue_stopped(dev) +#define rr_if_running(dev) netif_running(dev) #define rr_if_down(dev) {do{} while(0);} #endif @@ -1550,7 +1550,7 @@ static int rr_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) switch(cmd){ case SIOCRRGFW: - if (!suser()){ + if (!capable(CAP_SYS_RAWIO)){ error = -EPERM; goto out; } @@ -1582,7 +1582,7 @@ static int rr_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) kfree(image); break; case SIOCRRPFW: - if (!suser()){ + if (!capable(CAP_SYS_RAWIO)){ error = -EPERM; goto out; } diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c index 9b17cc79d..bcf6e7e5b 100644 --- a/drivers/net/sb1000.c +++ b/drivers/net/sb1000.c @@ -1052,7 +1052,7 @@ static int sb1000_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; case SIOCSCMFREQUENCY: /* set frequency */ - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EPERM; if(get_user(frequency, (int*) ifr->ifr_data)) return -EFAULT; @@ -1068,7 +1068,7 @@ static int sb1000_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; case SIOCSCMPIDS: /* set PIDs */ - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EPERM; if(copy_from_user(PID, ifr->ifr_data, sizeof(PID))) return -EFAULT; diff --git a/drivers/net/setup.c b/drivers/net/setup.c index d9019b046..f8f59b9fb 100644 --- a/drivers/net/setup.c +++ b/drivers/net/setup.c @@ -28,6 +28,7 @@ extern int dlci_setup(void); extern int lapbeth_init(void); extern int sdla_setup(void); extern int sdla_c_setup(void); +extern int comx_init(void); extern int abyss_probe(void); extern int madgemc_probe(void); @@ -75,7 +76,9 @@ struct net_probe pci_probes[] __initdata = { #if defined(CONFIG_8xx) {cpm_enet_init, 0}, #endif - /* +#if defined(CONFIG_COMX) + {comx_init(), 0}, +#endif /* * SLHC if present needs attaching so other people see it * even if not opened. */ diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c index 89cbb0e9e..9190b5a87 100644 --- a/drivers/net/shaper.c +++ b/drivers/net/shaper.c @@ -480,6 +480,8 @@ static void shaper_cache_update(struct hh_cache *hh, struct net_device *dev, } #endif +#ifdef CONFIG_INET + static int shaper_neigh_setup(struct neighbour *n) { if (n->nud_state == NUD_NONE) { @@ -499,6 +501,15 @@ static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p) return 0; } +#else /* !(CONFIG_INET) */ + +static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p) +{ + return 0; +} + +#endif + static int shaper_attach(struct net_device *shdev, struct shaper *sh, struct net_device *dev) { sh->dev = dev; diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index f8af277ea..2653270c3 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -1127,7 +1127,7 @@ static int mii_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd) data[3] = mdio_read(net_dev, data[0] & 0x1f, data[1] & 0x1f); return 0; case SIOCDEVPRIVATE+2: /* Write the specified MII register */ - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EPERM; mdio_write(net_dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); return 0; diff --git a/drivers/net/skfp/drvfbi.c b/drivers/net/skfp/drvfbi.c index e1e48bc91..93533c5ef 100644 --- a/drivers/net/skfp/drvfbi.c +++ b/drivers/net/skfp/drvfbi.c @@ -129,7 +129,7 @@ extern int AIX_vpdReadByte() ; /* * FDDI card reset */ -void card_start(smc) +static void card_start(smc) struct s_smc *smc ; { int i ; diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c index 6aceec4d5..b6b112318 100644 --- a/drivers/net/skfp/skfddi.c +++ b/drivers/net/skfp/skfddi.c @@ -1249,7 +1249,7 @@ static int skfp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) copy_to_user(ioc.data, skfp_ctl_get_stats(dev), ioc.len); break; case SKFP_CLR_STATS: /* Zero out the driver statistics */ - if (suser()) { + if (!capable(CAP_NET_ADMIN)) { memset(&lp->MacStat, 0, sizeof(lp->MacStat)); } else { status = -EPERM; diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index 344e682f8..059d4a4ea 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -1,4 +1,4 @@ -/* $Id: sunhme.c,v 1.92 2000/02/18 13:49:22 davem Exp $ +/* $Id: sunhme.c,v 1.93 2000/03/12 04:02:14 davem Exp $ * sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching, * auto carrier detecting ethernet driver. Also known as the * "Happy Meal Ethernet" found on SunSwift SBUS cards. @@ -254,7 +254,7 @@ do { (__txd)->tx_addr = (__addr); \ #define hme_read_desc32(__hp, __p) (*(__p)) #define hme_dma_map(__hp, __ptr, __size, __dir) \ sbus_map_single((__hp)->happy_dev, (__ptr), (__size), (__dir)) -#define hme_dma_unmap(__hp, __addr, __size) \ +#define hme_dma_unmap(__hp, __addr, __size, __dir) \ sbus_unmap_single((__hp)->happy_dev, (__addr), (__size), (__dir)) #define hme_dma_sync(__hp, __addr, __size, __dir) \ sbus_dma_sync_single((__hp)->happy_dev, (__addr), (__size), (__dir)) diff --git a/drivers/net/tokenring/Config.in b/drivers/net/tokenring/Config.in index 31688f34d..316b638c2 100644 --- a/drivers/net/tokenring/Config.in +++ b/drivers/net/tokenring/Config.in @@ -9,6 +9,7 @@ bool 'Token Ring driver support' CONFIG_TR if [ "$CONFIG_TR" != "n" ]; then dep_tristate ' IBM Tropic chipset based adapter support' CONFIG_IBMTR $CONFIG_TR dep_tristate ' IBM Olympic chipset PCI adapter support' CONFIG_IBMOL $CONFIG_TR + dep_tristate ' IBM Lanstreamer chipset PCI adapter support' CONFIG_IBMLS $CONFIG_TR dep_tristate ' Generic TMS380 Token Ring ISA/PCI adapter support' CONFIG_TMS380TR $CONFIG_TR if [ "$CONFIG_TMS380TR" != "n" ]; then dep_tristate ' Generic TMS380 PCI support' CONFIG_TMSPCI $CONFIG_TMS380TR diff --git a/drivers/net/tokenring/Makefile b/drivers/net/tokenring/Makefile index f90055b45..1fa4547c4 100644 --- a/drivers/net/tokenring/Makefile +++ b/drivers/net/tokenring/Makefile @@ -18,6 +18,7 @@ export-objs := tms380tr.o obj-$(CONFIG_IBMTR) += ibmtr.o obj-$(CONFIG_IBMOL) += olympic.o +obj-$(CONFIG_IBMLS) += lanstreamer.o obj-$(CONFIG_TMS380TR) += tms380tr.o obj-$(CONFIG_ABYSS) += abyss.o obj-$(CONFIG_MADGEMC) += madgemc.o diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c new file mode 100644 index 000000000..e0a339bfa --- /dev/null +++ b/drivers/net/tokenring/lanstreamer.c @@ -0,0 +1,1776 @@ +/* + * lanstreamer.c -- driver for the IBM Auto LANStreamer PCI Adapter + * + * Written By: Mike Sullivan, IBM Corporation + * + * Copyright (C) 1999 IBM Corporation + * + * Linux driver for IBM PCI tokenring cards based on the LanStreamer MPC + * chipset. + * + * This driver is based on the olympic driver for IBM PCI TokenRing cards (Pit/Pit-Phy/Olympic + * chipsets) written by: + * 1999 Peter De Schrijver All Rights Reserved + * 1999 Mike Phillips (phillim@amtrak.com) + * + * Base Driver Skeleton: + * Written 1993-94 by Donald Becker. + * + * Copyright 1993 United States Government as represented by the + * Director, National Security Agency. + * + * 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. + * + * NO WARRANTY + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + * solely responsible for determining the appropriateness of using and + * distributing the Program and assumes all risks associated with its + * exercise of rights under this Agreement, including but not limited to + * the risks and costs of program errors, damage to or loss of data, + * programs or equipment, and unavailability or interruption of operations. + * + * DISCLAIMER OF LIABILITY + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * 12/10/99 - Alpha Release 0.1.0 + * First release to the public + * 03/03/00 - Merged to kernel, indented -kr -i8 -bri0, fixed some missing + * malloc free checks, reviewed code. <alan@redhat.com> + * + * To Do: + * + * 1) Test Network Monitor Mode + * 2) Add auto reset logic on adapter errors + * 3) Test with varying options + * + * If Problems do Occur + * Most problems can be rectified by either closing and opening the interface + * (ifconfig down and up) or rmmod and insmod'ing the driver (a bit difficult + * if compiled into the kernel). + */ + +/* Change STREAMER_DEBUG to 1 to get verbose, and I mean really verbose, messages */ + +#define STREAMER_DEBUG 0 +#define STREAMER_DEBUG_PACKETS 0 + +/* Change STREAMER_NETWORK_MONITOR to receive mac frames through the arb channel. + * Will also create a /proc/net/streamer_tr entry if proc_fs is compiled into the + * kernel. + * Intended to be used to create a ring-error reporting network module + * i.e. it will give you the source address of beaconers on the ring + */ + +#define STREAMER_NETWORK_MONITOR 0 + +#include <linux/config.h> +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/timer.h> +#include <linux/in.h> +#include <linux/ioport.h> +#include <linux/string.h> +#include <linux/proc_fs.h> +#include <linux/ptrace.h> +#include <linux/skbuff.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/trdevice.h> +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <net/checksum.h> + +#include <asm/io.h> +#include <asm/system.h> +#include <asm/bitops.h> + +#include "lanstreamer.h" + +/* I've got to put some intelligence into the version number so that Peter and I know + * which version of the code somebody has got. + * Version Number = a.b.c.d where a.b.c is the level of code and d is the latest author. + * So 0.0.1.pds = Peter, 0.0.1.mlp = Mike + * + * Official releases will only have an a.b.c version number format. + */ + +static char *version = "LanStreamer.c v0.1.0 12/10/99 - Mike Sullivan"; + +static char *open_maj_error[] = { + "No error", "Lobe Media Test", "Physical Insertion", + "Address Verification", "Neighbor Notification (Ring Poll)", + "Request Parameters", "FDX Registration Request", + "FDX Lobe Media Test", "FDX Duplicate Address Check", + "Unknown stage" +}; + +static char *open_min_error[] = { + "No error", "Function Failure", "Signal Lost", "Wire Fault", + "Ring Speed Mismatch", "Timeout", "Ring Failure", "Ring Beaconing", + "Duplicate Node Address", "Request Parameters", "Remove Received", + "Reserved", "Reserved", "No Monitor Detected for RPL", + "Monitor Contention failer for RPL", "FDX Protocol Error" +}; + +/* Module paramters */ + +/* Ring Speed 0,4,16 + * 0 = Autosense + * 4,16 = Selected speed only, no autosense + * This allows the card to be the first on the ring + * and become the active monitor. + * + * WARNING: Some hubs will allow you to insert + * at the wrong speed + */ + +static int ringspeed[STREAMER_MAX_ADAPTERS] = { 0, }; + +MODULE_PARM(ringspeed, "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i"); + +/* Packet buffer size */ + +static int pkt_buf_sz[STREAMER_MAX_ADAPTERS] = { 0, }; + +MODULE_PARM(pkt_buf_sz, "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i"); + +/* Message Level */ + +static int message_level[STREAMER_MAX_ADAPTERS] = { 1, }; + +MODULE_PARM(message_level, + "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i"); + +static int streamer_scan(struct net_device *dev); +static int streamer_init(struct net_device *dev); +static int streamer_open(struct net_device *dev); +static int streamer_xmit(struct sk_buff *skb, struct net_device *dev); +static int streamer_close(struct net_device *dev); +static void streamer_set_rx_mode(struct net_device *dev); +static void streamer_interrupt(int irq, void *dev_id, + struct pt_regs *regs); +static struct net_device_stats *streamer_get_stats(struct net_device *dev); +static int streamer_set_mac_address(struct net_device *dev, void *addr); +static void streamer_arb_cmd(struct net_device *dev); +static int streamer_change_mtu(struct net_device *dev, int mtu); +static void streamer_srb_bh(struct net_device *dev); +static void streamer_asb_bh(struct net_device *dev); +#if STREAMER_NETWORK_MONITOR +#ifdef CONFIG_PROC_FS +static int sprintf_info(char *buffer, struct net_device *dev); +#endif +#endif + +int __init streamer_probe(struct net_device *dev) +{ + int cards_found; + + cards_found = streamer_scan(dev); + return cards_found ? 0 : -ENODEV; +} + +static int __init streamer_scan(struct net_device *dev) +{ + struct pci_dev *pci_device = NULL; + struct streamer_private *streamer_priv; + int card_no = 0; + if (pci_present()) + { + while ((pci_device = pci_find_device(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR, pci_device))) + { + pci_set_master(pci_device); + + /* Check to see if io has been allocated, if so, we've already done this card, + so continue on the card discovery loop */ + + if (check_region(pci_device->resource[0].start, STREAMER_IO_SPACE)) + { + card_no++; + continue; + } + + streamer_priv = kmalloc(sizeof(struct streamer_private), GFP_KERNEL); + if(streamer_priv==NULL) + { + printk(KERN_ERR "lanstreamer: out of memory.\n"); + break; + } + memset(streamer_priv, 0, sizeof(struct streamer_private)); +#ifndef MODULE + dev = init_trdev(dev, 0); + if(dev==NULL) + { + kfree(streamer_priv); + printk(KERN_ERR "lanstreamer: out of memory.\n"); + break; + } +#endif + dev->priv = (void *) streamer_priv; +#if STREAMER_DEBUG + printk("pci_device: %p, dev:%p, dev->priv: %p\n", + pci_device, dev, dev->priv); +#endif + dev->irq = pci_device->irq; + dev->base_addr = pci_device->resource[0].start; + dev->init = &streamer_init; + streamer_priv->streamer_mmio = ioremap(pci_device->resource[1].start, 256); + init_waitqueue_head(&streamer_priv->srb_wait); + init_waitqueue_head(&streamer_priv->trb_wait); + if ((pkt_buf_sz[card_no] < 100) || (pkt_buf_sz[card_no] > 18000)) + streamer_priv->pkt_buf_sz = PKT_BUF_SZ; + else + streamer_priv->pkt_buf_sz = pkt_buf_sz[card_no]; + + streamer_priv->streamer_ring_speed = ringspeed[card_no]; + streamer_priv->streamer_message_level = message_level[card_no]; + streamer_priv->streamer_multicast_set = 0; + + if (streamer_init(dev) == -1) { + unregister_netdevice(dev); + kfree(dev->priv); + return 0; + } + + dev->open = &streamer_open; + dev->hard_start_xmit = &streamer_xmit; + dev->change_mtu = &streamer_change_mtu; + + dev->stop = &streamer_close; + dev->do_ioctl = NULL; + dev->set_multicast_list = &streamer_set_rx_mode; + dev->get_stats = &streamer_get_stats; + dev->set_mac_address = &streamer_set_mac_address; + return 1; + } + } + return 0; +} + + +static int __init streamer_init(struct net_device *dev) +{ + struct streamer_private *streamer_priv; + __u8 *streamer_mmio; + unsigned long t; + unsigned int uaa_addr; + struct sk_buff *skb = 0; + __u16 misr; + + streamer_priv = (struct streamer_private *) dev->priv; + streamer_mmio = streamer_priv->streamer_mmio; + + printk("%s \n", version); + printk(KERN_INFO "%s: IBM PCI tokenring card. I/O at %hx, MMIO at %p, using irq %d\n", + dev->name, (unsigned int) dev->base_addr, + streamer_priv->streamer_mmio, dev->irq); + + request_region(dev->base_addr, STREAMER_IO_SPACE, "streamer"); + writew(readw(streamer_mmio + BCTL) | BCTL_SOFTRESET, streamer_mmio + BCTL); + t = jiffies; + /* Hold soft reset bit for a while */ + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ); + + writew(readw(streamer_mmio + BCTL) & ~BCTL_SOFTRESET, + streamer_mmio + BCTL); + +#if STREAMER_DEBUG + printk("BCTL: %x\n", readw(streamer_mmio + BCTL)); + printk("GPR: %x\n", readw(streamer_mmio + GPR)); + printk("SISRMASK: %x\n", readw(streamer_mmio + SISR_MASK)); +#endif + + if (streamer_priv->streamer_ring_speed == 0) { /* Autosense */ + writew(readw(streamer_mmio + GPR) | GPR_AUTOSENSE, + streamer_mmio + GPR); + if (streamer_priv->streamer_message_level) + printk(KERN_INFO "%s: Ringspeed autosense mode on\n", + dev->name); + } else if (streamer_priv->streamer_ring_speed == 16) { + if (streamer_priv->streamer_message_level) + printk(KERN_INFO "%s: Trying to open at 16 Mbps as requested\n", + dev->name); + writew(GPR_16MBPS, streamer_mmio + GPR); + } else if (streamer_priv->streamer_ring_speed == 4) { + if (streamer_priv->streamer_message_level) + printk(KERN_INFO "%s: Trying to open at 4 Mbps as requested\n", + dev->name); + writew(0, streamer_mmio + GPR); + } + + skb = dev_alloc_skb(streamer_priv->pkt_buf_sz); + if (!skb) { + printk(KERN_INFO "%s: skb allocation for diagnostics failed...proceeding\n", + dev->name); + } else { + streamer_priv->streamer_rx_ring[0].forward = 0; + streamer_priv->streamer_rx_ring[0].status = 0; + streamer_priv->streamer_rx_ring[0].buffer = virt_to_bus(skb->data); + streamer_priv->streamer_rx_ring[0].framelen_buflen = 512; /* streamer_priv->pkt_buf_sz; */ + writel(virt_to_bus(&streamer_priv->streamer_rx_ring[0]), streamer_mmio + RXBDA); + } + +#if STREAMER_DEBUG + printk("GPR = %x\n", readw(streamer_mmio + GPR)); +#endif + /* start solo init */ + writew(SISR_MI, streamer_mmio + SISR_MASK_SUM); + + while (!((readw(streamer_mmio + SISR)) & SISR_SRB_REPLY)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); + if (jiffies - t > 40 * HZ) { + printk(KERN_ERR + "IBM PCI tokenring card not responding\n"); + release_region(dev->base_addr, STREAMER_IO_SPACE); + return -1; + } + } + writew(~SISR_SRB_REPLY, streamer_mmio + SISR_RUM); + misr = readw(streamer_mmio + MISR_RUM); + writew(~misr, streamer_mmio + MISR_RUM); + + if (skb) + dev_kfree_skb(skb); /* release skb used for diagnostics */ + +#if STREAMER_DEBUG + printk("LAPWWO: %x, LAPA: %x LAPE: %x\n", + readw(streamer_mmio + LAPWWO), readw(streamer_mmio + LAPA), + readw(streamer_mmio + LAPE)); +#endif + +#if STREAMER_DEBUG + { + int i; + writew(readw(streamer_mmio + LAPWWO), + streamer_mmio + LAPA); + printk("initialization response srb dump: "); + for (i = 0; i < 10; i++) + printk("%x:", + ntohs(readw(streamer_mmio + LAPDINC))); + printk("\n"); + } +#endif + + writew(readw(streamer_mmio + LAPWWO) + 6, streamer_mmio + LAPA); + if (readw(streamer_mmio + LAPD)) { + printk(KERN_INFO "tokenring card intialization failed. errorcode : %x\n", + readw(streamer_mmio + LAPD)); + release_region(dev->base_addr, STREAMER_IO_SPACE); + return -1; + } + + writew(readw(streamer_mmio + LAPWWO) + 8, streamer_mmio + LAPA); + uaa_addr = ntohs(readw(streamer_mmio + LAPDINC)); + readw(streamer_mmio + LAPDINC); /* skip over Level.Addr field */ + streamer_priv->streamer_addr_table_addr = ntohs(readw(streamer_mmio + LAPDINC)); + streamer_priv->streamer_parms_addr = ntohs(readw(streamer_mmio + LAPDINC)); + +#if STREAMER_DEBUG + printk("UAA resides at %x\n", uaa_addr); +#endif + + /* setup uaa area for access with LAPD */ + writew(uaa_addr, streamer_mmio + LAPA); + + /* setup uaa area for access with LAPD */ + { + int i; + __u16 addr; + writew(uaa_addr, streamer_mmio + LAPA); + for (i = 0; i < 6; i += 2) { + addr = readw(streamer_mmio + LAPDINC); + dev->dev_addr[i] = addr & 0xff; + dev->dev_addr[i + 1] = (addr >> 8) & 0xff; + } +#if STREAMER_DEBUG + printk("Adapter address: "); + for (i = 0; i < 6; i++) { + printk("%02x:", dev->dev_addr[i]); + } + printk("\n"); +#endif + } + return 0; +} + +static int streamer_open(struct net_device *dev) +{ + struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv; + __u8 *streamer_mmio = streamer_priv->streamer_mmio; + unsigned long flags; + char open_error[255]; + int i, open_finished = 1; + __u16 srb_word; + __u16 srb_open; + + + if (request_irq(dev->irq, &streamer_interrupt, SA_SHIRQ, "streamer", dev)) { + return -EAGAIN; + } +#if STREAMER_DEBUG + printk("BMCTL: %x\n", readw(streamer_mmio + BMCTL_SUM)); + printk("pending ints: %x\n", readw(streamer_mmio + SISR)); +#endif + + writew(SISR_MI | SISR_SRB_REPLY, streamer_mmio + SISR_MASK); /* more ints later, doesn't stop arb cmd interrupt */ + writew(LISR_LIE, streamer_mmio + LISR); /* more ints later */ + + /* adapter is closed, so SRB is pointed to by LAPWWO */ + writew(readw(streamer_mmio + LAPWWO), streamer_mmio + LAPA); + +#if STREAMER_DEBUG + printk("LAPWWO: %x, LAPA: %x\n", readw(streamer_mmio + LAPWWO), + readw(streamer_mmio + LAPA)); + printk("LAPE: %x\n", readw(streamer_mmio + LAPE)); + printk("SISR Mask = %04x\n", readw(streamer_mmio + SISR_MASK)); +#endif + do { + int i; + + save_flags(flags); + cli(); + for (i = 0; i < SRB_COMMAND_SIZE; i += 2) { + writew(0, streamer_mmio + LAPDINC); + } + + writew(readw(streamer_mmio + LAPWWO), streamer_mmio + LAPA); + writew(SRB_OPEN_ADAPTER, streamer_mmio + LAPDINC); /* open */ + writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); + + writew(readw(streamer_mmio + LAPWWO) + 8, streamer_mmio + LAPA); +#if STREAMER_NETWORK_MONITOR + /* If Network Monitor, instruct card to copy MAC frames through the ARB */ + writew(ntohs(OPEN_ADAPTER_ENABLE_FDX | OPEN_ADAPTER_PASS_ADC_MAC | OPEN_ADAPTER_PASS_ATT_MAC | OPEN_ADAPTER_PASS_BEACON), streamer_mmio + LAPDINC); /* offset 8 word contains open options */ +#else + writew(ntohs(OPEN_ADAPTER_ENABLE_FDX), streamer_mmio + LAPDINC); /* Offset 8 word contains Open.Options */ +#endif + + if (streamer_priv->streamer_laa[0]) { + writew(readw(streamer_mmio + LAPWWO) + 12, streamer_mmio + LAPA); + writew(((__u16 *) (streamer_priv->streamer_laa))[0], streamer_mmio + LAPDINC); /* offset 12 word */ + writew(((__u16 *) (streamer_priv->streamer_laa))[2], streamer_mmio + LAPDINC); /* offset 14 word */ + writew(((__u16 *) (streamer_priv->streamer_laa))[4], streamer_mmio + LAPDINC); /* offset 16 word */ + memcpy(dev->dev_addr, streamer_priv->streamer_laa, dev->addr_len); + } + + /* save off srb open offset */ + srb_open = readw(streamer_mmio + LAPWWO); +#if STREAMER_DEBUG + writew(readw(streamer_mmio + LAPWWO), + streamer_mmio + LAPA); + printk("srb open request: \n"); + for (i = 0; i < 16; i++) { + printk("%x:", ntohs(readw(streamer_mmio + LAPDINC))); + } + printk("\n"); +#endif + + streamer_priv->srb_queued = 1; + + /* signal solo that SRB command has been issued */ + writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM); + + while (streamer_priv->srb_queued) { + interruptible_sleep_on_timeout(&streamer_priv->srb_wait, 5 * HZ); + if (signal_pending(current)) { + printk(KERN_WARNING "%s: SRB timed out.\n", dev->name); + printk(KERN_WARNING "SISR=%x MISR=%x, LISR=%x\n", + readw(streamer_mmio + SISR), + readw(streamer_mmio + MISR_RUM), + readw(streamer_mmio + LISR)); + streamer_priv->srb_queued = 0; + break; + } + } + restore_flags(flags); + +#if STREAMER_DEBUG + printk("SISR_MASK: %x\n", readw(streamer_mmio + SISR_MASK)); + printk("srb open response:\n"); + writew(srb_open, streamer_mmio + LAPA); + for (i = 0; i < 10; i++) { + printk("%x:", + ntohs(readw(streamer_mmio + LAPDINC))); + } +#endif + + /* If we get the same return response as we set, the interrupt wasn't raised and the open + * timed out. + */ + writew(srb_open + 2, streamer_mmio + LAPA); + srb_word = readw(streamer_mmio + LAPD) & 0xFF; + if (srb_word == STREAMER_CLEAR_RET_CODE) { + printk(KERN_WARNING "%s: Adapter Open time out or error.\n", + dev->name); + return -EIO; + } + + if (srb_word != 0) { + if (srb_word == 0x07) { + if (!streamer_priv->streamer_ring_speed && open_finished) { /* Autosense , first time around */ + printk(KERN_WARNING "%s: Retrying at different ring speed \n", + dev->name); + open_finished = 0; + } else { + __u16 error_code; + + writew(srb_open + 6, streamer_mmio + LAPA); + error_code = ntohs(readw(streamer_mmio + LAPD)); + strcpy(open_error, open_maj_error[(error_code & 0xf0) >> 4]); + strcat(open_error, " - "); + strcat(open_error, open_min_error[(error_code & 0x0f)]); + + if (!streamer_priv->streamer_ring_speed + && ((error_code & 0x0f) == 0x0d)) + { + printk(KERN_WARNING "%s: Tried to autosense ring speed with no monitors present\n", dev->name); + printk(KERN_WARNING "%s: Please try again with a specified ring speed \n", dev->name); + free_irq(dev->irq, dev); + return -EIO; + } + + printk(KERN_WARNING "%s: %s\n", + dev->name, open_error); + free_irq(dev->irq, dev); + return -EIO; + + } /* if autosense && open_finished */ + } else { + printk(KERN_WARNING "%s: Bad OPEN response: %x\n", + dev->name, srb_word); + free_irq(dev->irq, dev); + return -EIO; + } + } else + open_finished = 1; + } while (!(open_finished)); /* Will only loop if ring speed mismatch re-open attempted && autosense is on */ + + writew(srb_open + 18, streamer_mmio + LAPA); + srb_word = readw(streamer_mmio + LAPD) & 0xFF; + if (srb_word & (1 << 3)) + if (streamer_priv->streamer_message_level) + printk(KERN_INFO "%s: Opened in FDX Mode\n", dev->name); + + if (srb_word & 1) + streamer_priv->streamer_ring_speed = 16; + else + streamer_priv->streamer_ring_speed = 4; + + if (streamer_priv->streamer_message_level) + printk(KERN_INFO "%s: Opened in %d Mbps mode\n", + dev->name, + streamer_priv->streamer_ring_speed); + + writew(srb_open + 8, streamer_mmio + LAPA); + streamer_priv->asb = ntohs(readw(streamer_mmio + LAPDINC)); + streamer_priv->srb = ntohs(readw(streamer_mmio + LAPDINC)); + streamer_priv->arb = ntohs(readw(streamer_mmio + LAPDINC)); + readw(streamer_mmio + LAPDINC); /* offset 14 word is rsvd */ + streamer_priv->trb = ntohs(readw(streamer_mmio + LAPDINC)); + + streamer_priv->streamer_receive_options = 0x00; + streamer_priv->streamer_copy_all_options = 0; + + /* setup rx ring */ + /* enable rx channel */ + writew(~BMCTL_RX_DIS, streamer_mmio + BMCTL_RUM); + + /* setup rx descriptors */ + for (i = 0; i < STREAMER_RX_RING_SIZE; i++) { + struct sk_buff *skb; + + skb = dev_alloc_skb(streamer_priv->pkt_buf_sz); + if (skb == NULL) + break; + + skb->dev = dev; + + streamer_priv->streamer_rx_ring[i].forward = virt_to_bus(&streamer_priv->streamer_rx_ring[i + 1]); + streamer_priv->streamer_rx_ring[i].status = 0; + streamer_priv->streamer_rx_ring[i].buffer = virt_to_bus(skb->data); + streamer_priv->streamer_rx_ring[i].framelen_buflen = streamer_priv->pkt_buf_sz; + streamer_priv->rx_ring_skb[i] = skb; + } + streamer_priv->streamer_rx_ring[STREAMER_RX_RING_SIZE - 1].forward = + virt_to_bus(&streamer_priv->streamer_rx_ring[0]); + + if (i == 0) { + printk(KERN_WARNING "%s: Not enough memory to allocate rx buffers. Adapter disabled\n", dev->name); + free_irq(dev->irq, dev); + return -EIO; + } + + streamer_priv->rx_ring_last_received = STREAMER_RX_RING_SIZE - 1; /* last processed rx status */ + + writel(virt_to_bus(&streamer_priv->streamer_rx_ring[0]), streamer_mmio + RXBDA); + writel(virt_to_bus(&streamer_priv->streamer_rx_ring[STREAMER_RX_RING_SIZE - 1]), streamer_mmio + RXLBDA); + + /* set bus master interrupt event mask */ + writew(MISR_RX_NOBUF | MISR_RX_EOF, streamer_mmio + MISR_MASK); + + + /* setup tx ring */ + writew(~BMCTL_TX2_DIS, streamer_mmio + BMCTL_RUM); /* Enables TX channel 2 */ + for (i = 0; i < STREAMER_TX_RING_SIZE; i++) { + streamer_priv->streamer_tx_ring[i].forward = virt_to_bus(&streamer_priv->streamer_tx_ring[i + 1]); + streamer_priv->streamer_tx_ring[i].status = 0; + streamer_priv->streamer_tx_ring[i].bufcnt_framelen = 0; + streamer_priv->streamer_tx_ring[i].buffer = 0; + streamer_priv->streamer_tx_ring[i].buflen = 0; + } + streamer_priv->streamer_tx_ring[STREAMER_TX_RING_SIZE - 1].forward = + virt_to_bus(&streamer_priv->streamer_tx_ring[0]);; + + streamer_priv->free_tx_ring_entries = STREAMER_TX_RING_SIZE; + streamer_priv->tx_ring_free = 0; /* next entry in tx ring to use */ + streamer_priv->tx_ring_last_status = STREAMER_TX_RING_SIZE - 1; + + /* set Busmaster interrupt event mask (handle receives on interrupt only */ + writew(MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF, streamer_mmio + MISR_MASK); + /* set system event interrupt mask */ + writew(SISR_ADAPTER_CHECK | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_ASB_FREE, streamer_mmio + SISR_MASK_SUM); + +#if STREAMER_DEBUG + printk("BMCTL: %x\n", readw(streamer_mmio + BMCTL_SUM)); + printk("SISR MASK: %x\n", readw(streamer_mmio + SISR_MASK)); +#endif + +#if STREAMER_NETWORK_MONITOR + + writew(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA); + printk("%s: Node Address: %04x:%04x:%04x\n", dev->name, + ntohs(readw(streamer_mmio + LAPDINC)), + ntohs(readw(streamer_mmio + LAPDINC)), + ntohs(readw(streamer_mmio + LAPDINC))); + readw(streamer_mmio + LAPDINC); + readw(streamer_mmio + LAPDINC); + printk("%s: Functional Address: %04x:%04x\n", dev->name, + ntohs(readw(streamer_mmio + LAPDINC)), + ntohs(readw(streamer_mmio + LAPDINC))); + + writew(streamer_priv->streamer_parms_addr + 4, + streamer_mmio + LAPA); + printk("%s: NAUN Address: %04x:%04x:%04x\n", dev->name, + ntohs(readw(streamer_mmio + LAPDINC)), + ntohs(readw(streamer_mmio + LAPDINC)), + ntohs(readw(streamer_mmio + LAPDINC))); +#endif + + netif_start_queue(dev); + MOD_INC_USE_COUNT; + return 0; +} + +/* + * When we enter the rx routine we do not know how many frames have been + * queued on the rx channel. Therefore we start at the next rx status + * position and travel around the receive ring until we have completed + * all the frames. + * + * This means that we may process the frame before we receive the end + * of frame interrupt. This is why we always test the status instead + * of blindly processing the next frame. + * + */ +static void streamer_rx(struct net_device *dev) +{ + struct streamer_private *streamer_priv = + (struct streamer_private *) dev->priv; + __u8 *streamer_mmio = streamer_priv->streamer_mmio; + struct streamer_rx_desc *rx_desc; + int rx_ring_last_received, length, frame_length, buffer_cnt = 0; + struct sk_buff *skb, *skb2; + + /* setup the next rx descriptor to be received */ + rx_desc = &streamer_priv->streamer_rx_ring[(streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1)]; + rx_ring_last_received = streamer_priv->rx_ring_last_received; + + while (rx_desc->status & 0x01000000) { /* While processed descriptors are available */ + if (rx_ring_last_received != streamer_priv->rx_ring_last_received) + { + printk(KERN_WARNING "RX Error 1 rx_ring_last_received not the same %x %x\n", + rx_ring_last_received, streamer_priv->rx_ring_last_received); + } + streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1); + rx_ring_last_received = streamer_priv->rx_ring_last_received; + + length = rx_desc->framelen_buflen & 0xffff; /* buffer length */ + frame_length = (rx_desc->framelen_buflen >> 16) & 0xffff; + + if (rx_desc->status & 0x7E830000) { /* errors */ + if (streamer_priv->streamer_message_level) { + printk(KERN_WARNING "%s: Rx Error %x \n", + dev->name, rx_desc->status); + } + } else { /* received without errors */ + if (rx_desc->status & 0x80000000) { /* frame complete */ + buffer_cnt = 1; + skb = dev_alloc_skb(streamer_priv->pkt_buf_sz); + } else { + skb = dev_alloc_skb(frame_length); + } + + if (skb == NULL) + { + printk(KERN_WARNING "%s: Not enough memory to copy packet to upper layers. \n", dev->name); + streamer_priv->streamer_stats.rx_dropped++; + } else { /* we allocated an skb OK */ + skb->dev = dev; + + if (buffer_cnt == 1) { + skb2 = streamer_priv->rx_ring_skb[rx_ring_last_received]; +#if STREAMER_DEBUG_PACKETS + { + int i; + printk("streamer_rx packet print: skb->data2 %p skb->head %p\n", skb2->data, skb2->head); + for (i = 0; i < frame_length; i++) + { + printk("%x:", skb2->data[i]); + if (((i + 1) % 16) == 0) + printk("\n"); + } + printk("\n"); + } +#endif + skb_put(skb2, length); + skb2->protocol = tr_type_trans(skb2, dev); + /* recycle this descriptor */ + streamer_priv->streamer_rx_ring[rx_ring_last_received].status = 0; + streamer_priv->streamer_rx_ring[rx_ring_last_received].framelen_buflen = streamer_priv->pkt_buf_sz; + streamer_priv->streamer_rx_ring[rx_ring_last_received].buffer = virt_to_bus(skb->data); + streamer_priv-> rx_ring_skb[rx_ring_last_received] = skb; + /* place recycled descriptor back on the adapter */ + writel(virt_to_bus(&streamer_priv->streamer_rx_ring[rx_ring_last_received]),streamer_mmio + RXLBDA); + /* pass the received skb up to the protocol */ + netif_rx(skb2); + } else { + do { /* Walk the buffers */ + memcpy(skb_put(skb, length),bus_to_virt(rx_desc->buffer), length); /* copy this fragment */ + streamer_priv->streamer_rx_ring[rx_ring_last_received].status = 0; + streamer_priv->streamer_rx_ring[rx_ring_last_received].framelen_buflen = streamer_priv->pkt_buf_sz; + streamer_priv->streamer_rx_ring[rx_ring_last_received].buffer = virt_to_bus(skb->data); + /* give descriptor back to the adapter */ + writel(virt_to_bus(&streamer_priv->streamer_rx_ring[rx_ring_last_received]), streamer_mmio + RXLBDA); + + if (rx_desc->status & 0x80000000) + break; /* this descriptor completes the frame */ + + /* else get the next pending descriptor */ + if (rx_ring_last_received!= streamer_priv->rx_ring_last_received) + { + printk("RX Error rx_ring_last_received not the same %x %x\n", + rx_ring_last_received, + streamer_priv->rx_ring_last_received); + } + rx_desc = &streamer_priv->streamer_rx_ring[(streamer_priv->rx_ring_last_received+1) & (STREAMER_RX_RING_SIZE-1)]; + + length = rx_desc->framelen_buflen & 0xffff; /* buffer length */ + streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received+1) & (STREAMER_RX_RING_SIZE - 1); + rx_ring_last_received = streamer_priv->rx_ring_last_received; + } while (1); + + skb->protocol = tr_type_trans(skb, dev); + /* send up to the protocol */ + netif_rx(skb); + } + streamer_priv->streamer_stats.rx_packets++; + streamer_priv->streamer_stats.rx_bytes += length; + } /* if skb == null */ + } /* end received without errors */ + + /* try the next one */ + rx_desc = &streamer_priv->streamer_rx_ring[(rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1)]; + } /* end for all completed rx descriptors */ +} + +static void streamer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct streamer_private *streamer_priv = + (struct streamer_private *) dev->priv; + __u8 *streamer_mmio = streamer_priv->streamer_mmio; + __u16 sisr; + __u16 misr; + __u16 sisrmask; + + sisrmask = SISR_MI; + writew(~sisrmask, streamer_mmio + SISR_MASK_RUM); + sisr = readw(streamer_mmio + SISR); + writew(~sisr, streamer_mmio + SISR_RUM); + misr = readw(streamer_mmio + MISR_RUM); + writew(~misr, streamer_mmio + MISR_RUM); + + if (!sisr) { /* Interrupt isn't for us */ + return; + } + + if ((sisr & (SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY)) + || (misr & (MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF))) { + if (sisr & SISR_SRB_REPLY) { + if (streamer_priv->srb_queued == 1) { + wake_up_interruptible(&streamer_priv->srb_wait); + } else if (streamer_priv->srb_queued == 2) { + streamer_srb_bh(dev); + } + streamer_priv->srb_queued = 0; + } + /* SISR_SRB_REPLY */ + if (misr & MISR_TX2_EOF) { + while (streamer_priv->streamer_tx_ring[(streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1)].status) + { + streamer_priv->tx_ring_last_status = (streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1); + streamer_priv->free_tx_ring_entries++; + streamer_priv->streamer_stats.tx_bytes += streamer_priv->tx_ring_skb[streamer_priv->tx_ring_last_status]->len; + streamer_priv->streamer_stats.tx_packets++; + dev_kfree_skb_irq(streamer_priv->tx_ring_skb[streamer_priv->tx_ring_last_status]); + streamer_priv-> streamer_tx_ring[streamer_priv->tx_ring_last_status].buffer = 0xdeadbeef; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].status = 0; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].bufcnt_framelen = 0; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].buflen = 0; + } + netif_wake_queue(dev); + } + + if (misr & MISR_RX_EOF) { + streamer_rx(dev); + } + /* MISR_RX_EOF */ + if (sisr & SISR_ADAPTER_CHECK) { + printk(KERN_WARNING "%s: Adapter Check Interrupt Raised, 8 bytes of information follow:\n", dev->name); + writel(readl(streamer_mmio + LAPWWO), streamer_mmio + LAPA); + printk(KERN_WARNING "%s: Words %x:%x:%x:%x:\n", + dev->name, readw(streamer_mmio + LAPDINC), + readw(streamer_mmio + LAPDINC), + readw(streamer_mmio + LAPDINC), + readw(streamer_mmio + LAPDINC)); + free_irq(dev->irq, dev); + } + + /* SISR_ADAPTER_CHECK */ + if (sisr & SISR_ASB_FREE) { + /* Wake up anything that is waiting for the asb response */ + if (streamer_priv->asb_queued) { + streamer_asb_bh(dev); + } + } + /* SISR_ASB_FREE */ + if (sisr & SISR_ARB_CMD) { + streamer_arb_cmd(dev); + } + /* SISR_ARB_CMD */ + if (sisr & SISR_TRB_REPLY) { + /* Wake up anything that is waiting for the trb response */ + if (streamer_priv->trb_queued) { + wake_up_interruptible(&streamer_priv-> + trb_wait); + } + streamer_priv->trb_queued = 0; + } + /* SISR_TRB_REPLY */ + if (misr & MISR_RX_NOBUF) { + /* According to the documentation, we don't have to do anything, but trapping it keeps it out of + /var/log/messages. */ + } /* SISR_RX_NOBUF */ + } else { + printk(KERN_WARNING "%s: Unexpected interrupt: %x\n", + dev->name, sisr); + printk(KERN_WARNING "%s: SISR_MASK: %x\n", dev->name, + readw(streamer_mmio + SISR_MASK)); + } /* One if the interrupts we want */ + + writew(SISR_MI, streamer_mmio + SISR_MASK_SUM); +} + + +static int streamer_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct streamer_private *streamer_priv = + (struct streamer_private *) dev->priv; + __u8 *streamer_mmio = streamer_priv->streamer_mmio; + + netif_stop_queue(dev); + + if (streamer_priv->free_tx_ring_entries) { + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].status = 0; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].bufcnt_framelen = 0x00010000 | skb->len; + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buffer = virt_to_bus(skb->data); + streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buflen = skb->len; + streamer_priv->tx_ring_skb[streamer_priv->tx_ring_free] = skb; + streamer_priv->free_tx_ring_entries--; +#if STREAMER_DEBUG_PACKETS + { + int i; + printk("streamer_xmit packet print:\n"); + for (i = 0; i < skb->len; i++) { + printk("%x:", skb->data[i]); + if (((i + 1) % 16) == 0) + printk("\n"); + } + printk("\n"); + } +#endif + + writel(virt_to_bus (&streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free]),streamer_mmio + TX2LFDA); + + streamer_priv->tx_ring_free = (streamer_priv->tx_ring_free + 1) & (STREAMER_TX_RING_SIZE - 1); + netif_start_queue(dev); + return 0; + } else { + return 1; + } +} + + +static int streamer_close(struct net_device *dev) +{ + struct streamer_private *streamer_priv = + (struct streamer_private *) dev->priv; + __u8 *streamer_mmio = streamer_priv->streamer_mmio; + unsigned long flags; + int i; + + writew(streamer_priv->srb, streamer_mmio + LAPA); + writew(SRB_CLOSE_ADAPTER, streamer_mmio + LAPDINC); + writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); + + save_flags(flags); + cli(); + + streamer_priv->srb_queued = 1; + writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM); + + while (streamer_priv->srb_queued) + { + interruptible_sleep_on_timeout(&streamer_priv->srb_wait, + jiffies + 60 * HZ); + if (signal_pending(current)) + { + printk(KERN_WARNING "%s: SRB timed out.\n", dev->name); + printk(KERN_WARNING "SISR=%x MISR=%x LISR=%x\n", + readw(streamer_mmio + SISR), + readw(streamer_mmio + MISR_RUM), + readw(streamer_mmio + LISR)); + streamer_priv->srb_queued = 0; + break; + } + } + + restore_flags(flags); + streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1); + + for (i = 0; i < STREAMER_RX_RING_SIZE; i++) { + dev_kfree_skb(streamer_priv->rx_ring_skb[streamer_priv->rx_ring_last_received]); + streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1); + } + + /* reset tx/rx fifo's and busmaster logic */ + + /* TBD. Add graceful way to reset the LLC channel without doing a soft reset. + writel(readl(streamer_mmio+BCTL)|(3<<13),streamer_mmio+BCTL); + udelay(1); + writel(readl(streamer_mmio+BCTL)&~(3<<13),streamer_mmio+BCTL); + */ + +#if STREAMER_DEBUG + writew(streamer_priv->srb, streamer_mmio + LAPA); + printk("srb): "); + for (i = 0; i < 2; i++) { + printk("%x ", htons(readw(streamer_mmio + LAPDINC))); + } + printk("\n"); +#endif + netif_stop_queue(dev); + free_irq(dev->irq, dev); + + MOD_DEC_USE_COUNT; + return 0; +} + +static void streamer_set_rx_mode(struct net_device *dev) +{ + struct streamer_private *streamer_priv = + (struct streamer_private *) dev->priv; + __u8 *streamer_mmio = streamer_priv->streamer_mmio; + __u8 options = 0, set_mc_list = 0; + __u16 ata1, ata2; + struct dev_mc_list *dmi; + + writel(streamer_priv->srb, streamer_mmio + LAPA); + options = streamer_priv->streamer_copy_all_options; + + if (dev->flags & IFF_PROMISC) + options |= (3 << 5); /* All LLC and MAC frames, all through the main rx channel */ + else + options &= ~(3 << 5); + + if (dev->mc_count) { + set_mc_list = 1; + } + + /* Only issue the srb if there is a change in options */ + + if ((options ^ streamer_priv->streamer_copy_all_options)) + { + /* Now to issue the srb command to alter the copy.all.options */ + + writew(SRB_MODIFY_RECEIVE_OPTIONS, + streamer_mmio + LAPDINC); + writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); + writew(streamer_priv->streamer_receive_options | (options << 8), streamer_mmio + LAPDINC); + writew(0x414a, streamer_mmio + LAPDINC); + writew(0x454d, streamer_mmio + LAPDINC); + writew(0x2053, streamer_mmio + LAPDINC); + writew(0x2020, streamer_mmio + LAPDINC); + + streamer_priv->srb_queued = 2; /* Can't sleep, use srb_bh */ + + writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM); + + streamer_priv->streamer_copy_all_options = options; + return; + } + + if (set_mc_list ^ streamer_priv->streamer_multicast_set) + { /* Multicast options have changed */ + dmi = dev->mc_list; + + writel(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA); + ata1 = readw(streamer_mmio + LAPDINC); + ata2 = readw(streamer_mmio + LAPD); + + writel(streamer_priv->srb, streamer_mmio + LAPA); + + if (set_mc_list) + { + /* Turn multicast on */ + + /* RFC 1469 Says we must support using the functional address C0 00 00 04 00 00 + * We do this with a set functional address mask. + */ + + if (!(ata1 & 0x0400)) { /* need to set functional mask */ + writew(SRB_SET_FUNC_ADDRESS, streamer_mmio + LAPDINC); + writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); + writew(0, streamer_mmio + LAPDINC); + writew(ata1 | 0x0400, streamer_mmio + LAPDINC); + writew(ata2, streamer_mmio + LAPD); + + streamer_priv->srb_queued = 2; + writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM); + + streamer_priv->streamer_multicast_set = 1; + } + + } else { /* Turn multicast off */ + + if ((ata1 & 0x0400)) { /* Hmmm, need to reset the functional mask */ + writew(SRB_SET_FUNC_ADDRESS, streamer_mmio + LAPDINC); + writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); + writew(0, streamer_mmio + LAPDINC); + writew(ata1 & ~0x0400, streamer_mmio + LAPDINC); + writew(ata2, streamer_mmio + LAPD); + + streamer_priv->srb_queued = 2; + writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM); + + streamer_priv->streamer_multicast_set = 0; + } + } + + } +} + +static void streamer_srb_bh(struct net_device *dev) +{ + struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv; + __u8 *streamer_mmio = streamer_priv->streamer_mmio; + __u16 srb_word; + + writew(streamer_priv->srb, streamer_mmio + LAPA); + srb_word = readw(streamer_mmio + LAPDINC) & 0xFF; + + switch (srb_word) { + + /* SRB_MODIFY_RECEIVE_OPTIONS i.e. set_multicast_list options (promiscuous) + * At some point we should do something if we get an error, such as + * resetting the IFF_PROMISC flag in dev + */ + + case SRB_MODIFY_RECEIVE_OPTIONS: + srb_word = readw(streamer_mmio + LAPDINC) & 0xFF; + switch (srb_word) { + case 0x01: + printk(KERN_WARNING "%s: Unrecognized srb command\n", dev->name); + break; + case 0x04: + printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name); + break; + default: + if (streamer_priv->streamer_message_level) + printk(KERN_WARNING "%s: Receive Options Modified to %x,%x\n", + dev->name, + streamer_priv->streamer_copy_all_options, + streamer_priv->streamer_receive_options); + break; + } /* switch srb[2] */ + break; + + + /* SRB_SET_GROUP_ADDRESS - Multicast group setting + */ + case SRB_SET_GROUP_ADDRESS: + srb_word = readw(streamer_mmio + LAPDINC) & 0xFF; + switch (srb_word) { + case 0x00: + streamer_priv->streamer_multicast_set = 1; + break; + case 0x01: + printk(KERN_WARNING "%s: Unrecognized srb command \n",dev->name); + break; + case 0x04: + printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name); + break; + case 0x3c: + printk(KERN_WARNING "%s: Group/Functional address indicator bits not set correctly\n", dev->name); + break; + case 0x3e: /* If we ever implement individual multicast addresses, will need to deal with this */ + printk(KERN_WARNING "%s: Group address registers full\n", dev->name); + break; + case 0x55: + printk(KERN_INFO "%s: Group Address already set.\n", dev->name); + break; + default: + break; + } /* switch srb[2] */ + break; + + + /* SRB_RESET_GROUP_ADDRESS - Remove a multicast address from group list + */ + case SRB_RESET_GROUP_ADDRESS: + srb_word = readw(streamer_mmio + LAPDINC) & 0xFF; + switch (srb_word) { + case 0x00: + streamer_priv->streamer_multicast_set = 0; + break; + case 0x01: + printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name); + break; + case 0x04: + printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name); + break; + case 0x39: /* Must deal with this if individual multicast addresses used */ + printk(KERN_INFO "%s: Group address not found \n", dev->name); + break; + default: + break; + } /* switch srb[2] */ + break; + + + /* SRB_SET_FUNC_ADDRESS - Called by the set_rx_mode + */ + + case SRB_SET_FUNC_ADDRESS: + srb_word = readw(streamer_mmio + LAPDINC) & 0xFF; + switch (srb_word) { + case 0x00: + if (streamer_priv->streamer_message_level) + printk(KERN_INFO "%s: Functional Address Mask Set \n", dev->name); + break; + case 0x01: + printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name); + break; + case 0x04: + printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name); + break; + default: + break; + } /* switch srb[2] */ + break; + + /* SRB_READ_LOG - Read and reset the adapter error counters + */ + + case SRB_READ_LOG: + srb_word = readw(streamer_mmio + LAPDINC) & 0xFF; + switch (srb_word) { + case 0x00: + { + int i; + if (streamer_priv->streamer_message_level) + printk(KERN_INFO "%s: Read Log command complete\n", dev->name); + printk("Read Log statistics: "); + writew(streamer_priv->srb + 6, + streamer_mmio + LAPA); + for (i = 0; i < 5; i++) { + printk("%x:", ntohs(readw(streamer_mmio + LAPDINC))); + } + printk("\n"); + } + break; + case 0x01: + printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name); + break; + case 0x04: + printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name); + break; + + } /* switch srb[2] */ + break; + + /* SRB_READ_SR_COUNTERS - Read and reset the source routing bridge related counters */ + + case SRB_READ_SR_COUNTERS: + srb_word = readw(streamer_mmio + LAPDINC) & 0xFF; + switch (srb_word) { + case 0x00: + if (streamer_priv->streamer_message_level) + printk(KERN_INFO "%s: Read Source Routing Counters issued\n", dev->name); + break; + case 0x01: + printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name); + break; + case 0x04: + printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name); + break; + default: + break; + } /* switch srb[2] */ + break; + + default: + printk(KERN_WARNING "%s: Unrecognized srb bh return value.\n", dev->name); + break; + } /* switch srb[0] */ +} + +static struct net_device_stats *streamer_get_stats(struct net_device *dev) +{ + struct streamer_private *streamer_priv; + streamer_priv = (struct streamer_private *) dev->priv; + return (struct net_device_stats *) &streamer_priv->streamer_stats; +} + +static int streamer_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *saddr = addr; + struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv; + + if (netif_running(dev)) { + printk(KERN_WARNING "%s: Cannot set mac/laa address while card is open\n", dev->name); + return -EBUSY; + } + + memcpy(streamer_priv->streamer_laa, saddr->sa_data, dev->addr_len); + + if (streamer_priv->streamer_message_level) { + printk(KERN_INFO "%s: MAC/LAA Set to = %x.%x.%x.%x.%x.%x\n", + dev->name, streamer_priv->streamer_laa[0], + streamer_priv->streamer_laa[1], + streamer_priv->streamer_laa[2], + streamer_priv->streamer_laa[3], + streamer_priv->streamer_laa[4], + streamer_priv->streamer_laa[5]); + } + return 0; +} + +static void streamer_arb_cmd(struct net_device *dev) +{ + struct streamer_private *streamer_priv = + (struct streamer_private *) dev->priv; + __u8 *streamer_mmio = streamer_priv->streamer_mmio; + __u8 header_len; + __u16 frame_len, buffer_len; + struct sk_buff *mac_frame; + __u8 frame_data[256]; + __u16 buff_off; + __u16 lan_status = 0, lan_status_diff; /* Initialize to stop compiler warning */ + __u8 fdx_prot_error; + __u16 next_ptr; + __u16 arb_word; + +#if STREAMER_NETWORK_MONITOR + struct trh_hdr *mac_hdr; +#endif + + writew(streamer_priv->arb, streamer_mmio + LAPA); + arb_word = readw(streamer_mmio + LAPD) & 0xFF; + + if (arb_word == ARB_RECEIVE_DATA) { /* Receive.data, MAC frames */ + writew(streamer_priv->arb + 6, streamer_mmio + LAPA); + streamer_priv->mac_rx_buffer = buff_off = ntohs(readw(streamer_mmio + LAPDINC)); + header_len = readw(streamer_mmio + LAPDINC) & 0xff; /* 802.5 Token-Ring Header Length */ + frame_len = ntohs(readw(streamer_mmio + LAPDINC)); + +#if STREAMER_DEBUG + { + int i; + __u16 next; + __u8 status; + __u16 len; + + writew(ntohs(buff_off), streamer_mmio + LAPA); /*setup window to frame data */ + next = ntohs(readw(streamer_mmio + LAPDINC)); + status = + ntohs(readw(streamer_mmio + LAPDINC)) & 0xff; + len = ntohs(readw(streamer_mmio + LAPDINC)); + + /* print out 1st 14 bytes of frame data */ + for (i = 0; i < 7; i++) { + printk("Loc %d = %04x\n", i, + ntohs(readw + (streamer_mmio + LAPDINC))); + } + + printk("next %04x, fs %02x, len %04x \n", next, + status, len); + } +#endif + mac_frame = dev_alloc_skb(frame_len); + + /* Walk the buffer chain, creating the frame */ + + do { + int i; + __u16 rx_word; + + writew(ntohs(buff_off), streamer_mmio + LAPA); /* setup window to frame data */ + next_ptr = ntohs(readw(streamer_mmio + LAPDINC)); + readw(streamer_mmio + LAPDINC); /* read thru status word */ + buffer_len = ntohs(readw(streamer_mmio + LAPDINC)); + + if (buffer_len > 256) + break; + + i = 0; + while (i < buffer_len) { + rx_word = readw(streamer_mmio + LAPDINC); + frame_data[i] = rx_word & 0xff; + frame_data[i + 1] = (rx_word >> 8) & 0xff; + i += 2; + } + + memcpy_fromio(skb_put(mac_frame, buffer_len), + frame_data, buffer_len); + } while (next_ptr && (buff_off = next_ptr)); + +#if STREAMER_NETWORK_MONITOR + printk(KERN_WARNING "%s: Received MAC Frame, details: \n", + dev->name); + mac_hdr = (struct trh_hdr *) mac_frame->data; + printk(KERN_WARNING + "%s: MAC Frame Dest. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n", + dev->name, mac_hdr->daddr[0], mac_hdr->daddr[1], + mac_hdr->daddr[2], mac_hdr->daddr[3], + mac_hdr->daddr[4], mac_hdr->daddr[5]); + printk(KERN_WARNING + "%s: MAC Frame Srce. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n", + dev->name, mac_hdr->saddr[0], mac_hdr->saddr[1], + mac_hdr->saddr[2], mac_hdr->saddr[3], + mac_hdr->saddr[4], mac_hdr->saddr[5]); +#endif + mac_frame->dev = dev; + mac_frame->protocol = tr_type_trans(mac_frame, dev); + netif_rx(mac_frame); + + /* Now tell the card we have dealt with the received frame */ + + /* Set LISR Bit 1 */ + writel(LISR_ARB_FREE, streamer_priv->streamer_mmio + LISR_SUM); + + /* Is the ASB free ? */ + + if (!(readl(streamer_priv->streamer_mmio + SISR) & SISR_ASB_FREE)) + { + streamer_priv->asb_queued = 1; + writel(LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM); + return; + /* Drop out and wait for the bottom half to be run */ + } + + + writew(streamer_priv->asb, streamer_mmio + LAPA); + writew(ASB_RECEIVE_DATA, streamer_mmio + LAPDINC); /* Receive data */ + writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); /* Necessary ?? */ + writew(0, streamer_mmio + LAPDINC); + writew(ntohs(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD); + + writel(LISR_ASB_REPLY | LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM); + + streamer_priv->asb_queued = 2; + return; + + } else if (arb_word == ARB_LAN_CHANGE_STATUS) { /* Lan.change.status */ + writew(streamer_priv->arb + 6, streamer_mmio + LAPA); + lan_status = ntohs(readw(streamer_mmio + LAPDINC)); + fdx_prot_error = readw(streamer_mmio + LAPD) & 0xFF; + + /* Issue ARB Free */ + writew(LISR_ARB_FREE, streamer_priv->streamer_mmio + LISR_SUM); + + lan_status_diff = streamer_priv->streamer_lan_status ^ lan_status; + + if (lan_status_diff & (LSC_LWF | LSC_ARW | LSC_FPE | LSC_RR)) + { + if (lan_status_diff & LSC_LWF) + printk(KERN_WARNING "%s: Short circuit detected on the lobe\n", dev->name); + if (lan_status_diff & LSC_ARW) + printk(KERN_WARNING "%s: Auto removal error\n", dev->name); + if (lan_status_diff & LSC_FPE) + printk(KERN_WARNING "%s: FDX Protocol Error\n", dev->name); + if (lan_status_diff & LSC_RR) + printk(KERN_WARNING "%s: Force remove MAC frame received\n", dev->name); + + /* Adapter has been closed by the hardware */ + + /* reset tx/rx fifo's and busmaster logic */ + + /* @TBD. no llc reset on autostreamer writel(readl(streamer_mmio+BCTL)|(3<<13),streamer_mmio+BCTL); + udelay(1); + writel(readl(streamer_mmio+BCTL)&~(3<<13),streamer_mmio+BCTL); */ + netif_stop_queue(dev); + free_irq(dev->irq, dev); + + printk(KERN_WARNING "%s: Adapter has been closed \n", dev->name); + + } + /* If serious error */ + if (streamer_priv->streamer_message_level) { + if (lan_status_diff & LSC_SIG_LOSS) + printk(KERN_WARNING "%s: No receive signal detected \n", dev->name); + if (lan_status_diff & LSC_HARD_ERR) + printk(KERN_INFO "%s: Beaconing \n", dev->name); + if (lan_status_diff & LSC_SOFT_ERR) + printk(KERN_WARNING "%s: Adapter transmitted Soft Error Report Mac Frame \n", dev->name); + if (lan_status_diff & LSC_TRAN_BCN) + printk(KERN_INFO "%s: We are tranmitting the beacon, aaah\n", dev->name); + if (lan_status_diff & LSC_SS) + printk(KERN_INFO "%s: Single Station on the ring \n", dev->name); + if (lan_status_diff & LSC_RING_REC) + printk(KERN_INFO "%s: Ring recovery ongoing\n", dev->name); + if (lan_status_diff & LSC_FDX_MODE) + printk(KERN_INFO "%s: Operating in FDX mode\n", dev->name); + } + + if (lan_status_diff & LSC_CO) { + if (streamer_priv->streamer_message_level) + printk(KERN_INFO "%s: Counter Overflow \n", dev->name); + + /* Issue READ.LOG command */ + + writew(streamer_priv->srb, streamer_mmio + LAPA); + writew(SRB_READ_LOG, streamer_mmio + LAPDINC); + writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); + writew(0, streamer_mmio + LAPDINC); + streamer_priv->srb_queued = 2; /* Can't sleep, use srb_bh */ + + writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM); + } + + if (lan_status_diff & LSC_SR_CO) { + if (streamer_priv->streamer_message_level) + printk(KERN_INFO "%s: Source routing counters overflow\n", dev->name); + + /* Issue a READ.SR.COUNTERS */ + writew(streamer_priv->srb, streamer_mmio + LAPA); + writew(SRB_READ_SR_COUNTERS, + streamer_mmio + LAPDINC); + writew(STREAMER_CLEAR_RET_CODE, + streamer_mmio + LAPDINC); + streamer_priv->srb_queued = 2; /* Can't sleep, use srb_bh */ + writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM); + + } + streamer_priv->streamer_lan_status = lan_status; + } /* Lan.change.status */ + else + printk(KERN_WARNING "%s: Unknown arb command \n", dev->name); +} + +static void streamer_asb_bh(struct net_device *dev) +{ + struct streamer_private *streamer_priv = + (struct streamer_private *) dev->priv; + __u8 *streamer_mmio = streamer_priv->streamer_mmio; + + if (streamer_priv->asb_queued == 1) + { + /* Dropped through the first time */ + + writew(streamer_priv->asb, streamer_mmio + LAPA); + writew(ASB_RECEIVE_DATA, streamer_mmio + LAPDINC); /* Receive data */ + writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); /* Necessary ?? */ + writew(0, streamer_mmio + LAPDINC); + writew(ntohs(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD); + + writel(LISR_ASB_REPLY | LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM); + streamer_priv->asb_queued = 2; + + return; + } + + if (streamer_priv->asb_queued == 2) { + __u8 rc; + writew(streamer_priv->asb + 2, streamer_mmio + LAPA); + rc = readw(streamer_mmio + LAPD) & 0xff; + switch (rc) { + case 0x01: + printk(KERN_WARNING "%s: Unrecognized command code \n", dev->name); + break; + case 0x26: + printk(KERN_WARNING "%s: Unrecognized buffer address \n", dev->name); + break; + case 0xFF: + /* Valid response, everything should be ok again */ + break; + default: + printk(KERN_WARNING "%s: Invalid return code in asb\n", dev->name); + break; + } + } + streamer_priv->asb_queued = 0; +} + +static int streamer_change_mtu(struct net_device *dev, int mtu) +{ + struct streamer_private *streamer_priv = + (struct streamer_private *) dev->priv; + __u16 max_mtu; + + if (streamer_priv->streamer_ring_speed == 4) + max_mtu = 4500; + else + max_mtu = 18000; + + if (mtu > max_mtu) + return -EINVAL; + if (mtu < 100) + return -EINVAL; + + dev->mtu = mtu; + streamer_priv->pkt_buf_sz = mtu + TR_HLEN; + + return 0; +} + +#if STREAMER_NETWORK_MONITOR +#ifdef CONFIG_PROC_FS +static int streamer_proc_info(char *buffer, char **start, off_t offset, + int length, int *eof, void *data) +{ + struct pci_dev *pci_device = NULL; + int len = 0; + off_t begin = 0; + off_t pos = 0; + int size; + + struct net_device *dev; + + + size = sprintf(buffer, "IBM LanStreamer/MPC Chipset Token Ring Adapters\n"); + + pos += size; + len += size; + + while ((pci_device = pci_find_device(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR, pci_device))) + { + + for (dev = dev_base; dev != NULL; dev = dev->next) + { + if (dev->base_addr == (pci_device->base_address[0] & (~3))) + { /* Yep, a Streamer device */ + size = sprintf_info(buffer + len, dev); + len += size; + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + break; + } /* if */ + } /* for */ + } /* While */ + + *start = buffer + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) + len = length; /* Ending slop */ + return len; +} + +static int sprintf_info(char *buffer, struct net_device *dev) +{ + struct streamer_private *streamer_priv = + (struct streamer_private *) dev->priv; + __u8 *streamer_mmio = streamer_priv->streamer_mmio; + struct streamer_adapter_addr_table sat; + struct streamer_parameters_table spt; + int size = 0; + int i; + + writew(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA); + for (i = 0; i < 14; i += 2) { + __u16 io_word; + __u8 *datap = (__u8 *) & sat; + io_word = readw(streamer_mmio + LAPDINC); + datap[size] = io_word & 0xff; + datap[size + 1] = (io_word >> 8) & 0xff; + } + writew(streamer_priv->streamer_parms_addr, streamer_mmio + LAPA); + for (i = 0; i < 68; i += 2) { + __u16 io_word; + __u8 *datap = (__u8 *) & spt; + io_word = readw(streamer_mmio + LAPDINC); + datap[size] = io_word & 0xff; + datap[size + 1] = (io_word >> 8) & 0xff; + } + + + size = sprintf(buffer, "\n%6s: Adapter Address : Node Address : Functional Addr\n", dev->name); + + size += sprintf(buffer + size, + "%6s: %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x\n", + dev->name, dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], + dev->dev_addr[5], sat.node_addr[0], sat.node_addr[1], + sat.node_addr[2], sat.node_addr[3], sat.node_addr[4], + sat.node_addr[5], sat.func_addr[0], sat.func_addr[1], + sat.func_addr[2], sat.func_addr[3]); + + size += sprintf(buffer + size, "\n%6s: Token Ring Parameters Table:\n", dev->name); + + size += sprintf(buffer + size, "%6s: Physical Addr : Up Node Address : Poll Address : AccPri : Auth Src : Att Code :\n", dev->name); + + size += sprintf(buffer + size, + "%6s: %02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %04x : %04x : %04x :\n", + dev->name, spt.phys_addr[0], spt.phys_addr[1], + spt.phys_addr[2], spt.phys_addr[3], + spt.up_node_addr[0], spt.up_node_addr[1], + spt.up_node_addr[2], spt.up_node_addr[3], + spt.up_node_addr[4], spt.up_node_addr[4], + spt.poll_addr[0], spt.poll_addr[1], spt.poll_addr[2], + spt.poll_addr[3], spt.poll_addr[4], spt.poll_addr[5], + ntohs(spt.acc_priority), ntohs(spt.auth_source_class), + ntohs(spt.att_code)); + + size += sprintf(buffer + size, "%6s: Source Address : Bcn T : Maj. V : Lan St : Lcl Rg : Mon Err : Frame Correl : \n", dev->name); + + size += sprintf(buffer + size, + "%6s: %02x:%02x:%02x:%02x:%02x:%02x : %04x : %04x : %04x : %04x : %04x : %04x : \n", + dev->name, spt.source_addr[0], spt.source_addr[1], + spt.source_addr[2], spt.source_addr[3], + spt.source_addr[4], spt.source_addr[5], + ntohs(spt.beacon_type), ntohs(spt.major_vector), + ntohs(spt.lan_status), ntohs(spt.local_ring), + ntohs(spt.mon_error), ntohs(spt.frame_correl)); + + size += sprintf(buffer + size, "%6s: Beacon Details : Tx : Rx : NAUN Node Address : NAUN Node Phys : \n", + dev->name); + + size += sprintf(buffer + size, + "%6s: : %02x : %02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x : \n", + dev->name, ntohs(spt.beacon_transmit), + ntohs(spt.beacon_receive), spt.beacon_naun[0], + spt.beacon_naun[1], spt.beacon_naun[2], + spt.beacon_naun[3], spt.beacon_naun[4], + spt.beacon_naun[5], spt.beacon_phys[0], + spt.beacon_phys[1], spt.beacon_phys[2], + spt.beacon_phys[3]); + return size; +} +#endif +#endif + +#ifdef MODULE + +static struct net_device *dev_streamer[STREAMER_MAX_ADAPTERS]; + +int init_module(void) +{ + int i; + +#if STREAMER_NETWORK_MONITOR +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *ent; + + ent = create_proc_entry("net/streamer_tr", 0, 0); + ent->read_proc = &streamer_proc_info; +#endif +#endif + for (i = 0; (i < STREAMER_MAX_ADAPTERS); i++) + { + dev_streamer[i] = NULL; + dev_streamer[i] = init_trdev(dev_streamer[i], 0); + if (dev_streamer[i] == NULL) + return -ENOMEM; + + dev_streamer[i]->init = &streamer_probe; + + if (register_trdev(dev_streamer[i]) != 0) { + kfree_s(dev_streamer[i], sizeof(struct net_device)); + dev_streamer[i] = NULL; + if (i == 0) + { + printk(KERN_INFO "Streamer: No IBM LanStreamer PCI Token Ring cards found in system.\n"); + return -EIO; + } else { + printk(KERN_INFO "Streamer: %d IBM LanStreamer PCI Token Ring card(s) found in system.\n", i); + return 0; + } + } + } + + return 0; +} + +void cleanup_module(void) +{ + int i; + + for (i = 0; i < STREAMER_MAX_ADAPTERS; i++) + if (dev_streamer[i]) { + unregister_trdev(dev_streamer[i]); + release_region(dev_streamer[i]->base_addr, STREAMER_IO_SPACE); + kfree_s(dev_streamer[i]->priv, sizeof(struct streamer_private)); + kfree_s(dev_streamer[i], sizeof(struct net_device)); + dev_streamer[i] = NULL; + } +#if STREAMER_NETWORK_MONITOR +#ifdef CONFIG_PROC_FS + remove_proc_entry("net/streamer_tr", NULL); +#endif +#endif +} +#endif /* MODULE */ diff --git a/drivers/net/tokenring/lanstreamer.h b/drivers/net/tokenring/lanstreamer.h new file mode 100644 index 000000000..7ba86dfe5 --- /dev/null +++ b/drivers/net/tokenring/lanstreamer.h @@ -0,0 +1,319 @@ +/* + * lanstreamer.h -- driver for the IBM Auto LANStreamer PCI Adapter + * + * Written By: Mike Sullivan, IBM Corporation + * + * Copyright (C) 1999 IBM Corporation + * + * Linux driver for IBM PCI tokenring cards based on the LanStreamer MPC + * chipset. + * + * This driver is based on the olympic driver for IBM PCI TokenRing cards (Pit/Pit-Phy/Olympic + * chipsets) written by: + * 1999 Peter De Schrijver All Rights Reserved + * 1999 Mike Phillips (phillim@amtrak.com) + * + * Base Driver Skeleton: + * Written 1993-94 by Donald Becker. + * + * Copyright 1993 United States Government as represented by the + * Director, National Security Agency. + * + * 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. + * + * NO WARRANTY + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + * solely responsible for determining the appropriateness of using and + * distributing the Program and assumes all risks associated with its + * exercise of rights under this Agreement, including but not limited to + * the risks and costs of program errors, damage to or loss of data, + * programs or equipment, and unavailability or interruption of operations. + * + * DISCLAIMER OF LIABILITY + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * 12/10/99 - Alpha Release 0.1.0 + * First release to the public + * + */ + +#define BCTL 0x60 +#define BCTL_SOFTRESET (1<<15) + +#define GPR 0x4a +#define GPR_AUTOSENSE (1<<2) +#define GPR_16MBPS (1<<3) + +#define LISR 0x10 +#define LISR_SUM 0x12 +#define LISR_RUM 0x14 + +#define LISR_LIE (1<<15) +#define LISR_SLIM (1<<13) +#define LISR_SLI (1<<12) +#define LISR_BPEI (1<<9) +#define LISR_BPE (1<<8) +#define LISR_SRB_CMD (1<<5) +#define LISR_ASB_REPLY (1<<4) +#define LISR_ASB_FREE_REQ (1<<2) +#define LISR_ARB_FREE (1<<1) +#define LISR_TRB_FRAME (1<<0) + +#define SISR 0x16 +#define SISR_SUM 0x18 +#define SISR_RUM 0x1A +#define SISR_MASK 0x54 +#define SISR_MASK_SUM 0x56 +#define SISR_MASK_RUM 0x58 + +#define SISR_MI (1<<15) +#define SISR_TIMER (1<<11) +#define SISR_LAP_PAR_ERR (1<<10) +#define SISR_LAP_ACC_ERR (1<<9) +#define SISR_PAR_ERR (1<<8) +#define SISR_ADAPTER_CHECK (1<<6) +#define SISR_SRB_REPLY (1<<5) +#define SISR_ASB_FREE (1<<4) +#define SISR_ARB_CMD (1<<3) +#define SISR_TRB_REPLY (1<<2) + +#define MISR_RUM 0x5A +#define MISR_MASK 0x5C +#define MISR_MASK_RUM 0x5E + +#define MISR_TX2_IDLE (1<<15) +#define MISR_TX2_NO_STATUS (1<<14) +#define MISR_TX2_HALT (1<<13) +#define MISR_TX2_EOF (1<<12) +#define MISR_TX1_IDLE (1<<11) +#define MISR_TX1_NO_STATUS (1<<10) +#define MISR_TX1_HALT (1<<9) +#define MISR_TX1_EOF (1<<8) +#define MISR_RX_NOBUF (1<<5) +#define MISR_RX_EOB (1<<4) +#define MISR_RX_NO_STATUS (1<<2) +#define MISR_RX_HALT (1<<1) +#define MISR_RX_EOF (1<<0) + +#define LAPA 0x62 +#define LAPE 0x64 +#define LAPD 0x66 +#define LAPDINC 0x68 +#define LAPWWO 0x6A +#define LAPWWC 0x6C +#define LAPCTL 0x6E + +#define TIMER 0x4E4 + +#define BMCTL_SUM 0x50 +#define BMCTL_RUM 0x52 +#define BMCTL_TX1_DIS (1<<14) +#define BMCTL_TX2_DIS (1<<10) +#define BMCTL_RX_DIS (1<<6) + +#define RXLBDA 0x90 +#define RXBDA 0x94 +#define RXSTAT 0x98 +#define RXDBA 0x9C + +#define TX1LFDA 0xA0 +#define TX1FDA 0xA4 +#define TX1STAT 0xA8 +#define TX1DBA 0xAC +#define TX2LFDA 0xB0 +#define TX2FDA 0xB4 +#define TX2STAT 0xB8 +#define TX2DBA 0xBC + +#define STREAMER_IO_SPACE 256 + +#define SRB_COMMAND_SIZE 50 + +#define STREAMER_MAX_ADAPTERS 8 /* 0x08 __MODULE_STRING can't hand 0xnn */ + +/* Defines for LAN STATUS CHANGE reports */ +#define LSC_SIG_LOSS 0x8000 +#define LSC_HARD_ERR 0x4000 +#define LSC_SOFT_ERR 0x2000 +#define LSC_TRAN_BCN 0x1000 +#define LSC_LWF 0x0800 +#define LSC_ARW 0x0400 +#define LSC_FPE 0x0200 +#define LSC_RR 0x0100 +#define LSC_CO 0x0080 +#define LSC_SS 0x0040 +#define LSC_RING_REC 0x0020 +#define LSC_SR_CO 0x0010 +#define LSC_FDX_MODE 0x0004 + +/* Defines for OPEN ADAPTER command */ + +#define OPEN_ADAPTER_EXT_WRAP (1<<15) +#define OPEN_ADAPTER_DIS_HARDEE (1<<14) +#define OPEN_ADAPTER_DIS_SOFTERR (1<<13) +#define OPEN_ADAPTER_PASS_ADC_MAC (1<<12) +#define OPEN_ADAPTER_PASS_ATT_MAC (1<<11) +#define OPEN_ADAPTER_ENABLE_EC (1<<10) +#define OPEN_ADAPTER_CONTENDER (1<<8) +#define OPEN_ADAPTER_PASS_BEACON (1<<7) +#define OPEN_ADAPTER_ENABLE_FDX (1<<6) +#define OPEN_ADAPTER_ENABLE_RPL (1<<5) +#define OPEN_ADAPTER_INHIBIT_ETR (1<<4) +#define OPEN_ADAPTER_INTERNAL_WRAP (1<<3) + + +/* Defines for SRB Commands */ +#define SRB_CLOSE_ADAPTER 0x04 +#define SRB_CONFIGURE_BRIDGE 0x0c +#define SRB_CONFIGURE_HP_CHANNEL 0x13 +#define SRB_MODIFY_BRIDGE_PARMS 0x15 +#define SRB_MODIFY_OPEN_OPTIONS 0x01 +#define SRB_MODIFY_RECEIVE_OPTIONS 0x17 +#define SRB_NO_OPERATION 0x00 +#define SRB_OPEN_ADAPTER 0x03 +#define SRB_READ_LOG 0x08 +#define SRB_READ_SR_COUNTERS 0x16 +#define SRB_RESET_GROUP_ADDRESS 0x02 +#define SRB_RESET_TARGET_SEGMETN 0x14 +#define SRB_SAVE_CONFIGURATION 0x1b +#define SRB_SET_BRIDGE_PARMS 0x09 +#define SRB_SET_FUNC_ADDRESS 0x07 +#define SRB_SET_GROUP_ADDRESS 0x06 +#define SRB_SET_TARGET_SEGMENT 0x05 + +/* Clear return code */ +#define STREAMER_CLEAR_RET_CODE 0xfe + +/* ARB Commands */ +#define ARB_RECEIVE_DATA 0x81 +#define ARB_LAN_CHANGE_STATUS 0x84 + +/* ASB Response commands */ +#define ASB_RECEIVE_DATA 0x81 + + +/* Streamer defaults for buffers */ + +#define STREAMER_RX_RING_SIZE 16 /* should be a power of 2 */ +#define STREAMER_TX_RING_SIZE 8 /* should be a power of 2 */ + +#define PKT_BUF_SZ 4096 /* Default packet size */ + +/* Streamer data structures */ + +struct streamer_tx_desc { + __u32 forward; + __u32 status; + __u32 bufcnt_framelen; + __u32 buffer; + __u32 buflen; + __u32 rsvd1; + __u32 rsvd2; + __u32 rsvd3; +}; + +struct streamer_rx_desc { + __u32 forward; + __u32 status; + __u32 buffer; + __u32 framelen_buflen; +}; + +struct mac_receive_buffer { + __u16 next; + __u8 padding; + __u8 frame_status; + __u16 buffer_length; + __u8 frame_data; +}; + +struct streamer_private { + + __u16 srb; + __u16 trb; + __u16 arb; + __u16 asb; + + __u8 *streamer_mmio; + + volatile int srb_queued; /* True if an SRB is still posted */ + wait_queue_head_t srb_wait; + + volatile int asb_queued; /* True if an ASB is posted */ + + volatile int trb_queued; /* True if a TRB is posted */ + wait_queue_head_t trb_wait; + + struct streamer_rx_desc streamer_rx_ring[STREAMER_RX_RING_SIZE]; + struct streamer_tx_desc streamer_tx_ring[STREAMER_TX_RING_SIZE]; + struct sk_buff *tx_ring_skb[STREAMER_TX_RING_SIZE], + *rx_ring_skb[STREAMER_RX_RING_SIZE]; + int tx_ring_free, tx_ring_last_status, rx_ring_last_received, + free_tx_ring_entries; + + struct net_device_stats streamer_stats; + __u16 streamer_lan_status; + __u8 streamer_ring_speed; + __u16 pkt_buf_sz; + __u8 streamer_receive_options, streamer_copy_all_options, + streamer_message_level; + __u8 streamer_multicast_set; + __u16 streamer_addr_table_addr, streamer_parms_addr; + __u16 mac_rx_buffer; + __u8 streamer_laa[6]; +}; + +struct streamer_adapter_addr_table { + + __u8 node_addr[6]; + __u8 reserved[4]; + __u8 func_addr[4]; +}; + +struct streamer_parameters_table { + + __u8 phys_addr[4]; + __u8 up_node_addr[6]; + __u8 up_phys_addr[4]; + __u8 poll_addr[6]; + __u16 reserved; + __u16 acc_priority; + __u16 auth_source_class; + __u16 att_code; + __u8 source_addr[6]; + __u16 beacon_type; + __u16 major_vector; + __u16 lan_status; + __u16 soft_error_time; + __u16 reserved1; + __u16 local_ring; + __u16 mon_error; + __u16 beacon_transmit; + __u16 beacon_receive; + __u16 frame_correl; + __u8 beacon_naun[6]; + __u32 reserved2; + __u8 beacon_phys[4]; +}; diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index bbca052e7..19615012f 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -1105,7 +1105,7 @@ static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); return 0; case SIOCDEVPRIVATE+2: /* Write the specified MII register */ - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EPERM; mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); return 0; diff --git a/drivers/net/wan/Config.in b/drivers/net/wan/Config.in index bc4bd5331..e0a6f5e8f 100644 --- a/drivers/net/wan/Config.in +++ b/drivers/net/wan/Config.in @@ -16,6 +16,25 @@ if [ "$CONFIG_WAN" = "y" ]; then dep_tristate 'COSA/SRP sync serial boards support' CONFIG_COSA m + # + # COMX drivers + # + + tristate 'MultiGate (COMX) synchronous serial boards support' CONFIG_COMX + if [ "$CONFIG_COMX" != "n" ]; then + dep_tristate ' Support for COMX/CMX/HiCOMX boards' CONFIG_COMX_HW_COMX $CONFIG_COMX + dep_tristate ' Support for LoCOMX board' CONFIG_COMX_HW_LOCOMX $CONFIG_COMX + dep_tristate ' Support for MixCOM board' CONFIG_COMX_HW_MIXCOM $CONFIG_COMX + dep_tristate ' Support for HDLC and syncPPP protocols on MultiGate boards' CONFIG_COMX_PROTO_PPP $CONFIG_COMX + if [ "$CONFIG_LAPB" = "y" ]; then + dep_tristate ' Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_COMX + fi + if [ "$CONFIG_LAPB" = "m" ]; then + dep_tristate ' Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_LAPB + fi + dep_tristate ' Support for Frame Relay on MultiGate boards' CONFIG_COMX_PROTO_FR $CONFIG_COMX + fi + # There is no way to detect a Sealevel board. Force it modular dep_tristate 'Sealevel Systems 4021 support' CONFIG_SEALEVEL_4021 m diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile index 67f6bb59b..b2cd8aafb 100644 --- a/drivers/net/wan/Makefile +++ b/drivers/net/wan/Makefile @@ -49,6 +49,58 @@ else endif endif +ifeq ($(CONFIG_COMX_HW_COMX),y) +L_OBJS += comx-hw-comx.o +else + ifeq ($(CONFIG_COMX_HW_COMX),m) + M_OBJS += comx-hw-comx.o + endif +endif + +ifeq ($(CONFIG_COMX_HW_LOCOMX),y) +L_OBJS += comx-hw-locomx.o +CONFIG_85230_BUILTIN=y +else + ifeq ($(CONFIG_COMX_HW_LOCOMX),m) + M_OBJS += comx-hw-locomx.o + CONFIG_85230_MODULE=y + endif +endif + +ifeq ($(CONFIG_COMX_HW_MIXCOM),y) +L_OBJS += comx-hw-mixcom.o +else + ifeq ($(CONFIG_COMX_HW_MIXCOM),m) + M_OBJS += comx-hw-mixcom.o + endif +endif + +ifeq ($(CONFIG_COMX_PROTO_PPP),y) +L_OBJS += comx-proto-ppp.o +CONFIG_SYNCPPP_BUILTIN = y +else + ifeq ($(CONFIG_COMX_PROTO_PPP),m) + M_OBJS += comx-proto-ppp.o + CONFIG_SYNCPPP_MODULE = y + endif +endif + +ifeq ($(CONFIG_COMX_PROTO_LAPB),y) +L_OBJS += comx-proto-lapb.o +else + ifeq ($(CONFIG_COMX_PROTO_LAPB),m) + M_OBJS += comx-proto-lapb.o + endif +endif + +ifeq ($(CONFIG_COMX_PROTO_FR),y) +L_OBJS += comx-proto-fr.o +else + ifeq ($(CONFIG_COMX_PROTO_FR),m) + M_OBJS += comx-proto-fr.o + endif +endif + ifeq ($(CONFIG_COSA),y) L_OBJS += cosa.o CONFIG_SYNCPPP_BUILTIN = y @@ -180,8 +232,8 @@ clean: rm -f core *.o *.a *.s wanpipe.o: $(WANPIPE_OBJS) - ld -r -o $@ $(WANPIPE_OBJS) + $(LD) -r -o $@ $(WANPIPE_OBJS) cyclomx.o: $(CYCLOMX_OBJS) - ld -r -o $@ $(CYCLOMX_OBJS) + $(LD) -r -o $@ $(CYCLOMX_OBJS) diff --git a/drivers/net/wan/comx-hw-comx.c b/drivers/net/wan/comx-hw-comx.c new file mode 100644 index 000000000..7381dc8a9 --- /dev/null +++ b/drivers/net/wan/comx-hw-comx.c @@ -0,0 +1,1426 @@ +/* + * Hardware-level driver for the COMX and HICOMX cards + * for Linux kernel 2.2.X + * + * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>, + * Peter Bajan <bajan.peter@synergon.hu>, + * Rewritten by: Tivadar Szemethy <tiv@itc.hu> + * Currently maintained by: Gergely Madarasz <gorgo@itc.hu> + * + * Copyright (C) 1995-2000 ITConsult-Pro Co. <info@itc.hu> + * + * 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. + * + * Version 0.80 (99/06/11): + * - port back to kernel, add support builtin driver + * - cleaned up the source code a bit + * + * Version 0.81 (99/06/22): + * - cleaned up the board load functions, no more long reset + * timeouts + * - lower modem lines on close + * - some interrupt handling fixes + * + * Version 0.82 (99/08/24): + * - fix multiple board support + * + * Version 0.83 (99/11/30): + * - interrupt handling and locking fixes during initalization + * - really fix multiple board support + * + * Version 0.84 (99/12/02): + * - some workarounds for problematic hardware/firmware + * + * Version 0.85 (00/01/14): + * - some additional workarounds :/ + * - printk cleanups + */ + +#define VERSION "0.85" + +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/delay.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/netdevice.h> +#include <linux/proc_fs.h> +#include <linux/ioport.h> +#include <linux/init.h> + +#include "comx.h" +#include "comxhw.h" + +MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>, Tivadar Szemethy <tiv@itc.hu>, Arpad Bakay"); +MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n"); + +#define COMX_readw(dev, offset) (readw(dev->mem_start + offset + \ + (unsigned int)(((struct comx_privdata *)\ + ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \ + * COMX_CHANNEL_OFFSET)) + +#define COMX_WRITE(dev, offset, value) (writew(value, dev->mem_start + offset \ + + (unsigned int)(((struct comx_privdata *) \ + ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \ + * COMX_CHANNEL_OFFSET)) + +#define COMX_CMD(dev, cmd) (COMX_WRITE(dev, OFF_A_L2_CMD, cmd)) + +struct comx_firmware { + int len; + unsigned char *data; +}; + +struct comx_privdata { + struct comx_firmware *firmware; + u16 clock; + char channel; // channel no. + int memory_size; + short io_extent; + u_long histogram[5]; +}; + +static struct net_device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000]; +extern struct comx_hardware hicomx_hw; +extern struct comx_hardware comx_hw; +extern struct comx_hardware cmx_hw; + +static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static void COMX_board_on(struct net_device *dev) +{ + outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | + COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr); +} + +static void COMX_board_off(struct net_device *dev) +{ + outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | + COMX_ENABLE_BOARD_IT), dev->base_addr); +} + +static void HICOMX_board_on(struct net_device *dev) +{ + outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | + HICOMX_ENABLE_BOARD_MEM), dev->base_addr); +} + +static void HICOMX_board_off(struct net_device *dev) +{ + outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | + HICOMX_DISABLE_BOARD_MEM), dev->base_addr); +} + +static void COMX_set_clock(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + + COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock); +} + +static struct net_device *COMX_access_board(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct net_device *ret; + int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; + unsigned long flags; + + + save_flags(flags); cli(); + + ret = memory_used[mempos]; + + if(ret == dev) { + goto out; + } + + memory_used[mempos] = dev; + + if (!ch->twin || ret != ch->twin) { + if (ret) ((struct comx_channel *)ret->priv)->HW_board_off(ret); + ch->HW_board_on(dev); + } +out: + restore_flags(flags); + return ret; +} + +static void COMX_release_board(struct net_device *dev, struct net_device *savep) +{ + unsigned long flags; + int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; + struct comx_channel *ch = dev->priv; + + save_flags(flags); cli(); + + if (memory_used[mempos] == savep) { + goto out; + } + + memory_used[mempos] = savep; + if (!ch->twin || ch->twin != savep) { + ch->HW_board_off(dev); + if (savep) ((struct comx_channel*)savep->priv)->HW_board_on(savep); + } +out: + restore_flags(flags); +} + +static int COMX_txe(struct net_device *dev) +{ + struct net_device *savep; + struct comx_channel *ch = dev->priv; + int rc = 0; + + savep = ch->HW_access_board(dev); + if (COMX_readw(dev,OFF_A_L2_LINKUP) == LINKUP_READY) { + rc = COMX_readw(dev,OFF_A_L2_TxEMPTY); + } + ch->HW_release_board(dev,savep); + if(rc==0xffff) { + printk(KERN_ERR "%s, OFF_A_L2_TxEMPTY is %d\n",dev->name, rc); + } + return rc; +} + +static int COMX_send_packet(struct net_device *dev, struct sk_buff *skb) +{ + struct net_device *savep; + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + int ret = FRAME_DROPPED; + word tmp; + + savep = ch->HW_access_board(dev); + + if (ch->debug_flags & DEBUG_HW_TX) { + comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet"); + } + + if (skb->len > COMX_MAX_TX_SIZE) { + ret=FRAME_DROPPED; + goto out; + } + + tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY); + if ((ch->line_status & LINE_UP) && tmp==1) { + int lensave = skb->len; + int dest = COMX_readw(dev, OFF_A_L2_TxBUFP); + word *data = (word *)skb->data; + + if(dest==0xffff) { + printk(KERN_ERR "%s: OFF_A_L2_TxBUFP is %d\n", dev->name, dest); + ret=FRAME_DROPPED; + goto out; + } + + writew((unsigned short)skb->len, dev->mem_start + dest); + dest += 2; + while (skb->len > 1) { + writew(*data++, dev->mem_start + dest); + dest += 2; skb->len -= 2; + } + if (skb->len == 1) { + writew(*((byte *)data), dev->mem_start + dest); + } + writew(0, dev->mem_start + (int)hw->channel * + COMX_CHANNEL_OFFSET + OFF_A_L2_TxEMPTY); + ch->stats.tx_packets++; + ch->stats.tx_bytes += lensave; + ret = FRAME_ACCEPTED; + } else { + ch->stats.tx_dropped++; + printk(KERN_INFO "%s: frame dropped\n",dev->name); + if(tmp) { + printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n",dev->name,tmp); + } + } + +out: + ch->HW_release_board(dev, savep); + dev_kfree_skb(skb); + return ret; +} + +static inline int comx_read_buffer(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + word rbuf_offs; + struct sk_buff *skb; + word len; + int i=0; + word *writeptr; + + i = 0; + rbuf_offs = COMX_readw(dev, OFF_A_L2_RxBUFP); + if(rbuf_offs == 0xffff) { + printk(KERN_ERR "%s: OFF_A_L2_RxBUFP is %d\n",dev->name,rbuf_offs); + return 0; + } + len = readw(dev->mem_start + rbuf_offs); + if(len > COMX_MAX_RX_SIZE) { + printk(KERN_ERR "%s: packet length is %d\n",dev->name,len); + return 0; + } + if ((skb = dev_alloc_skb(len + 16)) == NULL) { + ch->stats.rx_dropped++; + COMX_WRITE(dev, OFF_A_L2_DAV, 0); + return 0; + } + rbuf_offs += 2; + skb_reserve(skb, 16); + skb_put(skb, len); + skb->dev = dev; + writeptr = (word *)skb->data; + while (i < len) { + *writeptr++ = readw(dev->mem_start + rbuf_offs); + rbuf_offs += 2; + i += 2; + } + COMX_WRITE(dev, OFF_A_L2_DAV, 0); + ch->stats.rx_packets++; + ch->stats.rx_bytes += len; + if (ch->debug_flags & DEBUG_HW_RX) { + comx_debug_skb(dev, skb, "COMX_interrupt receiving"); + } + ch->LINE_rx(dev, skb); + return 1; +} + +static inline char comx_line_change(struct net_device *dev, char linestat) +{ + struct comx_channel *ch=dev->priv; + char idle=1; + + + if (linestat & LINE_UP) { /* Vonal fol */ + if (ch->lineup_delay) { + if (!test_and_set_bit(0, &ch->lineup_pending)) { + ch->lineup_timer.function = comx_lineup_func; + ch->lineup_timer.data = (unsigned long)dev; + ch->lineup_timer.expires = jiffies + + HZ*ch->lineup_delay; + add_timer(&ch->lineup_timer); + idle=0; + } + } else { + idle=0; + ch->LINE_status(dev, ch->line_status |= LINE_UP); + } + } else { /* Vonal le */ + idle=0; + if (test_and_clear_bit(0, &ch->lineup_pending)) { + del_timer(&ch->lineup_timer); + } else { + ch->line_status &= ~LINE_UP; + if (ch->LINE_status) { + ch->LINE_status(dev, ch->line_status); + } + } + } + return idle; +} + + + +static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + struct net_device *interrupted; + unsigned long jiffs; + char idle = 0; + int count = 0; + word tmp; + + if (dev == NULL) { + printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq); + return; + } + + jiffs = jiffies; + + interrupted = ch->HW_access_board(dev); + + while (!idle && count < 5000) { + char channel = 0; + idle = 1; + + while (channel < 2) { + char linestat = 0; + char buffers_emptied = 0; + + if (channel == 1) { + if (ch->twin) { + dev = ch->twin; + ch = dev->priv; + hw = ch->HW_privdata; + } else { + break; + } + } else { + COMX_WRITE(dev, OFF_A_L1_REPENA, + COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00); + } + channel++; + + if ((ch->init_status & (HW_OPEN | LINE_OPEN)) != + (HW_OPEN | LINE_OPEN)) { + continue; + } + + /* Collect stats */ + tmp = COMX_readw(dev, OFF_A_L1_ABOREC); + COMX_WRITE(dev, OFF_A_L1_ABOREC, 0); + if(tmp==0xffff) { + printk(KERN_ERR "%s: OFF_A_L1_ABOREC is %d\n",dev->name,tmp); + break; + } else { + ch->stats.rx_missed_errors += (tmp >> 8) & 0xff; + ch->stats.rx_over_errors += tmp & 0xff; + } + tmp = COMX_readw(dev, OFF_A_L1_CRCREC); + COMX_WRITE(dev, OFF_A_L1_CRCREC, 0); + if(tmp==0xffff) { + printk(KERN_ERR "%s: OFF_A_L1_CRCREC is %d\n",dev->name,tmp); + break; + } else { + ch->stats.rx_crc_errors += (tmp >> 8) & 0xff; + ch->stats.rx_missed_errors += tmp & 0xff; + } + + if ((ch->line_status & LINE_UP) && ch->LINE_rx) { + tmp=COMX_readw(dev, OFF_A_L2_DAV); + while (tmp==1) { + idle=0; + buffers_emptied+=comx_read_buffer(dev); + tmp=COMX_readw(dev, OFF_A_L2_DAV); + } + if(tmp) { + printk(KERN_ERR "%s: OFF_A_L2_DAV is %d\n", dev->name, tmp); + break; + } + } + + tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY); + if (tmp==1 && ch->LINE_tx) { + ch->LINE_tx(dev); + } + if(tmp==0xffff) { + printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n", dev->name, tmp); + break; + } + + if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) { + linestat &= ~LINE_UP; + } else { + linestat |= LINE_UP; + } + + if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) { + ch->stats.tx_carrier_errors++; + idle &= comx_line_change(dev,linestat); + } + + hw->histogram[(int)buffers_emptied]++; + } + count++; + } + + if(count==5000) { + printk(KERN_WARNING "%s: interrupt stuck\n",dev->name); + } + + ch->HW_release_board(dev, interrupted); +} + +static int COMX_open(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + struct proc_dir_entry *procfile = ch->procdir->subdir; + unsigned long jiffs; + int twin_open=0; + int retval; + struct net_device *savep; + + if (!dev->base_addr || !dev->irq || !dev->mem_start) { + return -ENODEV; + } + + if (ch->twin && (((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN)) { + twin_open=1; + } + + if (!twin_open) { + if (check_region(dev->base_addr, hw->io_extent)) { + return -EAGAIN; + } + if (request_irq(dev->irq, COMX_interrupt, 0, dev->name, + (void *)dev)) { + printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq); + return -EAGAIN; + } + ch->init_status |= IRQ_ALLOCATED; + request_region(dev->base_addr, hw->io_extent, dev->name); + if (!ch->HW_load_board || ch->HW_load_board(dev)) { + ch->init_status &= ~IRQ_ALLOCATED; + retval=-ENODEV; + goto error; + } + } + + savep = ch->HW_access_board(dev); + COMX_WRITE(dev, OFF_A_L2_LINKUP, 0); + + if (ch->HW_set_clock) { + ch->HW_set_clock(dev); + } + + COMX_CMD(dev, COMX_CMD_INIT); + jiffs = jiffies; + while (COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiffs + HZ) { + schedule_timeout(1); + } + + if (jiffies >= jiffs + HZ) { + printk(KERN_ERR "%s: board timeout on INIT command\n", dev->name); + ch->HW_release_board(dev, savep); + retval=-EIO; + goto error; + } + udelay(1000); + + COMX_CMD(dev, COMX_CMD_OPEN); + + jiffs = jiffies; + while (COMX_readw(dev, OFF_A_L2_LINKUP) != 3 && jiffies < jiffs + HZ) { + schedule_timeout(1); + } + + if (jiffies >= jiffs + HZ) { + printk(KERN_ERR "%s: board timeout on OPEN command\n", dev->name); + ch->HW_release_board(dev, savep); + retval=-EIO; + goto error; + } + + ch->init_status |= HW_OPEN; + + /* Ez eleg ciki, de ilyen a rendszer */ + if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) { + ch->line_status &= ~LINE_UP; + } else { + ch->line_status |= LINE_UP; + } + + if (ch->LINE_status) { + ch->LINE_status(dev, ch->line_status); + } + + ch->HW_release_board(dev, savep); + + for ( ; procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IRQ) == 0 + || strcmp(procfile->name, FILENAME_IO) == 0 + || strcmp(procfile->name, FILENAME_MEMADDR) == 0 + || strcmp(procfile->name, FILENAME_CHANNEL) == 0 + || strcmp(procfile->name, FILENAME_FIRMWARE) == 0 + || strcmp(procfile->name, FILENAME_CLOCK) == 0) { + procfile->mode = S_IFREG | 0444; + + } + } + + return 0; + +error: + if(!twin_open) { + release_region(dev->base_addr, hw->io_extent); + free_irq(dev->irq, (void *)dev); + } + return retval; + +} + +static int COMX_close(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *procfile = ch->procdir->subdir; + struct comx_privdata *hw = ch->HW_privdata; + struct comx_channel *twin_ch; + struct net_device *savep; + + savep = ch->HW_access_board(dev); + + COMX_CMD(dev, COMX_CMD_CLOSE); + udelay(1000); + COMX_CMD(dev, COMX_CMD_EXIT); + + ch->HW_release_board(dev, savep); + + if (ch->init_status & IRQ_ALLOCATED) { + free_irq(dev->irq, (void *)dev); + ch->init_status &= ~IRQ_ALLOCATED; + } + release_region(dev->base_addr, hw->io_extent); + + if (ch->twin && (twin_ch = ch->twin->priv) && + (twin_ch->init_status & HW_OPEN)) { + /* Pass the irq to the twin */ + if (request_irq(dev->irq, COMX_interrupt, 0, ch->twin->name, + (void *)ch->twin) == 0) { + twin_ch->init_status |= IRQ_ALLOCATED; + } + } + + for ( ; procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IRQ) == 0 + || strcmp(procfile->name, FILENAME_IO) == 0 + || strcmp(procfile->name, FILENAME_MEMADDR) == 0 + || strcmp(procfile->name, FILENAME_CHANNEL) == 0 + || strcmp(procfile->name, FILENAME_FIRMWARE) == 0 + || strcmp(procfile->name, FILENAME_CLOCK) == 0) { + procfile->mode = S_IFREG | 0644; + } + } + + ch->init_status &= ~HW_OPEN; + return 0; +} + +static int COMX_statistics(struct net_device *dev, char *page) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + struct net_device *savep; + int len = 0; + + savep = ch->HW_access_board(dev); + + len += sprintf(page + len, "Board data: %s %s %s %s\nPBUFOVR: %02x, " + "MODSTAT: %02x, LINKUP: %02x, DAV: %02x\nRxBUFP: %02x, " + "TxEMPTY: %02x, TxBUFP: %02x\n", + (ch->init_status & HW_OPEN) ? "HW_OPEN" : "", + (ch->init_status & LINE_OPEN) ? "LINE_OPEN" : "", + (ch->init_status & FW_LOADED) ? "FW_LOADED" : "", + (ch->init_status & IRQ_ALLOCATED) ? "IRQ_ALLOCATED" : "", + COMX_readw(dev, OFF_A_L1_PBUFOVR) & 0xff, + (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) & 0xff, + COMX_readw(dev, OFF_A_L2_LINKUP) & 0xff, + COMX_readw(dev, OFF_A_L2_DAV) & 0xff, + COMX_readw(dev, OFF_A_L2_RxBUFP) & 0xff, + COMX_readw(dev, OFF_A_L2_TxEMPTY) & 0xff, + COMX_readw(dev, OFF_A_L2_TxBUFP) & 0xff); + + len += sprintf(page + len, "hist[0]: %8lu hist[1]: %8lu hist[2]: %8lu\n" + "hist[3]: %8lu hist[4]: %8lu\n",hw->histogram[0],hw->histogram[1], + hw->histogram[2],hw->histogram[3],hw->histogram[4]); + + ch->HW_release_board(dev, savep); + + return len; +} + +static int COMX_load_board(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + struct comx_firmware *fw = hw->firmware; + word board_segment = dev->mem_start >> 16; + int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; + unsigned long flags; + unsigned char id1, id2; + struct net_device *saved; + int retval; + int loopcount; + int len; + byte *COMX_address; + + if (!fw || !fw->len) { + struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL; + struct comx_privdata *twin_hw; + + if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) { + return -EAGAIN; + } + + if (!(fw = twin_hw->firmware) || !fw->len) { + return -EAGAIN; + } + } + + id1 = fw->data[OFF_FW_L1_ID]; + id2 = fw->data[OFF_FW_L1_ID + 1]; + + if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_COMX) { + printk(KERN_ERR "%s: incorrect firmware, load aborted\n", + dev->name); + return -EAGAIN; + } + + printk(KERN_INFO "%s: Loading COMX Layer 1 firmware %s\n", dev->name, + (char *)(fw->data + OFF_FW_L1_ID + 2)); + + id1 = fw->data[OFF_FW_L2_ID]; + id2 = fw->data[OFF_FW_L2_ID + 1]; + if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) { + printk(KERN_INFO "with Layer 2 code %s\n", + (char *)(fw->data + OFF_FW_L2_ID + 2)); + } + + outb_p(board_segment | COMX_BOARD_RESET, dev->base_addr); + /* 10 usec should be enough here */ + udelay(100); + + save_flags(flags); cli(); + saved=memory_used[mempos]; + if(saved) { + ((struct comx_channel *)saved->priv)->HW_board_off(saved); + } + memory_used[mempos]=dev; + + outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr); + + writeb(0, dev->mem_start + COMX_JAIL_OFFSET); + + loopcount=0; + while(loopcount++ < 10000 && + readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) { + udelay(100); + } + + if (readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) { + printk(KERN_ERR "%s: Can't reset board, JAIL value is %02x\n", + dev->name, readb(dev->mem_start + COMX_JAIL_OFFSET)); + retval=-ENODEV; + goto out; + } + + writeb(0x55, dev->mem_start + 0x18ff); + + loopcount=0; + while(loopcount++ < 10000 && readb(dev->mem_start + 0x18ff) != 0) { + udelay(100); + } + + if(readb(dev->mem_start + 0x18ff) != 0) { + printk(KERN_ERR "%s: Can't reset board, reset timeout\n", + dev->name); + retval=-ENODEV; + goto out; + } + + len = 0; + COMX_address = (byte *)dev->mem_start; + while (fw->len > len) { + writeb(fw->data[len++], COMX_address++); + } + + len = 0; + COMX_address = (byte *)dev->mem_start; + while (len != fw->len && readb(COMX_address++) == fw->data[len]) { + len++; + } + + if (len != fw->len) { + printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x " + "instead of 0x%02x\n", dev->name, len, + readb(COMX_address - 1), fw->data[len]); + retval=-EAGAIN; + goto out; + } + + writeb(0, dev->mem_start + COMX_JAIL_OFFSET); + + loopcount = 0; + while ( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) { + udelay(100); + } + + if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) { + printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n", + dev->name, COMX_readw(dev, OFF_A_L2_LINKUP)); + retval=-EAGAIN; + goto out; + } + + + ch->init_status |= FW_LOADED; + retval=0; + +out: + outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr); + if(saved) { + ((struct comx_channel *)saved->priv)->HW_board_on(saved); + } + memory_used[mempos]=saved; + restore_flags(flags); + return retval; +} + +static int CMX_load_board(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + struct comx_firmware *fw = hw->firmware; + word board_segment = dev->mem_start >> 16; + int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; + #if 0 + unsigned char id1, id2; + #endif + struct net_device *saved; + unsigned long flags; + int retval; + int loopcount; + int len; + byte *COMX_address; + + if (!fw || !fw->len) { + struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL; + struct comx_privdata *twin_hw; + + if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) { + return -EAGAIN; + } + + if (!(fw = twin_hw->firmware) || !fw->len) { + return -EAGAIN; + } + } + + /* Ide kell olyat tenni, hogy ellenorizze az ID-t */ + + if (inb_p(dev->base_addr) != CMX_ID_BYTE) { + printk(KERN_ERR "%s: CMX id byte is invalid(%02x)\n", dev->name, + inb_p(dev->base_addr)); + return -ENODEV; + } + + printk(KERN_INFO "%s: Loading CMX Layer 1 firmware %s\n", dev->name, + (char *)(fw->data + OFF_FW_L1_ID + 2)); + + save_flags(flags); cli(); + saved=memory_used[mempos]; + if(saved) { + ((struct comx_channel *)saved->priv)->HW_board_off(saved); + } + memory_used[mempos]=dev; + + outb_p(board_segment | COMX_ENABLE_BOARD_MEM | COMX_BOARD_RESET, + dev->base_addr); + + len = 0; + COMX_address = (byte *)dev->mem_start; + while (fw->len > len) { + writeb(fw->data[len++], COMX_address++); + } + + len = 0; + COMX_address = (byte *)dev->mem_start; + while (len != fw->len && readb(COMX_address++) == fw->data[len]) { + len++; + } + + outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr); + + if (len != fw->len) { + printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x " + "instead of 0x%02x\n", dev->name, len, + readb(COMX_address - 1), fw->data[len]); + retval=-EAGAIN; + goto out; + } + + loopcount=0; + while( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) { + udelay(100); + } + + if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) { + printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n", + dev->name, COMX_readw(dev, OFF_A_L2_LINKUP)); + retval=-EAGAIN; + goto out; + } + + ch->init_status |= FW_LOADED; + retval=0; + +out: + outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr); + if(saved) { + ((struct comx_channel *)saved->priv)->HW_board_on(saved); + } + memory_used[mempos]=saved; + restore_flags(flags); + return retval; +} + +static int HICOMX_load_board(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + struct comx_firmware *fw = hw->firmware; + word board_segment = dev->mem_start >> 12; + int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16; + struct net_device *saved; + unsigned char id1, id2; + unsigned long flags; + int retval; + int loopcount; + int len; + word *HICOMX_address; + char id = 1; + + if (!fw || !fw->len) { + struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL; + struct comx_privdata *twin_hw; + + if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) { + return -EAGAIN; + } + + if (!(fw = twin_hw->firmware) || !fw->len) { + return -EAGAIN; + } + } + + while (id != 4) { + if (inb_p(dev->base_addr + id++) != HICOMX_ID_BYTE) { + break; + } + } + + if (id != 4) { + printk(KERN_ERR "%s: can't find HICOMX at 0x%04x, id[%d] = %02x\n", + dev->name, (unsigned int)dev->base_addr, id - 1, + inb_p(dev->base_addr + id - 1)); + return -1; + } + + id1 = fw->data[OFF_FW_L1_ID]; + id2 = fw->data[OFF_FW_L1_ID + 1]; + if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_HICOMX) { + printk(KERN_ERR "%s: incorrect firmware, load aborted\n", dev->name); + return -EAGAIN; + } + + printk(KERN_INFO "%s: Loading HICOMX Layer 1 firmware %s\n", dev->name, + (char *)(fw->data + OFF_FW_L1_ID + 2)); + + id1 = fw->data[OFF_FW_L2_ID]; + id2 = fw->data[OFF_FW_L2_ID + 1]; + if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) { + printk(KERN_INFO "with Layer 2 code %s\n", + (char *)(fw->data + OFF_FW_L2_ID + 2)); + } + + outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr); + udelay(10); + + save_flags(flags); cli(); + saved=memory_used[mempos]; + if(saved) { + ((struct comx_channel *)saved->priv)->HW_board_off(saved); + } + memory_used[mempos]=dev; + + outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr); + outb_p(HICOMX_PRG_MEM, dev->base_addr + 1); + + len = 0; + HICOMX_address = (word *)dev->mem_start; + while (fw->len > len) { + writeb(fw->data[len++], HICOMX_address++); + } + + len = 0; + HICOMX_address = (word *)dev->mem_start; + while (len != fw->len && (readw(HICOMX_address++) & 0xff) == fw->data[len]) { + len++; + } + + if (len != fw->len) { + printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x " + "instead of 0x%02x\n", dev->name, len, + readw(HICOMX_address - 1) & 0xff, fw->data[len]); + retval=-EAGAIN; + goto out; + } + + outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr); + outb_p(HICOMX_DATA_MEM, dev->base_addr + 1); + + outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr); + + loopcount=0; + while(loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1) { + udelay(100); + } + + if ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) { + printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n", + dev->name, COMX_readw(dev, OFF_A_L2_LINKUP)); + retval=-EAGAIN; + goto out; + } + + ch->init_status |= FW_LOADED; + retval=0; + +out: + outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr); + outb_p(HICOMX_DATA_MEM, dev->base_addr + 1); + + if(saved) { + ((struct comx_channel *)saved->priv)->HW_board_on(saved); + } + memory_used[mempos]=saved; + restore_flags(flags); + return retval; +} + +static struct net_device *comx_twin_check(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *procfile = ch->procdir->parent->subdir; + struct comx_privdata *hw = ch->HW_privdata; + + struct net_device *twin; + struct comx_channel *ch_twin; + struct comx_privdata *hw_twin; + + + for ( ; procfile ; procfile = procfile->next) { + + if(!S_ISDIR(procfile->mode)) { + continue; + } + + twin=procfile->data; + ch_twin=twin->priv; + hw_twin=ch_twin->HW_privdata; + + + if (twin != dev && dev->irq && dev->base_addr && dev->mem_start && + dev->irq == twin->irq && dev->base_addr == twin->base_addr && + dev->mem_start == twin->mem_start && + hw->channel == (1 - hw_twin->channel) && + ch->hardware == ch_twin->hardware) { + return twin; + } + } + return NULL; +} + +static int comxhw_write_proc(struct file *file, const char *buffer, + u_long count, void *data) +{ + struct proc_dir_entry *entry = (struct proc_dir_entry *)data; + struct net_device *dev = entry->parent->data; + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + char *page; + + + if (file->f_dentry->d_inode->i_ino != entry->low_ino) { + printk(KERN_ERR "comx_write_proc: file <-> data internal error\n"); + return -EIO; + } + + if(ch->init_status & HW_OPEN) { + return -EAGAIN; + } + + if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) { + if (!(page = (char *)__get_free_page(GFP_KERNEL))) { + return -ENOMEM; + } + copy_from_user(page, buffer, count = (min(count, PAGE_SIZE))); + if (*(page + count - 1) == '\n') { + *(page + count - 1) = 0; + } + } else { + byte *tmp; + + if (!hw->firmware) { + if ((hw->firmware = kmalloc(sizeof(struct comx_firmware), + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + hw->firmware->len = 0; + hw->firmware->data = NULL; + } + + if ((tmp = kmalloc(count + file->f_pos, GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + + /* Ha nem 0 a fpos, akkor meglevo file-t irunk. Gyenge trukk. */ + if (hw->firmware && hw->firmware->len && file->f_pos + && hw->firmware->len < count + file->f_pos) { + memcpy(tmp, hw->firmware->data, hw->firmware->len); + } + if (hw->firmware->data) { + kfree(hw->firmware->data); + } + copy_from_user(tmp + file->f_pos, buffer, count); + hw->firmware->len = entry->size = file->f_pos + count; + hw->firmware->data = tmp; + file->f_pos += count; + return count; + } + + if (strcmp(entry->name, FILENAME_CHANNEL) == 0) { + hw->channel = simple_strtoul(page, NULL, 0); + if (hw->channel >= MAX_CHANNELNO) { + printk(KERN_ERR "Invalid channel number\n"); + hw->channel = 0; + } + if ((ch->twin = comx_twin_check(dev)) != NULL) { + struct comx_channel *twin_ch = ch->twin->priv; + twin_ch->twin = dev; + } + } else if (strcmp(entry->name, FILENAME_IRQ) == 0) { + dev->irq = simple_strtoul(page, NULL, 0); + if (dev->irq == 2) { + dev->irq = 9; + } + if (dev->irq < 3 || dev->irq > 15) { + printk(KERN_ERR "comxhw: Invalid irq number\n"); + dev->irq = 0; + } + if ((ch->twin = comx_twin_check(dev)) != NULL) { + struct comx_channel *twin_ch = ch->twin->priv; + twin_ch->twin = dev; + } + } else if (strcmp(entry->name, FILENAME_IO) == 0) { + dev->base_addr = simple_strtoul(page, NULL, 0); + if ((dev->base_addr & 3) != 0 || dev->base_addr < 0x300 + || dev->base_addr > 0x3fc) { + printk(KERN_ERR "Invalid io value\n"); + dev->base_addr = 0; + } + if ((ch->twin = comx_twin_check(dev)) != NULL) { + struct comx_channel *twin_ch = ch->twin->priv; + + twin_ch->twin = dev; + } + } else if (strcmp(entry->name, FILENAME_MEMADDR) == 0) { + dev->mem_start = simple_strtoul(page, NULL, 0); + if (dev->mem_start <= 0xf000 && dev->mem_start >= 0xa000) { + dev->mem_start *= 16; + } + if ((dev->mem_start & 0xfff) != 0 || dev->mem_start < COMX_MEM_MIN + || dev->mem_start + hw->memory_size > COMX_MEM_MAX) { + printk(KERN_ERR "Invalid memory page\n"); + dev->mem_start = 0; + } + dev->mem_end = dev->mem_start + hw->memory_size; + if ((ch->twin = comx_twin_check(dev)) != NULL) { + struct comx_channel *twin_ch = ch->twin->priv; + + twin_ch->twin = dev; + } + } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) { + if (strncmp("ext", page, 3) == 0) { + hw->clock = 0; + } else { + int kbps; + + kbps = simple_strtoul(page, NULL, 0); + hw->clock = kbps ? COMX_CLOCK_CONST/kbps : 0; + } + } + + free_page((unsigned long)page); + return count; +} + +static int comxhw_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct net_device *dev = file->parent->data; + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + int len = 0; + + + if (strcmp(file->name, FILENAME_IO) == 0) { + len = sprintf(page, "0x%03x\n", (unsigned int)dev->base_addr); + } else if (strcmp(file->name, FILENAME_IRQ) == 0) { + len = sprintf(page, "0x%02x\n", dev->irq == 9 ? 2 : dev->irq); + } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) { + len = sprintf(page, "%01d\n", hw->channel); + } else if (strcmp(file->name, FILENAME_MEMADDR) == 0) { + len = sprintf(page, "0x%05x\n", (unsigned int)dev->mem_start); + } else if (strcmp(file->name, FILENAME_TWIN) == 0) { + len = sprintf(page, "%s\n", ch->twin ? ch->twin->name : "none"); + } else if (strcmp(file->name, FILENAME_CLOCK) == 0) { + if (hw->clock) { + len = sprintf(page, "%-8d\n", COMX_CLOCK_CONST/hw->clock); + } else { + len = sprintf(page, "external\n"); + } + } else if (strcmp(file->name, FILENAME_FIRMWARE) == 0) { + len = min(FILE_PAGESIZE, min(count, + hw->firmware ? (hw->firmware->len - off) : 0)); + if (len < 0) { + len = 0; + } + *start = hw->firmware ? (hw->firmware->data + off) : NULL; + if (off + len >= (hw->firmware ? hw->firmware->len : 0) || len == 0) { + *eof = 1; + } + return len; + } + + if (off >= len) { + *eof = 1; + return 0; + } + + *start = page + off; + if (count >= len - off) { + *eof = 1; + } + return(min(count, len - off)); +} + +/* Called on echo comx >boardtype */ +static int COMX_init(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw; + struct proc_dir_entry *new_file; + + if ((ch->HW_privdata = kmalloc(sizeof(struct comx_privdata), + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + memset(hw = ch->HW_privdata, 0, sizeof(struct comx_privdata)); + + if (ch->hardware == &comx_hw || ch->hardware == &cmx_hw) { + hw->memory_size = COMX_MEMORY_SIZE; + hw->io_extent = COMX_IO_EXTENT; + dev->base_addr = COMX_DEFAULT_IO; + dev->irq = COMX_DEFAULT_IRQ; + dev->mem_start = COMX_DEFAULT_MEMADDR; + dev->mem_end = COMX_DEFAULT_MEMADDR + COMX_MEMORY_SIZE; + } else if (ch->hardware == &hicomx_hw) { + hw->memory_size = HICOMX_MEMORY_SIZE; + hw->io_extent = HICOMX_IO_EXTENT; + dev->base_addr = HICOMX_DEFAULT_IO; + dev->irq = HICOMX_DEFAULT_IRQ; + dev->mem_start = HICOMX_DEFAULT_MEMADDR; + dev->mem_end = HICOMX_DEFAULT_MEMADDR + HICOMX_MEMORY_SIZE; + } else { + printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__); + } + + if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, ch->procdir)) + == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = &comxhw_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->size = 6; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir)) + == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = &comxhw_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->size = 5; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = &comxhw_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->size = 2; // Ezt tudjuk + new_file->nlink = 1; + + if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) { + if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = &comxhw_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->size = 9; + new_file->nlink = 1; + } + + if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = &comxhw_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->size = 8; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = NULL; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &comxhw_read_proc; + new_file->write_proc = &comxhw_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->nlink = 1; + + if (ch->hardware == &comx_hw) { + ch->HW_board_on = COMX_board_on; + ch->HW_board_off = COMX_board_off; + ch->HW_load_board = COMX_load_board; + } else if (ch->hardware == &cmx_hw) { + ch->HW_board_on = COMX_board_on; + ch->HW_board_off = COMX_board_off; + ch->HW_load_board = CMX_load_board; + ch->HW_set_clock = COMX_set_clock; + } else if (ch->hardware == &hicomx_hw) { + ch->HW_board_on = HICOMX_board_on; + ch->HW_board_off = HICOMX_board_off; + ch->HW_load_board = HICOMX_load_board; + ch->HW_set_clock = COMX_set_clock; + } else { + printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__); + } + + ch->HW_access_board = COMX_access_board; + ch->HW_release_board = COMX_release_board; + ch->HW_txe = COMX_txe; + ch->HW_open = COMX_open; + ch->HW_close = COMX_close; + ch->HW_send_packet = COMX_send_packet; + ch->HW_statistics = COMX_statistics; + + if ((ch->twin = comx_twin_check(dev)) != NULL) { + struct comx_channel *twin_ch = ch->twin->priv; + + twin_ch->twin = dev; + } + + MOD_INC_USE_COUNT; + return 0; +} + +/* Called on echo valami >boardtype */ +static int COMX_exit(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_privdata *hw = ch->HW_privdata; + + if (hw->firmware) { + if (hw->firmware->data) kfree(hw->firmware->data); + kfree(hw->firmware); + } if (ch->twin) { + struct comx_channel *twin_ch = ch->twin->priv; + + twin_ch->twin = NULL; + } + + kfree(ch->HW_privdata); + remove_proc_entry(FILENAME_IO, ch->procdir); + remove_proc_entry(FILENAME_IRQ, ch->procdir); + remove_proc_entry(FILENAME_CHANNEL, ch->procdir); + remove_proc_entry(FILENAME_MEMADDR, ch->procdir); + remove_proc_entry(FILENAME_FIRMWARE, ch->procdir); + remove_proc_entry(FILENAME_TWIN, ch->procdir); + if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) { + remove_proc_entry(FILENAME_CLOCK, ch->procdir); + } + + MOD_DEC_USE_COUNT; + return 0; +} + +static int COMX_dump(struct net_device *dev) +{ + printk(KERN_INFO "%s: COMX_dump called, why ?\n", dev->name); + return 0; +} + +static struct comx_hardware comx_hw = { + "comx", + VERSION, + COMX_init, + COMX_exit, + COMX_dump, + NULL +}; + +static struct comx_hardware cmx_hw = { + "cmx", + VERSION, + COMX_init, + COMX_exit, + COMX_dump, + NULL +}; + +static struct comx_hardware hicomx_hw = { + "hicomx", + VERSION, + COMX_init, + COMX_exit, + COMX_dump, + NULL +}; + +#ifdef MODULE +#define comx_hw_comx_init init_module +#endif + +int __init comx_hw_comx_init(void) +{ + comx_register_hardware(&comx_hw); + comx_register_hardware(&cmx_hw); + comx_register_hardware(&hicomx_hw); + memset(memory_used, 0, sizeof(memory_used)); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + comx_unregister_hardware("comx"); + comx_unregister_hardware("cmx"); + comx_unregister_hardware("hicomx"); +} +#endif diff --git a/drivers/net/wan/comx-hw-locomx.c b/drivers/net/wan/comx-hw-locomx.c new file mode 100644 index 000000000..010f02373 --- /dev/null +++ b/drivers/net/wan/comx-hw-locomx.c @@ -0,0 +1,496 @@ +/* + * Hardware driver for the LoCOMX card, using the generic z85230 + * functions + * + * Author: Gergely Madarasz <gorgo@itc.hu> + * + * Based on skeleton code and old LoCOMX driver by Tivadar Szemethy <tiv@itc.hu> + * and the hostess_sv11 driver + * + * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu> + * + * 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. + * + * Version 0.10 (99/06/17): + * - rewritten for the z85230 layer + * + * Version 0.11 (99/06/21): + * - some printk's fixed + * - get rid of a memory leak (it was impossible though :)) + * + * Version 0.12 (99/07/07): + * - check CTS for modem lines, not DCD (which is always high + * in case of this board) + * Version 0.13 (99/07/08): + * - Fix the transmitter status check + * - Handle the net device statistics better + */ + +#define VERSION "0.13" + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/netdevice.h> +#include <linux/proc_fs.h> +#include <asm/types.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/ioport.h> +#include <linux/init.h> + +#include "comx.h" +#include "z85230.h" + +MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>"); +MODULE_DESCRIPTION("Hardware driver for the LoCOMX board"); + +#define RX_DMA 3 +#define TX_DMA 1 +#define LOCOMX_ID 0x33 +#define LOCOMX_IO_EXTENT 8 +#define LOCOMX_DEFAULT_IO 0x368 +#define LOCOMX_DEFAULT_IRQ 7 + +u8 z8530_locomx[] = { + 11, TCRTxCP, + 14, DTRREQ, + 255 +}; + +struct locomx_data { + int io_extent; + struct z8530_dev board; + struct timer_list status_timer; +}; + +static int LOCOMX_txe(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct locomx_data *hw = ch->HW_privdata; + + return (!hw->board.chanA.tx_next_skb); +} + + +static void locomx_rx(struct z8530_channel *c, struct sk_buff *skb) +{ + struct net_device *dev=c->netdevice; + struct comx_channel *ch=dev->priv; + + if (ch->debug_flags & DEBUG_HW_RX) { + comx_debug_skb(dev, skb, "locomx_rx receiving"); + } + ch->LINE_rx(dev,skb); +} + +static int LOCOMX_send_packet(struct net_device *dev, struct sk_buff *skb) +{ + struct comx_channel *ch = (struct comx_channel *)dev->priv; + struct locomx_data *hw = ch->HW_privdata; + + if (ch->debug_flags & DEBUG_HW_TX) { + comx_debug_bytes(dev, skb->data, skb->len, "LOCOMX_send_packet"); + } + + if (!(ch->line_status & LINE_UP)) { + return FRAME_DROPPED; + } + + if(z8530_queue_xmit(&hw->board.chanA,skb)) { + printk(KERN_WARNING "%s: FRAME_DROPPED\n",dev->name); + return FRAME_DROPPED; + } + + if (ch->debug_flags & DEBUG_HW_TX) { + comx_debug(dev, "%s: LOCOMX_send_packet was successful\n\n", dev->name); + } + + if(!hw->board.chanA.tx_next_skb) { + return FRAME_QUEUED; + } else { + return FRAME_ACCEPTED; + } +} + +static void locomx_status_timerfun(unsigned long d) +{ + struct net_device *dev=(struct net_device *)d; + struct comx_channel *ch=dev->priv; + struct locomx_data *hw=ch->HW_privdata; + + if(!(ch->line_status & LINE_UP) && + (hw->board.chanA.status & CTS)) { + ch->LINE_status(dev, ch->line_status | LINE_UP); + } + if((ch->line_status & LINE_UP) && + !(hw->board.chanA.status & CTS)) { + ch->LINE_status(dev, ch->line_status & ~LINE_UP); + } + mod_timer(&hw->status_timer,jiffies + ch->lineup_delay * HZ); +} + + +static int LOCOMX_open(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct locomx_data *hw = ch->HW_privdata; + struct proc_dir_entry *procfile = ch->procdir->subdir; + unsigned long flags; + int ret; + + if (!dev->base_addr || !dev->irq) { + return -ENODEV; + } + + if (check_region(dev->base_addr, hw->io_extent)) { + return -EAGAIN; + } + + request_region(dev->base_addr, hw->io_extent, dev->name); + + hw->board.chanA.ctrlio=dev->base_addr + 5; + hw->board.chanA.dataio=dev->base_addr + 7; + + hw->board.irq=dev->irq; + hw->board.chanA.netdevice=dev; + hw->board.chanA.dev=&hw->board; + hw->board.name=dev->name; + hw->board.chanA.txdma=TX_DMA; + hw->board.chanA.rxdma=RX_DMA; + hw->board.chanA.irqs=&z8530_nop; + hw->board.chanB.irqs=&z8530_nop; + + if(request_irq(dev->irq, z8530_interrupt, SA_INTERRUPT, + dev->name, &hw->board)) { + printk(KERN_ERR "%s: unable to obtain irq %d\n", dev->name, + dev->irq); + ret=-EAGAIN; + goto irq_fail; + } + if(request_dma(TX_DMA,"LoCOMX (TX)")) { + printk(KERN_ERR "%s: unable to obtain TX DMA (DMA channel %d)\n", + dev->name, TX_DMA); + ret=-EAGAIN; + goto dma1_fail; + } + + if(request_dma(RX_DMA,"LoCOMX (RX)")) { + printk(KERN_ERR "%s: unable to obtain RX DMA (DMA channel %d)\n", + dev->name, RX_DMA); + ret=-EAGAIN; + goto dma2_fail; + } + + save_flags(flags); + cli(); + + if(z8530_init(&hw->board)!=0) + { + printk(KERN_ERR "%s: Z8530 device not found.\n",dev->name); + ret=-ENODEV; + goto z8530_fail; + } + + hw->board.chanA.dcdcheck=CTS; + + z8530_channel_load(&hw->board.chanA, z8530_hdlc_kilostream_85230); + z8530_channel_load(&hw->board.chanA, z8530_locomx); + z8530_channel_load(&hw->board.chanB, z8530_dead_port); + + z8530_describe(&hw->board, "I/O", dev->base_addr); + + if((ret=z8530_sync_dma_open(dev, &hw->board.chanA))!=0) { + goto z8530_fail; + } + + restore_flags(flags); + + + hw->board.active=1; + hw->board.chanA.rx_function=locomx_rx; + + ch->init_status |= HW_OPEN; + if (hw->board.chanA.status & DCD) { + ch->line_status |= LINE_UP; + } else { + ch->line_status &= ~LINE_UP; + } + + comx_status(dev, ch->line_status); + + init_timer(&hw->status_timer); + hw->status_timer.function=locomx_status_timerfun; + hw->status_timer.data=(unsigned long)dev; + hw->status_timer.expires=jiffies + ch->lineup_delay * HZ; + add_timer(&hw->status_timer); + + for (; procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IO) == 0 || + strcmp(procfile->name, FILENAME_IRQ) == 0) { + procfile->mode = S_IFREG | 0444; + } + } + return 0; + +z8530_fail: + restore_flags(flags); + free_dma(RX_DMA); +dma2_fail: + free_dma(TX_DMA); +dma1_fail: + free_irq(dev->irq, &hw->board); +irq_fail: + release_region(dev->base_addr, hw->io_extent); + return ret; +} + +static int LOCOMX_close(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct locomx_data *hw = ch->HW_privdata; + struct proc_dir_entry *procfile = ch->procdir->subdir; + + hw->board.chanA.rx_function=z8530_null_rx; + netif_stop_queue(dev); + z8530_sync_dma_close(dev, &hw->board.chanA); + + z8530_shutdown(&hw->board); + + del_timer(&hw->status_timer); + free_dma(RX_DMA); + free_dma(TX_DMA); + free_irq(dev->irq,&hw->board); + release_region(dev->base_addr,8); + + for (; procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IO) == 0 || + strcmp(procfile->name, FILENAME_IRQ) == 0) { + procfile->mode = S_IFREG | 0644; + } + } + + ch->init_status &= ~HW_OPEN; + return 0; +} + +static int LOCOMX_statistics(struct net_device *dev,char *page) +{ + int len = 0; + + len += sprintf(page + len, "Hello\n"); + + return len; +} + +static int LOCOMX_dump(struct net_device *dev) { + printk(KERN_INFO "LOCOMX_dump called\n"); + return(-1); +} + +static int locomx_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct net_device *dev = file->parent->data; + int len = 0; + + if (strcmp(file->name, FILENAME_IO) == 0) { + len = sprintf(page, "0x%x\n", (unsigned int)dev->base_addr); + } else if (strcmp(file->name, FILENAME_IRQ) == 0) { + len = sprintf(page, "%d\n", (unsigned int)dev->irq); + } else { + printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", + file->name); + return -EBADF; + } + + if (off >= len) { + *eof = 1; + return 0; + } + + *start = page + off; + if (count >= len - off) { + *eof = 1; + } + return ( min(count, len - off) ); +} + +static int locomx_write_proc(struct file *file, const char *buffer, + u_long count, void *data) +{ + struct proc_dir_entry *entry = (struct proc_dir_entry *)data; + struct net_device *dev = (struct net_device *)entry->parent->data; + int val; + char *page; + + if (file->f_dentry->d_inode->i_ino != entry->low_ino) { + printk(KERN_ERR "hw_write_proc: file <-> data internal error\n"); + return -EIO; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) { + return -ENOMEM; + } + + copy_from_user(page, buffer, count = min(count, PAGE_SIZE)); + if (*(page + count - 1) == '\n') { + *(page + count - 1) = 0; + } + + if (strcmp(entry->name, FILENAME_IO) == 0) { + val = simple_strtoul(page, NULL, 0); + if (val != 0x360 && val != 0x368 && val != 0x370 && + val != 0x378) { + printk(KERN_ERR "LoCOMX: incorrect io address!\n"); + } else { + dev->base_addr = val; + } + } else if (strcmp(entry->name, FILENAME_IRQ) == 0) { + val = simple_strtoul(page, NULL, 0); + if (val != 3 && val != 4 && val != 5 && val != 6 && val != 7) { + printk(KERN_ERR "LoCOMX: incorrect irq value!\n"); + } else { + dev->irq = val; + } + } else { + printk(KERN_ERR "locomx_write_proc: internal error, filename %s\n", + entry->name); + free_page((unsigned long)page); + return -EBADF; + } + + free_page((unsigned long)page); + return count; +} + + + +static int LOCOMX_init(struct net_device *dev) +{ + struct comx_channel *ch = (struct comx_channel *)dev->priv; + struct locomx_data *hw; + struct proc_dir_entry *new_file; + + /* Alloc data for private structure */ + if ((ch->HW_privdata = kmalloc(sizeof(struct locomx_data), + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + + memset(hw = ch->HW_privdata, 0, sizeof(struct locomx_data)); + hw->io_extent = LOCOMX_IO_EXTENT; + + /* Register /proc files */ + if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &locomx_read_proc; + new_file->write_proc = &locomx_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &locomx_read_proc; + new_file->write_proc = &locomx_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->nlink = 1; + +/* No clock yet */ +/* + if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &locomx_read_proc; + new_file->write_proc = &locomx_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->nlink = 1; +*/ + + ch->HW_access_board = NULL; + ch->HW_release_board = NULL; + ch->HW_txe = LOCOMX_txe; + ch->HW_open = LOCOMX_open; + ch->HW_close = LOCOMX_close; + ch->HW_send_packet = LOCOMX_send_packet; + ch->HW_statistics = LOCOMX_statistics; + ch->HW_set_clock = NULL; + + ch->current_stats = &hw->board.chanA.stats; + memcpy(ch->current_stats, &ch->stats, sizeof(struct net_device_stats)); + + dev->base_addr = LOCOMX_DEFAULT_IO; + dev->irq = LOCOMX_DEFAULT_IRQ; + + + /* O.K. Count one more user on this module */ + MOD_INC_USE_COUNT; + return 0; +} + + +static int LOCOMX_exit(struct net_device *dev) +{ + struct comx_channel *ch = (struct comx_channel *)dev->priv; + + ch->HW_access_board = NULL; + ch->HW_release_board = NULL; + ch->HW_txe = NULL; + ch->HW_open = NULL; + ch->HW_close = NULL; + ch->HW_send_packet = NULL; + ch->HW_statistics = NULL; + ch->HW_set_clock = NULL; + memcpy(&ch->stats, ch->current_stats, sizeof(struct net_device_stats)); + ch->current_stats = &ch->stats; + + kfree(ch->HW_privdata); + + remove_proc_entry(FILENAME_IO, ch->procdir); + remove_proc_entry(FILENAME_IRQ, ch->procdir); +// remove_proc_entry(FILENAME_CLOCK, ch->procdir); + + MOD_DEC_USE_COUNT; + return 0; +} + +static struct comx_hardware locomx_hw = { + "locomx", + VERSION, + LOCOMX_init, + LOCOMX_exit, + LOCOMX_dump, + NULL +}; + +#ifdef MODULE +#define comx_hw_locomx_init init_module +#endif + +int __init comx_hw_locomx_init(void) +{ + comx_register_hardware(&locomx_hw); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + comx_unregister_hardware("locomx"); + return; +} +#endif diff --git a/drivers/net/wan/comx-hw-mixcom.c b/drivers/net/wan/comx-hw-mixcom.c new file mode 100644 index 000000000..552443f88 --- /dev/null +++ b/drivers/net/wan/comx-hw-mixcom.c @@ -0,0 +1,948 @@ +/* + * Hardware driver for the MixCom synchronous serial board + * + * Author: Gergely Madarasz <gorgo@itc.hu> + * + * based on skeleton driver code and a preliminary hscx driver by + * Tivadar Szemethy <tiv@itc.hu> + * + * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu> + * + * 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. + * + * Version 0.60 (99/06/11): + * - ported to the kernel, now works as builtin code + * + * Version 0.61 (99/06/11): + * - recognize the one-channel MixCOM card (id byte = 0x13) + * - printk fixes + * + * Version 0.62 (99/07/15): + * - fixes according to the new hw docs + * - report line status when open + * + * Version 0.63 (99/09/21): + * - line status report fixes + * + * Version 0.64 (99/12/01): + * - some more cosmetical fixes + */ + +#define VERSION "0.64" + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/netdevice.h> +#include <linux/proc_fs.h> +#include <asm/types.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include "comx.h" +#include "mixcom.h" +#include "hscx.h" + +MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>"); +MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board"); + +#define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> \ + HW_privdata)) + +#define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - \ + (1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET) + +#define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + \ + (1 - channel) * MIXCOM_CHANNEL_OFFSET) + +/* Values used to set the IRQ line */ +static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF}; + +static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"}; + +struct mixcom_privdata { + u16 clock; + char channel; + char txbusy; + struct sk_buff *sending; + unsigned tx_ptr; + struct sk_buff *recving; + unsigned rx_ptr; + unsigned char status; + char card_has_status; +}; + +static inline void wr_hscx(struct net_device *dev, int reg, unsigned char val) +{ + outb(val, dev->base_addr + reg); +} + +static inline unsigned char rd_hscx(struct net_device *dev, int reg) +{ + return inb(dev->base_addr + reg); +} + +static inline void hscx_cmd(struct net_device *dev, int cmd) +{ + unsigned long jiffs = jiffies; + unsigned char cec; + unsigned delay = 0; + + while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) && + (jiffs + HZ > jiffies)) { + udelay(1); + if (++delay > (100000 / HZ)) break; + } + if (cec) { + printk(KERN_WARNING "%s: CEC stuck, probably no clock!\n",dev->name); + } else { + wr_hscx(dev, HSCX_CMDR, cmd); + } +} + +static inline void hscx_fill_fifo(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + register word to_send = hw->sending->len - hw->tx_ptr; + + + outsb(dev->base_addr + HSCX_FIFO, + &(hw->sending->data[hw->tx_ptr]), min(to_send, 32)); + if (to_send <= 32) { + hscx_cmd(dev, HSCX_XTF | HSCX_XME); + kfree_skb(hw->sending); + hw->sending = NULL; + hw->tx_ptr = 0; + } else { + hscx_cmd(dev, HSCX_XTF); + hw->tx_ptr += 32; + } +} + +static inline void hscx_empty_fifo(struct net_device *dev, int cnt) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + + if (hw->recving == NULL) { + if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) { + ch->stats.rx_dropped++; + hscx_cmd(dev, HSCX_RHR); + } else { + skb_reserve(hw->recving, 16); + skb_put(hw->recving, HSCX_MTU); + } + hw->rx_ptr = 0; + } + if (cnt > 32 || !cnt || hw->recving == NULL) { + printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %p\n", + cnt, (void *)hw->recving); + return; + } + + insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt); + hw->rx_ptr += cnt; + hscx_cmd(dev, HSCX_RMC); +} + + +static int MIXCOM_txe(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + + return !test_bit(0, &hw->txbusy); +} + +static int mixcom_probe(struct net_device *dev) +{ + unsigned long flags; + int id, vstr, ret=0; + + save_flags(flags); cli(); + + id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET) & 0x7f; + + if (id != MIXCOM_ID ) { + ret=-ENODEV; + printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lx\n",dev->name, dev->base_addr); + goto out; + } + + vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f; + if(vstr>=sizeof(hscx_versions)/sizeof(char*) || + hscx_versions[vstr]==NULL) { + printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)\n",dev->name,dev->base_addr,vstr); + ret = -ENODEV; + } else { + printk(KERN_INFO "%s: HSCX chip version %s\n",dev->name,hscx_versions[vstr]); + ret = 0; + } + +out: + + restore_flags(flags); + return ret; +} + +#if 0 +static void MIXCOM_set_clock(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + + if (hw->clock) { + ; + } else { + ; + } +} +#endif + +static void mixcom_board_on(struct net_device *dev) +{ + outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET); + udelay(1000); + outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON, + MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET); + udelay(1000); +} + +static void mixcom_board_off(struct net_device *dev) +{ + outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET); + udelay(1000); +} + +static void mixcom_off(struct net_device *dev) +{ + wr_hscx(dev, HSCX_CCR1, 0x0); +} + +static void mixcom_on(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + + wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull + wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ ); + wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS ); + wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes + wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN ); + hscx_cmd(dev, HSCX_XRES | HSCX_RHR); + + if (ch->HW_set_clock) ch->HW_set_clock(dev); + +} + +static int MIXCOM_send_packet(struct net_device *dev, struct sk_buff *skb) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + unsigned long flags; + + if (ch->debug_flags & DEBUG_HW_TX) { + comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet"); + } + + if (!(ch->line_status & LINE_UP)) { + return FRAME_DROPPED; + } + + if (skb->len > HSCX_MTU) { + ch->stats.tx_errors++; + return FRAME_ERROR; + } + + save_flags(flags); cli(); + + if (test_and_set_bit(0, &hw->txbusy)) { + printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)\n", dev->name, skb->len); + restore_flags(flags); + return FRAME_DROPPED; + } + + + hw->sending = skb; + hw->tx_ptr = 0; + hw->txbusy = 1; +// atomic_inc(&skb->users); // save it + hscx_fill_fifo(dev); + restore_flags(flags); + + ch->stats.tx_packets++; + ch->stats.tx_bytes += skb->len; + + if (ch->debug_flags & DEBUG_HW_TX) { + comx_debug(dev, "MIXCOM_send_packet was successful\n\n"); + } + + return FRAME_ACCEPTED; +} + +static inline void mixcom_receive_frame(struct net_device *dev) +{ + struct comx_channel *ch=dev->priv; + struct mixcom_privdata *hw=ch->HW_privdata; + register byte rsta; + register word length; + + rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO | + HSCX_CRC | HSCX_RAB); + length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) | + rd_hscx(dev, HSCX_RBCL); + + if ( length > hw->rx_ptr ) { + hscx_empty_fifo(dev, length - hw->rx_ptr); + } + + if (!(rsta & HSCX_VFR)) { + ch->stats.rx_length_errors++; + } + if (rsta & HSCX_RDO) { + ch->stats.rx_over_errors++; + } + if (!(rsta & HSCX_CRC)) { + ch->stats.rx_crc_errors++; + } + if (rsta & HSCX_RAB) { + ch->stats.rx_frame_errors++; + } + ch->stats.rx_packets++; + ch->stats.rx_bytes += length; + + if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) { + skb_trim(hw->recving, hw->rx_ptr - 1); + if (ch->debug_flags & DEBUG_HW_RX) { + comx_debug_skb(dev, hw->recving, + "MIXCOM_interrupt receiving"); + } + hw->recving->dev = dev; + if (ch->LINE_rx) { + ch->LINE_rx(dev, hw->recving); + } + } + else if(hw->recving) { + kfree_skb(hw->recving); + } + hw->recving = NULL; + hw->rx_ptr = 0; +} + + +static inline void mixcom_extended_interrupt(struct net_device *dev) +{ + struct comx_channel *ch=dev->priv; + struct mixcom_privdata *hw=ch->HW_privdata; + register byte exir; + + exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC ); + + if (exir & HSCX_RFO) { + ch->stats.rx_over_errors++; + if (hw->rx_ptr) { + kfree_skb(hw->recving); + hw->recving = NULL; hw->rx_ptr = 0; + } + printk(KERN_ERR "MIXCOM: rx overrun\n"); + hscx_cmd(dev, HSCX_RHR); + } + + if (exir & HSCX_XDU) { // xmit underrun + ch->stats.tx_errors++; + ch->stats.tx_aborted_errors++; + if (hw->tx_ptr) { + kfree_skb(hw->sending); + hw->sending = NULL; + hw->tx_ptr = 0; + } + hscx_cmd(dev, HSCX_XRES); + clear_bit(0, &hw->txbusy); + if (ch->LINE_tx) { + ch->LINE_tx(dev); + } + printk(KERN_ERR "MIXCOM: tx underrun\n"); + } + + if (exir & HSCX_CSC) { + ch->stats.tx_carrier_errors++; + if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le + if (test_and_clear_bit(0, &ch->lineup_pending)) { + del_timer(&ch->lineup_timer); + } else if (ch->line_status & LINE_UP) { + ch->line_status &= ~LINE_UP; + if (ch->LINE_status) { + ch->LINE_status(dev,ch->line_status); + } + } + } + if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) & + HSCX_CTS)) { // Vonal fol + if (!test_and_set_bit(0,&ch->lineup_pending)) { + ch->lineup_timer.function = comx_lineup_func; + ch->lineup_timer.data = (unsigned long)dev; + ch->lineup_timer.expires = jiffies + HZ * + ch->lineup_delay; + add_timer(&ch->lineup_timer); + hscx_cmd(dev, HSCX_XRES); + clear_bit(0, &hw->txbusy); + if (hw->sending) { + kfree_skb(hw->sending); + } + hw->sending=NULL; + hw->tx_ptr = 0; + } + } + } +} + + +static void MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long flags; + struct net_device *dev = (struct net_device *)dev_id; + struct comx_channel *ch, *twin_ch; + struct mixcom_privdata *hw, *twin_hw; + register unsigned char ista; + + if (dev==NULL) { + printk(KERN_ERR "comx_interrupt: irq %d for unknown device\n",irq); + return; + } + + ch = dev->priv; + hw = ch->HW_privdata; + + save_flags(flags); cli(); + + while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF | + HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) { + register byte ista2 = 0; + + if (ista & HSCX_RME) { + mixcom_receive_frame(dev); + } + if (ista & HSCX_RPF) { + hscx_empty_fifo(dev, 32); + } + if (ista & HSCX_XPR) { + if (hw->tx_ptr) { + hscx_fill_fifo(dev); + } else { + clear_bit(0, &hw->txbusy); + ch->LINE_tx(dev); + } + } + + if (ista & HSCX_EXB) { + mixcom_extended_interrupt(dev); + } + + if ((ista & HSCX_EXA) && ch->twin) { + mixcom_extended_interrupt(ch->twin); + } + + if ((ista & HSCX_ICA) && ch->twin && + (ista2 = rd_hscx(ch->twin, HSCX_ISTA) & + (HSCX_RME | HSCX_RPF | HSCX_XPR ))) { + if (ista2 & HSCX_RME) { + mixcom_receive_frame(ch->twin); + } + if (ista2 & HSCX_RPF) { + hscx_empty_fifo(ch->twin, 32); + } + if (ista2 & HSCX_XPR) { + twin_ch=ch->twin->priv; + twin_hw=twin_ch->HW_privdata; + if (twin_hw->tx_ptr) { + hscx_fill_fifo(ch->twin); + } else { + clear_bit(0, &twin_hw->txbusy); + ch->LINE_tx(ch->twin); + } + } + } + } + + restore_flags(flags); + return; +} + +static int MIXCOM_open(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + struct proc_dir_entry *procfile = ch->procdir->subdir; + unsigned long flags; + + if (!dev->base_addr || !dev->irq) return -ENODEV; + + + if(hw->channel==1) { + if(!TWIN(dev) || !(COMX_CHANNEL(TWIN(dev))->init_status & + IRQ_ALLOCATED)) { + printk(KERN_ERR "%s: channel 0 not yet initialized\n",dev->name); + return -EAGAIN; + } + } + + + /* Is our hw present at all ? Not checking for channel 0 if it is already + open */ + if(hw->channel!=0 || !(ch->init_status & IRQ_ALLOCATED)) { + if (check_region(dev->base_addr, MIXCOM_IO_EXTENT)) { + return -EAGAIN; + } + if (mixcom_probe(dev)) { + return -ENODEV; + } + } + + save_flags(flags); cli(); + + if(hw->channel==1) { + request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name); + } + + if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) { + if (request_irq(dev->irq, MIXCOM_interrupt, 0, + dev->name, (void *)dev)) { + printk(KERN_ERR "MIXCOM: unable to obtain irq %d\n", dev->irq); + return -EAGAIN; + } + ch->init_status|=IRQ_ALLOCATED; + request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name); + mixcom_board_on(dev); + } + + mixcom_on(dev); + + + hw->status=inb(MIXCOM_BOARD_BASE(dev) + MIXCOM_STATUS_OFFSET); + if(hw->status != 0xff) { + printk(KERN_DEBUG "%s: board has status register, good\n", dev->name); + hw->card_has_status=1; + } + + hw->txbusy = 0; + ch->init_status |= HW_OPEN; + + if (rd_hscx(dev, HSCX_STAR) & HSCX_CTS) { + ch->line_status |= LINE_UP; + } else { + ch->line_status &= ~LINE_UP; + } + + restore_flags(flags); + + ch->LINE_status(dev, ch->line_status); + + for (; procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IO) == 0 || + strcmp(procfile->name, FILENAME_CHANNEL) == 0 || + strcmp(procfile->name, FILENAME_CLOCK) == 0 || + strcmp(procfile->name, FILENAME_IRQ) == 0) { + procfile->mode = S_IFREG | 0444; + } + } + + return 0; +} + +static int MIXCOM_close(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + struct proc_dir_entry *procfile = ch->procdir->subdir; + unsigned long flags; + + + save_flags(flags); cli(); + + mixcom_off(dev); + + /* This is channel 0, twin is not open, we can safely turn off everything */ + if(hw->channel==0 && (!(TWIN(dev)) || + !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN))) { + mixcom_board_off(dev); + free_irq(dev->irq, dev); + release_region(dev->base_addr, MIXCOM_IO_EXTENT); + ch->init_status &= ~IRQ_ALLOCATED; + } + + /* This is channel 1, channel 0 has already been shutdown, we can release + this one too */ + if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) { + if(COMX_CHANNEL(TWIN(dev))->init_status & IRQ_ALLOCATED) { + mixcom_board_off(TWIN(dev)); + free_irq(TWIN(dev)->irq, TWIN(dev)); + release_region(TWIN(dev)->base_addr, MIXCOM_IO_EXTENT); + COMX_CHANNEL(TWIN(dev))->init_status &= ~IRQ_ALLOCATED; + } + } + + /* the ioports for channel 1 can be safely released */ + if(hw->channel==1) { + release_region(dev->base_addr, MIXCOM_IO_EXTENT); + } + + restore_flags(flags); + + /* If we don't hold any hardware open */ + if(!(ch->init_status & IRQ_ALLOCATED)) { + for (; procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IO) == 0 || + strcmp(procfile->name, FILENAME_CHANNEL) == 0 || + strcmp(procfile->name, FILENAME_CLOCK) == 0 || + strcmp(procfile->name, FILENAME_IRQ) == 0) { + procfile->mode = S_IFREG | 0644; + } + } + } + + /* channel 0 was only waiting for us to close channel 1 + close it completely */ + + if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) { + for (procfile=COMX_CHANNEL(TWIN(dev))->procdir->subdir; + procfile ; procfile = procfile->next) { + if (strcmp(procfile->name, FILENAME_IO) == 0 || + strcmp(procfile->name, FILENAME_CHANNEL) == 0 || + strcmp(procfile->name, FILENAME_CLOCK) == 0 || + strcmp(procfile->name, FILENAME_IRQ) == 0) { + procfile->mode = S_IFREG | 0644; + } + } + } + + ch->init_status &= ~HW_OPEN; + return 0; +} + +static int MIXCOM_statistics(struct net_device *dev,char *page) +{ + struct comx_channel *ch = dev->priv; + // struct mixcom_privdata *hw = ch->HW_privdata; + int len = 0; + + if(ch->init_status && IRQ_ALLOCATED) { + len += sprintf(page + len, "Mixcom board: hardware open\n"); + } + + return len; +} + +static int MIXCOM_dump(struct net_device *dev) { + return 0; +} + +static int mixcom_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct net_device *dev = file->parent->data; + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + int len = 0; + + if (strcmp(file->name, FILENAME_IO) == 0) { + len = sprintf(page, "0x%x\n", + (unsigned int)MIXCOM_BOARD_BASE(dev)); + } else if (strcmp(file->name, FILENAME_IRQ) == 0) { + len = sprintf(page, "%d\n", (unsigned int)dev->irq); + } else if (strcmp(file->name, FILENAME_CLOCK) == 0) { + if (hw->clock) len = sprintf(page, "%d\n", hw->clock); + else len = sprintf(page, "external\n"); + } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) { + len = sprintf(page, "%01d\n", hw->channel); + } else if (strcmp(file->name, FILENAME_TWIN) == 0) { + if (ch->twin) { + len = sprintf(page, "%s\n",ch->twin->name); + } else { + len = sprintf(page, "none\n"); + } + } else { + printk(KERN_ERR "mixcom_read_proc: internal error, filename %s\n", file->name); + return -EBADF; + } + + if (off >= len) { + *eof = 1; + return 0; + } + *start = page + off; + if (count >= len - off) *eof = 1; + return ( min(count, len - off) ); +} + + +static struct net_device *mixcom_twin_check(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *procfile = ch->procdir->parent->subdir; + struct mixcom_privdata *hw = ch->HW_privdata; + + struct net_device *twin; + struct comx_channel *ch_twin; + struct mixcom_privdata *hw_twin; + + + for ( ; procfile ; procfile = procfile->next) { + if(!S_ISDIR(procfile->mode)) continue; + + twin = procfile->data; + ch_twin = twin->priv; + hw_twin = ch_twin->HW_privdata; + + + if (twin != dev && dev->irq && dev->base_addr && + dev->irq == twin->irq && + ch->hardware == ch_twin->hardware && + dev->base_addr == twin->base_addr + + (1-2*hw->channel)*MIXCOM_CHANNEL_OFFSET && + hw->channel == (1 - hw_twin->channel)) { + if (!TWIN(twin) || TWIN(twin)==dev) { + return twin; + } + } + } + return NULL; +} + + +static void setup_twin(struct net_device* dev) +{ + + if(TWIN(dev) && TWIN(TWIN(dev))) { + TWIN(TWIN(dev))=NULL; + } + if ((TWIN(dev) = mixcom_twin_check(dev)) != NULL) { + if (TWIN(TWIN(dev)) && TWIN(TWIN(dev)) != dev) { + TWIN(dev)=NULL; + } else { + TWIN(TWIN(dev))=dev; + } + } +} + +static int mixcom_write_proc(struct file *file, const char *buffer, + u_long count, void *data) +{ + struct proc_dir_entry *entry = (struct proc_dir_entry *)data; + struct net_device *dev = (struct net_device *)entry->parent->data; + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + char *page; + int value; + + if (file->f_dentry->d_inode->i_ino != entry->low_ino) { + printk(KERN_ERR "mixcom_write_proc: file <-> data internal error\n"); + return -EIO; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) { + return -ENOMEM; + } + + copy_from_user(page, buffer, count = min(count, PAGE_SIZE)); + if (*(page + count - 1) == '\n') { + *(page + count - 1) = 0; + } + + if (strcmp(entry->name, FILENAME_IO) == 0) { + value = simple_strtoul(page, NULL, 0); + if (value != 0x180 && value != 0x280 && value != 0x380) { + printk(KERN_ERR "MIXCOM: incorrect io address!\n"); + } else { + dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel); + } + } else if (strcmp(entry->name, FILENAME_IRQ) == 0) { + value = simple_strtoul(page, NULL, 0); + if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) { + printk(KERN_ERR "MIXCOM: incorrect irq value!\n"); + } else { + dev->irq = value; + } + } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) { + if (strncmp("ext", page, 3) == 0) { + hw->clock = 0; + } else { + int kbps; + + kbps = simple_strtoul(page, NULL, 0); + if (!kbps) { + hw->clock = 0; + } else { + hw->clock = kbps; + } + if (hw->clock < 32 || hw->clock > 2000) { + hw->clock = 0; + printk(KERN_ERR "MIXCOM: invalid clock rate!\n"); + } + } + if (ch->init_status & HW_OPEN && ch->HW_set_clock) { + ch->HW_set_clock(dev); + } + } else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) { + value = simple_strtoul(page, NULL, 0); + if (value > 2) { + printk(KERN_ERR "Invalid channel number\n"); + } else { + dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET; + hw->channel = value; + } + } else { + printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", + entry->name); + return -EBADF; + } + + setup_twin(dev); + + free_page((unsigned long)page); + return count; +} + +static int MIXCOM_init(struct net_device *dev) { + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw; + struct proc_dir_entry *new_file; + + if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata), + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + + memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata)); + + if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &mixcom_read_proc; + new_file->write_proc = &mixcom_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &mixcom_read_proc; + new_file->write_proc = &mixcom_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->nlink = 1; + +#if 0 + if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &mixcom_read_proc; + new_file->write_proc = &mixcom_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->nlink = 1; +#endif + + if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &mixcom_read_proc; + new_file->write_proc = &mixcom_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &mixcom_read_proc; + new_file->write_proc = &mixcom_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->nlink = 1; + + setup_twin(dev); + + /* Fill in ch_struct hw specific pointers */ + ch->HW_access_board = NULL; + ch->HW_release_board = NULL; + ch->HW_txe = MIXCOM_txe; + ch->HW_open = MIXCOM_open; + ch->HW_close = MIXCOM_close; + ch->HW_send_packet = MIXCOM_send_packet; + ch->HW_statistics = MIXCOM_statistics; + ch->HW_set_clock = NULL; + + dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0); + dev->irq = MIXCOM_DEFAULT_IRQ; + + MOD_INC_USE_COUNT; + return 0; +} + +static int MIXCOM_exit(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct mixcom_privdata *hw = ch->HW_privdata; + + if(hw->channel==0 && TWIN(dev)) { + return -EBUSY; + } + + if(hw->channel==1 && TWIN(dev)) { + TWIN(TWIN(dev))=NULL; + } + + kfree(ch->HW_privdata); + remove_proc_entry(FILENAME_IO, ch->procdir); + remove_proc_entry(FILENAME_IRQ, ch->procdir); +#if 0 + remove_proc_entry(FILENAME_CLOCK, ch->procdir); +#endif + remove_proc_entry(FILENAME_CHANNEL, ch->procdir); + remove_proc_entry(FILENAME_TWIN, ch->procdir); + + MOD_DEC_USE_COUNT; + return 0; +} + +static struct comx_hardware mixcomhw = { + "mixcom", + VERSION, + MIXCOM_init, + MIXCOM_exit, + MIXCOM_dump, + NULL +}; + +/* Module management */ + +#ifdef MODULE +#define comx_hw_mixcom_init init_module +#endif + +int __init comx_hw_mixcom_init(void) +{ + return(comx_register_hardware(&mixcomhw)); +} + +#ifdef MODULE +void +cleanup_module(void) +{ + comx_unregister_hardware("mixcom"); +} +#endif diff --git a/drivers/net/wan/comx-proto-fr.c b/drivers/net/wan/comx-proto-fr.c new file mode 100644 index 000000000..ad62e310c --- /dev/null +++ b/drivers/net/wan/comx-proto-fr.c @@ -0,0 +1,1006 @@ +/* + * Frame-relay protocol module for the COMX driver + * for Linux 2.2.X + * + * Original author: Tivadar Szemethy <tiv@itc.hu> + * Maintainer: Gergely Madarasz <gorgo@itc.hu> + * + * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu> + * + * 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. + * + * Version 0.70 (99/06/14): + * - cleaned up the source code a bit + * - ported back to kernel, now works as builtin code + * + * Version 0.71 (99/06/25): + * - use skb priorities and queues for sending keepalive + * - use device queues for slave->master data transmit + * - set IFF_RUNNING only line protocol up + * - fixes on slave device flags + * + * Version 0.72 (99/07/09): + * - handle slave tbusy with master tbusy (should be fixed) + * - fix the keepalive timer addition/deletion + */ + +#define VERSION "0.72" + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/netdevice.h> +#include <linux/proc_fs.h> +#include <linux/if_arp.h> +#include <linux/inetdevice.h> +#include <linux/pkt_sched.h> +#include <linux/init.h> +#include <asm/uaccess.h> + +#include "comx.h" +#include "comxhw.h" + +MODULE_AUTHOR("Author: Tivadar Szemethy <tiv@itc.hu>"); +MODULE_DESCRIPTION("Frame Relay protocol implementation for the COMX drivers" + "for Linux kernel 2.2.X"); + +#define FRAD_UI 0x03 +#define NLPID_IP 0xcc +#define NLPID_Q933_LMI 0x08 +#define NLPID_CISCO_LMI 0x09 +#define Q933_ENQ 0x75 +#define Q933_LINESTAT 0x51 +#define Q933_COUNTERS 0x53 + +#define MAXALIVECNT 3 /* No. of failures */ + +struct fr_data { + u16 dlci; + struct net_device *master; + char keepa_pend; + char keepa_freq; + char keepalivecnt, keeploopcnt; + struct timer_list keepa_timer; + u8 local_cnt, remote_cnt; +}; + +static struct comx_protocol fr_master_protocol; +static struct comx_protocol fr_slave_protocol; +static struct comx_hardware fr_dlci; + +static void fr_keepalive_send(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct sk_buff *skb; + u8 *fr_packet; + + skb=alloc_skb(dev->hard_header_len + 13, GFP_ATOMIC); + + if(skb==NULL) + return; + + skb_reserve(skb, dev->hard_header_len); + + fr_packet=(u8*)skb_put(skb, 13); + + fr_packet[0] = (fr->dlci & (1024 - 15)) >> 2; + fr_packet[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1 + fr_packet[2] = FRAD_UI; + fr_packet[3] = NLPID_Q933_LMI; + fr_packet[4] = 0; + fr_packet[5] = Q933_ENQ; + fr_packet[6] = Q933_LINESTAT; + fr_packet[7] = 0x01; + fr_packet[8] = 0x01; + fr_packet[9] = Q933_COUNTERS; + fr_packet[10] = 0x02; + fr_packet[11] = ++fr->local_cnt; + fr_packet[12] = fr->remote_cnt; + + skb->dev = dev; + skb->priority = TC_PRIO_CONTROL; + dev_queue_xmit(skb); +} + +static void fr_keepalive_timerfun(unsigned long d) +{ + struct net_device *dev = (struct net_device *)d; + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct comx_channel *sch; + struct fr_data *sfr; + struct net_device *sdev; + + if (ch->init_status & LINE_OPEN) { + if (fr->keepalivecnt == MAXALIVECNT) { + comx_status(dev, ch->line_status & ~PROTO_UP); + dev->flags &= ~IFF_RUNNING; + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && + (sfr = sch->LINE_privdata) + && (sfr->master == dev) && + (sdev->flags & IFF_UP)) { + sdev->flags &= ~IFF_RUNNING; + comx_status(sdev, + sch->line_status & ~PROTO_UP); + } + } + } + if (fr->keepalivecnt <= MAXALIVECNT) { + ++fr->keepalivecnt; + } + fr_keepalive_send(dev); + } + mod_timer(&fr->keepa_timer, jiffies + HZ * fr->keepa_freq); +} + +static void fr_rx_lmi(struct net_device *dev, struct sk_buff *skb, + u16 dlci, u8 nlpid) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct comx_channel *sch; + struct fr_data *sfr; + struct net_device *sdev; + + if (dlci != fr->dlci || nlpid != NLPID_Q933_LMI || !fr->keepa_freq) { + return; + } + + fr->remote_cnt = skb->data[7]; + if (skb->data[8] == fr->local_cnt) { // keepalive UP! + fr->keepalivecnt = 0; + if ((ch->line_status & LINE_UP) && + !(ch->line_status & PROTO_UP)) { + comx_status(dev, ch->line_status |= PROTO_UP); + dev->flags |= IFF_RUNNING; + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && + (sfr = sch->LINE_privdata) + && (sfr->master == dev) && + (sdev->flags & IFF_UP)) { + sdev->flags |= IFF_RUNNING; + comx_status(sdev, + sch->line_status | PROTO_UP); + } + } + } + } +} + +static void fr_set_keepalive(struct net_device *dev, int keepa) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + + if (!keepa && fr->keepa_freq) { // switch off + fr->keepa_freq = 0; + if (ch->line_status & LINE_UP) { + comx_status(dev, ch->line_status | PROTO_UP); + dev->flags |= IFF_RUNNING; + del_timer(&fr->keepa_timer); + } + return; + } + + if (keepa) { // bekapcs + if(fr->keepa_freq && (ch->line_status & LINE_UP)) { + del_timer(&fr->keepa_timer); + } + fr->keepa_freq = keepa; + fr->local_cnt = fr->remote_cnt = 0; + fr->keepa_timer.expires = jiffies + HZ; + fr->keepa_timer.function = fr_keepalive_timerfun; + fr->keepa_timer.data = (unsigned long)dev; + ch->line_status &= ~(PROTO_UP | PROTO_LOOP); + dev->flags &= ~IFF_RUNNING; + comx_status(dev, ch->line_status); + if(ch->line_status & LINE_UP) { + add_timer(&fr->keepa_timer); + } + } +} + +static void fr_rx(struct net_device *dev, struct sk_buff *skb) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct net_device *sdev = dev; + struct comx_channel *sch; + struct fr_data *sfr; + u16 dlci; + u8 nlpid; + + if(skb->len <= 4 || skb->data[2] != FRAD_UI) { + kfree_skb(skb); + return; + } + + /* Itt majd ki kell talalni, melyik slave kapja a csomagot */ + dlci = ((skb->data[0] & 0xfc) << 2) | ((skb->data[1] & 0xf0) >> 4); + if ((nlpid = skb->data[3]) == 0) { // Optional padding + nlpid = skb->data[4]; + skb_pull(skb, 1); + } + skb_pull(skb, 4); /* DLCI and header throw away */ + + if (ch->debug_flags & DEBUG_COMX_DLCI) { + comx_debug(dev, "Frame received, DLCI: %d, NLPID: 0x%02x\n", + dlci, nlpid); + comx_debug_skb(dev, skb, "Contents"); + } + + /* Megkeressuk, kihez tartozik */ + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && + (sfr->master == dev) && (sfr->dlci == dlci)) { + skb->dev = sdev; + if (ch->debug_flags & DEBUG_COMX_DLCI) { + comx_debug(dev, "Passing it to %s\n",sdev->name); + } + if (dev != sdev) { + sch->stats.rx_packets++; + sch->stats.rx_bytes += skb->len; + } + break; + } + } + switch(nlpid) { + case NLPID_IP: + skb->protocol = htons(ETH_P_IP); + skb->mac.raw = skb->data; + comx_rx(sdev, skb); + break; + case NLPID_Q933_LMI: + fr_rx_lmi(dev, skb, dlci, nlpid); + default: + kfree_skb(skb); + break; + } +} + +static int fr_tx(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct net_device *sdev; + struct comx_channel *sch; + struct fr_data *sfr; + int cnt = 1; + + /* Ha minden igaz, 2 helyen fog allni a tbusy: a masternel, + es annal a slave-nel aki eppen kuldott. + Egy helyen akkor all, ha a master kuldott. + Ez megint jo lesz majd, ha utemezni akarunk */ + + /* This should be fixed, the slave tbusy should be set when + the masters queue is full and reset when not */ + + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && + (sfr->master == dev) && (netif_queue_stopped(sdev))) { + netif_wake_queue(sdev); + cnt++; + } + } + + netif_wake_queue(dev); + return 0; +} + +static void fr_status(struct net_device *dev, unsigned short status) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct net_device *sdev; + struct comx_channel *sch; + struct fr_data *sfr; + + if (status & LINE_UP) { + if (!fr->keepa_freq) { + status |= PROTO_UP; + } + } else { + status &= ~(PROTO_UP | PROTO_LOOP); + } + + if (dev == fr->master && fr->keepa_freq) { + if (status & LINE_UP) { + fr->keepa_timer.expires = jiffies + HZ; + add_timer(&fr->keepa_timer); + fr->keepalivecnt = MAXALIVECNT + 1; + fr->keeploopcnt = 0; + } else { + del_timer(&fr->keepa_timer); + } + } + + /* Itt a status valtozast vegig kell vinni az osszes slave-n */ + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_FRAD || sdev->type == ARPHRD_DLCI) && + (sfr = sch->LINE_privdata) && (sfr->master == dev)) { + if(status & LINE_UP) { + netif_wake_queue(sdev); + } + comx_status(sdev, status); + if(status & (PROTO_UP | PROTO_LOOP)) { + dev->flags |= IFF_RUNNING; + } else { + dev->flags &= ~IFF_RUNNING; + } + } + } +} + +static int fr_open(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct proc_dir_entry *comxdir = ch->procdir; + struct comx_channel *mch; + + if (!(ch->init_status & HW_OPEN)) { + return -ENODEV; + } + + if ((ch->hardware == &fr_dlci && ch->protocol != &fr_slave_protocol) || + (ch->protocol == &fr_slave_protocol && ch->hardware != &fr_dlci)) { + printk(KERN_ERR "Trying to open an improperly set FR interface, giving up\n"); + return -EINVAL; + } + + if (!fr->master) { + return -ENODEV; + } + mch = fr->master->priv; + if (fr->master != dev && (!(mch->init_status & LINE_OPEN) + || (mch->protocol != &fr_master_protocol))) { + printk(KERN_ERR "Master %s is inactive, or incorrectly set up, " + "unable to open %s\n", fr->master->name, dev->name); + return -ENODEV; + } + + ch->init_status |= LINE_OPEN; + ch->line_status &= ~(PROTO_UP | PROTO_LOOP); + dev->flags &= ~IFF_RUNNING; + + if (fr->master == dev) { + if (fr->keepa_freq) { + fr->keepa_timer.function = fr_keepalive_timerfun; + fr->keepa_timer.data = (unsigned long)dev; + add_timer(&fr->keepa_timer); + } else { + if (ch->line_status & LINE_UP) { + ch->line_status |= PROTO_UP; + dev->flags |= IFF_RUNNING; + } + } + } else { + ch->line_status = mch->line_status; + if(fr->master->flags & IFF_RUNNING) { + dev->flags |= IFF_RUNNING; + } + } + + for (; comxdir ; comxdir = comxdir->next) { + if (strcmp(comxdir->name, FILENAME_DLCI) == 0 || + strcmp(comxdir->name, FILENAME_MASTER) == 0 || + strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) { + comxdir->mode = S_IFREG | 0444; + } + } +// comx_status(dev, ch->line_status); + return 0; +} + +static int fr_close(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct proc_dir_entry *comxdir = ch->procdir; + + if (fr->master == dev) { // Ha master + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct net_device *sdev = dev; + struct comx_channel *sch; + struct fr_data *sfr; + + if (!(ch->init_status & HW_OPEN)) { + return -ENODEV; + } + + if (fr->keepa_freq) { + del_timer(&fr->keepa_timer); + } + + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && + (sfr = sch->LINE_privdata) && + (sfr->master == dev) && + (sch->init_status & LINE_OPEN)) { + dev_close(sdev); + } + } + } + + ch->init_status &= ~LINE_OPEN; + ch->line_status &= ~(PROTO_UP | PROTO_LOOP); + dev->flags &= ~IFF_RUNNING; + + for (; comxdir ; comxdir = comxdir->next) { + if (strcmp(comxdir->name, FILENAME_DLCI) == 0 || + strcmp(comxdir->name, FILENAME_MASTER) == 0 || + strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) { + comxdir->mode = S_IFREG | 0444; + } + } + + return 0; +} + +static int fr_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct comx_channel *sch, *mch; + struct fr_data *fr = ch->LINE_privdata; + struct fr_data *sfr; + struct net_device *sdev; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + + if (!fr->master) { + printk(KERN_ERR "BUG: fr_xmit without a master!!! dev: %s\n", dev->name); + return 0; + } + + mch = fr->master->priv; + + /* Ennek majd a slave utemezeskor lesz igazan jelentosege */ + if (ch->debug_flags & DEBUG_COMX_DLCI) { + comx_debug_skb(dev, skb, "Sending frame"); + } + + if (dev != fr->master) { + struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC); + newskb->dev=fr->master; + dev_queue_xmit(newskb); + dev_kfree_skb(skb); + ch->stats.tx_packets++; + ch->stats.tx_bytes += skb->len; + } else { + netif_stop_queue(dev); + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && + (sfr->master == dev) && (netif_queue_stopped(sdev))) { + netif_stop_queue(sdev); + } + } + + switch(mch->HW_send_packet(dev, skb)) { + case FRAME_QUEUED: + netif_wake_queue(dev); + break; + case FRAME_ACCEPTED: + case FRAME_DROPPED: + break; + case FRAME_ERROR: + printk(KERN_ERR "%s: Transmit frame error (len %d)\n", + dev->name, skb->len); + break; + } + } + return 0; +} + +static int fr_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + + skb_push(skb, dev->hard_header_len); + /* Put in DLCI */ + skb->data[0] = (fr->dlci & (1024 - 15)) >> 2; + skb->data[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1 + skb->data[2] = FRAD_UI; + skb->data[3] = NLPID_IP; + + return dev->hard_header_len; +} + +static int fr_statistics(struct net_device *dev, char *page) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + int len = 0; + + if (fr->master == dev) { + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + struct net_device *sdev; + struct comx_channel *sch; + struct fr_data *sfr; + int slaves = 0; + + len += sprintf(page + len, + "This is a Frame Relay master device\nSlaves: "); + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && + (sfr = sch->LINE_privdata) && + (sfr->master == dev) && (sdev != dev)) { + slaves++; + len += sprintf(page + len, "%s ", sdev->name); + } + } + len += sprintf(page + len, "%s\n", slaves ? "" : "(none)"); + if (fr->keepa_freq) { + len += sprintf(page + len, "Line keepalive (value %d) " + "status %s [%d]\n", fr->keepa_freq, + ch->line_status & PROTO_LOOP ? "LOOP" : + ch->line_status & PROTO_UP ? "UP" : "DOWN", + fr->keepalivecnt); + } else { + len += sprintf(page + len, "Line keepalive protocol " + "is not set\n"); + } + } else { // if slave + len += sprintf(page + len, + "This is a Frame Relay slave device, master: %s\n", + fr->master ? fr->master->name : "(not set)"); + } + return len; +} + +static int fr_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct net_device *dev = file->parent->data; + struct comx_channel *ch = dev->priv; + struct fr_data *fr = NULL; + int len = 0; + + if (ch) { + fr = ch->LINE_privdata; + } + + if (strcmp(file->name, FILENAME_DLCI) == 0) { + len = sprintf(page, "%04d\n", fr->dlci); + } else if (strcmp(file->name, FILENAME_MASTER) == 0) { + len = sprintf(page, "%-9s\n", fr->master ? fr->master->name : + "(none)"); + } else if (strcmp(file->name, FILENAME_KEEPALIVE) == 0) { + len = fr->keepa_freq ? sprintf(page, "% 3d\n", fr->keepa_freq) + : sprintf(page, "off\n"); + } else { + printk(KERN_ERR "comxfr: internal error, filename %s\n", file->name); + return -EBADF; + } + + if (off >= len) { + *eof = 1; + return 0; + } + + *start = page + off; + if (count >= len - off) *eof = 1; + return ( min(count, len - off) ); +} + +static int fr_write_proc(struct file *file, const char *buffer, + u_long count, void *data) +{ + struct proc_dir_entry *entry = (struct proc_dir_entry *)data; + struct net_device *dev = entry->parent->data; + struct comx_channel *ch = dev->priv; + struct fr_data *fr = NULL; + char *page; + + if (ch) { + fr = ch->LINE_privdata; + } + + if (file->f_dentry->d_inode->i_ino != entry->low_ino) { + printk(KERN_ERR "comxfr_write_proc: file <-> data internal error\n"); + return -EIO; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) { + return -ENOMEM; + } + + copy_from_user(page, buffer, count); + if (*(page + count - 1) == '\n') { + *(page + count - 1) = 0; + } + + if (strcmp(entry->name, FILENAME_DLCI) == 0) { + u16 dlci_new = simple_strtoul(page, NULL, 10); + + if (dlci_new > 1023) { + printk(KERN_ERR "Invalid DLCI value\n"); + } + else fr->dlci = dlci_new; + } else if (strcmp(entry->name, FILENAME_MASTER) == 0) { + struct net_device *new_master = dev_get_by_name(page); + + if (new_master && new_master->type == ARPHRD_FRAD) { + struct comx_channel *sch = new_master->priv; + struct fr_data *sfr = sch->LINE_privdata; + + if (sfr && sfr->master == new_master) { + if(fr->master) + dev_put(fr->master); + fr->master = new_master; + /* Megorokli a master statuszat */ + ch->line_status = sch->line_status; + } + } + } else if (strcmp(entry->name, FILENAME_KEEPALIVE) == 0) { + int keepa_new = -1; + + if (strcmp(page, KEEPALIVE_OFF) == 0) { + keepa_new = 0; + } else { + keepa_new = simple_strtoul(page, NULL, 10); + } + + if (keepa_new < 0 || keepa_new > 100) { + printk(KERN_ERR "invalid keepalive\n"); + } else { + if (fr->keepa_freq && keepa_new != fr->keepa_freq) { + fr_set_keepalive(dev, 0); + } + if (keepa_new) { + fr_set_keepalive(dev, keepa_new); + } + } + } else { + printk(KERN_ERR "comxfr_write_proc: internal error, filename %s\n", + entry->name); + return -EBADF; + } + + free_page((unsigned long)page); + return count; +} + +static int fr_exit(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + struct net_device *sdev = dev; + struct comx_channel *sch; + struct fr_data *sfr; + struct proc_dir_entry *dir = ch->procdir->parent->subdir; + + /* Ha lezarunk egy master-t, le kell kattintani a slave-eket is */ + if (fr->master && fr->master == dev) { + for (; dir ; dir = dir->next) { + if(!S_ISDIR(dir->mode)) { + continue; + } + if ((sdev = dir->data) && (sch = sdev->priv) && + (sdev->type == ARPHRD_DLCI) && + (sfr = sch->LINE_privdata) && (sfr->master == dev)) { + dev_close(sdev); + sfr->master = NULL; + } + } + } + dev->flags = 0; + dev->type = 0; + dev->mtu = 0; + dev->hard_header_len = 0; + + ch->LINE_rx = NULL; + ch->LINE_tx = NULL; + ch->LINE_status = NULL; + ch->LINE_open = NULL; + ch->LINE_close = NULL; + ch->LINE_xmit = NULL; + ch->LINE_header = NULL; + ch->LINE_rebuild_header = NULL; + ch->LINE_statistics = NULL; + + ch->LINE_status = 0; + + if (fr->master != dev) { // if not master, remove dlci + if(fr->master) + dev_put(fr->master); + remove_proc_entry(FILENAME_DLCI, ch->procdir); + remove_proc_entry(FILENAME_MASTER, ch->procdir); + } else { + if (fr->keepa_freq) { + fr_set_keepalive(dev, 0); + } + remove_proc_entry(FILENAME_KEEPALIVE, ch->procdir); + remove_proc_entry(FILENAME_DLCI, ch->procdir); + } + + kfree(fr); + ch->LINE_privdata = NULL; + + MOD_DEC_USE_COUNT; + return 0; +} + +static int fr_master_init(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr; + struct proc_dir_entry *new_file; + + if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + memset(fr, 0, sizeof(struct fr_data)); + fr->master = dev; // this means master + fr->dlci = 0; // let's say default + + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->type = ARPHRD_FRAD; + dev->mtu = 1500; + dev->hard_header_len = 4; + dev->addr_len = 0; + + ch->LINE_rx = fr_rx; + ch->LINE_tx = fr_tx; + ch->LINE_status = fr_status; + ch->LINE_open = fr_open; + ch->LINE_close = fr_close; + ch->LINE_xmit = fr_xmit; + ch->LINE_header = fr_header; + ch->LINE_rebuild_header = NULL; + ch->LINE_statistics = fr_statistics; + + if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -ENOMEM; + } + new_file->data = (void *)new_file; + new_file->read_proc = &fr_read_proc; + new_file->write_proc = &fr_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->size = 5; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -ENOMEM; + } + new_file->data = (void *)new_file; + new_file->read_proc = &fr_read_proc; + new_file->write_proc = &fr_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->size = 4; + new_file->nlink = 1; + + fr_set_keepalive(dev, 0); + + MOD_INC_USE_COUNT; + return 0; +} + +static int fr_slave_init(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr; + struct proc_dir_entry *new_file; + + if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + memset(fr, 0, sizeof(struct fr_data)); + + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->type = ARPHRD_DLCI; + dev->mtu = 1500; + dev->hard_header_len = 4; + dev->addr_len = 0; + + ch->LINE_rx = fr_rx; + ch->LINE_tx = fr_tx; + ch->LINE_status = fr_status; + ch->LINE_open = fr_open; + ch->LINE_close = fr_close; + ch->LINE_xmit = fr_xmit; + ch->LINE_header = fr_header; + ch->LINE_rebuild_header = NULL; + ch->LINE_statistics = fr_statistics; + + if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -ENOMEM; + } + + new_file->data = (void *)new_file; + new_file->read_proc = &fr_read_proc; + new_file->write_proc = &fr_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->size = 5; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644, + ch->procdir)) == NULL) { + return -EIO; + } + new_file->data = (void *)new_file; + new_file->read_proc = &fr_read_proc; + new_file->write_proc = &fr_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->size = 10; + new_file->nlink = 1; + MOD_INC_USE_COUNT; + return 0; +} + +static int dlci_open(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + + ch->init_status |= HW_OPEN; + + MOD_INC_USE_COUNT; + return 0; +} + +static int dlci_close(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + + ch->init_status &= ~HW_OPEN; + + MOD_DEC_USE_COUNT; + return 0; +} + +static int dlci_txe(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct fr_data *fr = ch->LINE_privdata; + + if (!fr->master) { + return 0; + } + + ch = fr->master->priv; + fr = ch->LINE_privdata; + return ch->HW_txe(fr->master); +} + +static int dlci_statistics(struct net_device *dev, char *page) +{ + return 0; +} + +static int dlci_init(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + + ch->HW_open = dlci_open; + ch->HW_close = dlci_close; + ch->HW_txe = dlci_txe; + ch->HW_statistics = dlci_statistics; + + /* Nincs egyeb hw info, mert ugyis a fr->master-bol fog minden kiderulni */ + + MOD_INC_USE_COUNT; + return 0; +} + +static int dlci_exit(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + + ch->HW_open = NULL; + ch->HW_close = NULL; + ch->HW_txe = NULL; + ch->HW_statistics = NULL; + + MOD_DEC_USE_COUNT; + return 0; +} + +static int dlci_dump(struct net_device *dev) +{ + printk(KERN_INFO "dlci_dump %s, HOGY MI ???\n", dev->name); + return -1; +} + +static struct comx_protocol fr_master_protocol = { + "frad", + VERSION, + ARPHRD_FRAD, + fr_master_init, + fr_exit, + NULL +}; + +static struct comx_protocol fr_slave_protocol = { + "ietf-ip", + VERSION, + ARPHRD_DLCI, + fr_slave_init, + fr_exit, + NULL +}; + +static struct comx_hardware fr_dlci = { + "dlci", + VERSION, + dlci_init, + dlci_exit, + dlci_dump, + NULL +}; + +#ifdef MODULE +#define comx_proto_fr_init init_module +#endif + +int __init comx_proto_fr_init(void) +{ + int ret; + + if ((ret = comx_register_hardware(&fr_dlci))) { + return ret; + } + if ((ret = comx_register_protocol(&fr_master_protocol))) { + return ret; + } + return comx_register_protocol(&fr_slave_protocol); +} + +#ifdef MODULE +void cleanup_module(void) +{ + comx_unregister_hardware(fr_dlci.name); + comx_unregister_protocol(fr_master_protocol.name); + comx_unregister_protocol(fr_slave_protocol.name); +} +#endif /* MODULE */ + diff --git a/drivers/net/wan/comx-proto-lapb.c b/drivers/net/wan/comx-proto-lapb.c new file mode 100644 index 000000000..c0fc0c6bd --- /dev/null +++ b/drivers/net/wan/comx-proto-lapb.c @@ -0,0 +1,548 @@ +/* + * LAPB protocol module for the COMX driver + * for Linux kernel 2.2.X + * + * Original author: Tivadar Szemethy <tiv@itc.hu> + * Maintainer: Gergely Madarasz <gorgo@itc.hu> + * + * Copyright (C) 1997-1999 (C) ITConsult-Pro Co. <info@itc.hu> + * + * 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. + * + * Version 0.80 (99/06/14): + * - cleaned up the source code a bit + * - ported back to kernel, now works as non-module + * + */ + +#define VERSION "0.80" + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/proc_fs.h> +#include <linux/if_arp.h> +#include <linux/inetdevice.h> +#include <asm/uaccess.h> +#include <linux/lapb.h> +#include <linux/init.h> + +#include "comx.h" +#include "comxhw.h" + +static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode, + int size, struct proc_dir_entry *dir); + +static void comxlapb_rx(struct net_device *dev, struct sk_buff *skb) +{ + if (!dev || !dev->priv) { + dev_kfree_skb(skb); + } else { + lapb_data_received(dev->priv, skb); + } +} + +static int comxlapb_tx(struct net_device *dev) +{ + netif_wake_queue(dev); + return 0; +} + +static int comxlapb_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) +{ + return dev->hard_header_len; +} + +static void comxlapb_status(struct net_device *dev, unsigned short status) +{ + struct comx_channel *ch; + + if (!dev || !(ch = dev->priv)) { + return; + } + if (status & LINE_UP) { + netif_wake_queue(dev); + } + comx_status(dev, status); +} + +static int comxlapb_open(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + int err = 0; + + if (!(ch->init_status & HW_OPEN)) { + return -ENODEV; + } + + err = lapb_connect_request(ch); + + if (ch->debug_flags & DEBUG_COMX_LAPB) { + comx_debug(dev, "%s: lapb opened, error code: %d\n", + dev->name, err); + } + + if (!err) { + ch->init_status |= LINE_OPEN; + MOD_INC_USE_COUNT; + } + return err; +} + +static int comxlapb_close(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + + if (!(ch->init_status & HW_OPEN)) { + return -ENODEV; + } + + if (ch->debug_flags & DEBUG_COMX_LAPB) { + comx_debug(dev, "%s: lapb closed\n", dev->name); + } + + lapb_disconnect_request(ch); + + ch->init_status &= ~LINE_OPEN; + ch->line_status &= ~PROTO_UP; + MOD_DEC_USE_COUNT; + return 0; +} + +static int comxlapb_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct sk_buff *skb2; + + if (!dev || !(ch = dev->priv) || !(dev->flags & (IFF_UP | IFF_RUNNING))) { + return -ENODEV; + } + + if (dev->type == ARPHRD_X25) { // first byte tells what to do + switch(skb->data[0]) { + case 0x00: + break; // transmit + case 0x01: + lapb_connect_request(ch); + kfree_skb(skb); + return 0; + case 0x02: + lapb_disconnect_request(ch); + default: + kfree_skb(skb); + return 0; + } + skb_pull(skb,1); + } + + netif_stop_queue(dev); + + if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) { + lapb_data_request(ch, skb2); + } + + return FRAME_ACCEPTED; +} + +static int comxlapb_statistics(struct net_device *dev, char *page) +{ + struct lapb_parms_struct parms; + int len = 0; + + len += sprintf(page + len, "Line status: "); + if (lapb_getparms(dev->priv, &parms) != LAPB_OK) { + len += sprintf(page + len, "not initialized\n"); + return len; + } + len += sprintf(page + len, "%s (%s), T1: %d/%d, T2: %d/%d, N2: %d/%d, " + "window: %d\n", parms.mode & LAPB_DCE ? "DCE" : "DTE", + parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD", + parms.t1timer, parms.t1, parms.t2timer, parms.t2, + parms.n2count, parms.n2, parms.window); + + return len; +} + +static int comxlapb_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct net_device *dev = file->parent->data; + struct lapb_parms_struct parms; + int len = 0; + + if (lapb_getparms(dev->priv, &parms)) { + return -ENODEV; + } + + if (strcmp(file->name, FILENAME_T1) == 0) { + len += sprintf(page + len, "%02u / %02u\n", + parms.t1timer, parms.t1); + } else if (strcmp(file->name, FILENAME_T2) == 0) { + len += sprintf(page + len, "%02u / %02u\n", + parms.t2timer, parms.t2); + } else if (strcmp(file->name, FILENAME_N2) == 0) { + len += sprintf(page + len, "%02u / %02u\n", + parms.n2count, parms.n2); + } else if (strcmp(file->name, FILENAME_WINDOW) == 0) { + len += sprintf(page + len, "%u\n", parms.window); + } else if (strcmp(file->name, FILENAME_MODE) == 0) { + len += sprintf(page + len, "%s, %s\n", + parms.mode & LAPB_DCE ? "DCE" : "DTE", + parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD"); + } else { + printk(KERN_ERR "comxlapb: internal error, filename %s\n", file->name); + return -EBADF; + } + + if (off >= len) { + *eof = 1; + return 0; + } + + *start = page + off; + if (count >= len - off) { + *eof = 1; + } + return ( min(count, len - off) ); +} + +static int comxlapb_write_proc(struct file *file, const char *buffer, + u_long count, void *data) +{ + struct proc_dir_entry *entry = (struct proc_dir_entry *)data; + struct net_device *dev = entry->parent->data; + struct lapb_parms_struct parms; + unsigned long parm; + char *page; + + if (file->f_dentry->d_inode->i_ino != entry->low_ino) { + printk(KERN_ERR "comxlapb_write_proc: file <-> data internal error\n"); + return -EIO; + } + + if (lapb_getparms(dev->priv, &parms)) { + return -ENODEV; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) { + return -ENOMEM; + } + + copy_from_user(page, buffer, count); + if (*(page + count - 1) == '\n') { + *(page + count - 1) = 0; + } + + if (strcmp(entry->name, FILENAME_T1) == 0) { + parm=simple_strtoul(page,NULL,10); + if (parm > 0 && parm < 100) { + parms.t1=parm; + lapb_setparms(dev->priv, &parms); + } + } else if (strcmp(entry->name, FILENAME_T2) == 0) { + parm=simple_strtoul(page, NULL, 10); + if (parm > 0 && parm < 100) { + parms.t2=parm; + lapb_setparms(dev->priv, &parms); + } + } else if (strcmp(entry->name, FILENAME_N2) == 0) { + parm=simple_strtoul(page, NULL, 10); + if (parm > 0 && parm < 100) { + parms.n2=parm; + lapb_setparms(dev->priv, &parms); + } + } else if (strcmp(entry->name, FILENAME_WINDOW) == 0) { + parms.window = simple_strtoul(page, NULL, 10); + lapb_setparms(dev->priv, &parms); + } else if (strcmp(entry->name, FILENAME_MODE) == 0) { + if (comx_strcasecmp(page, "dte") == 0) { + parms.mode &= ~(LAPB_DCE | LAPB_DTE); + parms.mode |= LAPB_DTE; + } else if (comx_strcasecmp(page, "dce") == 0) { + parms.mode &= ~(LAPB_DTE | LAPB_DCE); + parms.mode |= LAPB_DCE; + } else if (comx_strcasecmp(page, "std") == 0 || + comx_strcasecmp(page, "standard") == 0) { + parms.mode &= ~LAPB_EXTENDED; + parms.mode |= LAPB_STANDARD; + } else if (comx_strcasecmp(page, "ext") == 0 || + comx_strcasecmp(page, "extended") == 0) { + parms.mode &= ~LAPB_STANDARD; + parms.mode |= LAPB_EXTENDED; + } + lapb_setparms(dev->priv, &parms); + } else { + printk(KERN_ERR "comxlapb_write_proc: internal error, filename %s\n", + entry->name); + return -EBADF; + } + + free_page((unsigned long)page); + return count; +} + +static void comxlapb_connected(void *token, int reason) +{ + struct comx_channel *ch = token; + struct proc_dir_entry *comxdir = ch->procdir->subdir; + + if (ch->debug_flags & DEBUG_COMX_LAPB) { + comx_debug(ch->dev, "%s: lapb connected, reason: %d\n", + ch->dev->name, reason); + } + + if (ch->dev->type == ARPHRD_X25) { + unsigned char *p; + struct sk_buff *skb; + + if ((skb = dev_alloc_skb(1)) == NULL) { + printk(KERN_ERR "comxlapb: out of memory!\n"); + return; + } + p = skb_put(skb,1); + *p = 0x01; // link established + skb->dev = ch->dev; + skb->protocol = htons(ETH_P_X25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + netif_rx(skb); + } + + for (; comxdir; comxdir = comxdir->next) { + if (strcmp(comxdir->name, FILENAME_MODE) == 0) { + comxdir->mode = S_IFREG | 0444; + } + } + + + ch->line_status |= PROTO_UP; + comx_status(ch->dev, ch->line_status); +} + +static void comxlapb_disconnected(void *token, int reason) +{ + struct comx_channel *ch = token; + struct proc_dir_entry *comxdir = ch->procdir->subdir; + + if (ch->debug_flags & DEBUG_COMX_LAPB) { + comx_debug(ch->dev, "%s: lapb disconnected, reason: %d\n", + ch->dev->name, reason); + } + + if (ch->dev->type == ARPHRD_X25) { + unsigned char *p; + struct sk_buff *skb; + + if ((skb = dev_alloc_skb(1)) == NULL) { + printk(KERN_ERR "comxlapb: out of memory!\n"); + return; + } + p = skb_put(skb,1); + *p = 0x02; // link disconnected + skb->dev = ch->dev; + skb->protocol = htons(ETH_P_X25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + netif_rx(skb); + } + + for (; comxdir; comxdir = comxdir->next) { + if (strcmp(comxdir->name, FILENAME_MODE) == 0) { + comxdir->mode = S_IFREG | 0644; + } + } + + ch->line_status &= ~PROTO_UP; + comx_status(ch->dev, ch->line_status); +} + +static void comxlapb_data_indication(void *token, struct sk_buff *skb) +{ + struct comx_channel *ch = token; + + if (ch->dev->type == ARPHRD_X25) { + skb_push(skb, 1); + skb->data[0] = 0; // indicate data for X25 + skb->protocol = htons(ETH_P_X25); + } else { + skb->protocol = htons(ETH_P_IP); + } + + skb->dev = ch->dev; + skb->mac.raw = skb->data; + comx_rx(ch->dev, skb); +} + +static void comxlapb_data_transmit(void *token, struct sk_buff *skb) +{ + struct comx_channel *ch = token; + + if (ch->HW_send_packet) { + ch->HW_send_packet(ch->dev, skb); + } +} + +static int comxlapb_exit(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + + dev->flags = 0; + dev->type = 0; + dev->mtu = 0; + dev->hard_header_len = 0; + + ch->LINE_rx = NULL; + ch->LINE_tx = NULL; + ch->LINE_status = NULL; + ch->LINE_open = NULL; + ch->LINE_close = NULL; + ch->LINE_xmit = NULL; + ch->LINE_header = NULL; + ch->LINE_statistics = NULL; + + if (ch->debug_flags & DEBUG_COMX_LAPB) { + comx_debug(dev, "%s: unregistering lapb\n", dev->name); + } + lapb_unregister(dev->priv); + + remove_proc_entry(FILENAME_T1, ch->procdir); + remove_proc_entry(FILENAME_T2, ch->procdir); + remove_proc_entry(FILENAME_N2, ch->procdir); + remove_proc_entry(FILENAME_MODE, ch->procdir); + remove_proc_entry(FILENAME_WINDOW, ch->procdir); + + MOD_DEC_USE_COUNT; + return 0; +} + +static int comxlapb_init(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct lapb_register_struct lapbreg; + + dev->mtu = 1500; + dev->hard_header_len = 4; + dev->addr_len = 0; + + ch->LINE_rx = comxlapb_rx; + ch->LINE_tx = comxlapb_tx; + ch->LINE_status = comxlapb_status; + ch->LINE_open = comxlapb_open; + ch->LINE_close = comxlapb_close; + ch->LINE_xmit = comxlapb_xmit; + ch->LINE_header = comxlapb_header; + ch->LINE_statistics = comxlapb_statistics; + + lapbreg.connect_confirmation = comxlapb_connected; + lapbreg.connect_indication = comxlapb_connected; + lapbreg.disconnect_confirmation = comxlapb_disconnected; + lapbreg.disconnect_indication = comxlapb_disconnected; + lapbreg.data_indication = comxlapb_data_indication; + lapbreg.data_transmit = comxlapb_data_transmit; + if (lapb_register(dev->priv, &lapbreg)) { + return -ENOMEM; + } + if (ch->debug_flags & DEBUG_COMX_LAPB) { + comx_debug(dev, "%s: lapb registered\n", dev->name); + } + + if (!create_comxlapb_proc_entry(FILENAME_T1, 0644, 8, ch->procdir)) { + return -ENOMEM; + } + if (!create_comxlapb_proc_entry(FILENAME_T2, 0644, 8, ch->procdir)) { + return -ENOMEM; + } + if (!create_comxlapb_proc_entry(FILENAME_N2, 0644, 8, ch->procdir)) { + return -ENOMEM; + } + if (!create_comxlapb_proc_entry(FILENAME_MODE, 0644, 14, ch->procdir)) { + return -ENOMEM; + } + if (!create_comxlapb_proc_entry(FILENAME_WINDOW, 0644, 0, ch->procdir)) { + return -ENOMEM; + } + + MOD_INC_USE_COUNT; + return 0; +} + +static int comxlapb_init_lapb(struct net_device *dev) +{ + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->type = ARPHRD_LAPB; + + return(comxlapb_init(dev)); +} + +static int comxlapb_init_x25(struct net_device *dev) +{ + dev->flags = IFF_NOARP; + dev->type = ARPHRD_X25; + + return(comxlapb_init(dev)); +} + +static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode, + int size, struct proc_dir_entry *dir) +{ + struct proc_dir_entry *new_file; + + if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) { + new_file->data = (void *)new_file; + new_file->read_proc = &comxlapb_read_proc; + new_file->write_proc = &comxlapb_write_proc; + new_file->proc_iops = &comx_normal_inode_ops; + new_file->size = size; + new_file->nlink = 1; + } + return(new_file); +} + +static struct comx_protocol comxlapb_protocol = { + "lapb", + VERSION, + ARPHRD_LAPB, + comxlapb_init_lapb, + comxlapb_exit, + NULL +}; + +static struct comx_protocol comx25_protocol = { + "x25", + VERSION, + ARPHRD_X25, + comxlapb_init_x25, + comxlapb_exit, + NULL +}; + +#ifdef MODULE +#define comx_proto_lapb_init init_module +#endif + +__initfunc(int comx_proto_lapb_init(void)) +{ + int ret; + + if ((ret = comx_register_protocol(&comxlapb_protocol)) != 0) { + return ret; + } + return comx_register_protocol(&comx25_protocol); +} + +#ifdef MODULE +void cleanup_module(void) +{ + comx_unregister_protocol(comxlapb_protocol.name); + comx_unregister_protocol(comx25_protocol.name); +} +#endif /* MODULE */ + diff --git a/drivers/net/wan/comx-proto-ppp.c b/drivers/net/wan/comx-proto-ppp.c new file mode 100644 index 000000000..0b791685d --- /dev/null +++ b/drivers/net/wan/comx-proto-ppp.c @@ -0,0 +1,269 @@ +/* + * Synchronous PPP / Cisco-HDLC driver for the COMX boards + * + * Author: Gergely Madarasz <gorgo@itc.hu> + * + * based on skeleton code by Tivadar Szemethy <tiv@itc.hu> + * + * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu> + * + * 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. + * + * + * Version 0.10 (99/06/10): + * - written the first code :) + * + * Version 0.20 (99/06/16): + * - added hdlc protocol + * - protocol up is IFF_RUNNING + * + * Version 0.21 (99/07/15): + * - some small fixes with the line status + * + * Version 0.22 (99/08/05): + * - don't test IFF_RUNNING but the pp_link_state of the sppp + * + * Version 0.23 (99/12/02): + * - tbusy fixes + * + */ + +#define VERSION "0.23" + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/netdevice.h> +#include <linux/proc_fs.h> +#include <linux/if_arp.h> +#include <linux/inetdevice.h> +#include <asm/uaccess.h> +#include <linux/init.h> + +#include "syncppp.h" +#include "comx.h" + +MODULE_AUTHOR("Author: Gergely Madarasz <gorgo@itc.hu>"); +MODULE_DESCRIPTION("Cisco-HDLC / Synchronous PPP driver for the COMX sync serial boards"); + +static struct comx_protocol syncppp_protocol; +static struct comx_protocol hdlc_protocol; + +struct syncppp_data { + struct timer_list status_timer; +}; + +static void syncppp_status_timerfun(unsigned long d) { + struct net_device *dev=(struct net_device *)d; + struct comx_channel *ch=dev->priv; + struct syncppp_data *spch=ch->LINE_privdata; + struct sppp *sp = (struct sppp *)sppp_of(dev); + + if(!(ch->line_status & PROTO_UP) && + (sp->pp_link_state==SPPP_LINK_UP)) { + comx_status(dev, ch->line_status | PROTO_UP); + } + if((ch->line_status & PROTO_UP) && + (sp->pp_link_state==SPPP_LINK_DOWN)) { + comx_status(dev, ch->line_status & ~PROTO_UP); + } + mod_timer(&spch->status_timer,jiffies + HZ*3); +} + +static int syncppp_tx(struct net_device *dev) +{ + struct comx_channel *ch=dev->priv; + + if(ch->line_status & LINE_UP) { + netif_wake_queue(dev); + } + return 0; +} + +static void syncppp_status(struct net_device *dev, unsigned short status) +{ + status &= ~(PROTO_UP | PROTO_LOOP); + if(status & LINE_UP) { + netif_wake_queue(dev); + sppp_open(dev); + } else { + /* Line went down */ + netif_stop_queue(dev); + sppp_close(dev); + } + comx_status(dev, status); +} + +static int syncppp_open(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct syncppp_data *spch = ch->LINE_privdata; + + if (!(ch->init_status & HW_OPEN)) return -ENODEV; + + ch->init_status |= LINE_OPEN; + ch->line_status &= ~(PROTO_UP | PROTO_LOOP); + + if(ch->line_status & LINE_UP) { + sppp_open(dev); + } + + init_timer(&spch->status_timer); + spch->status_timer.function=syncppp_status_timerfun; + spch->status_timer.data=(unsigned long)dev; + spch->status_timer.expires=jiffies + HZ*3; + add_timer(&spch->status_timer); + + return 0; +} + +static int syncppp_close(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct syncppp_data *spch = ch->LINE_privdata; + + if (!(ch->init_status & HW_OPEN)) return -ENODEV; + del_timer(&spch->status_timer); + + sppp_close(dev); + + ch->init_status &= ~LINE_OPEN; + ch->line_status &= ~(PROTO_UP | PROTO_LOOP); + + return 0; +} + +static int syncppp_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + + netif_stop_queue(dev); + switch(ch->HW_send_packet(dev, skb)) { + case FRAME_QUEUED: + netif_wake_queue(dev); + break; + case FRAME_ACCEPTED: + case FRAME_DROPPED: + break; + case FRAME_ERROR: + printk(KERN_ERR "%s: Transmit frame error (len %d)\n", + dev->name, skb->len); + break; + } + return 0; +} + + +static int syncppp_statistics(struct net_device *dev, char *page) +{ + int len = 0; + + len += sprintf(page + len, " "); + return len; +} + + +static int syncppp_exit(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + + sppp_detach(dev); + + dev->flags = 0; + dev->type = 0; + dev->mtu = 0; + + ch->LINE_rx = NULL; + ch->LINE_tx = NULL; + ch->LINE_status = NULL; + ch->LINE_open = NULL; + ch->LINE_close = NULL; + ch->LINE_xmit = NULL; + ch->LINE_header = NULL; + ch->LINE_rebuild_header = NULL; + ch->LINE_statistics = NULL; + + kfree(ch->LINE_privdata); + ch->LINE_privdata = NULL; + + MOD_DEC_USE_COUNT; + return 0; +} + +static int syncppp_init(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct ppp_device *pppdev = (struct ppp_device *)ch->if_ptr; + + ch->LINE_privdata = kmalloc(sizeof(struct syncppp_data), GFP_KERNEL); + + pppdev->dev = dev; + sppp_attach(pppdev); + + if(ch->protocol == &hdlc_protocol) { + pppdev->sppp.pp_flags |= PP_CISCO; + dev->type = ARPHRD_HDLC; + } else { + pppdev->sppp.pp_flags &= ~PP_CISCO; + dev->type = ARPHRD_PPP; + } + + ch->LINE_rx = sppp_input; + ch->LINE_tx = syncppp_tx; + ch->LINE_status = syncppp_status; + ch->LINE_open = syncppp_open; + ch->LINE_close = syncppp_close; + ch->LINE_xmit = syncppp_xmit; + ch->LINE_header = NULL; + ch->LINE_statistics = syncppp_statistics; + + + MOD_INC_USE_COUNT; + return 0; +} + +static struct comx_protocol syncppp_protocol = { + "ppp", + VERSION, + ARPHRD_PPP, + syncppp_init, + syncppp_exit, + NULL +}; + +static struct comx_protocol hdlc_protocol = { + "hdlc", + VERSION, + ARPHRD_PPP, + syncppp_init, + syncppp_exit, + NULL +}; + + +#ifdef MODULE +#define comx_proto_ppp_init init_module +#endif + +int __init comx_proto_ppp_init(void) +{ + int ret; + + if(0!=(ret=comx_register_protocol(&hdlc_protocol))) { + return ret; + } + return comx_register_protocol(&syncppp_protocol); +} + +#ifdef MODULE +void cleanup_module(void) +{ + comx_unregister_protocol(syncppp_protocol.name); + comx_unregister_protocol(hdlc_protocol.name); +} +#endif /* MODULE */ + diff --git a/drivers/net/wan/comx.c b/drivers/net/wan/comx.c new file mode 100644 index 000000000..d3ca69e86 --- /dev/null +++ b/drivers/net/wan/comx.c @@ -0,0 +1,1238 @@ +/* + * Device driver framework for the COMX line of synchronous serial boards + * + * for Linux kernel 2.2.X + * + * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>, + * Peter Bajan <bajan.peter@synergon.hu>, + * Previous maintainer: Tivadar Szemethy <tiv@itc.hu> + * Current maintainer: Gergely Madarasz <gorgo@itc.hu> + * + * Copyright (C) 1995-1999 ITConsult-Pro Co. + * + * 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. + * + * Version 0.80 (99/06/11): + * - clean up source code (playing a bit of indent) + * - port back to kernel, add support for non-module versions + * - add support for board resets when channel protocol is down + * - reset the device structure after protocol exit + * the syncppp driver needs it + * - add support for /proc/comx/protocols and + * /proc/comx/boardtypes + * + * Version 0.81 (99/06/21): + * - comment out the board reset support code, the locomx + * driver seems not buggy now + * - printk() levels fixed + * + * Version 0.82 (99/07/08): + * - Handle stats correctly if the lowlevel driver is + * is not a comx one (locomx - z85230) + * + * Version 0.83 (99/07/15): + * - reset line_status when interface is down + * + * Version 0.84 (99/12/01): + * - comx_status should not check for IFF_UP (to report + * line status from dev->open()) + */ + +#define VERSION "0.84" + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/version.h> + +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/netdevice.h> +#include <linux/proc_fs.h> +#include <asm/uaccess.h> +#include <linux/ctype.h> +#include <linux/init.h> + +#ifdef CONFIG_KMOD +#include <linux/kmod.h> +#endif + +#ifndef CONFIG_PROC_FS +#error For now, COMX really needs the /proc filesystem +#endif + +#include "comx.h" +#include "syncppp.h" + +MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>"); +MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters"); + +extern int comx_hw_comx_init(void); +extern int comx_hw_locomx_init(void); +extern int comx_hw_mixcom_init(void); +extern int comx_proto_hdlc_init(void); +extern int comx_proto_ppp_init(void); +extern int comx_proto_syncppp_init(void); +extern int comx_proto_lapb_init(void); +extern int comx_proto_fr_init(void); + +static struct comx_hardware *comx_channels = NULL; +static struct comx_protocol *comx_lines = NULL; + +struct inode_operations comx_normal_inode_ops; +static struct inode_operations comx_root_inode_ops; // for mkdir +static struct inode_operations comx_debug_inode_ops; // mas a file_ops +static struct file_operations comx_normal_file_ops; // with open/relase +static struct file_operations comx_debug_file_ops; // with lseek+read + +static void comx_delete_dentry(struct dentry *dentry); +static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode, + int size, struct proc_dir_entry *dir); + +static void comx_fill_inode(struct inode *inode, int fill); + +static struct dentry_operations comx_dentry_operations = { + NULL, /* revalidate */ + NULL, /* d_hash */ + NULL, /* d_compare */ + &comx_delete_dentry /* d_delete */ +}; + + +struct proc_dir_entry comx_root_dir = { + 0, 4, "comx", + S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, 2, 0, 0, + 0, &comx_root_inode_ops, + NULL, comx_fill_inode, + NULL, &proc_root, NULL +}; + +struct comx_debugflags_struct comx_debugflags[] = { + { "comx_rx", DEBUG_COMX_RX }, + { "comx_tx", DEBUG_COMX_TX }, + { "hw_tx", DEBUG_HW_TX }, + { "hw_rx", DEBUG_HW_RX }, + { "hdlc_keepalive", DEBUG_HDLC_KEEPALIVE }, + { "comxppp", DEBUG_COMX_PPP }, + { "comxlapb", DEBUG_COMX_LAPB }, + { "dlci", DEBUG_COMX_DLCI }, + { NULL, 0 } +}; + +static void comx_fill_inode(struct inode *inode, int fill) +{ + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +} + + +int comx_debug(struct net_device *dev, char *fmt, ...) +{ + struct comx_channel *ch = dev->priv; + char *page,*str; + va_list args; + int len; + + if (!ch->debug_area) return 0; + + if (!(page = (char *)__get_free_page(GFP_ATOMIC))) return -ENOMEM; + + va_start(args, fmt); + len = vsprintf(str = page, fmt, args); + va_end(args); + + if (len >= PAGE_SIZE) { + printk(KERN_ERR "comx_debug: PANIC! len = %d !!!\n", len); + free_page((unsigned long)page); + return -EINVAL; + } + + while (len) { + int to_copy; + int free = (ch->debug_start - ch->debug_end + ch->debug_size) + % ch->debug_size; + + to_copy = min( free ? free : ch->debug_size, + min (ch->debug_size - ch->debug_end, len) ); + memcpy(ch->debug_area + ch->debug_end, str, to_copy); + str += to_copy; + len -= to_copy; + ch->debug_end = (ch->debug_end + to_copy) % ch->debug_size; + if (ch->debug_start == ch->debug_end) // Full ? push start away + ch->debug_start = (ch->debug_start + len + 1) % + ch->debug_size; + ch->debug_file->size = (ch->debug_end - ch->debug_start + + ch->debug_size) % ch->debug_size; + } + + free_page((unsigned long)page); + return 0; +} + +int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg) +{ + struct comx_channel *ch = dev->priv; + + if (!ch->debug_area) return 0; + if (!skb) comx_debug(dev, "%s: %s NULL skb\n\n", dev->name, msg); + if (!skb->len) comx_debug(dev, "%s: %s empty skb\n\n", dev->name, msg); + + return comx_debug_bytes(dev, skb->data, skb->len, msg); +} + +int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len, + char *msg) +{ + int pos = 0; + struct comx_channel *ch = dev->priv; + + if (!ch->debug_area) return 0; + + comx_debug(dev, "%s: %s len %d\n", dev->name, msg, len); + + while (pos != len) { + char line[80]; + int i = 0; + + memset(line, 0, 80); + sprintf(line,"%04d ", pos); + do { + sprintf(line + 5 + (pos % 16) * 3, "%02x", bytes[pos]); + sprintf(line + 60 + (pos % 16), "%c", + isprint(bytes[pos]) ? bytes[pos] : '.'); + pos++; + } while (pos != len && pos % 16); + + while ( i++ != 78 ) if (line[i] == 0) line[i] = ' '; + line[77] = '\n'; + line[78] = 0; + + comx_debug(dev, "%s", line); + } + comx_debug(dev, "\n"); + return 0; +} + +static void comx_loadavg_timerfun(unsigned long d) +{ + struct net_device *dev = (struct net_device *)d; + struct comx_channel *ch = dev->priv; + + ch->avg_bytes[ch->loadavg_counter] = ch->current_stats->rx_bytes; + ch->avg_bytes[ch->loadavg_counter + ch->loadavg_size] = + ch->current_stats->tx_bytes; + + ch->loadavg_counter = (ch->loadavg_counter + 1) % ch->loadavg_size; + + mod_timer(&ch->loadavg_timer,jiffies + HZ * ch->loadavg[0]); +} + +#if 0 +static void comx_reset_timerfun(unsigned long d) +{ + struct net_device *dev = (struct net_device *)d; + struct comx_channel *ch = dev->priv; + + if(!(ch->line_status & (PROTO_LOOP | PROTO_UP))) { + if(test_and_set_bit(0,&ch->reset_pending) && ch->HW_reset) { + ch->HW_reset(dev); + } + } + + mod_timer(&ch->reset_timer, jiffies + HZ * ch->reset_timeout); +} +#endif + +static int comx_open(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *comxdir = ch->procdir->subdir; + int ret=0; + + if (!ch->protocol || !ch->hardware) return -ENODEV; + + if ((ret = ch->HW_open(dev))) return ret; + if ((ret = ch->LINE_open(dev))) { + ch->HW_close(dev); + return ret; + }; + + for (; comxdir ; comxdir = comxdir->next) { + if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 || + strcmp(comxdir->name, FILENAME_PROTOCOL) == 0) + comxdir->mode = S_IFREG | 0444; + } + +#if 0 + ch->reset_pending = 1; + ch->reset_timeout = 30; + ch->reset_timer.function = comx_reset_timerfun; + ch->reset_timer.data = (unsigned long)dev; + ch->reset_timer.expires = jiffies + HZ * ch->reset_timeout; + add_timer(&ch->reset_timer); +#endif + + return 0; +} + +static int comx_close(struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + struct proc_dir_entry *comxdir = ch->procdir->subdir; + int ret = -ENODEV; + + if (test_and_clear_bit(0, &ch->lineup_pending)) { + del_timer(&ch->lineup_timer); + } + +#if 0 + del_timer(&ch->reset_timer); +#endif + + if (ch->init_status & LINE_OPEN && ch->protocol && ch->LINE_close) { + ret = ch->LINE_close(dev); + } + + if (ret) return ret; + + if (ch->init_status & HW_OPEN && ch->hardware && ch->HW_close) { + ret = ch->HW_close(dev); + } + + ch->line_status=0; + + for (; comxdir ; comxdir = comxdir->next) { + if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 || + strcmp(comxdir->name, FILENAME_PROTOCOL) == 0) + comxdir->mode = S_IFREG | 0644; + } + + return ret; +} + +void comx_status(struct net_device *dev, int status) +{ + struct comx_channel *ch = dev->priv; + +#if 0 + if(status & (PROTO_UP | PROTO_LOOP)) { + clear_bit(0,&ch->reset_pending); + } +#endif + + printk(KERN_NOTICE "Interface %s: modem status %s, line protocol %s\n", + dev->name, status & LINE_UP ? "UP" : "DOWN", + status & PROTO_LOOP ? "LOOP" : status & PROTO_UP ? + "UP" : "DOWN"); + + ch->line_status = status; +} + +static int comx_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct comx_channel *ch = dev->priv; + int rc; + + if (skb->len > dev->mtu + dev->hard_header_len) { + printk(KERN_ERR "comx_xmit: %s: skb->len %d > dev->mtu %d\n", dev->name, + (int)skb->len, dev->mtu); + } + + if (ch->debug_flags & DEBUG_COMX_TX) { + comx_debug_skb(dev, skb, "comx_xmit skb"); + } + + rc=ch->LINE_xmit(skb, dev); +// if (!rc) dev_kfree_skb(skb); + + return rc; +} + +static int comx_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) +{ + struct comx_channel *ch = dev->priv; + + if (ch->LINE_header) { + return (ch->LINE_header(skb, dev, type, daddr, saddr, len)); + } else { + return 0; + } +} + +static int comx_rebuild_header(struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + struct comx_channel *ch = dev->priv; + + if (ch->LINE_rebuild_header) { + return(ch->LINE_rebuild_header(skb)); + } else { + return 0; + } +} + +int comx_rx(struct net_device *dev, struct sk_buff *skb) +{ + struct comx_channel *ch = dev->priv; + + if (ch->debug_flags & DEBUG_COMX_RX) { + comx_debug_skb(dev, skb, "comx_rx skb"); + } + if (skb) { + netif_rx(skb); + } + return 0; +} + +static struct net_device_stats *comx_stats(struct net_device *dev) +{ + struct comx_channel *ch = (struct comx_channel *)dev->priv; + + return ch->current_stats; +} + +void comx_lineup_func(unsigned long d) +{ + struct net_device *dev = (struct net_device *)d; + struct comx_channel *ch = dev->priv; + + del_timer(&ch->lineup_timer); + clear_bit(0, &ch->lineup_pending); + + if (ch->LINE_status) { + ch->LINE_status(dev, ch->line_status |= LINE_UP); + } +} + +#define LOADAVG(avg, off) (int) \ + ((ch->avg_bytes[(ch->loadavg_counter - 1 + ch->loadavg_size * 2) \ + % ch->loadavg_size + off] - ch->avg_bytes[(ch->loadavg_counter - 1 \ + - ch->loadavg[avg] / ch->loadavg[0] + ch->loadavg_size * 2) \ + % ch->loadavg_size + off]) / ch->loadavg[avg] * 8) + +static int comx_statistics(struct net_device *dev, char *page) +{ + struct comx_channel *ch = dev->priv; + int len = 0; + int tmp; + int i = 0; + char tmpstr[20]; + int tmpstrlen = 0; + + len += sprintf(page + len, "Interface administrative status is %s, " + "modem status is %s, protocol is %s\n", + dev->flags & IFF_UP ? "UP" : "DOWN", + ch->line_status & LINE_UP ? "UP" : "DOWN", + ch->line_status & PROTO_LOOP ? "LOOP" : + ch->line_status & PROTO_UP ? "UP" : "DOWN"); + len += sprintf(page + len, "Modem status changes: %lu, Transmitter status " + "is %s, tbusy: %d\n", ch->current_stats->tx_carrier_errors, ch->HW_txe ? + ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", (int)dev->tbusy); + len += sprintf(page + len, "Interface load (input): %d / %d / %d bits/s (", + LOADAVG(0,0), LOADAVG(1, 0), LOADAVG(2, 0)); + tmpstr[0] = 0; + for (i=0; i != 3; i++) { + char tf; + + tf = ch->loadavg[i] % 60 == 0 && + ch->loadavg[i] / 60 > 0 ? 'm' : 's'; + tmpstrlen += sprintf(tmpstr + tmpstrlen, "%d%c%s", + ch->loadavg[i] / (tf == 'm' ? 60 : 1), tf, + i == 2 ? ")\n" : "/"); + } + len += sprintf(page + len, + "%s (output): %d / %d / %d bits/s (%s", tmpstr, + LOADAVG(0,ch->loadavg_size), LOADAVG(1, ch->loadavg_size), + LOADAVG(2, ch->loadavg_size), tmpstr); + + len += sprintf(page + len, "Debug flags: "); + tmp = len; i = 0; + while (comx_debugflags[i].name) { + if (ch->debug_flags & comx_debugflags[i].value) + len += sprintf(page + len, "%s ", + comx_debugflags[i].name); + i++; + } + len += sprintf(page + len, "%s\n", tmp == len ? "none" : ""); + + len += sprintf(page + len, "RX errors: len: %lu, overrun: %lu, crc: %lu, " + "aborts: %lu\n buffer overrun: %lu, pbuffer overrun: %lu\n" + "TX errors: underrun: %lu\n", + ch->current_stats->rx_length_errors, ch->current_stats->rx_over_errors, + ch->current_stats->rx_crc_errors, ch->current_stats->rx_frame_errors, + ch->current_stats->rx_missed_errors, ch->current_stats->rx_fifo_errors, + ch->current_stats->tx_fifo_errors); + + if (ch->LINE_statistics && (ch->init_status & LINE_OPEN)) { + len += ch->LINE_statistics(dev, page + len); + } else { + len += sprintf(page+len, "Line status: driver not initialized\n"); + } + if (ch->HW_statistics && (ch->init_status & HW_OPEN)) { + len += ch->HW_statistics(dev, page + len); + } else { + len += sprintf(page+len, "Board status: driver not initialized\n"); + } + + return len; +} + +static int comx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct comx_channel *ch = dev->priv; + + if (ch->LINE_ioctl) { + return(ch->LINE_ioctl(dev, ifr, cmd)); + } + return -EINVAL; +} + +static void comx_reset_dev(struct net_device *dev) +{ + dev->open = comx_open; + dev->stop = comx_close; + dev->hard_start_xmit = comx_xmit; + dev->hard_header = comx_header; + dev->rebuild_header = comx_rebuild_header; + dev->get_stats = comx_stats; + dev->do_ioctl = comx_ioctl; + dev->change_mtu = NULL; + dev->tx_queue_len = 20; + dev->flags = IFF_NOARP; +} + +static int comx_init_dev(struct net_device *dev) +{ + struct comx_channel *ch; + + if ((ch = kmalloc(sizeof(struct comx_channel), GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + memset(ch, 0, sizeof(struct comx_channel)); + + ch->loadavg[0] = 5; + ch->loadavg[1] = 300; + ch->loadavg[2] = 900; + ch->loadavg_size = ch->loadavg[2] / ch->loadavg[0] + 1; + if ((ch->avg_bytes = kmalloc(ch->loadavg_size * + sizeof(unsigned long) * 2, GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + + memset(ch->avg_bytes, 0, ch->loadavg_size * sizeof(unsigned long) * 2); + ch->loadavg_counter = 0; + ch->loadavg_timer.function = comx_loadavg_timerfun; + ch->loadavg_timer.data = (unsigned long)dev; + ch->loadavg_timer.expires = jiffies + HZ * ch->loadavg[0]; + add_timer(&ch->loadavg_timer); + + dev->priv = (void *)ch; + ch->dev = dev; + ch->line_status &= ~LINE_UP; + + ch->current_stats = &ch->stats; + + comx_reset_dev(dev); + return 0; +} + +static int comx_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct net_device *dev = file->parent->data; + struct comx_channel *ch=(struct comx_channel *)dev->priv; + int len = 0; + + if (strcmp(file->name, FILENAME_STATUS) == 0) { + len = comx_statistics(dev, page); + } else if (strcmp(file->name, FILENAME_HARDWARE) == 0) { + len = sprintf(page, "%s\n", ch->hardware ? + ch->hardware->name : HWNAME_NONE); + } else if (strcmp(file->name, FILENAME_PROTOCOL) == 0) { + len = sprintf(page, "%s\n", ch->protocol ? + ch->protocol->name : PROTONAME_NONE); + } else if (strcmp(file->name, FILENAME_LINEUPDELAY) == 0) { + len = sprintf(page, "%01d\n", ch->lineup_delay); + } + + if (off >= len) { + *eof = 1; + return 0; + } + + *start = page + off; + if (count >= len - off) { + *eof = 1; + } + return( min(count, len - off) ); +} + + +static int comx_root_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct proc_dir_entry *file = (struct proc_dir_entry *)data; + struct comx_hardware *hw; + struct comx_protocol *line; + + int len = 0; + + if (strcmp(file->name, FILENAME_HARDWARELIST) == 0) { + for(hw=comx_channels;hw;hw=hw->next) + len+=sprintf(page+len, "%s\n", hw->name); + } else if (strcmp(file->name, FILENAME_PROTOCOLLIST) == 0) { + for(line=comx_lines;line;line=line->next) + len+=sprintf(page+len, "%s\n", line->name); + } + + if (off >= len) { + *eof = 1; + return 0; + } + + *start = page + off; + if (count >= len - off) { + *eof = 1; + } + return( min(count, len - off) ); +} + + + +static int comx_write_proc(struct file *file, const char *buffer, u_long count, + void *data) +{ + struct proc_dir_entry *entry = (struct proc_dir_entry *)data; + struct net_device *dev = (struct net_device *)entry->parent->data; + struct comx_channel *ch=(struct comx_channel *)dev->priv; + char *page; + struct comx_hardware *hw = comx_channels; + struct comx_protocol *line = comx_lines; + char str[30]; + int ret=0; + + if (file->f_dentry->d_inode->i_ino != entry->low_ino) { + printk(KERN_ERR "comx_write_proc: file <-> data internal error\n"); + return -EIO; + } + + if (count > PAGE_SIZE) { + printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE); + return -ENOSPC; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; + + copy_from_user(page, buffer, count); + + if (*(page + count - 1) == '\n') *(page + count - 1) = 0; + + if (strcmp(entry->name, FILENAME_DEBUG) == 0) { + int i; + int ret = 0; + + if ((i = simple_strtoul(page, NULL, 10)) != 0) { + unsigned long flags; + + save_flags(flags); cli(); + if (ch->debug_area) kfree(ch->debug_area); + if ((ch->debug_area = kmalloc(ch->debug_size = i, + GFP_KERNEL)) == NULL) { + ret = -ENOMEM; + } + ch->debug_start = ch->debug_end = 0; + restore_flags(flags); + free_page((unsigned long)page); + return count; + } + + if (*page != '+' && *page != '-') { + free_page((unsigned long)page); + return -EINVAL; + } + while (comx_debugflags[i].value && + strncmp(comx_debugflags[i].name, page + 1, + strlen(comx_debugflags[i].name))) { + i++; + } + + if (comx_debugflags[i].value == 0) { + printk(KERN_ERR "Invalid debug option\n"); + free_page((unsigned long)page); + return -EINVAL; + } + if (*page == '+') { + ch->debug_flags |= comx_debugflags[i].value; + } else { + ch->debug_flags &= ~comx_debugflags[i].value; + } + } else if (strcmp(entry->name, FILENAME_HARDWARE) == 0) { + if(strlen(page)>10) { + free_page((unsigned long)page); + return -EINVAL; + } + while (hw) { + if (strcmp(hw->name, page) == 0) { + break; + } else { + hw = hw->next; + } + } +#ifdef CONFIG_KMOD + if(!hw && comx_strcasecmp(HWNAME_NONE,page) != 0){ + sprintf(str,"comx-hw-%s",page); + request_module(str); + } + hw=comx_channels; + while (hw) { + if (comx_strcasecmp(hw->name, page) == 0) { + break; + } else { + hw = hw->next; + } + } +#endif + + if (comx_strcasecmp(HWNAME_NONE, page) != 0 && !hw) { + free_page((unsigned long)page); + return -ENODEV; + } + if (ch->init_status & HW_OPEN) { + free_page((unsigned long)page); + return -EBUSY; + } + if (ch->hardware && ch->hardware->hw_exit && + (ret=ch->hardware->hw_exit(dev))) { + free_page((unsigned long)page); + return ret; + } + ch->hardware = hw; + entry->size = strlen(page) + 1; + if (hw && hw->hw_init) hw->hw_init(dev); + } else if (strcmp(entry->name, FILENAME_PROTOCOL) == 0) { + if(strlen(page)>10) { + free_page((unsigned long)page); + return -EINVAL; + } + while (line) { + if (comx_strcasecmp(line->name, page) == 0) { + break; + } else { + line = line->next; + } + } +#ifdef CONFIG_KMOD + if(!line && comx_strcasecmp(PROTONAME_NONE, page) != 0) { + sprintf(str,"comx-proto-%s",page); + request_module(str); + } + line=comx_lines; + while (line) { + if (comx_strcasecmp(line->name, page) == 0) { + break; + } else { + line = line->next; + } + } +#endif + + if (comx_strcasecmp(PROTONAME_NONE, page) != 0 && !line) { + free_page((unsigned long)page); + return -ENODEV; + } + + if (ch->init_status & LINE_OPEN) { + free_page((unsigned long)page); + return -EBUSY; + } + + if (ch->protocol && ch->protocol->line_exit && + (ret=ch->protocol->line_exit(dev))) { + free_page((unsigned long)page); + return ret; + } + ch->protocol = line; + entry->size = strlen(page) + 1; + comx_reset_dev(dev); + if (line && line->line_init) line->line_init(dev); + } else if (strcmp(entry->name, FILENAME_LINEUPDELAY) == 0) { + int i; + + if ((i = simple_strtoul(page, NULL, 10)) != 0) { + if (i >=0 && i < 10) { + ch->lineup_delay = i; + } else { + printk(KERN_ERR "comx: invalid lineup_delay value\n"); + } + } + } + + free_page((unsigned long)page); + return count; +} + +static loff_t comx_debug_lseek(struct file *file, loff_t offset, int orig) +{ + switch(orig) { + case 0: + file->f_pos = max(0, min(offset, + file->f_dentry->d_inode->i_size)); + return(file->f_pos); + case 1: + file->f_pos = max(0, min(offset + file->f_pos, + file->f_dentry->d_inode->i_size)); + return(file->f_pos); + case 2: + file->f_pos = max(0, + min(offset + file->f_dentry->d_inode->i_size, + file->f_dentry->d_inode->i_size)); + return(file->f_pos); + } + return(file->f_pos); +} + +static int comx_file_open(struct inode *inode, struct file *file) +{ + + if((file->f_mode & FMODE_WRITE) && !(inode->i_mode & 0200)) { + return -EACCES; + } + + MOD_INC_USE_COUNT; + return 0; +} + +static int comx_file_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static ssize_t comx_debug_read(struct file *file, char *buffer, size_t count, + loff_t *ppos) +{ + struct proc_dir_entry *de = file->f_dentry->d_inode->u.generic_ip; + struct net_device *dev = de->parent->data; + struct comx_channel *ch = dev->priv; + loff_t copied = 0; + unsigned long flags; + + save_flags(flags); cli(); // We may run into trouble when debug_area is filled + // from irq inside read. no problem if the buffer is + // large enough + + while (count > 0 && ch->debug_start != ch->debug_end) { + int len; + + len = min( (ch->debug_end - ch->debug_start + ch->debug_size) + %ch->debug_size, min (ch->debug_size - + ch->debug_start, count)); + + if (len) copy_to_user(buffer + copied, + ch->debug_area + ch->debug_start, len); + ch->debug_start = (ch->debug_start + len) % ch->debug_size; + + de->size -= len; + count -= len; + copied += len; + } + + restore_flags(flags); + return copied; +} + +static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + struct proc_dir_entry *new_dir, *debug_file; + struct net_device *dev; + struct comx_channel *ch; + + if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR; + + if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR, + &comx_root_dir)) == NULL) { + return -EIO; + } + + new_dir->ops = &proc_dir_inode_operations; // ez egy normalis /proc konyvtar + new_dir->nlink = 2; + new_dir->data = NULL; // ide jon majd a struct dev + + /* Ezek kellenek */ + if (!create_comx_proc_entry(FILENAME_HARDWARE, 0644, + strlen(HWNAME_NONE) + 1, new_dir)) { + return -ENOMEM; + } + if (!create_comx_proc_entry(FILENAME_PROTOCOL, 0644, + strlen(PROTONAME_NONE) + 1, new_dir)) { + return -ENOMEM; + } + if (!create_comx_proc_entry(FILENAME_STATUS, 0444, 0, new_dir)) { + return -ENOMEM; + } + if (!create_comx_proc_entry(FILENAME_LINEUPDELAY, 0644, 2, new_dir)) { + return -ENOMEM; + } + + if ((debug_file = create_proc_entry(FILENAME_DEBUG, + S_IFREG | 0644, new_dir)) == NULL) { + return -ENOMEM; + } + debug_file->ops = &comx_debug_inode_ops; + debug_file->data = (void *)debug_file; + debug_file->read_proc = NULL; // see below + debug_file->write_proc = &comx_write_proc; + debug_file->nlink = 1; + + if ((dev = kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + memset(dev, 0, sizeof(struct net_device)); + dev->name = (char *)new_dir->name; + dev->init = comx_init_dev; + + if (register_netdevice(dev)) { + return -EIO; + } + ch=dev->priv; + if((ch->if_ptr = (void *)kmalloc(sizeof(struct ppp_device), + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + memset(ch->if_ptr, 0, sizeof(struct ppp_device)); + ch->debug_file = debug_file; + ch->procdir = new_dir; + new_dir->data = dev; + + ch->debug_start = ch->debug_end = 0; + if ((ch->debug_area = kmalloc(ch->debug_size = DEFAULT_DEBUG_SIZE, + GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + + ch->lineup_delay = DEFAULT_LINEUP_DELAY; + + MOD_INC_USE_COUNT; + return 0; +} + +static int comx_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct proc_dir_entry *entry = dentry->d_inode->u.generic_ip; + struct net_device *dev = entry->data; + struct comx_channel *ch = dev->priv; + int ret; + + /* Egyelore miert ne ? */ + if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR; + + if (dev->flags & IFF_UP) { + printk(KERN_ERR "%s: down interface before removing it\n", dev->name); + return -EBUSY; + } + + if (ch->protocol && ch->protocol->line_exit && + (ret=ch->protocol->line_exit(dev))) { + return ret; + } + if (ch->hardware && ch->hardware->hw_exit && + (ret=ch->hardware->hw_exit(dev))) { + if(ch->protocol && ch->protocol->line_init) { + ch->protocol->line_init(dev); + } + return ret; + } + ch->protocol = NULL; + ch->hardware = NULL; + + del_timer(&ch->loadavg_timer); + kfree(ch->avg_bytes); + + unregister_netdev(dev); + if (ch->debug_area) { + kfree(ch->debug_area); + } + if (dev->priv) { + kfree(dev->priv); + } + kfree(dev); + + remove_proc_entry(FILENAME_DEBUG, entry); + remove_proc_entry(FILENAME_LINEUPDELAY, entry); + remove_proc_entry(FILENAME_STATUS, entry); + remove_proc_entry(FILENAME_HARDWARE, entry); + remove_proc_entry(FILENAME_PROTOCOL, entry); + remove_proc_entry(dentry->d_name.name, &comx_root_dir); +// proc_unregister(&comx_root_dir, dentry->d_inode->i_ino); + + MOD_DEC_USE_COUNT; + return 0; +} + +static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry) +{ + struct proc_dir_entry *de; + struct inode *inode = NULL; + + if (!dir || !S_ISDIR(dir->i_mode)) { + return ERR_PTR(-ENOTDIR); + } + + if ((de = (struct proc_dir_entry *) dir->u.generic_ip) != NULL) { + for (de = de->subdir ; de ; de = de->next) { + if ((de && de->low_ino) && + (de->namelen == dentry->d_name.len) && + (memcmp(dentry->d_name.name, de->name, + de->namelen) == 0)) { + if ((inode = proc_get_inode(dir->i_sb, + de->low_ino, de)) == NULL) { + printk(KERN_ERR "COMX: lookup error\n"); + return ERR_PTR(-EINVAL); + } + break; + } + } + } + dentry->d_op = &comx_dentry_operations; + d_add(dentry, inode); + return NULL; +} + +int comx_strcasecmp(const char *cs, const char *ct) +{ + register signed char __res; + + while (1) { + if ((__res = toupper(*cs) - toupper(*ct++)) != 0 || !*cs++) { + break; + } + } + return __res; +} + +static void comx_delete_dentry(struct dentry *dentry) +{ + d_drop(dentry); +} + +static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode, + int size, struct proc_dir_entry *dir) +{ + struct proc_dir_entry *new_file; + + if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) { + new_file->ops = &comx_normal_inode_ops; + new_file->data = (void *)new_file; + new_file->read_proc = &comx_read_proc; + new_file->write_proc = &comx_write_proc; + new_file->size = size; + new_file->nlink = 1; + } + return(new_file); +} + +int comx_register_hardware(struct comx_hardware *comx_hw) +{ + struct comx_hardware *hw = comx_channels; + + if (!hw) { + comx_channels = comx_hw; + } else { + while (hw->next != NULL && strcmp(comx_hw->name, hw->name) != 0) { + hw = hw->next; + } + if (strcmp(comx_hw->name, hw->name) == 0) { + return -1; + } + hw->next = comx_hw; + } + + printk(KERN_INFO "COMX: driver for hardware type %s, version %s\n", comx_hw->name, comx_hw->version); + return 0; +} + +int comx_unregister_hardware(char *name) +{ + struct comx_hardware *hw = comx_channels; + + if (!hw) { + return -1; + } + + if (strcmp(hw->name, name) == 0) { + comx_channels = comx_channels->next; + return 0; + } + + while (hw->next != NULL && strcmp(hw->next->name,name) != 0) { + hw = hw->next; + } + + if (hw->next != NULL && strcmp(hw->next->name, name) == 0) { + hw->next = hw->next->next; + return 0; + } + return -1; +} + +int comx_register_protocol(struct comx_protocol *comx_line) +{ + struct comx_protocol *pr = comx_lines; + + if (!pr) { + comx_lines = comx_line; + } else { + while (pr->next != NULL && strcmp(comx_line->name, pr->name) !=0) { + pr = pr->next; + } + if (strcmp(comx_line->name, pr->name) == 0) { + return -1; + } + pr->next = comx_line; + } + + printk(KERN_INFO "COMX: driver for protocol type %s, version %s\n", comx_line->name, comx_line->version); + return 0; +} + +int comx_unregister_protocol(char *name) +{ + struct comx_protocol *pr = comx_lines; + + if (!pr) { + return -1; + } + + if (strcmp(pr->name, name) == 0) { + comx_lines = comx_lines->next; + return 0; + } + + while (pr->next != NULL && strcmp(pr->next->name,name) != 0) { + pr = pr->next; + } + + if (pr->next != NULL && strcmp(pr->next->name, name) == 0) { + pr->next = pr->next->next; + return 0; + } + return -1; +} + +#ifdef MODULE +#define comx_init init_module +#endif + +__initfunc(int comx_init(void)) +{ + struct proc_dir_entry *new_file; + + memcpy(&comx_root_inode_ops, &proc_dir_inode_operations, + sizeof(struct inode_operations)); + comx_root_inode_ops.lookup = &comx_lookup; + comx_root_inode_ops.mkdir = &comx_mkdir; + comx_root_inode_ops.rmdir = &comx_rmdir; + + memcpy(&comx_normal_inode_ops, &proc_net_inode_operations, + sizeof(struct inode_operations)); + comx_normal_inode_ops.default_file_ops = &comx_normal_file_ops; + comx_normal_inode_ops.lookup = &comx_lookup; + + memcpy(&comx_debug_inode_ops, &comx_normal_inode_ops, + sizeof(struct inode_operations)); + comx_debug_inode_ops.default_file_ops = &comx_debug_file_ops; + + memcpy(&comx_normal_file_ops, proc_net_inode_operations.default_file_ops, + sizeof(struct file_operations)); + comx_normal_file_ops.open = &comx_file_open; + comx_normal_file_ops.release = &comx_file_release; + + memcpy(&comx_debug_file_ops, &comx_normal_file_ops, + sizeof(struct file_operations)); + comx_debug_file_ops.llseek = &comx_debug_lseek; + comx_debug_file_ops.read = &comx_debug_read; + + if (proc_register(&proc_root, &comx_root_dir) < 0) return -ENOMEM; + + + if ((new_file = create_proc_entry(FILENAME_HARDWARELIST, + S_IFREG | 0444, &comx_root_dir)) == NULL) { + return -ENOMEM; + } + + new_file->ops = &comx_normal_inode_ops; + new_file->data = new_file; + new_file->read_proc = &comx_root_read_proc; + new_file->write_proc = NULL; + new_file->nlink = 1; + + if ((new_file = create_proc_entry(FILENAME_PROTOCOLLIST, + S_IFREG | 0444, &comx_root_dir)) == NULL) { + return -ENOMEM; + } + + new_file->ops = &comx_normal_inode_ops; + new_file->data = new_file; + new_file->read_proc = &comx_root_read_proc; + new_file->write_proc = NULL; + new_file->nlink = 1; + + + printk(KERN_INFO "COMX: driver version %s (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>\n", + VERSION); + +#ifndef MODULE +#ifdef CONFIG_COMX_HW_COMX + comx_hw_comx_init(); +#endif +#ifdef CONFIG_COMX_HW_LOCOMX + comx_hw_locomx_init(); +#endif +#ifdef CONFIG_COMX_HW_MIXCOM + comx_hw_mixcom_init(); +#endif +#ifdef CONFIG_COMX_PROTO_HDLC + comx_proto_hdlc_init(); +#endif +#ifdef CONFIG_COMX_PROTO_PPP + comx_proto_ppp_init(); +#endif +#ifdef CONFIG_COMX_PROTO_LAPB + comx_proto_lapb_init(); +#endif +#ifdef CONFIG_COMX_PROTO_FR + comx_proto_fr_init(); +#endif +#endif + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + remove_proc_entry(FILENAME_HARDWARELIST, &comx_root_dir); + remove_proc_entry(FILENAME_PROTOCOLLIST, &comx_root_dir); + proc_unregister(&proc_root, comx_root_dir.low_ino); +} +#endif + +EXPORT_SYMBOL(comx_register_hardware); +EXPORT_SYMBOL(comx_unregister_hardware); +EXPORT_SYMBOL(comx_register_protocol); +EXPORT_SYMBOL(comx_unregister_protocol); +EXPORT_SYMBOL(comx_debug_skb); +EXPORT_SYMBOL(comx_debug_bytes); +EXPORT_SYMBOL(comx_debug); +EXPORT_SYMBOL(comx_lineup_func); +EXPORT_SYMBOL(comx_status); +EXPORT_SYMBOL(comx_rx); +EXPORT_SYMBOL(comx_strcasecmp); +EXPORT_SYMBOL(comx_normal_inode_ops); +EXPORT_SYMBOL(comx_root_dir); diff --git a/drivers/net/wan/comx.h b/drivers/net/wan/comx.h new file mode 100644 index 000000000..e02849b90 --- /dev/null +++ b/drivers/net/wan/comx.h @@ -0,0 +1,240 @@ +/* + * General definitions for the COMX driver + * + * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>, + * Peter Bajan <bajan.peter@synergon.hu>, + * Previous maintainer: Tivadar Szemethy <tiv@itc.hu> + * Currently maintained by: Gergely Madarasz <gorgo@itc.hu> + * + * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu> + * + * 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. + * + * + * net_device_stats: + * rx_length_errors rec_len < 4 || rec_len > 2000 + * rx_over_errors receive overrun (OVR) + * rx_crc_errors rx crc error + * rx_frame_errors aborts rec'd (ABO) + * rx_fifo_errors status fifo overrun (PBUFOVR) + * rx_missed_errors receive buffer overrun (BUFOVR) + * tx_aborted_errors ? + * tx_carrier_errors modem line status changes + * tx_fifo_errors tx underrun (locomx) + */ +#include <linux/config.h> + +struct comx_protocol { + char *name; + char *version; + unsigned short encap_type; + int (*line_init)(struct net_device *dev); + int (*line_exit)(struct net_device *dev); + struct comx_protocol *next; + }; + +struct comx_hardware { + char *name; + char *version; + int (*hw_init)(struct net_device *dev); + int (*hw_exit)(struct net_device *dev); + int (*hw_dump)(struct net_device *dev); + struct comx_hardware *next; + }; + +struct comx_channel { + void *if_ptr; // General purpose pointer + struct net_device *dev; // Where we belong to + struct net_device *twin; // On dual-port cards + struct proc_dir_entry *procdir; // the directory + + unsigned char init_status; + unsigned char line_status; + + struct timer_list lineup_timer; // against line jitter + int lineup_pending; + unsigned char lineup_delay; + +#if 0 + struct timer_list reset_timer; // for board resetting + int reset_pending; + int reset_timeout; +#endif + + struct net_device_stats stats; + struct net_device_stats *current_stats; +#if 0 + unsigned long board_resets; +#endif + unsigned long *avg_bytes; + int loadavg_counter, loadavg_size; + int loadavg[3]; + struct timer_list loadavg_timer; + int debug_flags; + char *debug_area; + int debug_start, debug_end, debug_size; + struct proc_dir_entry *debug_file; +#ifdef CONFIG_COMX_DEBUG_RAW + char *raw; + int raw_len; +#endif + // LINE specific + struct comx_protocol *protocol; + void (*LINE_rx)(struct net_device *dev, struct sk_buff *skb); + int (*LINE_tx)(struct net_device *dev); + void (*LINE_status)(struct net_device *dev, u_short status); + int (*LINE_open)(struct net_device *dev); + int (*LINE_close)(struct net_device *dev); + int (*LINE_xmit)(struct sk_buff *skb, struct net_device *dev); + int (*LINE_header)(struct sk_buff *skb, struct net_device *dev, + u_short type,void *daddr, void *saddr, + unsigned len); + int (*LINE_rebuild_header)(struct sk_buff *skb); + int (*LINE_statistics)(struct net_device *dev, char *page); + int (*LINE_parameter_check)(struct net_device *dev); + int (*LINE_ioctl)(struct net_device *dev, struct ifreq *ifr, + int cmd); + void (*LINE_mod_use)(int); + void * LINE_privdata; + + // HW specific + + struct comx_hardware *hardware; + void (*HW_board_on)(struct net_device *dev); + void (*HW_board_off)(struct net_device *dev); + struct net_device *(*HW_access_board)(struct net_device *dev); + void (*HW_release_board)(struct net_device *dev, struct net_device *savep); + int (*HW_txe)(struct net_device *dev); + int (*HW_open)(struct net_device *dev); + int (*HW_close)(struct net_device *dev); + int (*HW_send_packet)(struct net_device *dev,struct sk_buff *skb); + int (*HW_statistics)(struct net_device *dev, char *page); +#if 0 + int (*HW_reset)(struct net_device *dev, char *page); +#endif + int (*HW_load_board)(struct net_device *dev); + void (*HW_set_clock)(struct net_device *dev); + void *HW_privdata; + }; + +struct comx_debugflags_struct { + char *name; + int value; + }; + +#define COMX_ROOT_DIR_NAME "comx" + +#define FILENAME_HARDWARE "boardtype" +#define FILENAME_HARDWARELIST "boardtypes" +#define FILENAME_PROTOCOL "protocol" +#define FILENAME_PROTOCOLLIST "protocols" +#define FILENAME_DEBUG "debug" +#define FILENAME_CLOCK "clock" +#define FILENAME_STATUS "status" +#define FILENAME_IO "io" +#define FILENAME_IRQ "irq" +#define FILENAME_KEEPALIVE "keepalive" +#define FILENAME_LINEUPDELAY "lineup_delay" +#define FILENAME_CHANNEL "channel" +#define FILENAME_FIRMWARE "firmware" +#define FILENAME_MEMADDR "memaddr" +#define FILENAME_TWIN "twin" +#define FILENAME_T1 "t1" +#define FILENAME_T2 "t2" +#define FILENAME_N2 "n2" +#define FILENAME_WINDOW "window" +#define FILENAME_MODE "mode" +#define FILENAME_DLCI "dlci" +#define FILENAME_MASTER "master" +#ifdef CONFIG_COMX_DEBUG_RAW +#define FILENAME_RAW "raw" +#endif + +#define PROTONAME_NONE "none" +#define HWNAME_NONE "none" +#define KEEPALIVE_OFF "off" + +#define FRAME_ACCEPTED 0 /* sending and xmitter busy */ +#define FRAME_DROPPED 1 +#define FRAME_ERROR 2 /* xmitter error */ +#define FRAME_QUEUED 3 /* sending but more can come */ + +#define LINE_UP 1 /* Modem UP */ +#define PROTO_UP 2 +#define PROTO_LOOP 4 + +#define HW_OPEN 1 +#define LINE_OPEN 2 +#define FW_LOADED 4 +#define IRQ_ALLOCATED 8 + +#define DEBUG_COMX_RX 2 +#define DEBUG_COMX_TX 4 +#define DEBUG_HW_TX 16 +#define DEBUG_HW_RX 32 +#define DEBUG_HDLC_KEEPALIVE 64 +#define DEBUG_COMX_PPP 128 +#define DEBUG_COMX_LAPB 256 +#define DEBUG_COMX_DLCI 512 + +#define DEBUG_PAGESIZE 3072 +#define DEFAULT_DEBUG_SIZE 4096 +#define DEFAULT_LINEUP_DELAY 1 +#define FILE_PAGESIZE 3072 + +#ifndef COMX_PPP_MAJOR +#define COMX_PPP_MAJOR 88 +#endif + + +#ifndef min +#define min(a,b) ((a) > (b) ? (b) : (a)) +#endif +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + + +#define COMX_CHANNEL(dev) ((struct comx_channel*)dev->priv) + +#define TWIN(dev) (COMX_CHANNEL(dev)->twin) + + +#ifndef byte +typedef u8 byte; +#endif +#ifndef word +typedef u16 word; +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +extern struct proc_dir_entry comx_root_dir; + +extern int comx_register_hardware(struct comx_hardware *comx_hw); +extern int comx_unregister_hardware(char *name); +extern int comx_register_protocol(struct comx_protocol *comx_line); +extern int comx_unregister_protocol(char *name); + +extern int comx_rx(struct net_device *dev, struct sk_buff *skb); +extern void comx_status(struct net_device *dev, int status); +extern void comx_lineup_func(unsigned long d); + +extern int comx_debug(struct net_device *dev, char *fmt, ...); +extern int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg); +extern int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len, + char *msg); +extern int comx_strcasecmp(const char *cs, const char *ct); + +extern struct inode_operations comx_normal_inode_ops; diff --git a/drivers/net/wan/comxhw.h b/drivers/net/wan/comxhw.h new file mode 100644 index 000000000..15230dc1f --- /dev/null +++ b/drivers/net/wan/comxhw.h @@ -0,0 +1,113 @@ +/* + * Defines for comxhw.c + * + * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>, + * Peter Bajan <bajan.peter@synergon.hu>, + * Previous maintainer: Tivadar Szemethy <tiv@itc.hu> + * Current maintainer: Gergely Madarasz <gorgo@itc.hu> + * + * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu> + * + * 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. + * + */ + +#define LOCOMX_IO_EXTENT 8 +#define COMX_IO_EXTENT 4 +#define HICOMX_IO_EXTENT 16 + +#define COMX_MAX_TX_SIZE 1600 +#define COMX_MAX_RX_SIZE 2048 + +#define COMX_JAIL_OFFSET 0xffff +#define COMX_JAIL_VALUE 0xfe +#define COMX_MEMORY_SIZE 65536 +#define HICOMX_MEMORY_SIZE 16384 +#define COMX_MEM_MIN 0xa0000 +#define COMX_MEM_MAX 0xf0000 + +#define COMX_DEFAULT_IO 0x360 +#define COMX_DEFAULT_IRQ 10 +#define COMX_DEFAULT_MEMADDR 0xd0000 +#define HICOMX_DEFAULT_IO 0x320 +#define HICOMX_DEFAULT_IRQ 10 +#define HICOMX_DEFAULT_MEMADDR 0xd0000 +#define LOCOMX_DEFAULT_IO 0x368 +#define LOCOMX_DEFAULT_IRQ 7 + +#define MAX_CHANNELNO 2 + +#define COMX_CHANNEL_OFFSET 0x2000 + +#define COMX_ENABLE_BOARD_IT 0x40 +#define COMX_BOARD_RESET 0x20 +#define COMX_ENABLE_BOARD_MEM 0x10 +#define COMX_DISABLE_BOARD_MEM 0 +#define COMX_DISABLE_ALL 0x00 + +#define HICOMX_DISABLE_ALL 0x00 +#define HICOMX_ENABLE_BOARD_MEM 0x02 +#define HICOMX_DISABLE_BOARD_MEM 0x0 +#define HICOMX_BOARD_RESET 0x01 +#define HICOMX_PRG_MEM 4 +#define HICOMX_DATA_MEM 0 +#define HICOMX_ID_BYTE 0x55 + +#define CMX_ID_BYTE 0x31 +#define COMX_CLOCK_CONST 8000 + +#define LINKUP_READY 3 + +#define OFF_FW_L1_ID 0x01e /* ID bytes */ +#define OFF_FW_L2_ID 0x1006 +#define FW_L1_ID_1 0xab +#define FW_L1_ID_2_COMX 0xc0 +#define FW_L1_ID_2_HICOMX 0xc1 +#define FW_L2_ID_1 0xab + +#define OFF_A_L2_CMD 0x130 /* command register for L2 */ +#define OFF_A_L2_CMDPAR 0x131 /* command parameter byte */ +#define OFF_A_L1_STATB 0x122 /* stat. block for L1 */ +#define OFF_A_L1_ABOREC 0x122 /* receive ABORT counter */ +#define OFF_A_L1_OVERRUN 0x123 /* receive overrun counter */ +#define OFF_A_L1_CRCREC 0x124 /* CRC error counter */ +#define OFF_A_L1_BUFFOVR 0x125 /* buffer overrun counter */ +#define OFF_A_L1_PBUFOVR 0x126 /* priority buffer overrun counter */ +#define OFF_A_L1_MODSTAT 0x127 /* current state of modem ctrl lines */ +#define OFF_A_L1_STATE 0x127 /* end of stat. block for L1 */ +#define OFF_A_L1_TXPC 0x128 /* Tx counter for the PC */ +#define OFF_A_L1_TXZ80 0x129 /* Tx counter for the Z80 */ +#define OFF_A_L1_RXPC 0x12a /* Rx counter for the PC */ +#define OFF_A_L1_RXZ80 0x12b /* Rx counter for the Z80 */ +#define OFF_A_L1_REPENA 0x12c /* IT rep disable */ +#define OFF_A_L1_CHNR 0x12d /* L1 channel logical number */ +#define OFF_A_L1_CLKINI 0x12e /* Timer Const */ +#define OFF_A_L2_LINKUP 0x132 /* Linkup byte */ +#define OFF_A_L2_DAV 0x134 /* Rx DAV */ +#define OFF_A_L2_RxBUFP 0x136 /* Rx buff relative to membase */ +#define OFF_A_L2_TxEMPTY 0x138 /* Tx Empty */ +#define OFF_A_L2_TxBUFP 0x13a /* Tx Buf */ +#define OFF_A_L2_NBUFFS 0x144 /* Number of buffers to fetch */ + +#define OFF_A_L2_SABMREC 0x164 /* LAPB no. of SABMs received */ +#define OFF_A_L2_SABMSENT 0x165 /* LAPB no. of SABMs sent */ +#define OFF_A_L2_REJREC 0x166 /* LAPB no. of REJs received */ +#define OFF_A_L2_REJSENT 0x167 /* LAPB no. of REJs sent */ +#define OFF_A_L2_FRMRREC 0x168 /* LAPB no. of FRMRs received */ +#define OFF_A_L2_FRMRSENT 0x169 /* LAPB no. of FRMRs sent */ +#define OFF_A_L2_PROTERR 0x16A /* LAPB no. of protocol errors rec'd */ +#define OFF_A_L2_LONGREC 0x16B /* LAPB no. of long frames */ +#define OFF_A_L2_INVNR 0x16C /* LAPB no. of invalid N(R)s rec'd */ +#define OFF_A_L2_UNDEFFR 0x16D /* LAPB no. of invalid frames */ + +#define OFF_A_L2_T1 0x174 /* T1 timer */ +#define OFF_A_L2_ADDR 0x176 /* DCE = 1, DTE = 3 */ + +#define COMX_CMD_INIT 1 +#define COMX_CMD_EXIT 2 +#define COMX_CMD_OPEN 16 +#define COMX_CMD_CLOSE 17 + diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 633514166..544fcb8dd 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -1150,19 +1150,19 @@ static int cosa_ioctl_common(struct cosa_data *cosa, { switch(cmd) { case COSAIORSET: /* Reset the device */ - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EACCES; return cosa_reset(cosa); case COSAIOSTRT: /* Start the firmware */ - if (!suser()) + if (!capable(CAP_SYS_RAWIO)) return -EACCES; return cosa_start(cosa, arg); case COSAIODOWNLD: /* Download the firmware */ - if (!suser()) + if (!capable(CAP_SYS_RAWIO)) return -EACCES; return cosa_download(cosa, (struct cosa_download *)arg); case COSAIORMEM: - if (!suser()) + if (!capable(CAP_SYS_RAWIO)) return -EACCES; return cosa_readmem(cosa, (struct cosa_download *)arg); case COSAIORTYPE: @@ -1189,7 +1189,7 @@ static int cosa_ioctl_common(struct cosa_data *cosa, case COSAIONRCHANS: return cosa->nchannels; case COSAIOBMSET: - if (!suser()) + if (!capable(CAP_SYS_RAWIO)) return -EACCES; if (is_8bit(cosa)) return -EINVAL; diff --git a/drivers/net/wan/hscx.h b/drivers/net/wan/hscx.h new file mode 100644 index 000000000..675b7b1f1 --- /dev/null +++ b/drivers/net/wan/hscx.h @@ -0,0 +1,103 @@ +#define HSCX_MTU 1600 + +#define HSCX_ISTA 0x00 +#define HSCX_MASK 0x00 +#define HSCX_STAR 0x01 +#define HSCX_CMDR 0x01 +#define HSCX_MODE 0x02 +#define HSCX_TIMR 0x03 +#define HSCX_EXIR 0x04 +#define HSCX_XAD1 0x04 +#define HSCX_RBCL 0x05 +#define HSCX_SAD2 0x05 +#define HSCX_RAH1 0x06 +#define HSCX_RSTA 0x07 +#define HSCX_RAH2 0x07 +#define HSCX_RAL1 0x08 +#define HSCX_RCHR 0x09 +#define HSCX_RAL2 0x09 +#define HSCX_XBCL 0x0a +#define HSCX_BGR 0x0b +#define HSCX_CCR2 0x0c +#define HSCX_RBCH 0x0d +#define HSCX_XBCH 0x0d +#define HSCX_VSTR 0x0e +#define HSCX_RLCR 0x0e +#define HSCX_CCR1 0x0f +#define HSCX_FIFO 0x1e + +#define HSCX_HSCX_CHOFFS 0x400 +#define HSCX_SEROFFS 0x1000 + +#define HSCX_RME 0x80 +#define HSCX_RPF 0x40 +#define HSCX_RSC 0x20 +#define HSCX_XPR 0x10 +#define HSCX_TIN 0x08 +#define HSCX_ICA 0x04 +#define HSCX_EXA 0x02 +#define HSCX_EXB 0x01 + +#define HSCX_XMR 0x80 +#define HSCX_XDU 0x40 +#define HSCX_EXE 0x40 +#define HSCX_PCE 0x20 +#define HSCX_RFO 0x10 +#define HSCX_CSC 0x08 +#define HSCX_RFS 0x04 + +#define HSCX_XDOV 0x80 +#define HSCX_XFW 0x40 +#define HSCX_XRNR 0x20 +#define HSCX_RRNR 0x10 +#define HSCX_RLI 0x08 +#define HSCX_CEC 0x04 +#define HSCX_CTS 0x02 +#define HSCX_WFA 0x01 + +#define HSCX_RMC 0x80 +#define HSCX_RHR 0x40 +#define HSCX_RNR 0x20 +#define HSCX_XREP 0x20 +#define HSCX_STI 0x10 +#define HSCX_XTF 0x08 +#define HSCX_XIF 0x04 +#define HSCX_XME 0x02 +#define HSCX_XRES 0x01 + +#define HSCX_AUTO 0x00 +#define HSCX_NONAUTO 0x40 +#define HSCX_TRANS 0x80 +#define HSCX_XTRANS 0xc0 +#define HSCX_ADM16 0x20 +#define HSCX_ADM8 0x00 +#define HSCX_TMD_EXT 0x00 +#define HSCX_TMD_INT 0x10 +#define HSCX_RAC 0x08 +#define HSCX_RTS 0x04 +#define HSCX_TLP 0x01 + +#define HSCX_VFR 0x80 +#define HSCX_RDO 0x40 +#define HSCX_CRC 0x20 +#define HSCX_RAB 0x10 + +#define HSCX_CIE 0x04 +#define HSCX_RIE 0x02 + +#define HSCX_DMA 0x80 +#define HSCX_NRM 0x40 +#define HSCX_CAS 0x20 +#define HSCX_XC 0x10 + +#define HSCX_OV 0x10 + +#define HSCX_CD 0x80 + +#define HSCX_RC 0x80 + +#define HSCX_PU 0x80 +#define HSCX_NRZ 0x00 +#define HSCX_NRZI 0x40 +#define HSCX_ODS 0x10 +#define HSCX_ITF 0x08 diff --git a/drivers/net/wan/mixcom.h b/drivers/net/wan/mixcom.h new file mode 100644 index 000000000..1815eef75 --- /dev/null +++ b/drivers/net/wan/mixcom.h @@ -0,0 +1,35 @@ +/* + * Defines for the mixcom board + * + * Author: Gergely Madarasz <gorgo@itc.hu> + * + * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu> + * + * 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. + * + */ + +#define MIXCOM_IO_EXTENT 0x20 + +#define MIXCOM_DEFAULT_IO 0x180 +#define MIXCOM_DEFAULT_IRQ 5 + +#define MIXCOM_ID 0x11 +#define MIXCOM_SERIAL_OFFSET 0x1000 +#define MIXCOM_CHANNEL_OFFSET 0x400 +#define MIXCOM_IT_OFFSET 0xc14 +#define MIXCOM_STATUS_OFFSET 0xc14 +#define MIXCOM_ID_OFFSET 0xc10 +#define MIXCOM_ON 0x1 +#define MIXCOM_OFF 0x0 + +/* Status register bits */ + +#define MIXCOM_CTSB 0x1 +#define MIXCOM_CTSA 0x2 +#define MIXCOM_CHANNELNO 0x20 +#define MIXCOM_POWERFAIL 0x40 +#define MIXCOM_BOOT 0x80 diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c index b161fbabc..be665f0ae 100644 --- a/drivers/net/wan/sbni.c +++ b/drivers/net/wan/sbni.c @@ -101,7 +101,7 @@ static void sbni_interrupt(int irq, void *dev_id, struct pt_regs *regs); static int sbni_close(struct net_device *dev); static void sbni_drop_tx_queue(struct net_device *dev); static struct enet_statistics *sbni_get_stats(struct net_device *dev); -void card_start(struct net_device *dev); +static void card_start(struct net_device *dev); static inline unsigned short sbni_recv(struct net_device *dev); void change_level(struct net_device *dev); static inline void sbni_xmit(struct net_device *dev); @@ -647,7 +647,7 @@ static int sbni_start_xmit(struct sk_buff *skb, struct net_device *dev) return 0; } -void card_start(struct net_device *dev) +static void card_start(struct net_device *dev) { struct net_local *lp = (struct net_local*)dev->priv; @@ -1200,6 +1200,8 @@ static int sbni_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) } case SIOCDEVRESINSTATS: { + if(!capable(CAP_NET_ADMIN)) + return -EPERM; DP( printk("%s: SIOCDEVRESINSTATS\n",dev->name); ) lp->in_stats.all_rx_number = 0; lp->in_stats.bad_rx_number = 0; diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c index e7875649e..0454118a8 100644 --- a/drivers/net/wan/sdla.c +++ b/drivers/net/wan/sdla.c @@ -1247,7 +1247,7 @@ static int sdla_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct frad_local *flp; - if(!suser()) + if(!capable(CAP_NET_ADMIN)) return -EPERM; flp = dev->priv; diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c index e039bbc28..5b8504616 100644 --- a/drivers/net/wan/syncppp.c +++ b/drivers/net/wan/syncppp.c @@ -147,13 +147,17 @@ static void sppp_print_bytes (u8 *p, u16 len); static int debug = 0; +MODULE_PARM(debug,"1i"); + /* * Interface down stub */ static void if_down(struct net_device *dev) { - ; + struct sppp *sp = &((struct ppp_device *)dev)->sppp; + + sp->pp_link_state=SPPP_LINK_DOWN; } /* @@ -182,8 +186,19 @@ static void sppp_clear_timeout(struct sppp *p) } } -/* - * Process the received packet. +/** + * sppp_input - receive and process a WAN PPP frame + * @skb: The buffer to process + * @dev: The device it arrived on + * + * This can be called directly by cards that do not have + * timing constraints but is normally called from the network layer + * after interrupt servicing to process frames queued via netif_rx. + * + * We process the options in the card. If the frame is destined for + * the protocol stacks then it requeues the frame for the upper level + * protocol. If it is a control from it is processed and discarded + * here. */ void sppp_input (struct net_device *dev, struct sk_buff *skb) @@ -194,7 +209,7 @@ void sppp_input (struct net_device *dev, struct sk_buff *skb) skb->dev=dev; skb->mac.raw=skb->data; - if (dev->flags & IFF_UP) + if (dev->flags & IFF_RUNNING) { /* Count received bytes, add FCS and one flag */ sp->ibytes+= skb->len + 3; @@ -324,7 +339,7 @@ static int sppp_hard_header(struct sk_buff *skb, struct net_device *dev, __u16 t h=(struct ppp_header *)skb->data; if(sp->pp_flags&PP_CISCO) { - h->address = CISCO_MULTICAST; + h->address = CISCO_UNICAST; h->control = 0; } else @@ -370,7 +385,7 @@ static void sppp_keepalive (unsigned long dummy) /* Keepalive mode disabled or channel down? */ if (! (sp->pp_flags & PP_KEEPALIVE) || - ! (dev->flags & IFF_RUNNING)) + ! (dev->flags & IFF_UP)) continue; /* No keepalive in PPP mode if LCP not opened yet. */ @@ -530,10 +545,10 @@ badreq: if (h->ident != sp->lcp.confid) break; sppp_clear_timeout (sp); - if (! (dev->flags & IFF_UP) && - (dev->flags & IFF_RUNNING)) { + if ((sp->pp_link_state != SPPP_LINK_UP) && + (dev->flags & IFF_UP)) { /* Coming out of loopback mode. */ - dev->flags |= IFF_UP; + sp->pp_link_state=SPPP_LINK_UP; printk (KERN_INFO "%s: up\n", dev->name); } switch (sp->lcp.state) { @@ -698,9 +713,9 @@ static void sppp_cisco_input (struct sppp *sp, struct sk_buff *skb) break; } sp->pp_loopcnt = 0; - if (! (dev->flags & IFF_UP) && - (dev->flags & IFF_RUNNING)) { - dev->flags |= IFF_UP; + if (sp->pp_link_state==SPPP_LINK_DOWN && + (dev->flags & IFF_UP)) { + sp->pp_link_state=SPPP_LINK_UP; printk (KERN_INFO "%s: up\n", dev->name); } break; @@ -825,11 +840,19 @@ static void sppp_cisco_send (struct sppp *sp, int type, long par1, long par2) dev_queue_xmit(skb); } +/** + * sppp_close - close down a synchronous PPP or Cisco HDLC link + * @dev: The network device to drop the link of + * + * This drops the logical interface to the channel. It is not + * done politely as we assume we will also be dropping DTR. Any + * timeouts are killed. + */ int sppp_close (struct net_device *dev) { struct sppp *sp = (struct sppp *)sppp_of(dev); - dev->flags &= ~IFF_RUNNING; + sp->pp_link_state = SPPP_LINK_DOWN; sp->lcp.state = LCP_STATE_CLOSED; sp->ipcp.state = IPCP_STATE_CLOSED; sppp_clear_timeout (sp); @@ -838,24 +861,49 @@ int sppp_close (struct net_device *dev) EXPORT_SYMBOL(sppp_close); +/** + * sppp_open - open a synchronous PPP or Cisco HDLC link + * @dev: Network device to activate + * + * Close down any existing synchronous session and commence + * from scratch. In the PPP case this means negotiating LCP/IPCP + * and friends, while for Cisco HDLC we simply need to staet sending + * keepalives + */ int sppp_open (struct net_device *dev) { struct sppp *sp = (struct sppp *)sppp_of(dev); sppp_close(dev); - dev->flags |= IFF_RUNNING; - if (!(sp->pp_flags & PP_CISCO)) + if (!(sp->pp_flags & PP_CISCO)) { sppp_lcp_open (sp); + } + sp->pp_link_state = SPPP_LINK_DOWN; return 0; } EXPORT_SYMBOL(sppp_open); +/** + * sppp_reopen - notify of physical link loss + * @dev: Device that lost the link + * + * This function informs the synchronous protocol code that + * the underlying link died (for example a carrier drop on X.21) + * + * We increment the magic numbers to ensure that if the other end + * failed to notice we will correctly start a new session. It happens + * do to the nature of telco circuits is that you can lose carrier on + * one endonly. + * + * Having done this we go back to negotiating. This function may + * be called from an interrupt context. + */ + int sppp_reopen (struct net_device *dev) { struct sppp *sp = (struct sppp *)sppp_of(dev); sppp_close(dev); - dev->flags |= IFF_RUNNING; if (!(sp->pp_flags & PP_CISCO)) { sp->lcp.magic = jiffies; @@ -864,12 +912,23 @@ int sppp_reopen (struct net_device *dev) sp->ipcp.state = IPCP_STATE_CLOSED; /* Give it a moment for the line to settle then go */ sppp_set_timeout (sp, 1); - } + } + sp->pp_link_state=SPPP_LINK_DOWN; return 0; } EXPORT_SYMBOL(sppp_reopen); +/** + * sppp_change_mtu - Change the link MTU + * @dev: Device to change MTU on + * @new_mtu: New MTU + * + * Change the MTU on the link. This can only be called with + * the link down. It returns an error if the link is up or + * the mtu is out of range. + */ + int sppp_change_mtu(struct net_device *dev, int new_mtu) { if(new_mtu<128||new_mtu>PPP_MTU||(dev->flags&IFF_UP)) @@ -880,6 +939,18 @@ int sppp_change_mtu(struct net_device *dev, int new_mtu) EXPORT_SYMBOL(sppp_change_mtu); +/** + * sppp_do_ioctl - Ioctl handler for ppp/hdlc + * @dev: Device subject to ioctl + * @ifr: Interface request block from the user + * @cmd: Command that is being issued + * + * This function handles the ioctls that may be issued by the user + * to control the settings of a PPP/HDLC link. It does both busy + * and security checks. This function is intended to be wrapped by + * callers who wish to add additional ioctl calls of their own. + */ + int sppp_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct sppp *sp = (struct sppp *)sppp_of(dev); @@ -913,6 +984,16 @@ int sppp_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) EXPORT_SYMBOL(sppp_do_ioctl); +/** + * sppp_attach - attach synchronous PPP/HDLC to a device + * @pd: PPP device to initialise + * + * This initialises the PPP/HDLC support on an interface. At the + * time of calling the dev element must point to the network device + * that this interface is attached to. The interface should not yet + * be registered. + */ + void sppp_attach(struct ppp_device *pd) { struct net_device *dev = pd->dev; @@ -973,6 +1054,15 @@ void sppp_attach(struct ppp_device *pd) EXPORT_SYMBOL(sppp_attach); +/** + * sppp_detach - release PPP resources from a device + * @dev: Network device to release + * + * Stop and free up any PPP/HDLC resources used by this + * interface. This must be called before the device is + * freed. + */ + void sppp_detach (struct net_device *dev) { struct sppp **q, *p, *sp = (struct sppp *)sppp_of(dev); @@ -1187,7 +1277,7 @@ static void sppp_cp_timeout (unsigned long arg) cli(); sp->pp_flags &= ~PP_TIMO; - if (! (sp->pp_if->flags & IFF_RUNNING) || (sp->pp_flags & PP_CISCO)) { + if (! (sp->pp_if->flags & IFF_UP) || (sp->pp_flags & PP_CISCO)) { restore_flags(flags); return; } @@ -1273,18 +1363,24 @@ static void sppp_print_bytes (u_char *p, u16 len) printk ("-%x", *p++); } -/* +/** + * sppp_rcv - receive and process a WAN PPP frame + * @skb: The buffer to process + * @dev: The device it arrived on + * @p: Unused + * * Protocol glue. This drives the deferred processing mode the poorer - * cards use. + * cards use. This can be called directly by cards that do not have + * timing constraints but is normally called from the network layer + * after interrupt servicing to process frames queued via netif_rx. */ -int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p) +static int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p) { sppp_input(dev,skb); return 0; } -EXPORT_SYMBOL(sppp_rcv); struct packet_type sppp_packet_type= { @@ -1305,8 +1401,6 @@ void sync_ppp_init(void) dev_add_pack(&sppp_packet_type); } -EXPORT_SYMBOL(sync_ppp_init); - #ifdef MODULE int init_module(void) diff --git a/drivers/net/wan/syncppp.h b/drivers/net/wan/syncppp.h index 1e2056869..8f6ed5c1f 100644 --- a/drivers/net/wan/syncppp.h +++ b/drivers/net/wan/syncppp.h @@ -47,6 +47,7 @@ struct sppp u32 ipkts,opkts; /* Packets in/out */ struct timer_list pp_timer; struct net_device *pp_if; + char pp_link_state; /* Link status */ }; struct ppp_device @@ -75,6 +76,9 @@ struct ppp_device #define IPCP_STATE_ACK_SENT 2 /* IPCP state: conf-ack sent */ #define IPCP_STATE_OPENED 3 /* IPCP state: opened */ +#define SPPP_LINK_DOWN 0 /* link down - no keepalive */ +#define SPPP_LINK_UP 1 /* link is up - keepalive ok */ + void sppp_attach (struct ppp_device *pd); void sppp_detach (struct net_device *dev); void sppp_input (struct net_device *dev, struct sk_buff *m); diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c index 2ff818b04..f1c618a90 100644 --- a/drivers/net/wan/z85230.c +++ b/drivers/net/wan/z85230.c @@ -55,7 +55,7 @@ static spinlock_t z8530_buffer_lock = SPIN_LOCK_UNLOCKED; /** - * z8530_read_port: + * z8530_read_port - Architecture specific interface function * @p: port to read * * Provided port access methods. The Comtrol SV11 requires no delays @@ -79,7 +79,7 @@ extern __inline__ int z8530_read_port(unsigned long p) } /** - * z8530_write_port: + * z8530_write_port - Architecture specific interface function * @p: port to write * @d: value to write * @@ -108,7 +108,7 @@ static void z8530_tx_done(struct z8530_channel *c); /** - * read_zsreg: + * read_zsreg - Read a register from a Z85230 * @c: Z8530 channel to read from (2 per chip) * @reg: Register to read * FIXME: Use a spinlock. @@ -133,7 +133,7 @@ extern inline u8 read_zsreg(struct z8530_channel *c, u8 reg) } /** - * read_zsdata: + * read_zsdata - Read the data port of a Z8530 channel * @c: The Z8530 channel to read the data port from * * The data port provides fast access to some things. We still @@ -148,7 +148,7 @@ extern inline u8 read_zsdata(struct z8530_channel *c) } /** - * write_zsreg: + * write_zsreg - Write to a Z8530 channel register * @c: The Z8530 channel * @reg: Register number * @val: Value to write @@ -169,11 +169,28 @@ extern inline void write_zsreg(struct z8530_channel *c, u8 reg, u8 val) restore_flags(flags); } +/** + * write_zsctrl - Write to a Z8530 control register + * @c: The Z8530 channel + * @val: Value to write + * + * Write directly to the control register on the Z8530 + */ + extern inline void write_zsctrl(struct z8530_channel *c, u8 val) { z8530_write_port(c->ctrlio, val); } +/** + * write_zsdata - Write to a Z8530 control register + * @c: The Z8530 channel + * @val: Value to write + * + * Write directly to the data register on the Z8530 + */ + + extern inline void write_zsdata(struct z8530_channel *c, u8 val) { z8530_write_port(c->dataio, val); @@ -249,7 +266,7 @@ u8 z8530_hdlc_kilostream_85230[]= EXPORT_SYMBOL(z8530_hdlc_kilostream_85230); /** - * z8530_flush_fifo: + * z8530_flush_fifo - Flush on chip RX FIFO * @c: Channel to flush * * Flush the receive FIFO. There is no specific option for this, we @@ -276,8 +293,8 @@ static void z8530_flush_fifo(struct z8530_channel *c) } /** - * z8530_rtsdtr: - * @c: The Z8530 channel to contro; + * z8530_rtsdtr - Control the outgoing DTS/RTS line + * @c: The Z8530 channel to control; * @set: 1 to set, 0 to clear * * Sets or clears DTR/RTS on the requested line. All locking is handled @@ -296,7 +313,7 @@ static void z8530_rtsdtr(struct z8530_channel *c, int set) } /** - * z8530_rx: + * z8530_rx - Handle a PIO receive event * @c: Z8530 channel to process * * Receive handler for receiving in PIO mode. This is much like the @@ -378,7 +395,7 @@ static void z8530_rx(struct z8530_channel *c) /** - * z8530_tx: + * z8530_tx - Handle a PIO transmit event * @c: Z8530 channel to process * * Z8530 transmit interrupt handler for the PIO mode. The basic @@ -421,7 +438,7 @@ static void z8530_tx(struct z8530_channel *c) } /** - * z8530_status: + * z8530_status - Handle a PIO status exception * @chan: Z8530 channel to process * * A status event occured in PIO synchronous mode. There are several @@ -479,7 +496,7 @@ struct z8530_irqhandler z8530_sync= EXPORT_SYMBOL(z8530_sync); /** - * z8530_dma_rx: + * z8530_dma_rx - Handle a DMA RX event * @chan: Channel to handle * * Non bus mastering DMA interfaces for the Z8x30 devices. This @@ -514,7 +531,7 @@ static void z8530_dma_rx(struct z8530_channel *chan) } /** - * z8530_dma_tx: + * z8530_dma_tx - Handle a DMA TX event * @chan: The Z8530 channel to handle * * We have received an interrupt while doing DMA transmissions. It @@ -525,17 +542,17 @@ static void z8530_dma_tx(struct z8530_channel *chan) { if(!chan->dma_tx) { - printk("Hey who turned the DMA off?\n"); + printk(KERN_WARNING "Hey who turned the DMA off?\n"); z8530_tx(chan); return; } /* This shouldnt occur in DMA mode */ - printk(KERN_ERR "DMA tx ??\n"); + printk(KERN_ERR "DMA tx - bogus event!\n"); z8530_tx(chan); } /** - * z8530_dma_status: + * z8530_dma_status - Handle a DMA status exception * @chan: Z8530 channel to process * * A status event occured on the Z8530. We receive these for two reasons @@ -606,7 +623,7 @@ struct z8530_irqhandler z8530_txdma_sync= EXPORT_SYMBOL(z8530_txdma_sync); /** - * z8530_rx_clear: + * z8530_rx_clear - Handle RX events from a stopped chip * @c: Z8530 channel to shut up * * Receive interrupt vectors for a Z8530 that is in 'parked' mode. @@ -635,7 +652,7 @@ static void z8530_rx_clear(struct z8530_channel *c) } /** - * z8530_tx_clear: + * z8530_tx_clear - Handle TX events from a stopped chip * @c: Z8530 channel to shut up * * Transmit interrupt vectors for a Z8530 that is in 'parked' mode. @@ -650,7 +667,7 @@ static void z8530_tx_clear(struct z8530_channel *c) } /** - * z8530_status_clear: + * z8530_status_clear - Handle status events from a stopped chip * @chan: Z8530 channel to shut up * * Status interrupt vectors for a Z8530 that is in 'parked' mode. @@ -678,7 +695,7 @@ struct z8530_irqhandler z8530_nop= EXPORT_SYMBOL(z8530_nop); /** - * z8530_interrupt: + * z8530_interrupt - Handle an interrupt from a Z8530 * @irq: Interrupt number * @dev_id: The Z8530 device that is interrupting. * @regs: unused @@ -758,7 +775,7 @@ static char reg_init[16]= /** - * z8530_sync_open: + * z8530_sync_open - Open a Z8530 channel for PIO * @dev: The network interface we are using * @c: The Z8530 channel to open in synchronous PIO mode * @@ -789,7 +806,7 @@ int z8530_sync_open(struct net_device *dev, struct z8530_channel *c) EXPORT_SYMBOL(z8530_sync_open); /** - * z8530_sync_close: + * z8530_sync_close - Close a PIO Z8530 channel * @dev: Network device to close * @c: Z8530 channel to disassociate and move to idle * @@ -814,7 +831,7 @@ int z8530_sync_close(struct net_device *dev, struct z8530_channel *c) EXPORT_SYMBOL(z8530_sync_close); /** - * z8530_sync_dma_open: + * z8530_sync_dma_open - Open a Z8530 for DMA I/O * @dev: The network device to attach * @c: The Z8530 channel to configure in sync DMA mode. * @@ -934,7 +951,7 @@ int z8530_sync_dma_open(struct net_device *dev, struct z8530_channel *c) EXPORT_SYMBOL(z8530_sync_dma_open); /** - * z8530_sync_dma_close: + * z8530_sync_dma_close - Close down DMA I/O * @dev: Network device to detach * @c: Z8530 channel to move into discard mode * @@ -999,7 +1016,7 @@ int z8530_sync_dma_close(struct net_device *dev, struct z8530_channel *c) EXPORT_SYMBOL(z8530_sync_dma_close); /** - * z8530_sync_txdma_open: + * z8530_sync_txdma_open - Open a Z8530 for TX driven DMA * @dev: The network device to attach * @c: The Z8530 channel to configure in sync DMA mode. * @@ -1099,7 +1116,7 @@ int z8530_sync_txdma_open(struct net_device *dev, struct z8530_channel *c) EXPORT_SYMBOL(z8530_sync_txdma_open); /** - * z8530_sync_txdma_close: + * z8530_sync_txdma_close - Close down a TX driven DMA channel * @dev: Network device to detach * @c: Z8530 channel to move into discard mode * @@ -1168,7 +1185,7 @@ static char *z8530_type_name[]={ }; /** - * z8530_describe: + * z8530_describe - Uniformly describe a Z8530 port * @dev: Z8530 device to describe * @mapping: string holding mapping type (eg "I/O" or "Mem") * @io: the port value in question @@ -1191,7 +1208,7 @@ void z8530_describe(struct z8530_dev *dev, char *mapping, unsigned long io) EXPORT_SYMBOL(z8530_describe); /** - * z8530_init: + * z8530_init - Initialise a Z8530 device * @dev: Z8530 device to initialise. * * Configure up a Z8530/Z85C30 or Z85230 chip. We check the device @@ -1273,7 +1290,7 @@ int z8530_init(struct z8530_dev *dev) EXPORT_SYMBOL(z8530_init); /** - * z8530_shutdown: + * z8530_shutdown - Shutdown a Z8530 device * @dev: The Z8530 chip to shutdown * * We set the interrupt handlers to silence any interrupts. We then @@ -1294,7 +1311,7 @@ int z8530_shutdown(struct z8530_dev *dev) EXPORT_SYMBOL(z8530_shutdown); /** - * z8530_channel_load: + * z8530_channel_load - Load channel data * @c: Z8530 channel to configure * @rtable: Table of register, value pairs * FIXME: ioctl to allow user uploaded tables @@ -1333,7 +1350,7 @@ EXPORT_SYMBOL(z8530_channel_load); /** - * z8530_tx_begin: + * z8530_tx_begin - Begin packet transmission * @c: The Z8530 channel to kick * * This is the speed sensitive side of transmission. If we are called @@ -1430,7 +1447,7 @@ static void z8530_tx_begin(struct z8530_channel *c) } /** - * z8530_tx_done: + * z8530_tx_done - TX complete callback * @c: The channel that completed a transmit. * * This is called when we complete a packet send. We wake the queue, @@ -1461,7 +1478,7 @@ static void z8530_tx_done(struct z8530_channel *c) } /** - * z8530_null_rx: + * z8530_null_rx - Discard a packet * @c: The channel the packet arrived on * @skb: The buffer * @@ -1477,7 +1494,7 @@ void z8530_null_rx(struct z8530_channel *c, struct sk_buff *skb) EXPORT_SYMBOL(z8530_null_rx); /** - * z8530_rx_done: + * z8530_rx_done - Receive completion callback * @c: The channel that completed a receive * * A new packet is complete. Our goal here is to get back into receive @@ -1630,7 +1647,7 @@ static void z8530_rx_done(struct z8530_channel *c) } /** - * spans_boundary: + * spans_boundary - Check a packet can be ISA DMA'd * @skb: The buffer to check * * Returns true if the buffer cross a DMA boundary on a PC. The poor @@ -1642,15 +1659,12 @@ extern inline int spans_boundary(struct sk_buff *skb) unsigned long a=(unsigned long)skb->data; a^=(a+skb->len); if(a&0x00010000) /* If the 64K bit is different.. */ - { - printk("spanner\n"); return 1; - } return 0; } /** - * z8530_queue_xmit: + * z8530_queue_xmit - Queue a packet * @c: The channel to use * @skb: The packet to kick down the channel * @@ -1707,7 +1721,7 @@ int z8530_queue_xmit(struct z8530_channel *c, struct sk_buff *skb) EXPORT_SYMBOL(z8530_queue_xmit); /** - * z8530_get_stats: + * z8530_get_stats - Get network statistics * @c: The channel to use * * Get the statistics block. We keep the statistics in software as diff --git a/drivers/net/wavelan.c b/drivers/net/wavelan.c index 9fc75e5e6..2162d5ff0 100644 --- a/drivers/net/wavelan.c +++ b/drivers/net/wavelan.c @@ -1990,7 +1990,7 @@ static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is } /* only super-user can see encryption key */ - if (!suser()) { + if (!capable(CAP_NET_ADMIN)) { ret = -EPERM; break; } @@ -2224,7 +2224,7 @@ static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is /* ------------------ PRIVATE IOCTL ------------------ */ case SIOCSIPQTHR: - if (!suser()) { + if (!capable(CAP_NET_ADMIN)) { ret = -EPERM; break; } @@ -2248,7 +2248,7 @@ static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is #ifdef HISTOGRAM case SIOCSIPHISTO: /* Verify that the user is root. */ - if (!suser()) { + if (!capable(CAP_NET_ADMIN)) { ret = -EPERM; break; } diff --git a/drivers/net/wavelan.h b/drivers/net/wavelan.h index f55bf48e2..38534c934 100644 --- a/drivers/net/wavelan.h +++ b/drivers/net/wavelan.h @@ -26,7 +26,7 @@ * product (OEM, like DEC RoamAbout, Digital Ocean, or Epson), * you might need to modify this part to accommodate your hardware. */ -const char MAC_ADDRESSES[][3] = +static const char MAC_ADDRESSES[][3] = { { 0x08, 0x00, 0x0E }, /* AT&T WaveLAN (standard) & DEC RoamAbout */ { 0x08, 0x00, 0x6A }, /* AT&T WaveLAN (alternate) */ @@ -49,14 +49,14 @@ const char MAC_ADDRESSES[][3] = * (as read in the offset register of the dac area). * Used to map channel numbers used by `wfreqsel' to frequencies */ -const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8, +static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8, 0xD0, 0xF0, 0xF8, 0x150 }; /* Frequencies of the 1.0 modem (fixed frequencies). * Use to map the PSA `subband' to a frequency * Note : all frequencies apart from the first one need to be multiplied by 10 */ -const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 }; +static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 }; diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c index 85febe47d..f28feea43 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -77,7 +77,6 @@ static int gx_fix = 0; #include <linux/version.h> #include <linux/module.h> -#include <linux/modversions.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/timer.h> |