⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bcm43xx_dma.c

📁 博通的bcm43xx系列Minipci接口无线网卡驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
	ring->mmio_base = dma_controller_base;	if (tx) {		ring->tx = 1;		ring->current_slot = -1;	} else {		switch (dma_controller_base) {		case BCM43xx_MMIO_DMA1_BASE:			ring->rx_buffersize = BCM43xx_DMA1_RXBUFFERSIZE;			ring->frameoffset = BCM43xx_DMA1_RX_FRAMEOFFSET;			break;		case BCM43xx_MMIO_DMA4_BASE:			ring->rx_buffersize = BCM43xx_DMA4_RXBUFFERSIZE;			ring->frameoffset = BCM43xx_DMA4_RX_FRAMEOFFSET;			break;		default:			assert(0);		}	}	err = alloc_ringmemory(ring);	if (err)		goto err_kfree_meta;	err = dmacontroller_setup(ring);	if (err)		goto err_free_ringmemory;out:	return ring;err_free_ringmemory:	free_ringmemory(ring);err_kfree_meta:	kfree(ring->meta);err_kfree_ring:	kfree(ring);	ring = NULL;	goto out;}/* Main cleanup function. */static void bcm43xx_destroy_dmaring(struct bcm43xx_dmaring *ring){	if (!ring)		return;	dprintk(KERN_INFO PFX "DMA 0x%04x (%s) max used slots: %d/%d\n",		ring->mmio_base,		(ring->tx) ? "TX" : "RX",		ring->max_used_slots, ring->nr_slots);	/* Device IRQs are disabled prior entering this function,	 * so no need to take care of concurrency with rx handler stuff.	 */	dmacontroller_cleanup(ring);	free_all_descbuffers(ring);	free_ringmemory(ring);	kfree(ring->meta);	kfree(ring);}void bcm43xx_dma_free(struct bcm43xx_private *bcm){	struct bcm43xx_dma *dma = bcm->current_core->dma;	bcm43xx_destroy_dmaring(dma->rx_ring1);	dma->rx_ring1 = NULL;	bcm43xx_destroy_dmaring(dma->rx_ring0);	dma->rx_ring0 = NULL;	bcm43xx_destroy_dmaring(dma->tx_ring3);	dma->tx_ring3 = NULL;	bcm43xx_destroy_dmaring(dma->tx_ring2);	dma->tx_ring2 = NULL;	bcm43xx_destroy_dmaring(dma->tx_ring1);	dma->tx_ring1 = NULL;	bcm43xx_destroy_dmaring(dma->tx_ring0);	dma->tx_ring0 = NULL;}int bcm43xx_dma_init(struct bcm43xx_private *bcm){	struct bcm43xx_dma *dma = bcm->current_core->dma;	struct bcm43xx_dmaring *ring;	int err = -ENOMEM;	/* setup TX DMA channels. */	ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA1_BASE,				     BCM43xx_TXRING_SLOTS, 1);	if (!ring)		goto out;	dma->tx_ring0 = ring;	ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA2_BASE,				     BCM43xx_TXRING_SLOTS, 1);	if (!ring)		goto err_destroy_tx0;	dma->tx_ring1 = ring;	ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA3_BASE,				     BCM43xx_TXRING_SLOTS, 1);	if (!ring)		goto err_destroy_tx1;	dma->tx_ring2 = ring;	ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA4_BASE,				     BCM43xx_TXRING_SLOTS, 1);	if (!ring)		goto err_destroy_tx2;	dma->tx_ring3 = ring;	/* setup RX DMA channels. */	ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA1_BASE,				     BCM43xx_RXRING_SLOTS, 0);	if (!ring)		goto err_destroy_tx3;	dma->rx_ring0 = ring;	if (bcm->current_core->rev < 5) {		ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA4_BASE,					     BCM43xx_RXRING_SLOTS, 0);		if (!ring)			goto err_destroy_rx0;		dma->rx_ring1 = ring;	}	dprintk(KERN_INFO PFX "DMA initialized\n");	err = 0;out:	return err;err_destroy_rx0:	bcm43xx_destroy_dmaring(dma->rx_ring0);	dma->rx_ring0 = NULL;err_destroy_tx3:	bcm43xx_destroy_dmaring(dma->tx_ring3);	dma->tx_ring3 = NULL;err_destroy_tx2:	bcm43xx_destroy_dmaring(dma->tx_ring2);	dma->tx_ring2 = NULL;err_destroy_tx1:	bcm43xx_destroy_dmaring(dma->tx_ring1);	dma->tx_ring1 = NULL;err_destroy_tx0:	bcm43xx_destroy_dmaring(dma->tx_ring0);	dma->tx_ring0 = NULL;	goto out;}/* Generate a cookie for the TX header. */static u16 generate_cookie(struct bcm43xx_dmaring *ring,			   int slot){	u16 cookie = 0x0000;	/* Use the upper 4 bits of the cookie as	 * DMA controller ID and store the slot number	 * in the lower 12 bits	 */	switch (ring->mmio_base) {	default:		assert(0);	case BCM43xx_MMIO_DMA1_BASE:		break;	case BCM43xx_MMIO_DMA2_BASE:		cookie = 0x1000;		break;	case BCM43xx_MMIO_DMA3_BASE:		cookie = 0x2000;		break;	case BCM43xx_MMIO_DMA4_BASE:		cookie = 0x3000;		break;	}	assert(((u16)slot & 0xF000) == 0x0000);	cookie |= (u16)slot;	return cookie;}/* Inspect a cookie and find out to which controller/slot it belongs. */staticstruct bcm43xx_dmaring * parse_cookie(struct bcm43xx_private *bcm,				      u16 cookie, int *slot){	struct bcm43xx_dma *dma = bcm->current_core->dma;	struct bcm43xx_dmaring *ring = NULL;	switch (cookie & 0xF000) {	case 0x0000:		ring = dma->tx_ring0;		break;	case 0x1000:		ring = dma->tx_ring1;		break;	case 0x2000:		ring = dma->tx_ring2;		break;	case 0x3000:		ring = dma->tx_ring3;		break;	default:		assert(0);	}	*slot = (cookie & 0x0FFF);	assert(*slot >= 0 && *slot < ring->nr_slots);	return ring;}static void dmacontroller_poke_tx(struct bcm43xx_dmaring *ring,				  int slot){	/* Everything is ready to start. Buffers are DMA mapped and	 * associated with slots.	 * "slot" is the last slot of the new frame we want to transmit.	 * Close your seat belts now, please.	 */	wmb();	slot = next_slot(ring, slot);	bcm43xx_write32(ring->bcm,			ring->mmio_base + BCM43xx_DMA_TX_DESC_INDEX,			(u32)(slot * sizeof(struct bcm43xx_dmadesc)));}static int dma_tx_fragment(struct bcm43xx_dmaring *ring,			   struct sk_buff *skb,			   u8 cur_frag){	int slot;	struct bcm43xx_dmadesc *desc;	struct bcm43xx_dmadesc_meta *meta;	u32 desc_ctl;	u32 desc_addr;	assert(skb_shinfo(skb)->nr_frags == 0);	slot = request_slot(ring);	desc = ring->vbase + slot;	meta = ring->meta + slot;	/* Add a device specific TX header. */	assert(skb_headroom(skb) >= sizeof(struct bcm43xx_txhdr));	/* Reserve enough headroom for the device tx header. */	__skb_push(skb, sizeof(struct bcm43xx_txhdr));	/* Now calculate and add the tx header.	 * The tx header includes the PLCP header.	 */	bcm43xx_generate_txhdr(ring->bcm,			       (struct bcm43xx_txhdr *)skb->data,			       skb->data + sizeof(struct bcm43xx_txhdr),			       skb->len - sizeof(struct bcm43xx_txhdr),			       (cur_frag == 0),			       generate_cookie(ring, slot));	meta->skb = skb;	meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);	if (unlikely(meta->dmaaddr + skb->len > BCM43xx_DMA_BUSADDRMAX)) {		return_slot(ring, slot);		printk(KERN_ERR PFX ">>>FATAL ERROR<<<  DMA TX SKB >1G "				    "(0x%08x, len: %u)\n",		       meta->dmaaddr, skb->len);		return -ENOMEM;	}	desc_addr = (u32)(meta->dmaaddr + ring->memoffset);	desc_ctl = BCM43xx_DMADTOR_FRAMESTART | BCM43xx_DMADTOR_FRAMEEND;	desc_ctl |= BCM43xx_DMADTOR_COMPIRQ;	desc_ctl |= (BCM43xx_DMADTOR_BYTECNT_MASK &		     (u32)(meta->skb->len - ring->frameoffset));	if (slot == ring->nr_slots - 1)		desc_ctl |= BCM43xx_DMADTOR_DTABLEEND;	set_desc_ctl(desc, desc_ctl);	set_desc_addr(desc, desc_addr);	/* Now transfer the whole frame. */	dmacontroller_poke_tx(ring, slot);	return 0;}int bcm43xx_dma_tx(struct bcm43xx_private *bcm,		   struct ieee80211_txb *txb){	/* We just received a packet from the kernel network subsystem.	 * Add headers and DMA map the memory. Poke	 * the device to send the stuff.	 * Note that this is called from atomic context.	 */	struct bcm43xx_dmaring *ring = bcm->current_core->dma->tx_ring1;	u8 i;	struct sk_buff *skb;	assert(ring->tx);	if (unlikely(free_slots(ring) < txb->nr_frags)) {		/* The queue should be stopped,		 * if we are low on free slots.		 * If this ever triggers, we have to lower the suspend_mark.		 */		dprintkl(KERN_ERR PFX "Out of DMA descriptor slots!\n");		return -ENOMEM;	}	for (i = 0; i < txb->nr_frags; i++) {		skb = txb->fragments[i];		/* Take skb from ieee80211_txb_free */		txb->fragments[i] = NULL;		dma_tx_fragment(ring, skb, i);		//TODO: handle failure of dma_tx_fragment	}	ieee80211_txb_free(txb);	return 0;}void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm,				   struct bcm43xx_xmitstatus *status){	struct bcm43xx_dmaring *ring;	struct bcm43xx_dmadesc *desc;	struct bcm43xx_dmadesc_meta *meta;	int is_last_fragment;	int slot;	ring = parse_cookie(bcm, status->cookie, &slot);	assert(ring);	assert(ring->tx);	assert(get_desc_ctl(ring->vbase + slot) & BCM43xx_DMADTOR_FRAMESTART);	while (1) {		assert(slot >= 0 && slot < ring->nr_slots);		desc = ring->vbase + slot;		meta = ring->meta + slot;		is_last_fragment = !!(get_desc_ctl(desc) & BCM43xx_DMADTOR_FRAMEEND);		unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1);		free_descriptor_buffer(ring, desc, meta, 1);		/* Everything belonging to the slot is unmapped		 * and freed, so we can return it.		 */		return_slot(ring, slot);		if (is_last_fragment)			break;		slot = next_slot(ring, slot);	}	bcm->stats.last_tx = jiffies;}static void dma_rx(struct bcm43xx_dmaring *ring,		   int *slot){	struct bcm43xx_dmadesc *desc;	struct bcm43xx_dmadesc_meta *meta;	struct bcm43xx_rxhdr *rxhdr;	struct sk_buff *skb;	u16 len;	int err;	dma_addr_t dmaaddr;	desc = ring->vbase + *slot;	meta = ring->meta + *slot;	sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize);	skb = meta->skb;	if (ring->mmio_base == BCM43xx_MMIO_DMA4_BASE) {		/* We received an xmit status. */		struct bcm43xx_hwxmitstatus *hw = (struct bcm43xx_hwxmitstatus *)skb->data;		struct bcm43xx_xmitstatus stat;		stat.cookie = le16_to_cpu(hw->cookie);		stat.flags = hw->flags;		stat.cnt1 = hw->cnt1;		stat.cnt2 = hw->cnt2;		stat.seq = le16_to_cpu(hw->seq);		stat.unknown = le16_to_cpu(hw->unknown);		bcm43xx_debugfs_log_txstat(ring->bcm, &stat);		bcm43xx_dma_handle_xmitstatus(ring->bcm, &stat);		/* recycle the descriptor buffer. */		sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize);		return;	}	rxhdr = (struct bcm43xx_rxhdr *)skb->data;	len = le16_to_cpu(rxhdr->frame_length);	if (len == 0) {		int i = 0;		do {			udelay(2);			barrier();			len = le16_to_cpu(rxhdr->frame_length);		} while (len == 0 && i++ < 5);		if (unlikely(len == 0)) {			/* recycle the descriptor buffer. */			sync_descbuffer_for_device(ring, meta->dmaaddr,						   ring->rx_buffersize);			goto drop;		}	}	if (unlikely(len > ring->rx_buffersize)) {		/* The data did not fit into one descriptor buffer		 * and is split over multiple buffers.		 * This should never happen, as we try to allocate buffers		 * big enough. So simply ignore this packet.		 */		int cnt = 0;		s32 tmp = len;		while (1) {			desc = ring->vbase + *slot;			meta = ring->meta + *slot;			/* recycle the descriptor buffer. */			sync_descbuffer_for_device(ring, meta->dmaaddr,						   ring->rx_buffersize);			*slot = next_slot(ring, *slot);			cnt++;			tmp -= ring->rx_buffersize;			if (tmp <= 0)				break;		}		printkl(KERN_ERR PFX "DMA RX buffer too small "				     "(len: %u, buffer: %u, nr-dropped: %d)\n",		        len, ring->rx_buffersize, cnt);		goto drop;	}	len -= IEEE80211_FCS_LEN;	dmaaddr = meta->dmaaddr;	err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC);	if (unlikely(err)) {		dprintkl(KERN_ERR PFX "DMA RX: setup_rx_descbuffer() failed\n");		sync_descbuffer_for_device(ring, dmaaddr,					   ring->rx_buffersize);		goto drop;	}	unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0);	skb_put(skb, len + ring->frameoffset);	skb_pull(skb, ring->frameoffset);	err = bcm43xx_rx(ring->bcm, skb, rxhdr);	if (err) {		dev_kfree_skb_irq(skb);		goto drop;	}drop:	return;}void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring){	u32 status;	u16 descptr;	int slot, current_slot;#ifdef CONFIG_BCM43XX_DEBUG	int used_slots = 0;#endif	assert(!ring->tx);	status = bcm43xx_read32(ring->bcm, ring->mmio_base + BCM43xx_DMA_RX_STATUS);	descptr = (status & BCM43xx_DMA_RXSTAT_DPTR_MASK);	current_slot = descptr / sizeof(struct bcm43xx_dmadesc);	assert(current_slot >= 0 && current_slot < ring->nr_slots);	slot = ring->current_slot;	for ( ; slot != current_slot; slot = next_slot(ring, slot)) {		dma_rx(ring, &slot);#ifdef CONFIG_BCM43XX_DEBUG		if (++used_slots > ring->max_used_slots)			ring->max_used_slots = used_slots;#endif	}	bcm43xx_write32(ring->bcm,			ring->mmio_base + BCM43xx_DMA_RX_DESC_INDEX,			(u32)(slot * sizeof(struct bcm43xx_dmadesc)));	ring->current_slot = slot;}/* vim: set ts=8 sw=8 sts=8: */

⌨️ 快捷键说明

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