/*
Broadcom BCM43xx wireless driver
DMA ringbuffer and descriptor allocation/management
Copyright (c) 2005 Michael Buesch <mbuesch@freenet.de>
Some code in this file is derived from the b44.c driver
Copyright (C) 2002 David S. Miller
Copyright (C) Pekka Pietikainen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "bcm43xx.h"
#include "bcm43xx_dma.h"
#include "bcm43xx_main.h"
#include "bcm43xx_debugfs.h"
#include "bcm43xx_power.h"
#include "bcm43xx_xmit.h"
#include <linux/dmapool.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <asm/semaphore.h>
static inline int free_slots(struct bcm43xx_dmaring *ring)
{
return (ring->nr_slots - ring->used_slots);
}
static inline int next_slot(struct bcm43xx_dmaring *ring, int slot)
{
assert(slot >= -1 && slot <= ring->nr_slots - 1);
if (slot == ring->nr_slots - 1)
return 0;
return slot + 1;
}
static inline int prev_slot(struct bcm43xx_dmaring *ring, int slot)
{
assert(slot >= 0 && slot <= ring->nr_slots - 1);
if (slot == 0)
return ring->nr_slots - 1;
return slot - 1;
}
/* Request a slot for usage. */
static inline
int request_slot(struct bcm43xx_dmaring *ring)
{
int slot;
assert(ring->tx);
assert(!ring->suspended);
assert(free_slots(ring) != 0);
slot = next_slot(ring, ring->current_slot);
ring->current_slot = slot;
ring->used_slots++;
/* Check the number of available slots and suspend TX,
* if we are running low on free slots.
*/
if (unlikely(free_slots(ring) < ring->suspend_mark)) {
netif_stop_queue(ring->bcm->net_dev);
ring->suspended = 1;
}
#ifdef CONFIG_BCM43XX_DEBUG
if (ring->used_slots > ring->max_used_slots)
ring->max_used_slots = ring->used_slots;
#endif /* CONFIG_BCM43XX_DEBUG*/
return slot;
}
/* Return a slot to the free slots. */
static inline
void return_slot(struct bcm43xx_dmaring *ring, int slot)
{
assert(ring->tx);
ring->used_slots--;
/* Check if TX is suspended and check if we have
* enough free slots to resume it again.
*/
if (unlikely(ring->suspended)) {
if (free_slots(ring) >= ring->resume_mark) {
ring->suspended = 0;
netif_wake_queue(ring->bcm->net_dev);
}
}
}
static inline
dma_addr_t map_descbuffer(struct bcm43xx_dmaring *ring,
unsigned char *buf,
size_t len,
int tx)
{
dma_addr_t dmaaddr;
if (tx) {
dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev,
buf, len,
DMA_TO_DEVICE);
} else {
dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev,
buf, len,
DMA_FROM_DEVICE);
}
return dmaaddr;
}
static inline
void unmap_descbuffer(struct bcm43xx_dmaring *ring,
dma_addr_t addr,
size_t len,
int tx)
{
if (tx) {
dma_unmap_single(&ring->bcm->pci_dev->dev,
addr, len,
DMA_TO_DEVICE);
} else {
dma_unmap_single(&ring->bcm->pci_dev->dev,
addr, len,
DMA_FROM_DEVICE);
}
}
static inline
void sync_descbuffer_for_cpu(struct bcm43xx_dmaring *ring,
dma_addr_t addr,
size_t len)
{
assert(!ring->tx);
dma_sync_single_for_cpu(&ring->bcm->pci_dev->dev,
addr, len, DMA_FROM_DEVICE);
}
static inline
void sync_descbuffer_for_device(struct bcm43xx_dmaring *ring,
dma_addr_t addr,
size_t len)
{
assert(!ring->tx);
dma_sync_single_for_device(&ring->bcm->pci_dev->dev,
addr, len, DMA_FROM_DEVICE);
}
/* Unmap and free a descriptor buffer. */
static inline
void free_descriptor_buffer(struct bcm43xx_dmaring *ring,
struct bcm43xx_dmadesc *desc,
struct bcm43xx_dmadesc_meta *meta,
int irq_context)
{
assert(meta->skb);
if (