patch-2.2.18 linux/drivers/net/xpds/xpds-encap-fr.c
Next file: linux/drivers/net/xpds/xpds-encap-fr.h
Previous file: linux/drivers/net/xpds/Makefile
Back to the patch index
Back to the overall index
- Lines: 752
- Date:
Tue Nov 7 13:19:28 2000
- Orig file:
v2.2.17/drivers/net/xpds/xpds-encap-fr.c
- Orig date:
Thu Jan 1 01:00:00 1970
diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/drivers/net/xpds/xpds-encap-fr.c linux/drivers/net/xpds/xpds-encap-fr.c
@@ -0,0 +1,751 @@
+/*
+ * Copyright 2000 Xpeed, Inc.
+ * fr.c $Revision: 1.23 $
+ * License to copy and distribute is GNU General Public License, version 2.
+ * Some code adapted from Mike McLagan's dlci.c 0.30 from Linux kernels
+ * 2.0-2.2.
+ */
+#define __KERNEL__ 1
+#include <linux/config.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/net.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include "xpds.h"
+#include "xpds-encap-fr.h"
+#include "xpds-softnet.h"
+
+#define XPDS_DLCI_LMI_OFF 0
+#define XPDS_DLCI_LMI_LT 1
+#define XPDS_DLCI_LMI_NT 2
+#define XPDS_DLCI_LMI_NT_BIDIRECTIONAL 3
+
+int xpds_dlci_t391 = 10;
+int xpds_dlci_n391 = 6;
+
+#define DEBUG_DLCI 4
+extern int xpds_debug_level;
+
+#if DEBUG
+#define dprintk if (xpds_debug_level & DEBUG_DLCI) printk
+#else
+#define dprintk if (0) printk
+#endif
+
+#define nrprintk if (net_ratelimit()) printk
+
+typedef struct {
+ u8 dlci_header[2] __attribute__ ((packed));
+ u8 protocol_discriminator __attribute__ ((packed));
+ u8 call_reference __attribute__ ((packed));
+ u8 padding __attribute__ ((packed));
+} dlci_status_head_t __attribute__ ((packed));
+
+typedef struct {
+ u8 message_type __attribute__ ((packed));
+ u8 locking_shift __attribute__ ((packed));
+ u8 report_content_id __attribute__ ((packed));
+ u8 report_content_length __attribute__ ((packed));
+ u8 report_type __attribute__ ((packed));
+ u8 liv_element_id __attribute__ ((packed));
+ u8 liv_length __attribute__ ((packed));
+ u8 liv_send_sequence __attribute__ ((packed));
+ u8 liv_receive_sequence __attribute__ ((packed));
+} dlci_status_tail_t __attribute__ ((packed));
+
+#define MESSAGE_STATUS_ENQUIRY 0x75
+#define MESSAGE_STATUS 0x7d
+
+#define REPORT_FULL_STATUS 0x00
+#define REPORT_LIV 0x01
+#define REPORT_SINGLE_PVC_STATS 0x02
+
+typedef struct {
+ u8 pvc_status_id __attribute__ ((packed));
+ u8 length __attribute__ ((packed));
+ u8 dlci_info[2] __attribute__ ((packed));
+ u8 flags __attribute__ ((packed));
+} dlci_pvc_status_t;
+
+#define PVC_FLAGS__NEW 0x08
+#define PVC_FLAGS__ACTIVE 0x02
+
+typedef struct dlci_timer_data_t {
+ struct net_device *dev;
+ int dlci_num;
+} dlci_timer_data_t;
+
+/*
+ * We generate LMI status enquiries in DLCI_LMI_NT mode.
+ */
+static void
+xpds_dlci_lmi_timer (void *p)
+{
+ dlci_timer_data_t *data;
+ struct net_device *dev;
+ int card_num;
+ struct frad_local *flp;
+ dlci_status_head_t *status_head;
+ dlci_status_tail_t *status_tail;
+ struct sk_buff *skb;
+ short dlci, lmi_dlci = 0;
+ int i;
+
+ data = (dlci_timer_data_t *) p;
+
+ dev = data->dev;
+ card_num = dev - xpds_devs;
+ flp = &(xpds_data[card_num].frad_data);
+
+ dprintk (KERN_DEBUG "%s: xpds_dlci_lmi_timer()\n", dev->name);
+
+ if (xpds_data[card_num].dlci_lmi != XPDS_DLCI_LMI_NT &&
+ xpds_data[card_num].dlci_lmi != XPDS_DLCI_LMI_NT_BIDIRECTIONAL){
+ return;
+ }
+
+ if (flp->no_initiate_lmi) return;
+
+ dlci = flp->dlci[data->dlci_num];
+ dprintk (KERN_DEBUG "%s: xpds_dlci_lmi_timer(), dev = %p, dlci = %d\n", xpds_devs[card_num].name, dev, dlci);
+
+ if (xpds_if_busy(dev)) {
+
+ skb = alloc_skb (sizeof (dlci_status_head_t) +
+ sizeof (dlci_status_tail_t), GFP_ATOMIC);
+ if (skb == NULL) {
+ printk (KERN_ERR "%s: unable to allocate skb in xlds_dlci_lmi_timer()\n", dev->name);
+ return;
+ }
+ skb->dev = dev;
+ skb->len = sizeof (dlci_status_head_t) +
+ sizeof (dlci_status_tail_t);
+ status_head = (dlci_status_head_t *) skb->data;
+ status_tail = (dlci_status_tail_t *) (skb->data +
+ sizeof (dlci_status_head_t));
+ status_head->dlci_header[0] = ((lmi_dlci >> 4) << 2) |
+ xpds_data[card_num].dlci_cr;
+ status_head->dlci_header[1] = (lmi_dlci << 4) | 1;
+ status_head->protocol_discriminator = 0x03;
+ status_head->call_reference = 0x08;
+ status_head->padding = 0;
+
+ status_tail->message_type = MESSAGE_STATUS_ENQUIRY;
+ status_tail->locking_shift = 0x95;
+ status_tail->report_content_id = 0x01;
+ status_tail->report_content_length = 0x01;
+ status_tail->liv_element_id = 0x03 /* 0x19 */;
+ status_tail->liv_length = 0x02;
+
+ status_tail->liv_send_sequence = (flp->liv_send_sequence) ++;
+ status_tail->liv_receive_sequence = flp->liv_receive_sequence;
+ if (flp->pvc_active[data->dlci_num]) {
+ if (flp->remote_liv_receive_sequence >=
+ flp->new_liv_send_sequence) {
+ flp->pvc_new[data->dlci_num] = 0;
+ } else {
+ flp->pvc_new[data->dlci_num] = 1;
+ }
+ } else {
+ flp->new_liv_send_sequence =
+ status_tail->liv_send_sequence;
+ }
+ dprintk (KERN_DEBUG "%s: dlci = %d ", xpds_devs[card_num].name, dlci);
+ dprintk (KERN_DEBUG "pvc = %d, new = %d\n",
+ flp->pvc_active[data->dlci_num], flp->pvc_new[data->dlci_num]);
+ dprintk (KERN_DEBUG "%s: liv_send_sequence = %d, liv_receive_sequence = %d\n", xpds_devs[card_num].name, status_tail->liv_send_sequence, status_tail->liv_receive_sequence);
+ flp->message_number ++;
+ if (flp->message_number >= xpds_dlci_n391) {
+ flp->message_number = 0;
+ }
+ status_tail->report_type =
+ (flp->message_number == 0) ?
+ REPORT_FULL_STATUS : REPORT_LIV;
+ dprintk (KERN_DEBUG "message_number = %d, sending %02x\n",
+ flp->message_number, status_tail->report_type);
+ dprintk (KERN_DEBUG "%s: sending LMI packet in xpds_dlci_lmi_timer()\n", xpds_devs[card_num].name);
+ xpds_tx (skb->data, skb->len, dev);
+
+ dev_kfree_skb (skb);
+ }
+
+ i = data->dlci_num;
+
+ init_timer(&(xpds_data[card_num].dlci_lmi_timers[i]));
+ xpds_data[card_num].dlci_lmi_timers[i].function = (void *)&xpds_dlci_lmi_timer;
+ xpds_data[card_num].dlci_lmi_timers[i].expires = jiffies + xpds_dlci_t391 * HZ;
+ xpds_data[card_num].dlci_lmi_timers[i].data = (unsigned long)&(xpds_data[card_num].dlci_lmi_timer_data[i]);
+ add_timer(&(xpds_data[card_num].dlci_lmi_timers[i]));
+
+ dprintk(KERN_DEBUG "%s: xpds_dlci_lmi_timer()\n", dev->name);
+}
+
+void
+xpds_dlci_install_lmi_timer (int i, struct net_device *dev)
+{
+ int card_num;
+
+ card_num = dev - xpds_devs;
+
+ dprintk (KERN_DEBUG "%s: xpds_dlci_install_lmi_timer (%d, %p)\n", xpds_devs[card_num].name, i, dev);
+ if (i < 0 || i >= CONFIG_DLCI_COUNT) {
+ printk (KERN_ERR "%s: invalid DLCI device number %d\n", xpds_devs[card_num].name, i);
+ return;
+ }
+ init_timer(&(xpds_data[card_num].dlci_lmi_timers[i]));
+ xpds_data[card_num].dlci_lmi_timers[i].function = (void *)&xpds_dlci_lmi_timer;
+ xpds_data[card_num].dlci_lmi_timers[i].expires = jiffies + xpds_dlci_t391 * HZ;
+ xpds_data[card_num].dlci_lmi_timer_data[i].dev = dev;
+ xpds_data[card_num].dlci_lmi_timer_data[i].dlci_num = i;
+ xpds_data[card_num].dlci_lmi_timers[i].data = (unsigned long)&(xpds_data[card_num].dlci_lmi_timer_data[i]);
+ add_timer(&(xpds_data[card_num].dlci_lmi_timers[i]));
+ dprintk (KERN_DEBUG "%s: xpds_dlci_install_lmi_timer() done\n",
+ dev->name);
+}
+
+void
+xpds_dlci_remove_lmi_timer (int i, struct net_device *dev)
+{
+ int card_num;
+
+ card_num = dev - xpds_devs;
+
+ dprintk (KERN_DEBUG "%s: xpds_dlci_remove_lmi_timer (%d, %p)\n",
+ dev->name, i, dev);
+ if (i < 0 || i >= CONFIG_DLCI_COUNT) {
+ printk (KERN_ERR "%s: invalid DLCI device number %d\n", xpds_devs[card_num].name, i);
+ return;
+ }
+ del_timer(&(xpds_data[card_num].dlci_lmi_timers[i]));
+ dprintk (KERN_DEBUG "%s: xpds_dlci_remove_lmi_timer() done\n",
+ dev->name);
+}
+
+
+static void
+xpds_dlci_handle_status (struct sk_buff *skb, struct net_device *dev)
+{
+ struct frad_local *flp;
+ dlci_status_head_t *dlci_status_head, *reply_status_head;
+ dlci_status_tail_t *dlci_status_tail, *reply_status_tail;
+ dlci_pvc_status_t *dlci_pvc_status, *reply_pvc_status;
+ struct sk_buff *reply_skb;
+ short dlci, lmi_dlci = 0;
+ int i;
+ int old_pvc_active;
+ int has_pvc_status;
+ int has_padding;
+ int card_num;
+
+ card_num = dev - xpds_devs;
+
+ if (xpds_data[card_num].dlci_lmi == XPDS_DLCI_LMI_OFF) return;
+
+ flp = &(xpds_data[card_num].frad_data);
+ skb->dev = dev;
+
+ dlci_status_head = (dlci_status_head_t *)skb->data;
+ if (dlci_status_head->padding != 0) {
+ /*
+ * no padding
+ */
+ has_padding = 0;
+ } else {
+ /*
+ * one byte of zero padding between call reference
+ * and message type
+ */
+ has_padding = 1;
+ }
+ dlci_status_tail = (dlci_status_tail_t *)
+ (skb->data + sizeof (*dlci_status_head) - 1 + has_padding);
+ if (skb->len < sizeof (*dlci_status_head) + sizeof (*dlci_status_tail) - 1 + has_padding) {
+ dprintk (KERN_ERR "%s: LMI packet of length %lu is too short\n", xpds_devs[card_num].name, (unsigned long) (skb->len));
+ }
+ dlci_pvc_status = (dlci_pvc_status_t *)
+ (skb->data + sizeof (*dlci_status_head) +
+ sizeof (*dlci_status_tail) - 1 + has_padding);
+
+ dprintk (KERN_DEBUG "%s: LMI packet received has protocol discriminator = 0x%02x\n", xpds_devs[card_num].name, dlci_status_head->protocol_discriminator);
+ dprintk (KERN_DEBUG "%s: LMI packet received has call reference = 0x%02x\n",
+ dev->name, dlci_status_head->call_reference);
+ if (dlci_status_head->protocol_discriminator != FRAD_I_UI ||
+ dlci_status_head->call_reference != FRAD_P_Q933) {
+ printk (KERN_NOTICE "%s: dlci_handle_status called with 0x%02x 0x%02x\n",
+ dev->name, dlci_status_head->protocol_discriminator,
+ dlci_status_head->call_reference);
+ return;
+ }
+
+ dprintk (KERN_DEBUG "LMI packet received has message type = 0x%02x\n",
+ dlci_status_tail->message_type);
+ if (dlci_status_tail->message_type != MESSAGE_STATUS_ENQUIRY &&
+ dlci_status_tail->message_type != MESSAGE_STATUS) {
+ printk (KERN_NOTICE "unknown message type 0x%02x\n",
+ dlci_status_tail->message_type);
+ return;
+ }
+
+ dprintk (KERN_DEBUG "LMI packet received has report type = 0x%02x\n",
+ dlci_status_tail->report_type);
+ if (dlci_status_tail->report_type != REPORT_FULL_STATUS &&
+ dlci_status_tail->report_type != REPORT_LIV) {
+ dprintk (KERN_DEBUG "report type is not full status or LIV\n");
+ return;
+ }
+
+ dprintk (KERN_DEBUG "LMI packet received has receive sequence = %d\n",
+ dlci_status_tail->liv_receive_sequence);
+ dprintk (KERN_DEBUG "LMI packet received has send sequence = %d\n",
+ dlci_status_tail->liv_send_sequence);
+
+ reply_skb = alloc_skb (sizeof (dlci_status_head_t) +
+ sizeof (dlci_status_tail_t) + sizeof (dlci_pvc_status_t),
+ GFP_ATOMIC);
+ if (reply_skb == NULL) {
+ printk (KERN_ERR "%s: unable to allocate reply_skb in xpds_dlci_handle_status().\n", dev->name);
+ return;
+ }
+ reply_skb->len = sizeof (dlci_status_head_t) +
+ sizeof (dlci_status_tail_t) /* + sizeof (dlci_pvc_status_t) */;
+ reply_status_head = (dlci_status_head_t *) reply_skb->data;
+ reply_status_tail = (dlci_status_tail_t *) (reply_skb->data +
+ sizeof (dlci_status_head_t));
+ reply_pvc_status = (dlci_pvc_status_t *) (reply_skb->data +
+ sizeof (dlci_status_head_t) + sizeof (dlci_status_tail_t) );
+
+ if (dlci_status_tail->liv_receive_sequence > flp->liv_send_sequence){
+ dprintk (KERN_DEBUG "received receive sequence number %d > %d\n",
+ dlci_status_tail->liv_receive_sequence,
+ flp->liv_send_sequence);
+ }
+ flp->remote_liv_receive_sequence =
+ dlci_status_tail->liv_receive_sequence;
+ flp->liv_send_sequence = dlci_status_tail->liv_receive_sequence + 1;
+
+ flp->remote_liv_send_sequence =
+ dlci_status_tail->liv_send_sequence;
+ flp->liv_receive_sequence = dlci_status_tail->liv_send_sequence;
+
+ if (dlci_status_tail->report_type == REPORT_FULL_STATUS) {
+ if (skb->len < sizeof (*dlci_status_head) + sizeof (*dlci_status_tail) - 1 + has_padding + sizeof (*dlci_pvc_status)) {
+ if (dlci_status_tail->message_type == MESSAGE_STATUS) {
+ printk (KERN_ERR "%s: LMI packet length %lu is too short for report type REPORT_FULL_STATUS\n", xpds_devs[card_num].name, (unsigned long) (skb->len));
+ }
+ dlci = -1;
+ has_pvc_status = 0;
+ } else {
+ dlci = ((dlci_pvc_status->dlci_info[0] & 0x7f) << 4) |
+ ((dlci_pvc_status->dlci_info[1] & 0x7f) >> 3);
+ has_pvc_status = 1;
+ }
+ } else {
+ dprintk (KERN_DEBUG "%s: dlci_status_tail->report_type = 0x%02x (not REPORT_FULL_STATUS)\n", xpds_devs[card_num].name, dlci_status_tail->report_type);
+ dlci = -1;
+ has_pvc_status = 0;
+ }
+ dprintk (KERN_DEBUG "%s: LMI packet received has DLCI = %d, has_pvc_status = %d\n", xpds_devs[card_num].name, dlci, has_pvc_status);
+
+ if (has_pvc_status) {
+ for (i = 0; i < CONFIG_DLCI_MAX; i ++) {
+ if (dlci == flp->dlci[i]) break;
+ }
+ if (i >= CONFIG_DLCI_MAX) {
+ int j;
+
+ printk (KERN_ERR "%s: invalid DLCI %d\n",
+ dev->name, dlci);
+
+ printk (KERN_ERR "%s: flp->dlci[] = {", xpds_devs[card_num].name);
+ for (j = 0; j < CONFIG_DLCI_MAX; j ++) {
+ if (j != 0) printk (", ");
+ printk ("%d", flp->dlci[j]);
+ }
+ printk ("}\n");
+ dev_kfree_skb (reply_skb);
+ return;
+ }
+ } else {
+ i = 0;
+ }
+
+ reply_status_head->dlci_header[0] = ((lmi_dlci >> 4) << 2) |
+ xpds_data[card_num].dlci_cr;
+ reply_status_head->dlci_header[1] = (lmi_dlci << 4) | 1;
+ reply_status_head->protocol_discriminator = 0x03;
+ reply_status_head->call_reference = 0x08;
+ reply_status_head->padding = 0;
+
+ reply_status_tail->message_type = MESSAGE_STATUS;
+ reply_status_tail->locking_shift = 0x95;
+ reply_status_tail->report_content_id = 0x01;
+ reply_status_tail->report_content_length = 0x01;
+ flp->message_number ++;
+ if (flp->message_number >= xpds_dlci_n391) {
+ flp->message_number = 0;
+ }
+ reply_status_tail->report_type =
+ (flp->message_number == 0) ?
+ REPORT_FULL_STATUS : REPORT_LIV;
+ dprintk (KERN_DEBUG "%s: message_number = %d, sending 0x%02x\n",
+ dev->name, flp->message_number, reply_status_tail->report_type);
+ reply_status_tail->liv_element_id = 0x03 /* 0x19 */;
+ reply_status_tail->liv_length = 0x02;
+ reply_status_tail->liv_send_sequence = (flp->liv_send_sequence) ++;
+ reply_status_tail->liv_receive_sequence = flp->liv_receive_sequence;
+
+ if (has_pvc_status) {
+ reply_pvc_status->pvc_status_id = 0x7;
+ reply_pvc_status->length = 3;
+ reply_pvc_status->dlci_info[0] = (dlci >> 6) & 0x3f;
+ reply_pvc_status->dlci_info[1] = ((dlci << 3) & 0x78) | 0x80;
+
+ dprintk (KERN_DEBUG "dlci_pvc_status->pvc_status_id = 0x%02x\n",
+ dlci_pvc_status->pvc_status_id);
+ dprintk (KERN_DEBUG "dlci_pvc_status->length = 0x%02x\n",
+ dlci_pvc_status->length);
+ dprintk (KERN_DEBUG "dlci_pvc_status->dlci_info = 0x%02x 0x%02x\n", dlci_pvc_status->dlci_info[0], dlci_pvc_status->dlci_info[1]);
+ dprintk (KERN_DEBUG "dlci_pvc_status->flags = 0x%02x\n",
+ dlci_pvc_status->flags);
+ old_pvc_active = flp->pvc_active[i];
+ flp->pvc_active[i] = (dlci_pvc_status->flags & PVC_FLAGS__ACTIVE) != 0;
+ if (! old_pvc_active && flp->pvc_active[i]) {
+ printk (KERN_NOTICE "%s: DLCI %d PVC became active\n", xpds_devs[card_num].name, dlci);
+ }
+ if (old_pvc_active && ! flp->pvc_active[i]) {
+ printk (KERN_NOTICE "%s: DLCI %d PVC became inactive\n", xpds_devs[card_num].name, dlci);
+ }
+ if (flp->pvc_active[i]) {
+ if (dlci_status_tail->liv_receive_sequence >=
+ flp->new_liv_send_sequence) {
+ flp->pvc_new[i] = 0;
+ } else {
+ flp->pvc_new[i] = 1;
+ }
+ } else {
+ flp->new_liv_send_sequence =
+ reply_status_tail->liv_send_sequence;
+ }
+ dprintk (KERN_DEBUG "dlci = %d\n", dlci);
+ dprintk (KERN_DEBUG "pvc = %d, new = %d\n",
+ flp->pvc_active[i], flp->pvc_new[i]);
+ dprintk (KERN_DEBUG "liv_send_sequence = %d, liv_receive_sequence = %d\n", reply_status_tail->liv_send_sequence, reply_status_tail->liv_receive_sequence);
+ reply_pvc_status->flags = 0x80 | (flp->pvc_new[i] << 3) |
+ (flp->pvc_active[i] << 1);
+ }
+
+ /* ... */
+
+ if (xpds_data[card_num].dlci_lmi == XPDS_DLCI_LMI_LT ||
+ xpds_data[card_num].dlci_lmi == XPDS_DLCI_LMI_NT_BIDIRECTIONAL){
+ dprintk (KERN_DEBUG "sending LMI packet in xpds_dlci_handle_status()\n");
+ xpds_tx (reply_skb->data, reply_skb->len, dev);
+ dev_kfree_skb(reply_skb);
+ }
+
+ /* This is freed by the caller. */
+ /* dev_kfree_skb(skb); */
+}
+
+void
+xpds_dlci_receive(struct sk_buff *skb, struct net_device *dev)
+{
+ int card_num;
+ struct frhdr *hdr;
+ struct frad_local *flp;
+ int process, header, actual_header_size;
+
+ dprintk(KERN_DEBUG "xpds_dlci_receive (%p, %p)\n", skb, dev);
+ card_num = dev - xpds_devs;
+ flp = &(xpds_data[card_num].frad_data);
+ hdr = (struct frhdr *) (skb->data);
+ process = 0;
+ header = sizeof(hdr->addr_control);
+ skb->dev = dev;
+
+ actual_header_size = skb->tail - skb->data;
+ if (actual_header_size < header + 2) {
+ nrprintk (KERN_NOTICE "%s: header is too short (%d bytes)\n",
+ dev->name, skb->tail - skb->data);
+ dev_kfree_skb(skb);
+ xpds_data[card_num].stats.rx_errors++;
+ return;
+ }
+
+ if (hdr->control != FRAD_I_UI) {
+ nrprintk(KERN_NOTICE "%s: Invalid header flag 0x%02X.\n",
+ dev->name, hdr->control);
+ xpds_data[card_num].stats.rx_errors++;
+ } else {
+ switch (hdr->IP_NLPID) {
+ case FRAD_P_PADDING:
+ if (hdr->NLPID != FRAD_P_SNAP) {
+ nrprintk(KERN_NOTICE
+ "%s: Unsupported NLPID 0x%02X.\n",
+ dev->name, hdr->NLPID);
+ xpds_data[card_num].stats.rx_errors++;
+ break;
+ }
+
+ if (actual_header_size < sizeof (struct frhdr)) {
+ nrprintk (KERN_NOTICE "%s: header is too short (%d bytes, IP_NLPID == FRAD_P_PADDING)\n", dev->name, skb->tail - skb->data);
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ if (hdr->OUI[0] == FRAD_OUI_BRIDGED_0 &&
+ hdr->OUI[1] == FRAD_OUI_BRIDGED_1 &&
+ hdr->OUI[2] == FRAD_OUI_BRIDGED_2) {
+ /* bridged ethernet */
+
+ struct ethhdr *ehdr;
+
+ header = sizeof(struct frhdr);
+ ehdr =
+ (struct ethhdr *) (skb->data + header);
+ skb->protocol = ntohs(ehdr->h_proto);
+ /* skb->mac.raw = (char *)&(ehdr->h_dest); */
+ dprintk(KERN_DEBUG
+ "hdr->NLPID = FRAD_P_SNAP (%02X), protocol = 0x%04x\n",
+ FRAD_P_SNAP, skb->protocol);
+ process = 1;
+ break;
+ }
+
+ if (hdr->OUI[0] + hdr->OUI[1] + hdr->OUI[2] != 0) {
+ nrprintk(KERN_NOTICE
+ "%s: Unsupported organizationally unique identifier 0x%02X-%02X-%02X.\n",
+ dev->name, hdr->OUI[0], hdr->OUI[1],
+ hdr->OUI[2]);
+ xpds_data[card_num].stats.rx_errors++;
+ break;
+ }
+
+ /* at this point, it's an EtherType frame */
+ header = sizeof(struct frhdr);
+ /* Already in network order ! */
+ skb->protocol = hdr->PID;
+ process = 1;
+ break;
+
+ case FRAD_P_IP:
+ dprintk(KERN_DEBUG
+ "hdr->IP_NLPID = FRAD_P_IP (%02X)\n",
+ FRAD_P_IP);
+ header =
+ sizeof(hdr->addr_control) +
+ sizeof(hdr->control) + sizeof(hdr->IP_NLPID);
+ if (actual_header_size < header) {
+ nrprintk (KERN_NOTICE "%s: header is too short (%d bytes, IP_NLPID == FRAD_P_IP)\n", dev->name, skb->tail - skb->data);
+ xpds_data[card_num].stats.rx_errors++;
+ } else {
+ skb->protocol = htons(ETH_P_IP);
+ process = 1;
+ }
+ break;
+
+ case FRAD_P_Q933:
+ /* status / status enquiry message */
+ dprintk(KERN_DEBUG
+ "hdr->IP_NLPID = FRAD_P_Q933 (%02X)\n",
+ FRAD_P_Q933);
+ xpds_dlci_handle_status(skb, dev);
+ break;
+ case FRAD_P_SNAP:
+ dprintk(KERN_DEBUG
+ "hdr->IP_NLPID = FRAD_P_SNAP (%02X)\n",
+ FRAD_P_SNAP);
+ skb->protocol = htons(ETH_P_ARP);
+ process = 1;
+ break;
+ case FRAD_P_CLNP:
+ nrprintk(KERN_NOTICE
+ "%s: Unsupported NLPID 0x%02X.\n",
+ dev->name, hdr->pad);
+ xpds_data[card_num].stats.rx_errors++;
+ break;
+
+ default:
+ nrprintk(KERN_NOTICE
+ "%s: Invalid pad byte 0x%02X.\n", dev->name,
+ hdr->pad);
+ xpds_data[card_num].stats.rx_errors++;
+ break;
+ }
+ }
+
+ if (process) {
+ dprintk(KERN_DEBUG "%s: header size = %d\n", dev->name,
+ header);
+ if (xpds_data[card_num].bridged_ethernet) {
+ skb_pull(skb, header);
+ } else {
+ struct ethhdr *ehdr;
+ skb_pull(skb, header);
+ skb_push(skb, sizeof(struct ethhdr));
+ ehdr = (struct ethhdr *) skb->data;
+ memcpy(ehdr->h_dest,
+ xpds_data[card_num].serial_data.mac_address,
+ sizeof(ehdr->h_dest));
+ /* +++ */
+ memset(ehdr->h_source, 0xff,
+ sizeof(ehdr->h_source));
+ /* +++ */
+ ehdr->h_proto = skb->protocol;
+ }
+ skb->ip_summed = CHECKSUM_NONE;
+#if DEBUG
+ {
+ long i, skblen;
+
+ dprintk(KERN_DEBUG
+ "%s: packet of length %ld to be given to netif_rx():", dev->name, (long) (skb->len));
+ skblen = skb->len > 256 ? 256 : skb->len;
+ for (i = 0; i < skblen; i++) {
+ dprintk(" %02x", skb->data[i]);
+ }
+ if (skb->len > skblen) dprintk (" ...");
+ dprintk("\n");
+ dprintk(KERN_DEBUG
+ "%s: skb->head = %p, skb->data = %p, skb->tail = %p, skb->end = %p\n",
+ dev->name, skb->head, skb->data, skb->tail,
+ skb->end);
+ }
+#endif
+ skb->protocol = eth_type_trans(skb, dev);
+ dprintk(KERN_DEBUG "calling netif_rx (%p)\n", skb);
+ netif_rx(skb);
+ } else {
+ dev_kfree_skb(skb);
+ }
+ dprintk(KERN_DEBUG "xpds_dlci_receive done\n");
+}
+
+static unsigned int
+xpds_dlci_add_fr_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, u8 * hdr)
+{
+ unsigned hlen;
+ short dlci;
+ struct frad_local *flp;
+ int card_num;
+ struct frhdr *frhdr;
+
+ frhdr = (struct frhdr *) hdr;
+ card_num = dev - xpds_devs;
+ dprintk(KERN_DEBUG "%s: adding frame relay header\n", dev->name);
+ flp = &(xpds_data[card_num].frad_data);
+ dlci = xpds_data[card_num].dlci;
+ hdr[0] = ((dlci >> 4) << 2) | xpds_data[card_num].dlci_cr;
+ hdr[1] = (dlci << 4) | 1;
+
+ frhdr->control = FRAD_I_UI;
+ dprintk(KERN_DEBUG "%s: type = 0x%04x\n", dev->name, type);
+ switch (type) {
+ case ETH_P_IP:
+ case ETH_P_ARP:
+ if (xpds_data[card_num].bridged_ethernet) {
+ frhdr->pad = FRAD_P_PADDING;
+ frhdr->NLPID = FRAD_P_SNAP;
+ frhdr->OUI[0] = FRAD_OUI_BRIDGED_0;
+ frhdr->OUI[1] = FRAD_OUI_BRIDGED_1;
+ frhdr->OUI[2] = FRAD_OUI_BRIDGED_2;
+ frhdr->PID = htons(FRAD_PID);
+ hlen = sizeof(*frhdr);
+ } else {
+ frhdr->IP_NLPID = FRAD_P_IP;
+ hlen =
+ sizeof(frhdr->addr_control) +
+ sizeof(frhdr->control) +
+ sizeof(frhdr->IP_NLPID);
+ }
+ break;
+
+ /* feel free to add other types, if necessary */
+
+ default:
+ frhdr->pad = FRAD_P_PADDING;
+ frhdr->NLPID = FRAD_P_SNAP;
+ memset(frhdr->OUI, 0, sizeof(frhdr->OUI));
+ frhdr->PID = htons(type);
+ hlen = sizeof(*frhdr);
+ break;
+ }
+
+ dprintk(KERN_DEBUG "type = 0x%02x, bridged = %d, hlen = %d\n",
+ type, xpds_data[card_num].bridged_ethernet, hlen);
+
+#if DEBUG
+ {
+ int i;
+
+ dprintk(KERN_DEBUG "dlci_header added:");
+ for (i = 0; i < hlen; i++) {
+ dprintk(" %02x", ((u8 *) hdr)[i]);
+ }
+ dprintk("\n");
+ }
+#endif
+ return hlen;
+}
+
+int
+xpds_dlci_transmit(struct sk_buff *skb, struct net_device *dev)
+{
+ u8 *hdr, *buffer;
+ int card_num, rc;
+ unsigned int ptr, len;
+
+ dprintk(KERN_DEBUG "xpds_dlci_transmit (%p, %p)\n", skb, dev);
+ card_num = dev - xpds_devs;
+
+ buffer = kmalloc (skb->len + sizeof(struct frhdr), GFP_ATOMIC);
+ if (buffer == NULL) {
+ printk (KERN_ERR "%s: failed to allocate buffer in xpds_dlci_transmit()\n", xpds_devs[card_num].name);
+ return -ENOMEM;
+ }
+ hdr = buffer;
+ ptr = xpds_dlci_add_fr_header(skb, dev, ntohs(skb->protocol), hdr);
+
+ if (xpds_data[card_num].bridged_ethernet) {
+ memcpy(buffer + ptr, skb->data,
+ sizeof(struct ethhdr));
+ ptr += sizeof(struct ethhdr);
+ }
+ memcpy(buffer + ptr,
+ skb->data + sizeof(struct ethhdr),
+ skb->len - sizeof(struct ethhdr));
+ len = ptr + skb->len - sizeof(struct ethhdr);
+
+ rc = xpds_tx(buffer, len, dev);
+ kfree (buffer);
+
+ if (rc) {
+ if (rc != -EBUSY) xpds_data[card_num].stats.tx_errors++;
+ } else {
+ xpds_data[card_num].stats.tx_packets++;
+ dev_kfree_skb(skb);
+ }
+
+ dprintk(KERN_DEBUG "xpds_dlci_transmit done\n");
+ return rc;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)