mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-18 09:44:18 +08:00
d3e709e63e
It is a relatively common idiom (8 instances) to first look up an IDR entry, and then remove it from the tree if it is found, possibly doing further operations upon the entry afterwards. If we change idr_remove() to return the removed object, all of these users can save themselves a walk of the IDR tree. Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com>
2751 lines
72 KiB
C
2751 lines
72 KiB
C
/*
|
|
* nicstar.c
|
|
*
|
|
* Device driver supporting CBR for IDT 77201/77211 "NICStAR" based cards.
|
|
*
|
|
* IMPORTANT: The included file nicstarmac.c was NOT WRITTEN BY ME.
|
|
* It was taken from the frle-0.22 device driver.
|
|
* As the file doesn't have a copyright notice, in the file
|
|
* nicstarmac.copyright I put the copyright notice from the
|
|
* frle-0.22 device driver.
|
|
* Some code is based on the nicstar driver by M. Welsh.
|
|
*
|
|
* Author: Rui Prior (rprior@inescn.pt)
|
|
* PowerPC support by Jay Talbott (jay_talbott@mcg.mot.com) April 1999
|
|
*
|
|
*
|
|
* (C) INESC 1999
|
|
*/
|
|
|
|
/*
|
|
* IMPORTANT INFORMATION
|
|
*
|
|
* There are currently three types of spinlocks:
|
|
*
|
|
* 1 - Per card interrupt spinlock (to protect structures and such)
|
|
* 2 - Per SCQ scq spinlock
|
|
* 3 - Per card resource spinlock (to access registers, etc.)
|
|
*
|
|
* These must NEVER be grabbed in reverse order.
|
|
*
|
|
*/
|
|
|
|
/* Header files */
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/atmdev.h>
|
|
#include <linux/atm.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/types.h>
|
|
#include <linux/string.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/init.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/idr.h>
|
|
#include <asm/io.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/atomic.h>
|
|
#include <linux/etherdevice.h>
|
|
#include "nicstar.h"
|
|
#ifdef CONFIG_ATM_NICSTAR_USE_SUNI
|
|
#include "suni.h"
|
|
#endif /* CONFIG_ATM_NICSTAR_USE_SUNI */
|
|
#ifdef CONFIG_ATM_NICSTAR_USE_IDT77105
|
|
#include "idt77105.h"
|
|
#endif /* CONFIG_ATM_NICSTAR_USE_IDT77105 */
|
|
|
|
/* Additional code */
|
|
|
|
#include "nicstarmac.c"
|
|
|
|
/* Configurable parameters */
|
|
|
|
#undef PHY_LOOPBACK
|
|
#undef TX_DEBUG
|
|
#undef RX_DEBUG
|
|
#undef GENERAL_DEBUG
|
|
#undef EXTRA_DEBUG
|
|
|
|
/* Do not touch these */
|
|
|
|
#ifdef TX_DEBUG
|
|
#define TXPRINTK(args...) printk(args)
|
|
#else
|
|
#define TXPRINTK(args...)
|
|
#endif /* TX_DEBUG */
|
|
|
|
#ifdef RX_DEBUG
|
|
#define RXPRINTK(args...) printk(args)
|
|
#else
|
|
#define RXPRINTK(args...)
|
|
#endif /* RX_DEBUG */
|
|
|
|
#ifdef GENERAL_DEBUG
|
|
#define PRINTK(args...) printk(args)
|
|
#else
|
|
#define PRINTK(args...)
|
|
#endif /* GENERAL_DEBUG */
|
|
|
|
#ifdef EXTRA_DEBUG
|
|
#define XPRINTK(args...) printk(args)
|
|
#else
|
|
#define XPRINTK(args...)
|
|
#endif /* EXTRA_DEBUG */
|
|
|
|
/* Macros */
|
|
|
|
#define CMD_BUSY(card) (readl((card)->membase + STAT) & NS_STAT_CMDBZ)
|
|
|
|
#define NS_DELAY mdelay(1)
|
|
|
|
#define PTR_DIFF(a, b) ((u32)((unsigned long)(a) - (unsigned long)(b)))
|
|
|
|
#ifndef ATM_SKB
|
|
#define ATM_SKB(s) (&(s)->atm)
|
|
#endif
|
|
|
|
#define scq_virt_to_bus(scq, p) \
|
|
(scq->dma + ((unsigned long)(p) - (unsigned long)(scq)->org))
|
|
|
|
/* Function declarations */
|
|
|
|
static u32 ns_read_sram(ns_dev * card, u32 sram_address);
|
|
static void ns_write_sram(ns_dev * card, u32 sram_address, u32 * value,
|
|
int count);
|
|
static int ns_init_card(int i, struct pci_dev *pcidev);
|
|
static void ns_init_card_error(ns_dev * card, int error);
|
|
static scq_info *get_scq(ns_dev *card, int size, u32 scd);
|
|
static void free_scq(ns_dev *card, scq_info * scq, struct atm_vcc *vcc);
|
|
static void push_rxbufs(ns_dev *, struct sk_buff *);
|
|
static irqreturn_t ns_irq_handler(int irq, void *dev_id);
|
|
static int ns_open(struct atm_vcc *vcc);
|
|
static void ns_close(struct atm_vcc *vcc);
|
|
static void fill_tst(ns_dev * card, int n, vc_map * vc);
|
|
static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb);
|
|
static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd,
|
|
struct sk_buff *skb);
|
|
static void process_tsq(ns_dev * card);
|
|
static void drain_scq(ns_dev * card, scq_info * scq, int pos);
|
|
static void process_rsq(ns_dev * card);
|
|
static void dequeue_rx(ns_dev * card, ns_rsqe * rsqe);
|
|
static void recycle_rx_buf(ns_dev * card, struct sk_buff *skb);
|
|
static void recycle_iovec_rx_bufs(ns_dev * card, struct iovec *iov, int count);
|
|
static void recycle_iov_buf(ns_dev * card, struct sk_buff *iovb);
|
|
static void dequeue_sm_buf(ns_dev * card, struct sk_buff *sb);
|
|
static void dequeue_lg_buf(ns_dev * card, struct sk_buff *lb);
|
|
static int ns_proc_read(struct atm_dev *dev, loff_t * pos, char *page);
|
|
static int ns_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg);
|
|
#ifdef EXTRA_DEBUG
|
|
static void which_list(ns_dev * card, struct sk_buff *skb);
|
|
#endif
|
|
static void ns_poll(unsigned long arg);
|
|
static void ns_phy_put(struct atm_dev *dev, unsigned char value,
|
|
unsigned long addr);
|
|
static unsigned char ns_phy_get(struct atm_dev *dev, unsigned long addr);
|
|
|
|
/* Global variables */
|
|
|
|
static struct ns_dev *cards[NS_MAX_CARDS];
|
|
static unsigned num_cards;
|
|
static struct atmdev_ops atm_ops = {
|
|
.open = ns_open,
|
|
.close = ns_close,
|
|
.ioctl = ns_ioctl,
|
|
.send = ns_send,
|
|
.phy_put = ns_phy_put,
|
|
.phy_get = ns_phy_get,
|
|
.proc_read = ns_proc_read,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
static struct timer_list ns_timer;
|
|
static char *mac[NS_MAX_CARDS];
|
|
module_param_array(mac, charp, NULL, 0);
|
|
MODULE_LICENSE("GPL");
|
|
|
|
/* Functions */
|
|
|
|
static int nicstar_init_one(struct pci_dev *pcidev,
|
|
const struct pci_device_id *ent)
|
|
{
|
|
static int index = -1;
|
|
unsigned int error;
|
|
|
|
index++;
|
|
cards[index] = NULL;
|
|
|
|
error = ns_init_card(index, pcidev);
|
|
if (error) {
|
|
cards[index--] = NULL; /* don't increment index */
|
|
goto err_out;
|
|
}
|
|
|
|
return 0;
|
|
err_out:
|
|
return -ENODEV;
|
|
}
|
|
|
|
static void nicstar_remove_one(struct pci_dev *pcidev)
|
|
{
|
|
int i, j;
|
|
ns_dev *card = pci_get_drvdata(pcidev);
|
|
struct sk_buff *hb;
|
|
struct sk_buff *iovb;
|
|
struct sk_buff *lb;
|
|
struct sk_buff *sb;
|
|
|
|
i = card->index;
|
|
|
|
if (cards[i] == NULL)
|
|
return;
|
|
|
|
if (card->atmdev->phy && card->atmdev->phy->stop)
|
|
card->atmdev->phy->stop(card->atmdev);
|
|
|
|
/* Stop everything */
|
|
writel(0x00000000, card->membase + CFG);
|
|
|
|
/* De-register device */
|
|
atm_dev_deregister(card->atmdev);
|
|
|
|
/* Disable PCI device */
|
|
pci_disable_device(pcidev);
|
|
|
|
/* Free up resources */
|
|
j = 0;
|
|
PRINTK("nicstar%d: freeing %d huge buffers.\n", i, card->hbpool.count);
|
|
while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL) {
|
|
dev_kfree_skb_any(hb);
|
|
j++;
|
|
}
|
|
PRINTK("nicstar%d: %d huge buffers freed.\n", i, j);
|
|
j = 0;
|
|
PRINTK("nicstar%d: freeing %d iovec buffers.\n", i,
|
|
card->iovpool.count);
|
|
while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL) {
|
|
dev_kfree_skb_any(iovb);
|
|
j++;
|
|
}
|
|
PRINTK("nicstar%d: %d iovec buffers freed.\n", i, j);
|
|
while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL)
|
|
dev_kfree_skb_any(lb);
|
|
while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL)
|
|
dev_kfree_skb_any(sb);
|
|
free_scq(card, card->scq0, NULL);
|
|
for (j = 0; j < NS_FRSCD_NUM; j++) {
|
|
if (card->scd2vc[j] != NULL)
|
|
free_scq(card, card->scd2vc[j]->scq, card->scd2vc[j]->tx_vcc);
|
|
}
|
|
idr_destroy(&card->idr);
|
|
dma_free_coherent(&card->pcidev->dev, NS_RSQSIZE + NS_RSQ_ALIGNMENT,
|
|
card->rsq.org, card->rsq.dma);
|
|
dma_free_coherent(&card->pcidev->dev, NS_TSQSIZE + NS_TSQ_ALIGNMENT,
|
|
card->tsq.org, card->tsq.dma);
|
|
free_irq(card->pcidev->irq, card);
|
|
iounmap(card->membase);
|
|
kfree(card);
|
|
}
|
|
|
|
static struct pci_device_id nicstar_pci_tbl[] = {
|
|
{ PCI_VDEVICE(IDT, PCI_DEVICE_ID_IDT_IDT77201), 0 },
|
|
{0,} /* terminate list */
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, nicstar_pci_tbl);
|
|
|
|
static struct pci_driver nicstar_driver = {
|
|
.name = "nicstar",
|
|
.id_table = nicstar_pci_tbl,
|
|
.probe = nicstar_init_one,
|
|
.remove = nicstar_remove_one,
|
|
};
|
|
|
|
static int __init nicstar_init(void)
|
|
{
|
|
unsigned error = 0; /* Initialized to remove compile warning */
|
|
|
|
XPRINTK("nicstar: nicstar_init() called.\n");
|
|
|
|
error = pci_register_driver(&nicstar_driver);
|
|
|
|
TXPRINTK("nicstar: TX debug enabled.\n");
|
|
RXPRINTK("nicstar: RX debug enabled.\n");
|
|
PRINTK("nicstar: General debug enabled.\n");
|
|
#ifdef PHY_LOOPBACK
|
|
printk("nicstar: using PHY loopback.\n");
|
|
#endif /* PHY_LOOPBACK */
|
|
XPRINTK("nicstar: nicstar_init() returned.\n");
|
|
|
|
if (!error) {
|
|
init_timer(&ns_timer);
|
|
ns_timer.expires = jiffies + NS_POLL_PERIOD;
|
|
ns_timer.data = 0UL;
|
|
ns_timer.function = ns_poll;
|
|
add_timer(&ns_timer);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static void __exit nicstar_cleanup(void)
|
|
{
|
|
XPRINTK("nicstar: nicstar_cleanup() called.\n");
|
|
|
|
del_timer(&ns_timer);
|
|
|
|
pci_unregister_driver(&nicstar_driver);
|
|
|
|
XPRINTK("nicstar: nicstar_cleanup() returned.\n");
|
|
}
|
|
|
|
static u32 ns_read_sram(ns_dev * card, u32 sram_address)
|
|
{
|
|
unsigned long flags;
|
|
u32 data;
|
|
sram_address <<= 2;
|
|
sram_address &= 0x0007FFFC; /* address must be dword aligned */
|
|
sram_address |= 0x50000000; /* SRAM read command */
|
|
spin_lock_irqsave(&card->res_lock, flags);
|
|
while (CMD_BUSY(card)) ;
|
|
writel(sram_address, card->membase + CMD);
|
|
while (CMD_BUSY(card)) ;
|
|
data = readl(card->membase + DR0);
|
|
spin_unlock_irqrestore(&card->res_lock, flags);
|
|
return data;
|
|
}
|
|
|
|
static void ns_write_sram(ns_dev * card, u32 sram_address, u32 * value,
|
|
int count)
|
|
{
|
|
unsigned long flags;
|
|
int i, c;
|
|
count--; /* count range now is 0..3 instead of 1..4 */
|
|
c = count;
|
|
c <<= 2; /* to use increments of 4 */
|
|
spin_lock_irqsave(&card->res_lock, flags);
|
|
while (CMD_BUSY(card)) ;
|
|
for (i = 0; i <= c; i += 4)
|
|
writel(*(value++), card->membase + i);
|
|
/* Note: DR# registers are the first 4 dwords in nicstar's memspace,
|
|
so card->membase + DR0 == card->membase */
|
|
sram_address <<= 2;
|
|
sram_address &= 0x0007FFFC;
|
|
sram_address |= (0x40000000 | count);
|
|
writel(sram_address, card->membase + CMD);
|
|
spin_unlock_irqrestore(&card->res_lock, flags);
|
|
}
|
|
|
|
static int ns_init_card(int i, struct pci_dev *pcidev)
|
|
{
|
|
int j;
|
|
struct ns_dev *card = NULL;
|
|
unsigned char pci_latency;
|
|
unsigned error;
|
|
u32 data;
|
|
u32 u32d[4];
|
|
u32 ns_cfg_rctsize;
|
|
int bcount;
|
|
unsigned long membase;
|
|
|
|
error = 0;
|
|
|
|
if (pci_enable_device(pcidev)) {
|
|
printk("nicstar%d: can't enable PCI device\n", i);
|
|
error = 2;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
if (dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32)) != 0) {
|
|
printk(KERN_WARNING
|
|
"nicstar%d: No suitable DMA available.\n", i);
|
|
error = 2;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
|
|
card = kmalloc(sizeof(*card), GFP_KERNEL);
|
|
if (!card) {
|
|
printk
|
|
("nicstar%d: can't allocate memory for device structure.\n",
|
|
i);
|
|
error = 2;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
cards[i] = card;
|
|
spin_lock_init(&card->int_lock);
|
|
spin_lock_init(&card->res_lock);
|
|
|
|
pci_set_drvdata(pcidev, card);
|
|
|
|
card->index = i;
|
|
card->atmdev = NULL;
|
|
card->pcidev = pcidev;
|
|
membase = pci_resource_start(pcidev, 1);
|
|
card->membase = ioremap(membase, NS_IOREMAP_SIZE);
|
|
if (!card->membase) {
|
|
printk("nicstar%d: can't ioremap() membase.\n", i);
|
|
error = 3;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
PRINTK("nicstar%d: membase at 0x%p.\n", i, card->membase);
|
|
|
|
pci_set_master(pcidev);
|
|
|
|
if (pci_read_config_byte(pcidev, PCI_LATENCY_TIMER, &pci_latency) != 0) {
|
|
printk("nicstar%d: can't read PCI latency timer.\n", i);
|
|
error = 6;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
#ifdef NS_PCI_LATENCY
|
|
if (pci_latency < NS_PCI_LATENCY) {
|
|
PRINTK("nicstar%d: setting PCI latency timer to %d.\n", i,
|
|
NS_PCI_LATENCY);
|
|
for (j = 1; j < 4; j++) {
|
|
if (pci_write_config_byte
|
|
(pcidev, PCI_LATENCY_TIMER, NS_PCI_LATENCY) != 0)
|
|
break;
|
|
}
|
|
if (j == 4) {
|
|
printk
|
|
("nicstar%d: can't set PCI latency timer to %d.\n",
|
|
i, NS_PCI_LATENCY);
|
|
error = 7;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
}
|
|
#endif /* NS_PCI_LATENCY */
|
|
|
|
/* Clear timer overflow */
|
|
data = readl(card->membase + STAT);
|
|
if (data & NS_STAT_TMROF)
|
|
writel(NS_STAT_TMROF, card->membase + STAT);
|
|
|
|
/* Software reset */
|
|
writel(NS_CFG_SWRST, card->membase + CFG);
|
|
NS_DELAY;
|
|
writel(0x00000000, card->membase + CFG);
|
|
|
|
/* PHY reset */
|
|
writel(0x00000008, card->membase + GP);
|
|
NS_DELAY;
|
|
writel(0x00000001, card->membase + GP);
|
|
NS_DELAY;
|
|
while (CMD_BUSY(card)) ;
|
|
writel(NS_CMD_WRITE_UTILITY | 0x00000100, card->membase + CMD); /* Sync UTOPIA with SAR clock */
|
|
NS_DELAY;
|
|
|
|
/* Detect PHY type */
|
|
while (CMD_BUSY(card)) ;
|
|
writel(NS_CMD_READ_UTILITY | 0x00000200, card->membase + CMD);
|
|
while (CMD_BUSY(card)) ;
|
|
data = readl(card->membase + DR0);
|
|
switch (data) {
|
|
case 0x00000009:
|
|
printk("nicstar%d: PHY seems to be 25 Mbps.\n", i);
|
|
card->max_pcr = ATM_25_PCR;
|
|
while (CMD_BUSY(card)) ;
|
|
writel(0x00000008, card->membase + DR0);
|
|
writel(NS_CMD_WRITE_UTILITY | 0x00000200, card->membase + CMD);
|
|
/* Clear an eventual pending interrupt */
|
|
writel(NS_STAT_SFBQF, card->membase + STAT);
|
|
#ifdef PHY_LOOPBACK
|
|
while (CMD_BUSY(card)) ;
|
|
writel(0x00000022, card->membase + DR0);
|
|
writel(NS_CMD_WRITE_UTILITY | 0x00000202, card->membase + CMD);
|
|
#endif /* PHY_LOOPBACK */
|
|
break;
|
|
case 0x00000030:
|
|
case 0x00000031:
|
|
printk("nicstar%d: PHY seems to be 155 Mbps.\n", i);
|
|
card->max_pcr = ATM_OC3_PCR;
|
|
#ifdef PHY_LOOPBACK
|
|
while (CMD_BUSY(card)) ;
|
|
writel(0x00000002, card->membase + DR0);
|
|
writel(NS_CMD_WRITE_UTILITY | 0x00000205, card->membase + CMD);
|
|
#endif /* PHY_LOOPBACK */
|
|
break;
|
|
default:
|
|
printk("nicstar%d: unknown PHY type (0x%08X).\n", i, data);
|
|
error = 8;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
writel(0x00000000, card->membase + GP);
|
|
|
|
/* Determine SRAM size */
|
|
data = 0x76543210;
|
|
ns_write_sram(card, 0x1C003, &data, 1);
|
|
data = 0x89ABCDEF;
|
|
ns_write_sram(card, 0x14003, &data, 1);
|
|
if (ns_read_sram(card, 0x14003) == 0x89ABCDEF &&
|
|
ns_read_sram(card, 0x1C003) == 0x76543210)
|
|
card->sram_size = 128;
|
|
else
|
|
card->sram_size = 32;
|
|
PRINTK("nicstar%d: %dK x 32bit SRAM size.\n", i, card->sram_size);
|
|
|
|
card->rct_size = NS_MAX_RCTSIZE;
|
|
|
|
#if (NS_MAX_RCTSIZE == 4096)
|
|
if (card->sram_size == 128)
|
|
printk
|
|
("nicstar%d: limiting maximum VCI. See NS_MAX_RCTSIZE in nicstar.h\n",
|
|
i);
|
|
#elif (NS_MAX_RCTSIZE == 16384)
|
|
if (card->sram_size == 32) {
|
|
printk
|
|
("nicstar%d: wasting memory. See NS_MAX_RCTSIZE in nicstar.h\n",
|
|
i);
|
|
card->rct_size = 4096;
|
|
}
|
|
#else
|
|
#error NS_MAX_RCTSIZE must be either 4096 or 16384 in nicstar.c
|
|
#endif
|
|
|
|
card->vpibits = NS_VPIBITS;
|
|
if (card->rct_size == 4096)
|
|
card->vcibits = 12 - NS_VPIBITS;
|
|
else /* card->rct_size == 16384 */
|
|
card->vcibits = 14 - NS_VPIBITS;
|
|
|
|
/* Initialize the nicstar eeprom/eprom stuff, for the MAC addr */
|
|
if (mac[i] == NULL)
|
|
nicstar_init_eprom(card->membase);
|
|
|
|
/* Set the VPI/VCI MSb mask to zero so we can receive OAM cells */
|
|
writel(0x00000000, card->membase + VPM);
|
|
|
|
/* Initialize TSQ */
|
|
card->tsq.org = dma_alloc_coherent(&card->pcidev->dev,
|
|
NS_TSQSIZE + NS_TSQ_ALIGNMENT,
|
|
&card->tsq.dma, GFP_KERNEL);
|
|
if (card->tsq.org == NULL) {
|
|
printk("nicstar%d: can't allocate TSQ.\n", i);
|
|
error = 10;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
card->tsq.base = PTR_ALIGN(card->tsq.org, NS_TSQ_ALIGNMENT);
|
|
card->tsq.next = card->tsq.base;
|
|
card->tsq.last = card->tsq.base + (NS_TSQ_NUM_ENTRIES - 1);
|
|
for (j = 0; j < NS_TSQ_NUM_ENTRIES; j++)
|
|
ns_tsi_init(card->tsq.base + j);
|
|
writel(0x00000000, card->membase + TSQH);
|
|
writel(ALIGN(card->tsq.dma, NS_TSQ_ALIGNMENT), card->membase + TSQB);
|
|
PRINTK("nicstar%d: TSQ base at 0x%p.\n", i, card->tsq.base);
|
|
|
|
/* Initialize RSQ */
|
|
card->rsq.org = dma_alloc_coherent(&card->pcidev->dev,
|
|
NS_RSQSIZE + NS_RSQ_ALIGNMENT,
|
|
&card->rsq.dma, GFP_KERNEL);
|
|
if (card->rsq.org == NULL) {
|
|
printk("nicstar%d: can't allocate RSQ.\n", i);
|
|
error = 11;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
card->rsq.base = PTR_ALIGN(card->rsq.org, NS_RSQ_ALIGNMENT);
|
|
card->rsq.next = card->rsq.base;
|
|
card->rsq.last = card->rsq.base + (NS_RSQ_NUM_ENTRIES - 1);
|
|
for (j = 0; j < NS_RSQ_NUM_ENTRIES; j++)
|
|
ns_rsqe_init(card->rsq.base + j);
|
|
writel(0x00000000, card->membase + RSQH);
|
|
writel(ALIGN(card->rsq.dma, NS_RSQ_ALIGNMENT), card->membase + RSQB);
|
|
PRINTK("nicstar%d: RSQ base at 0x%p.\n", i, card->rsq.base);
|
|
|
|
/* Initialize SCQ0, the only VBR SCQ used */
|
|
card->scq1 = NULL;
|
|
card->scq2 = NULL;
|
|
card->scq0 = get_scq(card, VBR_SCQSIZE, NS_VRSCD0);
|
|
if (card->scq0 == NULL) {
|
|
printk("nicstar%d: can't get SCQ0.\n", i);
|
|
error = 12;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
u32d[0] = scq_virt_to_bus(card->scq0, card->scq0->base);
|
|
u32d[1] = (u32) 0x00000000;
|
|
u32d[2] = (u32) 0xffffffff;
|
|
u32d[3] = (u32) 0x00000000;
|
|
ns_write_sram(card, NS_VRSCD0, u32d, 4);
|
|
ns_write_sram(card, NS_VRSCD1, u32d, 4); /* These last two won't be used */
|
|
ns_write_sram(card, NS_VRSCD2, u32d, 4); /* but are initialized, just in case... */
|
|
card->scq0->scd = NS_VRSCD0;
|
|
PRINTK("nicstar%d: VBR-SCQ0 base at 0x%p.\n", i, card->scq0->base);
|
|
|
|
/* Initialize TSTs */
|
|
card->tst_addr = NS_TST0;
|
|
card->tst_free_entries = NS_TST_NUM_ENTRIES;
|
|
data = NS_TST_OPCODE_VARIABLE;
|
|
for (j = 0; j < NS_TST_NUM_ENTRIES; j++)
|
|
ns_write_sram(card, NS_TST0 + j, &data, 1);
|
|
data = ns_tste_make(NS_TST_OPCODE_END, NS_TST0);
|
|
ns_write_sram(card, NS_TST0 + NS_TST_NUM_ENTRIES, &data, 1);
|
|
for (j = 0; j < NS_TST_NUM_ENTRIES; j++)
|
|
ns_write_sram(card, NS_TST1 + j, &data, 1);
|
|
data = ns_tste_make(NS_TST_OPCODE_END, NS_TST1);
|
|
ns_write_sram(card, NS_TST1 + NS_TST_NUM_ENTRIES, &data, 1);
|
|
for (j = 0; j < NS_TST_NUM_ENTRIES; j++)
|
|
card->tste2vc[j] = NULL;
|
|
writel(NS_TST0 << 2, card->membase + TSTB);
|
|
|
|
/* Initialize RCT. AAL type is set on opening the VC. */
|
|
#ifdef RCQ_SUPPORT
|
|
u32d[0] = NS_RCTE_RAWCELLINTEN;
|
|
#else
|
|
u32d[0] = 0x00000000;
|
|
#endif /* RCQ_SUPPORT */
|
|
u32d[1] = 0x00000000;
|
|
u32d[2] = 0x00000000;
|
|
u32d[3] = 0xFFFFFFFF;
|
|
for (j = 0; j < card->rct_size; j++)
|
|
ns_write_sram(card, j * 4, u32d, 4);
|
|
|
|
memset(card->vcmap, 0, sizeof(card->vcmap));
|
|
|
|
for (j = 0; j < NS_FRSCD_NUM; j++)
|
|
card->scd2vc[j] = NULL;
|
|
|
|
/* Initialize buffer levels */
|
|
card->sbnr.min = MIN_SB;
|
|
card->sbnr.init = NUM_SB;
|
|
card->sbnr.max = MAX_SB;
|
|
card->lbnr.min = MIN_LB;
|
|
card->lbnr.init = NUM_LB;
|
|
card->lbnr.max = MAX_LB;
|
|
card->iovnr.min = MIN_IOVB;
|
|
card->iovnr.init = NUM_IOVB;
|
|
card->iovnr.max = MAX_IOVB;
|
|
card->hbnr.min = MIN_HB;
|
|
card->hbnr.init = NUM_HB;
|
|
card->hbnr.max = MAX_HB;
|
|
|
|
card->sm_handle = NULL;
|
|
card->sm_addr = 0x00000000;
|
|
card->lg_handle = NULL;
|
|
card->lg_addr = 0x00000000;
|
|
|
|
card->efbie = 1; /* To prevent push_rxbufs from enabling the interrupt */
|
|
|
|
idr_init(&card->idr);
|
|
|
|
/* Pre-allocate some huge buffers */
|
|
skb_queue_head_init(&card->hbpool.queue);
|
|
card->hbpool.count = 0;
|
|
for (j = 0; j < NUM_HB; j++) {
|
|
struct sk_buff *hb;
|
|
hb = __dev_alloc_skb(NS_HBUFSIZE, GFP_KERNEL);
|
|
if (hb == NULL) {
|
|
printk
|
|
("nicstar%d: can't allocate %dth of %d huge buffers.\n",
|
|
i, j, NUM_HB);
|
|
error = 13;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
NS_PRV_BUFTYPE(hb) = BUF_NONE;
|
|
skb_queue_tail(&card->hbpool.queue, hb);
|
|
card->hbpool.count++;
|
|
}
|
|
|
|
/* Allocate large buffers */
|
|
skb_queue_head_init(&card->lbpool.queue);
|
|
card->lbpool.count = 0; /* Not used */
|
|
for (j = 0; j < NUM_LB; j++) {
|
|
struct sk_buff *lb;
|
|
lb = __dev_alloc_skb(NS_LGSKBSIZE, GFP_KERNEL);
|
|
if (lb == NULL) {
|
|
printk
|
|
("nicstar%d: can't allocate %dth of %d large buffers.\n",
|
|
i, j, NUM_LB);
|
|
error = 14;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
NS_PRV_BUFTYPE(lb) = BUF_LG;
|
|
skb_queue_tail(&card->lbpool.queue, lb);
|
|
skb_reserve(lb, NS_SMBUFSIZE);
|
|
push_rxbufs(card, lb);
|
|
/* Due to the implementation of push_rxbufs() this is 1, not 0 */
|
|
if (j == 1) {
|
|
card->rcbuf = lb;
|
|
card->rawcell = (struct ns_rcqe *) lb->data;
|
|
card->rawch = NS_PRV_DMA(lb);
|
|
}
|
|
}
|
|
/* Test for strange behaviour which leads to crashes */
|
|
if ((bcount =
|
|
ns_stat_lfbqc_get(readl(card->membase + STAT))) < card->lbnr.min) {
|
|
printk
|
|
("nicstar%d: Strange... Just allocated %d large buffers and lfbqc = %d.\n",
|
|
i, j, bcount);
|
|
error = 14;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
|
|
/* Allocate small buffers */
|
|
skb_queue_head_init(&card->sbpool.queue);
|
|
card->sbpool.count = 0; /* Not used */
|
|
for (j = 0; j < NUM_SB; j++) {
|
|
struct sk_buff *sb;
|
|
sb = __dev_alloc_skb(NS_SMSKBSIZE, GFP_KERNEL);
|
|
if (sb == NULL) {
|
|
printk
|
|
("nicstar%d: can't allocate %dth of %d small buffers.\n",
|
|
i, j, NUM_SB);
|
|
error = 15;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
NS_PRV_BUFTYPE(sb) = BUF_SM;
|
|
skb_queue_tail(&card->sbpool.queue, sb);
|
|
skb_reserve(sb, NS_AAL0_HEADER);
|
|
push_rxbufs(card, sb);
|
|
}
|
|
/* Test for strange behaviour which leads to crashes */
|
|
if ((bcount =
|
|
ns_stat_sfbqc_get(readl(card->membase + STAT))) < card->sbnr.min) {
|
|
printk
|
|
("nicstar%d: Strange... Just allocated %d small buffers and sfbqc = %d.\n",
|
|
i, j, bcount);
|
|
error = 15;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
|
|
/* Allocate iovec buffers */
|
|
skb_queue_head_init(&card->iovpool.queue);
|
|
card->iovpool.count = 0;
|
|
for (j = 0; j < NUM_IOVB; j++) {
|
|
struct sk_buff *iovb;
|
|
iovb = alloc_skb(NS_IOVBUFSIZE, GFP_KERNEL);
|
|
if (iovb == NULL) {
|
|
printk
|
|
("nicstar%d: can't allocate %dth of %d iovec buffers.\n",
|
|
i, j, NUM_IOVB);
|
|
error = 16;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
NS_PRV_BUFTYPE(iovb) = BUF_NONE;
|
|
skb_queue_tail(&card->iovpool.queue, iovb);
|
|
card->iovpool.count++;
|
|
}
|
|
|
|
/* Configure NICStAR */
|
|
if (card->rct_size == 4096)
|
|
ns_cfg_rctsize = NS_CFG_RCTSIZE_4096_ENTRIES;
|
|
else /* (card->rct_size == 16384) */
|
|
ns_cfg_rctsize = NS_CFG_RCTSIZE_16384_ENTRIES;
|
|
|
|
card->efbie = 1;
|
|
|
|
card->intcnt = 0;
|
|
if (request_irq
|
|
(pcidev->irq, &ns_irq_handler, IRQF_SHARED, "nicstar", card) != 0) {
|
|
printk("nicstar%d: can't allocate IRQ %d.\n", i, pcidev->irq);
|
|
error = 9;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
|
|
/* Register device */
|
|
card->atmdev = atm_dev_register("nicstar", &card->pcidev->dev, &atm_ops,
|
|
-1, NULL);
|
|
if (card->atmdev == NULL) {
|
|
printk("nicstar%d: can't register device.\n", i);
|
|
error = 17;
|
|
ns_init_card_error(card, error);
|
|
return error;
|
|
}
|
|
|
|
if (mac[i] == NULL || !mac_pton(mac[i], card->atmdev->esi)) {
|
|
nicstar_read_eprom(card->membase, NICSTAR_EPROM_MAC_ADDR_OFFSET,
|
|
card->atmdev->esi, 6);
|
|
if (ether_addr_equal(card->atmdev->esi, "\x00\x00\x00\x00\x00\x00")) {
|
|
nicstar_read_eprom(card->membase,
|
|
NICSTAR_EPROM_MAC_ADDR_OFFSET_ALT,
|
|
card->atmdev->esi, 6);
|
|
}
|
|
}
|
|
|
|
printk("nicstar%d: MAC address %pM\n", i, card->atmdev->esi);
|
|
|
|
card->atmdev->dev_data = card;
|
|
card->atmdev->ci_range.vpi_bits = card->vpibits;
|
|
card->atmdev->ci_range.vci_bits = card->vcibits;
|
|
card->atmdev->link_rate = card->max_pcr;
|
|
card->atmdev->phy = NULL;
|
|
|
|
#ifdef CONFIG_ATM_NICSTAR_USE_SUNI
|
|
if (card->max_pcr == ATM_OC3_PCR)
|
|
suni_init(card->atmdev);
|
|
#endif /* CONFIG_ATM_NICSTAR_USE_SUNI */
|
|
|
|
#ifdef CONFIG_ATM_NICSTAR_USE_IDT77105
|
|
if (card->max_pcr == ATM_25_PCR)
|
|
idt77105_init(card->atmdev);
|
|
#endif /* CONFIG_ATM_NICSTAR_USE_IDT77105 */
|
|
|
|
if (card->atmdev->phy && card->atmdev->phy->start)
|
|
card->atmdev->phy->start(card->atmdev);
|
|
|
|
writel(NS_CFG_RXPATH | NS_CFG_SMBUFSIZE | NS_CFG_LGBUFSIZE | NS_CFG_EFBIE | NS_CFG_RSQSIZE | NS_CFG_VPIBITS | ns_cfg_rctsize | NS_CFG_RXINT_NODELAY | NS_CFG_RAWIE | /* Only enabled if RCQ_SUPPORT */
|
|
NS_CFG_RSQAFIE | NS_CFG_TXEN | NS_CFG_TXIE | NS_CFG_TSQFIE_OPT | /* Only enabled if ENABLE_TSQFIE */
|
|
NS_CFG_PHYIE, card->membase + CFG);
|
|
|
|
num_cards++;
|
|
|
|
return error;
|
|
}
|
|
|
|
static void ns_init_card_error(ns_dev *card, int error)
|
|
{
|
|
if (error >= 17) {
|
|
writel(0x00000000, card->membase + CFG);
|
|
}
|
|
if (error >= 16) {
|
|
struct sk_buff *iovb;
|
|
while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL)
|
|
dev_kfree_skb_any(iovb);
|
|
}
|
|
if (error >= 15) {
|
|
struct sk_buff *sb;
|
|
while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL)
|
|
dev_kfree_skb_any(sb);
|
|
free_scq(card, card->scq0, NULL);
|
|
}
|
|
if (error >= 14) {
|
|
struct sk_buff *lb;
|
|
while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL)
|
|
dev_kfree_skb_any(lb);
|
|
}
|
|
if (error >= 13) {
|
|
struct sk_buff *hb;
|
|
while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL)
|
|
dev_kfree_skb_any(hb);
|
|
}
|
|
if (error >= 12) {
|
|
kfree(card->rsq.org);
|
|
}
|
|
if (error >= 11) {
|
|
kfree(card->tsq.org);
|
|
}
|
|
if (error >= 10) {
|
|
free_irq(card->pcidev->irq, card);
|
|
}
|
|
if (error >= 4) {
|
|
iounmap(card->membase);
|
|
}
|
|
if (error >= 3) {
|
|
pci_disable_device(card->pcidev);
|
|
kfree(card);
|
|
}
|
|
}
|
|
|
|
static scq_info *get_scq(ns_dev *card, int size, u32 scd)
|
|
{
|
|
scq_info *scq;
|
|
int i;
|
|
|
|
if (size != VBR_SCQSIZE && size != CBR_SCQSIZE)
|
|
return NULL;
|
|
|
|
scq = kmalloc(sizeof(*scq), GFP_KERNEL);
|
|
if (!scq)
|
|
return NULL;
|
|
scq->org = dma_alloc_coherent(&card->pcidev->dev,
|
|
2 * size, &scq->dma, GFP_KERNEL);
|
|
if (!scq->org) {
|
|
kfree(scq);
|
|
return NULL;
|
|
}
|
|
scq->skb = kmalloc_array(size / NS_SCQE_SIZE,
|
|
sizeof(*scq->skb),
|
|
GFP_KERNEL);
|
|
if (!scq->skb) {
|
|
dma_free_coherent(&card->pcidev->dev,
|
|
2 * size, scq->org, scq->dma);
|
|
kfree(scq);
|
|
return NULL;
|
|
}
|
|
scq->num_entries = size / NS_SCQE_SIZE;
|
|
scq->base = PTR_ALIGN(scq->org, size);
|
|
scq->next = scq->base;
|
|
scq->last = scq->base + (scq->num_entries - 1);
|
|
scq->tail = scq->last;
|
|
scq->scd = scd;
|
|
scq->num_entries = size / NS_SCQE_SIZE;
|
|
scq->tbd_count = 0;
|
|
init_waitqueue_head(&scq->scqfull_waitq);
|
|
scq->full = 0;
|
|
spin_lock_init(&scq->lock);
|
|
|
|
for (i = 0; i < scq->num_entries; i++)
|
|
scq->skb[i] = NULL;
|
|
|
|
return scq;
|
|
}
|
|
|
|
/* For variable rate SCQ vcc must be NULL */
|
|
static void free_scq(ns_dev *card, scq_info *scq, struct atm_vcc *vcc)
|
|
{
|
|
int i;
|
|
|
|
if (scq->num_entries == VBR_SCQ_NUM_ENTRIES)
|
|
for (i = 0; i < scq->num_entries; i++) {
|
|
if (scq->skb[i] != NULL) {
|
|
vcc = ATM_SKB(scq->skb[i])->vcc;
|
|
if (vcc->pop != NULL)
|
|
vcc->pop(vcc, scq->skb[i]);
|
|
else
|
|
dev_kfree_skb_any(scq->skb[i]);
|
|
}
|
|
} else { /* vcc must be != NULL */
|
|
|
|
if (vcc == NULL) {
|
|
printk
|
|
("nicstar: free_scq() called with vcc == NULL for fixed rate scq.");
|
|
for (i = 0; i < scq->num_entries; i++)
|
|
dev_kfree_skb_any(scq->skb[i]);
|
|
} else
|
|
for (i = 0; i < scq->num_entries; i++) {
|
|
if (scq->skb[i] != NULL) {
|
|
if (vcc->pop != NULL)
|
|
vcc->pop(vcc, scq->skb[i]);
|
|
else
|
|
dev_kfree_skb_any(scq->skb[i]);
|
|
}
|
|
}
|
|
}
|
|
kfree(scq->skb);
|
|
dma_free_coherent(&card->pcidev->dev,
|
|
2 * (scq->num_entries == VBR_SCQ_NUM_ENTRIES ?
|
|
VBR_SCQSIZE : CBR_SCQSIZE),
|
|
scq->org, scq->dma);
|
|
kfree(scq);
|
|
}
|
|
|
|
/* The handles passed must be pointers to the sk_buff containing the small
|
|
or large buffer(s) cast to u32. */
|
|
static void push_rxbufs(ns_dev * card, struct sk_buff *skb)
|
|
{
|
|
struct sk_buff *handle1, *handle2;
|
|
int id1, id2;
|
|
u32 addr1, addr2;
|
|
u32 stat;
|
|
unsigned long flags;
|
|
|
|
/* *BARF* */
|
|
handle2 = NULL;
|
|
addr2 = 0;
|
|
handle1 = skb;
|
|
addr1 = dma_map_single(&card->pcidev->dev,
|
|
skb->data,
|
|
(NS_PRV_BUFTYPE(skb) == BUF_SM
|
|
? NS_SMSKBSIZE : NS_LGSKBSIZE),
|
|
DMA_TO_DEVICE);
|
|
NS_PRV_DMA(skb) = addr1; /* save so we can unmap later */
|
|
|
|
#ifdef GENERAL_DEBUG
|
|
if (!addr1)
|
|
printk("nicstar%d: push_rxbufs called with addr1 = 0.\n",
|
|
card->index);
|
|
#endif /* GENERAL_DEBUG */
|
|
|
|
stat = readl(card->membase + STAT);
|
|
card->sbfqc = ns_stat_sfbqc_get(stat);
|
|
card->lbfqc = ns_stat_lfbqc_get(stat);
|
|
if (NS_PRV_BUFTYPE(skb) == BUF_SM) {
|
|
if (!addr2) {
|
|
if (card->sm_addr) {
|
|
addr2 = card->sm_addr;
|
|
handle2 = card->sm_handle;
|
|
card->sm_addr = 0x00000000;
|
|
card->sm_handle = NULL;
|
|
} else { /* (!sm_addr) */
|
|
|
|
card->sm_addr = addr1;
|
|
card->sm_handle = handle1;
|
|
}
|
|
}
|
|
} else { /* buf_type == BUF_LG */
|
|
|
|
if (!addr2) {
|
|
if (card->lg_addr) {
|
|
addr2 = card->lg_addr;
|
|
handle2 = card->lg_handle;
|
|
card->lg_addr = 0x00000000;
|
|
card->lg_handle = NULL;
|
|
} else { /* (!lg_addr) */
|
|
|
|
card->lg_addr = addr1;
|
|
card->lg_handle = handle1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (addr2) {
|
|
if (NS_PRV_BUFTYPE(skb) == BUF_SM) {
|
|
if (card->sbfqc >= card->sbnr.max) {
|
|
skb_unlink(handle1, &card->sbpool.queue);
|
|
dev_kfree_skb_any(handle1);
|
|
skb_unlink(handle2, &card->sbpool.queue);
|
|
dev_kfree_skb_any(handle2);
|
|
return;
|
|
} else
|
|
card->sbfqc += 2;
|
|
} else { /* (buf_type == BUF_LG) */
|
|
|
|
if (card->lbfqc >= card->lbnr.max) {
|
|
skb_unlink(handle1, &card->lbpool.queue);
|
|
dev_kfree_skb_any(handle1);
|
|
skb_unlink(handle2, &card->lbpool.queue);
|
|
dev_kfree_skb_any(handle2);
|
|
return;
|
|
} else
|
|
card->lbfqc += 2;
|
|
}
|
|
|
|
id1 = idr_alloc(&card->idr, handle1, 0, 0, GFP_ATOMIC);
|
|
if (id1 < 0)
|
|
goto out;
|
|
|
|
id2 = idr_alloc(&card->idr, handle2, 0, 0, GFP_ATOMIC);
|
|
if (id2 < 0)
|
|
goto out;
|
|
|
|
spin_lock_irqsave(&card->res_lock, flags);
|
|
while (CMD_BUSY(card)) ;
|
|
writel(addr2, card->membase + DR3);
|
|
writel(id2, card->membase + DR2);
|
|
writel(addr1, card->membase + DR1);
|
|
writel(id1, card->membase + DR0);
|
|
writel(NS_CMD_WRITE_FREEBUFQ | NS_PRV_BUFTYPE(skb),
|
|
card->membase + CMD);
|
|
spin_unlock_irqrestore(&card->res_lock, flags);
|
|
|
|
XPRINTK("nicstar%d: Pushing %s buffers at 0x%x and 0x%x.\n",
|
|
card->index,
|
|
(NS_PRV_BUFTYPE(skb) == BUF_SM ? "small" : "large"),
|
|
addr1, addr2);
|
|
}
|
|
|
|
if (!card->efbie && card->sbfqc >= card->sbnr.min &&
|
|
card->lbfqc >= card->lbnr.min) {
|
|
card->efbie = 1;
|
|
writel((readl(card->membase + CFG) | NS_CFG_EFBIE),
|
|
card->membase + CFG);
|
|
}
|
|
|
|
out:
|
|
return;
|
|
}
|
|
|
|
static irqreturn_t ns_irq_handler(int irq, void *dev_id)
|
|
{
|
|
u32 stat_r;
|
|
ns_dev *card;
|
|
struct atm_dev *dev;
|
|
unsigned long flags;
|
|
|
|
card = (ns_dev *) dev_id;
|
|
dev = card->atmdev;
|
|
card->intcnt++;
|
|
|
|
PRINTK("nicstar%d: NICStAR generated an interrupt\n", card->index);
|
|
|
|
spin_lock_irqsave(&card->int_lock, flags);
|
|
|
|
stat_r = readl(card->membase + STAT);
|
|
|
|
/* Transmit Status Indicator has been written to T. S. Queue */
|
|
if (stat_r & NS_STAT_TSIF) {
|
|
TXPRINTK("nicstar%d: TSI interrupt\n", card->index);
|
|
process_tsq(card);
|
|
writel(NS_STAT_TSIF, card->membase + STAT);
|
|
}
|
|
|
|
/* Incomplete CS-PDU has been transmitted */
|
|
if (stat_r & NS_STAT_TXICP) {
|
|
writel(NS_STAT_TXICP, card->membase + STAT);
|
|
TXPRINTK("nicstar%d: Incomplete CS-PDU transmitted.\n",
|
|
card->index);
|
|
}
|
|
|
|
/* Transmit Status Queue 7/8 full */
|
|
if (stat_r & NS_STAT_TSQF) {
|
|
writel(NS_STAT_TSQF, card->membase + STAT);
|
|
PRINTK("nicstar%d: TSQ full.\n", card->index);
|
|
process_tsq(card);
|
|
}
|
|
|
|
/* Timer overflow */
|
|
if (stat_r & NS_STAT_TMROF) {
|
|
writel(NS_STAT_TMROF, card->membase + STAT);
|
|
PRINTK("nicstar%d: Timer overflow.\n", card->index);
|
|
}
|
|
|
|
/* PHY device interrupt signal active */
|
|
if (stat_r & NS_STAT_PHYI) {
|
|
writel(NS_STAT_PHYI, card->membase + STAT);
|
|
PRINTK("nicstar%d: PHY interrupt.\n", card->index);
|
|
if (dev->phy && dev->phy->interrupt) {
|
|
dev->phy->interrupt(dev);
|
|
}
|
|
}
|
|
|
|
/* Small Buffer Queue is full */
|
|
if (stat_r & NS_STAT_SFBQF) {
|
|
writel(NS_STAT_SFBQF, card->membase + STAT);
|
|
printk("nicstar%d: Small free buffer queue is full.\n",
|
|
card->index);
|
|
}
|
|
|
|
/* Large Buffer Queue is full */
|
|
if (stat_r & NS_STAT_LFBQF) {
|
|
writel(NS_STAT_LFBQF, card->membase + STAT);
|
|
printk("nicstar%d: Large free buffer queue is full.\n",
|
|
card->index);
|
|
}
|
|
|
|
/* Receive Status Queue is full */
|
|
if (stat_r & NS_STAT_RSQF) {
|
|
writel(NS_STAT_RSQF, card->membase + STAT);
|
|
printk("nicstar%d: RSQ full.\n", card->index);
|
|
process_rsq(card);
|
|
}
|
|
|
|
/* Complete CS-PDU received */
|
|
if (stat_r & NS_STAT_EOPDU) {
|
|
RXPRINTK("nicstar%d: End of CS-PDU received.\n", card->index);
|
|
process_rsq(card);
|
|
writel(NS_STAT_EOPDU, card->membase + STAT);
|
|
}
|
|
|
|
/* Raw cell received */
|
|
if (stat_r & NS_STAT_RAWCF) {
|
|
writel(NS_STAT_RAWCF, card->membase + STAT);
|
|
#ifndef RCQ_SUPPORT
|
|
printk("nicstar%d: Raw cell received and no support yet...\n",
|
|
card->index);
|
|
#endif /* RCQ_SUPPORT */
|
|
/* NOTE: the following procedure may keep a raw cell pending until the
|
|
next interrupt. As this preliminary support is only meant to
|
|
avoid buffer leakage, this is not an issue. */
|
|
while (readl(card->membase + RAWCT) != card->rawch) {
|
|
|
|
if (ns_rcqe_islast(card->rawcell)) {
|
|
struct sk_buff *oldbuf;
|
|
|
|
oldbuf = card->rcbuf;
|
|
card->rcbuf = idr_find(&card->idr,
|
|
ns_rcqe_nextbufhandle(card->rawcell));
|
|
card->rawch = NS_PRV_DMA(card->rcbuf);
|
|
card->rawcell = (struct ns_rcqe *)
|
|
card->rcbuf->data;
|
|
recycle_rx_buf(card, oldbuf);
|
|
} else {
|
|
card->rawch += NS_RCQE_SIZE;
|
|
card->rawcell++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Small buffer queue is empty */
|
|
if (stat_r & NS_STAT_SFBQE) {
|
|
int i;
|
|
struct sk_buff *sb;
|
|
|
|
writel(NS_STAT_SFBQE, card->membase + STAT);
|
|
printk("nicstar%d: Small free buffer queue empty.\n",
|
|
card->index);
|
|
for (i = 0; i < card->sbnr.min; i++) {
|
|
sb = dev_alloc_skb(NS_SMSKBSIZE);
|
|
if (sb == NULL) {
|
|
writel(readl(card->membase + CFG) &
|
|
~NS_CFG_EFBIE, card->membase + CFG);
|
|
card->efbie = 0;
|
|
break;
|
|
}
|
|
NS_PRV_BUFTYPE(sb) = BUF_SM;
|
|
skb_queue_tail(&card->sbpool.queue, sb);
|
|
skb_reserve(sb, NS_AAL0_HEADER);
|
|
push_rxbufs(card, sb);
|
|
}
|
|
card->sbfqc = i;
|
|
process_rsq(card);
|
|
}
|
|
|
|
/* Large buffer queue empty */
|
|
if (stat_r & NS_STAT_LFBQE) {
|
|
int i;
|
|
struct sk_buff *lb;
|
|
|
|
writel(NS_STAT_LFBQE, card->membase + STAT);
|
|
printk("nicstar%d: Large free buffer queue empty.\n",
|
|
card->index);
|
|
for (i = 0; i < card->lbnr.min; i++) {
|
|
lb = dev_alloc_skb(NS_LGSKBSIZE);
|
|
if (lb == NULL) {
|
|
writel(readl(card->membase + CFG) &
|
|
~NS_CFG_EFBIE, card->membase + CFG);
|
|
card->efbie = 0;
|
|
break;
|
|
}
|
|
NS_PRV_BUFTYPE(lb) = BUF_LG;
|
|
skb_queue_tail(&card->lbpool.queue, lb);
|
|
skb_reserve(lb, NS_SMBUFSIZE);
|
|
push_rxbufs(card, lb);
|
|
}
|
|
card->lbfqc = i;
|
|
process_rsq(card);
|
|
}
|
|
|
|
/* Receive Status Queue is 7/8 full */
|
|
if (stat_r & NS_STAT_RSQAF) {
|
|
writel(NS_STAT_RSQAF, card->membase + STAT);
|
|
RXPRINTK("nicstar%d: RSQ almost full.\n", card->index);
|
|
process_rsq(card);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&card->int_lock, flags);
|
|
PRINTK("nicstar%d: end of interrupt service\n", card->index);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int ns_open(struct atm_vcc *vcc)
|
|
{
|
|
ns_dev *card;
|
|
vc_map *vc;
|
|
unsigned long tmpl, modl;
|
|
int tcr, tcra; /* target cell rate, and absolute value */
|
|
int n = 0; /* Number of entries in the TST. Initialized to remove
|
|
the compiler warning. */
|
|
u32 u32d[4];
|
|
int frscdi = 0; /* Index of the SCD. Initialized to remove the compiler
|
|
warning. How I wish compilers were clever enough to
|
|
tell which variables can truly be used
|
|
uninitialized... */
|
|
int inuse; /* tx or rx vc already in use by another vcc */
|
|
short vpi = vcc->vpi;
|
|
int vci = vcc->vci;
|
|
|
|
card = (ns_dev *) vcc->dev->dev_data;
|
|
PRINTK("nicstar%d: opening vpi.vci %d.%d \n", card->index, (int)vpi,
|
|
vci);
|
|
if (vcc->qos.aal != ATM_AAL5 && vcc->qos.aal != ATM_AAL0) {
|
|
PRINTK("nicstar%d: unsupported AAL.\n", card->index);
|
|
return -EINVAL;
|
|
}
|
|
|
|
vc = &(card->vcmap[vpi << card->vcibits | vci]);
|
|
vcc->dev_data = vc;
|
|
|
|
inuse = 0;
|
|
if (vcc->qos.txtp.traffic_class != ATM_NONE && vc->tx)
|
|
inuse = 1;
|
|
if (vcc->qos.rxtp.traffic_class != ATM_NONE && vc->rx)
|
|
inuse += 2;
|
|
if (inuse) {
|
|
printk("nicstar%d: %s vci already in use.\n", card->index,
|
|
inuse == 1 ? "tx" : inuse == 2 ? "rx" : "tx and rx");
|
|
return -EINVAL;
|
|
}
|
|
|
|
set_bit(ATM_VF_ADDR, &vcc->flags);
|
|
|
|
/* NOTE: You are not allowed to modify an open connection's QOS. To change
|
|
that, remove the ATM_VF_PARTIAL flag checking. There may be other changes
|
|
needed to do that. */
|
|
if (!test_bit(ATM_VF_PARTIAL, &vcc->flags)) {
|
|
scq_info *scq;
|
|
|
|
set_bit(ATM_VF_PARTIAL, &vcc->flags);
|
|
if (vcc->qos.txtp.traffic_class == ATM_CBR) {
|
|
/* Check requested cell rate and availability of SCD */
|
|
if (vcc->qos.txtp.max_pcr == 0 && vcc->qos.txtp.pcr == 0
|
|
&& vcc->qos.txtp.min_pcr == 0) {
|
|
PRINTK
|
|
("nicstar%d: trying to open a CBR vc with cell rate = 0 \n",
|
|
card->index);
|
|
clear_bit(ATM_VF_PARTIAL, &vcc->flags);
|
|
clear_bit(ATM_VF_ADDR, &vcc->flags);
|
|
return -EINVAL;
|
|
}
|
|
|
|
tcr = atm_pcr_goal(&(vcc->qos.txtp));
|
|
tcra = tcr >= 0 ? tcr : -tcr;
|
|
|
|
PRINTK("nicstar%d: target cell rate = %d.\n",
|
|
card->index, vcc->qos.txtp.max_pcr);
|
|
|
|
tmpl =
|
|
(unsigned long)tcra *(unsigned long)
|
|
NS_TST_NUM_ENTRIES;
|
|
modl = tmpl % card->max_pcr;
|
|
|
|
n = (int)(tmpl / card->max_pcr);
|
|
if (tcr > 0) {
|
|
if (modl > 0)
|
|
n++;
|
|
} else if (tcr == 0) {
|
|
if ((n =
|
|
(card->tst_free_entries -
|
|
NS_TST_RESERVED)) <= 0) {
|
|
PRINTK
|
|
("nicstar%d: no CBR bandwidth free.\n",
|
|
card->index);
|
|
clear_bit(ATM_VF_PARTIAL, &vcc->flags);
|
|
clear_bit(ATM_VF_ADDR, &vcc->flags);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (n == 0) {
|
|
printk
|
|
("nicstar%d: selected bandwidth < granularity.\n",
|
|
card->index);
|
|
clear_bit(ATM_VF_PARTIAL, &vcc->flags);
|
|
clear_bit(ATM_VF_ADDR, &vcc->flags);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (n > (card->tst_free_entries - NS_TST_RESERVED)) {
|
|
PRINTK
|
|
("nicstar%d: not enough free CBR bandwidth.\n",
|
|
card->index);
|
|
clear_bit(ATM_VF_PARTIAL, &vcc->flags);
|
|
clear_bit(ATM_VF_ADDR, &vcc->flags);
|
|
return -EINVAL;
|
|
} else
|
|
card->tst_free_entries -= n;
|
|
|
|
XPRINTK("nicstar%d: writing %d tst entries.\n",
|
|
card->index, n);
|
|
for (frscdi = 0; frscdi < NS_FRSCD_NUM; frscdi++) {
|
|
if (card->scd2vc[frscdi] == NULL) {
|
|
card->scd2vc[frscdi] = vc;
|
|
break;
|
|
}
|
|
}
|
|
if (frscdi == NS_FRSCD_NUM) {
|
|
PRINTK
|
|
("nicstar%d: no SCD available for CBR channel.\n",
|
|
card->index);
|
|
card->tst_free_entries += n;
|
|
clear_bit(ATM_VF_PARTIAL, &vcc->flags);
|
|
clear_bit(ATM_VF_ADDR, &vcc->flags);
|
|
return -EBUSY;
|
|
}
|
|
|
|
vc->cbr_scd = NS_FRSCD + frscdi * NS_FRSCD_SIZE;
|
|
|
|
scq = get_scq(card, CBR_SCQSIZE, vc->cbr_scd);
|
|
if (scq == NULL) {
|
|
PRINTK("nicstar%d: can't get fixed rate SCQ.\n",
|
|
card->index);
|
|
card->scd2vc[frscdi] = NULL;
|
|
card->tst_free_entries += n;
|
|
clear_bit(ATM_VF_PARTIAL, &vcc->flags);
|
|
clear_bit(ATM_VF_ADDR, &vcc->flags);
|
|
return -ENOMEM;
|
|
}
|
|
vc->scq = scq;
|
|
u32d[0] = scq_virt_to_bus(scq, scq->base);
|
|
u32d[1] = (u32) 0x00000000;
|
|
u32d[2] = (u32) 0xffffffff;
|
|
u32d[3] = (u32) 0x00000000;
|
|
ns_write_sram(card, vc->cbr_scd, u32d, 4);
|
|
|
|
fill_tst(card, n, vc);
|
|
} else if (vcc->qos.txtp.traffic_class == ATM_UBR) {
|
|
vc->cbr_scd = 0x00000000;
|
|
vc->scq = card->scq0;
|
|
}
|
|
|
|
if (vcc->qos.txtp.traffic_class != ATM_NONE) {
|
|
vc->tx = 1;
|
|
vc->tx_vcc = vcc;
|
|
vc->tbd_count = 0;
|
|
}
|
|
if (vcc->qos.rxtp.traffic_class != ATM_NONE) {
|
|
u32 status;
|
|
|
|
vc->rx = 1;
|
|
vc->rx_vcc = vcc;
|
|
vc->rx_iov = NULL;
|
|
|
|
/* Open the connection in hardware */
|
|
if (vcc->qos.aal == ATM_AAL5)
|
|
status = NS_RCTE_AAL5 | NS_RCTE_CONNECTOPEN;
|
|
else /* vcc->qos.aal == ATM_AAL0 */
|
|
status = NS_RCTE_AAL0 | NS_RCTE_CONNECTOPEN;
|
|
#ifdef RCQ_SUPPORT
|
|
status |= NS_RCTE_RAWCELLINTEN;
|
|
#endif /* RCQ_SUPPORT */
|
|
ns_write_sram(card,
|
|
NS_RCT +
|
|
(vpi << card->vcibits | vci) *
|
|
NS_RCT_ENTRY_SIZE, &status, 1);
|
|
}
|
|
|
|
}
|
|
|
|
set_bit(ATM_VF_READY, &vcc->flags);
|
|
return 0;
|
|
}
|
|
|
|
static void ns_close(struct atm_vcc *vcc)
|
|
{
|
|
vc_map *vc;
|
|
ns_dev *card;
|
|
u32 data;
|
|
int i;
|
|
|
|
vc = vcc->dev_data;
|
|
card = vcc->dev->dev_data;
|
|
PRINTK("nicstar%d: closing vpi.vci %d.%d \n", card->index,
|
|
(int)vcc->vpi, vcc->vci);
|
|
|
|
clear_bit(ATM_VF_READY, &vcc->flags);
|
|
|
|
if (vcc->qos.rxtp.traffic_class != ATM_NONE) {
|
|
u32 addr;
|
|
unsigned long flags;
|
|
|
|
addr =
|
|
NS_RCT +
|
|
(vcc->vpi << card->vcibits | vcc->vci) * NS_RCT_ENTRY_SIZE;
|
|
spin_lock_irqsave(&card->res_lock, flags);
|
|
while (CMD_BUSY(card)) ;
|
|
writel(NS_CMD_CLOSE_CONNECTION | addr << 2,
|
|
card->membase + CMD);
|
|
spin_unlock_irqrestore(&card->res_lock, flags);
|
|
|
|
vc->rx = 0;
|
|
if (vc->rx_iov != NULL) {
|
|
struct sk_buff *iovb;
|
|
u32 stat;
|
|
|
|
stat = readl(card->membase + STAT);
|
|
card->sbfqc = ns_stat_sfbqc_get(stat);
|
|
card->lbfqc = ns_stat_lfbqc_get(stat);
|
|
|
|
PRINTK
|
|
("nicstar%d: closing a VC with pending rx buffers.\n",
|
|
card->index);
|
|
iovb = vc->rx_iov;
|
|
recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
|
|
NS_PRV_IOVCNT(iovb));
|
|
NS_PRV_IOVCNT(iovb) = 0;
|
|
spin_lock_irqsave(&card->int_lock, flags);
|
|
recycle_iov_buf(card, iovb);
|
|
spin_unlock_irqrestore(&card->int_lock, flags);
|
|
vc->rx_iov = NULL;
|
|
}
|
|
}
|
|
|
|
if (vcc->qos.txtp.traffic_class != ATM_NONE) {
|
|
vc->tx = 0;
|
|
}
|
|
|
|
if (vcc->qos.txtp.traffic_class == ATM_CBR) {
|
|
unsigned long flags;
|
|
ns_scqe *scqep;
|
|
scq_info *scq;
|
|
|
|
scq = vc->scq;
|
|
|
|
for (;;) {
|
|
spin_lock_irqsave(&scq->lock, flags);
|
|
scqep = scq->next;
|
|
if (scqep == scq->base)
|
|
scqep = scq->last;
|
|
else
|
|
scqep--;
|
|
if (scqep == scq->tail) {
|
|
spin_unlock_irqrestore(&scq->lock, flags);
|
|
break;
|
|
}
|
|
/* If the last entry is not a TSR, place one in the SCQ in order to
|
|
be able to completely drain it and then close. */
|
|
if (!ns_scqe_is_tsr(scqep) && scq->tail != scq->next) {
|
|
ns_scqe tsr;
|
|
u32 scdi, scqi;
|
|
u32 data;
|
|
int index;
|
|
|
|
tsr.word_1 = ns_tsr_mkword_1(NS_TSR_INTENABLE);
|
|
scdi = (vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE;
|
|
scqi = scq->next - scq->base;
|
|
tsr.word_2 = ns_tsr_mkword_2(scdi, scqi);
|
|
tsr.word_3 = 0x00000000;
|
|
tsr.word_4 = 0x00000000;
|
|
*scq->next = tsr;
|
|
index = (int)scqi;
|
|
scq->skb[index] = NULL;
|
|
if (scq->next == scq->last)
|
|
scq->next = scq->base;
|
|
else
|
|
scq->next++;
|
|
data = scq_virt_to_bus(scq, scq->next);
|
|
ns_write_sram(card, scq->scd, &data, 1);
|
|
}
|
|
spin_unlock_irqrestore(&scq->lock, flags);
|
|
schedule();
|
|
}
|
|
|
|
/* Free all TST entries */
|
|
data = NS_TST_OPCODE_VARIABLE;
|
|
for (i = 0; i < NS_TST_NUM_ENTRIES; i++) {
|
|
if (card->tste2vc[i] == vc) {
|
|
ns_write_sram(card, card->tst_addr + i, &data,
|
|
1);
|
|
card->tste2vc[i] = NULL;
|
|
card->tst_free_entries++;
|
|
}
|
|
}
|
|
|
|
card->scd2vc[(vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE] = NULL;
|
|
free_scq(card, vc->scq, vcc);
|
|
}
|
|
|
|
/* remove all references to vcc before deleting it */
|
|
if (vcc->qos.txtp.traffic_class != ATM_NONE) {
|
|
unsigned long flags;
|
|
scq_info *scq = card->scq0;
|
|
|
|
spin_lock_irqsave(&scq->lock, flags);
|
|
|
|
for (i = 0; i < scq->num_entries; i++) {
|
|
if (scq->skb[i] && ATM_SKB(scq->skb[i])->vcc == vcc) {
|
|
ATM_SKB(scq->skb[i])->vcc = NULL;
|
|
atm_return(vcc, scq->skb[i]->truesize);
|
|
PRINTK
|
|
("nicstar: deleted pending vcc mapping\n");
|
|
}
|
|
}
|
|
|
|
spin_unlock_irqrestore(&scq->lock, flags);
|
|
}
|
|
|
|
vcc->dev_data = NULL;
|
|
clear_bit(ATM_VF_PARTIAL, &vcc->flags);
|
|
clear_bit(ATM_VF_ADDR, &vcc->flags);
|
|
|
|
#ifdef RX_DEBUG
|
|
{
|
|
u32 stat, cfg;
|
|
stat = readl(card->membase + STAT);
|
|
cfg = readl(card->membase + CFG);
|
|
printk("STAT = 0x%08X CFG = 0x%08X \n", stat, cfg);
|
|
printk
|
|
("TSQ: base = 0x%p next = 0x%p last = 0x%p TSQT = 0x%08X \n",
|
|
card->tsq.base, card->tsq.next,
|
|
card->tsq.last, readl(card->membase + TSQT));
|
|
printk
|
|
("RSQ: base = 0x%p next = 0x%p last = 0x%p RSQT = 0x%08X \n",
|
|
card->rsq.base, card->rsq.next,
|
|
card->rsq.last, readl(card->membase + RSQT));
|
|
printk("Empty free buffer queue interrupt %s \n",
|
|
card->efbie ? "enabled" : "disabled");
|
|
printk("SBCNT = %d count = %d LBCNT = %d count = %d \n",
|
|
ns_stat_sfbqc_get(stat), card->sbpool.count,
|
|
ns_stat_lfbqc_get(stat), card->lbpool.count);
|
|
printk("hbpool.count = %d iovpool.count = %d \n",
|
|
card->hbpool.count, card->iovpool.count);
|
|
}
|
|
#endif /* RX_DEBUG */
|
|
}
|
|
|
|
static void fill_tst(ns_dev * card, int n, vc_map * vc)
|
|
{
|
|
u32 new_tst;
|
|
unsigned long cl;
|
|
int e, r;
|
|
u32 data;
|
|
|
|
/* It would be very complicated to keep the two TSTs synchronized while
|
|
assuring that writes are only made to the inactive TST. So, for now I
|
|
will use only one TST. If problems occur, I will change this again */
|
|
|
|
new_tst = card->tst_addr;
|
|
|
|
/* Fill procedure */
|
|
|
|
for (e = 0; e < NS_TST_NUM_ENTRIES; e++) {
|
|
if (card->tste2vc[e] == NULL)
|
|
break;
|
|
}
|
|
if (e == NS_TST_NUM_ENTRIES) {
|
|
printk("nicstar%d: No free TST entries found. \n", card->index);
|
|
return;
|
|
}
|
|
|
|
r = n;
|
|
cl = NS_TST_NUM_ENTRIES;
|
|
data = ns_tste_make(NS_TST_OPCODE_FIXED, vc->cbr_scd);
|
|
|
|
while (r > 0) {
|
|
if (cl >= NS_TST_NUM_ENTRIES && card->tste2vc[e] == NULL) {
|
|
card->tste2vc[e] = vc;
|
|
ns_write_sram(card, new_tst + e, &data, 1);
|
|
cl -= NS_TST_NUM_ENTRIES;
|
|
r--;
|
|
}
|
|
|
|
if (++e == NS_TST_NUM_ENTRIES) {
|
|
e = 0;
|
|
}
|
|
cl += n;
|
|
}
|
|
|
|
/* End of fill procedure */
|
|
|
|
data = ns_tste_make(NS_TST_OPCODE_END, new_tst);
|
|
ns_write_sram(card, new_tst + NS_TST_NUM_ENTRIES, &data, 1);
|
|
ns_write_sram(card, card->tst_addr + NS_TST_NUM_ENTRIES, &data, 1);
|
|
card->tst_addr = new_tst;
|
|
}
|
|
|
|
static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb)
|
|
{
|
|
ns_dev *card;
|
|
vc_map *vc;
|
|
scq_info *scq;
|
|
unsigned long buflen;
|
|
ns_scqe scqe;
|
|
u32 flags; /* TBD flags, not CPU flags */
|
|
|
|
card = vcc->dev->dev_data;
|
|
TXPRINTK("nicstar%d: ns_send() called.\n", card->index);
|
|
if ((vc = (vc_map *) vcc->dev_data) == NULL) {
|
|
printk("nicstar%d: vcc->dev_data == NULL on ns_send().\n",
|
|
card->index);
|
|
atomic_inc(&vcc->stats->tx_err);
|
|
dev_kfree_skb_any(skb);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!vc->tx) {
|
|
printk("nicstar%d: Trying to transmit on a non-tx VC.\n",
|
|
card->index);
|
|
atomic_inc(&vcc->stats->tx_err);
|
|
dev_kfree_skb_any(skb);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (vcc->qos.aal != ATM_AAL5 && vcc->qos.aal != ATM_AAL0) {
|
|
printk("nicstar%d: Only AAL0 and AAL5 are supported.\n",
|
|
card->index);
|
|
atomic_inc(&vcc->stats->tx_err);
|
|
dev_kfree_skb_any(skb);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (skb_shinfo(skb)->nr_frags != 0) {
|
|
printk("nicstar%d: No scatter-gather yet.\n", card->index);
|
|
atomic_inc(&vcc->stats->tx_err);
|
|
dev_kfree_skb_any(skb);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ATM_SKB(skb)->vcc = vcc;
|
|
|
|
NS_PRV_DMA(skb) = dma_map_single(&card->pcidev->dev, skb->data,
|
|
skb->len, DMA_TO_DEVICE);
|
|
|
|
if (vcc->qos.aal == ATM_AAL5) {
|
|
buflen = (skb->len + 47 + 8) / 48 * 48; /* Multiple of 48 */
|
|
flags = NS_TBD_AAL5;
|
|
scqe.word_2 = cpu_to_le32(NS_PRV_DMA(skb));
|
|
scqe.word_3 = cpu_to_le32(skb->len);
|
|
scqe.word_4 =
|
|
ns_tbd_mkword_4(0, (u32) vcc->vpi, (u32) vcc->vci, 0,
|
|
ATM_SKB(skb)->
|
|
atm_options & ATM_ATMOPT_CLP ? 1 : 0);
|
|
flags |= NS_TBD_EOPDU;
|
|
} else { /* (vcc->qos.aal == ATM_AAL0) */
|
|
|
|
buflen = ATM_CELL_PAYLOAD; /* i.e., 48 bytes */
|
|
flags = NS_TBD_AAL0;
|
|
scqe.word_2 = cpu_to_le32(NS_PRV_DMA(skb) + NS_AAL0_HEADER);
|
|
scqe.word_3 = cpu_to_le32(0x00000000);
|
|
if (*skb->data & 0x02) /* Payload type 1 - end of pdu */
|
|
flags |= NS_TBD_EOPDU;
|
|
scqe.word_4 =
|
|
cpu_to_le32(*((u32 *) skb->data) & ~NS_TBD_VC_MASK);
|
|
/* Force the VPI/VCI to be the same as in VCC struct */
|
|
scqe.word_4 |=
|
|
cpu_to_le32((((u32) vcc->
|
|
vpi) << NS_TBD_VPI_SHIFT | ((u32) vcc->
|
|
vci) <<
|
|
NS_TBD_VCI_SHIFT) & NS_TBD_VC_MASK);
|
|
}
|
|
|
|
if (vcc->qos.txtp.traffic_class == ATM_CBR) {
|
|
scqe.word_1 = ns_tbd_mkword_1_novbr(flags, (u32) buflen);
|
|
scq = ((vc_map *) vcc->dev_data)->scq;
|
|
} else {
|
|
scqe.word_1 =
|
|
ns_tbd_mkword_1(flags, (u32) 1, (u32) 1, (u32) buflen);
|
|
scq = card->scq0;
|
|
}
|
|
|
|
if (push_scqe(card, vc, scq, &scqe, skb) != 0) {
|
|
atomic_inc(&vcc->stats->tx_err);
|
|
dev_kfree_skb_any(skb);
|
|
return -EIO;
|
|
}
|
|
atomic_inc(&vcc->stats->tx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int push_scqe(ns_dev * card, vc_map * vc, scq_info * scq, ns_scqe * tbd,
|
|
struct sk_buff *skb)
|
|
{
|
|
unsigned long flags;
|
|
ns_scqe tsr;
|
|
u32 scdi, scqi;
|
|
int scq_is_vbr;
|
|
u32 data;
|
|
int index;
|
|
|
|
spin_lock_irqsave(&scq->lock, flags);
|
|
while (scq->tail == scq->next) {
|
|
if (in_interrupt()) {
|
|
spin_unlock_irqrestore(&scq->lock, flags);
|
|
printk("nicstar%d: Error pushing TBD.\n", card->index);
|
|
return 1;
|
|
}
|
|
|
|
scq->full = 1;
|
|
wait_event_interruptible_lock_irq_timeout(scq->scqfull_waitq,
|
|
scq->tail != scq->next,
|
|
scq->lock,
|
|
SCQFULL_TIMEOUT);
|
|
|
|
if (scq->full) {
|
|
spin_unlock_irqrestore(&scq->lock, flags);
|
|
printk("nicstar%d: Timeout pushing TBD.\n",
|
|
card->index);
|
|
return 1;
|
|
}
|
|
}
|
|
*scq->next = *tbd;
|
|
index = (int)(scq->next - scq->base);
|
|
scq->skb[index] = skb;
|
|
XPRINTK("nicstar%d: sending skb at 0x%p (pos %d).\n",
|
|
card->index, skb, index);
|
|
XPRINTK("nicstar%d: TBD written:\n0x%x\n0x%x\n0x%x\n0x%x\n at 0x%p.\n",
|
|
card->index, le32_to_cpu(tbd->word_1), le32_to_cpu(tbd->word_2),
|
|
le32_to_cpu(tbd->word_3), le32_to_cpu(tbd->word_4),
|
|
scq->next);
|
|
if (scq->next == scq->last)
|
|
scq->next = scq->base;
|
|
else
|
|
scq->next++;
|
|
|
|
vc->tbd_count++;
|
|
if (scq->num_entries == VBR_SCQ_NUM_ENTRIES) {
|
|
scq->tbd_count++;
|
|
scq_is_vbr = 1;
|
|
} else
|
|
scq_is_vbr = 0;
|
|
|
|
if (vc->tbd_count >= MAX_TBD_PER_VC
|
|
|| scq->tbd_count >= MAX_TBD_PER_SCQ) {
|
|
int has_run = 0;
|
|
|
|
while (scq->tail == scq->next) {
|
|
if (in_interrupt()) {
|
|
data = scq_virt_to_bus(scq, scq->next);
|
|
ns_write_sram(card, scq->scd, &data, 1);
|
|
spin_unlock_irqrestore(&scq->lock, flags);
|
|
printk("nicstar%d: Error pushing TSR.\n",
|
|
card->index);
|
|
return 0;
|
|
}
|
|
|
|
scq->full = 1;
|
|
if (has_run++)
|
|
break;
|
|
wait_event_interruptible_lock_irq_timeout(scq->scqfull_waitq,
|
|
scq->tail != scq->next,
|
|
scq->lock,
|
|
SCQFULL_TIMEOUT);
|
|
}
|
|
|
|
if (!scq->full) {
|
|
tsr.word_1 = ns_tsr_mkword_1(NS_TSR_INTENABLE);
|
|
if (scq_is_vbr)
|
|
scdi = NS_TSR_SCDISVBR;
|
|
else
|
|
scdi = (vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE;
|
|
scqi = scq->next - scq->base;
|
|
tsr.word_2 = ns_tsr_mkword_2(scdi, scqi);
|
|
tsr.word_3 = 0x00000000;
|
|
tsr.word_4 = 0x00000000;
|
|
|
|
*scq->next = tsr;
|
|
index = (int)scqi;
|
|
scq->skb[index] = NULL;
|
|
XPRINTK
|
|
("nicstar%d: TSR written:\n0x%x\n0x%x\n0x%x\n0x%x\n at 0x%p.\n",
|
|
card->index, le32_to_cpu(tsr.word_1),
|
|
le32_to_cpu(tsr.word_2), le32_to_cpu(tsr.word_3),
|
|
le32_to_cpu(tsr.word_4), scq->next);
|
|
if (scq->next == scq->last)
|
|
scq->next = scq->base;
|
|
else
|
|
scq->next++;
|
|
vc->tbd_count = 0;
|
|
scq->tbd_count = 0;
|
|
} else
|
|
PRINTK("nicstar%d: Timeout pushing TSR.\n",
|
|
card->index);
|
|
}
|
|
data = scq_virt_to_bus(scq, scq->next);
|
|
ns_write_sram(card, scq->scd, &data, 1);
|
|
|
|
spin_unlock_irqrestore(&scq->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void process_tsq(ns_dev * card)
|
|
{
|
|
u32 scdi;
|
|
scq_info *scq;
|
|
ns_tsi *previous = NULL, *one_ahead, *two_ahead;
|
|
int serviced_entries; /* flag indicating at least on entry was serviced */
|
|
|
|
serviced_entries = 0;
|
|
|
|
if (card->tsq.next == card->tsq.last)
|
|
one_ahead = card->tsq.base;
|
|
else
|
|
one_ahead = card->tsq.next + 1;
|
|
|
|
if (one_ahead == card->tsq.last)
|
|
two_ahead = card->tsq.base;
|
|
else
|
|
two_ahead = one_ahead + 1;
|
|
|
|
while (!ns_tsi_isempty(card->tsq.next) || !ns_tsi_isempty(one_ahead) ||
|
|
!ns_tsi_isempty(two_ahead))
|
|
/* At most two empty, as stated in the 77201 errata */
|
|
{
|
|
serviced_entries = 1;
|
|
|
|
/* Skip the one or two possible empty entries */
|
|
while (ns_tsi_isempty(card->tsq.next)) {
|
|
if (card->tsq.next == card->tsq.last)
|
|
card->tsq.next = card->tsq.base;
|
|
else
|
|
card->tsq.next++;
|
|
}
|
|
|
|
if (!ns_tsi_tmrof(card->tsq.next)) {
|
|
scdi = ns_tsi_getscdindex(card->tsq.next);
|
|
if (scdi == NS_TSI_SCDISVBR)
|
|
scq = card->scq0;
|
|
else {
|
|
if (card->scd2vc[scdi] == NULL) {
|
|
printk
|
|
("nicstar%d: could not find VC from SCD index.\n",
|
|
card->index);
|
|
ns_tsi_init(card->tsq.next);
|
|
return;
|
|
}
|
|
scq = card->scd2vc[scdi]->scq;
|
|
}
|
|
drain_scq(card, scq, ns_tsi_getscqpos(card->tsq.next));
|
|
scq->full = 0;
|
|
wake_up_interruptible(&(scq->scqfull_waitq));
|
|
}
|
|
|
|
ns_tsi_init(card->tsq.next);
|
|
previous = card->tsq.next;
|
|
if (card->tsq.next == card->tsq.last)
|
|
card->tsq.next = card->tsq.base;
|
|
else
|
|
card->tsq.next++;
|
|
|
|
if (card->tsq.next == card->tsq.last)
|
|
one_ahead = card->tsq.base;
|
|
else
|
|
one_ahead = card->tsq.next + 1;
|
|
|
|
if (one_ahead == card->tsq.last)
|
|
two_ahead = card->tsq.base;
|
|
else
|
|
two_ahead = one_ahead + 1;
|
|
}
|
|
|
|
if (serviced_entries)
|
|
writel(PTR_DIFF(previous, card->tsq.base),
|
|
card->membase + TSQH);
|
|
}
|
|
|
|
static void drain_scq(ns_dev * card, scq_info * scq, int pos)
|
|
{
|
|
struct atm_vcc *vcc;
|
|
struct sk_buff *skb;
|
|
int i;
|
|
unsigned long flags;
|
|
|
|
XPRINTK("nicstar%d: drain_scq() called, scq at 0x%p, pos %d.\n",
|
|
card->index, scq, pos);
|
|
if (pos >= scq->num_entries) {
|
|
printk("nicstar%d: Bad index on drain_scq().\n", card->index);
|
|
return;
|
|
}
|
|
|
|
spin_lock_irqsave(&scq->lock, flags);
|
|
i = (int)(scq->tail - scq->base);
|
|
if (++i == scq->num_entries)
|
|
i = 0;
|
|
while (i != pos) {
|
|
skb = scq->skb[i];
|
|
XPRINTK("nicstar%d: freeing skb at 0x%p (index %d).\n",
|
|
card->index, skb, i);
|
|
if (skb != NULL) {
|
|
dma_unmap_single(&card->pcidev->dev,
|
|
NS_PRV_DMA(skb),
|
|
skb->len,
|
|
DMA_TO_DEVICE);
|
|
vcc = ATM_SKB(skb)->vcc;
|
|
if (vcc && vcc->pop != NULL) {
|
|
vcc->pop(vcc, skb);
|
|
} else {
|
|
dev_kfree_skb_irq(skb);
|
|
}
|
|
scq->skb[i] = NULL;
|
|
}
|
|
if (++i == scq->num_entries)
|
|
i = 0;
|
|
}
|
|
scq->tail = scq->base + pos;
|
|
spin_unlock_irqrestore(&scq->lock, flags);
|
|
}
|
|
|
|
static void process_rsq(ns_dev * card)
|
|
{
|
|
ns_rsqe *previous;
|
|
|
|
if (!ns_rsqe_valid(card->rsq.next))
|
|
return;
|
|
do {
|
|
dequeue_rx(card, card->rsq.next);
|
|
ns_rsqe_init(card->rsq.next);
|
|
previous = card->rsq.next;
|
|
if (card->rsq.next == card->rsq.last)
|
|
card->rsq.next = card->rsq.base;
|
|
else
|
|
card->rsq.next++;
|
|
} while (ns_rsqe_valid(card->rsq.next));
|
|
writel(PTR_DIFF(previous, card->rsq.base), card->membase + RSQH);
|
|
}
|
|
|
|
static void dequeue_rx(ns_dev * card, ns_rsqe * rsqe)
|
|
{
|
|
u32 vpi, vci;
|
|
vc_map *vc;
|
|
struct sk_buff *iovb;
|
|
struct iovec *iov;
|
|
struct atm_vcc *vcc;
|
|
struct sk_buff *skb;
|
|
unsigned short aal5_len;
|
|
int len;
|
|
u32 stat;
|
|
u32 id;
|
|
|
|
stat = readl(card->membase + STAT);
|
|
card->sbfqc = ns_stat_sfbqc_get(stat);
|
|
card->lbfqc = ns_stat_lfbqc_get(stat);
|
|
|
|
id = le32_to_cpu(rsqe->buffer_handle);
|
|
skb = idr_remove(&card->idr, id);
|
|
if (!skb) {
|
|
RXPRINTK(KERN_ERR
|
|
"nicstar%d: skb not found!\n", card->index);
|
|
return;
|
|
}
|
|
dma_sync_single_for_cpu(&card->pcidev->dev,
|
|
NS_PRV_DMA(skb),
|
|
(NS_PRV_BUFTYPE(skb) == BUF_SM
|
|
? NS_SMSKBSIZE : NS_LGSKBSIZE),
|
|
DMA_FROM_DEVICE);
|
|
dma_unmap_single(&card->pcidev->dev,
|
|
NS_PRV_DMA(skb),
|
|
(NS_PRV_BUFTYPE(skb) == BUF_SM
|
|
? NS_SMSKBSIZE : NS_LGSKBSIZE),
|
|
DMA_FROM_DEVICE);
|
|
vpi = ns_rsqe_vpi(rsqe);
|
|
vci = ns_rsqe_vci(rsqe);
|
|
if (vpi >= 1UL << card->vpibits || vci >= 1UL << card->vcibits) {
|
|
printk("nicstar%d: SDU received for out-of-range vc %d.%d.\n",
|
|
card->index, vpi, vci);
|
|
recycle_rx_buf(card, skb);
|
|
return;
|
|
}
|
|
|
|
vc = &(card->vcmap[vpi << card->vcibits | vci]);
|
|
if (!vc->rx) {
|
|
RXPRINTK("nicstar%d: SDU received on non-rx vc %d.%d.\n",
|
|
card->index, vpi, vci);
|
|
recycle_rx_buf(card, skb);
|
|
return;
|
|
}
|
|
|
|
vcc = vc->rx_vcc;
|
|
|
|
if (vcc->qos.aal == ATM_AAL0) {
|
|
struct sk_buff *sb;
|
|
unsigned char *cell;
|
|
int i;
|
|
|
|
cell = skb->data;
|
|
for (i = ns_rsqe_cellcount(rsqe); i; i--) {
|
|
sb = dev_alloc_skb(NS_SMSKBSIZE);
|
|
if (!sb) {
|
|
printk
|
|
("nicstar%d: Can't allocate buffers for aal0.\n",
|
|
card->index);
|
|
atomic_add(i, &vcc->stats->rx_drop);
|
|
break;
|
|
}
|
|
if (!atm_charge(vcc, sb->truesize)) {
|
|
RXPRINTK
|
|
("nicstar%d: atm_charge() dropped aal0 packets.\n",
|
|
card->index);
|
|
atomic_add(i - 1, &vcc->stats->rx_drop); /* already increased by 1 */
|
|
dev_kfree_skb_any(sb);
|
|
break;
|
|
}
|
|
/* Rebuild the header */
|
|
*((u32 *) sb->data) = le32_to_cpu(rsqe->word_1) << 4 |
|
|
(ns_rsqe_clp(rsqe) ? 0x00000001 : 0x00000000);
|
|
if (i == 1 && ns_rsqe_eopdu(rsqe))
|
|
*((u32 *) sb->data) |= 0x00000002;
|
|
skb_put(sb, NS_AAL0_HEADER);
|
|
memcpy(skb_tail_pointer(sb), cell, ATM_CELL_PAYLOAD);
|
|
skb_put(sb, ATM_CELL_PAYLOAD);
|
|
ATM_SKB(sb)->vcc = vcc;
|
|
__net_timestamp(sb);
|
|
vcc->push(vcc, sb);
|
|
atomic_inc(&vcc->stats->rx);
|
|
cell += ATM_CELL_PAYLOAD;
|
|
}
|
|
|
|
recycle_rx_buf(card, skb);
|
|
return;
|
|
}
|
|
|
|
/* To reach this point, the AAL layer can only be AAL5 */
|
|
|
|
if ((iovb = vc->rx_iov) == NULL) {
|
|
iovb = skb_dequeue(&(card->iovpool.queue));
|
|
if (iovb == NULL) { /* No buffers in the queue */
|
|
iovb = alloc_skb(NS_IOVBUFSIZE, GFP_ATOMIC);
|
|
if (iovb == NULL) {
|
|
printk("nicstar%d: Out of iovec buffers.\n",
|
|
card->index);
|
|
atomic_inc(&vcc->stats->rx_drop);
|
|
recycle_rx_buf(card, skb);
|
|
return;
|
|
}
|
|
NS_PRV_BUFTYPE(iovb) = BUF_NONE;
|
|
} else if (--card->iovpool.count < card->iovnr.min) {
|
|
struct sk_buff *new_iovb;
|
|
if ((new_iovb =
|
|
alloc_skb(NS_IOVBUFSIZE, GFP_ATOMIC)) != NULL) {
|
|
NS_PRV_BUFTYPE(iovb) = BUF_NONE;
|
|
skb_queue_tail(&card->iovpool.queue, new_iovb);
|
|
card->iovpool.count++;
|
|
}
|
|
}
|
|
vc->rx_iov = iovb;
|
|
NS_PRV_IOVCNT(iovb) = 0;
|
|
iovb->len = 0;
|
|
iovb->data = iovb->head;
|
|
skb_reset_tail_pointer(iovb);
|
|
/* IMPORTANT: a pointer to the sk_buff containing the small or large
|
|
buffer is stored as iovec base, NOT a pointer to the
|
|
small or large buffer itself. */
|
|
} else if (NS_PRV_IOVCNT(iovb) >= NS_MAX_IOVECS) {
|
|
printk("nicstar%d: received too big AAL5 SDU.\n", card->index);
|
|
atomic_inc(&vcc->stats->rx_err);
|
|
recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
|
|
NS_MAX_IOVECS);
|
|
NS_PRV_IOVCNT(iovb) = 0;
|
|
iovb->len = 0;
|
|
iovb->data = iovb->head;
|
|
skb_reset_tail_pointer(iovb);
|
|
}
|
|
iov = &((struct iovec *)iovb->data)[NS_PRV_IOVCNT(iovb)++];
|
|
iov->iov_base = (void *)skb;
|
|
iov->iov_len = ns_rsqe_cellcount(rsqe) * 48;
|
|
iovb->len += iov->iov_len;
|
|
|
|
#ifdef EXTRA_DEBUG
|
|
if (NS_PRV_IOVCNT(iovb) == 1) {
|
|
if (NS_PRV_BUFTYPE(skb) != BUF_SM) {
|
|
printk
|
|
("nicstar%d: Expected a small buffer, and this is not one.\n",
|
|
card->index);
|
|
which_list(card, skb);
|
|
atomic_inc(&vcc->stats->rx_err);
|
|
recycle_rx_buf(card, skb);
|
|
vc->rx_iov = NULL;
|
|
recycle_iov_buf(card, iovb);
|
|
return;
|
|
}
|
|
} else { /* NS_PRV_IOVCNT(iovb) >= 2 */
|
|
|
|
if (NS_PRV_BUFTYPE(skb) != BUF_LG) {
|
|
printk
|
|
("nicstar%d: Expected a large buffer, and this is not one.\n",
|
|
card->index);
|
|
which_list(card, skb);
|
|
atomic_inc(&vcc->stats->rx_err);
|
|
recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
|
|
NS_PRV_IOVCNT(iovb));
|
|
vc->rx_iov = NULL;
|
|
recycle_iov_buf(card, iovb);
|
|
return;
|
|
}
|
|
}
|
|
#endif /* EXTRA_DEBUG */
|
|
|
|
if (ns_rsqe_eopdu(rsqe)) {
|
|
/* This works correctly regardless of the endianness of the host */
|
|
unsigned char *L1L2 = (unsigned char *)
|
|
(skb->data + iov->iov_len - 6);
|
|
aal5_len = L1L2[0] << 8 | L1L2[1];
|
|
len = (aal5_len == 0x0000) ? 0x10000 : aal5_len;
|
|
if (ns_rsqe_crcerr(rsqe) ||
|
|
len + 8 > iovb->len || len + (47 + 8) < iovb->len) {
|
|
printk("nicstar%d: AAL5 CRC error", card->index);
|
|
if (len + 8 > iovb->len || len + (47 + 8) < iovb->len)
|
|
printk(" - PDU size mismatch.\n");
|
|
else
|
|
printk(".\n");
|
|
atomic_inc(&vcc->stats->rx_err);
|
|
recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
|
|
NS_PRV_IOVCNT(iovb));
|
|
vc->rx_iov = NULL;
|
|
recycle_iov_buf(card, iovb);
|
|
return;
|
|
}
|
|
|
|
/* By this point we (hopefully) have a complete SDU without errors. */
|
|
|
|
if (NS_PRV_IOVCNT(iovb) == 1) { /* Just a small buffer */
|
|
/* skb points to a small buffer */
|
|
if (!atm_charge(vcc, skb->truesize)) {
|
|
push_rxbufs(card, skb);
|
|
atomic_inc(&vcc->stats->rx_drop);
|
|
} else {
|
|
skb_put(skb, len);
|
|
dequeue_sm_buf(card, skb);
|
|
ATM_SKB(skb)->vcc = vcc;
|
|
__net_timestamp(skb);
|
|
vcc->push(vcc, skb);
|
|
atomic_inc(&vcc->stats->rx);
|
|
}
|
|
} else if (NS_PRV_IOVCNT(iovb) == 2) { /* One small plus one large buffer */
|
|
struct sk_buff *sb;
|
|
|
|
sb = (struct sk_buff *)(iov - 1)->iov_base;
|
|
/* skb points to a large buffer */
|
|
|
|
if (len <= NS_SMBUFSIZE) {
|
|
if (!atm_charge(vcc, sb->truesize)) {
|
|
push_rxbufs(card, sb);
|
|
atomic_inc(&vcc->stats->rx_drop);
|
|
} else {
|
|
skb_put(sb, len);
|
|
dequeue_sm_buf(card, sb);
|
|
ATM_SKB(sb)->vcc = vcc;
|
|
__net_timestamp(sb);
|
|
vcc->push(vcc, sb);
|
|
atomic_inc(&vcc->stats->rx);
|
|
}
|
|
|
|
push_rxbufs(card, skb);
|
|
|
|
} else { /* len > NS_SMBUFSIZE, the usual case */
|
|
|
|
if (!atm_charge(vcc, skb->truesize)) {
|
|
push_rxbufs(card, skb);
|
|
atomic_inc(&vcc->stats->rx_drop);
|
|
} else {
|
|
dequeue_lg_buf(card, skb);
|
|
skb_push(skb, NS_SMBUFSIZE);
|
|
skb_copy_from_linear_data(sb, skb->data,
|
|
NS_SMBUFSIZE);
|
|
skb_put(skb, len - NS_SMBUFSIZE);
|
|
ATM_SKB(skb)->vcc = vcc;
|
|
__net_timestamp(skb);
|
|
vcc->push(vcc, skb);
|
|
atomic_inc(&vcc->stats->rx);
|
|
}
|
|
|
|
push_rxbufs(card, sb);
|
|
|
|
}
|
|
|
|
} else { /* Must push a huge buffer */
|
|
|
|
struct sk_buff *hb, *sb, *lb;
|
|
int remaining, tocopy;
|
|
int j;
|
|
|
|
hb = skb_dequeue(&(card->hbpool.queue));
|
|
if (hb == NULL) { /* No buffers in the queue */
|
|
|
|
hb = dev_alloc_skb(NS_HBUFSIZE);
|
|
if (hb == NULL) {
|
|
printk
|
|
("nicstar%d: Out of huge buffers.\n",
|
|
card->index);
|
|
atomic_inc(&vcc->stats->rx_drop);
|
|
recycle_iovec_rx_bufs(card,
|
|
(struct iovec *)
|
|
iovb->data,
|
|
NS_PRV_IOVCNT(iovb));
|
|
vc->rx_iov = NULL;
|
|
recycle_iov_buf(card, iovb);
|
|
return;
|
|
} else if (card->hbpool.count < card->hbnr.min) {
|
|
struct sk_buff *new_hb;
|
|
if ((new_hb =
|
|
dev_alloc_skb(NS_HBUFSIZE)) !=
|
|
NULL) {
|
|
skb_queue_tail(&card->hbpool.
|
|
queue, new_hb);
|
|
card->hbpool.count++;
|
|
}
|
|
}
|
|
NS_PRV_BUFTYPE(hb) = BUF_NONE;
|
|
} else if (--card->hbpool.count < card->hbnr.min) {
|
|
struct sk_buff *new_hb;
|
|
if ((new_hb =
|
|
dev_alloc_skb(NS_HBUFSIZE)) != NULL) {
|
|
NS_PRV_BUFTYPE(new_hb) = BUF_NONE;
|
|
skb_queue_tail(&card->hbpool.queue,
|
|
new_hb);
|
|
card->hbpool.count++;
|
|
}
|
|
if (card->hbpool.count < card->hbnr.min) {
|
|
if ((new_hb =
|
|
dev_alloc_skb(NS_HBUFSIZE)) !=
|
|
NULL) {
|
|
NS_PRV_BUFTYPE(new_hb) =
|
|
BUF_NONE;
|
|
skb_queue_tail(&card->hbpool.
|
|
queue, new_hb);
|
|
card->hbpool.count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
iov = (struct iovec *)iovb->data;
|
|
|
|
if (!atm_charge(vcc, hb->truesize)) {
|
|
recycle_iovec_rx_bufs(card, iov,
|
|
NS_PRV_IOVCNT(iovb));
|
|
if (card->hbpool.count < card->hbnr.max) {
|
|
skb_queue_tail(&card->hbpool.queue, hb);
|
|
card->hbpool.count++;
|
|
} else
|
|
dev_kfree_skb_any(hb);
|
|
atomic_inc(&vcc->stats->rx_drop);
|
|
} else {
|
|
/* Copy the small buffer to the huge buffer */
|
|
sb = (struct sk_buff *)iov->iov_base;
|
|
skb_copy_from_linear_data(sb, hb->data,
|
|
iov->iov_len);
|
|
skb_put(hb, iov->iov_len);
|
|
remaining = len - iov->iov_len;
|
|
iov++;
|
|
/* Free the small buffer */
|
|
push_rxbufs(card, sb);
|
|
|
|
/* Copy all large buffers to the huge buffer and free them */
|
|
for (j = 1; j < NS_PRV_IOVCNT(iovb); j++) {
|
|
lb = (struct sk_buff *)iov->iov_base;
|
|
tocopy =
|
|
min_t(int, remaining, iov->iov_len);
|
|
skb_copy_from_linear_data(lb,
|
|
skb_tail_pointer
|
|
(hb), tocopy);
|
|
skb_put(hb, tocopy);
|
|
iov++;
|
|
remaining -= tocopy;
|
|
push_rxbufs(card, lb);
|
|
}
|
|
#ifdef EXTRA_DEBUG
|
|
if (remaining != 0 || hb->len != len)
|
|
printk
|
|
("nicstar%d: Huge buffer len mismatch.\n",
|
|
card->index);
|
|
#endif /* EXTRA_DEBUG */
|
|
ATM_SKB(hb)->vcc = vcc;
|
|
__net_timestamp(hb);
|
|
vcc->push(vcc, hb);
|
|
atomic_inc(&vcc->stats->rx);
|
|
}
|
|
}
|
|
|
|
vc->rx_iov = NULL;
|
|
recycle_iov_buf(card, iovb);
|
|
}
|
|
|
|
}
|
|
|
|
static void recycle_rx_buf(ns_dev * card, struct sk_buff *skb)
|
|
{
|
|
if (unlikely(NS_PRV_BUFTYPE(skb) == BUF_NONE)) {
|
|
printk("nicstar%d: What kind of rx buffer is this?\n",
|
|
card->index);
|
|
dev_kfree_skb_any(skb);
|
|
} else
|
|
push_rxbufs(card, skb);
|
|
}
|
|
|
|
static void recycle_iovec_rx_bufs(ns_dev * card, struct iovec *iov, int count)
|
|
{
|
|
while (count-- > 0)
|
|
recycle_rx_buf(card, (struct sk_buff *)(iov++)->iov_base);
|
|
}
|
|
|
|
static void recycle_iov_buf(ns_dev * card, struct sk_buff *iovb)
|
|
{
|
|
if (card->iovpool.count < card->iovnr.max) {
|
|
skb_queue_tail(&card->iovpool.queue, iovb);
|
|
card->iovpool.count++;
|
|
} else
|
|
dev_kfree_skb_any(iovb);
|
|
}
|
|
|
|
static void dequeue_sm_buf(ns_dev * card, struct sk_buff *sb)
|
|
{
|
|
skb_unlink(sb, &card->sbpool.queue);
|
|
if (card->sbfqc < card->sbnr.init) {
|
|
struct sk_buff *new_sb;
|
|
if ((new_sb = dev_alloc_skb(NS_SMSKBSIZE)) != NULL) {
|
|
NS_PRV_BUFTYPE(new_sb) = BUF_SM;
|
|
skb_queue_tail(&card->sbpool.queue, new_sb);
|
|
skb_reserve(new_sb, NS_AAL0_HEADER);
|
|
push_rxbufs(card, new_sb);
|
|
}
|
|
}
|
|
if (card->sbfqc < card->sbnr.init)
|
|
{
|
|
struct sk_buff *new_sb;
|
|
if ((new_sb = dev_alloc_skb(NS_SMSKBSIZE)) != NULL) {
|
|
NS_PRV_BUFTYPE(new_sb) = BUF_SM;
|
|
skb_queue_tail(&card->sbpool.queue, new_sb);
|
|
skb_reserve(new_sb, NS_AAL0_HEADER);
|
|
push_rxbufs(card, new_sb);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dequeue_lg_buf(ns_dev * card, struct sk_buff *lb)
|
|
{
|
|
skb_unlink(lb, &card->lbpool.queue);
|
|
if (card->lbfqc < card->lbnr.init) {
|
|
struct sk_buff *new_lb;
|
|
if ((new_lb = dev_alloc_skb(NS_LGSKBSIZE)) != NULL) {
|
|
NS_PRV_BUFTYPE(new_lb) = BUF_LG;
|
|
skb_queue_tail(&card->lbpool.queue, new_lb);
|
|
skb_reserve(new_lb, NS_SMBUFSIZE);
|
|
push_rxbufs(card, new_lb);
|
|
}
|
|
}
|
|
if (card->lbfqc < card->lbnr.init)
|
|
{
|
|
struct sk_buff *new_lb;
|
|
if ((new_lb = dev_alloc_skb(NS_LGSKBSIZE)) != NULL) {
|
|
NS_PRV_BUFTYPE(new_lb) = BUF_LG;
|
|
skb_queue_tail(&card->lbpool.queue, new_lb);
|
|
skb_reserve(new_lb, NS_SMBUFSIZE);
|
|
push_rxbufs(card, new_lb);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int ns_proc_read(struct atm_dev *dev, loff_t * pos, char *page)
|
|
{
|
|
u32 stat;
|
|
ns_dev *card;
|
|
int left;
|
|
|
|
left = (int)*pos;
|
|
card = (ns_dev *) dev->dev_data;
|
|
stat = readl(card->membase + STAT);
|
|
if (!left--)
|
|
return sprintf(page, "Pool count min init max \n");
|
|
if (!left--)
|
|
return sprintf(page, "Small %5d %5d %5d %5d \n",
|
|
ns_stat_sfbqc_get(stat), card->sbnr.min,
|
|
card->sbnr.init, card->sbnr.max);
|
|
if (!left--)
|
|
return sprintf(page, "Large %5d %5d %5d %5d \n",
|
|
ns_stat_lfbqc_get(stat), card->lbnr.min,
|
|
card->lbnr.init, card->lbnr.max);
|
|
if (!left--)
|
|
return sprintf(page, "Huge %5d %5d %5d %5d \n",
|
|
card->hbpool.count, card->hbnr.min,
|
|
card->hbnr.init, card->hbnr.max);
|
|
if (!left--)
|
|
return sprintf(page, "Iovec %5d %5d %5d %5d \n",
|
|
card->iovpool.count, card->iovnr.min,
|
|
card->iovnr.init, card->iovnr.max);
|
|
if (!left--) {
|
|
int retval;
|
|
retval =
|
|
sprintf(page, "Interrupt counter: %u \n", card->intcnt);
|
|
card->intcnt = 0;
|
|
return retval;
|
|
}
|
|
#if 0
|
|
/* Dump 25.6 Mbps PHY registers */
|
|
/* Now there's a 25.6 Mbps PHY driver this code isn't needed. I left it
|
|
here just in case it's needed for debugging. */
|
|
if (card->max_pcr == ATM_25_PCR && !left--) {
|
|
u32 phy_regs[4];
|
|
u32 i;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
while (CMD_BUSY(card)) ;
|
|
writel(NS_CMD_READ_UTILITY | 0x00000200 | i,
|
|
card->membase + CMD);
|
|
while (CMD_BUSY(card)) ;
|
|
phy_regs[i] = readl(card->membase + DR0) & 0x000000FF;
|
|
}
|
|
|
|
return sprintf(page, "PHY regs: 0x%02X 0x%02X 0x%02X 0x%02X \n",
|
|
phy_regs[0], phy_regs[1], phy_regs[2],
|
|
phy_regs[3]);
|
|
}
|
|
#endif /* 0 - Dump 25.6 Mbps PHY registers */
|
|
#if 0
|
|
/* Dump TST */
|
|
if (left-- < NS_TST_NUM_ENTRIES) {
|
|
if (card->tste2vc[left + 1] == NULL)
|
|
return sprintf(page, "%5d - VBR/UBR \n", left + 1);
|
|
else
|
|
return sprintf(page, "%5d - %d %d \n", left + 1,
|
|
card->tste2vc[left + 1]->tx_vcc->vpi,
|
|
card->tste2vc[left + 1]->tx_vcc->vci);
|
|
}
|
|
#endif /* 0 */
|
|
return 0;
|
|
}
|
|
|
|
static int ns_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg)
|
|
{
|
|
ns_dev *card;
|
|
pool_levels pl;
|
|
long btype;
|
|
unsigned long flags;
|
|
|
|
card = dev->dev_data;
|
|
switch (cmd) {
|
|
case NS_GETPSTAT:
|
|
if (get_user
|
|
(pl.buftype, &((pool_levels __user *) arg)->buftype))
|
|
return -EFAULT;
|
|
switch (pl.buftype) {
|
|
case NS_BUFTYPE_SMALL:
|
|
pl.count =
|
|
ns_stat_sfbqc_get(readl(card->membase + STAT));
|
|
pl.level.min = card->sbnr.min;
|
|
pl.level.init = card->sbnr.init;
|
|
pl.level.max = card->sbnr.max;
|
|
break;
|
|
|
|
case NS_BUFTYPE_LARGE:
|
|
pl.count =
|
|
ns_stat_lfbqc_get(readl(card->membase + STAT));
|
|
pl.level.min = card->lbnr.min;
|
|
pl.level.init = card->lbnr.init;
|
|
pl.level.max = card->lbnr.max;
|
|
break;
|
|
|
|
case NS_BUFTYPE_HUGE:
|
|
pl.count = card->hbpool.count;
|
|
pl.level.min = card->hbnr.min;
|
|
pl.level.init = card->hbnr.init;
|
|
pl.level.max = card->hbnr.max;
|
|
break;
|
|
|
|
case NS_BUFTYPE_IOVEC:
|
|
pl.count = card->iovpool.count;
|
|
pl.level.min = card->iovnr.min;
|
|
pl.level.init = card->iovnr.init;
|
|
pl.level.max = card->iovnr.max;
|
|
break;
|
|
|
|
default:
|
|
return -ENOIOCTLCMD;
|
|
|
|
}
|
|
if (!copy_to_user((pool_levels __user *) arg, &pl, sizeof(pl)))
|
|
return (sizeof(pl));
|
|
else
|
|
return -EFAULT;
|
|
|
|
case NS_SETBUFLEV:
|
|
if (!capable(CAP_NET_ADMIN))
|
|
return -EPERM;
|
|
if (copy_from_user(&pl, (pool_levels __user *) arg, sizeof(pl)))
|
|
return -EFAULT;
|
|
if (pl.level.min >= pl.level.init
|
|
|| pl.level.init >= pl.level.max)
|
|
return -EINVAL;
|
|
if (pl.level.min == 0)
|
|
return -EINVAL;
|
|
switch (pl.buftype) {
|
|
case NS_BUFTYPE_SMALL:
|
|
if (pl.level.max > TOP_SB)
|
|
return -EINVAL;
|
|
card->sbnr.min = pl.level.min;
|
|
card->sbnr.init = pl.level.init;
|
|
card->sbnr.max = pl.level.max;
|
|
break;
|
|
|
|
case NS_BUFTYPE_LARGE:
|
|
if (pl.level.max > TOP_LB)
|
|
return -EINVAL;
|
|
card->lbnr.min = pl.level.min;
|
|
card->lbnr.init = pl.level.init;
|
|
card->lbnr.max = pl.level.max;
|
|
break;
|
|
|
|
case NS_BUFTYPE_HUGE:
|
|
if (pl.level.max > TOP_HB)
|
|
return -EINVAL;
|
|
card->hbnr.min = pl.level.min;
|
|
card->hbnr.init = pl.level.init;
|
|
card->hbnr.max = pl.level.max;
|
|
break;
|
|
|
|
case NS_BUFTYPE_IOVEC:
|
|
if (pl.level.max > TOP_IOVB)
|
|
return -EINVAL;
|
|
card->iovnr.min = pl.level.min;
|
|
card->iovnr.init = pl.level.init;
|
|
card->iovnr.max = pl.level.max;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
|
|
}
|
|
return 0;
|
|
|
|
case NS_ADJBUFLEV:
|
|
if (!capable(CAP_NET_ADMIN))
|
|
return -EPERM;
|
|
btype = (long)arg; /* a long is the same size as a pointer or bigger */
|
|
switch (btype) {
|
|
case NS_BUFTYPE_SMALL:
|
|
while (card->sbfqc < card->sbnr.init) {
|
|
struct sk_buff *sb;
|
|
|
|
sb = __dev_alloc_skb(NS_SMSKBSIZE, GFP_KERNEL);
|
|
if (sb == NULL)
|
|
return -ENOMEM;
|
|
NS_PRV_BUFTYPE(sb) = BUF_SM;
|
|
skb_queue_tail(&card->sbpool.queue, sb);
|
|
skb_reserve(sb, NS_AAL0_HEADER);
|
|
push_rxbufs(card, sb);
|
|
}
|
|
break;
|
|
|
|
case NS_BUFTYPE_LARGE:
|
|
while (card->lbfqc < card->lbnr.init) {
|
|
struct sk_buff *lb;
|
|
|
|
lb = __dev_alloc_skb(NS_LGSKBSIZE, GFP_KERNEL);
|
|
if (lb == NULL)
|
|
return -ENOMEM;
|
|
NS_PRV_BUFTYPE(lb) = BUF_LG;
|
|
skb_queue_tail(&card->lbpool.queue, lb);
|
|
skb_reserve(lb, NS_SMBUFSIZE);
|
|
push_rxbufs(card, lb);
|
|
}
|
|
break;
|
|
|
|
case NS_BUFTYPE_HUGE:
|
|
while (card->hbpool.count > card->hbnr.init) {
|
|
struct sk_buff *hb;
|
|
|
|
spin_lock_irqsave(&card->int_lock, flags);
|
|
hb = skb_dequeue(&card->hbpool.queue);
|
|
card->hbpool.count--;
|
|
spin_unlock_irqrestore(&card->int_lock, flags);
|
|
if (hb == NULL)
|
|
printk
|
|
("nicstar%d: huge buffer count inconsistent.\n",
|
|
card->index);
|
|
else
|
|
dev_kfree_skb_any(hb);
|
|
|
|
}
|
|
while (card->hbpool.count < card->hbnr.init) {
|
|
struct sk_buff *hb;
|
|
|
|
hb = __dev_alloc_skb(NS_HBUFSIZE, GFP_KERNEL);
|
|
if (hb == NULL)
|
|
return -ENOMEM;
|
|
NS_PRV_BUFTYPE(hb) = BUF_NONE;
|
|
spin_lock_irqsave(&card->int_lock, flags);
|
|
skb_queue_tail(&card->hbpool.queue, hb);
|
|
card->hbpool.count++;
|
|
spin_unlock_irqrestore(&card->int_lock, flags);
|
|
}
|
|
break;
|
|
|
|
case NS_BUFTYPE_IOVEC:
|
|
while (card->iovpool.count > card->iovnr.init) {
|
|
struct sk_buff *iovb;
|
|
|
|
spin_lock_irqsave(&card->int_lock, flags);
|
|
iovb = skb_dequeue(&card->iovpool.queue);
|
|
card->iovpool.count--;
|
|
spin_unlock_irqrestore(&card->int_lock, flags);
|
|
if (iovb == NULL)
|
|
printk
|
|
("nicstar%d: iovec buffer count inconsistent.\n",
|
|
card->index);
|
|
else
|
|
dev_kfree_skb_any(iovb);
|
|
|
|
}
|
|
while (card->iovpool.count < card->iovnr.init) {
|
|
struct sk_buff *iovb;
|
|
|
|
iovb = alloc_skb(NS_IOVBUFSIZE, GFP_KERNEL);
|
|
if (iovb == NULL)
|
|
return -ENOMEM;
|
|
NS_PRV_BUFTYPE(iovb) = BUF_NONE;
|
|
spin_lock_irqsave(&card->int_lock, flags);
|
|
skb_queue_tail(&card->iovpool.queue, iovb);
|
|
card->iovpool.count++;
|
|
spin_unlock_irqrestore(&card->int_lock, flags);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
|
|
}
|
|
return 0;
|
|
|
|
default:
|
|
if (dev->phy && dev->phy->ioctl) {
|
|
return dev->phy->ioctl(dev, cmd, arg);
|
|
} else {
|
|
printk("nicstar%d: %s == NULL \n", card->index,
|
|
dev->phy ? "dev->phy->ioctl" : "dev->phy");
|
|
return -ENOIOCTLCMD;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef EXTRA_DEBUG
|
|
static void which_list(ns_dev * card, struct sk_buff *skb)
|
|
{
|
|
printk("skb buf_type: 0x%08x\n", NS_PRV_BUFTYPE(skb));
|
|
}
|
|
#endif /* EXTRA_DEBUG */
|
|
|
|
static void ns_poll(unsigned long arg)
|
|
{
|
|
int i;
|
|
ns_dev *card;
|
|
unsigned long flags;
|
|
u32 stat_r, stat_w;
|
|
|
|
PRINTK("nicstar: Entering ns_poll().\n");
|
|
for (i = 0; i < num_cards; i++) {
|
|
card = cards[i];
|
|
if (spin_is_locked(&card->int_lock)) {
|
|
/* Probably it isn't worth spinning */
|
|
continue;
|
|
}
|
|
spin_lock_irqsave(&card->int_lock, flags);
|
|
|
|
stat_w = 0;
|
|
stat_r = readl(card->membase + STAT);
|
|
if (stat_r & NS_STAT_TSIF)
|
|
stat_w |= NS_STAT_TSIF;
|
|
if (stat_r & NS_STAT_EOPDU)
|
|
stat_w |= NS_STAT_EOPDU;
|
|
|
|
process_tsq(card);
|
|
process_rsq(card);
|
|
|
|
writel(stat_w, card->membase + STAT);
|
|
spin_unlock_irqrestore(&card->int_lock, flags);
|
|
}
|
|
mod_timer(&ns_timer, jiffies + NS_POLL_PERIOD);
|
|
PRINTK("nicstar: Leaving ns_poll().\n");
|
|
}
|
|
|
|
static void ns_phy_put(struct atm_dev *dev, unsigned char value,
|
|
unsigned long addr)
|
|
{
|
|
ns_dev *card;
|
|
unsigned long flags;
|
|
|
|
card = dev->dev_data;
|
|
spin_lock_irqsave(&card->res_lock, flags);
|
|
while (CMD_BUSY(card)) ;
|
|
writel((u32) value, card->membase + DR0);
|
|
writel(NS_CMD_WRITE_UTILITY | 0x00000200 | (addr & 0x000000FF),
|
|
card->membase + CMD);
|
|
spin_unlock_irqrestore(&card->res_lock, flags);
|
|
}
|
|
|
|
static unsigned char ns_phy_get(struct atm_dev *dev, unsigned long addr)
|
|
{
|
|
ns_dev *card;
|
|
unsigned long flags;
|
|
u32 data;
|
|
|
|
card = dev->dev_data;
|
|
spin_lock_irqsave(&card->res_lock, flags);
|
|
while (CMD_BUSY(card)) ;
|
|
writel(NS_CMD_READ_UTILITY | 0x00000200 | (addr & 0x000000FF),
|
|
card->membase + CMD);
|
|
while (CMD_BUSY(card)) ;
|
|
data = readl(card->membase + DR0) & 0x000000FF;
|
|
spin_unlock_irqrestore(&card->res_lock, flags);
|
|
return (unsigned char)data;
|
|
}
|
|
|
|
module_init(nicstar_init);
|
|
module_exit(nicstar_cleanup);
|