📄 comx.c
字号:
/* * 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. * * Contributors: * Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.85) * * 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 * * Version 0.84 (99/12/01): * - comx_status should not check for IFF_UP (to report * line status from dev->open()) * * Version 0.85 (00/08/15): * - resource release on failure in comx_mkdir * - fix return value on failure at comx_write_proc * * Changed (00/10/29, Henner Eisen): * - comx_rx() / comxlapb_data_indication() return status. */#define VERSION "0.85"#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 <net/syncppp.h>#include "comx.h"MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters");MODULE_LICENSE("GPL");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;static int comx_mkdir(struct inode *, struct dentry *, int);static int comx_rmdir(struct inode *, struct dentry *);static struct dentry *comx_lookup(struct inode *, struct dentry *);static struct inode_operations comx_root_inode_ops = { lookup: comx_lookup, mkdir: comx_mkdir, rmdir: comx_rmdir,};static int 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 struct dentry_operations comx_dentry_operations = { d_delete: comx_delete_dentry,};static struct proc_dir_entry * comx_root_dir;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 } };int comx_debug(struct net_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_t(int, free ? free : ch->debug_size, min_t(int, 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 net_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 net_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 net_device *dev = (struct net_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 0static void comx_reset_timerfun(unsigned long d){ struct net_device *dev = (struct net_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 net_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 net_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 net_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 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 net_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 net_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 net_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 net_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); dev->last_rx = jiffies; } return 0;}static struct net_device_stats *comx_stats(struct net_device *dev){ struct comx_channel *ch = (struct comx_channel *)dev->priv; return ch->current_stats;}void comx_lineup_func(unsigned long d){ struct net_device *dev = (struct net_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 net_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", netif_running(dev)); 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 net_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 net_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 net_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) { kfree(ch); 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 net_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_t(int, 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -