patch-2.2.14 linux/drivers/net/comx.c
Next file: linux/drivers/net/comx.h
Previous file: linux/drivers/net/comx-proto-ppp.c
Back to the patch index
Back to the overall index
- Lines: 1235
- Date:
Tue Jan 4 10:12:17 2000
- Orig file:
v2.2.13/linux/drivers/net/comx.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.2.13/linux/drivers/net/comx.c linux/drivers/net/comx.c
@@ -0,0 +1,1234 @@
+/*
+ * Device driver framework for the COMX line of synchronous serial boards
+ *
+ * for Linux kernel 2.2.X
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co.
+ *
+ * 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.80 (99/06/11):
+ * - clean up source code (playing a bit of indent)
+ * - port back to kernel, add support for non-module versions
+ * - add support for board resets when channel protocol is down
+ * - reset the device structure after protocol exit
+ * the syncppp driver needs it
+ * - add support for /proc/comx/protocols and
+ * /proc/comx/boardtypes
+ *
+ * Version 0.81 (99/06/21):
+ * - comment out the board reset support code, the locomx
+ * driver seems not buggy now
+ * - printk() levels fixed
+ *
+ * Version 0.82 (99/07/08):
+ * - Handle stats correctly if the lowlevel driver is
+ * is not a comx one (locomx - z85230)
+ *
+ * Version 0.83 (99/07/15):
+ * - reset line_status when interface is down
+ *
+ */
+
+#define VERSION "0.82"
+
+#include <linux/config.h>
+#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 <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#ifndef CONFIG_PROC_FS
+#error For now, COMX really needs the /proc filesystem
+#endif
+
+#include "comx.h"
+#include "syncppp.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters");
+
+extern int comx_hw_comx_init(void);
+extern int comx_hw_locomx_init(void);
+extern int comx_hw_mixcom_init(void);
+extern int comx_proto_hdlc_init(void);
+extern int comx_proto_ppp_init(void);
+extern int comx_proto_syncppp_init(void);
+extern int comx_proto_lapb_init(void);
+extern int comx_proto_fr_init(void);
+
+static struct comx_hardware *comx_channels = NULL;
+static struct comx_protocol *comx_lines = NULL;
+
+struct inode_operations comx_normal_inode_ops;
+static struct inode_operations comx_root_inode_ops; // for mkdir
+static struct inode_operations comx_debug_inode_ops; // mas a file_ops
+static struct file_operations comx_normal_file_ops; // with open/relase
+static struct file_operations comx_debug_file_ops; // with lseek+read
+
+static void comx_delete_dentry(struct dentry *dentry);
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir);
+
+static void comx_fill_inode(struct inode *inode, int fill);
+
+static struct dentry_operations comx_dentry_operations = {
+ NULL, /* revalidate */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ &comx_delete_dentry /* d_delete */
+};
+
+
+struct proc_dir_entry comx_root_dir = {
+ 0, 4, "comx",
+ S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, 2, 0, 0,
+ 0, &comx_root_inode_ops,
+ NULL, comx_fill_inode,
+ NULL, &proc_root, NULL
+};
+
+struct comx_debugflags_struct comx_debugflags[] = {
+ { "comx_rx", DEBUG_COMX_RX },
+ { "comx_tx", DEBUG_COMX_TX },
+ { "hw_tx", DEBUG_HW_TX },
+ { "hw_rx", DEBUG_HW_RX },
+ { "hdlc_keepalive", DEBUG_HDLC_KEEPALIVE },
+ { "comxppp", DEBUG_COMX_PPP },
+ { "comxlapb", DEBUG_COMX_LAPB },
+ { "dlci", DEBUG_COMX_DLCI },
+ { NULL, 0 }
+};
+
+static void comx_fill_inode(struct inode *inode, int fill)
+{
+ if (fill)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+
+
+int comx_debug(struct device *dev, char *fmt, ...)
+{
+ struct comx_channel *ch = dev->priv;
+ char *page,*str;
+ va_list args;
+ int len;
+
+ if (!ch->debug_area) return 0;
+
+ if (!(page = (char *)__get_free_page(GFP_ATOMIC))) return -ENOMEM;
+
+ va_start(args, fmt);
+ len = vsprintf(str = page, fmt, args);
+ va_end(args);
+
+ if (len >= PAGE_SIZE) {
+ printk(KERN_ERR "comx_debug: PANIC! len = %d !!!\n", len);
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+
+ while (len) {
+ int to_copy;
+ int free = (ch->debug_start - ch->debug_end + ch->debug_size)
+ % ch->debug_size;
+
+ to_copy = min( free ? free : ch->debug_size,
+ min (ch->debug_size - ch->debug_end, len) );
+ memcpy(ch->debug_area + ch->debug_end, str, to_copy);
+ str += to_copy;
+ len -= to_copy;
+ ch->debug_end = (ch->debug_end + to_copy) % ch->debug_size;
+ if (ch->debug_start == ch->debug_end) // Full ? push start away
+ ch->debug_start = (ch->debug_start + len + 1) %
+ ch->debug_size;
+ ch->debug_file->size = (ch->debug_end - ch->debug_start +
+ ch->debug_size) % ch->debug_size;
+ }
+
+ free_page((unsigned long)page);
+ return 0;
+}
+
+int comx_debug_skb(struct device *dev, struct sk_buff *skb, char *msg)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (!ch->debug_area) return 0;
+ if (!skb) comx_debug(dev, "%s: %s NULL skb\n\n", dev->name, msg);
+ if (!skb->len) comx_debug(dev, "%s: %s empty skb\n\n", dev->name, msg);
+
+ return comx_debug_bytes(dev, skb->data, skb->len, msg);
+}
+
+int comx_debug_bytes(struct device *dev, unsigned char *bytes, int len,
+ char *msg)
+{
+ int pos = 0;
+ struct comx_channel *ch = dev->priv;
+
+ if (!ch->debug_area) return 0;
+
+ comx_debug(dev, "%s: %s len %d\n", dev->name, msg, len);
+
+ while (pos != len) {
+ char line[80];
+ int i = 0;
+
+ memset(line, 0, 80);
+ sprintf(line,"%04d ", pos);
+ do {
+ sprintf(line + 5 + (pos % 16) * 3, "%02x", bytes[pos]);
+ sprintf(line + 60 + (pos % 16), "%c",
+ isprint(bytes[pos]) ? bytes[pos] : '.');
+ pos++;
+ } while (pos != len && pos % 16);
+
+ while ( i++ != 78 ) if (line[i] == 0) line[i] = ' ';
+ line[77] = '\n';
+ line[78] = 0;
+
+ comx_debug(dev, "%s", line);
+ }
+ comx_debug(dev, "\n");
+ return 0;
+}
+
+static void comx_loadavg_timerfun(unsigned long d)
+{
+ struct device *dev = (struct device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ ch->avg_bytes[ch->loadavg_counter] = ch->current_stats->rx_bytes;
+ ch->avg_bytes[ch->loadavg_counter + ch->loadavg_size] =
+ ch->current_stats->tx_bytes;
+
+ ch->loadavg_counter = (ch->loadavg_counter + 1) % ch->loadavg_size;
+
+ mod_timer(&ch->loadavg_timer,jiffies + HZ * ch->loadavg[0]);
+}
+
+#if 0
+static void comx_reset_timerfun(unsigned long d)
+{
+ struct device *dev = (struct device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ if(!(ch->line_status & (PROTO_LOOP | PROTO_UP))) {
+ if(test_and_set_bit(0,&ch->reset_pending) && ch->HW_reset) {
+ ch->HW_reset(dev);
+ }
+ }
+
+ mod_timer(&ch->reset_timer, jiffies + HZ * ch->reset_timeout);
+}
+#endif
+
+static int comx_open(struct device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+ int ret=0;
+
+ if (!ch->protocol || !ch->hardware) return -ENODEV;
+
+ if ((ret = ch->HW_open(dev))) return ret;
+ if ((ret = ch->LINE_open(dev))) {
+ ch->HW_close(dev);
+ return ret;
+ };
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+ strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+ comxdir->mode = S_IFREG | 0444;
+ }
+
+#if 0
+ ch->reset_pending = 1;
+ ch->reset_timeout = 30;
+ ch->reset_timer.function = comx_reset_timerfun;
+ ch->reset_timer.data = (unsigned long)dev;
+ ch->reset_timer.expires = jiffies + HZ * ch->reset_timeout;
+ add_timer(&ch->reset_timer);
+#endif
+
+ return 0;
+}
+
+static int comx_close(struct device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+ int ret = -ENODEV;
+
+ if (test_and_clear_bit(0, &ch->lineup_pending)) {
+ del_timer(&ch->lineup_timer);
+ }
+
+#if 0
+ del_timer(&ch->reset_timer);
+#endif
+
+ if (ch->init_status & LINE_OPEN && ch->protocol && ch->LINE_close) {
+ ret = ch->LINE_close(dev);
+ }
+
+ if (ret) return ret;
+
+ if (ch->init_status & HW_OPEN && ch->hardware && ch->HW_close) {
+ ret = ch->HW_close(dev);
+ }
+
+ ch->line_status=0;
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+ strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+ comxdir->mode = S_IFREG | 0644;
+ }
+
+ return ret;
+}
+
+void comx_status(struct device *dev, int status)
+{
+ struct comx_channel *ch = dev->priv;
+
+#if 0
+ if(status & (PROTO_UP | PROTO_LOOP)) {
+ clear_bit(0,&ch->reset_pending);
+ }
+#endif
+
+ if (dev->flags & IFF_UP) {
+ printk(KERN_NOTICE "Interface %s: modem status %s, line protocol %s\n",
+ dev->name, status & LINE_UP ? "UP" : "DOWN",
+ status & PROTO_LOOP ? "LOOP" : status & PROTO_UP ?
+ "UP" : "DOWN");
+ }
+
+ ch->line_status = status;
+}
+
+static int comx_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ int rc;
+
+ if (skb->len > dev->mtu + dev->hard_header_len) {
+ printk(KERN_ERR "comx_xmit: %s: skb->len %d > dev->mtu %d\n", dev->name,
+ (int)skb->len, dev->mtu);
+ }
+
+ if (ch->debug_flags & DEBUG_COMX_TX) {
+ comx_debug_skb(dev, skb, "comx_xmit skb");
+ }
+
+ rc=ch->LINE_xmit(skb, dev);
+// if (!rc) dev_kfree_skb(skb);
+
+ return rc;
+}
+
+static int comx_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_header) {
+ return (ch->LINE_header(skb, dev, type, daddr, saddr, len));
+ } else {
+ return 0;
+ }
+}
+
+static int comx_rebuild_header(struct sk_buff *skb)
+{
+ struct device *dev = skb->dev;
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_rebuild_header) {
+ return(ch->LINE_rebuild_header(skb));
+ } else {
+ return 0;
+ }
+}
+
+int comx_rx(struct device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->debug_flags & DEBUG_COMX_RX) {
+ comx_debug_skb(dev, skb, "comx_rx skb");
+ }
+ if (skb) {
+ netif_rx(skb);
+ }
+ return 0;
+}
+
+static struct net_device_stats *comx_stats(struct device *dev)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+ return ch->current_stats;
+}
+
+void comx_lineup_func(unsigned long d)
+{
+ struct device *dev = (struct device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ del_timer(&ch->lineup_timer);
+ clear_bit(0, &ch->lineup_pending);
+
+ if (ch->LINE_status) {
+ ch->LINE_status(dev, ch->line_status |= LINE_UP);
+ }
+}
+
+#define LOADAVG(avg, off) (int) \
+ ((ch->avg_bytes[(ch->loadavg_counter - 1 + ch->loadavg_size * 2) \
+ % ch->loadavg_size + off] - ch->avg_bytes[(ch->loadavg_counter - 1 \
+ - ch->loadavg[avg] / ch->loadavg[0] + ch->loadavg_size * 2) \
+ % ch->loadavg_size + off]) / ch->loadavg[avg] * 8)
+
+static int comx_statistics(struct device *dev, char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ int len = 0;
+ int tmp;
+ int i = 0;
+ char tmpstr[20];
+ int tmpstrlen = 0;
+
+ len += sprintf(page + len, "Interface administrative status is %s, "
+ "modem status is %s, protocol is %s\n",
+ dev->flags & IFF_UP ? "UP" : "DOWN",
+ ch->line_status & LINE_UP ? "UP" : "DOWN",
+ ch->line_status & PROTO_LOOP ? "LOOP" :
+ ch->line_status & PROTO_UP ? "UP" : "DOWN");
+ len += sprintf(page + len, "Modem status changes: %lu, Transmitter status "
+ "is %s, tbusy: %d\n", ch->current_stats->tx_carrier_errors, ch->HW_txe ?
+ ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", (int)dev->tbusy);
+ len += sprintf(page + len, "Interface load (input): %d / %d / %d bits/s (",
+ LOADAVG(0,0), LOADAVG(1, 0), LOADAVG(2, 0));
+ tmpstr[0] = 0;
+ for (i=0; i != 3; i++) {
+ char tf;
+
+ tf = ch->loadavg[i] % 60 == 0 &&
+ ch->loadavg[i] / 60 > 0 ? 'm' : 's';
+ tmpstrlen += sprintf(tmpstr + tmpstrlen, "%d%c%s",
+ ch->loadavg[i] / (tf == 'm' ? 60 : 1), tf,
+ i == 2 ? ")\n" : "/");
+ }
+ len += sprintf(page + len,
+ "%s (output): %d / %d / %d bits/s (%s", tmpstr,
+ LOADAVG(0,ch->loadavg_size), LOADAVG(1, ch->loadavg_size),
+ LOADAVG(2, ch->loadavg_size), tmpstr);
+
+ len += sprintf(page + len, "Debug flags: ");
+ tmp = len; i = 0;
+ while (comx_debugflags[i].name) {
+ if (ch->debug_flags & comx_debugflags[i].value)
+ len += sprintf(page + len, "%s ",
+ comx_debugflags[i].name);
+ i++;
+ }
+ len += sprintf(page + len, "%s\n", tmp == len ? "none" : "");
+
+ len += sprintf(page + len, "RX errors: len: %lu, overrun: %lu, crc: %lu, "
+ "aborts: %lu\n buffer overrun: %lu, pbuffer overrun: %lu\n"
+ "TX errors: underrun: %lu\n",
+ ch->current_stats->rx_length_errors, ch->current_stats->rx_over_errors,
+ ch->current_stats->rx_crc_errors, ch->current_stats->rx_frame_errors,
+ ch->current_stats->rx_missed_errors, ch->current_stats->rx_fifo_errors,
+ ch->current_stats->tx_fifo_errors);
+
+ if (ch->LINE_statistics && (ch->init_status & LINE_OPEN)) {
+ len += ch->LINE_statistics(dev, page + len);
+ } else {
+ len += sprintf(page+len, "Line status: driver not initialized\n");
+ }
+ if (ch->HW_statistics && (ch->init_status & HW_OPEN)) {
+ len += ch->HW_statistics(dev, page + len);
+ } else {
+ len += sprintf(page+len, "Board status: driver not initialized\n");
+ }
+
+ return len;
+}
+
+static int comx_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_ioctl) {
+ return(ch->LINE_ioctl(dev, ifr, cmd));
+ }
+ return -EINVAL;
+}
+
+static void comx_reset_dev(struct device *dev)
+{
+ dev->open = comx_open;
+ dev->stop = comx_close;
+ dev->hard_start_xmit = comx_xmit;
+ dev->hard_header = comx_header;
+ dev->rebuild_header = comx_rebuild_header;
+ dev->get_stats = comx_stats;
+ dev->do_ioctl = comx_ioctl;
+ dev->change_mtu = NULL;
+ dev->tx_queue_len = 20;
+ dev->flags = IFF_NOARP;
+}
+
+static int comx_init_dev(struct device *dev)
+{
+ struct comx_channel *ch;
+
+ if ((ch = kmalloc(sizeof(struct comx_channel), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(ch, 0, sizeof(struct comx_channel));
+
+ ch->loadavg[0] = 5;
+ ch->loadavg[1] = 300;
+ ch->loadavg[2] = 900;
+ ch->loadavg_size = ch->loadavg[2] / ch->loadavg[0] + 1;
+ if ((ch->avg_bytes = kmalloc(ch->loadavg_size *
+ sizeof(unsigned long) * 2, GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(ch->avg_bytes, 0, ch->loadavg_size * sizeof(unsigned long) * 2);
+ ch->loadavg_counter = 0;
+ ch->loadavg_timer.function = comx_loadavg_timerfun;
+ ch->loadavg_timer.data = (unsigned long)dev;
+ ch->loadavg_timer.expires = jiffies + HZ * ch->loadavg[0];
+ add_timer(&ch->loadavg_timer);
+
+ dev->priv = (void *)ch;
+ ch->dev = dev;
+ ch->line_status &= ~LINE_UP;
+
+ ch->current_stats = &ch->stats;
+
+ comx_reset_dev(dev);
+ return 0;
+}
+
+static int comx_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 device *dev = file->parent->data;
+ struct comx_channel *ch=(struct comx_channel *)dev->priv;
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_STATUS) == 0) {
+ len = comx_statistics(dev, page);
+ } else if (strcmp(file->name, FILENAME_HARDWARE) == 0) {
+ len = sprintf(page, "%s\n", ch->hardware ?
+ ch->hardware->name : HWNAME_NONE);
+ } else if (strcmp(file->name, FILENAME_PROTOCOL) == 0) {
+ len = sprintf(page, "%s\n", ch->protocol ?
+ ch->protocol->name : PROTONAME_NONE);
+ } else if (strcmp(file->name, FILENAME_LINEUPDELAY) == 0) {
+ len = sprintf(page, "%01d\n", ch->lineup_delay);
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return( min(count, len - off) );
+}
+
+
+static int comx_root_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 comx_hardware *hw;
+ struct comx_protocol *line;
+
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_HARDWARELIST) == 0) {
+ for(hw=comx_channels;hw;hw=hw->next)
+ len+=sprintf(page+len, "%s\n", hw->name);
+ } else if (strcmp(file->name, FILENAME_PROTOCOLLIST) == 0) {
+ for(line=comx_lines;line;line=line->next)
+ len+=sprintf(page+len, "%s\n", line->name);
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return( min(count, len - off) );
+}
+
+
+
+static int comx_write_proc(struct file *file, const char *buffer, u_long count,
+ void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct device *dev = (struct device *)entry->parent->data;
+ struct comx_channel *ch=(struct comx_channel *)dev->priv;
+ char *page;
+ struct comx_hardware *hw = comx_channels;
+ struct comx_protocol *line = comx_lines;
+ char str[30];
+ int ret=0;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (count > PAGE_SIZE) {
+ printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
+ return -ENOSPC;
+ }
+
+ 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_DEBUG) == 0) {
+ int i;
+ int ret = 0;
+
+ if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ if (ch->debug_area) kfree(ch->debug_area);
+ if ((ch->debug_area = kmalloc(ch->debug_size = i,
+ GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ }
+ ch->debug_start = ch->debug_end = 0;
+ restore_flags(flags);
+ free_page((unsigned long)page);
+ return count;
+ }
+
+ if (*page != '+' && *page != '-') {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (comx_debugflags[i].value &&
+ strncmp(comx_debugflags[i].name, page + 1,
+ strlen(comx_debugflags[i].name))) {
+ i++;
+ }
+
+ if (comx_debugflags[i].value == 0) {
+ printk(KERN_ERR "Invalid debug option\n");
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ if (*page == '+') {
+ ch->debug_flags |= comx_debugflags[i].value;
+ } else {
+ ch->debug_flags &= ~comx_debugflags[i].value;
+ }
+ } else if (strcmp(entry->name, FILENAME_HARDWARE) == 0) {
+ if(strlen(page)>10) {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (hw) {
+ if (strcmp(hw->name, page) == 0) {
+ break;
+ } else {
+ hw = hw->next;
+ }
+ }
+#ifdef CONFIG_KMOD
+ if(!hw && comx_strcasecmp(HWNAME_NONE,page) != 0){
+ sprintf(str,"comx-hw-%s",page);
+ request_module(str);
+ }
+ hw=comx_channels;
+ while (hw) {
+ if (comx_strcasecmp(hw->name, page) == 0) {
+ break;
+ } else {
+ hw = hw->next;
+ }
+ }
+#endif
+
+ if (comx_strcasecmp(HWNAME_NONE, page) != 0 && !hw) {
+ free_page((unsigned long)page);
+ return -ENODEV;
+ }
+ if (ch->init_status & HW_OPEN) {
+ free_page((unsigned long)page);
+ return -EBUSY;
+ }
+ if (ch->hardware && ch->hardware->hw_exit &&
+ (ret=ch->hardware->hw_exit(dev))) {
+ free_page((unsigned long)page);
+ return ret;
+ }
+ ch->hardware = hw;
+ entry->size = strlen(page) + 1;
+ if (hw && hw->hw_init) hw->hw_init(dev);
+ } else if (strcmp(entry->name, FILENAME_PROTOCOL) == 0) {
+ if(strlen(page)>10) {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (line) {
+ if (comx_strcasecmp(line->name, page) == 0) {
+ break;
+ } else {
+ line = line->next;
+ }
+ }
+#ifdef CONFIG_KMOD
+ if(!line && comx_strcasecmp(PROTONAME_NONE, page) != 0) {
+ sprintf(str,"comx-proto-%s",page);
+ request_module(str);
+ }
+ line=comx_lines;
+ while (line) {
+ if (comx_strcasecmp(line->name, page) == 0) {
+ break;
+ } else {
+ line = line->next;
+ }
+ }
+#endif
+
+ if (comx_strcasecmp(PROTONAME_NONE, page) != 0 && !line) {
+ free_page((unsigned long)page);
+ return -ENODEV;
+ }
+
+ if (ch->init_status & LINE_OPEN) {
+ free_page((unsigned long)page);
+ return -EBUSY;
+ }
+
+ if (ch->protocol && ch->protocol->line_exit &&
+ (ret=ch->protocol->line_exit(dev))) {
+ free_page((unsigned long)page);
+ return ret;
+ }
+ ch->protocol = line;
+ entry->size = strlen(page) + 1;
+ comx_reset_dev(dev);
+ if (line && line->line_init) line->line_init(dev);
+ } else if (strcmp(entry->name, FILENAME_LINEUPDELAY) == 0) {
+ int i;
+
+ if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+ if (i >=0 && i < 10) {
+ ch->lineup_delay = i;
+ } else {
+ printk(KERN_ERR "comx: invalid lineup_delay value\n");
+ }
+ }
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static loff_t comx_debug_lseek(struct file *file, loff_t offset, int orig)
+{
+ switch(orig) {
+ case 0:
+ file->f_pos = max(0, min(offset,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ case 1:
+ file->f_pos = max(0, min(offset + file->f_pos,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ case 2:
+ file->f_pos = max(0,
+ min(offset + file->f_dentry->d_inode->i_size,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ }
+ return(file->f_pos);
+}
+
+static int comx_file_open(struct inode *inode, struct file *file)
+{
+
+ if((file->f_mode & FMODE_WRITE) && !(inode->i_mode & 0200)) {
+ return -EACCES;
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comx_file_release(struct inode *inode, struct file *file)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static ssize_t comx_debug_read(struct file *file, char *buffer, size_t count,
+ loff_t *ppos)
+{
+ struct proc_dir_entry *de = file->f_dentry->d_inode->u.generic_ip;
+ struct device *dev = de->parent->data;
+ struct comx_channel *ch = dev->priv;
+ loff_t copied = 0;
+ unsigned long flags;
+
+ save_flags(flags); cli(); // We may run into trouble when debug_area is filled
+ // from irq inside read. no problem if the buffer is
+ // large enough
+
+ while (count > 0 && ch->debug_start != ch->debug_end) {
+ int len;
+
+ len = min( (ch->debug_end - ch->debug_start + ch->debug_size)
+ %ch->debug_size, min (ch->debug_size -
+ ch->debug_start, count));
+
+ if (len) copy_to_user(buffer + copied,
+ ch->debug_area + ch->debug_start, len);
+ ch->debug_start = (ch->debug_start + len) % ch->debug_size;
+
+ de->size -= len;
+ count -= len;
+ copied += len;
+ }
+
+ restore_flags(flags);
+ return copied;
+}
+
+static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct proc_dir_entry *new_dir, *debug_file;
+ struct device *dev;
+ struct comx_channel *ch;
+
+ if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+ if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR,
+ &comx_root_dir)) == NULL) {
+ return -EIO;
+ }
+
+ new_dir->ops = &proc_dir_inode_operations; // ez egy normalis /proc konyvtar
+ new_dir->nlink = 2;
+ new_dir->data = NULL; // ide jon majd a struct dev
+
+ /* Ezek kellenek */
+ if (!create_comx_proc_entry(FILENAME_HARDWARE, 0644,
+ strlen(HWNAME_NONE) + 1, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_PROTOCOL, 0644,
+ strlen(PROTONAME_NONE) + 1, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_STATUS, 0444, 0, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_LINEUPDELAY, 0644, 2, new_dir)) {
+ return -ENOMEM;
+ }
+
+ if ((debug_file = create_proc_entry(FILENAME_DEBUG,
+ S_IFREG | 0644, new_dir)) == NULL) {
+ return -ENOMEM;
+ }
+ debug_file->ops = &comx_debug_inode_ops;
+ debug_file->data = (void *)debug_file;
+ debug_file->read_proc = NULL; // see below
+ debug_file->write_proc = &comx_write_proc;
+ debug_file->nlink = 1;
+
+ /* struct ppp_device is a bit larger then struct device and the
+ syncppp driver needs it */
+ if ((dev = kmalloc(sizeof(struct ppp_device), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(dev, 0, sizeof(struct ppp_device));
+ dev->name = (char *)new_dir->name;
+ dev->init = comx_init_dev;
+
+ if (register_netdevice(dev)) {
+ return -EIO;
+ }
+ ch=dev->priv;
+ ch->debug_file = debug_file;
+ ch->procdir = new_dir;
+ new_dir->data = dev;
+
+ ch->debug_start = ch->debug_end = 0;
+ if ((ch->debug_area = kmalloc(ch->debug_size = DEFAULT_DEBUG_SIZE,
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ ch->lineup_delay = DEFAULT_LINEUP_DELAY;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comx_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct proc_dir_entry *entry = dentry->d_inode->u.generic_ip;
+ struct device *dev = entry->data;
+ struct comx_channel *ch = dev->priv;
+ int ret;
+
+ /* Egyelore miert ne ? */
+ if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+ if (dev->flags & IFF_UP) {
+ printk(KERN_ERR "%s: down interface before removing it\n", dev->name);
+ return -EBUSY;
+ }
+
+ if (ch->protocol && ch->protocol->line_exit &&
+ (ret=ch->protocol->line_exit(dev))) {
+ return ret;
+ }
+ if (ch->hardware && ch->hardware->hw_exit &&
+ (ret=ch->hardware->hw_exit(dev))) {
+ if(ch->protocol && ch->protocol->line_init) {
+ ch->protocol->line_init(dev);
+ }
+ return ret;
+ }
+ ch->protocol = NULL;
+ ch->hardware = NULL;
+
+ del_timer(&ch->loadavg_timer);
+ kfree(ch->avg_bytes);
+
+ unregister_netdev(dev);
+ if (ch->debug_area) {
+ kfree(ch->debug_area);
+ }
+ if (dev->priv) {
+ kfree(dev->priv);
+ }
+ kfree(dev);
+
+ remove_proc_entry(FILENAME_DEBUG, entry);
+ remove_proc_entry(FILENAME_LINEUPDELAY, entry);
+ remove_proc_entry(FILENAME_STATUS, entry);
+ remove_proc_entry(FILENAME_HARDWARE, entry);
+ remove_proc_entry(FILENAME_PROTOCOL, entry);
+ remove_proc_entry(dentry->d_name.name, &comx_root_dir);
+// proc_unregister(&comx_root_dir, dentry->d_inode->i_ino);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct proc_dir_entry *de;
+ struct inode *inode = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ return ERR_PTR(-ENOTDIR);
+ }
+
+ if ((de = (struct proc_dir_entry *) dir->u.generic_ip) != NULL) {
+ for (de = de->subdir ; de ; de = de->next) {
+ if ((de && de->low_ino) &&
+ (de->namelen == dentry->d_name.len) &&
+ (memcmp(dentry->d_name.name, de->name,
+ de->namelen) == 0)) {
+ if ((inode = proc_get_inode(dir->i_sb,
+ de->low_ino, de)) == NULL) {
+ printk(KERN_ERR "COMX: lookup error\n");
+ return ERR_PTR(-EINVAL);
+ }
+ break;
+ }
+ }
+ }
+ dentry->d_op = &comx_dentry_operations;
+ d_add(dentry, inode);
+ return NULL;
+}
+
+int comx_strcasecmp(const char *cs, const char *ct)
+{
+ register signed char __res;
+
+ while (1) {
+ if ((__res = toupper(*cs) - toupper(*ct++)) != 0 || !*cs++) {
+ break;
+ }
+ }
+ return __res;
+}
+
+static void comx_delete_dentry(struct dentry *dentry)
+{
+ d_drop(dentry);
+}
+
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir)
+{
+ struct proc_dir_entry *new_file;
+
+ if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comx_read_proc;
+ new_file->write_proc = &comx_write_proc;
+ new_file->size = size;
+ new_file->nlink = 1;
+ }
+ return(new_file);
+}
+
+int comx_register_hardware(struct comx_hardware *comx_hw)
+{
+ struct comx_hardware *hw = comx_channels;
+
+ if (!hw) {
+ comx_channels = comx_hw;
+ } else {
+ while (hw->next != NULL && strcmp(comx_hw->name, hw->name) != 0) {
+ hw = hw->next;
+ }
+ if (strcmp(comx_hw->name, hw->name) == 0) {
+ return -1;
+ }
+ hw->next = comx_hw;
+ }
+
+ printk(KERN_INFO "COMX: driver for hardware type %s, version %s\n", comx_hw->name, comx_hw->version);
+ return 0;
+}
+
+int comx_unregister_hardware(char *name)
+{
+ struct comx_hardware *hw = comx_channels;
+
+ if (!hw) {
+ return -1;
+ }
+
+ if (strcmp(hw->name, name) == 0) {
+ comx_channels = comx_channels->next;
+ return 0;
+ }
+
+ while (hw->next != NULL && strcmp(hw->next->name,name) != 0) {
+ hw = hw->next;
+ }
+
+ if (hw->next != NULL && strcmp(hw->next->name, name) == 0) {
+ hw->next = hw->next->next;
+ return 0;
+ }
+ return -1;
+}
+
+int comx_register_protocol(struct comx_protocol *comx_line)
+{
+ struct comx_protocol *pr = comx_lines;
+
+ if (!pr) {
+ comx_lines = comx_line;
+ } else {
+ while (pr->next != NULL && strcmp(comx_line->name, pr->name) !=0) {
+ pr = pr->next;
+ }
+ if (strcmp(comx_line->name, pr->name) == 0) {
+ return -1;
+ }
+ pr->next = comx_line;
+ }
+
+ printk(KERN_INFO "COMX: driver for protocol type %s, version %s\n", comx_line->name, comx_line->version);
+ return 0;
+}
+
+int comx_unregister_protocol(char *name)
+{
+ struct comx_protocol *pr = comx_lines;
+
+ if (!pr) {
+ return -1;
+ }
+
+ if (strcmp(pr->name, name) == 0) {
+ comx_lines = comx_lines->next;
+ return 0;
+ }
+
+ while (pr->next != NULL && strcmp(pr->next->name,name) != 0) {
+ pr = pr->next;
+ }
+
+ if (pr->next != NULL && strcmp(pr->next->name, name) == 0) {
+ pr->next = pr->next->next;
+ return 0;
+ }
+ return -1;
+}
+
+#ifdef MODULE
+#define comx_init init_module
+#endif
+
+__initfunc(int comx_init(void))
+{
+ struct proc_dir_entry *new_file;
+
+ memcpy(&comx_root_inode_ops, &proc_dir_inode_operations,
+ sizeof(struct inode_operations));
+ comx_root_inode_ops.lookup = &comx_lookup;
+ comx_root_inode_ops.mkdir = &comx_mkdir;
+ comx_root_inode_ops.rmdir = &comx_rmdir;
+
+ memcpy(&comx_normal_inode_ops, &proc_net_inode_operations,
+ sizeof(struct inode_operations));
+ comx_normal_inode_ops.default_file_ops = &comx_normal_file_ops;
+ comx_normal_inode_ops.lookup = &comx_lookup;
+
+ memcpy(&comx_debug_inode_ops, &comx_normal_inode_ops,
+ sizeof(struct inode_operations));
+ comx_debug_inode_ops.default_file_ops = &comx_debug_file_ops;
+
+ memcpy(&comx_normal_file_ops, proc_net_inode_operations.default_file_ops,
+ sizeof(struct file_operations));
+ comx_normal_file_ops.open = &comx_file_open;
+ comx_normal_file_ops.release = &comx_file_release;
+
+ memcpy(&comx_debug_file_ops, &comx_normal_file_ops,
+ sizeof(struct file_operations));
+ comx_debug_file_ops.llseek = &comx_debug_lseek;
+ comx_debug_file_ops.read = &comx_debug_read;
+
+ if (proc_register(&proc_root, &comx_root_dir) < 0) return -ENOMEM;
+
+
+ if ((new_file = create_proc_entry(FILENAME_HARDWARELIST,
+ S_IFREG | 0444, &comx_root_dir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = new_file;
+ new_file->read_proc = &comx_root_read_proc;
+ new_file->write_proc = NULL;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_PROTOCOLLIST,
+ S_IFREG | 0444, &comx_root_dir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = new_file;
+ new_file->read_proc = &comx_root_read_proc;
+ new_file->write_proc = NULL;
+ new_file->nlink = 1;
+
+
+ printk(KERN_INFO "COMX: driver version %s (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>\n",
+ VERSION);
+
+#ifndef MODULE
+#ifdef CONFIG_COMX_HW_COMX
+ comx_hw_comx_init();
+#endif
+#ifdef CONFIG_COMX_HW_LOCOMX
+ comx_hw_locomx_init();
+#endif
+#ifdef CONFIG_COMX_HW_MIXCOM
+ comx_hw_mixcom_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_HDLC
+ comx_proto_hdlc_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_PPP
+ comx_proto_ppp_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_LAPB
+ comx_proto_lapb_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_FR
+ comx_proto_fr_init();
+#endif
+#endif
+
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ remove_proc_entry(FILENAME_HARDWARELIST, &comx_root_dir);
+ remove_proc_entry(FILENAME_PROTOCOLLIST, &comx_root_dir);
+ proc_unregister(&proc_root, comx_root_dir.low_ino);
+}
+#endif
+
+EXPORT_SYMBOL(comx_register_hardware);
+EXPORT_SYMBOL(comx_unregister_hardware);
+EXPORT_SYMBOL(comx_register_protocol);
+EXPORT_SYMBOL(comx_unregister_protocol);
+EXPORT_SYMBOL(comx_debug_skb);
+EXPORT_SYMBOL(comx_debug_bytes);
+EXPORT_SYMBOL(comx_debug);
+EXPORT_SYMBOL(comx_lineup_func);
+EXPORT_SYMBOL(comx_status);
+EXPORT_SYMBOL(comx_rx);
+EXPORT_SYMBOL(comx_strcasecmp);
+EXPORT_SYMBOL(comx_normal_inode_ops);
+EXPORT_SYMBOL(comx_root_dir);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)