/* * Frame-relay protocol module for the COMX driver * for Linux 2.2.X * * Original author: Tivadar Szemethy * Maintainer: Gergely Madarasz * * Copyright (C) 1998-1999 ITConsult-Pro Co. * * Contributors: * Arnaldo Carvalho de Melo (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 #include #include #include #include #include #include #include #include #include #include #include "comx.h" #include "comxhw.h" MODULE_AUTHOR("Author: Tivadar Szemethy "); 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; newskb->dev=fr->master; dev_queue_xmit(newskb); ch->stats.tx_bytes += skb->len; ch->stats.tx_packets++; dev_kfree_skb(skb); } else { netif_stop_queue(dev); 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_stop_queue(sdev); } } switch(mch->HW_send_packet(dev, skb)) { case FRAME_QUEUED: netif_wake_queue(dev); break; case FRAME_ACCEPTED: case FRAME_DROPPED: break; case FRAME_ERROR: printk(KERN_ERR "%s: Transmit frame error (len %d)\n", dev->name, skb->len); break; } } return 0; } static int fr_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { struct comx_channel *ch = dev->priv; struct fr_data *fr = ch->LINE_privdata; skb_push(skb, dev->hard_header_len); /* Put in DLCI */ skb->data[0] = (fr->dlci & (1024 - 15)) >> 2; skb->data[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1 skb->data[2] = FRAD_UI; skb->data[3] = NLPID_IP; return dev->hard_header_len; } static int fr_statistics(struct net_device *dev, char *page) { struct comx_channel *ch = dev->priv; struct fr_data *fr = ch->LINE_privdata; int len = 0; if (fr->master == dev) { struct proc_dir_entry *dir = ch->procdir->parent->subdir; struct net_device *sdev; struct comx_channel *sch; struct fr_data *sfr; int slaves = 0; len += sprintf(page + len, "This is a Frame Relay master device\nSlaves: "); 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 != dev)) { slaves++; len += sprintf(page + len, "%s ", sdev->name); } } len += sprintf(page + len, "%s\n", slaves ? "" : "(none)"); if (fr->keepa_freq) { len += sprintf(page + len, "Line keepalive (value %d) " "status %s [%d]\n", fr->keepa_freq, ch->line_status & PROTO_LOOP ? "LOOP" : ch->line_status & PROTO_UP ? "UP" : "DOWN", fr->keepalivecnt); } else { len += sprintf(page + len, "Line keepalive protocol " "is not set\n"); } } else { // if slave len += sprintf(page + len, "This is a Frame Relay slave device, master: %s\n", fr->master ? fr->master->name : "(not set)"); } return len; } static int fr_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { struct proc_dir_entry *file = (struct proc_dir_entry *)data; struct net_device *dev = file->parent->data; struct comx_channel *ch = dev->priv; struct fr_data *fr = NULL; int len = 0; if (ch) { fr = ch->LINE_privdata; } if (strcmp(file->name, FILENAME_DLCI) == 0) { len = sprintf(page, "%04d\n", fr->dlci); } else if (strcmp(file->name, FILENAME_MASTER) == 0) { len = sprintf(page, "%-9s\n", fr->master ? fr->master->name : "(none)"); } else if (strcmp(file->name, FILENAME_KEEPALIVE) == 0) { len = fr->keepa_freq ? sprintf(page, "% 3d\n", fr->keepa_freq) : sprintf(page, "off\n"); } else { printk(KERN_ERR "comxfr: internal error, filename %s\n", file->name); return -EBADF; } if (off >= len) { *eof = 1; return 0; } *start = page + off; if (count >= len - off) *eof = 1; return min_t(int, count, len - off); } static int fr_write_proc(struct file *file, const char *buffer, u_long count, void *data) { struct proc_dir_entry *entry = (struct proc_dir_entry *)data; struct net_device *dev = entry->parent->data; struct comx_channel *ch = dev->priv; struct fr_data *fr = NULL; char *page; if (ch) { fr = ch->LINE_privdata; } if (!(page = (char *)__get_free_page(GFP_KERNEL))) { return -ENOMEM; } copy_from_user(page, buffer, count); if (*(page + count - 1) == '\n') { *(page + count - 1) = 0; } if (strcmp(entry->name, FILENAME_DLCI) == 0) { u16 dlci_new = simple_strtoul(page, NULL, 10); if (dlci_new > 1023) { printk(KERN_ERR "Invalid DLCI value\n"); } else fr->dlci = dlci_new; } else if (strcmp(entry->name, FILENAME_MASTER) == 0) { struct net_device *new_master = dev_get_by_name(page); if (new_master && new_master->type == ARPHRD_FRAD) { struct comx_channel *sch = new_master->priv; struct fr_data *sfr = sch->LINE_privdata; if (sfr && sfr->master == new_master) { if(fr->master) dev_put(fr->master); fr->master = new_master; /* Megorokli a master statuszat */ ch->line_status = sch->line_status; } } } else if (strcmp(entry->name, FILENAME_KEEPALIVE) == 0) { int keepa_new = -1; if (strcmp(page, KEEPALIVE_OFF) == 0) { keepa_new = 0; } else { keepa_new = simple_strtoul(page, NULL, 10); } if (keepa_new < 0 || keepa_new > 100) { printk(KERN_ERR "invalid keepalive\n"); } else { if (fr->keepa_freq && keepa_new != fr->keepa_freq) { fr_set_keepalive(dev, 0); } if (keepa_new) { fr_set_keepalive(dev, keepa_new); } } } else { printk(KERN_ERR "comxfr_write_proc: internal error, filename %s\n", entry->name); count = -EBADF; } free_page((unsigned long)page); return count; } static int fr_exit(struct net_device *dev) { struct comx_channel *ch = dev->priv; struct fr_data *fr = ch->LINE_privdata; struct net_device *sdev = dev; struct comx_channel *sch; struct fr_data *sfr; struct proc_dir_entry *dir = ch->procdir->parent->subdir; /* Ha lezarunk egy master-t, le kell kattintani a slave-eket is */ if (fr->master && fr->master == dev) { 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)) { dev_close(sdev); sfr->master = NULL; } } } dev->flags = 0; dev->type = 0; dev->mtu = 0; dev->hard_header_len = 0; ch->LINE_rx = NULL; ch->LINE_tx = NULL; ch->LINE_status = NULL; ch->LINE_open = NULL; ch->LINE_close = NULL; ch->LINE_xmit = NULL; ch->LINE_header = NULL; ch->LINE_rebuild_header = NULL; ch->LINE_statistics = NULL; ch->LINE_status = 0; if (fr->master != dev) { // if not master, remove dlci if(fr->master) dev_put(fr->master); remove_proc_entry(FILENAME_DLCI, ch->procdir); remove_proc_entry(FILENAME_MASTER, ch->procdir); } else { if (fr->keepa_freq) { fr_set_keepalive(dev, 0); } remove_proc_entry(FILENAME_KEEPALIVE, ch->procdir); remove_proc_entry(FILENAME_DLCI, ch->procdir); } kfree(fr); ch->LINE_privdata = NULL; MOD_DEC_USE_COUNT; return 0; } static int fr_master_init(struct net_device *dev) { struct comx_channel *ch = dev->priv; struct fr_data *fr; struct proc_dir_entry *new_file; if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), GFP_KERNEL)) == NULL) { return -ENOMEM; } memset(fr, 0, sizeof(struct fr_data)); fr->master = dev; // this means master fr->dlci = 0; // let's say default dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; dev->type = ARPHRD_FRAD; dev->mtu = 1500; dev->hard_header_len = 4; dev->addr_len = 0; ch->LINE_rx = fr_rx; ch->LINE_tx = fr_tx; ch->LINE_status = fr_status; ch->LINE_open = fr_open; ch->LINE_close = fr_close; ch->LINE_xmit = fr_xmit; ch->LINE_header = fr_header; ch->LINE_rebuild_header = NULL; ch->LINE_statistics = fr_statistics; if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, ch->procdir)) == NULL) { goto cleanup_LINE_privdata; } new_file->data = (void *)new_file; new_file->read_proc = &fr_read_proc; new_file->write_proc = &fr_write_proc; new_file->size = 5; new_file->nlink = 1; if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644, ch->procdir)) == NULL) { goto cleanup_filename_dlci; } new_file->data = (void *)new_file; new_file->read_proc = &fr_read_proc; new_file->write_proc = &fr_write_proc; new_file->size = 4; new_file->nlink = 1; fr_set_keepalive(dev, 0); MOD_INC_USE_COUNT; return 0; cleanup_filename_dlci: remove_proc_entry(FILENAME_DLCI, ch->procdir); cleanup_LINE_privdata: kfree(fr); return -EIO; } static int fr_slave_init(struct net_device *dev) { struct comx_channel *ch = dev->priv; struct fr_data *fr; struct proc_dir_entry *new_file; if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), GFP_KERNEL)) == NULL) { return -ENOMEM; } memset(fr, 0, sizeof(struct fr_data)); dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; dev->type = ARPHRD_DLCI; dev->mtu = 1500; dev->hard_header_len = 4; dev->addr_len = 0; ch->LINE_rx = fr_rx; ch->LINE_tx = fr_tx; ch->LINE_status = fr_status; ch->LINE_open = fr_open; ch->LINE_close = fr_close; ch->LINE_xmit = fr_xmit; ch->LINE_header = fr_header; ch->LINE_rebuild_header = NULL; ch->LINE_statistics = fr_statistics; if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, ch->procdir)) == NULL) { goto cleanup_LINE_privdata; } new_file->data = (void *)new_file; new_file->read_proc = &fr_read_proc; new_file->write_proc = &fr_write_proc; new_file->size = 5; new_file->nlink = 1; if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644, ch->procdir)) == NULL) { goto cleanup_filename_dlci; } new_file->data = (void *)new_file; new_file->read_proc = &fr_read_proc; new_file->write_proc = &fr_write_proc; new_file->size = 10; new_file->nlink = 1; MOD_INC_USE_COUNT; return 0; cleanup_filename_dlci: remove_proc_entry(FILENAME_DLCI, ch->procdir); cleanup_LINE_privdata: kfree(fr); return -EIO; } static int dlci_open(struct net_device *dev) { struct comx_channel *ch = dev->priv; ch->init_status |= HW_OPEN; MOD_INC_USE_COUNT; return 0; } static int dlci_close(struct net_device *dev) { struct comx_channel *ch = dev->priv; ch->init_status &= ~HW_OPEN; MOD_DEC_USE_COUNT; return 0; } static int dlci_txe(struct net_device *dev) { struct comx_channel *ch = dev->priv; struct fr_data *fr = ch->LINE_privdata; if (!fr->master) { return 0; } ch = fr->master->priv; fr = ch->LINE_privdata; return ch->HW_txe(fr->master); } static int dlci_statistics(struct net_device *dev, char *page) { return 0; } static int dlci_init(struct net_device *dev) { struct comx_channel *ch = dev->priv; ch->HW_open = dlci_open; ch->HW_close = dlci_close; ch->HW_txe = dlci_txe; ch->HW_statistics = dlci_statistics; /* Nincs egyeb hw info, mert ugyis a fr->master-bol fog minden kiderulni */ MOD_INC_USE_COUNT; return 0; } static int dlci_exit(struct net_device *dev) { struct comx_channel *ch = dev->priv; ch->HW_open = NULL; ch->HW_close = NULL; ch->HW_txe = NULL; ch->HW_statistics = NULL; MOD_DEC_USE_COUNT; return 0; } static int dlci_dump(struct net_device *dev) { printk(KERN_INFO "dlci_dump %s, HOGY MI ???\n", dev->name); return -1; } static struct comx_protocol fr_master_protocol = { name: "frad", version: VERSION, encap_type: ARPHRD_FRAD, line_init: fr_master_init, line_exit: fr_exit, }; static struct comx_protocol fr_slave_protocol = { name: "ietf-ip", version: VERSION, encap_type: ARPHRD_DLCI, line_init: fr_slave_init, line_exit: fr_exit, }; static struct comx_hardware fr_dlci = { name: "dlci", version: VERSION, hw_init: dlci_init, hw_exit: dlci_exit, hw_dump: dlci_dump, }; #ifdef MODULE #define comx_proto_fr_init init_module #endif int __init comx_proto_fr_init(void) { int ret; if ((ret = comx_register_hardware(&fr_dlci))) { return ret; } if ((ret = comx_register_protocol(&fr_master_protocol))) { return ret; } return comx_register_protocol(&fr_slave_protocol); } #ifdef MODULE void cleanup_module(void) { comx_unregister_hardware(fr_dlci.name); comx_unregister_protocol(fr_master_protocol.name); comx_unregister_protocol(fr_slave_protocol.name); } #endif /* MODULE */