mirror of
https://github.com/torvalds/linux.git
synced 2024-11-30 16:11:38 +00:00
5a0e3ad6af
percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
1661 lines
43 KiB
C
1661 lines
43 KiB
C
/* drivers/atm/zatm.c - ZeitNet ZN122x device driver */
|
|
|
|
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
|
|
|
|
|
|
#include <linux/module.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/uio.h>
|
|
#include <linux/init.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/atm_zatm.h>
|
|
#include <linux/capability.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/slab.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
|
|
*/
|
|
|
|
#define ZATM_COPPER 1
|
|
|
|
#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 64
|
|
|
|
static 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 0
|
|
static 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]);
|
|
}
|
|
#endif
|
|
|
|
|
|
static 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 0
|
|
printk("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;
|
|
__net_timestamp(skb);
|
|
#if 0
|
|
printk("[-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 0
|
|
printk("dummy: 0x%08lx, 0x%08lx\n",dummy[0],dummy[1]);
|
|
#endif
|
|
size = error ? 0 : ntohs(((__be16 *) 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]) {
|
|
int pos;
|
|
vcc = zatm_dev->rx_map[chan];
|
|
pos = ZATM_VCC(vcc)->pool;
|
|
if (skb == zatm_dev->last_free[pos])
|
|
zatm_dev->last_free[pos] = NULL;
|
|
skb_unlink(skb, zatm_dev->pool + pos);
|
|
}
|
|
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 = DIV_ROUND_UP(vcc->qos.rxtp.max_sdu + ATM_AAL5_TRAILER,
|
|
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;
|
|
shift = (1-(vcc->vci & 1)) << 4;
|
|
zpokel(zatm_dev,(zpeekl(zatm_dev,pos) & ~(0xffff << shift)) |
|
|
((zatm_vcc->rx_chan | uPD98401_RXLT_ENBL) << shift),pos);
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void close_rx(struct atm_vcc *vcc)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
struct zatm_vcc *zatm_vcc;
|
|
unsigned long flags;
|
|
int pos,shift;
|
|
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
if (!zatm_vcc->rx_chan) return;
|
|
DPRINTK("close_rx\n");
|
|
/* disable receiver */
|
|
if (vcc->vpi != ATM_VPI_UNSPEC && vcc->vci != ATM_VCI_UNSPEC) {
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
pos = vcc->vci >> 1;
|
|
shift = (1-(vcc->vci & 1)) << 4;
|
|
zpokel(zatm_dev,zpeekl(zatm_dev,pos) & ~(0xffff << shift),pos);
|
|
zwait;
|
|
zout(uPD98401_NOP,CMR);
|
|
zwait;
|
|
zout(uPD98401_NOP,CMR);
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
}
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
zwait;
|
|
zout(uPD98401_DEACT_CHAN | uPD98401_CHAN_RT | (zatm_vcc->rx_chan <<
|
|
uPD98401_CHAN_ADDR_SHIFT),CMR);
|
|
zwait;
|
|
udelay(10); /* why oh why ... ? */
|
|
zout(uPD98401_CLOSE_CHAN | uPD98401_CHAN_RT | (zatm_vcc->rx_chan <<
|
|
uPD98401_CHAN_ADDR_SHIFT),CMR);
|
|
zwait;
|
|
if (!(zin(CMR) & uPD98401_CHAN_ADDR))
|
|
printk(KERN_CRIT DEV_LABEL "(itf %d): can't close RX channel "
|
|
"%d\n",vcc->dev->number,zatm_vcc->rx_chan);
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
zatm_dev->rx_map[zatm_vcc->rx_chan] = NULL;
|
|
zatm_vcc->rx_chan = 0;
|
|
unuse_pool(vcc->dev,zatm_vcc->pool);
|
|
}
|
|
|
|
|
|
static int start_rx(struct atm_dev *dev)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
int size,i;
|
|
|
|
DPRINTK("start_rx\n");
|
|
zatm_dev = ZATM_DEV(dev);
|
|
size = sizeof(struct atm_vcc *)*zatm_dev->chans;
|
|
zatm_dev->rx_map = kzalloc(size,GFP_KERNEL);
|
|
if (!zatm_dev->rx_map) return -ENOMEM;
|
|
/* set VPI/VCI split (use all VCIs and give what's left to VPIs) */
|
|
zpokel(zatm_dev,(1 << dev->ci_range.vci_bits)-1,uPD98401_VRR);
|
|
/* prepare free buffer pools */
|
|
for (i = 0; i <= ZATM_LAST_POOL; i++) {
|
|
zatm_dev->pool_info[i].ref_count = 0;
|
|
zatm_dev->pool_info[i].rqa_count = 0;
|
|
zatm_dev->pool_info[i].rqu_count = 0;
|
|
zatm_dev->pool_info[i].low_water = LOW_MARK;
|
|
zatm_dev->pool_info[i].high_water = HIGH_MARK;
|
|
zatm_dev->pool_info[i].offset = 0;
|
|
zatm_dev->pool_info[i].next_off = 0;
|
|
zatm_dev->pool_info[i].next_cnt = 0;
|
|
zatm_dev->pool_info[i].next_thres = OFF_CNG_THRES;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*----------------------------------- TX ------------------------------------*/
|
|
|
|
|
|
static int do_tx(struct sk_buff *skb)
|
|
{
|
|
struct atm_vcc *vcc;
|
|
struct zatm_dev *zatm_dev;
|
|
struct zatm_vcc *zatm_vcc;
|
|
u32 *dsc;
|
|
unsigned long flags;
|
|
|
|
EVENT("do_tx\n",0,0);
|
|
DPRINTK("sending skb %p\n",skb);
|
|
vcc = ATM_SKB(skb)->vcc;
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
EVENT("iovcnt=%d\n",skb_shinfo(skb)->nr_frags,0);
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
if (!skb_shinfo(skb)->nr_frags) {
|
|
if (zatm_vcc->txing == RING_ENTRIES-1) {
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
return RING_BUSY;
|
|
}
|
|
zatm_vcc->txing++;
|
|
dsc = zatm_vcc->ring+zatm_vcc->ring_curr;
|
|
zatm_vcc->ring_curr = (zatm_vcc->ring_curr+RING_WORDS) &
|
|
(RING_ENTRIES*RING_WORDS-1);
|
|
dsc[1] = 0;
|
|
dsc[2] = skb->len;
|
|
dsc[3] = virt_to_bus(skb->data);
|
|
mb();
|
|
dsc[0] = uPD98401_TXPD_V | uPD98401_TXPD_DP | uPD98401_TXPD_SM
|
|
| (vcc->qos.aal == ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0 |
|
|
(ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ?
|
|
uPD98401_CLPM_1 : uPD98401_CLPM_0));
|
|
EVENT("dsc (0x%lx)\n",(unsigned long) dsc,0);
|
|
}
|
|
else {
|
|
printk("NONONONOO!!!!\n");
|
|
dsc = NULL;
|
|
#if 0
|
|
u32 *put;
|
|
int i;
|
|
|
|
dsc = kmalloc(uPD98401_TXPD_SIZE * 2 +
|
|
uPD98401_TXBD_SIZE * ATM_SKB(skb)->iovcnt, GFP_ATOMIC);
|
|
if (!dsc) {
|
|
if (vcc->pop)
|
|
vcc->pop(vcc, skb);
|
|
else
|
|
dev_kfree_skb_irq(skb);
|
|
return -EAGAIN;
|
|
}
|
|
/* @@@ should check alignment */
|
|
put = dsc+8;
|
|
dsc[0] = uPD98401_TXPD_V | uPD98401_TXPD_DP |
|
|
(vcc->aal == ATM_AAL5 ? uPD98401_TXPD_AAL5 : 0 |
|
|
(ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ?
|
|
uPD98401_CLPM_1 : uPD98401_CLPM_0));
|
|
dsc[1] = 0;
|
|
dsc[2] = ATM_SKB(skb)->iovcnt * uPD98401_TXBD_SIZE;
|
|
dsc[3] = virt_to_bus(put);
|
|
for (i = 0; i < ATM_SKB(skb)->iovcnt; i++) {
|
|
*put++ = ((struct iovec *) skb->data)[i].iov_len;
|
|
*put++ = virt_to_bus(((struct iovec *)
|
|
skb->data)[i].iov_base);
|
|
}
|
|
put[-2] |= uPD98401_TXBD_LAST;
|
|
#endif
|
|
}
|
|
ZATM_PRV_DSC(skb) = dsc;
|
|
skb_queue_tail(&zatm_vcc->tx_queue,skb);
|
|
DPRINTK("QRP=0x%08lx\n",zpeekl(zatm_dev,zatm_vcc->tx_chan*VC_SIZE/4+
|
|
uPD98401_TXVC_QRP));
|
|
zwait;
|
|
zout(uPD98401_TX_READY | (zatm_vcc->tx_chan <<
|
|
uPD98401_CHAN_ADDR_SHIFT),CMR);
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
EVENT("done\n",0,0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static inline void dequeue_tx(struct atm_vcc *vcc)
|
|
{
|
|
struct zatm_vcc *zatm_vcc;
|
|
struct sk_buff *skb;
|
|
|
|
EVENT("dequeue_tx\n",0,0);
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
skb = skb_dequeue(&zatm_vcc->tx_queue);
|
|
if (!skb) {
|
|
printk(KERN_CRIT DEV_LABEL "(itf %d): dequeue_tx but not "
|
|
"txing\n",vcc->dev->number);
|
|
return;
|
|
}
|
|
#if 0 /* @@@ would fail on CLP */
|
|
if (*ZATM_PRV_DSC(skb) != (uPD98401_TXPD_V | uPD98401_TXPD_DP |
|
|
uPD98401_TXPD_SM | uPD98401_TXPD_AAL5)) printk("@#*$!!!! (%08x)\n",
|
|
*ZATM_PRV_DSC(skb));
|
|
#endif
|
|
*ZATM_PRV_DSC(skb) = 0; /* mark as invalid */
|
|
zatm_vcc->txing--;
|
|
if (vcc->pop) vcc->pop(vcc,skb);
|
|
else dev_kfree_skb_irq(skb);
|
|
while ((skb = skb_dequeue(&zatm_vcc->backlog)))
|
|
if (do_tx(skb) == RING_BUSY) {
|
|
skb_queue_head(&zatm_vcc->backlog,skb);
|
|
break;
|
|
}
|
|
atomic_inc(&vcc->stats->tx);
|
|
wake_up(&zatm_vcc->tx_wait);
|
|
}
|
|
|
|
|
|
static void poll_tx(struct atm_dev *dev,int mbx)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
unsigned long pos;
|
|
u32 x;
|
|
|
|
EVENT("poll_tx\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) {
|
|
int chan;
|
|
|
|
#if 1
|
|
u32 data,*addr;
|
|
|
|
EVENT("MBX: host 0x%lx, nic 0x%x\n",pos,x);
|
|
addr = (u32 *) pos;
|
|
data = *addr;
|
|
chan = (data & uPD98401_TXI_CONN) >> uPD98401_TXI_CONN_SHIFT;
|
|
EVENT("addr = 0x%lx, data = 0x%08x,",(unsigned long) addr,
|
|
data);
|
|
EVENT("chan = %d\n",chan,0);
|
|
#else
|
|
NO !
|
|
chan = (zatm_dev->mbx_start[mbx][pos >> 2] & uPD98401_TXI_CONN)
|
|
>> uPD98401_TXI_CONN_SHIFT;
|
|
#endif
|
|
if (chan < zatm_dev->chans && zatm_dev->tx_map[chan])
|
|
dequeue_tx(zatm_dev->tx_map[chan]);
|
|
else {
|
|
printk(KERN_CRIT DEV_LABEL "(itf %d): TX indication "
|
|
"for non-existing channel %d\n",dev->number,chan);
|
|
event_dump();
|
|
}
|
|
if (((pos += 4) & 0xffff) == zatm_dev->mbx_end[mbx])
|
|
pos = zatm_dev->mbx_start[mbx];
|
|
}
|
|
zout(pos & 0xffff,MTA(mbx));
|
|
}
|
|
|
|
|
|
/*
|
|
* BUG BUG BUG: Doesn't handle "new-style" rate specification yet.
|
|
*/
|
|
|
|
static int alloc_shaper(struct atm_dev *dev,int *pcr,int min,int max,int ubr)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
unsigned long flags;
|
|
unsigned long i,m,c;
|
|
int shaper;
|
|
|
|
DPRINTK("alloc_shaper (min = %d, max = %d)\n",min,max);
|
|
zatm_dev = ZATM_DEV(dev);
|
|
if (!zatm_dev->free_shapers) return -EAGAIN;
|
|
for (shaper = 0; !((zatm_dev->free_shapers >> shaper) & 1); shaper++);
|
|
zatm_dev->free_shapers &= ~1 << shaper;
|
|
if (ubr) {
|
|
c = 5;
|
|
i = m = 1;
|
|
zatm_dev->ubr_ref_cnt++;
|
|
zatm_dev->ubr = shaper;
|
|
*pcr = 0;
|
|
}
|
|
else {
|
|
if (min) {
|
|
if (min <= 255) {
|
|
i = min;
|
|
m = ATM_OC3_PCR;
|
|
}
|
|
else {
|
|
i = 255;
|
|
m = ATM_OC3_PCR*255/min;
|
|
}
|
|
}
|
|
else {
|
|
if (max > zatm_dev->tx_bw) max = zatm_dev->tx_bw;
|
|
if (max <= 255) {
|
|
i = max;
|
|
m = ATM_OC3_PCR;
|
|
}
|
|
else {
|
|
i = 255;
|
|
m = DIV_ROUND_UP(ATM_OC3_PCR*255, max);
|
|
}
|
|
}
|
|
if (i > m) {
|
|
printk(KERN_CRIT DEV_LABEL "shaper algorithm botched "
|
|
"[%d,%d] -> i=%ld,m=%ld\n",min,max,i,m);
|
|
m = i;
|
|
}
|
|
*pcr = i*ATM_OC3_PCR/m;
|
|
c = 20; /* @@@ should use max_cdv ! */
|
|
if ((min && *pcr < min) || (max && *pcr > max)) return -EINVAL;
|
|
if (zatm_dev->tx_bw < *pcr) return -EAGAIN;
|
|
zatm_dev->tx_bw -= *pcr;
|
|
}
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
DPRINTK("i = %d, m = %d, PCR = %d\n",i,m,*pcr);
|
|
zpokel(zatm_dev,(i << uPD98401_IM_I_SHIFT) | m,uPD98401_IM(shaper));
|
|
zpokel(zatm_dev,c << uPD98401_PC_C_SHIFT,uPD98401_PC(shaper));
|
|
zpokel(zatm_dev,0,uPD98401_X(shaper));
|
|
zpokel(zatm_dev,0,uPD98401_Y(shaper));
|
|
zpokel(zatm_dev,uPD98401_PS_E,uPD98401_PS(shaper));
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
return shaper;
|
|
}
|
|
|
|
|
|
static void dealloc_shaper(struct atm_dev *dev,int shaper)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
unsigned long flags;
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
if (shaper == zatm_dev->ubr) {
|
|
if (--zatm_dev->ubr_ref_cnt) return;
|
|
zatm_dev->ubr = -1;
|
|
}
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
zpokel(zatm_dev,zpeekl(zatm_dev,uPD98401_PS(shaper)) & ~uPD98401_PS_E,
|
|
uPD98401_PS(shaper));
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
zatm_dev->free_shapers |= 1 << shaper;
|
|
}
|
|
|
|
|
|
static void close_tx(struct atm_vcc *vcc)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
struct zatm_vcc *zatm_vcc;
|
|
unsigned long flags;
|
|
int chan;
|
|
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
chan = zatm_vcc->tx_chan;
|
|
if (!chan) return;
|
|
DPRINTK("close_tx\n");
|
|
if (skb_peek(&zatm_vcc->backlog)) {
|
|
printk("waiting for backlog to drain ...\n");
|
|
event_dump();
|
|
wait_event(zatm_vcc->tx_wait, !skb_peek(&zatm_vcc->backlog));
|
|
}
|
|
if (skb_peek(&zatm_vcc->tx_queue)) {
|
|
printk("waiting for TX queue to drain ...\n");
|
|
event_dump();
|
|
wait_event(zatm_vcc->tx_wait, !skb_peek(&zatm_vcc->tx_queue));
|
|
}
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
#if 0
|
|
zwait;
|
|
zout(uPD98401_DEACT_CHAN | (chan << uPD98401_CHAN_ADDR_SHIFT),CMR);
|
|
#endif
|
|
zwait;
|
|
zout(uPD98401_CLOSE_CHAN | (chan << uPD98401_CHAN_ADDR_SHIFT),CMR);
|
|
zwait;
|
|
if (!(zin(CMR) & uPD98401_CHAN_ADDR))
|
|
printk(KERN_CRIT DEV_LABEL "(itf %d): can't close TX channel "
|
|
"%d\n",vcc->dev->number,chan);
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
zatm_vcc->tx_chan = 0;
|
|
zatm_dev->tx_map[chan] = NULL;
|
|
if (zatm_vcc->shaper != zatm_dev->ubr) {
|
|
zatm_dev->tx_bw += vcc->qos.txtp.min_pcr;
|
|
dealloc_shaper(vcc->dev,zatm_vcc->shaper);
|
|
}
|
|
kfree(zatm_vcc->ring);
|
|
}
|
|
|
|
|
|
static int open_tx_first(struct atm_vcc *vcc)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
struct zatm_vcc *zatm_vcc;
|
|
unsigned long flags;
|
|
u32 *loop;
|
|
unsigned short chan;
|
|
int unlimited;
|
|
|
|
DPRINTK("open_tx_first\n");
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
zatm_vcc->tx_chan = 0;
|
|
if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0;
|
|
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;
|
|
unlimited = vcc->qos.txtp.traffic_class == ATM_UBR &&
|
|
(!vcc->qos.txtp.max_pcr || vcc->qos.txtp.max_pcr == ATM_MAX_PCR ||
|
|
vcc->qos.txtp.max_pcr >= ATM_OC3_PCR);
|
|
if (unlimited && zatm_dev->ubr != -1) zatm_vcc->shaper = zatm_dev->ubr;
|
|
else {
|
|
int uninitialized_var(pcr);
|
|
|
|
if (unlimited) vcc->qos.txtp.max_sdu = ATM_MAX_AAL5_PDU;
|
|
if ((zatm_vcc->shaper = alloc_shaper(vcc->dev,&pcr,
|
|
vcc->qos.txtp.min_pcr,vcc->qos.txtp.max_pcr,unlimited))
|
|
< 0) {
|
|
close_tx(vcc);
|
|
return zatm_vcc->shaper;
|
|
}
|
|
if (pcr > ATM_OC3_PCR) pcr = ATM_OC3_PCR;
|
|
vcc->qos.txtp.min_pcr = vcc->qos.txtp.max_pcr = pcr;
|
|
}
|
|
zatm_vcc->tx_chan = chan;
|
|
skb_queue_head_init(&zatm_vcc->tx_queue);
|
|
init_waitqueue_head(&zatm_vcc->tx_wait);
|
|
/* initialize ring */
|
|
zatm_vcc->ring = kzalloc(RING_SIZE,GFP_KERNEL);
|
|
if (!zatm_vcc->ring) return -ENOMEM;
|
|
loop = zatm_vcc->ring+RING_ENTRIES*RING_WORDS;
|
|
loop[0] = uPD98401_TXPD_V;
|
|
loop[1] = loop[2] = 0;
|
|
loop[3] = virt_to_bus(zatm_vcc->ring);
|
|
zatm_vcc->ring_curr = 0;
|
|
zatm_vcc->txing = 0;
|
|
skb_queue_head_init(&zatm_vcc->backlog);
|
|
zpokel(zatm_dev,virt_to_bus(zatm_vcc->ring),
|
|
chan*VC_SIZE/4+uPD98401_TXVC_QRP);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int open_tx_second(struct atm_vcc *vcc)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
struct zatm_vcc *zatm_vcc;
|
|
unsigned long flags;
|
|
|
|
DPRINTK("open_tx_second\n");
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
zatm_vcc = ZATM_VCC(vcc);
|
|
if (!zatm_vcc->tx_chan) return 0;
|
|
/* set up VC descriptor */
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
zpokel(zatm_dev,0,zatm_vcc->tx_chan*VC_SIZE/4);
|
|
zpokel(zatm_dev,uPD98401_TXVC_L | (zatm_vcc->shaper <<
|
|
uPD98401_TXVC_SHP_SHIFT) | (vcc->vpi << uPD98401_TXVC_VPI_SHIFT) |
|
|
vcc->vci,zatm_vcc->tx_chan*VC_SIZE/4+1);
|
|
zpokel(zatm_dev,0,zatm_vcc->tx_chan*VC_SIZE/4+2);
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
zatm_dev->tx_map[zatm_vcc->tx_chan] = vcc;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int start_tx(struct atm_dev *dev)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
int i;
|
|
|
|
DPRINTK("start_tx\n");
|
|
zatm_dev = ZATM_DEV(dev);
|
|
zatm_dev->tx_map = kmalloc(sizeof(struct atm_vcc *)*
|
|
zatm_dev->chans,GFP_KERNEL);
|
|
if (!zatm_dev->tx_map) return -ENOMEM;
|
|
zatm_dev->tx_bw = ATM_OC3_PCR;
|
|
zatm_dev->free_shapers = (1 << NR_SHAPERS)-1;
|
|
zatm_dev->ubr = -1;
|
|
zatm_dev->ubr_ref_cnt = 0;
|
|
/* initialize shapers */
|
|
for (i = 0; i < NR_SHAPERS; i++) zpokel(zatm_dev,0,uPD98401_PS(i));
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*------------------------------- interrupts --------------------------------*/
|
|
|
|
|
|
static irqreturn_t zatm_int(int irq,void *dev_id)
|
|
{
|
|
struct atm_dev *dev;
|
|
struct zatm_dev *zatm_dev;
|
|
u32 reason;
|
|
int handled = 0;
|
|
|
|
dev = dev_id;
|
|
zatm_dev = ZATM_DEV(dev);
|
|
while ((reason = zin(GSR))) {
|
|
handled = 1;
|
|
EVENT("reason 0x%x\n",reason,0);
|
|
if (reason & uPD98401_INT_PI) {
|
|
EVENT("PHY int\n",0,0);
|
|
dev->phy->interrupt(dev);
|
|
}
|
|
if (reason & uPD98401_INT_RQA) {
|
|
unsigned long pools;
|
|
int i;
|
|
|
|
pools = zin(RQA);
|
|
EVENT("RQA (0x%08x)\n",pools,0);
|
|
for (i = 0; pools; i++) {
|
|
if (pools & 1) {
|
|
refill_pool(dev,i);
|
|
zatm_dev->pool_info[i].rqa_count++;
|
|
}
|
|
pools >>= 1;
|
|
}
|
|
}
|
|
if (reason & uPD98401_INT_RQU) {
|
|
unsigned long pools;
|
|
int i;
|
|
pools = zin(RQU);
|
|
printk(KERN_WARNING DEV_LABEL "(itf %d): RQU 0x%08lx\n",
|
|
dev->number,pools);
|
|
event_dump();
|
|
for (i = 0; pools; i++) {
|
|
if (pools & 1) {
|
|
refill_pool(dev,i);
|
|
zatm_dev->pool_info[i].rqu_count++;
|
|
}
|
|
pools >>= 1;
|
|
}
|
|
}
|
|
/* don't handle RD */
|
|
if (reason & uPD98401_INT_SPE)
|
|
printk(KERN_ALERT DEV_LABEL "(itf %d): system parity "
|
|
"error at 0x%08x\n",dev->number,zin(ADDR));
|
|
if (reason & uPD98401_INT_CPE)
|
|
printk(KERN_ALERT DEV_LABEL "(itf %d): control memory "
|
|
"parity error at 0x%08x\n",dev->number,zin(ADDR));
|
|
if (reason & uPD98401_INT_SBE) {
|
|
printk(KERN_ALERT DEV_LABEL "(itf %d): system bus "
|
|
"error at 0x%08x\n",dev->number,zin(ADDR));
|
|
event_dump();
|
|
}
|
|
/* don't handle IND */
|
|
if (reason & uPD98401_INT_MF) {
|
|
printk(KERN_CRIT DEV_LABEL "(itf %d): mailbox full "
|
|
"(0x%x)\n",dev->number,(reason & uPD98401_INT_MF)
|
|
>> uPD98401_INT_MF_SHIFT);
|
|
event_dump();
|
|
/* @@@ should try to recover */
|
|
}
|
|
if (reason & uPD98401_INT_MM) {
|
|
if (reason & 1) poll_rx(dev,0);
|
|
if (reason & 2) poll_rx(dev,1);
|
|
if (reason & 4) poll_tx(dev,2);
|
|
if (reason & 8) poll_tx(dev,3);
|
|
}
|
|
/* @@@ handle RCRn */
|
|
}
|
|
return IRQ_RETVAL(handled);
|
|
}
|
|
|
|
|
|
/*----------------------------- (E)EPROM access -----------------------------*/
|
|
|
|
|
|
static void __devinit eprom_set(struct zatm_dev *zatm_dev,unsigned long value,
|
|
unsigned short cmd)
|
|
{
|
|
int error;
|
|
|
|
if ((error = pci_write_config_dword(zatm_dev->pci_dev,cmd,value)))
|
|
printk(KERN_ERR DEV_LABEL ": PCI write failed (0x%02x)\n",
|
|
error);
|
|
}
|
|
|
|
|
|
static unsigned long __devinit eprom_get(struct zatm_dev *zatm_dev,
|
|
unsigned short cmd)
|
|
{
|
|
unsigned int value;
|
|
int error;
|
|
|
|
if ((error = pci_read_config_dword(zatm_dev->pci_dev,cmd,&value)))
|
|
printk(KERN_ERR DEV_LABEL ": PCI read failed (0x%02x)\n",
|
|
error);
|
|
return value;
|
|
}
|
|
|
|
|
|
static void __devinit eprom_put_bits(struct zatm_dev *zatm_dev,
|
|
unsigned long data,int bits,unsigned short cmd)
|
|
{
|
|
unsigned long value;
|
|
int i;
|
|
|
|
for (i = bits-1; i >= 0; i--) {
|
|
value = ZEPROM_CS | (((data >> i) & 1) ? ZEPROM_DI : 0);
|
|
eprom_set(zatm_dev,value,cmd);
|
|
eprom_set(zatm_dev,value | ZEPROM_SK,cmd);
|
|
eprom_set(zatm_dev,value,cmd);
|
|
}
|
|
}
|
|
|
|
|
|
static void __devinit eprom_get_byte(struct zatm_dev *zatm_dev,
|
|
unsigned char *byte,unsigned short cmd)
|
|
{
|
|
int i;
|
|
|
|
*byte = 0;
|
|
for (i = 8; i; i--) {
|
|
eprom_set(zatm_dev,ZEPROM_CS,cmd);
|
|
eprom_set(zatm_dev,ZEPROM_CS | ZEPROM_SK,cmd);
|
|
*byte <<= 1;
|
|
if (eprom_get(zatm_dev,cmd) & ZEPROM_DO) *byte |= 1;
|
|
eprom_set(zatm_dev,ZEPROM_CS,cmd);
|
|
}
|
|
}
|
|
|
|
|
|
static unsigned char __devinit eprom_try_esi(struct atm_dev *dev,
|
|
unsigned short cmd,int offset,int swap)
|
|
{
|
|
unsigned char buf[ZEPROM_SIZE];
|
|
struct zatm_dev *zatm_dev;
|
|
int i;
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
for (i = 0; i < ZEPROM_SIZE; i += 2) {
|
|
eprom_set(zatm_dev,ZEPROM_CS,cmd); /* select EPROM */
|
|
eprom_put_bits(zatm_dev,ZEPROM_CMD_READ,ZEPROM_CMD_LEN,cmd);
|
|
eprom_put_bits(zatm_dev,i >> 1,ZEPROM_ADDR_LEN,cmd);
|
|
eprom_get_byte(zatm_dev,buf+i+swap,cmd);
|
|
eprom_get_byte(zatm_dev,buf+i+1-swap,cmd);
|
|
eprom_set(zatm_dev,0,cmd); /* deselect EPROM */
|
|
}
|
|
memcpy(dev->esi,buf+offset,ESI_LEN);
|
|
return memcmp(dev->esi,"\0\0\0\0\0",ESI_LEN); /* assumes ESI_LEN == 6 */
|
|
}
|
|
|
|
|
|
static void __devinit eprom_get_esi(struct atm_dev *dev)
|
|
{
|
|
if (eprom_try_esi(dev,ZEPROM_V1_REG,ZEPROM_V1_ESI_OFF,1)) return;
|
|
(void) eprom_try_esi(dev,ZEPROM_V2_REG,ZEPROM_V2_ESI_OFF,0);
|
|
}
|
|
|
|
|
|
/*--------------------------------- entries ---------------------------------*/
|
|
|
|
|
|
static int __devinit zatm_init(struct atm_dev *dev)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
struct pci_dev *pci_dev;
|
|
unsigned short command;
|
|
int error,i,last;
|
|
unsigned long t0,t1,t2;
|
|
|
|
DPRINTK(">zatm_init\n");
|
|
zatm_dev = ZATM_DEV(dev);
|
|
spin_lock_init(&zatm_dev->lock);
|
|
pci_dev = zatm_dev->pci_dev;
|
|
zatm_dev->base = pci_resource_start(pci_dev, 0);
|
|
zatm_dev->irq = pci_dev->irq;
|
|
if ((error = pci_read_config_word(pci_dev,PCI_COMMAND,&command))) {
|
|
printk(KERN_ERR DEV_LABEL "(itf %d): init error 0x%02x\n",
|
|
dev->number,error);
|
|
return -EINVAL;
|
|
}
|
|
if ((error = pci_write_config_word(pci_dev,PCI_COMMAND,
|
|
command | PCI_COMMAND_IO | PCI_COMMAND_MASTER))) {
|
|
printk(KERN_ERR DEV_LABEL "(itf %d): can't enable IO (0x%02x)"
|
|
"\n",dev->number,error);
|
|
return -EIO;
|
|
}
|
|
eprom_get_esi(dev);
|
|
printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%x,irq=%d,",
|
|
dev->number,pci_dev->revision,zatm_dev->base,zatm_dev->irq);
|
|
/* reset uPD98401 */
|
|
zout(0,SWR);
|
|
while (!(zin(GSR) & uPD98401_INT_IND));
|
|
zout(uPD98401_GMR_ONE /*uPD98401_BURST4*/,GMR);
|
|
last = MAX_CRAM_SIZE;
|
|
for (i = last-RAM_INCREMENT; i >= 0; i -= RAM_INCREMENT) {
|
|
zpokel(zatm_dev,0x55555555,i);
|
|
if (zpeekl(zatm_dev,i) != 0x55555555) last = i;
|
|
else {
|
|
zpokel(zatm_dev,0xAAAAAAAA,i);
|
|
if (zpeekl(zatm_dev,i) != 0xAAAAAAAA) last = i;
|
|
else zpokel(zatm_dev,i,i);
|
|
}
|
|
}
|
|
for (i = 0; i < last; i += RAM_INCREMENT)
|
|
if (zpeekl(zatm_dev,i) != i) break;
|
|
zatm_dev->mem = i << 2;
|
|
while (i) zpokel(zatm_dev,0,--i);
|
|
/* reset again to rebuild memory pointers */
|
|
zout(0,SWR);
|
|
while (!(zin(GSR) & uPD98401_INT_IND));
|
|
zout(uPD98401_GMR_ONE | uPD98401_BURST8 | uPD98401_BURST4 |
|
|
uPD98401_BURST2 | uPD98401_GMR_PM | uPD98401_GMR_DR,GMR);
|
|
/* TODO: should shrink allocation now */
|
|
printk("mem=%dkB,%s (",zatm_dev->mem >> 10,zatm_dev->copper ? "UTP" :
|
|
"MMF");
|
|
for (i = 0; i < ESI_LEN; i++)
|
|
printk("%02X%s",dev->esi[i],i == ESI_LEN-1 ? ")\n" : "-");
|
|
do {
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
t0 = zpeekl(zatm_dev,uPD98401_TSR);
|
|
udelay(10);
|
|
t1 = zpeekl(zatm_dev,uPD98401_TSR);
|
|
udelay(1010);
|
|
t2 = zpeekl(zatm_dev,uPD98401_TSR);
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
}
|
|
while (t0 > t1 || t1 > t2); /* loop if wrapping ... */
|
|
zatm_dev->khz = t2-2*t1+t0;
|
|
printk(KERN_NOTICE DEV_LABEL "(itf %d): uPD98401 %d.%d at %d.%03d "
|
|
"MHz\n",dev->number,
|
|
(zin(VER) & uPD98401_MAJOR) >> uPD98401_MAJOR_SHIFT,
|
|
zin(VER) & uPD98401_MINOR,zatm_dev->khz/1000,zatm_dev->khz % 1000);
|
|
return uPD98402_init(dev);
|
|
}
|
|
|
|
|
|
static int __devinit zatm_start(struct atm_dev *dev)
|
|
{
|
|
struct zatm_dev *zatm_dev = ZATM_DEV(dev);
|
|
struct pci_dev *pdev = zatm_dev->pci_dev;
|
|
unsigned long curr;
|
|
int pools,vccs,rx;
|
|
int error, i, ld;
|
|
|
|
DPRINTK("zatm_start\n");
|
|
zatm_dev->rx_map = zatm_dev->tx_map = NULL;
|
|
for (i = 0; i < NR_MBX; i++)
|
|
zatm_dev->mbx_start[i] = 0;
|
|
error = request_irq(zatm_dev->irq, zatm_int, IRQF_SHARED, DEV_LABEL, dev);
|
|
if (error < 0) {
|
|
printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n",
|
|
dev->number,zatm_dev->irq);
|
|
goto done;
|
|
}
|
|
/* define memory regions */
|
|
pools = NR_POOLS;
|
|
if (NR_SHAPERS*SHAPER_SIZE > pools*POOL_SIZE)
|
|
pools = NR_SHAPERS*SHAPER_SIZE/POOL_SIZE;
|
|
vccs = (zatm_dev->mem-NR_SHAPERS*SHAPER_SIZE-pools*POOL_SIZE)/
|
|
(2*VC_SIZE+RX_SIZE);
|
|
ld = -1;
|
|
for (rx = 1; rx < vccs; rx <<= 1) ld++;
|
|
dev->ci_range.vpi_bits = 0; /* @@@ no VPI for now */
|
|
dev->ci_range.vci_bits = ld;
|
|
dev->link_rate = ATM_OC3_PCR;
|
|
zatm_dev->chans = vccs; /* ??? */
|
|
curr = rx*RX_SIZE/4;
|
|
DPRINTK("RX pool 0x%08lx\n",curr);
|
|
zpokel(zatm_dev,curr,uPD98401_PMA); /* receive pool */
|
|
zatm_dev->pool_base = curr;
|
|
curr += pools*POOL_SIZE/4;
|
|
DPRINTK("Shapers 0x%08lx\n",curr);
|
|
zpokel(zatm_dev,curr,uPD98401_SMA); /* shapers */
|
|
curr += NR_SHAPERS*SHAPER_SIZE/4;
|
|
DPRINTK("Free 0x%08lx\n",curr);
|
|
zpokel(zatm_dev,curr,uPD98401_TOS); /* free pool */
|
|
printk(KERN_INFO DEV_LABEL "(itf %d): %d shapers, %d pools, %d RX, "
|
|
"%ld VCs\n",dev->number,NR_SHAPERS,pools,rx,
|
|
(zatm_dev->mem-curr*4)/VC_SIZE);
|
|
/* create mailboxes */
|
|
for (i = 0; i < NR_MBX; i++) {
|
|
void *mbx;
|
|
dma_addr_t mbx_dma;
|
|
|
|
if (!mbx_entries[i])
|
|
continue;
|
|
mbx = pci_alloc_consistent(pdev, 2*MBX_SIZE(i), &mbx_dma);
|
|
if (!mbx) {
|
|
error = -ENOMEM;
|
|
goto out;
|
|
}
|
|
/*
|
|
* Alignment provided by pci_alloc_consistent() isn't enough
|
|
* for this device.
|
|
*/
|
|
if (((unsigned long)mbx ^ mbx_dma) & 0xffff) {
|
|
printk(KERN_ERR DEV_LABEL "(itf %d): system "
|
|
"bus incompatible with driver\n", dev->number);
|
|
pci_free_consistent(pdev, 2*MBX_SIZE(i), mbx, mbx_dma);
|
|
error = -ENODEV;
|
|
goto out;
|
|
}
|
|
DPRINTK("mbx@0x%08lx-0x%08lx\n", mbx, mbx + MBX_SIZE(i));
|
|
zatm_dev->mbx_start[i] = (unsigned long)mbx;
|
|
zatm_dev->mbx_dma[i] = mbx_dma;
|
|
zatm_dev->mbx_end[i] = (zatm_dev->mbx_start[i] + MBX_SIZE(i)) &
|
|
0xffff;
|
|
zout(mbx_dma >> 16, MSH(i));
|
|
zout(mbx_dma, MSL(i));
|
|
zout(zatm_dev->mbx_end[i], MBA(i));
|
|
zout((unsigned long)mbx & 0xffff, MTA(i));
|
|
zout((unsigned long)mbx & 0xffff, MWA(i));
|
|
}
|
|
error = start_tx(dev);
|
|
if (error)
|
|
goto out;
|
|
error = start_rx(dev);
|
|
if (error)
|
|
goto out_tx;
|
|
error = dev->phy->start(dev);
|
|
if (error)
|
|
goto out_rx;
|
|
zout(0xffffffff,IMR); /* enable interrupts */
|
|
/* enable TX & RX */
|
|
zout(zin(GMR) | uPD98401_GMR_SE | uPD98401_GMR_RE,GMR);
|
|
done:
|
|
return error;
|
|
|
|
out_rx:
|
|
kfree(zatm_dev->rx_map);
|
|
out_tx:
|
|
kfree(zatm_dev->tx_map);
|
|
out:
|
|
while (i-- > 0) {
|
|
pci_free_consistent(pdev, 2*MBX_SIZE(i),
|
|
(void *)zatm_dev->mbx_start[i],
|
|
zatm_dev->mbx_dma[i]);
|
|
}
|
|
free_irq(zatm_dev->irq, dev);
|
|
goto done;
|
|
}
|
|
|
|
|
|
static void zatm_close(struct atm_vcc *vcc)
|
|
{
|
|
DPRINTK(">zatm_close\n");
|
|
if (!ZATM_VCC(vcc)) return;
|
|
clear_bit(ATM_VF_READY,&vcc->flags);
|
|
close_rx(vcc);
|
|
EVENT("close_tx\n",0,0);
|
|
close_tx(vcc);
|
|
DPRINTK("zatm_close: done waiting\n");
|
|
/* deallocate memory */
|
|
kfree(ZATM_VCC(vcc));
|
|
vcc->dev_data = NULL;
|
|
clear_bit(ATM_VF_ADDR,&vcc->flags);
|
|
}
|
|
|
|
|
|
static int zatm_open(struct atm_vcc *vcc)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
struct zatm_vcc *zatm_vcc;
|
|
short vpi = vcc->vpi;
|
|
int vci = vcc->vci;
|
|
int error;
|
|
|
|
DPRINTK(">zatm_open\n");
|
|
zatm_dev = ZATM_DEV(vcc->dev);
|
|
if (!test_bit(ATM_VF_PARTIAL,&vcc->flags))
|
|
vcc->dev_data = NULL;
|
|
if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
|
|
set_bit(ATM_VF_ADDR,&vcc->flags);
|
|
if (vcc->qos.aal != ATM_AAL5) return -EINVAL; /* @@@ AAL0 */
|
|
DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi,
|
|
vcc->vci);
|
|
if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) {
|
|
zatm_vcc = kmalloc(sizeof(struct zatm_vcc),GFP_KERNEL);
|
|
if (!zatm_vcc) {
|
|
clear_bit(ATM_VF_ADDR,&vcc->flags);
|
|
return -ENOMEM;
|
|
}
|
|
vcc->dev_data = zatm_vcc;
|
|
ZATM_VCC(vcc)->tx_chan = 0; /* for zatm_close after open_rx */
|
|
if ((error = open_rx_first(vcc))) {
|
|
zatm_close(vcc);
|
|
return error;
|
|
}
|
|
if ((error = open_tx_first(vcc))) {
|
|
zatm_close(vcc);
|
|
return error;
|
|
}
|
|
}
|
|
if (vci == ATM_VPI_UNSPEC || vpi == ATM_VCI_UNSPEC) return 0;
|
|
if ((error = open_rx_second(vcc))) {
|
|
zatm_close(vcc);
|
|
return error;
|
|
}
|
|
if ((error = open_tx_second(vcc))) {
|
|
zatm_close(vcc);
|
|
return error;
|
|
}
|
|
set_bit(ATM_VF_READY,&vcc->flags);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int zatm_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flags)
|
|
{
|
|
printk("Not yet implemented\n");
|
|
return -ENOSYS;
|
|
/* @@@ */
|
|
}
|
|
|
|
|
|
static int zatm_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
unsigned long flags;
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
switch (cmd) {
|
|
case ZATM_GETPOOLZ:
|
|
if (!capable(CAP_NET_ADMIN)) return -EPERM;
|
|
/* fall through */
|
|
case ZATM_GETPOOL:
|
|
{
|
|
struct zatm_pool_info info;
|
|
int pool;
|
|
|
|
if (get_user(pool,
|
|
&((struct zatm_pool_req __user *) arg)->pool_num))
|
|
return -EFAULT;
|
|
if (pool < 0 || pool > ZATM_LAST_POOL)
|
|
return -EINVAL;
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
info = zatm_dev->pool_info[pool];
|
|
if (cmd == ZATM_GETPOOLZ) {
|
|
zatm_dev->pool_info[pool].rqa_count = 0;
|
|
zatm_dev->pool_info[pool].rqu_count = 0;
|
|
}
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
return copy_to_user(
|
|
&((struct zatm_pool_req __user *) arg)->info,
|
|
&info,sizeof(info)) ? -EFAULT : 0;
|
|
}
|
|
case ZATM_SETPOOL:
|
|
{
|
|
struct zatm_pool_info info;
|
|
int pool;
|
|
|
|
if (!capable(CAP_NET_ADMIN)) return -EPERM;
|
|
if (get_user(pool,
|
|
&((struct zatm_pool_req __user *) arg)->pool_num))
|
|
return -EFAULT;
|
|
if (pool < 0 || pool > ZATM_LAST_POOL)
|
|
return -EINVAL;
|
|
if (copy_from_user(&info,
|
|
&((struct zatm_pool_req __user *) arg)->info,
|
|
sizeof(info))) return -EFAULT;
|
|
if (!info.low_water)
|
|
info.low_water = zatm_dev->
|
|
pool_info[pool].low_water;
|
|
if (!info.high_water)
|
|
info.high_water = zatm_dev->
|
|
pool_info[pool].high_water;
|
|
if (!info.next_thres)
|
|
info.next_thres = zatm_dev->
|
|
pool_info[pool].next_thres;
|
|
if (info.low_water >= info.high_water ||
|
|
info.low_water < 0)
|
|
return -EINVAL;
|
|
spin_lock_irqsave(&zatm_dev->lock, flags);
|
|
zatm_dev->pool_info[pool].low_water =
|
|
info.low_water;
|
|
zatm_dev->pool_info[pool].high_water =
|
|
info.high_water;
|
|
zatm_dev->pool_info[pool].next_thres =
|
|
info.next_thres;
|
|
spin_unlock_irqrestore(&zatm_dev->lock, flags);
|
|
return 0;
|
|
}
|
|
default:
|
|
if (!dev->phy->ioctl) return -ENOIOCTLCMD;
|
|
return dev->phy->ioctl(dev,cmd,arg);
|
|
}
|
|
}
|
|
|
|
|
|
static int zatm_getsockopt(struct atm_vcc *vcc,int level,int optname,
|
|
void __user *optval,int optlen)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
static int zatm_setsockopt(struct atm_vcc *vcc,int level,int optname,
|
|
void __user *optval,unsigned int optlen)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int zatm_send(struct atm_vcc *vcc,struct sk_buff *skb)
|
|
{
|
|
int error;
|
|
|
|
EVENT(">zatm_send 0x%lx\n",(unsigned long) skb,0);
|
|
if (!ZATM_VCC(vcc)->tx_chan || !test_bit(ATM_VF_READY,&vcc->flags)) {
|
|
if (vcc->pop) vcc->pop(vcc,skb);
|
|
else dev_kfree_skb(skb);
|
|
return -EINVAL;
|
|
}
|
|
if (!skb) {
|
|
printk(KERN_CRIT "!skb in zatm_send ?\n");
|
|
if (vcc->pop) vcc->pop(vcc,skb);
|
|
return -EINVAL;
|
|
}
|
|
ATM_SKB(skb)->vcc = vcc;
|
|
error = do_tx(skb);
|
|
if (error != RING_BUSY) return error;
|
|
skb_queue_tail(&ZATM_VCC(vcc)->backlog,skb);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void zatm_phy_put(struct atm_dev *dev,unsigned char value,
|
|
unsigned long addr)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
zwait;
|
|
zout(value,CER);
|
|
zout(uPD98401_IND_ACC | uPD98401_IA_B0 |
|
|
(uPD98401_IA_TGT_PHY << uPD98401_IA_TGT_SHIFT) | addr,CMR);
|
|
}
|
|
|
|
|
|
static unsigned char zatm_phy_get(struct atm_dev *dev,unsigned long addr)
|
|
{
|
|
struct zatm_dev *zatm_dev;
|
|
|
|
zatm_dev = ZATM_DEV(dev);
|
|
zwait;
|
|
zout(uPD98401_IND_ACC | uPD98401_IA_B0 | uPD98401_IA_RW |
|
|
(uPD98401_IA_TGT_PHY << uPD98401_IA_TGT_SHIFT) | addr,CMR);
|
|
zwait;
|
|
return zin(CER) & 0xff;
|
|
}
|
|
|
|
|
|
static const struct atmdev_ops ops = {
|
|
.open = zatm_open,
|
|
.close = zatm_close,
|
|
.ioctl = zatm_ioctl,
|
|
.getsockopt = zatm_getsockopt,
|
|
.setsockopt = zatm_setsockopt,
|
|
.send = zatm_send,
|
|
.phy_put = zatm_phy_put,
|
|
.phy_get = zatm_phy_get,
|
|
.change_qos = zatm_change_qos,
|
|
};
|
|
|
|
static int __devinit zatm_init_one(struct pci_dev *pci_dev,
|
|
const struct pci_device_id *ent)
|
|
{
|
|
struct atm_dev *dev;
|
|
struct zatm_dev *zatm_dev;
|
|
int ret = -ENOMEM;
|
|
|
|
zatm_dev = kmalloc(sizeof(*zatm_dev), GFP_KERNEL);
|
|
if (!zatm_dev) {
|
|
printk(KERN_EMERG "%s: memory shortage\n", DEV_LABEL);
|
|
goto out;
|
|
}
|
|
|
|
dev = atm_dev_register(DEV_LABEL, &ops, -1, NULL);
|
|
if (!dev)
|
|
goto out_free;
|
|
|
|
ret = pci_enable_device(pci_dev);
|
|
if (ret < 0)
|
|
goto out_deregister;
|
|
|
|
ret = pci_request_regions(pci_dev, DEV_LABEL);
|
|
if (ret < 0)
|
|
goto out_disable;
|
|
|
|
zatm_dev->pci_dev = pci_dev;
|
|
dev->dev_data = zatm_dev;
|
|
zatm_dev->copper = (int)ent->driver_data;
|
|
if ((ret = zatm_init(dev)) || (ret = zatm_start(dev)))
|
|
goto out_release;
|
|
|
|
pci_set_drvdata(pci_dev, dev);
|
|
zatm_dev->more = zatm_boards;
|
|
zatm_boards = dev;
|
|
ret = 0;
|
|
out:
|
|
return ret;
|
|
|
|
out_release:
|
|
pci_release_regions(pci_dev);
|
|
out_disable:
|
|
pci_disable_device(pci_dev);
|
|
out_deregister:
|
|
atm_dev_deregister(dev);
|
|
out_free:
|
|
kfree(zatm_dev);
|
|
goto out;
|
|
}
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
static struct pci_device_id zatm_pci_tbl[] __devinitdata = {
|
|
{ PCI_VENDOR_ID_ZEITNET, PCI_DEVICE_ID_ZEITNET_1221,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, ZATM_COPPER },
|
|
{ PCI_VENDOR_ID_ZEITNET, PCI_DEVICE_ID_ZEITNET_1225,
|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
|
{ 0, }
|
|
};
|
|
MODULE_DEVICE_TABLE(pci, zatm_pci_tbl);
|
|
|
|
static struct pci_driver zatm_driver = {
|
|
.name = DEV_LABEL,
|
|
.id_table = zatm_pci_tbl,
|
|
.probe = zatm_init_one,
|
|
};
|
|
|
|
static int __init zatm_init_module(void)
|
|
{
|
|
return pci_register_driver(&zatm_driver);
|
|
}
|
|
|
|
module_init(zatm_init_module);
|
|
/* module_exit not defined so not unloadable */
|