--- linux-2.4.20/Documentation/networking/generic-hdlc.txt 2003-04-07 17:14:17.000000000 +0200 +++ linux-2.4/Documentation/networking/generic-hdlc.txt 2003-04-07 00:25:09.000000000 +0200 @@ -0,0 +1,129 @@ +Generic HDLC layer +Krzysztof Halasa +January, 2003 + + +Generic HDLC layer currently supports: +- 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). + +There are hardware drivers for the following cards: +- C101 by Moxa Technologies Co., Ltd. +- 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: + http://hq.pm.waw.pl/hdlc/ + +Compile sethdlc.c utility: + gcc -O2 -Wall -o sethdlc sethdlc.c +Make sure you're using a correct version of sethdlc for your kernel. + +Use sethdlc to set physical interface, clock rate, HDLC mode used, +and add any required PVCs if using Frame Relay. +Usually you want something like: + + sethdlc hdlc0 clock int rate 128000 + sethdlc hdlc0 cisco interval 10 timeout 25 +or + sethdlc hdlc0 rs232 clock ext + sethdlc fr lmi ansi + sethdlc create 99 + +In Frame Relay mode, ifconfig master hdlc device up (without assigning +any IP address to it) before using pvc devices. + + +Setting interface: + +* v35 | rs232 | x21 | t1 | e1 - sets physical interface for a given port + if the card has software-selectable interfaces + loopback - activate hardware loopback (for testing only) +* clock ext - external clock (uses DTE RX and TX clock) +* clock int - internal clock (provides clock signal on DCE clock output) +* clock txint - TX internal, RX external (provides TX clock on DCE output) +* clock txfromrx - TX clock derived from RX clock (TX clock on DCE output) +* rate - sets clock rate in bps (not required for external clock or + for txfromrx) + +Setting protocol: + +* hdlc - sets raw HDLC (IP-only) mode + nrz / nrzi / fm-mark / fm-space / manchester - sets transmission code + 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 + we assume the link is down + +* ppp - sets synchronous PPP mode + +* x25 - sets X.25 mode + +* fr - Frame Relay mode + lmi ansi / ccitt / none - LMI (link management) type + dce - Frame Relay DCE (network) side LMI instead of default DTE (user). + It has nothing to do with clocks! + t391 - link integrity verification polling timer (in seconds) - user + t392 - polling verification timer (in seconds) - network + n391 - full status polling counter - user + n392 - error threshold - both user and network + n393 - monitored events count - both user and network + +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. + + + + +Board-specific issues +--------------------- + +n2.o and c101.o need parameters to work (note double quotes): + + insmod n2 hw='"io,irq,ram,ports[:io,irq,...]"' +example: + insmod n2 hw='"0x300,10,0xD0000,01"' + +or + insmod c101 hw='"irq,ram[:irq,...]" +example: + insmod c101 hw='"9,0xdc000"' + +If built into the kernel, these drivers need kernel (command line) parameters: + n2=io,irq,ram,ports:... +or + c101=irq,ram:... + + + +If you have a problem with N2 or C101 card, you can issue the "private" +command to see port's packet descriptor rings (in kernel logs): + + sethdlc hdlc0 private + +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. + +For patches and other info look at http://hq.pm.waw.pl/hdlc/ --- linux-2.4.20/include/linux/if.h 2001-11-22 20:47:07.000000000 +0100 +++ linux-2.4/include/linux/if.h 2003-04-07 00:27:45.000000000 +0200 @@ -22,6 +22,9 @@ #include /* for "__kernel_caddr_t" et al */ #include /* for "struct sockaddr" et al */ +#define IFNAMSIZ 16 +#include + /* Standard interface flags (netdevice->flags). */ #define IFF_UP 0x1 /* interface is up */ #define IFF_BROADCAST 0x2 /* broadcast address valid */ @@ -48,6 +51,33 @@ /* Private (from user) interface flags (netdevice->priv_flags). */ #define IFF_802_1Q_VLAN 0x1 /* 802.1Q VLAN device. */ + +#define IF_GET_IFACE 0x0001 /* for querying only */ +#define IF_GET_PROTO 0x0002 + +/* For definitions see hdlc.h */ +#define IF_IFACE_V35 0x1000 /* V.35 serial interface */ +#define IF_IFACE_V24 0x1001 /* V.24 serial interface */ +#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 /* can't be set by software */ + +/* For definitions see hdlc.h */ +#define IF_PROTO_HDLC 0x2000 /* raw HDLC protocol */ +#define IF_PROTO_PPP 0x2001 /* PPP protocol */ +#define IF_PROTO_CISCO 0x2002 /* Cisco HDLC protocol */ +#define IF_PROTO_FR 0x2003 /* Frame Relay protocol */ +#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 + + /* * Device mapping structure. I'd just gone off and designed a * beautiful scheme using only loadable modules with arguments @@ -69,6 +99,24 @@ /* 3 bytes spare */ }; +struct if_settings +{ + unsigned int type; /* Type of physical device or protocol */ + unsigned int size; /* Size of the data allocated by the caller */ + union { + /* {atm/eth/dsl}_settings anyone ? */ + raw_hdlc_proto *raw_hdlc; + cisco_proto *cisco; + fr_proto *fr; + fr_proto_pvc *fr_pvc; + fr_proto_pvc_info *fr_pvc_info; + + /* interface settings */ + sync_serial_settings *sync; + te1_settings *te1; + } ifs_ifsu; +}; + /* * Interface request structure used for socket * ioctl's. All interface ioctl's must have parameter @@ -79,7 +127,6 @@ struct ifreq { #define IFHWADDRLEN 6 -#define IFNAMSIZ 16 union { char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ @@ -98,6 +145,7 @@ char ifru_slave[IFNAMSIZ]; /* Just fits the size */ char ifru_newname[IFNAMSIZ]; char * ifru_data; + struct if_settings ifru_settings; } ifr_ifru; }; @@ -117,6 +165,7 @@ #define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */ #define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */ #define ifr_newname ifr_ifru.ifru_newname /* New name */ +#define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/ /* * Structure used in SIOCGIFCONF request. --- linux-2.4.20/include/linux/if_ether.h 2002-11-29 00:53:15.000000000 +0100 +++ linux-2.4/include/linux/if_ether.h 2003-04-07 00:18:44.000000000 +0200 @@ -85,6 +85,7 @@ #define ETH_P_CONTROL 0x0016 /* Card specific control frames */ #define ETH_P_IRDA 0x0017 /* Linux-IrDA */ #define ETH_P_ECONET 0x0018 /* Acorn Econet */ +#define ETH_P_HDLC 0x0019 /* HDLC frames */ /* * This is an Ethernet frame header. --- linux-2.4.20/include/linux/hdlc.h 2001-07-26 22:51:45.000000000 +0200 +++ linux-2.4/include/linux/hdlc.h 2003-04-07 00:30:26.000000000 +0200 @@ -1,75 +1,59 @@ /* * Generic HDLC support routines for Linux * - * Copyright (C) 1999, 2000 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 #define __HDLC_H -/* Ioctls - to be changed */ -#define HDLCGSLOTMAP (0x89F4) /* E1/T1 slot bitmap */ -#define HDLCGCLOCK (0x89F5) /* clock sources */ -#define HDLCGCLOCKRATE (0x89F6) /* clock rate */ -#define HDLCGMODE (0x89F7) /* internal to hdlc.c - protocol used */ -#define HDLCGLINE (0x89F8) /* physical interface */ -#define HDLCSSLOTMAP (0x89F9) -#define HDLCSCLOCK (0x89FA) -#define HDLCSCLOCKRATE (0x89FB) -#define HDLCSMODE (0x89FC) /* internal to hdlc.c - select protocol */ -#define HDLCPVC (0x89FD) /* internal to hdlc.c - create/delete PVC */ -#define HDLCSLINE (0x89FE) -#define HDLCRUN (0x89FF) /* Download firmware and run board */ - -/* Modes */ -#define MODE_NONE 0x00000000 /* Not initialized */ -#define MODE_DCE 0x00000080 /* DCE */ -#define MODE_HDLC 0x00000100 /* Raw HDLC frames */ -#define MODE_CISCO 0x00000200 -#define MODE_PPP 0x00000400 -#define MODE_FR 0x00000800 /* Any LMI */ -#define MODE_FR_ANSI 0x00000801 -#define MODE_FR_CCITT 0x00000802 -#define MODE_X25 0x00001000 -#define MODE_MASK 0x0000FF00 -#define MODE_SOFT 0x80000000 /* Driver modes, using hardware HDLC */ - -/* Lines */ -#define LINE_DEFAULT 0x00000000 -#define LINE_V35 0x00000001 -#define LINE_RS232 0x00000002 -#define LINE_X21 0x00000003 -#define LINE_T1 0x00000004 -#define LINE_E1 0x00000005 -#define LINE_MASK 0x000000FF -#define LINE_LOOPBACK 0x80000000 /* On-card loopback */ - -#define CLOCK_EXT 0 /* External TX and RX clock - DTE */ -#define CLOCK_INT 1 /* Internal TX and RX clock - DCE */ -#define CLOCK_TXINT 2 /* Internal TX and external RX clock */ -#define CLOCK_TXFROMRX 3 /* TX clock derived from external RX clock */ +#define GENERIC_HDLC_VERSION 4 /* For synchronization with sethdlc utility */ +#define CLOCK_DEFAULT 0 /* Default setting */ +#define CLOCK_EXT 1 /* External TX and RX clock - DTE */ +#define CLOCK_INT 2 /* Internal TX and RX clock - DCE */ +#define CLOCK_TXINT 3 /* Internal TX and external RX clock */ +#define CLOCK_TXFROMRX 4 /* TX clock derived from external RX clock */ + + +#define ENCODING_DEFAULT 0 /* Default setting */ +#define ENCODING_NRZ 1 +#define ENCODING_NRZI 2 +#define ENCODING_FM_MARK 3 +#define ENCODING_FM_SPACE 4 +#define ENCODING_MANCHESTER 5 + + +#define PARITY_DEFAULT 0 /* Default setting */ +#define PARITY_NONE 1 /* No parity */ +#define PARITY_CRC16_PR0 2 /* CRC16, initial value 0x0000 */ +#define PARITY_CRC16_PR1 3 /* CRC16, initial value 0xFFFF */ +#define PARITY_CRC16_PR0_CCITT 4 /* CRC16, initial 0x0000, ITU-T version */ +#define PARITY_CRC16_PR1_CCITT 5 /* CRC16, initial 0xFFFF, ITU-T version */ +#define PARITY_CRC32_PR0_CCITT 6 /* CRC32, initial value 0x00000000 */ +#define PARITY_CRC32_PR1_CCITT 7 /* CRC32, initial value 0xFFFFFFFF */ + +#define LMI_DEFAULT 0 /* Default setting */ +#define LMI_NONE 1 /* No LMI, all PVCs are static */ +#define LMI_ANSI 2 /* ANSI Annex D */ +#define LMI_CCITT 3 /* ITU-T Annex A */ -#define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */ -#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */ #ifdef __KERNEL__ #include #include #include +#include -#define MAXLEN_LMISTAT 20 /* max size of status enquiry frame */ +#define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */ +#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10 + 14 + 4) /* for ETH+VLAN over FR */ -#define LINK_STATE_RELIABLE 0x01 -#define LINK_STATE_REQUEST 0x02 /* full stat sent (DCE) / req pending (DTE) */ -#define LINK_STATE_CHANGED 0x04 /* change in PVCs state, send full report */ -#define LINK_STATE_FULLREP_SENT 0x08 /* full report sent */ +#define MAXLEN_LMISTAT 20 /* max size of status enquiry frame */ #define PVC_STATE_NEW 0x01 #define PVC_STATE_ACTIVE 0x02 @@ -112,6 +96,7 @@ typedef struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) unsigned ea1 : 1; unsigned cr : 1; unsigned dlcih: 6; @@ -121,6 +106,19 @@ unsigned becn : 1; unsigned fecn : 1; unsigned dlcil: 4; +#elif defined (__BIG_ENDIAN_BITFIELD) + unsigned dlcih: 6; + unsigned cr : 1; + unsigned ea1 : 1; + + unsigned dlcil: 4; + unsigned fecn : 1; + unsigned becn : 1; + unsigned de : 1; + unsigned ea2 : 1; +#else +#error "Please fix " +#endif }__attribute__ ((packed)) fr_hdr; @@ -146,68 +144,107 @@ 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; - - u8 state; - u8 newstate; + 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 { + 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; -typedef struct { - u32 last_errors; /* last errors bit list */ - int last_poll; /* ! */ - u8 T391; /* ! link integrity verification polling timer */ - u8 T392; /* ! polling verification timer */ - u8 N391; /* full status polling counter */ - u8 N392; /* error threshold */ - u8 N393; /* monitored events count */ - u8 N391cnt; - - u8 state; /* ! */ - u32 txseq; /* ! TX sequence number - Cisco uses 4 bytes */ - u32 rxseq; /* ! RX sequence number */ -}fr_lmi; /* ! means used in Cisco HDLC as well */ - - typedef struct hdlc_device_struct { - /* to be initialized by hardware driver: */ + /* To be initialized by hardware driver */ struct net_device netdev; /* master net device - must be first */ struct net_device_stats stats; - struct ppp_device pppdev; - struct ppp_device *syncppp_ptr; + /* used by HDLC layer to take control over HDLC device from hw driver*/ + int (*attach)(struct hdlc_device_struct *hdlc, + unsigned short encoding, unsigned short parity); + + /* hardware driver must handle this instead of dev->hard_start_xmit */ + int (*xmit)(struct sk_buff *skb, struct net_device *dev); - /* set_mode may be NULL if HDLC-only board */ - int (*set_mode)(struct hdlc_device_struct *hdlc, int mode); - int (*open)(struct hdlc_device_struct *hdlc); - void (*close)(struct hdlc_device_struct *hdlc); - int (*xmit)(struct hdlc_device_struct *hdlc, struct sk_buff *skb); - int (*ioctl)(struct hdlc_device_struct *hdlc, struct ifreq *ifr, - int cmd); - - /* Only in "hardware" FR modes etc. - may be NULL */ - int (*create_pvc)(pvc_device *pvc); - void (*destroy_pvc)(pvc_device *pvc); - int (*open_pvc)(pvc_device *pvc); - void (*close_pvc)(pvc_device *pvc); - - /* for hdlc.c internal use only */ - pvc_device *first_pvc; - u16 pvc_count; - int mode; - struct timer_list timer; - fr_lmi lmi; + /* Things below are for HDLC layer internal use only */ + int (*ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); + int (*open)(struct hdlc_device_struct *hdlc); + 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 dce_pvc_count; + + struct timer_list timer; + int last_poll; + int reliable; + int dce_changed; + int request; + int fullrep_sent; + u32 last_errors; /* last errors bit list */ + u8 n391cnt; + u8 txseq; /* TX sequence number */ + u8 rxseq; /* RX sequence number */ + }fr; + + struct { + cisco_proto settings; + + struct timer_list timer; + int last_poll; + int up; + u32 txseq; /* TX sequence number */ + u32 rxseq; /* RX sequence number */ + }cisco; + + struct { + raw_hdlc_proto settings; + }raw_hdlc; + + struct { + struct ppp_device pppdev; + struct ppp_device *syncppp_ptr; + int (*old_change_mtu)(struct net_device *dev, + int new_mtu); + }ppp; + }state; }hdlc_device; + +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); +int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr); + + +/* Exported from hdlc.o */ + +/* Called by hardware driver when a user requests HDLC service */ +int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); + +/* Must be used by hardware driver on module startup/exit */ int register_hdlc_device(hdlc_device *hdlc); void unregister_hdlc_device(hdlc_device *hdlc); -void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb); static __inline__ struct net_device* hdlc_to_dev(hdlc_device *hdlc) @@ -222,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; } @@ -240,97 +271,75 @@ } -static __inline__ const char *pvc_to_name(pvc_device *pvc) +static __inline__ u16 q922_to_dlci(u8 *hdr) { - return pvc_to_dev(pvc)->name; + return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4); } -static __inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status, u8 *state) -{ - *state &= ~(PVC_STATE_ACTIVE | PVC_STATE_NEW); - if (status[2] & 0x08) - *state |= PVC_STATE_NEW; - else if (status[2] & 0x02) - *state |= PVC_STATE_ACTIVE; - - return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3); -} - -static __inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status, - u8 state) +static __inline__ void dlci_to_q922(u8 *hdr, u16 dlci) { - status[0] = (dlci>>4) & 0x3F; - status[1] = ((dlci<<3) & 0x78) | 0x80; - status[2] = 0x80; - - if (state & PVC_STATE_NEW) - status[2] |= 0x08; - else if (state & PVC_STATE_ACTIVE) - status[2] |= 0x02; + hdr[0] = (dlci >> 2) & 0xFC; + hdr[1] = ((dlci << 4) & 0xF0) | 0x01; } -static __inline__ u16 netdev_dlci(struct net_device *dev) +static __inline__ void debug_frame(const struct sk_buff *skb) { - return ntohs(*(u16*)dev->dev_addr); -} - - + int i; -static __inline__ u16 q922_to_dlci(u8 *hdr) -{ - return ((hdr[0] & 0xFC)<<2) | ((hdr[1] & 0xF0)>>4); + for (i=0; ilen; i++) { + if (i == 100) { + printk("...\n"); + return; + } + printk(" %02X", skb->data[i]); + } + printk("\n"); } -static __inline__ void dlci_to_q922(u8 *hdr, u16 dlci) +/* Must be called by hardware driver when HDLC device is being opened */ +static __inline__ int hdlc_open(hdlc_device *hdlc) { - hdr[0] = (dlci>>2) & 0xFC; - hdr[1] = ((dlci<<4) & 0xF0) | 0x01; -} + if (hdlc->proto == -1) + return -ENOSYS; /* no protocol attached */ + if (hdlc->open) + return hdlc->open(hdlc); + return 0; +} -static __inline__ int mode_is(hdlc_device *hdlc, int mask) +/* Must be called by hardware driver when HDLC device is being closed */ +static __inline__ void hdlc_close(hdlc_device *hdlc) { - return (hdlc->mode & mask) == mask; + if (hdlc->stop) + hdlc->stop(hdlc); } - -static __inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci) +/* May be used by hardware driver to gain control over HDLC device */ +static __inline__ void hdlc_proto_detach(hdlc_device *hdlc) { - pvc_device *pvc=hdlc->first_pvc; - - while (pvc) { - if (netdev_dlci(&pvc->netdev) == dlci) - return pvc; - pvc=pvc->next; - } - - return NULL; + if (hdlc->proto_detach) + hdlc->proto_detach(hdlc); + hdlc->proto_detach = NULL; } - -static __inline__ void debug_frame(const struct sk_buff *skb) +static __inline__ unsigned short hdlc_type_trans(struct sk_buff *skb, + struct net_device *dev) { - int i; - - for (i=0; ilen; i++) { - if (i == 100) { - printk("...\n"); - return; - } - printk(" %02X", skb->data[i]); - } - printk("\n"); + 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.20/include/linux/hdlc/ioctl.h 2003-04-07 17:14:17.000000000 +0200 +++ linux-2.4/include/linux/hdlc/ioctl.h 2003-04-07 00:25:09.000000000 +0200 @@ -0,0 +1,48 @@ +#ifndef __HDLC_IOCTL_H__ +#define __HDLC_IOCTL_H__ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; +} sync_serial_settings; /* V.35, V.24, X.21 */ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; + unsigned int slot_map; +} te1_settings; /* T1, E1 */ + +typedef struct { + unsigned short encoding; + unsigned short parity; +} raw_hdlc_proto; + +typedef struct { + unsigned int t391; + unsigned int t392; + unsigned int n391; + unsigned int n392; + unsigned int n393; + unsigned short lmi; + unsigned short dce; /* 1 for DCE (network side) operation */ +} fr_proto; + +typedef struct { + unsigned int dlci; +} 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 */ + +#endif /* __HDLC_IOCTL_H__ */ --- linux-2.4.20/include/linux/sockios.h 2001-11-07 23:39:36.000000000 +0100 +++ linux-2.4/include/linux/sockios.h 2003-04-07 00:18:44.000000000 +0200 @@ -81,6 +81,8 @@ #define SIOCGMIIREG 0x8948 /* Read MII PHY register. */ #define SIOCSMIIREG 0x8949 /* Write MII PHY register. */ +#define SIOCWANDEV 0x894A /* get/set netdev parameters */ + /* ARP cache control calls. */ /* 0x8950 - 0x8952 * obsolete calls, don't re-use */ #define SIOCDARP 0x8953 /* delete ARP table entry */ --- linux-2.4.20/drivers/net/wan/Makefile 2002-08-03 02:39:44.000000000 +0200 +++ linux-2.4/drivers/net/wan/Makefile 2003-04-07 14:22:20.000000000 +0200 @@ -9,7 +9,7 @@ O_TARGET := wan.o -export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc.o +export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc_generic.o list-multi = wanpipe.o cyclomx.o wanpipe-objs = sdlamain.o sdla_ft1.o $(wanpipe-y) @@ -22,6 +22,14 @@ cyclomx-objs = cycx_main.o $(cyclomx-y) cyclomx-$(CONFIG_CYCLOMX_X25) += cycx_x25.o +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 +hdlc-$(CONFIG_HDLC_X25) += hdlc_x25.o +hdlc-objs := $(hdlc-y) obj-$(CONFIG_HOSTESS_SV11) += z85230.o syncppp.o hostess_sv11.o obj-$(CONFIG_SEALEVEL_4021) += z85230.o syncppp.o sealevel.o @@ -64,12 +72,17 @@ obj-$(CONFIG_LAPBETHER) += lapbether.o obj-$(CONFIG_SBNI) += sbni.o obj-$(CONFIG_HDLC) += hdlc.o -obj-$(CONFIG_HDLC_PPP) += syncppp.o +ifeq ($(CONFIG_HDLC_PPP),y) + obj-$(CONFIG_HDLC) += syncppp.o +endif obj-$(CONFIG_N2) += n2.o obj-$(CONFIG_C101) += c101.o include $(TOPDIR)/Rules.make +hdlc.o: $(hdlc-objs) + $(LD) -r -o $@ $(hdlc-objs) + wanpipe.o: $(wanpipe-objs) $(LD) -r -o $@ $(wanpipe-objs) --- linux-2.4.20/drivers/net/wan/hdlc.c 2001-09-14 01:04:43.000000000 +0200 +++ linux-2.4/drivers/net/wan/hdlc.c 2003-04-07 17:14:17.000000000 +0200 @@ -1,1453 +0,0 @@ -/* - * Generic HDLC support routines for Linux - * - * Copyright (C) 1999, 2000 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. - * - * Current status: - * - this is work in progress - * - not heavily tested on SMP - * - currently supported: - * * raw IP-in-HDLC - * * Cisco HDLC - * * Frame Relay with ANSI or CCITT LMI (both user and network side) - * * PPP (using syncppp.c) - * * X.25 - * - * Use sethdlc utility to set line parameters, protocol and PVCs - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* #define DEBUG_PKT */ -/* #define DEBUG_HARD_HEADER */ -/* #define DEBUG_FECN */ -/* #define DEBUG_BECN */ - -static const char* version = "HDLC support module revision 1.02 for Linux 2.4"; - - -#define CISCO_MULTICAST 0x8F /* Cisco multicast address */ -#define CISCO_UNICAST 0x0F /* Cisco unicast address */ -#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */ -#define CISCO_SYS_INFO 0x2000 /* Cisco interface/system info */ -#define CISCO_ADDR_REQ 0 /* Cisco address request */ -#define CISCO_ADDR_REPLY 1 /* Cisco address reply */ -#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */ - -static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); - -/******************************************************** - * - * Cisco HDLC support - * - *******************************************************/ - -static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev, - u16 type, void *daddr, void *saddr, - unsigned int len) -{ - hdlc_header *data; -#ifdef DEBUG_HARD_HEADER - printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name); -#endif - - skb_push(skb, sizeof(hdlc_header)); - data = (hdlc_header*)skb->data; - if (type == CISCO_KEEPALIVE) - data->address = CISCO_MULTICAST; - else - data->address = CISCO_UNICAST; - data->control = 0; - data->protocol = htons(type); - - return sizeof(hdlc_header); -} - - - -static void cisco_keepalive_send(hdlc_device *hdlc, u32 type, - u32 par1, u32 par2) -{ - struct sk_buff *skb; - cisco_packet *data; - - skb = dev_alloc_skb(sizeof(hdlc_header)+sizeof(cisco_packet)); - if (!skb) { - printk(KERN_WARNING "%s: Memory squeeze on cisco_keepalive_send()\n", - hdlc_to_name(hdlc)); - return; - } - skb_reserve(skb, 4); - cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE, - NULL, NULL, 0); - data = (cisco_packet*)skb->tail; - - data->type = htonl(type); - data->par1 = htonl(par1); - data->par2 = htonl(par2); - data->rel = 0xFFFF; - data->time = htonl(jiffies * 1000 / HZ); - - skb_put(skb, sizeof(cisco_packet)); - skb->priority = TC_PRIO_CONTROL; - skb->dev = hdlc_to_dev(hdlc); - - dev_queue_xmit(skb); -} - - - -static void cisco_netif(hdlc_device *hdlc, struct sk_buff *skb) -{ - hdlc_header *data = (hdlc_header*)skb->data; - cisco_packet *cisco_data; - struct in_device *in_dev; - u32 addr, mask; - - if (skb->lenaddress != CISCO_MULTICAST && - data->address != CISCO_UNICAST) - goto rx_error; - - 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); - return; - - case CISCO_KEEPALIVE: - if (skb->len != CISCO_PACKET_LEN && - skb->len != CISCO_BIG_PACKET_LEN) { - printk(KERN_INFO "%s: Invalid length of Cisco " - "control packet (%d bytes)\n", - hdlc_to_name(hdlc), skb->len); - goto rx_error; - } - - cisco_data = (cisco_packet*)skb->data; - - switch(ntohl (cisco_data->type)) { - case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */ - in_dev = hdlc_to_dev(hdlc)->ip_ptr; - addr = 0; - mask = ~0; /* is the mask correct? */ - - if (in_dev != NULL) { - struct in_ifaddr **ifap = &in_dev->ifa_list; - - while (*ifap != NULL) { - if (strcmp(hdlc_to_name(hdlc), - (*ifap)->ifa_label) == 0) { - addr = (*ifap)->ifa_local; - mask = (*ifap)->ifa_mask; - break; - } - ifap = &(*ifap)->ifa_next; - } - - cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY, - addr, mask); - } - dev_kfree_skb_any(skb); - return; - - case CISCO_ADDR_REPLY: - printk(KERN_INFO "%s: Unexpected Cisco IP address " - "reply\n", hdlc_to_name(hdlc)); - goto rx_error; - - case CISCO_KEEPALIVE_REQ: - hdlc->lmi.rxseq = ntohl(cisco_data->par1); - if (ntohl(cisco_data->par2) == hdlc->lmi.txseq) { - hdlc->lmi.last_poll = jiffies; - if (!(hdlc->lmi.state & LINK_STATE_RELIABLE)) { - u32 sec, min, hrs, days; - sec = ntohl(cisco_data->time) / 1000; - min = sec / 60; sec -= min * 60; - hrs = min / 60; min -= hrs * 60; - days = hrs / 24; hrs -= days * 24; - printk(KERN_INFO "%s: Link up (peer " - "uptime %ud%uh%um%us)\n", - hdlc_to_name(hdlc), days, hrs, - min, sec); - } - hdlc->lmi.state |= LINK_STATE_RELIABLE; - } - - dev_kfree_skb_any(skb); - return; - } /* switch(keepalive type) */ - } /* switch(protocol) */ - - printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc), - data->protocol); - dev_kfree_skb_any(skb); - return; - - rx_error: - hdlc->stats.rx_errors++; /* Mark error */ - dev_kfree_skb_any(skb); -} - - - -static void cisco_timer(unsigned long arg) -{ - hdlc_device *hdlc = (hdlc_device*)arg; - - if ((hdlc->lmi.state & LINK_STATE_RELIABLE) && - (jiffies - hdlc->lmi.last_poll >= hdlc->lmi.T392 * HZ)) { - hdlc->lmi.state &= ~LINK_STATE_RELIABLE; - printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc)); - } - - cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ, ++hdlc->lmi.txseq, - hdlc->lmi.rxseq); - hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ; - - hdlc->timer.function = cisco_timer; - hdlc->timer.data = arg; - add_timer(&hdlc->timer); -} - - - -/****************************************************************** - * - * generic Frame Relay routines - * - *****************************************************************/ - - -static int fr_hard_header(struct sk_buff *skb, struct net_device *dev, - u16 type, void *daddr, void *saddr, unsigned int len) -{ - u16 head_len; - - if (!daddr) - daddr = dev->broadcast; - -#ifdef DEBUG_HARD_HEADER - printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name); -#endif - - switch(type) { - case ETH_P_IP: - head_len = 4; - skb_push(skb, head_len); - skb->data[3] = NLPID_IP; - break; - - case ETH_P_IPV6: - head_len = 4; - skb_push(skb, head_len); - skb->data[3] = NLPID_IPV6; - break; - - case LMI_PROTO: - head_len = 4; - skb_push(skb, head_len); - skb->data[3] = LMI_PROTO; - break; - - default: - head_len = 10; - skb_push(skb, head_len); - skb->data[3] = FR_PAD; - skb->data[4] = NLPID_SNAP; - 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; - } - - memcpy(skb->data, daddr, 2); - skb->data[2] = FR_UI; - - return head_len; -} - - - -static inline void fr_log_dlci_active(pvc_device *pvc) -{ - printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc), - pvc->state & PVC_STATE_ACTIVE ? "" : "in", - pvc->state & PVC_STATE_NEW ? " new" : ""); -} - - - -static inline u8 fr_lmi_nextseq(u8 x) -{ - x++; - return x ? x : 1; -} - - - -static void fr_lmi_send(hdlc_device *hdlc, int fullrep) -{ - struct sk_buff *skb; - pvc_device *pvc = hdlc->first_pvc; - int len = mode_is(hdlc, MODE_FR_ANSI) ? LMI_ANSI_LENGTH : LMI_LENGTH; - int stat_len = 3; - u8 *data; - int i = 0; - - if (mode_is(hdlc, MODE_DCE) && fullrep) { - len += hdlc->pvc_count * (2 + stat_len); - if (len > HDLC_MAX_MTU) { - printk(KERN_WARNING "%s: Too many PVCs while sending " - "LMI full report\n", hdlc_to_name(hdlc)); - return; - } - } - - skb = dev_alloc_skb(len); - if (!skb) { - printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n", - 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); - data = skb->tail; - data[i++] = LMI_CALLREF; - data[i++] = mode_is(hdlc, MODE_DCE) ? LMI_STATUS : LMI_STATUS_ENQUIRY; - if (mode_is(hdlc, MODE_FR_ANSI)) - data[i++] = LMI_ANSI_LOCKSHIFT; - data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : - LMI_REPTYPE; - data[i++] = LMI_REPT_LEN; - data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY; - - data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE; - data[i++] = LMI_INTEG_LEN; - data[i++] = hdlc->lmi.txseq = fr_lmi_nextseq(hdlc->lmi.txseq); - data[i++] = hdlc->lmi.rxseq; - - if (mode_is(hdlc, MODE_DCE) && fullrep) { - while (pvc) { - data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? - LMI_CCITT_PVCSTAT:LMI_PVCSTAT; - data[i++] = stat_len; - - if ((hdlc->lmi.state & LINK_STATE_RELIABLE) && - (pvc->netdev.flags & IFF_UP) && - !(pvc->state & (PVC_STATE_ACTIVE|PVC_STATE_NEW))) { - pvc->state |= PVC_STATE_NEW; - fr_log_dlci_active(pvc); - } - - dlci_to_status(hdlc, netdev_dlci(&pvc->netdev), - data+i, pvc->state); - i += stat_len; - pvc = pvc->next; - } - } - - skb_put(skb, i); - skb->priority = TC_PRIO_CONTROL; - skb->dev = hdlc_to_dev(hdlc); - - dev_queue_xmit(skb); -} - - - -static void fr_timer(unsigned long arg) -{ - hdlc_device *hdlc = (hdlc_device*)arg; - int i, cnt = 0, reliable; - u32 list; - - if (mode_is(hdlc, MODE_DCE)) - reliable = (jiffies - hdlc->lmi.last_poll < hdlc->lmi.T392*HZ); - else { - hdlc->lmi.last_errors <<= 1; /* Shift the list */ - if (hdlc->lmi.state & LINK_STATE_REQUEST) { - printk(KERN_INFO "%s: No LMI status reply received\n", - hdlc_to_name(hdlc)); - hdlc->lmi.last_errors |= 1; - } - - for (i = 0, list = hdlc->lmi.last_errors; i < hdlc->lmi.N393; - i++, list >>= 1) - cnt += (list & 1); /* errors count */ - - reliable = (cnt < hdlc->lmi.N392); - } - - if ((hdlc->lmi.state & LINK_STATE_RELIABLE) != - (reliable ? LINK_STATE_RELIABLE : 0)) { - pvc_device *pvc = hdlc->first_pvc; - - while (pvc) {/* Deactivate all PVCs */ - pvc->state &= ~(PVC_STATE_NEW | PVC_STATE_ACTIVE); - pvc = pvc->next; - } - - hdlc->lmi.state ^= LINK_STATE_RELIABLE; - printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc), - reliable ? "" : "un"); - - if (reliable) { - hdlc->lmi.N391cnt = 0; /* Request full status */ - hdlc->lmi.state |= LINK_STATE_CHANGED; - } - } - - if (mode_is(hdlc, MODE_DCE)) - hdlc->timer.expires = jiffies + hdlc->lmi.T392*HZ; - else { - if (hdlc->lmi.N391cnt) - hdlc->lmi.N391cnt--; - - fr_lmi_send(hdlc, hdlc->lmi.N391cnt == 0); - - hdlc->lmi.state |= LINK_STATE_REQUEST; - hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ; - } - - hdlc->timer.function = fr_timer; - hdlc->timer.data = arg; - add_timer(&hdlc->timer); -} - - - -static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb) -{ - int stat_len; - pvc_device *pvc; - int reptype = -1, error; - u8 rxseq, txseq; - int i; - - if (skb->len < (mode_is(hdlc, MODE_FR_ANSI) ? - LMI_ANSI_LENGTH : LMI_LENGTH)) { - printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc)); - return 1; - } - - if (skb->data[5] != (!mode_is(hdlc, MODE_DCE) ? - LMI_STATUS : LMI_STATUS_ENQUIRY)) { - printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n", - hdlc_to_name(hdlc), skb->data[2], - mode_is(hdlc, MODE_DCE) ? "enquiry" : "reply"); - return 1; - } - - i = mode_is(hdlc, MODE_FR_ANSI) ? 7 : 6; - - if (skb->data[i] != - (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) { - printk(KERN_INFO "%s: Not a report type=%x\n", - hdlc_to_name(hdlc), skb->data[i]); - return 1; - } - i++; - - i++; /* Skip length field */ - - reptype = skb->data[i++]; - - if (skb->data[i]!= - (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE)) { - printk(KERN_INFO "%s: Unsupported status element=%x\n", - hdlc_to_name(hdlc), skb->data[i]); - return 1; - } - i++; - - i++; /* Skip length field */ - - hdlc->lmi.rxseq = skb->data[i++]; /* TX sequence from peer */ - rxseq = skb->data[i++]; /* Should confirm our sequence */ - - txseq = hdlc->lmi.txseq; - - if (mode_is(hdlc, MODE_DCE)) { - if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) { - printk(KERN_INFO "%s: Unsupported report type=%x\n", - hdlc_to_name(hdlc), reptype); - return 1; - } - } - - error = 0; - if (!(hdlc->lmi.state & LINK_STATE_RELIABLE)) - error = 1; - - if (rxseq == 0 || rxseq != txseq) { - hdlc->lmi.N391cnt = 0; /* Ask for full report next time */ - error = 1; - } - - if (mode_is(hdlc, MODE_DCE)) { - if ((hdlc->lmi.state & LINK_STATE_FULLREP_SENT) && !error) { -/* Stop sending full report - the last one has been confirmed by DTE */ - hdlc->lmi.state &= ~LINK_STATE_FULLREP_SENT; - pvc = hdlc->first_pvc; - while (pvc) { - if (pvc->state & PVC_STATE_NEW) { - pvc->state &= ~PVC_STATE_NEW; - pvc->state |= PVC_STATE_ACTIVE; - fr_log_dlci_active(pvc); - -/* Tell DTE that new PVC is now active */ - hdlc->lmi.state |= LINK_STATE_CHANGED; - } - pvc = pvc->next; - } - } - - if (hdlc->lmi.state & LINK_STATE_CHANGED) { - reptype = LMI_FULLREP; - hdlc->lmi.state |= LINK_STATE_FULLREP_SENT; - hdlc->lmi.state &= ~LINK_STATE_CHANGED; - } - - fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0); - return 0; - } - - /* DTE */ - - if (reptype != LMI_FULLREP || error) - return 0; - - stat_len = 3; - pvc = hdlc->first_pvc; - - while (pvc) { - pvc->newstate = 0; - pvc = pvc->next; - } - - while (skb->len >= i + 2 + stat_len) { - u16 dlci; - u8 state = 0; - - if (skb->data[i] != (mode_is(hdlc, MODE_FR_CCITT) ? - LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { - printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n", - hdlc_to_name(hdlc), skb->data[i]); - return 1; - } - i++; - - if (skb->data[i] != stat_len) { - printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n", - hdlc_to_name(hdlc), skb->data[i]); - return 1; - } - i++; - - dlci = status_to_dlci(hdlc, skb->data+i, &state); - pvc = find_pvc(hdlc, dlci); - - if (pvc) - pvc->newstate = state; - else if (state == PVC_STATE_NEW) - printk(KERN_INFO "%s: new PVC available, DLCI=%u\n", - hdlc_to_name(hdlc), dlci); - - i += stat_len; - } - - pvc = hdlc->first_pvc; - - while (pvc) { - if (pvc->newstate == PVC_STATE_NEW) - pvc->newstate = PVC_STATE_ACTIVE; - - pvc->newstate |= (pvc->state & - ~(PVC_STATE_NEW|PVC_STATE_ACTIVE)); - if (pvc->state != pvc->newstate) { - pvc->state = pvc->newstate; - fr_log_dlci_active(pvc); - } - pvc = pvc->next; - } - - /* Next full report after N391 polls */ - hdlc->lmi.N391cnt = hdlc->lmi.N391; - - return 0; -} - - - -static void fr_netif(hdlc_device *hdlc, struct sk_buff *skb) -{ - fr_hdr *fh = (fr_hdr*)skb->data; - u8 *data = skb->data; - u16 dlci; - pvc_device *pvc; - - if (skb->len<4 || fh->ea1 || data[2] != FR_UI) - goto rx_error; - - dlci = q922_to_dlci(skb->data); - - if (dlci == LMI_DLCI) { - if (data[3] == LMI_PROTO) { - if (fr_lmi_recv(hdlc, skb)) - goto rx_error; - else { - /* No request pending */ - hdlc->lmi.state &= ~LINK_STATE_REQUEST; - hdlc->lmi.last_poll = jiffies; - dev_kfree_skb_any(skb); - return; - } - } - - printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n", - hdlc_to_name(hdlc)); - goto rx_error; - } - - pvc = find_pvc(hdlc, dlci); - if (!pvc) { -#ifdef DEBUG_PKT - 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 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; - } - - pvc->stats.rx_packets++; /* PVC traffic */ - pvc->stats.rx_bytes += skb->len; - - if ((pvc->state & PVC_STATE_FECN) != (fh->fecn ? PVC_STATE_FECN : 0)) { -#ifdef DEBUG_FECN - printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc), - fh->fecn ? "N" : "FF"); -#endif - pvc->state ^= PVC_STATE_FECN; - } - - if ((pvc->state & PVC_STATE_BECN) != (fh->becn ? PVC_STATE_BECN : 0)) { -#ifdef DEBUG_FECN - printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc), - fh->becn ? "N" : "FF"); -#endif - pvc->state ^= PVC_STATE_BECN; - } - - if (pvc->state & PVC_STATE_BECN) - pvc->stats.rx_compressed++; - - if (data[3] == NLPID_IP) { - skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ - skb->protocol = htons(ETH_P_IP); - skb->dev = &pvc->netdev; - netif_rx(skb); - return; - } - - - if (data[3] == NLPID_IPV6) { - skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ - skb->protocol = htons(ETH_P_IPV6); - skb->dev = &pvc->netdev; - netif_rx(skb); - return; - } - - if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD && - data[6] == FR_PAD && data[7] == FR_PAD && - ((data[8]<<8) | data[9]) == ETH_P_ARP) { - skb_pull(skb, 10); - skb->protocol = htons(ETH_P_ARP); - skb->dev = &pvc->netdev; - netif_rx(skb); - return; - } - - printk(KERN_INFO "%s: Unusupported protocol %x\n", - hdlc_to_name(hdlc), data[3]); - dev_kfree_skb_any(skb); - return; - - rx_error: - hdlc->stats.rx_errors++; /* Mark error */ - dev_kfree_skb_any(skb); -} - - - -static void fr_cisco_open(hdlc_device *hdlc) -{ - hdlc->lmi.state = LINK_STATE_CHANGED; - hdlc->lmi.txseq = hdlc->lmi.rxseq = 0; - hdlc->lmi.last_errors = 0xFFFFFFFF; - hdlc->lmi.N391cnt = 0; - - init_timer(&hdlc->timer); - hdlc->timer.expires = jiffies + HZ; /* First poll after 1 second */ - hdlc->timer.function = mode_is(hdlc, MODE_FR) ? fr_timer : cisco_timer; - hdlc->timer.data = (unsigned long)hdlc; - add_timer(&hdlc->timer); -} - - - -static void fr_cisco_close(hdlc_device *hdlc) -{ - pvc_device *pvc = hdlc->first_pvc; - - del_timer_sync(&hdlc->timer); - - while(pvc) { /* NULL in Cisco mode */ - dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */ - pvc = pvc->next; - } -} - - - -/****************************************************************** - * - * generic HDLC routines - * - *****************************************************************/ - - - -static int hdlc_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - - - -/******************************************************** - * - * PVC device routines - * - *******************************************************/ - -static int pvc_open(struct net_device *dev) -{ - pvc_device *pvc = dev_to_pvc(dev); - int result = 0; - - if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0) - return -EIO; /* Master must be UP in order to activate PVC */ - - memset(&(pvc->stats), 0, sizeof(struct net_device_stats)); - pvc->state = 0; - - if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->open_pvc) - result = pvc->master->open_pvc(pvc); - if (result) - return result; - - pvc->master->lmi.state |= LINK_STATE_CHANGED; - return 0; -} - - - -static int pvc_close(struct net_device *dev) -{ - pvc_device *pvc = dev_to_pvc(dev); - pvc->state = 0; - - if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->close_pvc) - pvc->master->close_pvc(pvc); - - pvc->master->lmi.state |= LINK_STATE_CHANGED; - return 0; -} - - - -static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) -{ - pvc_device *pvc = dev_to_pvc(dev); - - if (pvc->state & PVC_STATE_ACTIVE) { - skb->dev = hdlc_to_dev(pvc->master); - pvc->stats.tx_bytes += skb->len; - pvc->stats.tx_packets++; - if (pvc->state & PVC_STATE_FECN) - pvc->stats.tx_compressed++; /* TX Congestion counter */ - dev_queue_xmit(skb); - } else { - pvc->stats.tx_dropped++; - dev_kfree_skb(skb); - } - - return 0; -} - - - -static struct net_device_stats *pvc_get_stats(struct net_device *dev) -{ - pvc_device *pvc = dev_to_pvc(dev); - return &pvc->stats; -} - - - -static int pvc_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - - - -static void destroy_pvc_list(hdlc_device *hdlc) -{ - pvc_device *pvc = hdlc->first_pvc; - while(pvc) { - pvc_device *next = pvc->next; - unregister_netdevice(&pvc->netdev); - kfree(pvc); - pvc = next; - } - - hdlc->first_pvc = NULL; /* All PVCs destroyed */ - hdlc->pvc_count = 0; - hdlc->lmi.state |= LINK_STATE_CHANGED; -} - - - -/******************************************************** - * - * X.25 protocol support routines - * - *******************************************************/ - -#ifdef CONFIG_HDLC_X25 -/* These functions are callbacks called by LAPB layer */ - -void x25_connect_disconnect(void *token, int reason, int code) -{ - hdlc_device *hdlc = token; - struct sk_buff *skb; - unsigned char *ptr; - - if ((skb = dev_alloc_skb(1)) == NULL) { - printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc)); - return; - } - - ptr = skb_put(skb, 1); - *ptr = code; - - skb->dev = hdlc_to_dev(hdlc); - skb->protocol = htons(ETH_P_X25); - skb->mac.raw = skb->data; - skb->pkt_type = PACKET_HOST; - - netif_rx(skb); -} - -void x25_connected(void *token, int reason) -{ - x25_connect_disconnect(token, reason, 1); -} - -void x25_disconnected(void *token, int reason) -{ - x25_connect_disconnect(token, reason, 2); -} - - -int x25_data_indication(void *token, struct sk_buff *skb) -{ - hdlc_device *hdlc = token; - unsigned char *ptr; - - ptr = skb_push(skb, 1); - *ptr = 0; - - skb->dev = hdlc_to_dev(hdlc); - skb->protocol = htons(ETH_P_X25); - skb->mac.raw = skb->data; - skb->pkt_type = PACKET_HOST; - - return netif_rx(skb); -} - - -void x25_data_transmit(void *token, struct sk_buff *skb) -{ - hdlc_device *hdlc = token; - hdlc->xmit(hdlc, skb); /* Ignore return value :-( */ -} -#endif /* CONFIG_HDLC_X25 */ - - -/******************************************************** - * - * HDLC device routines - * - *******************************************************/ - -static int hdlc_open(struct net_device *dev) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - int result; - - if (hdlc->mode == MODE_NONE) - return -ENOSYS; - - memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); - - if (mode_is(hdlc, MODE_FR | MODE_SOFT) || - mode_is(hdlc, MODE_CISCO | MODE_SOFT)) - fr_cisco_open(hdlc); -#ifdef CONFIG_HDLC_PPP - else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { - sppp_attach(&hdlc->pppdev); - /* sppp_attach nukes them. We don't need syncppp's ioctl */ - dev->do_ioctl = hdlc_ioctl; - hdlc->pppdev.sppp.pp_flags &= ~PP_CISCO; - dev->type = ARPHRD_PPP; - result = sppp_open(dev); - if (result) { - sppp_detach(dev); - return result; - } - } -#endif -#ifdef CONFIG_HDLC_X25 - else if (mode_is(hdlc, MODE_X25)) { - struct lapb_register_struct cb; - - cb.connect_confirmation = x25_connected; - cb.connect_indication = x25_connected; - cb.disconnect_confirmation = x25_disconnected; - cb.disconnect_indication = x25_disconnected; - cb.data_indication = x25_data_indication; - cb.data_transmit = x25_data_transmit; - - result = lapb_register(hdlc, &cb); - if (result != LAPB_OK) - return result; - } -#endif - result = hdlc->open(hdlc); - if (result) { - if (mode_is(hdlc, MODE_FR | MODE_SOFT) || - mode_is(hdlc, MODE_CISCO | MODE_SOFT)) - fr_cisco_close(hdlc); -#ifdef CONFIG_HDLC_PPP - else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { - sppp_close(dev); - sppp_detach(dev); - dev->rebuild_header = NULL; - dev->change_mtu = hdlc_change_mtu; - dev->mtu = HDLC_MAX_MTU; - dev->hard_header_len = 16; - } -#endif -#ifdef CONFIG_HDLC_X25 - else if (mode_is(hdlc, MODE_X25)) - lapb_unregister(hdlc); -#endif - } - - return result; -} - - - -static int hdlc_close(struct net_device *dev) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - - hdlc->close(hdlc); - - if (mode_is(hdlc, MODE_FR | MODE_SOFT) || - mode_is(hdlc, MODE_CISCO | MODE_SOFT)) - fr_cisco_close(hdlc); -#ifdef CONFIG_HDLC_PPP - else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { - sppp_close(dev); - sppp_detach(dev); - dev->rebuild_header = NULL; - dev->change_mtu = hdlc_change_mtu; - dev->mtu = HDLC_MAX_MTU; - dev->hard_header_len = 16; - } -#endif -#ifdef CONFIG_HDLC_X25 - else if (mode_is(hdlc, MODE_X25)) - lapb_unregister(hdlc); -#endif - return 0; -} - - - -static int hdlc_xmit(struct sk_buff *skb, struct net_device *dev) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - -#ifdef CONFIG_HDLC_X25 - if (mode_is(hdlc, MODE_X25 | MODE_SOFT)) { - int result; - - - /* X.25 to LAPB */ - switch (skb->data[0]) { - case 0: /* Data to be transmitted */ - skb_pull(skb, 1); - if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK) - dev_kfree_skb(skb); - return 0; - - case 1: - if ((result = lapb_connect_request(hdlc))!= LAPB_OK) { - if (result == LAPB_CONNECTED) { - /* Send connect confirm. msg to level 3 */ - x25_connected(hdlc, 0); - } else { - printk(KERN_ERR "%s: LAPB connect " - "request failed, error code = " - "%i\n", hdlc_to_name(hdlc), - result); - } - } - break; - - case 2: - if ((result=lapb_disconnect_request(hdlc))!=LAPB_OK) { - if (result == LAPB_NOTCONNECTED) { - /* Send disconnect confirm. msg to level 3 */ - x25_disconnected(hdlc, 0); - } else { - printk(KERN_ERR "%s: LAPB disconnect " - "request failed, error code = " - "%i\n", hdlc_to_name(hdlc), - result); - } - } - break; - - default: - /* to be defined */ - break; - } - - dev_kfree_skb(skb); - return 0; - } /* MODE_X25 */ -#endif /* CONFIG_HDLC_X25 */ - - return hdlc->xmit(hdlc, skb); -} - - - -void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb) -{ -/* skb contains raw HDLC frame, in both hard- and software modes */ - skb->mac.raw = skb->data; - - switch(hdlc->mode & MODE_MASK) { - case MODE_HDLC: - skb->protocol = htons(ETH_P_IP); - skb->dev = hdlc_to_dev(hdlc); - netif_rx(skb); - return; - - case MODE_FR: - fr_netif(hdlc, skb); - return; - - case MODE_CISCO: - cisco_netif(hdlc, skb); - return; - -#ifdef CONFIG_HDLC_PPP - case MODE_PPP: -#if 0 - sppp_input(hdlc_to_dev(hdlc), skb); -#else - skb->protocol = htons(ETH_P_WAN_PPP); - skb->dev = hdlc_to_dev(hdlc); - netif_rx(skb); -#endif - return; -#endif -#ifdef CONFIG_HDLC_X25 - case MODE_X25: - skb->dev = hdlc_to_dev(hdlc); - if (lapb_data_received(hdlc, skb) == LAPB_OK) - return; - break; -#endif - } - - hdlc->stats.rx_errors++; - dev_kfree_skb_any(skb); -} - - - -static struct net_device_stats *hdlc_get_stats(struct net_device *dev) -{ - return &dev_to_hdlc(dev)->stats; -} - - - -static int hdlc_set_mode(hdlc_device *hdlc, int mode) -{ - int result = -1; /* Default to soft modes */ - struct net_device *dev = hdlc_to_dev(hdlc); - - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - - if(dev->flags & IFF_UP) - return -EBUSY; - - dev->addr_len = 0; - dev->hard_header = NULL; - hdlc->mode = MODE_NONE; - - if (!(mode & MODE_SOFT)) - switch(mode & MODE_MASK) { - case MODE_HDLC: - result = hdlc->set_mode ? - hdlc->set_mode(hdlc, MODE_HDLC) : 0; - break; - - case MODE_CISCO: /* By card */ -#ifdef CONFIG_HDLC_PPP - case MODE_PPP: -#endif -#ifdef CONFIG_HDLC_X25 - case MODE_X25: -#endif - case MODE_FR: - result = hdlc->set_mode ? - hdlc->set_mode(hdlc, mode) : -ENOSYS; - break; - - default: - return -EINVAL; - } - - if (result) { - mode |= MODE_SOFT; /* Try "host software" protocol */ - - switch(mode & MODE_MASK) { - case MODE_CISCO: - dev->hard_header = cisco_hard_header; - break; - -#ifdef CONFIG_HDLC_PPP - case MODE_PPP: - break; -#endif -#ifdef CONFIG_HDLC_X25 - case MODE_X25: - break; -#endif - - case MODE_FR: - dev->hard_header = fr_hard_header; - dev->addr_len = 2; - *(u16*)dev->dev_addr = htons(LMI_DLCI); - dlci_to_q922(dev->broadcast, LMI_DLCI); - break; - - default: - return -EINVAL; - } - - result = hdlc->set_mode ? - hdlc->set_mode(hdlc, MODE_HDLC) : 0; - } - - if (result) - return result; - - hdlc->mode = mode; - switch(mode & MODE_MASK) { -#ifdef CONFIG_HDLC_PPP - case MODE_PPP: dev->type = ARPHRD_PPP; break; -#endif -#ifdef CONFIG_HDLC_X25 - case MODE_X25: dev->type = ARPHRD_X25; break; -#endif - case MODE_FR: dev->type = ARPHRD_FRAD; break; - case MODE_CISCO: dev->type = ARPHRD_CISCO; break; - default: dev->type = ARPHRD_RAWHDLC; - } - - memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); - destroy_pvc_list(hdlc); - return 0; -} - - - -static int hdlc_fr_pvc(hdlc_device *hdlc, int dlci) -{ - pvc_device **pvc_p = &hdlc->first_pvc; - pvc_device *pvc; - int result, create = 1; /* Create or delete PVC */ - - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - - if(dlci<0) { - dlci = -dlci; - create = 0; - } - - if(dlci <= 0 || dlci >= 1024) - return -EINVAL; /* Only 10 bits for DLCI, DLCI=0 is reserved */ - - if(!mode_is(hdlc, MODE_FR)) - return -EINVAL; /* Only meaningfull on FR */ - - while(*pvc_p) { - if (netdev_dlci(&(*pvc_p)->netdev) == dlci) - break; - pvc_p = &(*pvc_p)->next; - } - - 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 " - "hdlc_fr_pvc()\n", hdlc_to_name(hdlc)); - return -ENOBUFS; - } - memset(pvc, 0, sizeof(pvc_device)); - - 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; - pvc->netdev.irq = hdlc_to_dev(hdlc)->irq; - - 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; - } - - if (!mode_is(hdlc, MODE_SOFT) && hdlc->create_pvc) { - result = hdlc->create_pvc(pvc); - if (result) { - unregister_netdevice(&pvc->netdev); - kfree(pvc); - *pvc_p = NULL; - return result; - } - } - - hdlc->lmi.state |= LINK_STATE_CHANGED; - hdlc->pvc_count++; - return 0; - } - - if (*pvc_p == NULL) /* Delete PVC */ - return -ENOENT; - - pvc = *pvc_p; - - if (pvc->netdev.flags & IFF_UP) - return -EBUSY; /* PVC in use */ - - if (!mode_is(hdlc, MODE_SOFT) && hdlc->destroy_pvc) - hdlc->destroy_pvc(pvc); - - hdlc->lmi.state |= LINK_STATE_CHANGED; - hdlc->pvc_count--; - *pvc_p = pvc->next; - unregister_netdevice(&pvc->netdev); - kfree(pvc); - return 0; -} - - - -static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - - switch(cmd) { - case HDLCGMODE: - ifr->ifr_ifru.ifru_ivalue = hdlc->mode; - return 0; - - case HDLCSMODE: - return hdlc_set_mode(hdlc, ifr->ifr_ifru.ifru_ivalue); - - case HDLCPVC: - return hdlc_fr_pvc(hdlc, ifr->ifr_ifru.ifru_ivalue); - - default: - if (hdlc->ioctl != NULL) - return hdlc->ioctl(hdlc, ifr, cmd); - } - - return -EINVAL; -} - - - -static int hdlc_init(struct net_device *dev) -{ - hdlc_device *hdlc = dev_to_hdlc(dev); - - memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); - - dev->get_stats = hdlc_get_stats; - dev->open = hdlc_open; - dev->stop = hdlc_close; - dev->hard_start_xmit = hdlc_xmit; - dev->do_ioctl = hdlc_ioctl; - dev->change_mtu = hdlc_change_mtu; - dev->mtu = HDLC_MAX_MTU; - - dev->type = ARPHRD_RAWHDLC; - dev->hard_header_len = 16; - - dev->flags = IFF_POINTOPOINT | IFF_NOARP; - - return 0; -} - - - -int register_hdlc_device(hdlc_device *hdlc) -{ - int result; - struct net_device *dev = hdlc_to_dev(hdlc); - - dev->init = hdlc_init; - dev->priv = &hdlc->syncppp_ptr; - hdlc->syncppp_ptr = &hdlc->pppdev; - hdlc->pppdev.dev = dev; - hdlc->mode = MODE_NONE; - hdlc->lmi.T391 = 10; /* polling verification timer */ - hdlc->lmi.T392 = 15; /* link integrity verification polling timer */ - hdlc->lmi.N391 = 6; /* full status polling counter */ - hdlc->lmi.N392 = 3; /* error threshold */ - hdlc->lmi.N393 = 4; /* monitored events count */ - - result = dev_alloc_name(dev, "hdlc%d"); - if (result<0) - return result; - - result = register_netdev(dev); - if (result != 0) - return -EIO; - - MOD_INC_USE_COUNT; - return 0; -} - - - -void unregister_hdlc_device(hdlc_device *hdlc) -{ - destroy_pvc_list(hdlc); - unregister_netdev(hdlc_to_dev(hdlc)); - MOD_DEC_USE_COUNT; -} - -MODULE_AUTHOR("Krzysztof Halasa "); -MODULE_DESCRIPTION("HDLC support module"); -MODULE_LICENSE("GPL"); - -EXPORT_SYMBOL(hdlc_netif_rx); -EXPORT_SYMBOL(register_hdlc_device); -EXPORT_SYMBOL(unregister_hdlc_device); - -static int __init hdlc_module_init(void) -{ - printk(KERN_INFO "%s\n", version); - return 0; -} - - -module_init(hdlc_module_init); --- linux-2.4.20/drivers/net/wan/hdlc_cisco.c 2003-04-07 17:14:17.000000000 +0200 +++ linux-2.4/drivers/net/wan/hdlc_cisco.c 2003-04-07 00:25:09.000000000 +0200 @@ -0,0 +1,318 @@ +/* + * Generic HDLC support routines for Linux + * Cisco HDLC support + * + * Copyright (C) 2000 - 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 + + +#define CISCO_MULTICAST 0x8F /* Cisco multicast address */ +#define CISCO_UNICAST 0x0F /* Cisco unicast address */ +#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */ +#define CISCO_SYS_INFO 0x2000 /* Cisco interface/system info */ +#define CISCO_ADDR_REQ 0 /* Cisco address request */ +#define CISCO_ADDR_REPLY 1 /* Cisco address reply */ +#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */ + + +static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev, + u16 type, void *daddr, void *saddr, + unsigned int len) +{ + hdlc_header *data; +#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER + printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name); +#endif + + skb_push(skb, sizeof(hdlc_header)); + data = (hdlc_header*)skb->data; + if (type == CISCO_KEEPALIVE) + data->address = CISCO_MULTICAST; + else + data->address = CISCO_UNICAST; + data->control = 0; + data->protocol = htons(type); + + return sizeof(hdlc_header); +} + + + +static void cisco_keepalive_send(hdlc_device *hdlc, u32 type, + u32 par1, u32 par2) +{ + struct sk_buff *skb; + cisco_packet *data; + + skb = dev_alloc_skb(sizeof(hdlc_header) + sizeof(cisco_packet)); + if (!skb) { + printk(KERN_WARNING + "%s: Memory squeeze on cisco_keepalive_send()\n", + hdlc_to_name(hdlc)); + return; + } + skb_reserve(skb, 4); + cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE, + NULL, NULL, 0); + data = (cisco_packet*)skb->tail; + + data->type = htonl(type); + data->par1 = htonl(par1); + data->par2 = htonl(par2); + data->rel = 0xFFFF; + /* 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); + hdlc_header *data = (hdlc_header*)skb->data; + cisco_packet *cisco_data; + struct in_device *in_dev; + u32 addr, mask; + + if (skb->len < sizeof(hdlc_header)) + goto rx_error; + + if (data->address != CISCO_MULTICAST && + data->address != CISCO_UNICAST) + goto rx_error; + + skb_pull(skb, sizeof(hdlc_header)); + + switch(ntohs(data->protocol)) { + case CISCO_SYS_INFO: + /* Packet is not needed, drop it. */ + dev_kfree_skb_any(skb); + return; + + case CISCO_KEEPALIVE: + if (skb->len != CISCO_PACKET_LEN && + skb->len != CISCO_BIG_PACKET_LEN) { + printk(KERN_INFO "%s: Invalid length of Cisco " + "control packet (%d bytes)\n", + hdlc_to_name(hdlc), skb->len); + goto rx_error; + } + + cisco_data = (cisco_packet*)skb->data; + + switch(ntohl (cisco_data->type)) { + case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */ + in_dev = hdlc_to_dev(hdlc)->ip_ptr; + addr = 0; + mask = ~0; /* is the mask correct? */ + + if (in_dev != NULL) { + struct in_ifaddr **ifap = &in_dev->ifa_list; + + while (*ifap != NULL) { + if (strcmp(hdlc_to_name(hdlc), + (*ifap)->ifa_label) == 0) { + addr = (*ifap)->ifa_local; + mask = (*ifap)->ifa_mask; + break; + } + ifap = &(*ifap)->ifa_next; + } + + cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY, + addr, mask); + } + dev_kfree_skb_any(skb); + return; + + case CISCO_ADDR_REPLY: + printk(KERN_INFO "%s: Unexpected Cisco IP address " + "reply\n", hdlc_to_name(hdlc)); + goto rx_error; + + case CISCO_KEEPALIVE_REQ: + hdlc->state.cisco.rxseq = ntohl(cisco_data->par1); + if (ntohl(cisco_data->par2) == hdlc->state.cisco.txseq) { + hdlc->state.cisco.last_poll = jiffies; + if (!hdlc->state.cisco.up) { + u32 sec, min, hrs, days; + sec = ntohl(cisco_data->time) / 1000; + min = sec / 60; sec -= min * 60; + hrs = min / 60; min -= hrs * 60; + days = hrs / 24; hrs -= days * 24; + printk(KERN_INFO "%s: Link up (peer " + "uptime %ud%uh%um%us)\n", + hdlc_to_name(hdlc), days, hrs, + min, sec); + } + hdlc->state.cisco.up = 1; + } + + dev_kfree_skb_any(skb); + return; + } /* switch(keepalive type) */ + } /* switch(protocol) */ + + printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc), + data->protocol); + dev_kfree_skb_any(skb); + return; + + rx_error: + hdlc->stats.rx_errors++; /* Mark error */ + dev_kfree_skb_any(skb); +} + + + +static void cisco_timer(unsigned long arg) +{ + hdlc_device *hdlc = (hdlc_device*)arg; + + if (hdlc->state.cisco.up && + jiffies - hdlc->state.cisco.last_poll >= + hdlc->state.cisco.settings.timeout * HZ) { + hdlc->state.cisco.up = 0; + printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc)); + } + + cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ, + ++hdlc->state.cisco.txseq, + hdlc->state.cisco.rxseq); + hdlc->state.cisco.timer.expires = jiffies + + hdlc->state.cisco.settings.interval * HZ; + hdlc->state.cisco.timer.function = cisco_timer; + hdlc->state.cisco.timer.data = arg; + add_timer(&hdlc->state.cisco.timer); +} + + + +static int cisco_open(hdlc_device *hdlc) +{ + hdlc->state.cisco.last_poll = 0; + hdlc->state.cisco.up = 0; + hdlc->state.cisco.txseq = hdlc->state.cisco.rxseq = 0; + + init_timer(&hdlc->state.cisco.timer); + hdlc->state.cisco.timer.expires = jiffies + HZ; /*First poll after 1s*/ + hdlc->state.cisco.timer.function = cisco_timer; + hdlc->state.cisco.timer.data = (unsigned long)hdlc; + add_timer(&hdlc->state.cisco.timer); + return 0; +} + + + +static void cisco_close(hdlc_device *hdlc) +{ + del_timer_sync(&hdlc->state.cisco.timer); +} + + + +int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + cisco_proto *cisco_s = ifr->ifr_settings.ifs_ifsu.cisco; + const size_t size = sizeof(cisco_proto); + cisco_proto new_settings; + struct net_device *dev = hdlc_to_dev(hdlc); + int result; + + switch (ifr->ifr_settings.type) { + case IF_GET_PROTO: + ifr->ifr_settings.type = IF_PROTO_CISCO; + if (ifr->ifr_settings.size < size) { + ifr->ifr_settings.size = size; /* data size wanted */ + return -ENOBUFS; + } + if (copy_to_user(cisco_s, &hdlc->state.cisco.settings, size)) + return -EFAULT; + return 0; + + case IF_PROTO_CISCO: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + if (copy_from_user(&new_settings, cisco_s, size)) + return -EFAULT; + + if (new_settings.interval < 1 || + new_settings.timeout < 2) + return -EINVAL; + + result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); + + if (result) + return result; + + hdlc_proto_detach(hdlc); + memcpy(&hdlc->state.cisco.settings, &new_settings, size); + + 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; + dev->type = ARPHRD_CISCO; + dev->addr_len = 0; + return 0; + } + + return -EINVAL; +} --- linux-2.4.20/drivers/net/wan/hdlc_fr.c 2003-04-07 17:14:17.000000000 +0200 +++ linux-2.4/drivers/net/wan/hdlc_fr.c 2003-04-07 00:25:09.000000000 +0200 @@ -0,0 +1,1063 @@ +/* + * Generic HDLC support routines for Linux + * Frame Relay support + * + * Copyright (C) 1999 - 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. + * + + 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 +#include +#include +#include +#include +#include +#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 (pvc->dlci == dlci) + return pvc; + if (pvc->dlci > dlci) + return NULL; /* the listed is sorted */ + pvc = pvc->next; + } + + return NULL; +} + + +__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__ struct net_device** get_dev_p(pvc_device *pvc, int type) +{ + 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(u16 dlci, u8 *status, + int active, int new) +{ + status[0] = (dlci>>4) & 0x3F; + status[1] = ((dlci<<3) & 0x78) | 0x80; + status[2] = 0x80; + + if (new) + status[2] |= 0x08; + else if (active) + status[2] |= 0x02; +} + + + +static int fr_hard_header(struct sk_buff **skb_p, u16 dlci) +{ + u16 head_len; + struct sk_buff *skb = *skb_p; + + switch(skb->protocol) { + case __constant_ntohs(ETH_P_IP): + head_len = 4; + skb_push(skb, head_len); + skb->data[3] = NLPID_IP; + break; + + case __constant_ntohs(ETH_P_IPV6): + head_len = 4; + skb_push(skb, head_len); + skb->data[3] = NLPID_IPV6; + break; + + 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); + skb->data[3] = FR_PAD; + skb->data[4] = NLPID_SNAP; + skb->data[5] = FR_PAD; + skb->data[6] = FR_PAD; + skb->data[7] = FR_PAD; + *(u16*)(skb->data + 8) = skb->protocol; + } + + dlci_to_q922(skb->data, dlci); + skb->data[2] = FR_UI; + return 0; +} + + + +static int pvc_open(struct net_device *dev) +{ + pvc_device *pvc = dev_to_pvc(dev); + + if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0) + return -EIO; /* Master must be UP in order to activate PVC */ + + if (pvc->open_count++ == 0) { + if (pvc->master->state.fr.settings.lmi == LMI_NONE) + pvc->state.active = 1; + + pvc->master->state.fr.dce_changed = 1; + } + return 0; +} + + + +static int pvc_close(struct net_device *dev) +{ + pvc_device *pvc = dev_to_pvc(dev); + + 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; +} + + + +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 (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 -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 int pvc_xmit(struct sk_buff *skb, struct net_device *dev) +{ + pvc_device *pvc = dev_to_pvc(dev); + 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; +} + + + +static int pvc_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + + + +static inline void fr_log_dlci_active(pvc_device *pvc) +{ + 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"); +} + + + +static inline u8 fr_lmi_nextseq(u8 x) +{ + x++; + return x ? x : 1; +} + + + +static void fr_lmi_send(hdlc_device *hdlc, int fullrep) +{ + struct sk_buff *skb; + pvc_device *pvc = hdlc->state.fr.first_pvc; + int len = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? LMI_ANSI_LENGTH + : LMI_LENGTH; + int stat_len = 3; + u8 *data; + int i = 0; + + if (hdlc->state.fr.settings.dce && fullrep) { + 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; + } + } + + skb = dev_alloc_skb(len); + if (!skb) { + printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n", + hdlc_to_name(hdlc)); + return; + } + memset(skb->data, 0, len); + skb_reserve(skb, 4); + 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 + ? LMI_STATUS : LMI_STATUS_ENQUIRY; + if (hdlc->state.fr.settings.lmi == LMI_ANSI) + data[i++] = LMI_ANSI_LOCKSHIFT; + data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_REPTYPE : LMI_REPTYPE; + data[i++] = LMI_REPT_LEN; + data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY; + + data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_ALIVE : LMI_ALIVE; + data[i++] = LMI_INTEG_LEN; + data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq); + data[i++] = hdlc->state.fr.rxseq; + + if (hdlc->state.fr.settings.dce && fullrep) { + while (pvc) { + data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT; + data[i++] = stat_len; + + /* 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(pvc->dlci, data + i, + pvc->state.active, pvc->state.new); + i += stat_len; + pvc = pvc->next; + } + } + + skb_put(skb, i); + skb->priority = TC_PRIO_CONTROL; + skb->dev = hdlc_to_dev(hdlc); + skb->nh.raw = skb->data; + + dev_queue_xmit(skb); +} + + + +static void fr_timer(unsigned long arg) +{ + hdlc_device *hdlc = (hdlc_device*)arg; + int i, cnt = 0, reliable; + u32 list; + + if (hdlc->state.fr.settings.dce) + reliable = (jiffies - hdlc->state.fr.last_poll < + hdlc->state.fr.settings.t392 * HZ); + else { + hdlc->state.fr.last_errors <<= 1; /* Shift the list */ + if (hdlc->state.fr.request) { + if (hdlc->state.fr.reliable) + printk(KERN_INFO "%s: No LMI status reply " + "received\n", hdlc_to_name(hdlc)); + hdlc->state.fr.last_errors |= 1; + } + + list = hdlc->state.fr.last_errors; + for (i = 0; i < hdlc->state.fr.settings.n393; i++, list >>= 1) + cnt += (list & 1); /* errors count */ + + reliable = (cnt < hdlc->state.fr.settings.n392); + } + + if (hdlc->state.fr.reliable != reliable) { + pvc_device *pvc = hdlc->state.fr.first_pvc; + + hdlc->state.fr.reliable = reliable; + printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc), + reliable ? "" : "un"); + + if (reliable) { + hdlc->state.fr.n391cnt = 0; /* Request full status */ + hdlc->state.fr.dce_changed = 1; + } else { + while (pvc) { /* Deactivate all PVCs */ + pvc->state.exist = 0; + pvc->state.active = pvc->state.new = 0; + pvc = pvc->next; + } + } + } + + if (hdlc->state.fr.settings.dce) + hdlc->state.fr.timer.expires = jiffies + + hdlc->state.fr.settings.t392 * HZ; + else { + if (hdlc->state.fr.n391cnt) + hdlc->state.fr.n391cnt--; + + fr_lmi_send(hdlc, hdlc->state.fr.n391cnt == 0); + + hdlc->state.fr.request = 1; + hdlc->state.fr.timer.expires = jiffies + + hdlc->state.fr.settings.t391 * HZ; + } + + hdlc->state.fr.timer.function = fr_timer; + hdlc->state.fr.timer.data = arg; + add_timer(&hdlc->state.fr.timer); +} + + + +static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb) +{ + int stat_len; + pvc_device *pvc; + int reptype = -1, error, no_ram; + u8 rxseq, txseq; + int i; + + if (skb->len < ((hdlc->state.fr.settings.lmi == LMI_ANSI) + ? LMI_ANSI_LENGTH : LMI_LENGTH)) { + printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc)); + return 1; + } + + if (skb->data[5] != (!hdlc->state.fr.settings.dce ? + LMI_STATUS : LMI_STATUS_ENQUIRY)) { + printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n", + hdlc_to_name(hdlc), skb->data[2], + hdlc->state.fr.settings.dce ? "enquiry" : "reply"); + return 1; + } + + i = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? 7 : 6; + + if (skb->data[i] != + ((hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) { + printk(KERN_INFO "%s: Not a report type=%x\n", + hdlc_to_name(hdlc), skb->data[i]); + return 1; + } + i++; + + i++; /* Skip length field */ + + reptype = skb->data[i++]; + + if (skb->data[i]!= + ((hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_ALIVE : LMI_ALIVE)) { + printk(KERN_INFO "%s: Unsupported status element=%x\n", + hdlc_to_name(hdlc), skb->data[i]); + return 1; + } + i++; + + i++; /* Skip length field */ + + hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */ + rxseq = skb->data[i++]; /* Should confirm our sequence */ + + txseq = hdlc->state.fr.txseq; + + if (hdlc->state.fr.settings.dce) { + if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) { + printk(KERN_INFO "%s: Unsupported report type=%x\n", + hdlc_to_name(hdlc), reptype); + return 1; + } + } + + error = 0; + if (!hdlc->state.fr.reliable) + error = 1; + + if (rxseq == 0 || rxseq != txseq) { + hdlc->state.fr.n391cnt = 0; /* Ask for full report next time */ + error = 1; + } + + if (hdlc->state.fr.settings.dce) { + if (hdlc->state.fr.fullrep_sent && !error) { +/* Stop sending full report - the last one has been confirmed by DTE */ + hdlc->state.fr.fullrep_sent = 0; + pvc = hdlc->state.fr.first_pvc; + while (pvc) { + if (pvc->state.new) { + pvc->state.new = 0; + +/* Tell DTE that new PVC is now active */ + hdlc->state.fr.dce_changed = 1; + } + pvc = pvc->next; + } + } + + if (hdlc->state.fr.dce_changed) { + reptype = LMI_FULLREP; + hdlc->state.fr.fullrep_sent = 1; + hdlc->state.fr.dce_changed = 0; + } + + fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0); + return 0; + } + + /* DTE */ + + if (reptype != LMI_FULLREP || error) + return 0; + + stat_len = 3; + pvc = hdlc->state.fr.first_pvc; + + while (pvc) { + pvc->state.deleted = 1; + pvc = pvc->next; + } + + no_ram = 0; + while (skb->len >= i + 2 + stat_len) { + u16 dlci; + unsigned int active, new; + + if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT) + ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { + printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n", + hdlc_to_name(hdlc), skb->data[i]); + return 1; + } + i++; + + if (skb->data[i] != stat_len) { + printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n", + hdlc_to_name(hdlc), skb->data[i]); + return 1; + } + i++; + + 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; + } + + if (pvc) { + 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); + } + } + + i += stat_len; + } + + pvc = hdlc->state.fr.first_pvc; + + while (pvc) { + if (pvc->state.deleted && pvc->state.exist) { + pvc->state.active = pvc->state.new = 0; + pvc->state.exist = 0; + fr_log_dlci_active(pvc); + } + pvc = pvc->next; + } + + /* Next full report after N391 polls */ + hdlc->state.fr.n391cnt = hdlc->state.fr.settings.n391; + + return 0; +} + + + +static void fr_rx(struct sk_buff *skb) +{ + hdlc_device *hdlc = dev_to_hdlc(skb->dev); + fr_hdr *fh = (fr_hdr*)skb->data; + u8 *data = skb->data; + u16 dlci; + pvc_device *pvc; + struct net_device *dev = NULL; + + if (skb->len <= 4 || fh->ea1 || data[2] != FR_UI) + goto rx_error; + + dlci = q922_to_dlci(skb->data); + + if (dlci == LMI_DLCI) { + if (hdlc->state.fr.settings.lmi == LMI_NONE) + goto rx_error; /* LMI packet with no LMI? */ + + if (data[3] == LMI_PROTO) { + if (fr_lmi_recv(hdlc, skb)) + goto rx_error; + else { + /* No request pending */ + hdlc->state.fr.request = 0; + hdlc->state.fr.last_poll = jiffies; + dev_kfree_skb_any(skb); + return; + } + } + + printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n", + hdlc_to_name(hdlc)); + goto rx_error; + } + + pvc = find_pvc(hdlc, dlci); + if (!pvc) { +#ifdef CONFIG_HDLC_DEBUG_PKT + printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n", + hdlc_to_name(hdlc), dlci); +#endif + dev_kfree_skb_any(skb); + return; + } + + if (pvc->state.fecn != fh->fecn) { +#ifdef CONFIG_HDLC_DEBUG_ECN + 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) { +#ifdef CONFIG_HDLC_DEBUG_ECN + 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 (data[3] == NLPID_IP) { + skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ + dev = pvc->main; + skb->protocol = htons(ETH_P_IP); + + } 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); + + } 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); + + switch ((((u32)oui) << 16) | pid) { + case ETH_P_ARP: /* routed frame with SNAP */ + 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; + } + } 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; + } + + 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: + hdlc->stats.rx_errors++; /* Mark error */ + dev_kfree_skb_any(skb); +} + + + +static int fr_open(hdlc_device *hdlc) +{ + if (hdlc->state.fr.settings.lmi != LMI_NONE) { + hdlc->state.fr.last_poll = 0; + hdlc->state.fr.reliable = 0; + hdlc->state.fr.dce_changed = 1; + hdlc->state.fr.request = 0; + hdlc->state.fr.fullrep_sent = 0; + hdlc->state.fr.last_errors = 0xFFFFFFFF; + hdlc->state.fr.n391cnt = 0; + hdlc->state.fr.txseq = hdlc->state.fr.rxseq = 0; + + init_timer(&hdlc->state.fr.timer); + /* First poll after 1 s */ + hdlc->state.fr.timer.expires = jiffies + HZ; + hdlc->state.fr.timer.function = fr_timer; + hdlc->state.fr.timer.data = (unsigned long)hdlc; + add_timer(&hdlc->state.fr.timer); + } else + hdlc->state.fr.reliable = 1; + + return 0; +} + + + +static void fr_close(hdlc_device *hdlc) +{ + pvc_device *pvc = hdlc->state.fr.first_pvc; + + if (hdlc->state.fr.settings.lmi != LMI_NONE) + del_timer_sync(&hdlc->state.fr.timer); + + 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_add_pvc(hdlc_device *hdlc, unsigned int dlci, int type) +{ + pvc_device *pvc = NULL; + struct net_device *dev; + int result, used; + char * prefix = "pvc%d"; + + if (type == ARPHRD_ETHER) + prefix = "pvceth%d"; + + 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 (*get_dev_p(pvc, type)) + return -EEXIST; + + used = pvc_is_used(pvc); + + 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)); + + 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; +} + + + +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; + + if ((dev = *get_dev_p(pvc, type)) == NULL) + return -ENOENT; + + if (dev->flags & IFF_UP) + return -EBUSY; /* PVC in use */ + + 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; +} + + + +static void fr_destroy(hdlc_device *hdlc) +{ + pvc_device *pvc = hdlc->state.fr.first_pvc; + while(pvc) { + pvc_device *next = pvc->next; + 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.dce_pvc_count = 0; + hdlc->state.fr.dce_changed = 1; +} + + + +int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + fr_proto *fr_s = ifr->ifr_settings.ifs_ifsu.fr; + const size_t size = sizeof(fr_proto); + fr_proto new_settings; + struct net_device *dev = hdlc_to_dev(hdlc); + fr_proto_pvc pvc; + int result; + + switch (ifr->ifr_settings.type) { + case IF_GET_PROTO: + ifr->ifr_settings.type = IF_PROTO_FR; + if (ifr->ifr_settings.size < size) { + ifr->ifr_settings.size = size; /* data size wanted */ + return -ENOBUFS; + } + if (copy_to_user(fr_s, &hdlc->state.fr.settings, size)) + return -EFAULT; + return 0; + + case IF_PROTO_FR: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + if (copy_from_user(&new_settings, fr_s, size)) + return -EFAULT; + + if (new_settings.lmi == LMI_DEFAULT) + new_settings.lmi = LMI_ANSI; + + if ((new_settings.lmi != LMI_NONE && + new_settings.lmi != LMI_ANSI && + new_settings.lmi != LMI_CCITT) || + new_settings.t391 < 1 || + new_settings.t392 < 2 || + new_settings.n391 < 1 || + new_settings.n392 < 1 || + new_settings.n393 < new_settings.n392 || + new_settings.n393 > 32 || + (new_settings.dce != 0 && + new_settings.dce != 1)) + return -EINVAL; + + result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); + if (result) + return result; + + if (hdlc->proto != IF_PROTO_FR) { + hdlc_proto_detach(hdlc); + hdlc->state.fr.first_pvc = NULL; + 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 = NULL; + dev->type = ARPHRD_FRAD; + 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; + + if (copy_from_user(&pvc, ifr->ifr_settings.ifs_ifsu.fr_pvc, + sizeof(fr_proto_pvc))) + return -EFAULT; + + 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.20/drivers/net/wan/hdlc_generic.c 2003-04-07 17:14:17.000000000 +0200 +++ linux-2.4/drivers/net/wan/hdlc_generic.c 2003-04-07 15:06:13.000000000 +0200 @@ -0,0 +1,206 @@ +/* + * Generic HDLC support routines for Linux + * + * Copyright (C) 1999 - 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. + * + * Currently supported: + * * raw IP-in-HDLC + * * Cisco HDLC + * * Frame Relay with ANSI or CCITT LMI (both user and network side) + * * PPP + * * X.25 + * + * Use sethdlc utility to set line parameters, protocol and PVCs + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static const char* version = "HDLC support module revision 1.14"; + + +static int hdlc_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + + + +static struct net_device_stats *hdlc_get_stats(struct net_device *dev) +{ + return &dev_to_hdlc(dev)->stats; +} + + + +static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *p) +{ + 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; +} + + +#ifndef CONFIG_HDLC_RAW +#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 + +#ifndef CONFIG_HDLC_CISCO +#define hdlc_cisco_ioctl(hdlc, ifr) -ENOSYS +#endif + +#ifndef CONFIG_HDLC_FR +#define hdlc_fr_ioctl(hdlc, ifr) -ENOSYS +#endif + +#ifndef CONFIG_HDLC_X25 +#define hdlc_x25_ioctl(hdlc, ifr) -ENOSYS +#endif + + +int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + unsigned int proto; + + if (cmd != SIOCWANDEV) + return -EINVAL; + + 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: + case IF_PROTO_X25: + proto = ifr->ifr_settings.type; + break; + + default: + proto = hdlc->proto; + } + + 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); + case IF_PROTO_X25: return hdlc_x25_ioctl(hdlc, ifr); + default: return -EINVAL; + } +} + + + +int register_hdlc_device(hdlc_device *hdlc) +{ + int result; + struct net_device *dev = hdlc_to_dev(hdlc); + + dev->get_stats = hdlc_get_stats; + dev->change_mtu = hdlc_change_mtu; + dev->mtu = HDLC_MAX_MTU; + + dev->type = ARPHRD_RAWHDLC; + dev->hard_header_len = 16; + + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + + hdlc->proto = -1; + hdlc->proto_detach = NULL; + + result = dev_alloc_name(dev, "hdlc%d"); + if (result<0) + return result; + + result = register_netdev(dev); + if (result != 0) + return -EIO; + + MOD_INC_USE_COUNT; + return 0; +} + + + +void unregister_hdlc_device(hdlc_device *hdlc) +{ + rtnl_lock(); + hdlc_proto_detach(hdlc); + unregister_netdevice(hdlc_to_dev(hdlc)); + rtnl_unlock(); + MOD_DEC_USE_COUNT; +} + + + +MODULE_AUTHOR("Krzysztof Halasa "); +MODULE_DESCRIPTION("HDLC support module"); +MODULE_LICENSE("GPL v2"); + +EXPORT_SYMBOL(hdlc_ioctl); +EXPORT_SYMBOL(register_hdlc_device); +EXPORT_SYMBOL(unregister_hdlc_device); + +struct packet_type hdlc_packet_type= +{ + __constant_htons(ETH_P_HDLC), + NULL, + hdlc_rcv, + NULL, + NULL +}; + + +static int __init hdlc_module_init(void) +{ + printk(KERN_INFO "%s\n", version); + dev_add_pack(&hdlc_packet_type); + return 0; +} + + + +static void __exit hdlc_module_exit(void) +{ + dev_remove_pack(&hdlc_packet_type); +} + + +module_init(hdlc_module_init); +module_exit(hdlc_module_exit); --- linux-2.4.20/drivers/net/wan/hdlc_ppp.c 2003-04-07 17:14:17.000000000 +0200 +++ linux-2.4/drivers/net/wan/hdlc_ppp.c 2003-04-07 00:25:09.000000000 +0200 @@ -0,0 +1,116 @@ +/* + * Generic HDLC support routines for Linux + * Point-to-point protocol support + * + * Copyright (C) 1999 - 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 + + +static int ppp_open(hdlc_device *hdlc) +{ + struct net_device *dev = hdlc_to_dev(hdlc); + void *old_ioctl; + int result; + + dev->priv = &hdlc->state.ppp.syncppp_ptr; + hdlc->state.ppp.syncppp_ptr = &hdlc->state.ppp.pppdev; + hdlc->state.ppp.pppdev.dev = dev; + + old_ioctl = dev->do_ioctl; + hdlc->state.ppp.old_change_mtu = dev->change_mtu; + sppp_attach(&hdlc->state.ppp.pppdev); + /* sppp_attach nukes them. We don't need syncppp's ioctl */ + dev->do_ioctl = old_ioctl; + hdlc->state.ppp.pppdev.sppp.pp_flags &= ~PP_CISCO; + dev->type = ARPHRD_PPP; + result = sppp_open(dev); + if (result) { + sppp_detach(dev); + return result; + } + + return 0; +} + + + +static void ppp_close(hdlc_device *hdlc) +{ + struct net_device *dev = hdlc_to_dev(hdlc); + + sppp_close(dev); + sppp_detach(dev); + dev->rebuild_header = NULL; + dev->change_mtu = hdlc->state.ppp.old_change_mtu; + dev->mtu = HDLC_MAX_MTU; + dev->hard_header_len = 16; +} + + + +static unsigned short ppp_type_trans(struct sk_buff *skb, + struct net_device *dev) +{ + return __constant_htons(ETH_P_WAN_PPP); +} + + + +int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + struct net_device *dev = hdlc_to_dev(hdlc); + int result; + + switch (ifr->ifr_settings.type) { + case IF_GET_PROTO: + ifr->ifr_settings.type = IF_PROTO_PPP; + return 0; /* return protocol only, no settable parameters */ + + case IF_PROTO_PPP: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + /* no settable parameters */ + + result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); + if (result) + return result; + + hdlc_proto_detach(hdlc); + + hdlc->open = ppp_open; + hdlc->stop = ppp_close; + 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; + dev->type = ARPHRD_PPP; + dev->addr_len = 0; + return 0; + } + + return -EINVAL; +} --- linux-2.4.20/drivers/net/wan/hdlc_x25.c 2003-04-07 17:14:17.000000000 +0200 +++ linux-2.4/drivers/net/wan/hdlc_x25.c 2003-04-07 00:25:09.000000000 +0200 @@ -0,0 +1,216 @@ +/* + * Generic HDLC support routines for Linux + * X.25 support + * + * Copyright (C) 1999 - 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 + +/* These functions are callbacks called by LAPB layer */ + +static void x25_connect_disconnect(void *token, int reason, int code) +{ + hdlc_device *hdlc = token; + struct sk_buff *skb; + unsigned char *ptr; + + if ((skb = dev_alloc_skb(1)) == NULL) { + printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc)); + return; + } + + ptr = skb_put(skb, 1); + *ptr = code; + + skb->dev = hdlc_to_dev(hdlc); + skb->protocol = htons(ETH_P_X25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + netif_rx(skb); +} + + + +static void x25_connected(void *token, int reason) +{ + x25_connect_disconnect(token, reason, 1); +} + + + +static void x25_disconnected(void *token, int reason) +{ + x25_connect_disconnect(token, reason, 2); +} + + + +static int x25_data_indication(void *token, struct sk_buff *skb) +{ + hdlc_device *hdlc = token; + unsigned char *ptr; + + ptr = skb_push(skb, 1); + *ptr = 0; + + skb->dev = hdlc_to_dev(hdlc); + skb->protocol = htons(ETH_P_X25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + return netif_rx(skb); +} + + + +static void x25_data_transmit(void *token, struct sk_buff *skb) +{ + hdlc_device *hdlc = token; + hdlc->xmit(skb, hdlc_to_dev(hdlc)); /* Ignore return value :-( */ +} + + + +static int x25_xmit(struct sk_buff *skb, struct net_device *dev) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + int result; + + + /* X.25 to LAPB */ + switch (skb->data[0]) { + case 0: /* Data to be transmitted */ + skb_pull(skb, 1); + if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK) + dev_kfree_skb(skb); + return 0; + + case 1: + if ((result = lapb_connect_request(hdlc))!= LAPB_OK) { + if (result == LAPB_CONNECTED) + /* Send connect confirm. msg to level 3 */ + x25_connected(hdlc, 0); + else + printk(KERN_ERR "%s: LAPB connect request " + "failed, error code = %i\n", + hdlc_to_name(hdlc), result); + } + break; + + case 2: + if ((result = lapb_disconnect_request(hdlc)) != LAPB_OK) { + if (result == LAPB_NOTCONNECTED) + /* Send disconnect confirm. msg to level 3 */ + x25_disconnected(hdlc, 0); + else + printk(KERN_ERR "%s: LAPB disconnect request " + "failed, error code = %i\n", + hdlc_to_name(hdlc), result); + } + break; + + default: /* to be defined */ + break; + } + + dev_kfree_skb(skb); + return 0; +} + + + +static int x25_open(hdlc_device *hdlc) +{ + struct lapb_register_struct cb; + int result; + + cb.connect_confirmation = x25_connected; + cb.connect_indication = x25_connected; + cb.disconnect_confirmation = x25_disconnected; + cb.disconnect_indication = x25_disconnected; + cb.data_indication = x25_data_indication; + cb.data_transmit = x25_data_transmit; + + result = lapb_register(hdlc, &cb); + if (result != LAPB_OK) + return result; + return 0; +} + + + +static void x25_close(hdlc_device *hdlc) +{ + lapb_unregister(hdlc); +} + + + +static void x25_rx(struct sk_buff *skb) +{ + hdlc_device *hdlc = dev_to_hdlc(skb->dev); + + if (lapb_data_received(hdlc, skb) == LAPB_OK) + return; + hdlc->stats.rx_errors++; + dev_kfree_skb_any(skb); +} + + + +int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + struct net_device *dev = hdlc_to_dev(hdlc); + int result; + + switch (ifr->ifr_settings.type) { + case IF_GET_PROTO: + ifr->ifr_settings.type = IF_PROTO_X25; + return 0; /* return protocol only, no settable parameters */ + + case IF_PROTO_X25: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(dev->flags & IFF_UP) + return -EBUSY; + + result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); + if (result) + return result; + + hdlc_proto_detach(hdlc); + + 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; + dev->type = ARPHRD_X25; + dev->addr_len = 0; + return 0; + } + + return -EINVAL; +} --- linux-2.4.20/drivers/net/wan/hdlc_raw.c 2003-04-07 17:14:17.000000000 +0200 +++ linux-2.4/drivers/net/wan/hdlc_raw.c 2003-04-07 00:25:09.000000000 +0200 @@ -0,0 +1,93 @@ +/* + * Generic HDLC support routines for Linux + * HDLC support + * + * Copyright (C) 1999 - 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 + + +static unsigned short raw_type_trans(struct sk_buff *skb, + struct net_device *dev) +{ + return __constant_htons(ETH_P_IP); +} + + + +int hdlc_raw_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; + + switch (ifr->ifr_settings.type) { + case IF_GET_PROTO: + ifr->ifr_settings.type = IF_PROTO_HDLC; + 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: + 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 = 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; + } + + return -EINVAL; +} --- linux-2.4.20/drivers/net/wan/hdlc_raw_eth.c 2003-04-07 17:14:17.000000000 +0200 +++ linux-2.4/drivers/net/wan/hdlc_raw_eth.c 2003-04-07 00:25:09.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.20/drivers/net/wan/hd64570.h 2001-03-07 04:44:36.000000000 +0100 +++ linux-2.4/drivers/net/wan/hd64570.h 2003-04-07 00:18:41.000000000 +0200 @@ -152,7 +152,7 @@ u32 bp; /* Buffer Pointer (24 bits) */ u16 len; /* Data Length */ u8 stat; /* Status */ - u8 unused2; + u8 unused; /* pads to 2-byte boundary */ }__attribute__ ((packed)) pkt_desc; @@ -202,7 +202,11 @@ #define MD0_CRC_ITU_0 0x06 #define MD0_CRC_ITU 0x07 -#define MD2_NRZI 0x20 /* NRZI mode */ +#define MD2_NRZ 0x00 +#define MD2_NRZI 0x20 +#define MD2_MANCHESTER 0x80 +#define MD2_FM_MARK 0xA0 +#define MD2_FM_SPACE 0xC0 #define MD2_LOOPBACK 0x03 /* Local data Loopback */ #define CTL_NORTS 0x01 --- linux-2.4.20/drivers/net/wan/hd6457x.c 2001-03-07 04:44:36.000000000 +0100 +++ linux-2.4/drivers/net/wan/hd6457x.c 2003-04-07 00:25:13.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,14 +55,6 @@ #error Either hd64570.h or hd64572.h must be included #endif - -static card_t *first_card; -static card_t **new_card = &first_card; - - -/* Maximum events to handle at each interrupt - should I increase it? */ -#define INTR_WORK 4 - #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) @@ -63,11 +68,19 @@ #define sca_ina(reg, card) sca_inw(reg, card) #define writea(value, ptr) writew(value, ptr) +#else /* HD64572 */ +#define sca_outa(value, reg, card) sca_outl(value, reg, card) +#define sca_ina(reg, card) sca_inl(reg, card) +#define writea(value, ptr) writel(value, ptr) +#endif + static inline int sca_intr_status(card_t *card) { + u8 result = 0; + +#ifdef __HD64570_H /* HD64570 */ u8 isr0 = sca_in(ISR0, card); u8 isr1 = sca_in(ISR1, card); - u8 result = 0; if (isr1 & 0x03) result |= SCA_INTR_DMAC_RX(0); if (isr1 & 0x0C) result |= SCA_INTR_DMAC_TX(0); @@ -76,19 +89,8 @@ if (isr0 & 0x0F) result |= SCA_INTR_MSCI(0); if (isr0 & 0xF0) result |= SCA_INTR_MSCI(1); - return result; -} - #else /* HD64572 */ -#define sca_outa(value, reg, card) sca_outl(value, reg, card) -#define sca_ina(reg, card) sca_inl(reg, card) -#define writea(value, ptr) writel(value, ptr) - - -static inline int sca_intr_status(card_t *card) -{ u32 isr0 = sca_inl(ISR0, card); - u8 result = 0; if (isr0 & 0x0000000F) result |= SCA_INTR_DMAC_RX(0); if (isr0 & 0x000000F0) result |= SCA_INTR_DMAC_TX(0); @@ -97,11 +99,17 @@ if (isr0 & 0x003E0000) result |= SCA_INTR_MSCI(0); if (isr0 & 0x3E000000) result |= SCA_INTR_MSCI(1); - return result; -} - #endif /* HD64570 vs HD64572 */ + if (!(result & SCA_INTR_DMAC_TX(0))) + if (sca_in(DSR_TX(0), card) & DSR_EOM) + result |= SCA_INTR_DMAC_TX(0); + if (!(result & SCA_INTR_DMAC_TX(1))) + if (sca_in(DSR_TX(1), card) & DSR_EOM) + result |= SCA_INTR_DMAC_TX(1); + + return result; +} @@ -119,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)) @@ -149,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; } @@ -162,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; @@ -174,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); @@ -185,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); @@ -250,8 +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; @@ -289,13 +307,17 @@ openwin(card, 0); #endif skb_put(skb, len); -#ifdef DEBUG_PKT +#ifdef CONFIG_HDLC_DEBUG_PKT printk(KERN_DEBUG "%s RX(%i):", hdlc_to_name(&port->hdlc), skb->len); debug_frame(skb); #endif port->hdlc.stats.rx_packets++; port->hdlc.stats.rx_bytes += skb->len; - hdlc_netif_rx(&port->hdlc, skb); + skb->mac.raw = skb->data; + skb->dev = hdlc_to_dev(&port->hdlc); + skb->dev->last_rx = jiffies; + skb->protocol = hdlc_type_trans(skb, hdlc_to_dev(&port->hdlc)); + netif_rx(skb); } @@ -320,14 +342,9 @@ pkt_desc *desc; u32 cda = sca_ina(dmac + CDAL, card); - if (cda == desc_off) + if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc))) break; /* No frame received */ -#ifdef __HD64572_H - if (cda == desc_off + 8) - break; /* SCA-II updates CDA in 2 steps */ -#endif - desc = desc_address(port, port->rxin, 0); stat = readb(&desc->stat); if (!(stat & ST_RX_EOM)) @@ -346,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 */ @@ -371,22 +388,18 @@ DSR_TX(phy_node(port)), card); while (1) { - u32 desc_off = desc_offset(port, port->txlast, 1); pkt_desc *desc; - u16 len; - if (sca_ina(dmac + CDAL, card) == desc_off) + u32 desc_off = desc_offset(port, port->txlast, 1); + u32 cda = sca_ina(dmac + CDAL, card); + 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); - len = readw(&desc->len); - port->hdlc.stats.tx_packets++; - port->hdlc.stats.tx_bytes += 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); } netif_wake_queue(hdlc_to_dev(&port->hdlc)); @@ -398,7 +411,6 @@ static void sca_intr(int irq, void* dev_id, struct pt_regs *regs) { card_t *card = dev_id; - int boguscnt = INTR_WORK; int i; u8 stat; @@ -419,17 +431,9 @@ if (stat & SCA_INTR_DMAC_TX(i)) sca_tx_intr(port); } - - if (--boguscnt < 0) { - printk(KERN_ERR "%s: too much work at " - "interrupt\n", - hdlc_to_name(&port->hdlc)); - goto exit; - } } } - exit: #ifndef ALL_PAGES_ALWAYS_MAPPED openwin(card, page); /* Restore original page */ #endif @@ -437,47 +441,22 @@ -static inline int sca_set_loopback(port_t *port, int line) +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); - - switch(line) { - case LINE_DEFAULT: - md2 &= ~MD2_LOOPBACK; - port->line &= ~LINE_LOOPBACK; - break; - - case LINE_LOOPBACK: - md2 |= MD2_LOOPBACK; - port->line |= LINE_LOOPBACK; - break; - - default: - return -EINVAL; - } - - sca_out(md2, msci + MD2, card); - return 0; -} - - - -static void sca_set_clock(port_t *port) -{ - card_t *card = port_to_card(port); - u8 msci = get_msci(port); unsigned int tmc, br = 10, brv = 1024; - if (port->clkrate > 0) { + + if (port->settings.clock_rate > 0) { /* Try lower br for better accuracy*/ do { br--; brv >>= 1; /* brv = 2^9 = 512 max in specs */ /* Baud Rate = CLOCK_BASE / TMC / 2^BR */ - tmc = CLOCK_BASE / (brv * port->clkrate); + tmc = CLOCK_BASE / (brv * port->settings.clock_rate); }while(br > 1 && tmc <= 128); if (tmc < 1) { @@ -487,11 +466,11 @@ } else if (tmc > 255) tmc = 256; /* tmc=0 means 256 - low baud rates */ - port->clkrate = CLOCK_BASE / (brv * tmc); + port->settings.clock_rate = CLOCK_BASE / (brv * tmc); } else { br = 9; /* Minimum clock rate */ tmc = 256; /* 8bit = 0 */ - port->clkrate = CLOCK_BASE / (256 * 512); + port->settings.clock_rate = CLOCK_BASE / (256 * 512); } port->rxs = (port->rxs & ~CLK_BRG_MASK) | br; @@ -509,27 +488,59 @@ /* Set BRG bits */ sca_out(port->rxs, msci + RXS, card); sca_out(port->txs, msci + TXS, card); + + if (port->settings.loopback) + md2 |= MD2_LOOPBACK; + else + md2 &= ~MD2_LOOPBACK; + + sca_out(md2, msci + MD2, card); + } -static void sca_set_hdlc_mode(port_t *port, u8 idle, u8 crc, u8 nrzi) +static void sca_open(hdlc_device *hdlc) { + port_t *port = hdlc_to_port(hdlc); card_t* card = port_to_card(port); - u8 msci = get_msci(port); - u8 md2 = (nrzi ? MD2_NRZI : 0) | - ((port->line & LINE_LOOPBACK) ? MD2_LOOPBACK : 0); - u8 ctl = (idle ? CTL_IDLE : 0); -#ifdef __HD64572_H - ctl |= CTL_URCT | CTL_URSKP; /* Skip the rest of underrun frame */ + u16 msci = get_msci(port); + u8 md0, md2; + + switch(port->encoding) { + case ENCODING_NRZ: md2 = MD2_NRZ; break; + case ENCODING_NRZI: md2 = MD2_NRZI; break; + case ENCODING_FM_MARK: md2 = MD2_FM_MARK; break; + case ENCODING_FM_SPACE: md2 = MD2_FM_SPACE; break; + default: md2 = MD2_MANCHESTER; + } + + if (port->settings.loopback) + md2 |= MD2_LOOPBACK; + + switch(port->parity) { + case PARITY_CRC16_PR0: md0 = MD0_HDLC | MD0_CRC_16_0; break; + case PARITY_CRC16_PR1: md0 = MD0_HDLC | MD0_CRC_16; break; +#ifdef __HD64570_H + 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 + case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU; break; + default: md0 = MD0_HDLC | MD0_CRC_NONE; + } sca_out(CMD_RESET, msci + CMD, card); - sca_out(MD0_HDLC | crc, msci + MD0, card); + sca_out(md0, msci + MD0, card); sca_out(0x00, msci + MD1, card); /* no address field check */ sca_out(md2, msci + MD2, card); sca_out(0x7E, msci + IDL, card); /* flag character 0x7E */ - sca_out(ctl, msci + CTL, card); +#ifdef __HD64570_H + sca_out(CTL_IDLE, msci + CTL, card); +#else + /* Skip the rest of underrun frame */ + sca_out(CTL_IDLE | CTL_URCT | CTL_URSKP, msci + CTL, card); +#endif #ifdef __HD64570_H /* Allow at least 8 bytes before requesting RX DMA operation */ @@ -539,24 +550,28 @@ sca_out(0x14, msci + TRC1, card); /* +1=TXRDY/DMA deactiv condition */ #else sca_out(0x0F, msci + RNR, card); /* +1=RX DMA activation condition */ - /* Setting than to larger value may cause Illegal Access */ - sca_out(0x20, msci + TNR0, card); /* =TX DMA activation condition */ - sca_out(0x30, msci + TNR1, card); /* +1=TX DMA deactivation condition*/ - sca_out(0x04, msci + TCR, card); /* =Critical TX DMA activ condition */ + sca_out(0x3C, msci + TFS, card); /* +1 = TX start */ + sca_out(0x38, msci + TCR, card); /* =Critical TX DMA activ condition */ + sca_out(0x38, msci + TNR0, card); /* =TX DMA activation condition */ + sca_out(0x3F, msci + TNR1, card); /* +1=TX DMA deactivation condition*/ #endif +/* We're using the following interrupts: + - TXINT (DMAC completed all transmisions, underflow or CTS change) + - all DMA interrupts +*/ #ifdef __HD64570_H /* MSCI TX INT IRQ enable */ sca_out(IE0_TXINT, msci + IE0, card); - sca_out(IE1_UDRN, msci + IE1, card); /* TX underrun IRQ */ + sca_out(IE1_UDRN, msci + IE1, card); /* TX underrun -> TXINT */ sca_out(sca_in(IER0, card) | (phy_node(port) ? 0x80 : 0x08), IER0, card); /* DMA IRQ enable */ sca_out(sca_in(IER1, card) | (phy_node(port) ? 0xF0 : 0x0F), IER1, card); #else - /* MSCI TX INT and underrrun IRQ enable */ + /* MSCI TX INT IRQ enable */ sca_outl(IE0_TXINT | IE0_UDRN, msci + IE0, card); /* DMA & MSCI IRQ enable */ sca_outl(sca_inl(IER0, card) | @@ -573,11 +588,52 @@ sca_out(port->txs, msci + TXS, card); sca_out(CMD_TX_ENABLE, msci + CMD, card); sca_out(CMD_RX_ENABLE, msci + CMD, card); + + netif_start_queue(hdlc_to_dev(hdlc)); +} + + + +static void sca_close(hdlc_device *hdlc) +{ + port_t *port = hdlc_to_port(hdlc); + + /* reset channel */ + netif_stop_queue(hdlc_to_dev(hdlc)); + sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port)); +} + + + +static int sca_attach(hdlc_device *hdlc, unsigned short encoding, + unsigned short parity) +{ + if (encoding != ENCODING_NRZ && + encoding != ENCODING_NRZI && + encoding != ENCODING_FM_MARK && + encoding != ENCODING_FM_SPACE && + encoding != ENCODING_MANCHESTER) + return -EINVAL; + + if (parity != PARITY_NONE && + parity != PARITY_CRC16_PR0 && + parity != PARITY_CRC16_PR1 && +#ifdef __HD64570_H + parity != PARITY_CRC16_PR0_CCITT && +#else + parity != PARITY_CRC32_PR1_CCITT && +#endif + parity != PARITY_CRC16_PR1_CCITT) + return -EINVAL; + + hdlc_to_port(hdlc)->encoding = encoding; + hdlc_to_port(hdlc)->parity = parity; + return 0; } -#ifdef DEBUG_RINGS +#ifdef CONFIG_HDLC_DEBUG_RINGS static void sca_dump_rings(hdlc_device *hdlc) { port_t *port = hdlc_to_port(hdlc); @@ -592,14 +648,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))); @@ -611,7 +666,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"); @@ -644,34 +699,14 @@ openwin(card, page); /* Restore original page */ #endif } -#endif /* DEBUG_RINGS */ +#endif /* CONFIG_HDLC_DEBUG_RINGS */ -static void sca_open(hdlc_device *hdlc) -{ - port_t *port = hdlc_to_port(hdlc); - - sca_set_hdlc_mode(port, 1, MD0_CRC_ITU, 0); - netif_start_queue(hdlc_to_dev(hdlc)); -} - - -static void sca_close(hdlc_device *hdlc) -{ - port_t *port = hdlc_to_port(hdlc); - - /* reset channel */ - netif_stop_queue(hdlc_to_dev(hdlc)); - sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port)); -} - - - -static int sca_xmit(hdlc_device *hdlc, struct sk_buff *skb) +static int sca_xmit(struct sk_buff *skb, struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); - struct net_device *dev = hdlc_to_dev(hdlc); card_t *card = port_to_card(port); pkt_desc *desc; u32 buff, len; @@ -685,7 +720,7 @@ desc = desc_address(port, port->txin + 1, 1); if (readb(&desc->stat)) { /* allow 1 packet gap */ /* should never happen - previous xmit should stop queue */ -#ifdef DEBUG_PKT +#ifdef CONFIG_HDLC_DEBUG_PKT printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name); #endif netif_stop_queue(dev); @@ -693,7 +728,7 @@ return 1; /* request packet to be queued */ } -#ifdef DEBUG_PKT +#ifdef CONFIG_HDLC_DEBUG_PKT printk(KERN_DEBUG "%s TX(%i):", hdlc_to_name(hdlc), skb->len); debug_frame(skb); #endif @@ -723,7 +758,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); @@ -740,7 +775,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.20/drivers/net/wan/n2.c 2001-09-14 01:04:43.000000000 +0200 +++ linux-2.4/drivers/net/wan/n2.c 2003-04-07 00:25:13.000000000 +0200 @@ -1,12 +1,11 @@ /* * SDL Inc. RISCom/N2 synchronous serial card 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. * * For information see http://hq.pm.waw.pl/hdlc/ * @@ -17,10 +16,10 @@ * SDL Inc. PPP/HDLC/CISCO driver */ +#include #include #include #include -#include #include #include #include @@ -33,17 +32,22 @@ #include #include "hd64570.h" -#define DEBUG_RINGS -/* #define DEBUG_PKT */ -static const char* version = "SDL RISCom/N2 driver revision: 1.02 for Linux 2.4"; +static const char* version = "SDL RISCom/N2 driver version: 1.14"; 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 */ @@ -87,17 +91,17 @@ hdlc_device hdlc; /* HDLC device struct - must be first */ struct card_s *card; spinlock_t lock; /* TX lock */ - int clkmode; /* clock mode */ - int clkrate; /* clock rate */ - int line; /* loopback only */ + 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; @@ -108,14 +112,18 @@ 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; }card_t; +static card_t *first_card; +static card_t **new_card = &first_card; + #define sca_reg(reg, card) (0x8000 | (card)->io | \ ((reg) & 0x0F) | (((reg) & 0xF0) << 6)) @@ -157,7 +165,7 @@ -static int n2_set_clock(port_t *port, int value) +static void n2_set_iface(port_t *port) { card_t *card = port->card; int io = card->io; @@ -166,13 +174,7 @@ u8 rxs = port->rxs & CLK_BRG_MASK; u8 txs = port->txs & CLK_BRG_MASK; - switch(value) { - case CLOCK_EXT: - mcr &= port->phy_node ? ~CLOCK_OUT_PORT1 : ~CLOCK_OUT_PORT0; - rxs |= CLK_LINE_RX; /* RXC input */ - txs |= CLK_LINE_TX; /* TXC input */ - break; - + switch(port->settings.clock_type) { case CLOCK_INT: mcr |= port->phy_node ? CLOCK_OUT_PORT1 : CLOCK_OUT_PORT0; rxs |= CLK_BRG_RX; /* BRG output */ @@ -191,8 +193,10 @@ txs |= CLK_RXCLK_TX; /* RX clock */ break; - default: - return -EINVAL; + default: /* Clock EXTernal */ + mcr &= port->phy_node ? ~CLOCK_OUT_PORT1 : ~CLOCK_OUT_PORT0; + rxs |= CLK_LINE_RX; /* RXC input */ + txs |= CLK_LINE_TX; /* TXC input */ } outb(mcr, io + N2_MCR); @@ -200,33 +204,38 @@ port->txs = txs; sca_out(rxs, msci + RXS, card); sca_out(txs, msci + TXS, card); - port->clkmode = value; - return 0; + sca_set_port(port); } -static int n2_open(hdlc_device *hdlc) +static int n2_open(struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); int io = port->card->io; - u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1 : TX422_PORT0); + u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1:TX422_PORT0); + + int result = hdlc_open(hdlc); + if (result) + return result; 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); - n2_set_clock(port, port->clkmode); + n2_set_iface(port); return 0; } -static void n2_close(hdlc_device *hdlc) +static int n2_close(struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); int io = port->card->io; u8 mcr = inb(io+N2_MCR) | (port->phy_node ? TX422_PORT1 : TX422_PORT0); @@ -234,108 +243,63 @@ sca_close(hdlc); mcr |= port->phy_node ? DTR_PORT1 : DTR_PORT0; /* set DTR OFF */ outb(mcr, io + N2_MCR); + hdlc_close(hdlc); MOD_DEC_USE_COUNT; + return 0; } -static int n2_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd) +static int n2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - int value = ifr->ifr_ifru.ifru_ivalue; - int result = 0; + const size_t size = sizeof(sync_serial_settings); + sync_serial_settings new_line, *line = ifr->ifr_settings.ifs_ifsu.sync; + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - - switch(cmd) { - case HDLCSCLOCK: - result = n2_set_clock(port, value); - case HDLCGCLOCK: - value = port->clkmode; - break; - - case HDLCSCLOCKRATE: - port->clkrate = value; - sca_set_clock(port); - case HDLCGCLOCKRATE: - value = port->clkrate; - break; - - case HDLCSLINE: - result = sca_set_loopback(port, value); - case HDLCGLINE: - value = port->line; - break; - -#ifdef DEBUG_RINGS - case HDLCRUN: +#ifdef CONFIG_HDLC_DEBUG_RINGS + if (cmd == SIOCDEVPRIVATE) { sca_dump_rings(hdlc); return 0; -#endif /* DEBUG_RINGS */ - - default: - return -EINVAL; } +#endif + if (cmd != SIOCWANDEV) + return hdlc_ioctl(dev, ifr, cmd); - ifr->ifr_ifru.ifru_ivalue = value; - return result; -} - - - -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; + switch(ifr->ifr_settings.type) { + case IF_GET_IFACE: + ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL; + 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; - for (i = 0; i < bcount; i++) - writeb(~i, bp + i); - for (i = 0; i < bcount; i++) - if (readb(bp + i) != (~i & 0xff)) - return 0; + case IF_IFACE_SYNC_SERIAL: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (copy_from_user(&new_line, line, size)) + return -EFAULT; + + if (new_line.clock_type != CLOCK_EXT && + new_line.clock_type != CLOCK_TXFROMRX && + new_line.clock_type != CLOCK_INT && + new_line.clock_type != CLOCK_TXINT) + return -EINVAL; /* No such clock setting */ - /* 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; + if (new_line.loopback != 0 && new_line.loopback != 1) + return -EINVAL; - for (i = 0; i < wcount; i++) - writew(0xAA55, dp + i); - for (i = 0; i < wcount; i++) - if (readw(dp + i) != 0xAA55) - return 0; + memcpy(&port->settings, &new_line, size); /* Update settings */ + n2_set_iface(port); + return 0; - for (i = 0; i < wcount; i++) - writew(page, dp + i); + default: + return hdlc_ioctl(dev, ifr, cmd); } - - return page; } @@ -363,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"); @@ -378,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; @@ -438,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); @@ -465,6 +432,7 @@ sca_init(card, 0); for (cnt = 0; cnt < 2; cnt++) { port_t *port = &card->ports[cnt]; + struct net_device *dev = hdlc_to_dev(&port->hdlc); if ((cnt == 0 && !valid0) || (cnt == 1 && !valid1)) continue; @@ -476,14 +444,16 @@ port->log_node = 1; spin_lock_init(&port->lock); - hdlc_to_dev(&port->hdlc)->irq = irq; - hdlc_to_dev(&port->hdlc)->mem_start = winbase; - hdlc_to_dev(&port->hdlc)->mem_end = winbase + USE_WINDOWSIZE-1; - hdlc_to_dev(&port->hdlc)->tx_queue_len = 50; - port->hdlc.ioctl = n2_ioctl; - port->hdlc.open = n2_open; - port->hdlc.close = n2_close; + dev->irq = irq; + dev->mem_start = winbase; + dev->mem_end = winbase + USE_WINDOWSIZE-1; + dev->tx_queue_len = 50; + dev->do_ioctl = n2_ioctl; + dev->open = n2_open; + dev->stop = n2_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_WARNING "n2: unable to register hdlc " @@ -542,7 +512,7 @@ break; hw++; } - + if (!valid[0] && !valid[1]) break; /* at least one port must be used */ @@ -550,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"); @@ -586,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.20/drivers/net/wan/c101.c 2001-09-14 01:04:43.000000000 +0200 +++ linux-2.4/drivers/net/wan/c101.c 2003-04-07 13:46:18.000000000 +0200 @@ -1,12 +1,11 @@ /* * Moxa C101 synchronous serial card driver for Linux * - * Copyright (C) 2000 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/ * @@ -15,6 +14,7 @@ * Moxa C101 User's Manual */ +#include #include #include #include @@ -29,10 +29,8 @@ #include "hd64570.h" -#define DEBUG_RINGS -/* #define DEBUG_PKT */ -static const char* version = "Moxa C101 driver revision: 1.02 for Linux 2.4"; +static const char* version = "Moxa C101 driver version: 1.14"; static const char* devname = "C101"; #define C101_PAGE 0x1D00 @@ -42,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 @@ -51,32 +53,40 @@ typedef struct card_s { hdlc_device hdlc; /* HDLC device struct - must be first */ spinlock_t lock; /* TX lock */ - int clkmode; /* clock mode */ - int clkrate; /* clock speed */ - int line; /* loopback only */ u8 *win0base; /* ISA window base address */ u32 phy_winbase; /* ISA physical base address */ + 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; typedef card_t port_t; +static card_t *first_card; +static card_t **new_card = &first_card; + #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) @@ -105,18 +115,13 @@ #include "hd6457x.c" -static int c101_set_clock(port_t *port, int value) +static void c101_set_iface(port_t *port) { u8 msci = get_msci(port); u8 rxs = port->rxs & CLK_BRG_MASK; u8 txs = port->txs & CLK_BRG_MASK; - switch(value) { - case CLOCK_EXT: - rxs |= CLK_LINE_RX; /* RXC input */ - txs |= CLK_LINE_TX; /* TXC input */ - break; - + switch(port->settings.clock_type) { case CLOCK_INT: rxs |= CLK_BRG_RX; /* TX clock */ txs |= CLK_RXCLK_TX; /* BRG output */ @@ -132,90 +137,108 @@ txs |= CLK_RXCLK_TX; /* RX clock */ break; - default: - return -EINVAL; + default: /* EXTernal clock */ + rxs |= CLK_LINE_RX; /* RXC input */ + txs |= CLK_LINE_TX; /* TXC input */ } port->rxs = rxs; port->txs = txs; sca_out(rxs, msci + RXS, port); sca_out(txs, msci + TXS, port); - port->clkmode = value; - return 0; + sca_set_port(port); } -static int c101_open(hdlc_device *hdlc) +static int c101_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; writeb(1, port->win0base + C101_DTR); sca_out(0, MSCI1_OFFSET + CTL, port); /* RTS uses ch#2 output */ sca_open(hdlc); - c101_set_clock(port, port->clkmode); + c101_set_iface(port); return 0; } -static void c101_close(hdlc_device *hdlc) +static int c101_close(struct net_device *dev) { + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); sca_close(hdlc); writeb(0, port->win0base + C101_DTR); sca_out(CTL_NORTS, MSCI1_OFFSET + CTL, port); + hdlc_close(hdlc); MOD_DEC_USE_COUNT; + return 0; } -static int c101_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd) +static int c101_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - int value = ifr->ifr_ifru.ifru_ivalue; - int result = 0; + const size_t size = sizeof(sync_serial_settings); + sync_serial_settings new_line, *line = ifr->ifr_settings.ifs_ifsu.sync; + hdlc_device *hdlc = dev_to_hdlc(dev); port_t *port = hdlc_to_port(hdlc); - if(!capable(CAP_NET_ADMIN)) - return -EPERM; +#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(cmd) { - case HDLCSCLOCK: - result = c101_set_clock(port, value); - case HDLCGCLOCK: - value = port->clkmode; - break; + switch(ifr->ifr_settings.type) { + case IF_GET_IFACE: + ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL; + 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 HDLCSCLOCKRATE: - port->clkrate = value; - sca_set_clock(port); - case HDLCGCLOCKRATE: - value = port->clkrate; - break; + case IF_IFACE_SYNC_SERIAL: + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (copy_from_user(&new_line, line, size)) + return -EFAULT; + + if (new_line.clock_type != CLOCK_EXT && + new_line.clock_type != CLOCK_TXFROMRX && + new_line.clock_type != CLOCK_INT && + new_line.clock_type != CLOCK_TXINT) + return -EINVAL; /* No such clock setting */ - case HDLCSLINE: - result = sca_set_loopback(port, value); - case HDLCGLINE: - value = port->line; - break; + if (new_line.loopback != 0 && new_line.loopback != 1) + return -EINVAL; -#ifdef DEBUG_RINGS - case HDLCRUN: - sca_dump_rings(hdlc); + memcpy(&port->settings, &new_line, size); /* Update settings */ + c101_set_iface(port); return 0; -#endif /* DEBUG_RINGS */ default: - return -EINVAL; + return hdlc_ioctl(dev, ifr, cmd); } - - ifr->ifr_ifru.ifru_ivalue = value; - return result; } static void c101_destroy_card(card_t *card) { + readb(card->win0base + C101_PAGE); /* Resets SCA? */ + if (card->irq) free_irq(card->irq, card); @@ -229,8 +252,9 @@ -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; int result; @@ -271,9 +295,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 */ @@ -284,15 +309,19 @@ sca_init(card, 0); + dev = hdlc_to_dev(&card->hdlc); + spin_lock_init(&card->lock); - hdlc_to_dev(&card->hdlc)->irq = irq; - hdlc_to_dev(&card->hdlc)->mem_start = winbase; - hdlc_to_dev(&card->hdlc)->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1; - hdlc_to_dev(&card->hdlc)->tx_queue_len = 50; - card->hdlc.ioctl = c101_ioctl; - card->hdlc.open = c101_open; - card->hdlc.close = c101_close; + dev->irq = irq; + dev->mem_start = winbase; + dev->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1; + dev->tx_queue_len = 50; + dev->do_ioctl = c101_ioctl; + dev->open = c101_open; + dev->stop = c101_close; + card->hdlc.attach = sca_attach; card->hdlc.xmit = sca_xmit; + card->settings.clock_type = CLOCK_EXT; result = register_hdlc_device(&card->hdlc); if (result) { @@ -334,7 +363,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"); @@ -371,6 +400,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.20/Documentation/Configure.help 2002-11-29 00:53:08.000000000 +0100 +++ linux-2.4/Documentation/Configure.help 2003-04-07 14:22:20.000000000 +0200 @@ -10493,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 @@ -10507,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 @@ -10556,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.20/drivers/net/wan/Config.in 2002-11-29 00:53:14.000000000 +0100 +++ linux-2.4/drivers/net/wan/Config.in 2003-04-07 14:22:20.000000000 +0200 @@ -66,8 +63,14 @@ tristate ' SyncLink HDLC/SYNCPPP support' CONFIG_SYNCLINK_SYNCPPP - tristate ' Generic HDLC driver' CONFIG_HDLC +# Generic HDLC + + 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 if [ "$CONFIG_LAPB" = "m" -a "$CONFIG_HDLC" = "m" -o "$CONFIG_LAPB" = "y" ]; then bool ' X.25 protocol support' CONFIG_HDLC_X25 @@ -76,6 +79,10 @@ fi dep_tristate ' SDL RISCom/N2 support' CONFIG_N2 $CONFIG_HDLC dep_tristate ' Moxa C101 support' CONFIG_C101 $CONFIG_HDLC + bool ' Debug received/transmitted packets' CONFIG_HDLC_DEBUG_PKT + bool ' Debug hard_header routines' CONFIG_HDLC_DEBUG_HARD_HEADER + bool ' Debug FECN/BECN conditions' CONFIG_HDLC_DEBUG_ECN + bool ' Debug RX/TX packet rings' CONFIG_HDLC_DEBUG_RINGS fi tristate ' Frame relay DLCI support' CONFIG_DLCI --- linux-2.4.20/net/core/dev.c 2002-11-29 00:53:15.000000000 +0100 +++ linux-2.4/net/core/dev.c 2003-04-07 00:18:44.000000000 +0200 @@ -2155,7 +2155,8 @@ cmd == SIOCETHTOOL || cmd == SIOCGMIIPHY || cmd == SIOCGMIIREG || - cmd == SIOCSMIIREG) { + cmd == SIOCSMIIREG || + cmd == SIOCWANDEV) { if (dev->do_ioctl) { if (!netif_device_present(dev)) return -ENODEV; @@ -2321,8 +2322,9 @@ */ default: - if (cmd >= SIOCDEVPRIVATE && - cmd <= SIOCDEVPRIVATE + 15) { + if (cmd == SIOCWANDEV || + (cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15)) { dev_load(ifr.ifr_name); dev_probe_lock(); rtnl_lock();