📄 zatm.c
字号:
/* 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 __i386__#ifdef CONFIG_ATM_ZATM_EXACT_TS#warning Precise timestamping only available on i386 platform#undef CONFIG_ATM_ZATM_EXACT_TS#endif#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; save_flags(flags); cli(); free = zpeekl(zatm_dev,zatm_dev->pool_base+2*pool) & uPD98401_RXFP_REMAIN; restore_flags(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); cli(); 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); restore_flags(flags); free++; } if (first) { cli(); zwait; zout(virt_to_bus(first),CER); zout(uPD98401_ADD_BAT | (pool << uPD98401_POOL_SHIFT) | count, CMR); restore_flags(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 */ save_flags(flags); cli(); 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); restore_flags(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);}static void zatm_feedback(struct atm_vcc *vcc,struct sk_buff *skb, unsigned long start,unsigned long dest,int len){ struct zatm_pool_info *pool; unsigned long offset,flags; DPRINTK("start 0x%08lx dest 0x%08lx len %d\n",start,dest,len); if (len < PAGE_SIZE) return; pool = &ZATM_DEV(vcc->dev)->pool_info[ZATM_VCC(vcc)->pool]; offset = (dest-start) & (PAGE_SIZE-1); save_flags(flags); cli(); if (!offset || pool->offset == offset) { pool->next_cnt = 0; restore_flags(flags); return; } if (offset != pool->next_off) { pool->next_off = offset; pool->next_cnt = 0; restore_flags(flags); return; } if (++pool->next_cnt >= pool->next_thres) { pool->offset = pool->next_off; pool->next_cnt = 0; } restore_flags(flags);}/*----------------------- high-precision timestamps -------------------------*/#ifdef CONFIG_ATM_ZATM_EXACT_TSstatic struct timer_list sync_timer;/* * Note: the exact time is not normalized, i.e. tv_usec can be > 1000000. * This must be handled by higher layers. */static inline struct timeval exact_time(struct zatm_dev *zatm_dev,u32 ticks){ struct timeval tmp; tmp = zatm_dev->last_time; tmp.tv_usec += ((s64) (ticks-zatm_dev->last_clk)* (s64) zatm_dev->factor) >> TIMER_SHIFT; return tmp;}static void zatm_clock_sync(unsigned long dummy){ struct atm_dev *atm_dev; struct zatm_dev *zatm_dev; for (atm_dev = zatm_boards; atm_dev; atm_dev = zatm_dev->more) { unsigned long flags,interval; int diff; struct timeval now,expected; u32 ticks; zatm_dev = ZATM_DEV(atm_dev); save_flags(flags); cli(); ticks = zpeekl(zatm_dev,uPD98401_TSR); do_gettimeofday(&now); restore_flags(flags); expected = exact_time(zatm_dev,ticks); diff = 1000000*(expected.tv_sec-now.tv_sec)+ (expected.tv_usec-now.tv_usec); zatm_dev->timer_history[zatm_dev->th_curr].real = now; zatm_dev->timer_history[zatm_dev->th_curr].expected = expected; zatm_dev->th_curr = (zatm_dev->th_curr+1) & (ZATM_TIMER_HISTORY_SIZE-1); interval = 1000000*(now.tv_sec-zatm_dev->last_real_time.tv_sec) +(now.tv_usec-zatm_dev->last_real_time.tv_usec); if (diff >= -ADJ_REP_THRES && diff <= ADJ_REP_THRES) zatm_dev->timer_diffs = 0; else#ifndef AGGRESSIVE_DEBUGGING if (++zatm_dev->timer_diffs >= ADJ_MSG_THRES)#endif { zatm_dev->timer_diffs = 0; printk(KERN_INFO DEV_LABEL ": TSR update after %ld us:" " calculation differed by %d us\n",interval,diff);#ifdef AGGRESSIVE_DEBUGGING printk(KERN_DEBUG " %d.%08d -> %d.%08d (%lu)\n", zatm_dev->last_real_time.tv_sec, zatm_dev->last_real_time.tv_usec, now.tv_sec,now.tv_usec,interval); printk(KERN_DEBUG " %u -> %u (%d)\n", zatm_dev->last_clk,ticks,ticks-zatm_dev->last_clk); printk(KERN_DEBUG " factor %u\n",zatm_dev->factor);#endif } if (diff < -ADJ_IGN_THRES || diff > ADJ_IGN_THRES) { /* filter out any major changes (e.g. time zone setup and such) */ zatm_dev->last_time = now; zatm_dev->factor = (1000 << TIMER_SHIFT)/(zatm_dev->khz+1); } else { zatm_dev->last_time = expected; /* * Is the accuracy of udelay really only about 1:300 on * a 90 MHz Pentium ? Well, the following line avoids * the problem, but ... * * What it does is simply: * * zatm_dev->factor = (interval << TIMER_SHIFT)/ * (ticks-zatm_dev->last_clk); */#define S(x) #x /* "stringification" ... */#define SX(x) S(x) asm("movl %2,%%ebx\n\t" "subl %3,%%ebx\n\t" "xorl %%edx,%%edx\n\t" "shldl $" SX(TIMER_SHIFT) ",%1,%%edx\n\t" "shl $" SX(TIMER_SHIFT) ",%1\n\t" "divl %%ebx\n\t" : "=a" (zatm_dev->factor) : "0" (interval-diff),"g" (ticks), "g" (zatm_dev->last_clk) : "ebx","edx","cc");#undef S#undef SX#ifdef AGGRESSIVE_DEBUGGING printk(KERN_DEBUG " (%ld << %d)/(%u-%u) = %u\n", interval,TIMER_SHIFT,ticks,zatm_dev->last_clk, zatm_dev->factor);#endif } zatm_dev->last_real_time = now; zatm_dev->last_clk = ticks; } mod_timer(&sync_timer,sync_timer.expires+POLL_INTERVAL*HZ);}static void __init zatm_clock_init(struct zatm_dev *zatm_dev){ static int start_timer = 1; unsigned long flags;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -