--- linux-2.4.orig/Documentation/networking/generic-hdlc.txt 2003-03-29 23:40:48.000000000 +0100 +++ linux-2.4/Documentation/networking/generic-hdlc.txt 2003-03-30 22:14:01.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.orig/include/linux/if.h 2003-03-29 23:41:03.000000000 +0100 +++ linux-2.4/include/linux/if.h 2003-03-30 22:14:01.000000000 +0200 @@ -21,6 +21,8 @@ #include /* for "__kernel_caddr_t" et al */ #include /* for "struct sockaddr" et al */ + +#define IFNAMSIZ 16 #include /* Standard interface flags (netdevice->flags). */ @@ -59,7 +61,7 @@ #define IF_IFACE_X21 0x1002 /* X.21 serial interface */ #define IF_IFACE_T1 0x1003 /* T1 telco serial interface */ #define IF_IFACE_E1 0x1004 /* E1 telco serial interface */ -#define IF_IFACE_SYNC_SERIAL 0x1005 /* cant'b be set by software */ +#define IF_IFACE_SYNC_SERIAL 0x1005 /* can't be set by software */ /* For definitions see hdlc.h */ #define IF_PROTO_HDLC 0x2000 /* raw HDLC protocol */ @@ -69,7 +71,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 +109,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 +127,6 @@ struct ifreq { #define IFHWADDRLEN 6 -#define IFNAMSIZ 16 union { char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ --- linux-2.4.orig/include/linux/hdlc.h 2003-03-29 23:41:03.000000000 +0100 +++ linux-2.4/include/linux/hdlc.h 2003-03-30 22:14:01.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 @@ -52,7 +51,7 @@ #include #define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */ -#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */ +#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10 + 14 + 4) /* for ETH+VLAN over FR */ #define MAXLEN_LMISTAT 20 /* max size of status enquiry frame */ @@ -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,19 +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); -} - - - static __inline__ u16 q922_to_dlci(u8 *hdr) { return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4); @@ -345,5 +331,15 @@ } +static __inline__ unsigned short hdlc_type_trans(struct sk_buff *skb, + struct net_device *dev) +{ + hdlc_device *hdlc = dev_to_hdlc(skb->dev); + if (hdlc->type_trans) + return hdlc->type_trans(skb, dev); + else + return __constant_htons(ETH_P_HDLC); +} + #endif /* __KERNEL */ #endif /* __HDLC_H */ --- linux-2.4.orig/include/linux/hdlc/ioctl.h 2003-03-29 23:41:03.000000000 +0100 +++ linux-2.4/include/linux/hdlc/ioctl.h 2003-03-30 22:14:01.000000000 +0200 @@ -34,22 +34,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.orig/drivers/net/wan/Makefile 2003-03-29 23:40:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/Makefile 2003-03-30 22:14:01.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 --- linux-2.4.orig/drivers/net/wan/hdlc_cisco.c 2003-03-29 23:40:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/hdlc_cisco.c 2003-03-30 22:14:01.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.orig/drivers/net/wan/hdlc_fr.c 2003-03-29 23:40:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/hdlc_fr.c 2003-03-30 22:14:01.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.orig/drivers/net/wan/hdlc_generic.c 2003-03-29 23:40:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/hdlc_generic.c 2003-03-30 22:14:01.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.12"; 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); @@ -162,7 +170,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.orig/drivers/net/wan/hdlc_ppp.c 2003-03-29 23:40:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/hdlc_ppp.c 2003-03-30 22:14:01.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.orig/drivers/net/wan/hdlc_x25.c 2003-03-29 23:40:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/hdlc_x25.c 2003-03-30 22:14:01.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.orig/drivers/net/wan/hdlc_raw.c 2003-03-29 23:40:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/hdlc_raw.c 2003-03-30 22:14:01.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.orig/drivers/net/wan/hdlc_raw_eth.c 2003-03-30 22:15:44.000000000 +0200 +++ linux-2.4/drivers/net/wan/hdlc_raw_eth.c 2003-03-30 22:14:01.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.orig/drivers/net/wan/hd6457x.c 2003-03-29 23:40:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/hd6457x.c 2003-03-30 22:14:01.000000000 +0200 @@ -1,16 +1,29 @@ /* * 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 + * + * We use the following SCA memory map: + * + * Packet buffer descriptor rings - starting from winbase or win0base: + * rx_ring_buffers * sizeof(pkt_desc) = logical channel #0 RX ring + * tx_ring_buffers * sizeof(pkt_desc) = logical channel #0 TX ring + * rx_ring_buffers * sizeof(pkt_desc) = logical channel #1 RX ring (if used) + * tx_ring_buffers * sizeof(pkt_desc) = logical channel #1 TX ring (if used) + * + * Packet data buffers - starting from winbase + buff_offset: + * rx_ring_buffers * HDLC_MAX_MRU = logical channel #0 RX buffers + * tx_ring_buffers * HDLC_MAX_MRU = logical channel #0 TX buffers + * rx_ring_buffers * HDLC_MAX_MRU = logical channel #0 RX buffers (if used) + * tx_ring_buffers * HDLC_MAX_MRU = logical channel #0 TX buffers (if used) */ #include @@ -42,8 +55,6 @@ #error Either hd64570.h or hd64572.h must be included #endif -static char sca_version[]="1.09"; - #define get_msci(port) (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET) #define get_dmac_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET) #define get_dmac_tx(port) (phy_node(port) ? DMAC1TX_OFFSET : DMAC0TX_OFFSET) @@ -116,24 +127,35 @@ -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) % (transmit ? port_to_card(port)->tx_ring_buffers + : port_to_card(port)->rx_ring_buffers); +} + + + +static inline u16 desc_abs_number(port_t *port, u16 desc, int transmit) { - return (desc + 1) % port_to_card(port)->ring_buffers; + u16 rx_buffs = port_to_card(port)->rx_ring_buffers; + u16 tx_buffs = port_to_card(port)->tx_ring_buffers; + + desc %= (transmit ? tx_buffs : rx_buffs); // called with "X + 1" etc. + return log_node(port) * (rx_buffs + tx_buffs) + + transmit * rx_buffs + desc; } -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; - return ((log_node(port) * 2 + transmit) * buffs + (desc % buffs)) * - sizeof(pkt_desc); + return desc_abs_number(port, desc, transmit) * sizeof(pkt_desc); } -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)) @@ -146,12 +168,10 @@ -static inline u32 buffer_offset(port_t *port, u8 desc, u8 transmit) +static inline u32 buffer_offset(port_t *port, u16 desc, int transmit) { - u8 buffs = port_to_card(port)->ring_buffers; return port_to_card(port)->buff_offset + - ((log_node(port) * 2 + transmit) * buffs + (desc % buffs)) * - (u32)HDLC_MAX_MRU; + desc_abs_number(port, desc, transmit) * (u32)HDLC_MAX_MRU; } @@ -159,8 +179,7 @@ static void sca_init_sync_port(port_t *port) { card_t *card = port_to_card(port); - u8 transmit, i; - u16 dmac, buffs = card->ring_buffers; + int transmit, i; port->rxin = 0; port->txin = 0; @@ -171,6 +190,10 @@ #endif for (transmit = 0; transmit < 2; transmit++) { + u16 dmac = transmit ? get_dmac_tx(port) : get_dmac_rx(port); + u16 buffs = transmit ? card->tx_ring_buffers + : card->rx_ring_buffers; + for (i = 0; i < buffs; i++) { pkt_desc* desc = desc_address(port, i, transmit); u16 chain_off = desc_offset(port, i + 1, transmit); @@ -182,7 +205,6 @@ writeb(0, &desc->stat); } - dmac = transmit ? get_dmac_tx(port) : get_dmac_rx(port); /* DMA disable - to halt state */ sca_out(0, transmit ? DSR_TX(phy_node(port)) : DSR_RX(phy_node(port)), card); @@ -247,7 +269,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; @@ -294,7 +316,7 @@ skb->mac.raw = skb->data; skb->dev = hdlc_to_dev(&port->hdlc); skb->dev->last_rx = jiffies; - skb->protocol = htons(ETH_P_HDLC); + skb->protocol = hdlc_type_trans(skb, hdlc_to_dev(&port->hdlc)); netif_rx(skb); } @@ -341,7 +363,7 @@ /* 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); } /* make sure RX DMA is enabled */ @@ -377,8 +399,7 @@ port->hdlc.stats.tx_packets++; 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); } netif_wake_queue(hdlc_to_dev(&port->hdlc)); @@ -436,7 +457,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; @@ -496,7 +517,7 @@ { port_t *port = hdlc_to_port(hdlc); card_t* card = port_to_card(port); - u8 msci = get_msci(port); + u16 msci = get_msci(port); u8 md0, md2; switch(port->encoding) { @@ -517,7 +538,7 @@ case PARITY_CRC16_PR0_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU_0; break; #else case PARITY_CRC32_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU32; break; -#endif +#endif case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU; break; default: md0 = MD0_HDLC | MD0_CRC_NONE; } @@ -614,7 +635,7 @@ parity != PARITY_CRC16_PR0_CCITT && #else parity != PARITY_CRC32_PR1_CCITT && -#endif +#endif parity != PARITY_CRC16_PR1_CCITT) return -EINVAL; @@ -640,14 +661,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; cnt < port_to_card(port)->rx_ring_buffers; cnt++) printk(" %02X", readb(&(desc_address(port, cnt, 0)->stat))); @@ -659,7 +679,7 @@ port->txlast, sca_in(DSR_TX(phy_node(port)), card) & DSR_DE ? "" : "in"); - for (cnt = 0; cntring_buffers; cnt++) + for (cnt = 0; cnt < port_to_card(port)->tx_ring_buffers; cnt++) printk(" %02X", readb(&(desc_address(port, cnt, 1)->stat))); printk("\n"); @@ -751,7 +771,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 +788,49 @@ } -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 + } + + 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.orig/drivers/net/wan/n2.c 2003-03-29 23:40:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/n2.c 2003-03-30 22:14:01.000000000 +0200 @@ -1,12 +1,11 @@ /* * SDL Inc. RISCom/N2 synchronous serial card driver for Linux * - * Copyright (C) 1998-2002 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. * * For information see http://hq.pm.waw.pl/hdlc/ * @@ -34,14 +33,21 @@ #include "hd64570.h" -static const char* version = "SDL RISCom/N2 driver version: 1.10"; +static const char* version = "SDL RISCom/N2 driver version: 1.13"; static const char* devname = "RISCom/N2"; #define USE_WINDOWSIZE 16384 #define USE_BUS16BITS 1 #define CLOCK_BASE 9830400 /* 9.8304 MHz */ - +#define MAX_PAGES 16 /* 16 RAM pages at max */ +#define MAX_RAM_SIZE 0x80000 /* 512 KB */ +#if MAX_RAM_SIZE > MAX_PAGES * USE_WINDOWSIZE +#undef MAX_RAM_SIZE +#define MAX_RAM_SIZE (MAX_PAGES * USE_WINDOWSIZE) +#endif #define N2_IOPORTS 0x10 +#define NEED_DETECT_RAM +#define MAX_TX_BUFFERS 10 static char *hw = NULL; /* pointer to hw=xxx command line string */ @@ -86,16 +92,16 @@ struct card_s *card; spinlock_t lock; /* TX lock */ sync_serial_settings settings; + int valid; /* port enabled */ + 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 valid; /* port enabled */ u8 phy_node; /* physical port # - 0 or 1 */ u8 log_node; /* logical port # */ - u8 rxin; /* rx ring buffer 'in' pointer */ - u8 txin; /* tx ring buffer 'in' and 'last' pointers */ - u8 txlast; - u8 rxpart; /* partial frame received, next frame invalid*/ }port_t; @@ -106,8 +112,9 @@ u32 ram_size; /* number of bytes */ u16 io; /* IO Base address */ u16 buff_offset; /* offset of first buffer of first channel */ + u16 rx_ring_buffers; /* number of buffers in a ring */ + u16 tx_ring_buffers; u8 irq; /* IRQ (3-15) */ - u8 ring_buffers; /* number of buffers in a ring */ port_t ports[2]; struct card_s *next_card; @@ -216,7 +223,7 @@ MOD_INC_USE_COUNT; mcr &= port->phy_node ? ~DTR_PORT1 : ~DTR_PORT0; /* set DTR ON */ outb(mcr, io + N2_MCR); - + outb(inb(io + N2_PCR) | PCR_ENWIN, io + N2_PCR); /* open window */ outb(inb(io + N2_PSR) | PSR_DMAEN, io + N2_PSR); /* enable dma */ sca_open(hdlc); @@ -297,62 +304,6 @@ -static u8 n2_count_page(card_t *card) -{ - u8 page; - int i, bcount = USE_WINDOWSIZE, wcount = USE_WINDOWSIZE/2; - u16 *dp = (u16*)card->winbase; - u8 *bp = (u8*)card->winbase; - u8 psr = inb(card->io + N2_PSR) & PSR_WINBITS; - - - for (page = 0; page < 16; page++) { - outb(psr | page, card->io + N2_PSR); /* select a page */ - writeb(page, dp); - if (readb(dp) != page) - break; /* If can't read back, no good memory */ - - outb(psr, card->io + N2_PSR); /* goto page 0 */ - if (readb(dp)) - break; /* If page 0 changed, then wrapped around */ - - outb(psr | page, card->io + N2_PSR); /* select page again */ - - /* first do byte tests */ - for (i = 0; i < bcount; i++) - writeb(i, bp + i); - for (i = 0; i < bcount; i++) - if (readb(bp + i) != (i & 0xff)) - return 0; - - for (i = 0; i < bcount; i++) - writeb(~i, bp + i); - for (i = 0; i < bcount; i++) - if (readb(bp + i) != (~i & 0xff)) - return 0; - - /* next do 16-bit tests */ - for (i = 0; i < wcount; i++) - writew(0x55AA, dp + i); - for (i = 0; i < wcount; i++) - if (readw(dp + i) != 0x55AA) - return 0; - - for (i = 0; i < wcount; i++) - writew(0xAA55, dp + i); - for (i = 0; i < wcount; i++) - if (readw(dp + i) != 0xAA55) - return 0; - - for (i = 0; i < wcount; i++) - writew(page, dp + i); - } - - return page; -} - - - static void n2_destroy_card(card_t *card) { int cnt; @@ -376,11 +327,12 @@ -static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase, - long valid0, long valid1) +static int __init n2_run(unsigned long io, unsigned long irq, + unsigned long winbase, long valid0, long valid1) { card_t *card; u8 cnt, pcr; + int i; if (io < 0x200 || io > 0x3FF || (io % N2_IOPORTS) != 0) { printk(KERN_ERR "n2: invalid I/O port value\n"); @@ -391,7 +343,7 @@ printk(KERN_ERR "n2: invalid IRQ value\n"); return -ENODEV; } - + if (winbase < 0xA0000 || winbase > 0xFFFFF || (winbase & 0xFFF) != 0) { printk(KERN_ERR "n2: invalid RAM value\n"); return -ENODEV; @@ -451,25 +403,27 @@ pcr = PCR_ENWIN | PCR_VPM | (USE_BUS16BITS ? PCR_BUS16 : 0); outb(pcr, io + N2_PCR); - cnt = n2_count_page(card); - if (!cnt) { - printk(KERN_ERR "n2: memory test failed.\n"); - n2_destroy_card(card); - return -EIO; - } + card->ram_size = sca_detect_ram(card, card->winbase, MAX_RAM_SIZE); - card->ram_size = cnt * USE_WINDOWSIZE; + /* number of TX + RX buffers for one port */ + i = card->ram_size / ((valid0 + valid1) * (sizeof(pkt_desc) + + HDLC_MAX_MRU)); - /* 4 rings required for 2 ports, 2 rings for one port */ - card->ring_buffers = card->ram_size / - ((valid0 + valid1) * 2 * (sizeof(pkt_desc) + HDLC_MAX_MRU)); + card->tx_ring_buffers = min(i / 2, MAX_TX_BUFFERS); + card->rx_ring_buffers = i - card->tx_ring_buffers; - card->buff_offset = (valid0 + valid1) * 2 * (sizeof(pkt_desc)) - * card->ring_buffers; + card->buff_offset = (valid0 + valid1) * sizeof(pkt_desc) * + (card->tx_ring_buffers + card->rx_ring_buffers); printk(KERN_DEBUG "n2: RISCom/N2 %u KB RAM, IRQ%u, " - "using %u packets rings\n", card->ram_size / 1024, card->irq, - card->ring_buffers); + "using %u TX + %u RX packets rings\n", card->ram_size / 1024, + card->irq, card->tx_ring_buffers, card->rx_ring_buffers); + + if (card->tx_ring_buffers < 1) { + printk(KERN_ERR "n2: RAM test failed\n"); + n2_destroy_card(card); + return -EIO; + } pcr |= PCR_RUNSCA; /* run SCA */ outb(pcr, io + N2_PCR); @@ -531,7 +485,7 @@ return -ENOSYS; /* no parameters specified, abort */ } - printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version); + printk(KERN_INFO "%s\n", version); do { unsigned long io, irq, ram; @@ -558,7 +512,7 @@ break; hw++; } - + if (!valid[0] && !valid[1]) break; /* at least one port must be used */ @@ -566,7 +520,7 @@ n2_run(io, irq, ram, valid[0], valid[1]); if (*hw == '\x0') - return 0; + return first_card ? 0 : -ENOSYS; }while(*hw++ == ':'); printk(KERN_ERR "n2: invalid hardware parameters\n"); @@ -602,6 +556,6 @@ MODULE_AUTHOR("Krzysztof Halasa "); MODULE_DESCRIPTION("RISCom/N2 serial port driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_PARM(hw, "s"); /* hw=io,irq,ram,ports:io,irq,... */ EXPORT_NO_SYMBOLS; --- linux-2.4.orig/drivers/net/wan/c101.c 2003-03-29 23:40:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/c101.c 2003-03-30 22:14:01.000000000 +0200 @@ -1,12 +1,11 @@ /* * Moxa C101 synchronous serial card driver for Linux * - * Copyright (C) 2000-2002 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. * * For information see http://hq.pm.waw.pl/hdlc/ * @@ -31,7 +30,7 @@ #include "hd64570.h" -static const char* version = "Moxa C101 driver version: 1.10"; +static const char* version = "Moxa C101 driver version: 1.13"; static const char* devname = "C101"; #define C101_PAGE 0x1D00 @@ -41,6 +40,10 @@ #define C101_MAPPED_RAM_SIZE 0x4000 #define RAM_SIZE (256 * 1024) +#define TX_RING_BUFFERS 10 +#define RX_RING_BUFFERS ((RAM_SIZE - C101_WINDOW_SIZE) / \ + (sizeof(pkt_desc) + HDLC_MAX_MRU) - TX_RING_BUFFERS) + #define CLOCK_BASE 9830400 /* 9.8304 MHz */ #define PAGE0_ALWAYS_MAPPED @@ -52,20 +55,20 @@ spinlock_t lock; /* TX lock */ u8 *win0base; /* ISA window base address */ u32 phy_winbase; /* ISA physical base address */ - u16 buff_offset; /* offset of first buffer of first channel */ sync_serial_settings settings; + int rxpart; /* partial frame received, next frame invalid*/ unsigned short encoding; unsigned short parity; + u16 rx_ring_buffers; /* number of buffers in a ring */ + u16 tx_ring_buffers; + u16 buff_offset; /* offset of first buffer of first channel */ + 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 irq; /* IRQ (3-15) */ - u8 ring_buffers; /* number of buffers in a ring */ u8 page; - u8 rxin; /* rx ring buffer 'in' pointer */ - u8 txin; /* tx ring buffer 'in' and 'last' pointers */ - u8 txlast; - u8 rxpart; /* partial frame received, next frame invalid*/ - struct card_s *next_card; }card_t; @@ -78,7 +81,12 @@ #define sca_in(reg, card) readb((card)->win0base + C101_SCA + (reg)) #define sca_out(value, reg, card) writeb(value, (card)->win0base + C101_SCA + (reg)) #define sca_inw(reg, card) readw((card)->win0base + C101_SCA + (reg)) -#define sca_outw(value, reg, card) writew(value, (card)->win0base + C101_SCA + (reg)) + +/* EDA address register must be set in EDAL, EDAH order - 8 bit ISA bus */ +#define sca_outw(value, reg, card) do { \ + writeb(value & 0xFF, (card)->win0base + C101_SCA + (reg)); \ + writeb((value >> 8 ) & 0xFF, (card)->win0base + C101_SCA + (reg+1));\ +} while(0) #define port_to_card(port) (port) #define log_node(port) (0) @@ -242,7 +250,7 @@ -static int c101_run(unsigned long irq, unsigned long winbase) +static int __init c101_run(unsigned long irq, unsigned long winbase) { struct net_device *dev; card_t *card; @@ -285,9 +293,10 @@ return -EBUSY; } - /* 2 rings required for 1 port */ - card->ring_buffers = (RAM_SIZE -C101_WINDOW_SIZE) / (2 * HDLC_MAX_MRU); - printk(KERN_DEBUG "c101: using %u packets rings\n",card->ring_buffers); + card->tx_ring_buffers = TX_RING_BUFFERS; + card->rx_ring_buffers = RX_RING_BUFFERS; + printk(KERN_DEBUG "c101: using %u TX + %u RX packets rings\n", + card->tx_ring_buffers, card->rx_ring_buffers); card->buff_offset = C101_WINDOW_SIZE; /* Bytes 1D00-1FFF reserved */ @@ -337,7 +346,7 @@ return -ENOSYS; /* no parameters specified, abort */ } - printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version); + printk(KERN_INFO "%s\n", version); do { unsigned long irq, ram; @@ -352,7 +361,7 @@ c101_run(irq, ram); if (*hw == '\x0') - return 0; + return first_card ? 0 : -ENOSYS; }while(*hw++ == ':'); printk(KERN_ERR "c101: invalid hardware parameters\n"); @@ -389,6 +398,6 @@ MODULE_AUTHOR("Krzysztof Halasa "); MODULE_DESCRIPTION("Moxa C101 serial port driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_PARM(hw, "s"); /* hw=irq,ram:irq,... */ EXPORT_NO_SYMBOLS; --- linux-2.4.orig/drivers/net/wan/farsync.c 2003-03-29 23:40:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/farsync.c 2003-03-30 22:14:01.000000000 +0200 @@ -764,7 +764,7 @@ /* Push upstream */ skb->mac.raw = skb->data; skb->dev = hdlc_to_dev ( &port->hdlc ); - skb->protocol = htons ( ETH_P_HDLC ); + skb->protocol = hdlc_type_trans(skb, skb->dev); netif_rx ( skb ); port_to_dev ( port )->last_rx = jiffies; --- linux-2.4.orig/drivers/net/wan/dscc4.c 2003-03-29 23:40:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/dscc4.c 2003-03-30 22:14:01.000000000 +0200 @@ -488,7 +488,7 @@ dpriv->rx_skbuff[dirty] = skb; if (skb) { skb->dev = dev; - skb->protocol = htons(ETH_P_HDLC); + skb->protocol = hdlc_type_trans(skb, dev); skb->mac.raw = skb->data; rx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data, len, PCI_DMA_FROMDEVICE); --- linux-2.4.orig/Documentation/Configure.help 2003-03-29 23:40:48.000000000 +0100 +++ linux-2.4/Documentation/Configure.help 2003-03-30 22:15:26.000000000 +0200 @@ -9649,14 +9649,15 @@ This driver supports the FarSync T-Series X.21 (and V.35/V.24) cards from FarSite Communications Ltd. Synchronous communication is supported on all ports at speeds up to - 8Mb/s (128K on V.24) using synchronous PPP or Cisco HDLC. + 8Mb/s (128K on V.24) using synchronous PPP, Cisco HDLC, raw HDLC, + Frame Relay or X.25/LAPB. If you want to compile this 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 farsync.o and if you want the module to be automatically loaded when the interface is referenced then you - should add "alias syncX farsync" to /etc/modules.conf for each + should add "alias hdlcX farsync" to /etc/modules.conf for each interface, where X is 0, 1, 2, ... Frame Relay (DLCI) support @@ -10531,6 +10532,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 @@ -10545,13 +10555,6 @@ If unsure, say N here. -Frame-Relay bridging support -CONFIG_HDLC_FR_BRIDGE - Say Y to this option if you want generic HDLC driver to support - bridging LAN frames over Frame-Relay links. - - If unsure, say N here. - Synchronous Point-to-Point Protocol (PPP) support CONFIG_HDLC_PPP Say Y to this option if you want generic HDLC driver to support @@ -10594,6 +10597,25 @@ 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. + Ethernet (10 or 100Mbit) CONFIG_NET_ETHERNET Ethernet (also called IEEE 802.3 or ISO 8802-2) is the most common --- linux-2.4.orig/drivers/net/wan/Config.in 2003-03-29 23:40:58.000000000 +0100 +++ linux-2.4/drivers/net/wan/Config.in 2003-03-30 22:14:01.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