zatm.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,628 行 · 第 1/3 页

C
1,628
字号
/* drivers/atm/zatm.c - ZeitNet ZN122x device driver */ /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */#include <linux/config.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/pci.h>#include <linux/errno.h>#include <linux/atm.h>#include <linux/atmdev.h>#include <linux/sonet.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/delay.h>#include <linux/ioport.h> /* for request_region */#include <linux/uio.h>#include <linux/init.h>#include <linux/atm_zatm.h>#include <linux/capability.h>#include <linux/bitops.h>#include <asm/byteorder.h>#include <asm/system.h>#include <asm/string.h>#include <asm/io.h>#include <asm/atomic.h>#include <asm/uaccess.h>#include "uPD98401.h"#include "uPD98402.h"#include "zeprom.h"#include "zatm.h"/* * TODO: * * Minor features *  - support 64 kB SDUs (will have to use multibuffer batches then :-( ) *  - proper use of CDV, credit = max(1,CDVT*PCR) *  - AAL0 *  - better receive timestamps *  - OAM */#if 0#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)#else#define DPRINTK(format,args...)#endif#ifndef CONFIG_ATM_ZATM_DEBUG#define NULLCHECK(x)#define EVENT(s,a,b)static void event_dump(void){}#else/*  * NULL pointer checking */#define NULLCHECK(x) \  if ((unsigned long) (x) < 0x30) printk(KERN_CRIT #x "==0x%x\n", (int) (x))/* * Very extensive activity logging. Greatly improves bug detection speed but * costs a few Mbps if enabled. */#define EV 64static const char *ev[EV];static unsigned long ev_a[EV],ev_b[EV];static int ec = 0;static void EVENT(const char *s,unsigned long a,unsigned long b){	ev[ec] = s; 	ev_a[ec] = a;	ev_b[ec] = b;	ec = (ec+1) % EV;}static void event_dump(void){	int n,i;	printk(KERN_NOTICE "----- event dump follows -----\n");	for (n = 0; n < EV; n++) {		i = (ec+n) % EV;		printk(KERN_NOTICE);		printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]);	}	printk(KERN_NOTICE "----- event dump ends here -----\n");}#endif /* CONFIG_ATM_ZATM_DEBUG */#define RING_BUSY	1	/* indication from do_tx that PDU has to be				   backlogged */static struct atm_dev *zatm_boards = NULL;static unsigned long dummy[2] = {0,0};#define zin_n(r) inl(zatm_dev->base+r*4)#define zin(r) inl(zatm_dev->base+uPD98401_##r*4)#define zout(v,r) outl(v,zatm_dev->base+uPD98401_##r*4)#define zwait while (zin(CMR) & uPD98401_BUSY)/* RX0, RX1, TX0, TX1 */static const int mbx_entries[NR_MBX] = { 1024,1024,1024,1024 };static const int mbx_esize[NR_MBX] = { 16,16,4,4 }; /* entry size in bytes */#define MBX_SIZE(i) (mbx_entries[i]*mbx_esize[i])/*-------------------------------- utilities --------------------------------*/static void zpokel(struct zatm_dev *zatm_dev,u32 value,u32 addr){	zwait;	zout(value,CER);	zout(uPD98401_IND_ACC | uPD98401_IA_BALL |	    (uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);}static u32 zpeekl(struct zatm_dev *zatm_dev,u32 addr){	zwait;	zout(uPD98401_IND_ACC | uPD98401_IA_BALL | uPD98401_IA_RW |	  (uPD98401_IA_TGT_CM << uPD98401_IA_TGT_SHIFT) | addr,CMR);	zwait;	return zin(CER);}/*------------------------------- free lists --------------------------------*//* * Free buffer head structure: *   [0] pointer to buffer (for SAR) *   [1] buffer descr link pointer (for SAR) *   [2] back pointer to skb (for poll_rx) *   [3] data *   ... */struct rx_buffer_head {	u32		buffer;	/* pointer to buffer (for SAR) */	u32		link;	/* buffer descriptor link pointer (for SAR) */	struct sk_buff	*skb;	/* back pointer to skb (for poll_rx) */};static void refill_pool(struct atm_dev *dev,int pool){	struct zatm_dev *zatm_dev;	struct sk_buff *skb;	struct rx_buffer_head *first;	unsigned long flags;	int align,offset,free,count,size;	EVENT("refill_pool\n",0,0);	zatm_dev = ZATM_DEV(dev);	size = (64 << (pool <= ZATM_AAL5_POOL_BASE ? 0 :	    pool-ZATM_AAL5_POOL_BASE))+sizeof(struct rx_buffer_head);	if (size < PAGE_SIZE) {		align = 32; /* for 32 byte alignment */		offset = sizeof(struct rx_buffer_head);	}	else {		align = 4096;		offset = zatm_dev->pool_info[pool].offset+		    sizeof(struct rx_buffer_head);	}	size += align;	spin_lock_irqsave(&zatm_dev->lock, flags);	free = zpeekl(zatm_dev,zatm_dev->pool_base+2*pool) &	    uPD98401_RXFP_REMAIN;	spin_unlock_irqrestore(&zatm_dev->lock, flags);	if (free >= zatm_dev->pool_info[pool].low_water) return;	EVENT("starting ... POOL: 0x%x, 0x%x\n",	    zpeekl(zatm_dev,zatm_dev->pool_base+2*pool),	    zpeekl(zatm_dev,zatm_dev->pool_base+2*pool+1));	EVENT("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);	count = 0;	first = NULL;	while (free < zatm_dev->pool_info[pool].high_water) {		struct rx_buffer_head *head;		skb = alloc_skb(size,GFP_ATOMIC);		if (!skb) {			printk(KERN_WARNING DEV_LABEL "(Itf %d): got no new "			    "skb (%d) with %d free\n",dev->number,size,free);			break;		}		skb_reserve(skb,(unsigned char *) ((((unsigned long) skb->data+		    align+offset-1) & ~(unsigned long) (align-1))-offset)-		    skb->data);		head = (struct rx_buffer_head *) skb->data;		skb_reserve(skb,sizeof(struct rx_buffer_head));		if (!first) first = head;		count++;		head->buffer = virt_to_bus(skb->data);		head->link = 0;		head->skb = skb;		EVENT("enq skb 0x%08lx/0x%08lx\n",(unsigned long) skb,		    (unsigned long) head);		spin_lock_irqsave(&zatm_dev->lock, flags);		if (zatm_dev->last_free[pool])			((struct rx_buffer_head *) (zatm_dev->last_free[pool]->			    data))[-1].link = virt_to_bus(head);		zatm_dev->last_free[pool] = skb;		skb_queue_tail(&zatm_dev->pool[pool],skb);		spin_unlock_irqrestore(&zatm_dev->lock, flags);		free++;	}	if (first) {		spin_lock_irqsave(&zatm_dev->lock, flags);		zwait;		zout(virt_to_bus(first),CER);		zout(uPD98401_ADD_BAT | (pool << uPD98401_POOL_SHIFT) | count,		    CMR);		spin_unlock_irqrestore(&zatm_dev->lock, flags);		EVENT ("POOL: 0x%x, 0x%x\n",		    zpeekl(zatm_dev,zatm_dev->pool_base+2*pool),		    zpeekl(zatm_dev,zatm_dev->pool_base+2*pool+1));		EVENT("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);	}}static void drain_free(struct atm_dev *dev,int pool){	skb_queue_purge(&ZATM_DEV(dev)->pool[pool]);}static int pool_index(int max_pdu){	int i;	if (max_pdu % ATM_CELL_PAYLOAD)		printk(KERN_ERR DEV_LABEL ": driver error in pool_index: "		    "max_pdu is %d\n",max_pdu);	if (max_pdu > 65536) return -1;	for (i = 0; (64 << i) < max_pdu; i++);	return i+ZATM_AAL5_POOL_BASE;}/* use_pool isn't reentrant */static void use_pool(struct atm_dev *dev,int pool){	struct zatm_dev *zatm_dev;	unsigned long flags;	int size;	zatm_dev = ZATM_DEV(dev);	if (!(zatm_dev->pool_info[pool].ref_count++)) {		skb_queue_head_init(&zatm_dev->pool[pool]);		size = pool-ZATM_AAL5_POOL_BASE;		if (size < 0) size = 0; /* 64B... */		else if (size > 10) size = 10; /* ... 64kB */		spin_lock_irqsave(&zatm_dev->lock, flags);		zpokel(zatm_dev,((zatm_dev->pool_info[pool].low_water/4) <<		    uPD98401_RXFP_ALERT_SHIFT) |		    (1 << uPD98401_RXFP_BTSZ_SHIFT) |		    (size << uPD98401_RXFP_BFSZ_SHIFT),		    zatm_dev->pool_base+pool*2);		zpokel(zatm_dev,(unsigned long) dummy,zatm_dev->pool_base+		    pool*2+1);		spin_unlock_irqrestore(&zatm_dev->lock, flags);		zatm_dev->last_free[pool] = NULL;		refill_pool(dev,pool);	}	DPRINTK("pool %d: %d\n",pool,zatm_dev->pool_info[pool].ref_count);}static void unuse_pool(struct atm_dev *dev,int pool){	if (!(--ZATM_DEV(dev)->pool_info[pool].ref_count))		drain_free(dev,pool);}/*----------------------------------- RX ------------------------------------*/#if 0static void exception(struct atm_vcc *vcc){   static int count = 0;   struct zatm_dev *zatm_dev = ZATM_DEV(vcc->dev);   struct zatm_vcc *zatm_vcc = ZATM_VCC(vcc);   unsigned long *qrp;   int i;   if (count++ > 2) return;   for (i = 0; i < 8; i++)	printk("TX%d: 0x%08lx\n",i,	  zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+i));   for (i = 0; i < 5; i++)	printk("SH%d: 0x%08lx\n",i,	  zpeekl(zatm_dev,uPD98401_IM(zatm_vcc->shaper)+16*i));   qrp = (unsigned long *) zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+     uPD98401_TXVC_QRP);   printk("qrp=0x%08lx\n",(unsigned long) qrp);   for (i = 0; i < 4; i++) printk("QRP[%d]: 0x%08lx",i,qrp[i]);}#endifstatic const char *err_txt[] = {	"No error",	"RX buf underflow",	"RX FIFO overrun",	"Maximum len violation",	"CRC error",	"User abort",	"Length violation",	"T1 error",	"Deactivated",	"???",	"???",	"???",	"???",	"???",	"???",	"???"};static void poll_rx(struct atm_dev *dev,int mbx){	struct zatm_dev *zatm_dev;	unsigned long pos;	u32 x;	int error;	EVENT("poll_rx\n",0,0);	zatm_dev = ZATM_DEV(dev);	pos = (zatm_dev->mbx_start[mbx] & ~0xffffUL) | zin(MTA(mbx));	while (x = zin(MWA(mbx)), (pos & 0xffff) != x) {		u32 *here;		struct sk_buff *skb;		struct atm_vcc *vcc;		int cells,size,chan;		EVENT("MBX: host 0x%lx, nic 0x%x\n",pos,x);		here = (u32 *) pos;		if (((pos += 16) & 0xffff) == zatm_dev->mbx_end[mbx])			pos = zatm_dev->mbx_start[mbx];		cells = here[0] & uPD98401_AAL5_SIZE;#if 0printk("RX IND: 0x%x, 0x%x, 0x%x, 0x%x\n",here[0],here[1],here[2],here[3]);{unsigned long *x;		printk("POOL: 0x%08x, 0x%08x\n",zpeekl(zatm_dev,		      zatm_dev->pool_base),		      zpeekl(zatm_dev,zatm_dev->pool_base+1));		x = (unsigned long *) here[2];		printk("[0..3] = 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx\n",		    x[0],x[1],x[2],x[3]);}#endif		error = 0;		if (here[3] & uPD98401_AAL5_ERR) {			error = (here[3] & uPD98401_AAL5_ES) >>			    uPD98401_AAL5_ES_SHIFT;			if (error == uPD98401_AAL5_ES_DEACT ||			    error == uPD98401_AAL5_ES_FREE) continue;		}EVENT("error code 0x%x/0x%x\n",(here[3] & uPD98401_AAL5_ES) >>  uPD98401_AAL5_ES_SHIFT,error);		skb = ((struct rx_buffer_head *) bus_to_virt(here[2]))->skb;		do_gettimeofday(&skb->stamp);#if 0printk("[-3..0] 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n",((unsigned *) skb->data)[-3],  ((unsigned *) skb->data)[-2],((unsigned *) skb->data)[-1],  ((unsigned *) skb->data)[0]);#endif		EVENT("skb 0x%lx, here 0x%lx\n",(unsigned long) skb,		    (unsigned long) here);#if 0printk("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);#endif		size = error ? 0 : ntohs(((u16 *) skb->data)[cells*		    ATM_CELL_PAYLOAD/sizeof(u16)-3]);		EVENT("got skb 0x%lx, size %d\n",(unsigned long) skb,size);		chan = (here[3] & uPD98401_AAL5_CHAN) >>		    uPD98401_AAL5_CHAN_SHIFT;		if (chan < zatm_dev->chans && zatm_dev->rx_map[chan]) {			vcc = zatm_dev->rx_map[chan];			if (skb == zatm_dev->last_free[ZATM_VCC(vcc)->pool])				zatm_dev->last_free[ZATM_VCC(vcc)->pool] = NULL;			skb_unlink(skb);		}		else {			printk(KERN_ERR DEV_LABEL "(itf %d): RX indication "			    "for non-existing channel\n",dev->number);			size = 0;			vcc = NULL;			event_dump();		}		if (error) {			static unsigned long silence = 0;			static int last_error = 0;			if (error != last_error ||			    time_after(jiffies, silence)  || silence == 0){				printk(KERN_WARNING DEV_LABEL "(itf %d): "				    "chan %d error %s\n",dev->number,chan,				    err_txt[error]);				last_error = error;				silence = (jiffies+2*HZ)|1;			}			size = 0;		}		if (size && (size > cells*ATM_CELL_PAYLOAD-ATM_AAL5_TRAILER ||		    size <= (cells-1)*ATM_CELL_PAYLOAD-ATM_AAL5_TRAILER)) {			printk(KERN_ERR DEV_LABEL "(itf %d): size %d with %d "			    "cells\n",dev->number,size,cells);			size = 0;			event_dump();		}		if (size > ATM_MAX_AAL5_PDU) {			printk(KERN_ERR DEV_LABEL "(itf %d): size too big "			    "(%d)\n",dev->number,size);			size = 0;			event_dump();		}		if (!size) {			dev_kfree_skb_irq(skb);			if (vcc) atomic_inc(&vcc->stats->rx_err);			continue;		}		if (!atm_charge(vcc,skb->truesize)) {			dev_kfree_skb_irq(skb);			continue;		}		skb->len = size;		ATM_SKB(skb)->vcc = vcc;		vcc->push(vcc,skb);		atomic_inc(&vcc->stats->rx);	}	zout(pos & 0xffff,MTA(mbx));#if 0 /* probably a stupid idea */	refill_pool(dev,zatm_vcc->pool);		/* maybe this saves us a few interrupts */#endif}static int open_rx_first(struct atm_vcc *vcc){	struct zatm_dev *zatm_dev;	struct zatm_vcc *zatm_vcc;	unsigned long flags;	unsigned short chan;	int cells;	DPRINTK("open_rx_first (0x%x)\n",inb_p(0xc053));	zatm_dev = ZATM_DEV(vcc->dev);	zatm_vcc = ZATM_VCC(vcc);	zatm_vcc->rx_chan = 0;	if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0;	if (vcc->qos.aal == ATM_AAL5) {		if (vcc->qos.rxtp.max_sdu > 65464)			vcc->qos.rxtp.max_sdu = 65464;			/* fix this - we may want to receive 64kB SDUs			   later */		cells = (vcc->qos.rxtp.max_sdu+ATM_AAL5_TRAILER+		    ATM_CELL_PAYLOAD-1)/ATM_CELL_PAYLOAD;		zatm_vcc->pool = pool_index(cells*ATM_CELL_PAYLOAD);	}	else {		cells = 1;		zatm_vcc->pool = ZATM_AAL0_POOL;	}	if (zatm_vcc->pool < 0) return -EMSGSIZE;	spin_lock_irqsave(&zatm_dev->lock, flags);	zwait;	zout(uPD98401_OPEN_CHAN,CMR);	zwait;	DPRINTK("0x%x 0x%x\n",zin(CMR),zin(CER));	chan = (zin(CMR) & uPD98401_CHAN_ADDR) >> uPD98401_CHAN_ADDR_SHIFT;	spin_unlock_irqrestore(&zatm_dev->lock, flags);	DPRINTK("chan is %d\n",chan);	if (!chan) return -EAGAIN;	use_pool(vcc->dev,zatm_vcc->pool);	DPRINTK("pool %d\n",zatm_vcc->pool);	/* set up VC descriptor */	spin_lock_irqsave(&zatm_dev->lock, flags);	zpokel(zatm_dev,zatm_vcc->pool << uPD98401_RXVC_POOL_SHIFT,	    chan*VC_SIZE/4);	zpokel(zatm_dev,uPD98401_RXVC_OD | (vcc->qos.aal == ATM_AAL5 ?	    uPD98401_RXVC_AR : 0) | cells,chan*VC_SIZE/4+1);	zpokel(zatm_dev,0,chan*VC_SIZE/4+2);	zatm_vcc->rx_chan = chan;	zatm_dev->rx_map[chan] = vcc;	spin_unlock_irqrestore(&zatm_dev->lock, flags);	return 0;}static int open_rx_second(struct atm_vcc *vcc){	struct zatm_dev *zatm_dev;	struct zatm_vcc *zatm_vcc;	unsigned long flags;	int pos,shift;	DPRINTK("open_rx_second (0x%x)\n",inb_p(0xc053));	zatm_dev = ZATM_DEV(vcc->dev);	zatm_vcc = ZATM_VCC(vcc);	if (!zatm_vcc->rx_chan) return 0;	spin_lock_irqsave(&zatm_dev->lock, flags);	/* should also handle VPI @@@ */	pos = vcc->vci >> 1;

⌨️ 快捷键说明

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