--- linux-2.4/Documentation/Configure.help 2003-03-13 23:24:29.000000000 +0100 +++ linux-2.4/Documentation/Configure.help 2004-05-24 22:43:24.000000000 +0200 @@ -10561,6 +10561,15 @@ If unsure, say N here. +Raw HDLC Ethernet device support +CONFIG_HDLC_RAW_ETH + Say Y to this option if you want generic HDLC driver to support + raw HDLC Ethernet device emulation over WAN (Wide Area Network) + connections. + You will need it for Ethernet over HDLC bridges. + + If unsure, say N here. + Cisco HDLC support CONFIG_HDLC_CISCO Say Y to this option if you want generic HDLC driver to support @@ -10624,6 +10626,37 @@ If unsure, say N here. +CONFIG_HDLC_DEBUG_PKT + This option is for developers only - do NOT use on production + systems. + +CONFIG_HDLC_DEBUG_HARD_HEADER + This option is for developers only - do NOT use on production + systems. + +CONFIG_HDLC_DEBUG_ECN + This option is for developers only - do NOT use on production + systems. + +CONFIG_HDLC_DEBUG_RINGS + If you answer Y here you will be able to get a diagnostic dump of + port's TX and RX packet rings, using "sethdlc hdlcX private" + command. It does not affect normal operations. + + If unsure, say Y here. + +CONFIG_TAHOE9XX + This driver is for Tahoe 931/932/971/972 cards + If you have such a card, say Y or M here and see + + + If you want to compile the driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module + will be called tahoe9xx.o. + + If unsure, say N here. + Ethernet (10 or 100Mbit) CONFIG_NET_ETHERNET Ethernet (also called IEEE 802.3 or ISO 8802-2) is the most common --- linux-2.4/Documentation/networking/generic-hdlc.txt 2003-03-13 23:22:34.000000000 +0100 +++ linux-2.4/Documentation/networking/generic-hdlc.txt 2004-05-24 22:43:24.000000000 +0200 @@ -1,11 +1,13 @@ -Generic HDLC layer for Linux kernel 2.4/2.5 +Generic HDLC layer Krzysztof Halasa -May, 2001 +January, 2003 Generic HDLC layer currently supports: -- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP), -- raw HDLC (IPv4 only), +- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP). + Normal (routed) and Ethernet-bridged (Ethernet device emulation) + interfaces can share a single PVC. +- raw HDLC - either IP (IPv4) interface or Ethernet device emulation. - Cisco HDLC, - PPP (uses syncppp.c), - X.25 (uses X.25 routines). @@ -15,6 +17,10 @@ - RISCom/N2 by SDL Communications Inc. - and others, some not in the official kernel. +Ethernet device emulation (using HDLC or Frame-Relay PVC) is compatible +with IEEE 802.1Q (VLANs) and 802.1D (Ethernet bridging). + + Make sure the hdlc.o and the hardware driver are loaded. It should create a number of "hdlc" (hdlc0 etc) network devices, one for each WAN port. You'll need the "sethdlc" utility, get it from: @@ -58,6 +64,9 @@ no-parity / crc16 / crc16-pr0 (CRC16 with preset zeros) / crc32-itu crc16-itu (CRC16 with ITU-T polynomial) / crc16-itu-pr0 - sets parity +* hdlc-eth - Ethernet device emulation using HDLC. Parity and encoding + as above. + * cisco - sets Cisco HDLC mode (IP, IPv6 and IPX supported) interval - time in seconds between keepalive packets timeout - time in seconds after last received keepalive packet before @@ -77,7 +86,12 @@ n392 - error threshold - both user and network n393 - monitored events count - both user and network -* create | delete n - FR only - adds / deletes PVC interface with DLCI #n. +Frame-Relay only: +* create n | delete n - adds / deletes PVC interface with DLCI #n. + Newly created interface will be named pvc0, pvc1 etc. + +* create ether n | delete ether n - adds a device for Ethernet-bridged + frames. The device will be named pvceth0, pvceth1 etc. @@ -104,11 +118,11 @@ If you have a problem with N2 or C101 card, you can issue the "private" -command to see port's packet descriptor rings: +command to see port's packet descriptor rings (in kernel logs): sethdlc hdlc0 private -The hardware driver have to be build with CONFIG_HDLC_DEBUG_RINGS. +The hardware driver has to be build with CONFIG_HDLC_DEBUG_RINGS. Attaching this info to bug reports would be helpful. Anyway, let me know if you have problems using this. --- linux-2.4/drivers/net/wan/Config.in 2003-03-13 23:22:57.000000000 +0100 +++ linux-2.4/drivers/net/wan/Config.in 2004-05-24 22:43:24.000000000 +0200 @@ -68,6 +68,7 @@ tristate ' Generic HDLC layer' CONFIG_HDLC if [ "$CONFIG_HDLC" != "n" ]; then bool ' Raw HDLC support' CONFIG_HDLC_RAW + bool ' Raw HDLC Ethernet device support' CONFIG_HDLC_RAW_ETH bool ' Cisco HDLC support' CONFIG_HDLC_CISCO bool ' Frame Relay support' CONFIG_HDLC_FR bool ' Synchronous Point-to-Point Protocol (PPP) support' CONFIG_HDLC_PPP @@ -76,6 +77,7 @@ else comment ' X.25/LAPB support is disabled' fi + dep_tristate ' Tahoe 9xx support' CONFIG_TAHOE9XX $CONFIG_HDLC dep_tristate ' SDL RISCom/N2 support' CONFIG_N2 $CONFIG_HDLC dep_tristate ' Moxa C101 support' CONFIG_C101 $CONFIG_HDLC dep_tristate ' FarSync T-Series support' CONFIG_FARSYNC $CONFIG_HDLC --- linux-2.4/drivers/net/wan/hd64570.h 2003-03-13 23:22:57.000000000 +0100 +++ linux-2.4/drivers/net/wan/hd64570.h 2004-05-24 22:43:24.000000000 +0200 @@ -152,7 +152,7 @@ u32 bp; /* Buffer Pointer (24 bits) */ u16 len; /* Data Length */ u8 stat; /* Status */ - u8 unused; /* pads to 2-byte boundary */ + u8 unused2; }__attribute__ ((packed)) pkt_desc; --- linux-2.4/drivers/net/wan/hd6457x.c 2003-03-13 23:22:57.000000000 +0100 +++ linux-2.4/drivers/net/wan/hd6457x.c 2004-05-25 00:37:54.000000000 +0200 @@ -1,16 +1,16 @@ /* * Hitachi SCA HD64570 and HD64572 common driver for Linux * - * Copyright (C) 1998-2000 Krzysztof Halasa + * Copyright (C) 1998-2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. * * Sources of information: * Hitachi HD64570 SCA User's Manual * Hitachi HD64572 SCA-II User's Manual + * */ #include @@ -116,14 +116,14 @@ -static inline u8 next_desc(port_t *port, u8 desc) +static inline u16 next_desc(port_t *port, u16 desc, int transmit) { return (desc + 1) % port_to_card(port)->ring_buffers; } -static inline u16 desc_offset(port_t *port, u8 desc, u8 transmit) +static inline u16 desc_offset(port_t *port, u16 desc, int transmit) { /* Descriptor offset always fits in 16 bytes */ u8 buffs = port_to_card(port)->ring_buffers; @@ -133,7 +133,7 @@ -static inline pkt_desc* desc_address(port_t *port, u8 desc, u8 transmit) +static inline pkt_desc* desc_address(port_t *port, u16 desc, int transmit) { #ifdef PAGE0_ALWAYS_MAPPED return (pkt_desc*)(win0base(port_to_card(port)) @@ -159,7 +159,7 @@ static void sca_init_sync_port(port_t *port) { card_t *card = port_to_card(port); - u8 transmit, i; + int transmit, i; u16 dmac, buffs = card->ring_buffers; port->rxin = 0; @@ -233,7 +233,7 @@ card_t* card = port_to_card(port); u8 stat = sca_in(msci + ST1, card); /* read MSCI ST1 status */ - /* printk(KERN_DEBUG "MSCI INT: ST1=%02X ILAR=%02X\n", + /*printk(KERN_DEBUG "MSCI INT: ST1=%02X ILAR=%02X\n", stat, sca_in(ILAR, card)); */ /* Reset MSCI TX underrun status bit */ @@ -247,7 +247,7 @@ -static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, u8 rxin) +static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, u16 rxin) { struct sk_buff *skb; u16 len; @@ -303,6 +303,7 @@ /* Receive DMA interrupt service */ static inline void sca_rx_intr(port_t *port) { + int j=1000; u16 dmac = get_dmac_rx(port); card_t *card = port_to_card(port); u8 stat = sca_in(DSR_RX(phy_node(port)), card); /* read DMA Status */ @@ -315,11 +316,12 @@ if (stat & DSR_BOF) stats->rx_over_errors++; /* Dropped one or more frames */ - while (1) { + while (j) { u32 desc_off = desc_offset(port, port->rxin, 0); pkt_desc *desc; u32 cda = sca_ina(dmac + CDAL, card); + j--; if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc))) break; /* No frame received */ @@ -341,9 +343,10 @@ /* Set new error descriptor address */ sca_outa(desc_off, dmac + EDAL, card); - port->rxin = next_desc(port, port->rxin); + port->rxin = next_desc(port, port->rxin, 0); } - + if (!j) printk(KERN_INFO "sca_rx_intr: loop\n"); + /* make sure RX DMA is enabled */ sca_out(DSR_DE, DSR_RX(phy_node(port)), card); } @@ -356,6 +359,7 @@ u16 dmac = get_dmac_tx(port); card_t* card = port_to_card(port); u8 stat; + int j=1000; spin_lock(&port->lock); @@ -365,21 +369,22 @@ sca_out((stat & (DSR_EOT | DSR_EOM | DSR_BOF | DSR_COF)) | DSR_DWE, DSR_TX(phy_node(port)), card); - while (1) { + while (j) { pkt_desc *desc; u32 desc_off = desc_offset(port, port->txlast, 1); u32 cda = sca_ina(dmac + CDAL, card); + j--; if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc))) break; /* Transmitter is/will_be sending this frame */ desc = desc_address(port, port->txlast, 1); port->hdlc.stats.tx_packets++; - port->hdlc.stats.tx_bytes += readw(&desc->len); + port ->hdlc.stats.tx_bytes += readw(&desc->len); writeb(0, &desc->stat); /* Free descriptor */ - port->txlast = (port->txlast + 1) % - port_to_card(port)->ring_buffers; - } + port->txlast = next_desc(port, port->txlast, 1); + } + if (!j) printk(KERN_INFO "sca_tx_intr: loop\n"); netif_wake_queue(hdlc_to_dev(&port->hdlc)); spin_unlock(&port->lock); @@ -390,9 +395,7 @@ static void sca_intr(int irq, void* dev_id, struct pt_regs *regs) { card_t *card = dev_id; -/* Maximum events to handle at each interrupt - should I increase it? */ - int boguscnt = 4; - int i; + int i, j=1000; u8 stat; #ifndef ALL_PAGES_ALWAYS_MAPPED @@ -400,6 +403,11 @@ #endif while((stat = sca_intr_status(card)) != 0) { + j--; + if (!j) { + printk(KERN_INFO "sca_intr: loop\n"); + break; + } for (i = 0; i < 2; i++) { port_t *port = get_port(card, i); if (port) { @@ -413,18 +421,9 @@ sca_tx_intr(port); } - if (--boguscnt < 0) { -#if 0 - printk(KERN_ERR "%s: too much work at " - "interrupt\n", - hdlc_to_name(&port->hdlc)); -#endif - goto exit; - } } } - exit: #ifndef ALL_PAGES_ALWAYS_MAPPED openwin(card, page); /* Restore original page */ #endif @@ -436,7 +435,7 @@ static void sca_set_port(port_t *port) { card_t* card = port_to_card(port); - u8 msci = get_msci(port); + u16 msci = get_msci(port); u8 md2 = sca_in(msci + MD2, card); unsigned int tmc, br = 10, brv = 1024; @@ -640,14 +639,13 @@ openwin(card, 0); #endif - printk(KERN_ERR "RX ring: CDA=%u EDA=%u DSR=%02X in=%u " - "%sactive", + printk(KERN_ERR "RX ring: CDA=%u EDA=%u DSR=%02X in=%u %sactive", sca_ina(get_dmac_rx(port) + CDAL, card), sca_ina(get_dmac_rx(port) + EDAL, card), sca_in(DSR_RX(phy_node(port)), card), port->rxin, sca_in(DSR_RX(phy_node(port)), card) & DSR_DE?"":"in"); - for (cnt = 0; cntring_buffers; cnt++) + for (cnt = 0; cntrx_ring_buffers; cnt++) printk(" %02X", readb(&(desc_address(port, cnt, 0)->stat))); @@ -751,7 +749,7 @@ writeb(ST_TX_EOM, &desc->stat); dev->trans_start = jiffies; - port->txin = next_desc(port, port->txin); + port->txin = next_desc(port, port->txin, 1); sca_outa(desc_offset(port, port->txin, 1), get_dmac_tx(port) + EDAL, card); @@ -768,7 +766,50 @@ } -static void sca_init(card_t *card, int wait_states) + +#ifdef NEED_DETECT_RAM +static u32 __devinit sca_detect_ram(card_t *card, u8 *rambase, u32 ramsize) +{ + /* Round RAM size to 32 bits, fill from end to start */ + u32 i = ramsize &= ~3; + +#ifndef ALL_PAGES_ALWAYS_MAPPED + u32 size = winsize(card); + + openwin(card, (i - 4) / size); /* select last window */ +#endif + do { + i -= 4; +#ifndef ALL_PAGES_ALWAYS_MAPPED + if ((i + 4) % size == 0) + openwin(card, i / size); + writel(i ^ 0x12345678, rambase + i % size); +#else + writel(i ^ 0x12345678, rambase + i); +#endif + }while (i > 0); + + for (i = 0; i < ramsize ; i += 4) { +#ifndef ALL_PAGES_ALWAYS_MAPPED + if (i % size == 0) + openwin(card, i / size); + + if (readl(rambase + i % size) != (i ^ 0x12345678)) + break; +#else + if (readl(rambase + i) != (i ^ 0x12345678)) + break; +#endif + } + for (i = 0; i < ramsize ; i += 4) writel(0, rambase + i); + + return i; +} +#endif /* NEED_DETECT_RAM */ + + + +static void __devinit sca_init(card_t *card, int wait_states) { sca_out(wait_states, WCRL, card); /* Wait Control */ sca_out(wait_states, WCRM, card); --- linux-2.4/drivers/net/wan/hdlc_cisco.c 2003-03-13 23:22:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/hdlc_cisco.c 2004-05-24 22:43:24.000000000 +0200 @@ -2,12 +2,11 @@ * Generic HDLC support routines for Linux * Cisco HDLC support * - * Copyright (C) 2000 - 2001 Krzysztof Halasa + * Copyright (C) 2000 - 2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. */ #include @@ -80,17 +79,43 @@ data->par1 = htonl(par1); data->par2 = htonl(par2); data->rel = 0xFFFF; - data->time = htonl(jiffies * 1000 / HZ); + /* we will need do_div here if 1000 % HZ != 0 */ + data->time = htonl(jiffies * (1000 / HZ)); skb_put(skb, sizeof(cisco_packet)); skb->priority = TC_PRIO_CONTROL; skb->dev = hdlc_to_dev(hdlc); + skb->nh.raw = skb->data; dev_queue_xmit(skb); } +static unsigned short cisco_type_trans(struct sk_buff *skb, + struct net_device *dev) +{ + hdlc_header *data = (hdlc_header*)skb->data; + + if (skb->len < sizeof(hdlc_header)) + return __constant_htons(ETH_P_HDLC); + + if (data->address != CISCO_MULTICAST && + data->address != CISCO_UNICAST) + return __constant_htons(ETH_P_HDLC); + + switch(data->protocol) { + case __constant_htons(ETH_P_IP): + case __constant_htons(ETH_P_IPX): + case __constant_htons(ETH_P_IPV6): + skb_pull(skb, sizeof(hdlc_header)); + return data->protocol; + default: + return __constant_htons(ETH_P_HDLC); + } +} + + static void cisco_rx(struct sk_buff *skb) { hdlc_device *hdlc = dev_to_hdlc(skb->dev); @@ -109,14 +134,6 @@ skb_pull(skb, sizeof(hdlc_header)); switch(ntohs(data->protocol)) { - case ETH_P_IP: - case ETH_P_IPX: - case ETH_P_IPV6: - skb->protocol = data->protocol; - skb->dev = hdlc_to_dev(hdlc); - netif_rx(skb); - return; - case CISCO_SYS_INFO: /* Packet is not needed, drop it. */ dev_kfree_skb_any(skb); @@ -288,6 +305,7 @@ hdlc->open = cisco_open; hdlc->stop = cisco_close; hdlc->netif_rx = cisco_rx; + hdlc->type_trans = cisco_type_trans; hdlc->proto = IF_PROTO_CISCO; dev->hard_start_xmit = hdlc->xmit; dev->hard_header = cisco_hard_header; --- linux-2.4/drivers/net/wan/hdlc_fr.c 2003-03-13 23:22:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/hdlc_fr.c 2004-05-24 22:43:24.000000000 +0200 @@ -2,13 +2,22 @@ * Generic HDLC support routines for Linux * Frame Relay support * - * Copyright (C) 1999 - 2001 Krzysztof Halasa + * Copyright (C) 1999 - 2003 Krzysztof Halasa * * 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. - */ + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + + Theory of PVC state in DCE mode: + + (exist,new) -> 0,0 when "PVC create" or if "link unreliable" + 0,x -> 1,1 if "link reliable" when sending FULL STATUS + 1,1 -> 1,0 if received FULL STATUS ACK + + (active) -> 0 when "ifconfig PVC down" or "link unreliable" or "PVC create" + -> 1 when "PVC up" and (exist,new) = 1,0 +*/ #include #include @@ -20,19 +29,23 @@ #include #include #include +#include #include #include #include +#include #include __inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci) { - pvc_device *pvc=hdlc->state.fr.first_pvc; - - while (pvc) { - if (netdev_dlci(&pvc->netdev) == dlci) + pvc_device *pvc = hdlc->state.fr.first_pvc; + + while(pvc) { + if (pvc->dlci == dlci) return pvc; + if (pvc->dlci > dlci) + return NULL; /* the listed is sorted */ pvc = pvc->next; } @@ -40,18 +53,72 @@ } +__inline__ pvc_device* add_pvc(hdlc_device *hdlc, u16 dlci) +{ + pvc_device *pvc, **pvc_p = &hdlc->state.fr.first_pvc; + + while(*pvc_p) { + if ((*pvc_p)->dlci == dlci) + return *pvc_p; + if ((*pvc_p)->dlci > dlci) + break; /* the listed is sorted */ + pvc_p = &(*pvc_p)->next; + } + + pvc = kmalloc(sizeof(pvc_device), GFP_ATOMIC); + if (!pvc) + return NULL; + + memset(pvc, 0, sizeof(pvc_device)); + pvc->dlci = dlci; + pvc->master = hdlc; + pvc->next = *pvc_p; /* Put it in the chain */ + *pvc_p = pvc; + return pvc; +} + + +__inline__ int pvc_is_used(pvc_device *pvc) +{ + return pvc->main != NULL || pvc->ether != NULL; +} + + +__inline__ void delete_unused_pvcs(hdlc_device *hdlc) +{ + pvc_device **pvc_p = &hdlc->state.fr.first_pvc; + + while(*pvc_p) { + if (!pvc_is_used(*pvc_p)) { + pvc_device *pvc = *pvc_p; + *pvc_p = pvc->next; + kfree(pvc); + continue; + } + pvc_p = &(*pvc_p)->next; + } +} -__inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status, - int *active, int *new) + +__inline__ struct net_device** get_dev_p(pvc_device *pvc, int type) { - *new = (status[2] & 0x08); - *active = (!*new && (status[2] & 0x02)); + if (type == ARPHRD_ETHER) + return &pvc->ether; + else + return &pvc->main; +} + + +__inline__ u16 status_to_dlci(u8 *status, int *active, int *new) +{ + *new = (status[2] & 0x08) ? 1 : 0; + *active = (status[2] & 0x02) ? 1 : 0; return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3); } -__inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status, +__inline__ void dlci_to_status(u16 dlci, u8 *status, int active, int new) { status[0] = (dlci>>4) & 0x3F; @@ -66,37 +133,50 @@ -static int fr_hard_header(struct sk_buff *skb, struct net_device *dev, - u16 type, void *daddr, void *saddr, unsigned int len) +static int fr_hard_header(struct sk_buff **skb_p, u16 dlci) { u16 head_len; + struct sk_buff *skb = *skb_p; - if (!daddr) - daddr = dev->broadcast; - -#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER - printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name); -#endif - - switch(type) { - case ETH_P_IP: + switch(skb->protocol) { + case __constant_ntohs(ETH_P_IP): head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_IP; break; - case ETH_P_IPV6: + case __constant_ntohs(ETH_P_IPV6): head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_IPV6; break; - case LMI_PROTO: + case __constant_ntohs(LMI_PROTO): head_len = 4; skb_push(skb, head_len); skb->data[3] = LMI_PROTO; break; + case __constant_ntohs(ETH_P_802_3): + head_len = 10; + if (skb_headroom(skb) < head_len) { + struct sk_buff *skb2 = skb_realloc_headroom(skb, + head_len); + if (!skb2) + return -ENOBUFS; + dev_kfree_skb(skb); + skb = *skb_p = skb2; + } + skb_push(skb, head_len); + skb->data[3] = FR_PAD; + skb->data[4] = NLPID_SNAP; + skb->data[5] = FR_PAD; + skb->data[6] = 0x80; + skb->data[7] = 0xC2; + skb->data[8] = 0x00; + skb->data[9] = 0x07; /* bridged Ethernet frame w/out FCS */ + break; + default: head_len = 10; skb_push(skb, head_len); @@ -105,14 +185,12 @@ skb->data[5] = FR_PAD; skb->data[6] = FR_PAD; skb->data[7] = FR_PAD; - skb->data[8] = type>>8; - skb->data[9] = (u8)type; + *(u16*)(skb->data + 8) = skb->protocol; } - memcpy(skb->data, daddr, 2); + dlci_to_q922(skb->data, dlci); skb->data[2] = FR_UI; - - return head_len; + return 0; } @@ -124,13 +202,12 @@ if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0) return -EIO; /* Master must be UP in order to activate PVC */ - if (pvc->master->state.fr.settings.lmi != LMI_NONE) - pvc->state.active = 0; - else - pvc->state.active = 1; + if (pvc->open_count++ == 0) { + if (pvc->master->state.fr.settings.lmi == LMI_NONE) + pvc->state.active = 1; - pvc->state.new = 0; - pvc->master->state.fr.changed = 1; + pvc->master->state.fr.dce_changed = 1; + } return 0; } @@ -139,38 +216,94 @@ static int pvc_close(struct net_device *dev) { pvc_device *pvc = dev_to_pvc(dev); - pvc->state.active = pvc->state.new = 0; - pvc->master->state.fr.changed = 1; + + if (--pvc->open_count == 0) { + if (pvc->master->state.fr.settings.lmi == LMI_NONE) + pvc->state.active = 0; + + if (pvc->master->state.fr.settings.dce) { + pvc->master->state.fr.dce_changed = 1; + pvc->state.active = 0; + } + } return 0; } -static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) +int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { pvc_device *pvc = dev_to_pvc(dev); + fr_proto_pvc_info info; - if (pvc->state.active) { - skb->dev = hdlc_to_dev(pvc->master); - pvc->stats.tx_bytes += skb->len; - pvc->stats.tx_packets++; - if (pvc->state.fecn) - pvc->stats.tx_compressed++; /* TX Congestion counter */ - dev_queue_xmit(skb); - } else { - pvc->stats.tx_dropped++; - dev_kfree_skb(skb); + if (ifr->ifr_settings.type == IF_GET_PROTO) { + if (dev->type == ARPHRD_ETHER) + ifr->ifr_settings.type = IF_PROTO_FR_ETH_PVC; + else + ifr->ifr_settings.type = IF_PROTO_FR_PVC; + + if (ifr->ifr_settings.size < sizeof(info)) { + /* data size wanted */ + ifr->ifr_settings.size = sizeof(info); + return -ENOBUFS; + } + + info.dlci = pvc->dlci; + memcpy(info.master, hdlc_to_name(pvc->master), IFNAMSIZ); + if (copy_to_user(ifr->ifr_settings.ifs_ifsu.fr_pvc_info, + &info, sizeof(info))) + return -EFAULT; + return 0; } - return 0; + return -EINVAL; +} + + +__inline__ struct net_device_stats *pvc_get_stats(struct net_device *dev) +{ + return (struct net_device_stats *) + ((char *)dev + sizeof(struct net_device)); } -static struct net_device_stats *pvc_get_stats(struct net_device *dev) +static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) { pvc_device *pvc = dev_to_pvc(dev); - return &pvc->stats; + struct net_device_stats *stats = pvc_get_stats(dev); + + if (pvc->state.active) { + if (dev->type == ARPHRD_ETHER) { + int pad = ETH_ZLEN - skb->len; + if (pad > 0) { /* Pad the frame with zeros */ + int len = skb->len; + if (skb_tailroom(skb) < pad) + if (pskb_expand_head(skb, 0, pad, + GFP_ATOMIC)) { + stats->tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + skb_put(skb, pad); + memset(skb->data + len, 0, pad); + } + skb->protocol = __constant_htons(ETH_P_802_3); + } + if (!fr_hard_header(&skb, pvc->dlci)) { + stats->tx_bytes += skb->len; + stats->tx_packets++; + if (pvc->state.fecn) /* TX Congestion counter */ + stats->tx_compressed++; + skb->dev = hdlc_to_dev(pvc->master); + dev_queue_xmit(skb); + return 0; + } + } + + stats->tx_dropped++; + dev_kfree_skb(skb); + return 0; } @@ -187,9 +320,15 @@ static inline void fr_log_dlci_active(pvc_device *pvc) { - printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc), - pvc->state.active ? "" : "in", - pvc->state.new ? " new" : ""); + printk(KERN_INFO "%s: DLCI %d [%s%s%s]%s %s\n", + hdlc_to_name(pvc->master), + pvc->dlci, + pvc->main ? pvc->main->name : "", + pvc->main && pvc->ether ? " " : "", + pvc->ether ? pvc->ether->name : "", + pvc->state.new ? " new" : "", + !pvc->state.exist ? "deleted" : + pvc->state.active ? "active" : "inactive"); } @@ -213,8 +352,8 @@ int i = 0; if (hdlc->state.fr.settings.dce && fullrep) { - len += hdlc->state.fr.pvc_count * (2 + stat_len); - if (len > HDLC_MAX_MTU) { + len += hdlc->state.fr.dce_pvc_count * (2 + stat_len); + if (len > HDLC_MAX_MRU) { printk(KERN_WARNING "%s: Too many PVCs while sending " "LMI full report\n", hdlc_to_name(hdlc)); return; @@ -224,12 +363,13 @@ skb = dev_alloc_skb(len); if (!skb) { printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n", - hdlc_to_name(hdlc)); + hdlc_to_name(hdlc)); return; } memset(skb->data, 0, len); skb_reserve(skb, 4); - fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0); + skb->protocol = __constant_htons(LMI_PROTO); + fr_hard_header(&skb, LMI_DLCI); data = skb->tail; data[i++] = LMI_CALLREF; data[i++] = hdlc->state.fr.settings.dce @@ -253,16 +393,20 @@ ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT; data[i++] = stat_len; - if (hdlc->state.fr.reliable && - (pvc->netdev.flags & IFF_UP) && - !pvc->state.active && - !pvc->state.new) { - pvc->state.new = 1; + /* LMI start/restart */ + if (hdlc->state.fr.reliable && !pvc->state.exist) { + pvc->state.exist = pvc->state.new = 1; + fr_log_dlci_active(pvc); + } + + /* ifconfig PVC up */ + if (pvc->open_count && !pvc->state.active && + pvc->state.exist && !pvc->state.new) { + pvc->state.active = 1; fr_log_dlci_active(pvc); } - dlci_to_status(hdlc, netdev_dlci(&pvc->netdev), - data + i, + dlci_to_status(pvc->dlci, data + i, pvc->state.active, pvc->state.new); i += stat_len; pvc = pvc->next; @@ -272,6 +416,7 @@ skb_put(skb, i); skb->priority = TC_PRIO_CONTROL; skb->dev = hdlc_to_dev(hdlc); + skb->nh.raw = skb->data; dev_queue_xmit(skb); } @@ -312,10 +457,11 @@ if (reliable) { hdlc->state.fr.n391cnt = 0; /* Request full status */ - hdlc->state.fr.changed = 1; + hdlc->state.fr.dce_changed = 1; } else { while (pvc) { /* Deactivate all PVCs */ - pvc->state.new = pvc->state.active = 0; + pvc->state.exist = 0; + pvc->state.active = pvc->state.new = 0; pvc = pvc->next; } } @@ -346,7 +492,7 @@ { int stat_len; pvc_device *pvc; - int reptype = -1, error; + int reptype = -1, error, no_ram; u8 rxseq, txseq; int i; @@ -420,20 +566,18 @@ while (pvc) { if (pvc->state.new) { pvc->state.new = 0; - pvc->state.active = 1; - fr_log_dlci_active(pvc); /* Tell DTE that new PVC is now active */ - hdlc->state.fr.changed = 1; + hdlc->state.fr.dce_changed = 1; } pvc = pvc->next; } } - if (hdlc->state.fr.changed) { + if (hdlc->state.fr.dce_changed) { reptype = LMI_FULLREP; hdlc->state.fr.fullrep_sent = 1; - hdlc->state.fr.changed = 0; + hdlc->state.fr.dce_changed = 0; } fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0); @@ -449,13 +593,14 @@ pvc = hdlc->state.fr.first_pvc; while (pvc) { - pvc->state.deleted = pvc->state.active; /* mark active PVCs */ + pvc->state.deleted = 1; pvc = pvc->next; } + no_ram = 0; while (skb->len >= i + 2 + stat_len) { u16 dlci; - int active, new; + unsigned int active, new; if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT) ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { @@ -472,21 +617,28 @@ } i++; - dlci = status_to_dlci(hdlc, skb->data + i, &active, &new); - pvc = find_pvc(hdlc, dlci); + dlci = status_to_dlci(skb->data + i, &active, &new); + + pvc = add_pvc(hdlc, dlci); + + if (!pvc && !no_ram) { + printk(KERN_WARNING + "%s: Memory squeeze on fr_lmi_recv()\n", + hdlc_to_name(hdlc)); + no_ram = 1; + } - active |= new; if (pvc) { - if (active && !pvc->state.active && - (pvc->netdev.flags & IFF_UP)) { + pvc->state.exist = 1; + pvc->state.deleted = 0; + if (active != pvc->state.active || + new != pvc->state.new || + !pvc->state.exist) { + pvc->state.new = new; pvc->state.active = active; fr_log_dlci_active(pvc); } - pvc->state.deleted = 0; } - else if (new) - printk(KERN_INFO "%s: new PVC available, DLCI=%u\n", - hdlc_to_name(hdlc), dlci); i += stat_len; } @@ -494,10 +646,10 @@ pvc = hdlc->state.fr.first_pvc; while (pvc) { - if (pvc->state.deleted) { + if (pvc->state.deleted && pvc->state.exist) { pvc->state.active = pvc->state.new = 0; + pvc->state.exist = 0; fr_log_dlci_active(pvc); - pvc->state.deleted = 0; } pvc = pvc->next; } @@ -517,8 +669,9 @@ u8 *data = skb->data; u16 dlci; pvc_device *pvc; + struct net_device *dev = NULL; - if (skb->len<4 || fh->ea1 || data[2] != FR_UI) + if (skb->len <= 4 || fh->ea1 || data[2] != FR_UI) goto rx_error; dlci = q922_to_dlci(skb->data); @@ -550,57 +703,39 @@ printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n", hdlc_to_name(hdlc), dlci); #endif - goto rx_error; - } - - if ((pvc->netdev.flags & IFF_UP) == 0) { -#ifdef CONFIG_HDLC_DEBUG_PKT - printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n", - hdlc_to_name(hdlc), dlci); -#endif - goto rx_error; + dev_kfree_skb_any(skb); + return; } - pvc->stats.rx_packets++; /* PVC traffic */ - pvc->stats.rx_bytes += skb->len; - - if (pvc->state.fecn != (fh->fecn ? PVC_STATE_FECN : 0)) { + if (pvc->state.fecn != fh->fecn) { #ifdef CONFIG_HDLC_DEBUG_ECN - printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc), - fh->fecn ? "N" : "FF"); + printk(KERN_DEBUG "%s: DLCI %d FECN O%s\n", hdlc_to_name(pvc), + dlci, fh->fecn ? "N" : "FF"); #endif pvc->state.fecn ^= 1; } - if (pvc->state.becn != (fh->becn ? PVC_STATE_BECN : 0)) { + if (pvc->state.becn != fh->becn) { #ifdef CONFIG_HDLC_DEBUG_ECN - printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc), - fh->becn ? "N" : "FF"); + printk(KERN_DEBUG "%s: DLCI %d BECN O%s\n", hdlc_to_name(pvc), + dlci, fh->becn ? "N" : "FF"); #endif pvc->state.becn ^= 1; } - if (pvc->state.becn) - pvc->stats.rx_compressed++; - - skb->dev = &pvc->netdev; if (data[3] == NLPID_IP) { skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ + dev = pvc->main; skb->protocol = htons(ETH_P_IP); - netif_rx(skb); - return; - } - - if (data[3] == NLPID_IPV6) { + } else if (data[3] == NLPID_IPV6) { skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ + dev = pvc->main; skb->protocol = htons(ETH_P_IPV6); - netif_rx(skb); - return; - } - if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD) { + } else if (skb->len > 10 && data[3] == FR_PAD && + data[4] == NLPID_SNAP && data[5] == FR_PAD) { u16 oui = ntohs(*(u16*)(data + 6)); u16 pid = ntohs(*(u16*)(data + 8)); skb_pull(skb, 10); @@ -610,23 +745,39 @@ case ETH_P_IPX: case ETH_P_IP: /* a long variant */ case ETH_P_IPV6: + dev = pvc->main; skb->protocol = htons(pid); break; + case 0x80C20007: /* bridged Ethernet frame */ + if ((dev = pvc->ether) != NULL) + skb->protocol = eth_type_trans(skb, dev); + break; + default: printk(KERN_INFO "%s: Unsupported protocol, OUI=%x " "PID=%x\n", hdlc_to_name(hdlc), oui, pid); dev_kfree_skb_any(skb); return; } - - netif_rx(skb); + } else { + printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x " + "length = %i\n", hdlc_to_name(hdlc), data[3], skb->len); + dev_kfree_skb_any(skb); return; } - printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x\n", - hdlc_to_name(hdlc), data[3]); - dev_kfree_skb_any(skb); + if (dev) { + struct net_device_stats *stats = pvc_get_stats(dev); + stats->rx_packets++; /* PVC traffic */ + stats->rx_bytes += skb->len; + if (pvc->state.becn) + stats->rx_compressed++; + skb->dev = dev; + netif_rx(skb); + } else + dev_kfree_skb_any(skb); + return; rx_error: @@ -641,7 +792,7 @@ if (hdlc->state.fr.settings.lmi != LMI_NONE) { hdlc->state.fr.last_poll = 0; hdlc->state.fr.reliable = 0; - hdlc->state.fr.changed = 1; + hdlc->state.fr.dce_changed = 1; hdlc->state.fr.request = 0; hdlc->state.fr.fullrep_sent = 0; hdlc->state.fr.last_errors = 0xFFFFFFFF; @@ -669,90 +820,119 @@ if (hdlc->state.fr.settings.lmi != LMI_NONE) del_timer_sync(&hdlc->state.fr.timer); - while(pvc) { - dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */ + while(pvc) { /* Shutdown all PVCs for this FRAD */ + if (pvc->main) + dev_close(pvc->main); + if (pvc->ether) + dev_close(pvc->ether); + pvc->state.active = pvc->state.new = pvc->state.fecn = + pvc->state.becn = 0; + pvc->state.exist = 0; pvc = pvc->next; } } - -static int fr_pvc(hdlc_device *hdlc, unsigned int dlci, int create) +static int fr_add_pvc(hdlc_device *hdlc, unsigned int dlci, int type) { - pvc_device **pvc_p = &hdlc->state.fr.first_pvc; - pvc_device *pvc; - int result; + pvc_device *pvc = NULL; + struct net_device *dev; + int result, used; + char * prefix = "pvc%d"; - if(dlci <= 0 || dlci >= 1024) - return -EINVAL; /* Only 10 bits for DLCI, DLCI 0 reserved */ + if (type == ARPHRD_ETHER) + prefix = "pvceth%d"; - while(*pvc_p) { - if (netdev_dlci(&(*pvc_p)->netdev) == dlci) - break; - pvc_p = &(*pvc_p)->next; + if ((pvc = add_pvc(hdlc, dlci)) == NULL) { + printk(KERN_WARNING "%s: Memory squeeze on fr_add_pvc()\n", + hdlc_to_name(hdlc)); + return -ENOBUFS; } - if (create) { /* Create PVC */ - if (*pvc_p != NULL) - return -EEXIST; - - pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL); - if (!pvc) { - printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n", - hdlc_to_name(hdlc)); - return -ENOBUFS; - } - memset(pvc, 0, sizeof(pvc_device)); + if (*get_dev_p(pvc, type)) + return -EEXIST; - pvc->netdev.hard_start_xmit = pvc_xmit; - pvc->netdev.get_stats = pvc_get_stats; - pvc->netdev.open = pvc_open; - pvc->netdev.stop = pvc_close; - pvc->netdev.change_mtu = pvc_change_mtu; - pvc->netdev.mtu = HDLC_MAX_MTU; - - pvc->netdev.type = ARPHRD_DLCI; - pvc->netdev.hard_header_len = 16; - pvc->netdev.hard_header = fr_hard_header; - pvc->netdev.tx_queue_len = 0; - pvc->netdev.flags = IFF_POINTOPOINT; - - pvc->master = hdlc; - *(u16*)pvc->netdev.dev_addr = htons(dlci); - dlci_to_q922(pvc->netdev.broadcast, dlci); - pvc->netdev.addr_len = 2; + used = pvc_is_used(pvc); - result = dev_alloc_name(&pvc->netdev, "pvc%d"); - if (result < 0) { - kfree(pvc); - *pvc_p = NULL; - return result; - } - - if (register_netdevice(&pvc->netdev) != 0) { - kfree(pvc); - *pvc_p = NULL; - return -EIO; - } + dev = kmalloc(sizeof(struct net_device) + + sizeof(struct net_device_stats), GFP_KERNEL); + if (!dev) { + printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n", + hdlc_to_name(hdlc)); + delete_unused_pvcs(hdlc); + return -ENOBUFS; + } + memset(dev, 0, sizeof(struct net_device) + + sizeof(struct net_device_stats)); - hdlc->state.fr.changed = 1; - hdlc->state.fr.pvc_count++; - return 0; + if (type == ARPHRD_ETHER) { + ether_setup(dev); + memcpy(dev->dev_addr, "\x00\x01", 2); + get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2); + } else { + dev->type = ARPHRD_DLCI; + dev->flags = IFF_POINTOPOINT; + dev->hard_header_len = 10; + dev->addr_len = 2; + *(u16*)dev->dev_addr = htons(dlci); + dlci_to_q922(dev->broadcast, dlci); + } + dev->hard_start_xmit = pvc_xmit; + dev->get_stats = pvc_get_stats; + dev->open = pvc_open; + dev->stop = pvc_close; + dev->do_ioctl = pvc_ioctl; + dev->change_mtu = pvc_change_mtu; + dev->mtu = HDLC_MAX_MTU; + dev->tx_queue_len = 0; + dev->priv = pvc; + + result = dev_alloc_name(dev, prefix); + if (result < 0) { + kfree(dev); + delete_unused_pvcs(hdlc); + return result; + } + + if (register_netdevice(dev) != 0) { + kfree(dev); + delete_unused_pvcs(hdlc); + return -EIO; + } + + *get_dev_p(pvc, type) = dev; + if (!used) { + hdlc->state.fr.dce_changed = 1; + hdlc->state.fr.dce_pvc_count++; } + return 0; +} + + - if (*pvc_p == NULL) /* Delete PVC */ +static int fr_del_pvc(hdlc_device *hdlc, unsigned int dlci, int type) +{ + pvc_device *pvc; + struct net_device *dev; + + if ((pvc = find_pvc(hdlc, dlci)) == NULL) return -ENOENT; - pvc = *pvc_p; + if ((dev = *get_dev_p(pvc, type)) == NULL) + return -ENOENT; - if (pvc->netdev.flags & IFF_UP) + if (dev->flags & IFF_UP) return -EBUSY; /* PVC in use */ - hdlc->state.fr.changed = 1; - hdlc->state.fr.pvc_count--; - *pvc_p = pvc->next; - unregister_netdevice(&pvc->netdev); - kfree(pvc); + unregister_netdevice(dev); + kfree(dev); + *get_dev_p(pvc, type) = NULL; + + if (!pvc_is_used(pvc)) { + hdlc->state.fr.dce_pvc_count--; + hdlc->state.fr.dce_changed = 1; + } + delete_unused_pvcs(hdlc); return 0; } @@ -763,14 +943,21 @@ pvc_device *pvc = hdlc->state.fr.first_pvc; while(pvc) { pvc_device *next = pvc->next; - unregister_netdev(&pvc->netdev); + if (pvc->main) { + unregister_netdevice(pvc->main); + kfree(pvc->main); + } + if (pvc->ether) { + unregister_netdevice(pvc->ether); + kfree(pvc->ether); + } kfree(pvc); pvc = next; } hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */ - hdlc->state.fr.pvc_count = 0; - hdlc->state.fr.changed = 1; + hdlc->state.fr.dce_pvc_count = 0; + hdlc->state.fr.dce_changed = 1; } @@ -828,25 +1015,27 @@ if (hdlc->proto != IF_PROTO_FR) { hdlc_proto_detach(hdlc); hdlc->state.fr.first_pvc = NULL; - hdlc->state.fr.pvc_count = 0; + hdlc->state.fr.dce_pvc_count = 0; } memcpy(&hdlc->state.fr.settings, &new_settings, size); hdlc->open = fr_open; hdlc->stop = fr_close; hdlc->netif_rx = fr_rx; + hdlc->type_trans = NULL; hdlc->proto_detach = fr_destroy; hdlc->proto = IF_PROTO_FR; dev->hard_start_xmit = hdlc->xmit; - dev->hard_header = fr_hard_header; + dev->hard_header = NULL; dev->type = ARPHRD_FRAD; - dev->addr_len = 2; - *(u16*)dev->dev_addr = htons(LMI_DLCI); - dlci_to_q922(dev->broadcast, LMI_DLCI); + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->addr_len = 0; return 0; case IF_PROTO_FR_ADD_PVC: case IF_PROTO_FR_DEL_PVC: + case IF_PROTO_FR_ADD_ETH_PVC: + case IF_PROTO_FR_DEL_ETH_PVC: if(!capable(CAP_NET_ADMIN)) return -EPERM; @@ -854,8 +1043,20 @@ sizeof(fr_proto_pvc))) return -EFAULT; - return fr_pvc(hdlc, pvc.dlci, - ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC); + if (pvc.dlci <= 0 || pvc.dlci >= 1024) + return -EINVAL; /* Only 10 bits, DLCI 0 reserved */ + + if (ifr->ifr_settings.type == IF_PROTO_FR_ADD_ETH_PVC || + ifr->ifr_settings.type == IF_PROTO_FR_DEL_ETH_PVC) + result = ARPHRD_ETHER; /* bridged Ethernet device */ + else + result = ARPHRD_DLCI; + + if (ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC || + ifr->ifr_settings.type == IF_PROTO_FR_ADD_ETH_PVC) + return fr_add_pvc(hdlc, pvc.dlci, result); + else + return fr_del_pvc(hdlc, pvc.dlci, result); } return -EINVAL; --- linux-2.4/drivers/net/wan/hdlc_generic.c 2003-03-13 23:22:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/hdlc_generic.c 2004-05-24 22:43:24.000000000 +0200 @@ -1,17 +1,13 @@ /* * Generic HDLC support routines for Linux * - * Copyright (C) 1999 - 2001 Krzysztof Halasa + * Copyright (C) 1999 - 2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. * - * Current status: - * - this is work in progress - * - not heavily tested on SMP - * - currently supported: + * Currently supported: * * raw IP-in-HDLC * * Cisco HDLC * * Frame Relay with ANSI or CCITT LMI (both user and network side) @@ -37,7 +33,7 @@ #include -static const char* version = "HDLC support module revision 1.11"; +static const char* version = "HDLC support module revision 1.14"; static int hdlc_change_mtu(struct net_device *dev, int new_mtu) @@ -60,7 +56,13 @@ static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p) { - dev_to_hdlc(dev)->netif_rx(skb); + hdlc_device *hdlc = dev_to_hdlc(dev); + if (hdlc->netif_rx) + hdlc->netif_rx(skb); + else { + hdlc->stats.rx_dropped++; /* Shouldn't happen */ + dev_kfree_skb(skb); + } return 0; } @@ -69,6 +71,10 @@ #define hdlc_raw_ioctl(hdlc, ifr) -ENOSYS #endif +#ifndef CONFIG_HDLC_RAW_ETH +#define hdlc_raw_eth_ioctl(hdlc, ifr) -ENOSYS +#endif + #ifndef CONFIG_HDLC_PPP #define hdlc_ppp_ioctl(hdlc, ifr) -ENOSYS #endif @@ -96,6 +102,7 @@ switch(ifr->ifr_settings.type) { case IF_PROTO_HDLC: + case IF_PROTO_HDLC_ETH: case IF_PROTO_PPP: case IF_PROTO_CISCO: case IF_PROTO_FR: @@ -109,6 +116,7 @@ switch(proto) { case IF_PROTO_HDLC: return hdlc_raw_ioctl(hdlc, ifr); + case IF_PROTO_HDLC_ETH: return hdlc_raw_eth_ioctl(hdlc, ifr); case IF_PROTO_PPP: return hdlc_ppp_ioctl(hdlc, ifr); case IF_PROTO_CISCO: return hdlc_cisco_ioctl(hdlc, ifr); case IF_PROTO_FR: return hdlc_fr_ioctl(hdlc, ifr); @@ -152,9 +160,10 @@ void unregister_hdlc_device(hdlc_device *hdlc) { + rtnl_lock(); hdlc_proto_detach(hdlc); - - unregister_netdev(hdlc_to_dev(hdlc)); + unregister_netdevice(hdlc_to_dev(hdlc)); + rtnl_unlock(); MOD_DEC_USE_COUNT; } @@ -162,7 +171,7 @@ MODULE_AUTHOR("Krzysztof Halasa "); MODULE_DESCRIPTION("HDLC support module"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); EXPORT_SYMBOL(hdlc_ioctl); EXPORT_SYMBOL(register_hdlc_device); --- linux-2.4/drivers/net/wan/hdlc_ppp.c 2003-03-13 23:22:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/hdlc_ppp.c 2004-05-24 22:43:24.000000000 +0200 @@ -2,12 +2,11 @@ * Generic HDLC support routines for Linux * Point-to-point protocol support * - * Copyright (C) 1999 - 2001 Krzysztof Halasa + * Copyright (C) 1999 - 2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. */ #include @@ -68,10 +67,10 @@ -static void ppp_rx(struct sk_buff *skb) +static unsigned short ppp_type_trans(struct sk_buff *skb, + struct net_device *dev) { - skb->protocol = htons(ETH_P_WAN_PPP); - netif_rx(skb); + return __constant_htons(ETH_P_WAN_PPP); } @@ -103,7 +102,8 @@ hdlc->open = ppp_open; hdlc->stop = ppp_close; - hdlc->netif_rx = ppp_rx; + hdlc->netif_rx = NULL; + hdlc->type_trans = ppp_type_trans; hdlc->proto = IF_PROTO_PPP; dev->hard_start_xmit = hdlc->xmit; dev->hard_header = NULL; --- linux-2.4/drivers/net/wan/hdlc_raw.c 2003-03-13 23:22:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/hdlc_raw.c 2004-05-24 22:43:24.000000000 +0200 @@ -2,12 +2,11 @@ * Generic HDLC support routines for Linux * HDLC support * - * Copyright (C) 1999 - 2001 Krzysztof Halasa + * Copyright (C) 1999 - 2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. */ #include @@ -26,10 +25,10 @@ #include -static void raw_rx(struct sk_buff *skb) +static unsigned short raw_type_trans(struct sk_buff *skb, + struct net_device *dev) { - skb->protocol = htons(ETH_P_IP); - netif_rx(skb); + return __constant_htons(ETH_P_IP); } @@ -67,7 +66,7 @@ new_settings.encoding = ENCODING_NRZ; if (new_settings.parity == PARITY_DEFAULT) - new_settings.parity = PARITY_NONE; + new_settings.parity = PARITY_CRC16_PR1_CCITT; result = hdlc->attach(hdlc, new_settings.encoding, new_settings.parity); @@ -79,11 +78,13 @@ hdlc->open = NULL; hdlc->stop = NULL; - hdlc->netif_rx = raw_rx; + hdlc->netif_rx = NULL; + hdlc->type_trans = raw_type_trans; hdlc->proto = IF_PROTO_HDLC; dev->hard_start_xmit = hdlc->xmit; dev->hard_header = NULL; dev->type = ARPHRD_RAWHDLC; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; dev->addr_len = 0; return 0; } --- linux-2.4/drivers/net/wan/hdlc_raw_eth.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4/drivers/net/wan/hdlc_raw_eth.c 2004-05-25 00:31:49.000000000 +0200 @@ -0,0 +1,110 @@ +/* + * Generic HDLC support routines for Linux + * HDLC Ethernet emulation support + * + * Copyright (C) 2002-2003 Krzysztof Halasa + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static int eth_tx(struct sk_buff *skb, struct net_device *dev) +{ + int pad = ETH_ZLEN - skb->len; + if (pad > 0) { /* Pad the frame with zeros */ + int len = skb->len; + if (skb_tailroom(skb) < pad) + if (pskb_expand_head(skb, 0, pad, GFP_ATOMIC)) { + dev_to_hdlc(dev)->stats.tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + skb_put(skb, pad); + memset(skb->data + len, 0, pad); + } + return dev_to_hdlc(dev)->xmit(skb, dev); +} + + +int hdlc_raw_eth_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + raw_hdlc_proto *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc; + const size_t size = sizeof(raw_hdlc_proto); + raw_hdlc_proto new_settings; + struct net_device *dev = hdlc_to_dev(hdlc); + int result; + void *old_ch_mtu; + int old_qlen; + + switch (ifr->ifr_settings.type) { + case IF_GET_PROTO: + ifr->ifr_settings.type = IF_PROTO_HDLC_ETH; + if (ifr->ifr_settings.size < size) { + ifr->ifr_settings.size = size; /* data size wanted */ + return -ENOBUFS; + } + if (copy_to_user(raw_s, &hdlc->state.raw_hdlc.settings, size)) + return -EFAULT; + return 0; + + case IF_PROTO_HDLC_ETH: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (dev->flags & IFF_UP) + return -EBUSY; + + if (copy_from_user(&new_settings, raw_s, size)) + return -EFAULT; + + if (new_settings.encoding == ENCODING_DEFAULT) + new_settings.encoding = ENCODING_NRZ; + + if (new_settings.parity == PARITY_DEFAULT) + new_settings.parity = PARITY_CRC16_PR1_CCITT; + + result = hdlc->attach(hdlc, new_settings.encoding, + new_settings.parity); + if (result) + return result; + + hdlc_proto_detach(hdlc); + memcpy(&hdlc->state.raw_hdlc.settings, &new_settings, size); + + hdlc->open = NULL; + hdlc->stop = NULL; + hdlc->netif_rx = NULL; + hdlc->type_trans = eth_type_trans; + hdlc->proto = IF_PROTO_HDLC_ETH; + dev->hard_start_xmit = eth_tx; + old_ch_mtu = dev->change_mtu; + old_qlen = dev->tx_queue_len; + ether_setup(dev); + dev->change_mtu = old_ch_mtu; + dev->tx_queue_len = old_qlen; + memcpy(dev->dev_addr, "\x00\x01", 2); + get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2); + return 0; + } + + return -EINVAL; +} --- linux-2.4/drivers/net/wan/hdlc_x25.c 2003-03-13 23:22:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/hdlc_x25.c 2004-05-24 22:43:24.000000000 +0200 @@ -2,12 +2,11 @@ * Generic HDLC support routines for Linux * X.25 support * - * Copyright (C) 1999 - 2001 Krzysztof Halasa + * Copyright (C) 1999 - 2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. */ #include @@ -204,6 +203,7 @@ hdlc->open = x25_open; hdlc->stop = x25_close; hdlc->netif_rx = x25_rx; + hdlc->type_trans = NULL; hdlc->proto = IF_PROTO_X25; dev->hard_start_xmit = x25_xmit; dev->hard_header = NULL; --- linux-2.4/drivers/net/wan/Makefile 2003-03-13 23:22:57.000000000 +0100 +++ linux-2.4/drivers/net/wan/Makefile 2004-05-24 22:43:24.000000000 +0200 @@ -24,6 +24,7 @@ hdlc-y := hdlc_generic.o hdlc-$(CONFIG_HDLC_RAW) += hdlc_raw.o +hdlc-$(CONFIG_HDLC_RAW_ETH) += hdlc_raw_eth.o hdlc-$(CONFIG_HDLC_CISCO) += hdlc_cisco.o hdlc-$(CONFIG_HDLC_FR) += hdlc_fr.o hdlc-$(CONFIG_HDLC_PPP) += hdlc_ppp.o @@ -76,6 +77,7 @@ endif obj-$(CONFIG_N2) += n2.o obj-$(CONFIG_C101) += c101.o +obj-$(CONFIG_TAHOE9XX) += tahoe9xx.o include $(TOPDIR)/Rules.make --- linux-2.4/drivers/net/wan/tahoe9xx.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4/drivers/net/wan/tahoe9xx.c 2004-05-25 00:47:24.000000000 +0200 @@ -0,0 +1,795 @@ +/* + * Tahoe 9xx synchronous serial card driver for Linux + * + * Copyright (C) 2002-2003 Krzysztof Halasa + * Copyright (C) 2003 Piotr Kaczmarzyk + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * For information see http://hq.pm.waw.pl/hdlc/ + * + * Sources of information: + * Hitachi HD64570 SCA User's Manual + * PLX Technology Inc. PCI9052 Data Book + * Dallas Semiconductor DS21554 Datasheet + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hd64570.h" + +static const char* version = "Tahoe 9xx driver version: 1.14t"; +static const char* devname = "TAHOE9XX"; + +#define TAHOE9XX_PLX_SIZE 0x80 /* PLX control window size (128b) */ +#define TAHOE9XX_SCA_SIZE 0x100 /* SCA window size (256b) */ +#define ALL_PAGES_ALWAYS_MAPPED +#define NEED_DETECT_RAM +#define MAX_TX_BUFFERS 10 + +#define CLOCK_BASE 9216000 + +#define G703_AMI 0 +#define G703_HDB3 1 + +#define DEFAULT_LICR 0x80 + +#define PCI_VENDOR_ID_TAHOE 0x8246 +#define PCI_DEVICE_ID_TAHOE931 0x0931 +#define PCI_DEVICE_ID_TAHOE932 0x0932 +#define PCI_DEVICE_ID_TAHOE971 0x0971 +#define PCI_DEVICE_ID_TAHOE972 0x0972 + +/* + * PLX PCI9052 local configuration and shared runtime registers. + * This structure can be used to access 9052 registers (memory mapped). + */ +typedef struct { + u32 loc_addr_range[4]; /* 00-0Ch : Local Address Ranges */ + u32 loc_rom_range; /* 10h : Local ROM Range */ + u32 loc_addr_base[4]; /* 14-20h : Local Address Base Addrs */ + u32 loc_rom_base; /* 24h : Local ROM Base */ + u32 loc_bus_descr[4]; /* 28-34h : Local Bus Descriptors */ + u32 rom_bus_descr; /* 38h : ROM Bus Descriptor */ + u32 cs_base[4]; /* 3C-48h : Chip Select Base Addrs */ + u32 intr_ctrl_stat; /* 4Ch : Interrupt Control/Status */ + u32 init_ctrl; /* 50h : EEPROM ctrl, Init Ctrl, etc */ +}plx9052; + + + + + +typedef struct ds21554_s { + u8 vcr1; /* Counter: Violation Error */ + u8 vcr2; + u8 crccr1; /* Counter: CRC4 Error */ + u8 crccr2; + u8 ebcr1; /* Counter: E-bit Error (FEBE) */ + u8 ebcr2; + u8 sr1; /* Status: Status Register 1 */ + u8 sr2; /* Status: Status Register 2 */ + u8 rir; /* Status: Receive Information */ + u8 reserved1[6]; + u8 idr; /* Misc: Device Indentification */ + u8 rcr1; /* Control: Receive Control 1 */ + u8 rcr2; /* Control: Receive Control 2 */ + u8 tcr1; /* Control: Transmit Control 1 */ + u8 tcr2; /* Control: Transmit Control 2 */ + u8 ccr1; /* Control: Common Control 1 */ + u8 test1; + u8 imr1; /* Interrupt Mask 1 */ + u8 imr2; /* Interrupt Mask 2 */ + u8 licr; /* Control: Line interface */ + u8 test2; + u8 ccr2; /* Control: Common Control 2 */ + u8 ccr3; /* Control: Common Control 3 */ + u8 tsacr; /* Control: Transmit Sa bit */ + u8 ccr6; /* Control: Common Control 6 */ + u8 ssr; /* Status: Synchronizer Status */ + u8 rnaf; /* Receive non-align frame */ + u8 taf; /* Transmit align frame */ + u8 tnaf; /* Transmit non-align frame */ + u8 tcbr1; /* Transmit channel blocking */ + u8 tcbr2; + u8 tcbr3; + u8 tcbr4; + u8 tir1; /* Transmit idle */ + u8 tir2; + u8 tir3; + u8 tir4; + u8 tidr; /* Transmit idle definition */ + u8 rcbr1; /* Receive channel blocking */ + u8 rcbr2; + u8 rcbr3; + u8 rcbr4; + u8 raf; /* Receive align frame */ + u8 rs1; /* Receive signalling */ + u8 rs2; + u8 rs3; + u8 rs4; + u8 rs5; + u8 rs6; + u8 rs7; + u8 rs8; + u8 rs9; + u8 rs10; + u8 rs11; + u8 rs12; + u8 rs13; + u8 rs14; + u8 rs15; + u8 rs16; + u8 ts1; /* Transmit signaling */ + u8 ts2; + u8 ts3; + u8 ts4; + u8 ts5; + u8 ts6; + u8 ts7; + u8 ts8; + u8 ts9; + u8 ts10; + u8 ts11; + u8 ts12; + u8 ts13; + u8 ts14; + u8 ts15; + u8 ts16; + u8 tsiaf; /* Transmit Si Bits Align Frame */ + u8 tsinaf; /* Transmit Si Bits Non-align Frame */ + u8 tra; /* Transmit Remote Alarm Bits */ + u8 tsa4; /* Transmit Sa Bits */ + u8 tsa5; + u8 tsa6; + u8 tsa7; + u8 tsa8; + u8 rsiaf; /* Receive Si Bits Align Frame */ + u8 rsinaf; /* Receive Si Bits Non-Align Frame */ + u8 rra; /* Receive Remote Alarm Bits */ + u8 rsa4; /* Receive Sa Bits */ + u8 rsa5; + u8 rsa6; + u8 rsa7; + u8 rsa8; + u8 tc1; /* Transmit channel */ + u8 tc2; + u8 tc3; + u8 tc4; + u8 tc5; + u8 tc6; + u8 tc7; + u8 tc8; + u8 tc9; + u8 tc10; + u8 tc11; + u8 tc12; + u8 tc13; + u8 tc14; + u8 tc15; + u8 tc16; + u8 tc17; + u8 tc18; + u8 tc19; + u8 tc20; + u8 tc21; + u8 tc22; + u8 tc23; + u8 tc24; + u8 tc25; + u8 tc26; + u8 tc27; + u8 tc28; + u8 tc29; + u8 tc30; + u8 tc31; + u8 tc32; + u8 rc1; /* Receive channel */ + u8 rc2; + u8 rc3; + u8 rc4; + u8 rc5; + u8 rc6; + u8 rc7; + u8 rc8; + u8 rc9; + u8 rc10; + u8 rc11; + u8 rc12; + u8 rc13; + u8 rc14; + u8 rc15; + u8 rc16; + u8 rc17; + u8 rc18; + u8 rc19; + u8 rc20; + u8 rc21; + u8 rc22; + u8 rc23; + u8 rc24; + u8 rc25; + u8 rc26; + u8 rc27; + u8 rc28; + u8 rc29; + u8 rc30; + u8 rc31; + u8 rc32; + u8 tcc1; /* Transmit channel control */ + u8 tcc2; + u8 tcc3; + u8 tcc4; + u8 rcc1; /* Receive channel control */ + u8 rcc2; + u8 rcc3; + u8 rcc4; + u8 ccr4; /* Control: Common Control 4 */ + u8 tds0m; /* Transmit DS0 Monitor */ + u8 ccr5; /* Control: Common Control 5 */ + u8 rds0m; /* Receive DS0 Monitor */ + u8 test3; + u8 reserved2[3]; + u8 hcr; /* HDLC Control */ + u8 hsr; /* HDLC Status */ + u8 himr; /* HDLC Interrupt Mask */ + u8 rhir; /* Receive HDLC Information */ + u8 rhfr; /* Receive HDLC FIFO */ + u8 ibo; /* Interleave Bus Operation */ + u8 thir; /* Transmit HDLC Information */ + u8 thfr; /* Transmit HDLC FIFO */ + u8 rdc1; /* Receive HDLC DS0 Control 1 */ + u8 rdc2; /* Receive HDLC DS0 Control 2 */ + u8 tdc1; /* Transmit HDLC DS0 Control 1 */ + u8 tdc2; /* Transmit HDLC DS0 Control 2 */ + u8 reserved3[4]; +} ds21554_t; + +typedef struct port_s { + hdlc_device hdlc; /* HDLC device struct - must be first */ + struct card_s *card; + spinlock_t lock; /* TX lock */ + te1_settings settings; + int rxpart; /* partial frame received, next frame invalid*/ + unsigned short encoding; + unsigned short parity; + u16 rxin; /* rx ring buffer 'in' pointer */ + u16 txin; /* tx ring buffer 'in' and 'last' pointers */ + u16 txlast; + u8 rxs, txs, tmc; /* SCA registers */ + u8 phy_node; /* physical port # - 0 or 1 */ + u32 dsphys; /* DS21544 memory base (physical) */ + ds21554_t* dsbase; /* DS21544 memory base (virtual) */ + u8 g703_on; /* Enable/disable G.703 transceiver */ + u8 g703_coding; /* G.703 line coding */ + u8 g703_idlecode; /* G.703 idle timeslots contents */ +}port_t; + +typedef struct card_s { + u32 ramphys; /* buffer memory base (physical) */ + u8* rambase; /* buffer memory base (virtual) */ + u32 ramsize; /* buffer memory size */ + u32 scaphys; /* SCA memory base (physical) */ + u8* scabase; /* SCA memory base (virtual) */ + u32 plxphys; /* PLX registers memory base (physical) */ + plx9052* plxbase; /* PLX registers memory base (virtual) */ + u16 ring_buffers; /* number of buffers in a ring */ + u16 buff_offset; /* offset of first buffer of first channel */ + u8 irq; /* interrupt request level */ + u8 no_ports; /* number of ports */ + char dev_name[10]; /* device name */ + port_t ports[2]; +}card_t; + +#define sca_in(reg, card) readb(card->scabase + (reg)) +#define sca_out(value, reg, card) writeb(value, card->scabase + (reg)) +#define sca_inw(reg, card) readw(card->scabase + (reg)) +#define sca_outw(value, reg, card) writew(value, card->scabase + (reg)) +#define sca_inl(reg, card) readl(card->scabase + (reg)) +#define sca_outl(value, reg, card) writel(value, card->scabase + (reg)) + +#define port_to_card(port) (port->card) +#define log_node(port) (port->phy_node) +#define phy_node(port) (port->phy_node) +#define winbase(card) (card->rambase) +#define get_port(card, port) (&card->ports[port]) +#define sca_flush(card) (sca_in(IER0, card)); + +static inline void new_memcpy_toio(char *dest, char *src, int length) +{ + int len; + + do { + len = length > 128 ? 128 : length; /* 32 */ + memcpy_toio(dest, src, len); + dest += len; + src += len; + length -= len; + readb(dest); + } while (len); +} + +#undef memcpy_toio +#define memcpy_toio new_memcpy_toio + +#include "hd6457x.c" + +void init_ds21554(port_t *port); + +static void t9xx_set_iface(port_t *port) +{ + card_t *card = port->card; + u8 msci = get_msci(port); + u8 rxs = port->rxs & CLK_BRG_MASK; + u8 txs = port->txs & CLK_BRG_MASK; + + if (port->dsbase) { + init_ds21554(port); + } + + rxs |= CLK_LINE_RX; /* RXC input */ + txs |= CLK_LINE_TX; /* TXC input */ + + port->rxs = rxs; + port->txs = txs; + sca_out(rxs, msci + RXS, card); + sca_out(txs, msci + TXS, card); + sca_set_port(port); +} + + + +static int t9xx_open(struct net_device *dev) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + port_t *port = hdlc_to_port(hdlc); + int result = hdlc_open(hdlc); + + if (result) + return result; + + MOD_INC_USE_COUNT; + sca_open(hdlc); + t9xx_set_iface(port); + sca_flush(port_to_card(port)); + return 0; +} + + + +static int t9xx_close(struct net_device *dev) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + + sca_close(hdlc); + sca_flush(port_to_card(dev_to_port(dev))); + hdlc_close(hdlc); + MOD_DEC_USE_COUNT; + return 0; +} + + + +static int t9xx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + const size_t size = sizeof(te1_settings); + te1_settings new_line, *line = ifr->ifr_settings.ifs_ifsu.te1; + hdlc_device *hdlc = dev_to_hdlc(dev); + port_t *port = hdlc_to_port(hdlc); + +#ifdef CONFIG_HDLC_DEBUG_RINGS + if (cmd == SIOCDEVPRIVATE) { + sca_dump_rings(hdlc); + return 0; + } +#endif + if (cmd != SIOCWANDEV) + return hdlc_ioctl(dev, ifr, cmd); + + switch(ifr->ifr_settings.type) { + case IF_GET_IFACE: + if (port->dsbase) + ifr->ifr_settings.type = IF_IFACE_E1; + else + ifr->ifr_settings.type = IF_IFACE_V35; + if (ifr->ifr_settings.size < size) { + ifr->ifr_settings.size = size; /* data size wanted */ + return -ENOBUFS; + } + if (copy_to_user(line, &port->settings, size)) + return -EFAULT; + return 0; + + case IF_IFACE_E1: + case IF_IFACE_V35: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (copy_from_user(&new_line, line, size)) + return -EFAULT; + + if (new_line.clock_type != CLOCK_EXT) + return -EINVAL; /* No such clock setting */ + + if (new_line.loopback > 3) + return -EINVAL; + + memcpy(&port->settings, &new_line, size); /* Update settings */ + t9xx_set_iface(port); + sca_flush(port_to_card(port)); + return 0; + + default: + return hdlc_ioctl(dev, ifr, cmd); + } +} + + + +static void t9xx_pci_remove_one(struct pci_dev *pdev) +{ + int i, ports; + card_t *card = pci_get_drvdata(pdev); + + if ((pdev->subsystem_device == PCI_DEVICE_ID_TAHOE932) || + (pdev->subsystem_device == PCI_DEVICE_ID_TAHOE972)) + ports = 2; + else + ports = 1; + for(i = 0; i < ports; i++) + if (card->ports[i].card) + unregister_hdlc_device(&card->ports[i].hdlc); + + if (card->irq) + free_irq(card->irq, card); + iounmap(card->rambase); + iounmap(card->scabase); + iounmap(card->plxbase); + pci_release_regions(pdev); + kfree(card); +} + +static void ds21554_update_licr(port_t *port) +{ + port->dsbase->licr = DEFAULT_LICR | (port->settings.egl << 4) | (port->g703_on == 0); +} + +static void ds21554_update_registers(port_t *port) +{ + ds21554_t *ds = port->dsbase; + + if (port->settings.slot_map == 0xffffffff) { + /* Unframed */ + ds->ccr1 = 0x08 | (port->g703_coding << 6) | (port->g703_coding << 2); + ds->tcr1 = 0x40; + ds->tcr2 = 0x00; + ds->ccr2 = 0x80; //84 + ds->tir1 = 0; ds->tir2 = 0; ds->tir3 = 0; ds->tir4 = 0; + ds->tcc1 = 0; ds->tcc2 = 0; ds->tcc3 = 0; ds->tcc4 = 0; + ds->rcc1 = 0; ds->rcc2 = 0; ds->rcc3 = 0; ds->rcc4 = 0; + ds->rcbr1 = 0xff; ds->rcbr2 = 0xff; + ds->rcbr3 = 0xff; ds->rcbr4 = 0xff; + ds->tcbr1 = 0xff; ds->tcbr2 = 0xff; + ds->tcbr3 = 0xff; ds->tcbr4 = 0xff; + ds->tsacr = 0x00; + ds->tdc1 = 0x00; + + ds->rcr2 = 0x04; + ds->ccr3 = 0x02; + + ds->ccr6 = 0x03; /* elastic buffers reset */ + udelay(1000); + ds->ccr6 = 0x00; /* elastic buffers reset */ + + ds->ccr5 = 0x60; /* elastic buffers align */ + udelay(1000); + ds->ccr5 = 0x00; + } else { + ds->ccr1 = 0x08 | (port->g703_coding << 6) | (port->g703_coding << 2) + | (port->settings.crc4 << 4) | (port->settings.crc4); + ds->tcr1 = 0x00; + ds->ccr2 = 0x94; /* Automatic alarm generation */ + + /* Receive channels */ + ds->rcbr1 = port->settings.slot_map & 0xff; + ds->rcbr2 = (port->settings.slot_map >> 8) & 0xff; + ds->rcbr3 = (port->settings.slot_map >> 16) & 0xff; + ds->rcbr4 = (port->settings.slot_map >> 24) & 0xff; + + /* Transmit channels */ + ds->tcbr1 = port->settings.slot_map & 0xff; + ds->tcbr2 = (port->settings.slot_map >> 8) & 0xff; + ds->tcbr3 = (port->settings.slot_map >> 16) & 0xff; + ds->tcbr4 = (port->settings.slot_map >> 24) & 0xff; + + /* Transmit idle */ + /* (w pozostalych szczelinach wysylamy kod idle) */ + ds->tir1 = ~port->settings.slot_map & 0xfe; // Slot 1 nigdy nie jest idle + ds->tir2 = (~port->settings.slot_map >> 8) & 0xff; + ds->tir3 = (~port->settings.slot_map >> 16) & 0xff; + ds->tir4 = (~port->settings.slot_map >> 24) & 0xff; + ds->rcr2 = 0x06; /* RSYSCLK = 2048, rx elastic store enabled */ + ds->ccr3 = 0x82; /* TSYSCLK = 2048, tx elastic store enabled */ + + ds->ccr6 = 0x07; /* elastic buffers reset */ + udelay(1000); + ds->ccr6 = 0x04; /* elastic buffers reset */ + + ds->ccr5 = 0x60; /* elastic buffers align */ + udelay(1000); + ds->ccr5 = 0x00; + } +} + +void init_ds21554(port_t *port) +{ + ds21554_t *ds = port->dsbase; + + ds->ccr2 = 0x04; + udelay(1000); + ds->ccr5 = 0x80; /* Line Interface Reset */ + udelay(1000); + + ds->ccr5 = 0xe0; /* Elastic Buffers Reset */ + udelay(1000); + ds->ccr5 = 0x00; + ds->ccr6 = 0x04; /* TCLK from RCLK */ + ds->tcr2 = 0x00; + ds21554_update_licr(port); + + /* Setup HDB3, CRC4, CAS/CCS, G.802 */ + ds21554_update_registers(port); + + ds->ccr2 = 0x94; /* Automatic alarm generation */ + + ds->taf = 0x1b; + ds->tnaf = 0x40; + + ds21554_update_registers(port); + + ds->tidr = port->g703_idlecode; + +// ds->ccr4 |= 0x40; +} + + +static int __devinit t9xx_pci_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + card_t *card; + u8 rev_id, tahoe97x = 0; + u32 *p; + int i; + +#ifndef MODULE + static int printed_version; + if (!printed_version++) + printk(KERN_INFO "%s\n", version); +#endif + + i = pci_enable_device(pdev); + if (i) + return i; + + i = pci_request_regions(pdev, "Tahoe9xx"); + if (i) + return i; + + card = kmalloc(sizeof(card_t), GFP_KERNEL); + if (card == NULL) { + printk(KERN_ERR "%s: unable to allocate memory\n", card->dev_name); + return -ENOBUFS; + } + memset(card, 0, sizeof(card_t)); + pci_set_drvdata(pdev, card); + + sprintf(card->dev_name, "Tahoe"); + switch (pdev->subsystem_device) { + case PCI_DEVICE_ID_TAHOE931: + strcat(card->dev_name, "931"); + card->no_ports = 1; + break; + case PCI_DEVICE_ID_TAHOE932: + strcat(card->dev_name, "932"); + card->no_ports = 2; + break; + case PCI_DEVICE_ID_TAHOE971: + strcat(card->dev_name, "971"); + tahoe97x = 1; + card->no_ports = 1; + break; + case PCI_DEVICE_ID_TAHOE972: + strcat(card->dev_name, "972"); + tahoe97x = 1; + card->no_ports = 2; + break; + default: + strcat(card->dev_name, "9xx"); + card->no_ports = 0; + break; + } + + pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id); + if (pci_resource_len(pdev, 0) != TAHOE9XX_PLX_SIZE || + pci_resource_len(pdev, 2) != TAHOE9XX_SCA_SIZE || + pci_resource_len(pdev, 3) < 16384) { + printk(KERN_ERR "%s: invalid card EEPROM parameters\n", card->dev_name); + kfree(card); + return -EFAULT; + } + + card->plxphys = pci_resource_start(pdev,0) & PCI_BASE_ADDRESS_MEM_MASK; + card->plxbase = ioremap(card->plxphys, TAHOE9XX_PLX_SIZE); + + card->scaphys = pci_resource_start(pdev,2) & PCI_BASE_ADDRESS_MEM_MASK; + card->scabase = ioremap(card->scaphys, TAHOE9XX_SCA_SIZE); + + card->ramphys = pci_resource_start(pdev,3) & PCI_BASE_ADDRESS_MEM_MASK; + card->rambase = ioremap(card->ramphys, pci_resource_len(pdev,3)); + + if (tahoe97x) { + for (i=0; i < card->no_ports; i++) { + card->ports[i].dsphys = pci_resource_start(pdev,4+i) & PCI_BASE_ADDRESS_MEM_MASK; + card->ports[i].dsbase = (ds21554_t *)ioremap(card->ports[i].dsphys, pci_resource_len(pdev,4+i)); + } + } else { + for (i=0; i < card->no_ports; i++) + card->ports[i].dsbase = (ds21554_t *)0; + } + + /* Reset PLX */ + p = &card->plxbase->init_ctrl; + writel(readl(p) | 0x40000000, p); + readl(p); /* Flush the write - do not use sca_flush */ + udelay(1); + + writel(readl(p) & ~0x40000000, p); + readl(p); /* Flush the write - do not use sca_flush */ + udelay(1); + + card->ramsize = sca_detect_ram(card, card->rambase, + pci_resource_len(pdev,3)); + + /* number of TX + RX buffers for one port - this is dual port card */ + card->ring_buffers = card->ramsize / (card->no_ports * 2 * (sizeof(pkt_desc) + HDLC_MAX_MRU)); + + card->buff_offset = 2 * sizeof(pkt_desc) * card->ring_buffers * card->no_ports; + + printk(KERN_INFO "%s: %u KB RAM at 0x%x, IRQ%u, " + "using %u packets rings\n", card->dev_name, card->ramsize / 1024, + card->ramphys, pdev->irq, + card->ring_buffers); + if (tahoe97x) { + for (i=0; i < card->no_ports; i++) { + /* Make sure DS21554 is there */ + card->ports[i].dsbase->idr = 0x00; + card->ports[i].dsbase->tc1 = 0xaa; + card->ports[i].dsbase->rc1 = 0x55; + if (((card->ports[i].dsbase->idr & 0xf0) == 0xa0) && (card->ports[i].dsbase->tc1 == 0xaa) && (card->ports[i].dsbase->rc1 == 0x55)) { + printk(KERN_INFO "%s: DS21554 (port %d) detected at 0x%x\n", card->dev_name, i, card->ports[i].dsphys); + /* Clear registers */ + memset(card->ports[i].dsbase, 0, 256); + /* Default settings */ + card->ports[i].g703_on = 1; + card->ports[i].settings.egl = 0; + card->ports[i].g703_coding = G703_HDB3; + card->ports[i].g703_idlecode = 0x54; + card->ports[i].settings.crc4 = 1; + /* Unframed */ + card->ports[i].settings.slot_map = 0xffffffff; + init_ds21554(&card->ports[i]); + } else + printk(KERN_INFO "%s: DS21554 (port %d) test failed!\n", card->dev_name, i); + } + } + + if (card->ring_buffers < 1) { + printk(KERN_ERR "%s: RAM test failed\n", card->dev_name); + t9xx_pci_remove_one(pdev); + return -EFAULT; + } + + /* Enable interrupts on the PCI bridge */ + p = &card->plxbase->intr_ctrl_stat; + writew(readw(p) | 0x0040, p); + + /* Allocate IRQ */ + if(request_irq(pdev->irq, sca_intr, SA_SHIRQ, devname, card)) { + printk(KERN_WARNING "%s: could not allocate IRQ%d.\n", card->dev_name, + pdev->irq); + t9xx_pci_remove_one(pdev); + return -EBUSY; + } + card->irq = pdev->irq; + + sca_init(card, 0); + + for(i = 0; i < card->no_ports; i++) { + port_t *port = &card->ports[i]; + struct net_device *dev = hdlc_to_dev(&port->hdlc); + port->phy_node = i; + + spin_lock_init(&port->lock); + dev->irq = card->irq; + dev->mem_start = card->ramphys; + dev->mem_end = card->ramphys + card->ramsize - 1; + dev->tx_queue_len = 50; + dev->do_ioctl = t9xx_ioctl; + dev->open = t9xx_open; + dev->stop = t9xx_close; + port->hdlc.attach = sca_attach; + port->hdlc.xmit = sca_xmit; + port->settings.clock_type = CLOCK_EXT; + if(register_hdlc_device(&port->hdlc)) { + printk(KERN_ERR "%s: unable to register hdlc " + "device\n", card->dev_name); + t9xx_pci_remove_one(pdev); + return -ENOBUFS; + } + port->card = card; + sca_init_sync_port(port); /* Set up SCA memory */ + + printk(KERN_INFO "%s: %s node %d\n", + hdlc_to_name(&port->hdlc), card->dev_name, port->phy_node); + } + + sca_flush(card); + return 0; +} + + + +static struct pci_device_id t9xx_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_TAHOE, PCI_ANY_ID, + 0, 0, 0 }, + { 0, } +}; + +static struct pci_driver t9xx_pci_driver = { + name: "Tahoe9xx", + id_table: t9xx_pci_tbl, + probe: t9xx_pci_init_one, + remove: t9xx_pci_remove_one, +}; + +static int __init t9xx_init_module(void) +{ +#ifdef MODULE + printk(KERN_INFO "%s\n", version); +#endif + return pci_module_init(&t9xx_pci_driver); +} + + + +static void __exit t9xx_cleanup_module(void) +{ + pci_unregister_driver(&t9xx_pci_driver); +} + +MODULE_AUTHOR("Piotr Kaczmarzyk "); +MODULE_DESCRIPTION("Tahoe 9xx serial port driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(pci, t9xx_pci_tbl); +EXPORT_NO_SYMBOLS; +module_init(t9xx_init_module); +module_exit(t9xx_cleanup_module); --- linux-2.4/include/linux/hdlc/ioctl.h 2003-03-13 23:23:13.000000000 +0100 +++ linux-2.4.20-8-tahoe/include/linux/hdlc/ioctl.h 2004-05-24 22:47:23.000000000 +0200 @@ -12,6 +12,8 @@ unsigned int clock_type; /* internal, external, TX-internal etc. */ unsigned short loopback; unsigned int slot_map; + unsigned short crc4; + unsigned short egl; } te1_settings; /* T1, E1 */ typedef struct { @@ -34,22 +36,15 @@ } fr_proto_pvc; /* for creating/deleting FR PVCs */ typedef struct { + unsigned int dlci; + char master[IFNAMSIZ]; /* Name of master FRAD device */ +}fr_proto_pvc_info; /* for returning PVC information only */ + +typedef struct { unsigned int interval; unsigned int timeout; } cisco_proto; /* PPP doesn't need any info now - supply length = 0 to ioctl */ -union hdlc_settings { - raw_hdlc_proto raw_hdlc; - cisco_proto cisco; - fr_proto fr; - fr_proto_pvc fr_pvc; -}; - -union line_settings { - sync_serial_settings sync; - te1_settings te1; -}; - #endif /* __HDLC_IOCTL_H__ */ --- linux-2.4/include/linux/hdlc.h 2003-03-13 23:32:17.000000000 +0100 +++ linux-2.4.20-8-tahoe/include/linux/hdlc.h 2004-05-25 01:46:18.000000000 +0200 @@ -1,12 +1,11 @@ /* * Generic HDLC support routines for Linux * - * Copyright (C) 1999-2002 Krzysztof Halasa + * Copyright (C) 1999-2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. */ #ifndef __HDLC_H @@ -145,17 +144,20 @@ typedef struct pvc_device_struct { - struct net_device netdev; /* PVC net device - must be first */ - struct net_device_stats stats; struct hdlc_device_struct *master; - struct pvc_device_struct *next; + struct net_device *main; + struct net_device *ether; /* bridged Ethernet interface */ + struct pvc_device_struct *next; /* Sorted in ascending DLCI order */ + int dlci; + int open_count; struct { - int active; - int new; - int deleted; - int fecn; - int becn; + unsigned int new: 1; + unsigned int active: 1; + unsigned int exist: 1; + unsigned int deleted: 1; + unsigned int fecn: 1; + unsigned int becn: 1; }state; }pvc_device; @@ -180,18 +182,20 @@ void (*stop)(struct hdlc_device_struct *hdlc); void (*proto_detach)(struct hdlc_device_struct *hdlc); void (*netif_rx)(struct sk_buff *skb); + unsigned short (*type_trans)(struct sk_buff *skb, + struct net_device *dev); int proto; /* IF_PROTO_HDLC/CISCO/FR/etc. */ union { struct { fr_proto settings; pvc_device *first_pvc; - int pvc_count; + int dce_pvc_count; struct timer_list timer; int last_poll; int reliable; - int changed; + int dce_changed; int request; int fullrep_sent; u32 last_errors; /* last errors bit list */ @@ -226,6 +230,7 @@ int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr); +int hdlc_raw_eth_ioctl(hdlc_device *hdlc, struct ifreq *ifr); int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr); int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr); int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr); @@ -254,15 +259,9 @@ } -static __inline__ struct net_device* pvc_to_dev(pvc_device *pvc) -{ - return &pvc->netdev; -} - - static __inline__ pvc_device* dev_to_pvc(struct net_device *dev) { - return (pvc_device*)dev; + return (pvc_device*)dev->priv; } @@ -272,12 +271,6 @@ } -static __inline__ const char *pvc_to_name(pvc_device *pvc) -{ - return pvc_to_dev(pvc)->name; -} - - static __inline__ u16 netdev_dlci(struct net_device *dev) { return ntohs(*(u16*)dev->dev_addr); @@ -344,6 +337,5 @@ hdlc->proto_detach = NULL; } - #endif /* __KERNEL */ #endif /* __HDLC_H */ --- linux-2.4/include/linux/if.h 2003-03-13 23:32:16.000000000 +0100 +++ linux-2.4.20-8-tahoe/include/linux/if.h 2004-05-24 22:47:42.000000000 +0200 @@ -21,6 +21,9 @@ #include /* for "__kernel_caddr_t" et al */ #include /* for "struct sockaddr" et al */ + +#define IFNAMSIZ 16 + #include /* Standard interface flags (netdevice->flags). */ @@ -69,7 +72,11 @@ #define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */ #define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */ #define IF_PROTO_X25 0x2006 /* X.25 */ - +#define IF_PROTO_HDLC_ETH 0x2007 /* raw HDLC, Ethernet emulation */ +#define IF_PROTO_FR_ADD_ETH_PVC 0x2008 /* Create FR Ethernet-bridged PVC */ +#define IF_PROTO_FR_DEL_ETH_PVC 0x2009 /* Delete FR Ethernet-bridged PVC */ +#define IF_PROTO_FR_PVC 0x200A /* for reading PVC status */ +#define IF_PROTO_FR_ETH_PVC 0x200B /* @@ -103,6 +110,7 @@ cisco_proto *cisco; fr_proto *fr; fr_proto_pvc *fr_pvc; + fr_proto_pvc_info *fr_pvc_info; /* interface settings */ sync_serial_settings *sync; @@ -120,7 +128,6 @@ struct ifreq { #define IFHWADDRLEN 6 -#define IFNAMSIZ 16 union { char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */