📄 comx-proto-fr.c
字号:
/* * Frame-relay protocol module for the COMX driver * for Linux 2.2.X * * Original author: Tivadar Szemethy <tiv@itc.hu> * Maintainer: Gergely Madarasz <gorgo@itc.hu> * * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu> * * Contributors: * Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.73) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Version 0.70 (99/06/14): * - cleaned up the source code a bit * - ported back to kernel, now works as builtin code * * Version 0.71 (99/06/25): * - use skb priorities and queues for sending keepalive * - use device queues for slave->master data transmit * - set IFF_RUNNING only line protocol up * - fixes on slave device flags * * Version 0.72 (99/07/09): * - handle slave tbusy with master tbusy (should be fixed) * - fix the keepalive timer addition/deletion * * Version 0.73 (00/08/15) * - resource release on failure at fr_master_init and * fr_slave_init */#define VERSION "0.73"#include <linux/module.h>#include <linux/version.h>#include <linux/types.h>#include <linux/sched.h>#include <linux/netdevice.h>#include <linux/proc_fs.h>#include <linux/if_arp.h>#include <linux/inetdevice.h>#include <linux/pkt_sched.h>#include <linux/init.h>#include <asm/uaccess.h>#include "comx.h"#include "comxhw.h"MODULE_AUTHOR("Author: Tivadar Szemethy <tiv@itc.hu>");MODULE_DESCRIPTION("Frame Relay protocol implementation for the COMX drivers" "for Linux kernel 2.4.X");MODULE_LICENSE("GPL");#define FRAD_UI 0x03#define NLPID_IP 0xcc#define NLPID_Q933_LMI 0x08#define NLPID_CISCO_LMI 0x09 #define Q933_ENQ 0x75#define Q933_LINESTAT 0x51#define Q933_COUNTERS 0x53#define MAXALIVECNT 3 /* No. of failures */struct fr_data { u16 dlci; struct net_device *master; char keepa_pend; char keepa_freq; char keepalivecnt, keeploopcnt; struct timer_list keepa_timer; u8 local_cnt, remote_cnt;};static struct comx_protocol fr_master_protocol;static struct comx_protocol fr_slave_protocol;static struct comx_hardware fr_dlci;static void fr_keepalive_send(struct net_device *dev) { struct comx_channel *ch = dev->priv; struct fr_data *fr = ch->LINE_privdata; struct sk_buff *skb; u8 *fr_packet; skb=alloc_skb(dev->hard_header_len + 13, GFP_ATOMIC); if(skb==NULL) return; skb_reserve(skb, dev->hard_header_len); fr_packet=(u8*)skb_put(skb, 13); fr_packet[0] = (fr->dlci & (1024 - 15)) >> 2; fr_packet[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1 fr_packet[2] = FRAD_UI; fr_packet[3] = NLPID_Q933_LMI; fr_packet[4] = 0; fr_packet[5] = Q933_ENQ; fr_packet[6] = Q933_LINESTAT; fr_packet[7] = 0x01; fr_packet[8] = 0x01; fr_packet[9] = Q933_COUNTERS; fr_packet[10] = 0x02; fr_packet[11] = ++fr->local_cnt; fr_packet[12] = fr->remote_cnt; skb->dev = dev; skb->priority = TC_PRIO_CONTROL; dev_queue_xmit(skb);}static void fr_keepalive_timerfun(unsigned long d) { struct net_device *dev = (struct net_device *)d; struct comx_channel *ch = dev->priv; struct fr_data *fr = ch->LINE_privdata; struct proc_dir_entry *dir = ch->procdir->parent->subdir; struct comx_channel *sch; struct fr_data *sfr; struct net_device *sdev; if (ch->init_status & LINE_OPEN) { if (fr->keepalivecnt == MAXALIVECNT) { comx_status(dev, ch->line_status & ~PROTO_UP); dev->flags &= ~IFF_RUNNING; for (; dir ; dir = dir->next) { if(!S_ISDIR(dir->mode)) { continue; } if ((sdev = dir->data) && (sch = sdev->priv) && (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && (sfr->master == dev) && (sdev->flags & IFF_UP)) { sdev->flags &= ~IFF_RUNNING; comx_status(sdev, sch->line_status & ~PROTO_UP); } } } if (fr->keepalivecnt <= MAXALIVECNT) { ++fr->keepalivecnt; } fr_keepalive_send(dev); } mod_timer(&fr->keepa_timer, jiffies + HZ * fr->keepa_freq);}static void fr_rx_lmi(struct net_device *dev, struct sk_buff *skb, u16 dlci, u8 nlpid) { struct comx_channel *ch = dev->priv; struct fr_data *fr = ch->LINE_privdata; struct proc_dir_entry *dir = ch->procdir->parent->subdir; struct comx_channel *sch; struct fr_data *sfr; struct net_device *sdev; if (dlci != fr->dlci || nlpid != NLPID_Q933_LMI || !fr->keepa_freq) { return; } fr->remote_cnt = skb->data[7]; if (skb->data[8] == fr->local_cnt) { // keepalive UP! fr->keepalivecnt = 0; if ((ch->line_status & LINE_UP) && !(ch->line_status & PROTO_UP)) { comx_status(dev, ch->line_status |= PROTO_UP); dev->flags |= IFF_RUNNING; for (; dir ; dir = dir->next) { if(!S_ISDIR(dir->mode)) { continue; } if ((sdev = dir->data) && (sch = sdev->priv) && (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && (sfr->master == dev) && (sdev->flags & IFF_UP)) { sdev->flags |= IFF_RUNNING; comx_status(sdev, sch->line_status | PROTO_UP); } } } }}static void fr_set_keepalive(struct net_device *dev, int keepa) { struct comx_channel *ch = dev->priv; struct fr_data *fr = ch->LINE_privdata; if (!keepa && fr->keepa_freq) { // switch off fr->keepa_freq = 0; if (ch->line_status & LINE_UP) { comx_status(dev, ch->line_status | PROTO_UP); dev->flags |= IFF_RUNNING; del_timer(&fr->keepa_timer); } return; } if (keepa) { // bekapcs if(fr->keepa_freq && (ch->line_status & LINE_UP)) { del_timer(&fr->keepa_timer); } fr->keepa_freq = keepa; fr->local_cnt = fr->remote_cnt = 0; fr->keepa_timer.expires = jiffies + HZ; fr->keepa_timer.function = fr_keepalive_timerfun; fr->keepa_timer.data = (unsigned long)dev; ch->line_status &= ~(PROTO_UP | PROTO_LOOP); dev->flags &= ~IFF_RUNNING; comx_status(dev, ch->line_status); if(ch->line_status & LINE_UP) { add_timer(&fr->keepa_timer); } }}static void fr_rx(struct net_device *dev, struct sk_buff *skb) { struct comx_channel *ch = dev->priv; struct proc_dir_entry *dir = ch->procdir->parent->subdir; struct net_device *sdev = dev; struct comx_channel *sch; struct fr_data *sfr; u16 dlci; u8 nlpid; if(skb->len <= 4 || skb->data[2] != FRAD_UI) { kfree_skb(skb); return; } /* Itt majd ki kell talalni, melyik slave kapja a csomagot */ dlci = ((skb->data[0] & 0xfc) << 2) | ((skb->data[1] & 0xf0) >> 4); if ((nlpid = skb->data[3]) == 0) { // Optional padding nlpid = skb->data[4]; skb_pull(skb, 1); } skb_pull(skb, 4); /* DLCI and header throw away */ if (ch->debug_flags & DEBUG_COMX_DLCI) { comx_debug(dev, "Frame received, DLCI: %d, NLPID: 0x%02x\n", dlci, nlpid); comx_debug_skb(dev, skb, "Contents"); } /* Megkeressuk, kihez tartozik */ for (; dir ; dir = dir->next) { if(!S_ISDIR(dir->mode)) { continue; } if ((sdev = dir->data) && (sch = sdev->priv) && (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && (sfr->master == dev) && (sfr->dlci == dlci)) { skb->dev = sdev; if (ch->debug_flags & DEBUG_COMX_DLCI) { comx_debug(dev, "Passing it to %s\n",sdev->name); } if (dev != sdev) { sch->stats.rx_packets++; sch->stats.rx_bytes += skb->len; } break; } } switch(nlpid) { case NLPID_IP: skb->protocol = htons(ETH_P_IP); skb->mac.raw = skb->data; comx_rx(sdev, skb); break; case NLPID_Q933_LMI: fr_rx_lmi(dev, skb, dlci, nlpid); default: kfree_skb(skb); break; }}static int fr_tx(struct net_device *dev) { struct comx_channel *ch = dev->priv; struct proc_dir_entry *dir = ch->procdir->parent->subdir; struct net_device *sdev; struct comx_channel *sch; struct fr_data *sfr; int cnt = 1; /* Ha minden igaz, 2 helyen fog allni a tbusy: a masternel, es annal a slave-nel aki eppen kuldott. Egy helyen akkor all, ha a master kuldott. Ez megint jo lesz majd, ha utemezni akarunk */ /* This should be fixed, the slave tbusy should be set when the masters queue is full and reset when not */ for (; dir ; dir = dir->next) { if(!S_ISDIR(dir->mode)) { continue; } if ((sdev = dir->data) && (sch = sdev->priv) && (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && (sfr->master == dev) && (netif_queue_stopped(sdev))) { netif_wake_queue(sdev); cnt++; } } netif_wake_queue(dev); return 0;}static void fr_status(struct net_device *dev, unsigned short status){ struct comx_channel *ch = dev->priv; struct fr_data *fr = ch->LINE_privdata; struct proc_dir_entry *dir = ch->procdir->parent->subdir; struct net_device *sdev; struct comx_channel *sch; struct fr_data *sfr; if (status & LINE_UP) { if (!fr->keepa_freq) { status |= PROTO_UP; } } else { status &= ~(PROTO_UP | PROTO_LOOP); } if (dev == fr->master && fr->keepa_freq) { if (status & LINE_UP) { fr->keepa_timer.expires = jiffies + HZ; add_timer(&fr->keepa_timer); fr->keepalivecnt = MAXALIVECNT + 1; fr->keeploopcnt = 0; } else { del_timer(&fr->keepa_timer); } } /* Itt a status valtozast vegig kell vinni az osszes slave-n */ for (; dir ; dir = dir->next) { if(!S_ISDIR(dir->mode)) { continue; } if ((sdev = dir->data) && (sch = sdev->priv) && (sdev->type == ARPHRD_FRAD || sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && (sfr->master == dev)) { if(status & LINE_UP) { netif_wake_queue(sdev); } comx_status(sdev, status); if(status & (PROTO_UP | PROTO_LOOP)) { dev->flags |= IFF_RUNNING; } else { dev->flags &= ~IFF_RUNNING; } } }}static int fr_open(struct net_device *dev){ struct comx_channel *ch = dev->priv; struct fr_data *fr = ch->LINE_privdata; struct proc_dir_entry *comxdir = ch->procdir; struct comx_channel *mch; if (!(ch->init_status & HW_OPEN)) { return -ENODEV; } if ((ch->hardware == &fr_dlci && ch->protocol != &fr_slave_protocol) || (ch->protocol == &fr_slave_protocol && ch->hardware != &fr_dlci)) { printk(KERN_ERR "Trying to open an improperly set FR interface, giving up\n"); return -EINVAL; } if (!fr->master) { return -ENODEV; } mch = fr->master->priv; if (fr->master != dev && (!(mch->init_status & LINE_OPEN) || (mch->protocol != &fr_master_protocol))) { printk(KERN_ERR "Master %s is inactive, or incorrectly set up, " "unable to open %s\n", fr->master->name, dev->name); return -ENODEV; } ch->init_status |= LINE_OPEN; ch->line_status &= ~(PROTO_UP | PROTO_LOOP); dev->flags &= ~IFF_RUNNING; if (fr->master == dev) { if (fr->keepa_freq) { fr->keepa_timer.function = fr_keepalive_timerfun; fr->keepa_timer.data = (unsigned long)dev; add_timer(&fr->keepa_timer); } else { if (ch->line_status & LINE_UP) { ch->line_status |= PROTO_UP; dev->flags |= IFF_RUNNING; } } } else { ch->line_status = mch->line_status; if(fr->master->flags & IFF_RUNNING) { dev->flags |= IFF_RUNNING; } } for (; comxdir ; comxdir = comxdir->next) { if (strcmp(comxdir->name, FILENAME_DLCI) == 0 || strcmp(comxdir->name, FILENAME_MASTER) == 0 || strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) { comxdir->mode = S_IFREG | 0444; } }// comx_status(dev, ch->line_status); return 0;}static int fr_close(struct net_device *dev){ struct comx_channel *ch = dev->priv; struct fr_data *fr = ch->LINE_privdata; struct proc_dir_entry *comxdir = ch->procdir; if (fr->master == dev) { // Ha master struct proc_dir_entry *dir = ch->procdir->parent->subdir; struct net_device *sdev = dev; struct comx_channel *sch; struct fr_data *sfr; if (!(ch->init_status & HW_OPEN)) { return -ENODEV; } if (fr->keepa_freq) { del_timer(&fr->keepa_timer); } for (; dir ; dir = dir->next) { if(!S_ISDIR(dir->mode)) { continue; } if ((sdev = dir->data) && (sch = sdev->priv) && (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) && (sfr->master == dev) && (sch->init_status & LINE_OPEN)) { dev_close(sdev); } } } ch->init_status &= ~LINE_OPEN; ch->line_status &= ~(PROTO_UP | PROTO_LOOP); dev->flags &= ~IFF_RUNNING; for (; comxdir ; comxdir = comxdir->next) { if (strcmp(comxdir->name, FILENAME_DLCI) == 0 || strcmp(comxdir->name, FILENAME_MASTER) == 0 || strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) { comxdir->mode = S_IFREG | 0444; } } return 0;}static int fr_xmit(struct sk_buff *skb, struct net_device *dev){ struct comx_channel *ch = dev->priv; struct comx_channel *sch, *mch; struct fr_data *fr = ch->LINE_privdata; struct fr_data *sfr; struct net_device *sdev; struct proc_dir_entry *dir = ch->procdir->parent->subdir; if (!fr->master) { printk(KERN_ERR "BUG: fr_xmit without a master!!! dev: %s\n", dev->name); return 0; } mch = fr->master->priv; /* Ennek majd a slave utemezeskor lesz igazan jelentosege */ if (ch->debug_flags & DEBUG_COMX_DLCI) { comx_debug_skb(dev, skb, "Sending frame"); } if (dev != fr->master) { struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC); if (!newskb) return -ENOMEM;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -