⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 comx-hw-comx.c

📁 linux和2410结合开发 用他可以生成2410所需的zImage文件
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Hardware-level driver for the COMX and HICOMX cards * for Linux kernel 2.2.X * * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>, *                    Peter Bajan <bajan.peter@synergon.hu>, * Rewritten by: Tivadar Szemethy <tiv@itc.hu> * Currently maintained by: Gergely Madarasz <gorgo@itc.hu> * * Copyright (C) 1995-2000 ITConsult-Pro Co. <info@itc.hu> * * Contributors: * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 0.86 * * 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): *		- port back to kernel, add support builtin driver  *		- cleaned up the source code a bit * * Version 0.81 (99/06/22): *		- cleaned up the board load functions, no more long reset *		  timeouts *		- lower modem lines on close *		- some interrupt handling fixes * * Version 0.82 (99/08/24): *		- fix multiple board support * * Version 0.83 (99/11/30): *		- interrupt handling and locking fixes during initalization *		- really fix multiple board support *  * Version 0.84 (99/12/02): *		- some workarounds for problematic hardware/firmware * * Version 0.85 (00/01/14): *		- some additional workarounds :/ *		- printk cleanups * Version 0.86 (00/08/15): * 		- resource release on failure at COMX_init */#define VERSION "0.86"#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/ioport.h>#include <linux/init.h>#include <linux/delay.h>#include <asm/uaccess.h>#include <asm/io.h>#include "comx.h"#include "comxhw.h"MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>, Tivadar Szemethy <tiv@itc.hu>, Arpad Bakay");MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n");MODULE_LICENSE("GPL");#define	COMX_readw(dev, offset)	(readw(dev->mem_start + offset + \	(unsigned int)(((struct comx_privdata *)\	((struct comx_channel *)dev->priv)->HW_privdata)->channel) \	* COMX_CHANNEL_OFFSET))#define COMX_WRITE(dev, offset, value)	(writew(value, dev->mem_start + offset \	+ (unsigned int)(((struct comx_privdata *) \	((struct comx_channel *)dev->priv)->HW_privdata)->channel) \	* COMX_CHANNEL_OFFSET))#define COMX_CMD(dev, cmd)	(COMX_WRITE(dev, OFF_A_L2_CMD, cmd))struct comx_firmware {	int	len;	unsigned char *data;};struct comx_privdata {	struct comx_firmware *firmware;	u16	clock;	char	channel;		// channel no.	int	memory_size;	short	io_extent;	u_long	histogram[5];};static struct net_device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000];extern struct comx_hardware hicomx_hw;extern struct comx_hardware comx_hw;extern struct comx_hardware cmx_hw;static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs);static void COMX_board_on(struct net_device *dev){	outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | 	    COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr);}static void COMX_board_off(struct net_device *dev){	outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | 	   COMX_ENABLE_BOARD_IT), dev->base_addr);}static void HICOMX_board_on(struct net_device *dev){	outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | 	   HICOMX_ENABLE_BOARD_MEM), dev->base_addr);}static void HICOMX_board_off(struct net_device *dev){	outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | 	   HICOMX_DISABLE_BOARD_MEM), dev->base_addr);}static void COMX_set_clock(struct net_device *dev){	struct comx_channel *ch = dev->priv;	struct comx_privdata *hw = ch->HW_privdata;	COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock);}static struct net_device *COMX_access_board(struct net_device *dev){	struct comx_channel *ch = dev->priv;	struct net_device *ret;	int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;	unsigned long flags;	save_flags(flags); cli();		ret = memory_used[mempos];	if(ret == dev) {		goto out;	}	memory_used[mempos] = dev;	if (!ch->twin || ret != ch->twin) {		if (ret) ((struct comx_channel *)ret->priv)->HW_board_off(ret);		ch->HW_board_on(dev);	}out:	restore_flags(flags);	return ret;}static void COMX_release_board(struct net_device *dev, struct net_device *savep){	unsigned long flags;	int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;	struct comx_channel *ch = dev->priv;	save_flags(flags); cli();	if (memory_used[mempos] == savep) {		goto out;	}	memory_used[mempos] = savep;	if (!ch->twin || ch->twin != savep) {		ch->HW_board_off(dev);		if (savep) ((struct comx_channel*)savep->priv)->HW_board_on(savep);	}out:	restore_flags(flags);}static int COMX_txe(struct net_device *dev) {	struct net_device *savep;	struct comx_channel *ch = dev->priv;	int rc = 0;	savep = ch->HW_access_board(dev);	if (COMX_readw(dev,OFF_A_L2_LINKUP) == LINKUP_READY) {		rc = COMX_readw(dev,OFF_A_L2_TxEMPTY);	} 	ch->HW_release_board(dev,savep);	if(rc==0xffff) {		printk(KERN_ERR "%s, OFF_A_L2_TxEMPTY is %d\n",dev->name, rc);	}	return rc;}static int COMX_send_packet(struct net_device *dev, struct sk_buff *skb){	struct net_device *savep;	struct comx_channel *ch = dev->priv;	struct comx_privdata *hw = ch->HW_privdata;	int ret = FRAME_DROPPED;	word tmp;	savep = ch->HW_access_board(dev);		if (ch->debug_flags & DEBUG_HW_TX) {		comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet");	}	if (skb->len > COMX_MAX_TX_SIZE) {		ret=FRAME_DROPPED;		goto out;	}	tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);	if ((ch->line_status & LINE_UP) && tmp==1) {		int lensave = skb->len;		int dest = COMX_readw(dev, OFF_A_L2_TxBUFP);		word *data = (word *)skb->data;		if(dest==0xffff) {			printk(KERN_ERR "%s: OFF_A_L2_TxBUFP is %d\n", dev->name, dest);			ret=FRAME_DROPPED;			goto out;		}							writew((unsigned short)skb->len, dev->mem_start + dest);		dest += 2;		while (skb->len > 1) {			writew(*data++, dev->mem_start + dest);			dest += 2; skb->len -= 2;		}		if (skb->len == 1) {			writew(*((byte *)data), dev->mem_start + dest);		}		writew(0, dev->mem_start + (int)hw->channel * 		   COMX_CHANNEL_OFFSET + OFF_A_L2_TxEMPTY);		ch->stats.tx_packets++;			ch->stats.tx_bytes += lensave; 		ret = FRAME_ACCEPTED;	} else {		ch->stats.tx_dropped++;		printk(KERN_INFO "%s: frame dropped\n",dev->name);		if(tmp) {			printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n",dev->name,tmp);		}	}	out:	ch->HW_release_board(dev, savep);	dev_kfree_skb(skb);	return ret;}static inline int comx_read_buffer(struct net_device *dev) {	struct comx_channel *ch = dev->priv;	word rbuf_offs;	struct sk_buff *skb;	word len;	int i=0;	word *writeptr;	i = 0;	rbuf_offs = COMX_readw(dev, OFF_A_L2_RxBUFP);	if(rbuf_offs == 0xffff) {		printk(KERN_ERR "%s: OFF_A_L2_RxBUFP is %d\n",dev->name,rbuf_offs);		return 0;	}	len = readw(dev->mem_start + rbuf_offs);	if(len > COMX_MAX_RX_SIZE) {		printk(KERN_ERR "%s: packet length is %d\n",dev->name,len);		return 0;	}	if ((skb = dev_alloc_skb(len + 16)) == NULL) {		ch->stats.rx_dropped++;		COMX_WRITE(dev, OFF_A_L2_DAV, 0);		return 0;	}	rbuf_offs += 2;	skb_reserve(skb, 16);	skb_put(skb, len);	skb->dev = dev;	writeptr = (word *)skb->data;	while (i < len) {		*writeptr++ = readw(dev->mem_start + rbuf_offs);		rbuf_offs += 2; 		i += 2;	}	COMX_WRITE(dev, OFF_A_L2_DAV, 0);	ch->stats.rx_packets++;	ch->stats.rx_bytes += len;	if (ch->debug_flags & DEBUG_HW_RX) {		comx_debug_skb(dev, skb, "COMX_interrupt receiving");	}	ch->LINE_rx(dev, skb);	return 1;}static inline char comx_line_change(struct net_device *dev, char linestat){	struct comx_channel *ch=dev->priv;	char idle=1;			if (linestat & LINE_UP) { /* Vonal fol */		if (ch->lineup_delay) {			if (!test_and_set_bit(0, &ch->lineup_pending)) {				ch->lineup_timer.function = comx_lineup_func;				ch->lineup_timer.data = (unsigned long)dev;				ch->lineup_timer.expires = jiffies +					HZ*ch->lineup_delay;				add_timer(&ch->lineup_timer);				idle=0;			}		} else {			idle=0;			ch->LINE_status(dev, ch->line_status |= LINE_UP);		}	} else { /* Vonal le */		idle=0;		if (test_and_clear_bit(0, &ch->lineup_pending)) {			del_timer(&ch->lineup_timer);		} else {			ch->line_status &= ~LINE_UP;			if (ch->LINE_status) {				ch->LINE_status(dev, ch->line_status);			}		}	}	return idle;}static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs){	struct net_device *dev = dev_id;	struct comx_channel *ch = dev->priv;	struct comx_privdata *hw = ch->HW_privdata;	struct net_device *interrupted;	unsigned long jiffs;	char idle = 0;	int count = 0;	word tmp;	if (dev == NULL) {		printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq);		return;	}	jiffs = jiffies;	interrupted = ch->HW_access_board(dev);	while (!idle && count < 5000) {		char channel = 0;		idle = 1;		while (channel < 2) {			char linestat = 0;			char buffers_emptied = 0;			if (channel == 1) {				if (ch->twin) {					dev = ch->twin;					ch = dev->priv;					hw = ch->HW_privdata;				} else {					break;				}			} else {				COMX_WRITE(dev, OFF_A_L1_REPENA, 				    COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00);			}			channel++;			if ((ch->init_status & (HW_OPEN | LINE_OPEN)) != 			   (HW_OPEN | LINE_OPEN)) {				continue;			}				/* Collect stats */			tmp = COMX_readw(dev, OFF_A_L1_ABOREC);			COMX_WRITE(dev, OFF_A_L1_ABOREC, 0);			if(tmp==0xffff) {				printk(KERN_ERR "%s: OFF_A_L1_ABOREC is %d\n",dev->name,tmp);				break;			} else {				ch->stats.rx_missed_errors += (tmp >> 8) & 0xff;				ch->stats.rx_over_errors += tmp & 0xff;			}			tmp = COMX_readw(dev, OFF_A_L1_CRCREC);			COMX_WRITE(dev, OFF_A_L1_CRCREC, 0);			if(tmp==0xffff) {				printk(KERN_ERR "%s: OFF_A_L1_CRCREC is %d\n",dev->name,tmp);				break;			} else {				ch->stats.rx_crc_errors += (tmp >> 8) & 0xff;				ch->stats.rx_missed_errors += tmp & 0xff;			}						if ((ch->line_status & LINE_UP) && ch->LINE_rx) {				tmp=COMX_readw(dev, OFF_A_L2_DAV); 				while (tmp==1) {					idle=0;					buffers_emptied+=comx_read_buffer(dev);					tmp=COMX_readw(dev, OFF_A_L2_DAV); 				}				if(tmp) {					printk(KERN_ERR "%s: OFF_A_L2_DAV is %d\n", dev->name, tmp);					break;				}			}			tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);			if (tmp==1 && ch->LINE_tx) {				ch->LINE_tx(dev);			} 			if(tmp==0xffff) {				printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n", dev->name, tmp);				break;			}			if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {				linestat &= ~LINE_UP;			} else {				linestat |= LINE_UP;			}			if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) {				ch->stats.tx_carrier_errors++;				idle &= comx_line_change(dev,linestat);			}							hw->histogram[(int)buffers_emptied]++;		}		count++;	}	if(count==5000) {		printk(KERN_WARNING "%s: interrupt stuck\n",dev->name);	}	ch->HW_release_board(dev, interrupted);}static int COMX_open(struct net_device *dev){	struct comx_channel *ch = dev->priv;	struct comx_privdata *hw = ch->HW_privdata;	struct proc_dir_entry *procfile = ch->procdir->subdir;	unsigned long jiffs;	int twin_open=0;	int retval;	struct net_device *savep;	if (!dev->base_addr || !dev->irq || !dev->mem_start) {		return -ENODEV;	}	if (ch->twin && (((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN)) {		twin_open=1;	}	if (!twin_open) {		if (check_region(dev->base_addr, hw->io_extent)) {			return -EAGAIN;		}		if (request_irq(dev->irq, COMX_interrupt, 0, dev->name, 		   (void *)dev)) {			printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq);			return -EAGAIN;		}		ch->init_status |= IRQ_ALLOCATED;		request_region(dev->base_addr, hw->io_extent, dev->name);		if (!ch->HW_load_board || ch->HW_load_board(dev)) {			ch->init_status &= ~IRQ_ALLOCATED;			retval=-ENODEV;			goto error;		}	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -