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

📄 comx-hw-mixcom.c

📁 内核linux2.4.20,可跟rtlinux3.2打补丁 组成实时linux系统,编译内核
💻 C
📖 第 1 页 / 共 2 页
字号:
/*  * Hardware driver for the MixCom synchronous serial board  * * Author: Gergely Madarasz <gorgo@itc.hu> * * based on skeleton driver code and a preliminary hscx driver by  * Tivadar Szemethy <tiv@itc.hu> * * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu> * * Contributors: * Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.65) * * 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.60 (99/06/11): *		- ported to the kernel, now works as builtin code * * Version 0.61 (99/06/11): *		- recognize the one-channel MixCOM card (id byte = 0x13) *		- printk fixes *  * Version 0.62 (99/07/15): *		- fixes according to the new hw docs  *		- report line status when open * * Version 0.63 (99/09/21): *		- line status report fixes * * Version 0.64 (99/12/01): *		- some more cosmetical fixes * * Version 0.65 (00/08/15) *		- resource release on failure at MIXCOM_init */#define VERSION "0.65"#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/types.h>#include <asm/uaccess.h>#include <asm/io.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/init.h>#include "comx.h"#include "mixcom.h"#include "hscx.h"MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board");MODULE_LICENSE("GPL");#define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> \	HW_privdata))#define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - \	(1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET)#define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + \	(1 - channel) * MIXCOM_CHANNEL_OFFSET)/* Values used to set the IRQ line */static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF};static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"};struct mixcom_privdata {	u16	clock;	char	channel;	long	txbusy;	struct sk_buff *sending;	unsigned tx_ptr;	struct sk_buff *recving;	unsigned rx_ptr;	unsigned char status;	char	card_has_status;};static inline void wr_hscx(struct net_device *dev, int reg, unsigned char val) {	outb(val, dev->base_addr + reg);}static inline unsigned char rd_hscx(struct net_device *dev, int reg){	return inb(dev->base_addr + reg);}static inline void hscx_cmd(struct net_device *dev, int cmd){	unsigned long jiffs = jiffies;	unsigned char cec;	unsigned delay = 0;	while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) && 	    (jiffs + HZ > jiffies)) {		udelay(1);		if (++delay > (100000 / HZ)) break;	}	if (cec) {		printk(KERN_WARNING "%s: CEC stuck, probably no clock!\n",dev->name);	} else {		wr_hscx(dev, HSCX_CMDR, cmd);	}}static inline void hscx_fill_fifo(struct net_device *dev){	struct comx_channel *ch = dev->priv;	struct mixcom_privdata *hw = ch->HW_privdata;	register word to_send = hw->sending->len - hw->tx_ptr;	outsb(dev->base_addr + HSCX_FIFO,        	&(hw->sending->data[hw->tx_ptr]), min_t(unsigned int, to_send, 32));	if (to_send <= 32) {        	hscx_cmd(dev, HSCX_XTF | HSCX_XME);	        kfree_skb(hw->sending);        	hw->sending = NULL;         	hw->tx_ptr = 0;        } else {	        hscx_cmd(dev, HSCX_XTF);        	hw->tx_ptr += 32;        }}static inline void hscx_empty_fifo(struct net_device *dev, int cnt){	struct comx_channel *ch = dev->priv;	struct mixcom_privdata *hw = ch->HW_privdata;	if (hw->recving == NULL) {        	if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) {	                ch->stats.rx_dropped++;        	        hscx_cmd(dev, HSCX_RHR);                } else {	                skb_reserve(hw->recving, 16);        	        skb_put(hw->recving, HSCX_MTU);                }	        hw->rx_ptr = 0;        }	if (cnt > 32 || !cnt || hw->recving == NULL) {        	printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %p\n",		        cnt, (void *)hw->recving);	        return;        }        	insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt);	hw->rx_ptr += cnt;	hscx_cmd(dev, HSCX_RMC);}static int MIXCOM_txe(struct net_device *dev){	struct comx_channel *ch = dev->priv;	struct mixcom_privdata *hw = ch->HW_privdata;	return !test_bit(0, &hw->txbusy);}static int mixcom_probe(struct net_device *dev){	unsigned long flags;	int id, vstr, ret=0;	save_flags(flags); cli();	id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET) & 0x7f; 	if (id != MIXCOM_ID ) {		ret=-ENODEV;		printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lx\n",dev->name, dev->base_addr);		goto out;	}	vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f;	if(vstr>=sizeof(hscx_versions)/sizeof(char*) || 	    hscx_versions[vstr]==NULL) {		printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)\n",dev->name,dev->base_addr,vstr);		ret = -ENODEV;	} else {		printk(KERN_INFO "%s: HSCX chip version %s\n",dev->name,hscx_versions[vstr]);		ret = 0;	}out:	restore_flags(flags);	return ret;}#if 0static void MIXCOM_set_clock(struct net_device *dev){	struct comx_channel *ch = dev->priv;	struct mixcom_privdata *hw = ch->HW_privdata;	if (hw->clock) {		;	} else {		;	}}#endifstatic void mixcom_board_on(struct net_device *dev){	outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);	udelay(1000);	outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON, 		MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);	udelay(1000);}static void mixcom_board_off(struct net_device *dev){	outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);	udelay(1000);}static void mixcom_off(struct net_device *dev){	wr_hscx(dev, HSCX_CCR1, 0x0);}static void mixcom_on(struct net_device *dev){	struct comx_channel *ch = dev->priv;	wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull	wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ );	wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS );	wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes	wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN );	hscx_cmd(dev, HSCX_XRES | HSCX_RHR);	if (ch->HW_set_clock) ch->HW_set_clock(dev);}static int MIXCOM_send_packet(struct net_device *dev, struct sk_buff *skb) {	struct comx_channel *ch = dev->priv;	struct mixcom_privdata *hw = ch->HW_privdata;	unsigned long flags;	if (ch->debug_flags & DEBUG_HW_TX) {		comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet");	}	if (!(ch->line_status & LINE_UP)) {		return FRAME_DROPPED;	}	if (skb->len > HSCX_MTU) {		ch->stats.tx_errors++;			return FRAME_ERROR;	}	save_flags(flags); cli();	if (test_and_set_bit(0, &hw->txbusy)) {		printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)\n", dev->name, skb->len);		restore_flags(flags);		return FRAME_DROPPED;	}	hw->sending = skb;	hw->tx_ptr = 0;	hw->txbusy = 1;//	atomic_inc(&skb->users);	// save it	hscx_fill_fifo(dev);	restore_flags(flags);	ch->stats.tx_packets++;	ch->stats.tx_bytes += skb->len; 	if (ch->debug_flags & DEBUG_HW_TX) {		comx_debug(dev, "MIXCOM_send_packet was successful\n\n");	}	return FRAME_ACCEPTED;}static inline void mixcom_receive_frame(struct net_device *dev) {	struct comx_channel *ch=dev->priv;	struct mixcom_privdata *hw=ch->HW_privdata;	register byte rsta;	register word length;	rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO | 		HSCX_CRC | HSCX_RAB);	length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) | 		rd_hscx(dev, HSCX_RBCL);	if ( length > hw->rx_ptr ) {		hscx_empty_fifo(dev, length - hw->rx_ptr);	}		if (!(rsta & HSCX_VFR)) {		ch->stats.rx_length_errors++;	}	if (rsta & HSCX_RDO) {		ch->stats.rx_over_errors++;	}	if (!(rsta & HSCX_CRC)) {		ch->stats.rx_crc_errors++;	}	if (rsta & HSCX_RAB) {		ch->stats.rx_frame_errors++;	}	ch->stats.rx_packets++; 	ch->stats.rx_bytes += length;	if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) {		skb_trim(hw->recving, hw->rx_ptr - 1);		if (ch->debug_flags & DEBUG_HW_RX) {			comx_debug_skb(dev, hw->recving,				"MIXCOM_interrupt receiving");		}		hw->recving->dev = dev;		if (ch->LINE_rx) {			ch->LINE_rx(dev, hw->recving);		}	}	else if(hw->recving) {		kfree_skb(hw->recving);	}	hw->recving = NULL; 	hw->rx_ptr = 0;}static inline void mixcom_extended_interrupt(struct net_device *dev) {	struct comx_channel *ch=dev->priv;	struct mixcom_privdata *hw=ch->HW_privdata;	register byte exir;	exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC );	if (exir & HSCX_RFO) {		ch->stats.rx_over_errors++;		if (hw->rx_ptr) {			kfree_skb(hw->recving);			hw->recving = NULL; hw->rx_ptr = 0;		}		printk(KERN_ERR "MIXCOM: rx overrun\n");		hscx_cmd(dev, HSCX_RHR);	}	if (exir & HSCX_XDU) { // xmit underrun		ch->stats.tx_errors++;		ch->stats.tx_aborted_errors++;		if (hw->tx_ptr) {			kfree_skb(hw->sending);			hw->sending = NULL; 			hw->tx_ptr = 0;		}		hscx_cmd(dev, HSCX_XRES);		clear_bit(0, &hw->txbusy);		if (ch->LINE_tx) {			ch->LINE_tx(dev);		}		printk(KERN_ERR "MIXCOM: tx underrun\n");	}	if (exir & HSCX_CSC) {        		ch->stats.tx_carrier_errors++;		if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le			if (test_and_clear_bit(0, &ch->lineup_pending)) {               			del_timer(&ch->lineup_timer);			} else if (ch->line_status & LINE_UP) {        		       	ch->line_status &= ~LINE_UP;                		if (ch->LINE_status) {                      			ch->LINE_status(dev,ch->line_status);                      		}		      	}		}		if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) & 		    HSCX_CTS)) { // Vonal fol			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);		                hscx_cmd(dev, HSCX_XRES);        		        clear_bit(0, &hw->txbusy);                		if (hw->sending) {					kfree_skb(hw->sending);				}				hw->sending=NULL;				hw->tx_ptr = 0;			}		}	}}static void MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs){	unsigned long flags;	struct net_device *dev = (struct net_device *)dev_id;	struct comx_channel *ch, *twin_ch;	struct mixcom_privdata *hw, *twin_hw;	register unsigned char ista;	if (dev==NULL) {		printk(KERN_ERR "comx_interrupt: irq %d for unknown device\n",irq);		return;	}	ch = dev->priv; 	hw = ch->HW_privdata;	save_flags(flags); cli(); 	while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF | 	    HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) {		register byte ista2 = 0;		if (ista & HSCX_RME) {			mixcom_receive_frame(dev);		}		if (ista & HSCX_RPF) {			hscx_empty_fifo(dev, 32);		}		if (ista & HSCX_XPR) {			if (hw->tx_ptr) {				hscx_fill_fifo(dev);			} else {				clear_bit(0, &hw->txbusy);               			ch->LINE_tx(dev);			}		}				if (ista & HSCX_EXB) {			mixcom_extended_interrupt(dev);		}				if ((ista & HSCX_EXA) && ch->twin)  {			mixcom_extended_interrupt(ch->twin);		}			if ((ista & HSCX_ICA) && ch->twin &&		    (ista2 = rd_hscx(ch->twin, HSCX_ISTA) &		    (HSCX_RME | HSCX_RPF | HSCX_XPR ))) {			if (ista2 & HSCX_RME) {				mixcom_receive_frame(ch->twin);			}			if (ista2 & HSCX_RPF) {				hscx_empty_fifo(ch->twin, 32);			}			if (ista2 & HSCX_XPR) {				twin_ch=ch->twin->priv;				twin_hw=twin_ch->HW_privdata;				if (twin_hw->tx_ptr) {					hscx_fill_fifo(ch->twin);				} else {					clear_bit(0, &twin_hw->txbusy);					ch->LINE_tx(ch->twin);				}			}		}	}	restore_flags(flags);	return;

⌨️ 快捷键说明

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