typhoon.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,314 行 · 第 1/5 页

C
2,314
字号
	 * process of issuing a command, so we don't need to respond.	 */	if(spin_trylock(&tp->command_lock)) {		cmd = (struct cmd_desc *)(ring->ringBase + ring->lastWrite);		typhoon_inc_cmd_index(&ring->lastWrite, 1);		INIT_COMMAND_NO_RESPONSE(cmd, TYPHOON_CMD_HELLO_RESP);		smp_wmb();		writel(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);		spin_unlock(&tp->command_lock);	}}static inttyphoon_process_response(struct typhoon *tp, int resp_size,				struct resp_desc *resp_save){	struct typhoon_indexes *indexes = tp->indexes;	struct resp_desc *resp;	u8 *base = tp->respRing.ringBase;	int count, len, wrap_len;	u32 cleared;	u32 ready;	cleared = le32_to_cpu(indexes->respCleared);	ready = le32_to_cpu(indexes->respReady);	while(cleared != ready) {		resp = (struct resp_desc *)(base + cleared);		count = resp->numDesc + 1;		if(resp_save && resp->seqNo) {			if(count > resp_size) {				resp_save->flags = TYPHOON_RESP_ERROR;				goto cleanup;			}			wrap_len = 0;			len = count * sizeof(*resp);			if(unlikely(cleared + len > RESPONSE_RING_SIZE)) {				wrap_len = cleared + len - RESPONSE_RING_SIZE;				len = RESPONSE_RING_SIZE - cleared;			}			memcpy(resp_save, resp, len);			if(unlikely(wrap_len)) {				resp_save += len / sizeof(*resp);				memcpy(resp_save, base, wrap_len);			}			resp_save = NULL;		} else if(resp->cmd == TYPHOON_CMD_READ_MEDIA_STATUS) {			typhoon_media_status(tp->dev, resp);		} else if(resp->cmd == TYPHOON_CMD_HELLO_RESP) {			typhoon_hello(tp);		} else {			printk(KERN_ERR "%s: dumping unexpected response "			       "0x%04x:%d:0x%02x:0x%04x:%08x:%08x\n",			       tp->name, le16_to_cpu(resp->cmd),			       resp->numDesc, resp->flags,			       le16_to_cpu(resp->parm1),			       le32_to_cpu(resp->parm2),			       le32_to_cpu(resp->parm3));		}cleanup:		typhoon_inc_resp_index(&cleared, count);	}	indexes->respCleared = cpu_to_le32(cleared);	wmb();	return (resp_save == NULL);}static inline inttyphoon_num_free(int lastWrite, int lastRead, int ringSize){	/* this works for all descriptors but rx_desc, as they are a	 * different size than the cmd_desc -- everyone else is the same	 */	lastWrite /= sizeof(struct cmd_desc);	lastRead /= sizeof(struct cmd_desc);	return (ringSize + lastRead - lastWrite - 1) % ringSize;}static inline inttyphoon_num_free_cmd(struct typhoon *tp){	int lastWrite = tp->cmdRing.lastWrite;	int cmdCleared = le32_to_cpu(tp->indexes->cmdCleared);	return typhoon_num_free(lastWrite, cmdCleared, COMMAND_ENTRIES);}static inline inttyphoon_num_free_resp(struct typhoon *tp){	int respReady = le32_to_cpu(tp->indexes->respReady);	int respCleared = le32_to_cpu(tp->indexes->respCleared);	return typhoon_num_free(respReady, respCleared, RESPONSE_ENTRIES);}static inline inttyphoon_num_free_tx(struct transmit_ring *ring){	/* if we start using the Hi Tx ring, this needs updating */	return typhoon_num_free(ring->lastWrite, ring->lastRead, TXLO_ENTRIES);}static inttyphoon_issue_command(struct typhoon *tp, int num_cmd, struct cmd_desc *cmd,		      int num_resp, struct resp_desc *resp){	struct typhoon_indexes *indexes = tp->indexes;	struct basic_ring *ring = &tp->cmdRing;	struct resp_desc local_resp;	int i, err = 0;	int got_resp;	int freeCmd, freeResp;	int len, wrap_len;	spin_lock(&tp->command_lock);	freeCmd = typhoon_num_free_cmd(tp);	freeResp = typhoon_num_free_resp(tp);	if(freeCmd < num_cmd || freeResp < num_resp) {		printk("%s: no descs for cmd, had (needed) %d (%d) cmd, "			"%d (%d) resp\n", tp->name, freeCmd, num_cmd,			freeResp, num_resp);		err = -ENOMEM;		goto out;	}	if(cmd->flags & TYPHOON_CMD_RESPOND) {		/* If we're expecting a response, but the caller hasn't given		 * us a place to put it, we'll provide one.		 */		tp->awaiting_resp = 1;		if(resp == NULL) {			resp = &local_resp;			num_resp = 1;		}	}	wrap_len = 0;	len = num_cmd * sizeof(*cmd);	if(unlikely(ring->lastWrite + len > COMMAND_RING_SIZE)) {		wrap_len = ring->lastWrite + len - COMMAND_RING_SIZE;		len = COMMAND_RING_SIZE - ring->lastWrite;	}	memcpy(ring->ringBase + ring->lastWrite, cmd, len);	if(unlikely(wrap_len)) {		struct cmd_desc *wrap_ptr = cmd;		wrap_ptr += len / sizeof(*cmd);		memcpy(ring->ringBase, wrap_ptr, wrap_len);	}	typhoon_inc_cmd_index(&ring->lastWrite, num_cmd);	/* "I feel a presence... another warrior is on the the mesa."	 */	wmb();	writel(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);	typhoon_post_pci_writes(tp->ioaddr);	if((cmd->flags & TYPHOON_CMD_RESPOND) == 0)		goto out;	/* Ugh. We'll be here about 8ms, spinning our thumbs, unable to	 * preempt or do anything other than take interrupts. So, don't	 * wait for a response unless you have to.	 *	 * I've thought about trying to sleep here, but we're called	 * from many contexts that don't allow that. Also, given the way	 * 3Com has implemented irq coalescing, we would likely timeout --	 * this has been observed in real life!	 *	 * The big killer is we have to wait to get stats from the card,	 * though we could go to a periodic refresh of those if we don't	 * mind them getting somewhat stale. The rest of the waiting	 * commands occur during open/close/suspend/resume, so they aren't	 * time critical. Creating SAs in the future will also have to	 * wait here.	 */	got_resp = 0;	for(i = 0; i < TYPHOON_WAIT_TIMEOUT && !got_resp; i++) {		if(indexes->respCleared != indexes->respReady)			got_resp = typhoon_process_response(tp, num_resp,								resp);		udelay(TYPHOON_UDELAY);	}	if(!got_resp) {		err = -ETIMEDOUT;		goto out;	}	/* Collect the error response even if we don't care about the	 * rest of the response	 */	if(resp->flags & TYPHOON_RESP_ERROR)		err = -EIO;out:	if(tp->awaiting_resp) {		tp->awaiting_resp = 0;		smp_wmb();		/* Ugh. If a response was added to the ring between		 * the call to typhoon_process_response() and the clearing		 * of tp->awaiting_resp, we could have missed the interrupt		 * and it could hang in the ring an indeterminate amount of		 * time. So, check for it, and interrupt ourselves if this		 * is the case.		 */		if(indexes->respCleared != indexes->respReady)			writel(1, tp->ioaddr + TYPHOON_REG_SELF_INTERRUPT);	}	spin_unlock(&tp->command_lock);	return err;}static voidtyphoon_vlan_rx_register(struct net_device *dev, struct vlan_group *grp){	struct typhoon *tp = (struct typhoon *) dev->priv;	struct cmd_desc xp_cmd;	int err;	spin_lock_bh(&tp->state_lock);	if(!tp->vlgrp != !grp) {		/* We've either been turned on for the first time, or we've		 * been turned off. Update the 3XP.		 */		if(grp)			tp->offload |= TYPHOON_OFFLOAD_VLAN;		else			tp->offload &= ~TYPHOON_OFFLOAD_VLAN;		/* If the interface is up, the runtime is running -- and we		 * must be up for the vlan core to call us.		 *		 * Do the command outside of the spin lock, as it is slow.		 */		INIT_COMMAND_WITH_RESPONSE(&xp_cmd,					TYPHOON_CMD_SET_OFFLOAD_TASKS);		xp_cmd.parm2 = tp->offload;		xp_cmd.parm3 = tp->offload;		spin_unlock_bh(&tp->state_lock);		err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);		if(err < 0)			printk("%s: vlan offload error %d\n", tp->name, -err);		spin_lock_bh(&tp->state_lock);	}	/* now make the change visible */	tp->vlgrp = grp;	spin_unlock_bh(&tp->state_lock);}static voidtyphoon_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid){	struct typhoon *tp = (struct typhoon *) dev->priv;	spin_lock_bh(&tp->state_lock);	if(tp->vlgrp)		tp->vlgrp->vlan_devices[vid] = NULL;	spin_unlock_bh(&tp->state_lock);}static inline voidtyphoon_tso_fill(struct sk_buff *skb, struct transmit_ring *txRing,			u32 ring_dma){	struct tcpopt_desc *tcpd;	u32 tcpd_offset = ring_dma;	tcpd = (struct tcpopt_desc *) (txRing->ringBase + txRing->lastWrite);	tcpd_offset += txRing->lastWrite;	tcpd_offset += offsetof(struct tcpopt_desc, bytesTx);	typhoon_inc_tx_index(&txRing->lastWrite, 1);	tcpd->flags = TYPHOON_OPT_DESC | TYPHOON_OPT_TCP_SEG;	tcpd->numDesc = 1;	tcpd->mss_flags = cpu_to_le16(skb_tso_size(skb));	tcpd->mss_flags |= TYPHOON_TSO_FIRST | TYPHOON_TSO_LAST;	tcpd->respAddrLo = cpu_to_le32(tcpd_offset);	tcpd->bytesTx = cpu_to_le32(skb->len);	tcpd->status = 0;}static inttyphoon_start_tx(struct sk_buff *skb, struct net_device *dev){	struct typhoon *tp = (struct typhoon *) dev->priv;	struct transmit_ring *txRing;	struct tx_desc *txd, *first_txd;	dma_addr_t skb_dma;	int numDesc;	/* we have two rings to choose from, but we only use txLo for now	 * If we start using the Hi ring as well, we'll need to update	 * typhoon_stop_runtime(), typhoon_interrupt(), typhoon_num_free_tx(),	 * and TXHI_ENTIRES to match, as well as update the TSO code below	 * to get the right DMA address	 */	txRing = &tp->txLoRing;	/* We need one descriptor for each fragment of the sk_buff, plus the	 * one for the ->data area of it.	 *	 * The docs say a maximum of 16 fragment descriptors per TCP option	 * descriptor, then make a new packet descriptor and option descriptor	 * for the next 16 fragments. The engineers say just an option	 * descriptor is needed. I've tested up to 26 fragments with a single	 * packet descriptor/option descriptor combo, so I use that for now.	 *	 * If problems develop with TSO, check this first.	 */	numDesc = skb_shinfo(skb)->nr_frags + 1;	if(skb_tso_size(skb))		numDesc++;	/* When checking for free space in the ring, we need to also	 * account for the initial Tx descriptor, and we always must leave	 * at least one descriptor unused in the ring so that it doesn't	 * wrap and look empty.	 *	 * The only time we should loop here is when we hit the race	 * between marking the queue awake and updating the cleared index.	 * Just loop and it will appear. This comes from the acenic driver.	 */	while(unlikely(typhoon_num_free_tx(txRing) < (numDesc + 2)))		smp_rmb();	first_txd = (struct tx_desc *) (txRing->ringBase + txRing->lastWrite);	typhoon_inc_tx_index(&txRing->lastWrite, 1);	first_txd->flags = TYPHOON_TX_DESC | TYPHOON_DESC_VALID;	first_txd->numDesc = 0;	first_txd->len = 0;	first_txd->addr = (u64)((unsigned long) skb) & 0xffffffff;	first_txd->addrHi = (u64)((unsigned long) skb) >> 32;	first_txd->processFlags = 0;	if(skb->ip_summed == CHECKSUM_HW) {		/* The 3XP will figure out if this is UDP/TCP */		first_txd->processFlags |= TYPHOON_TX_PF_TCP_CHKSUM;		first_txd->processFlags |= TYPHOON_TX_PF_UDP_CHKSUM;		first_txd->processFlags |= TYPHOON_TX_PF_IP_CHKSUM;	}	if(vlan_tx_tag_present(skb)) {		first_txd->processFlags |=		    TYPHOON_TX_PF_INSERT_VLAN | TYPHOON_TX_PF_VLAN_PRIORITY;		first_txd->processFlags |=		    cpu_to_le32(htons(vlan_tx_tag_get(skb)) <<				TYPHOON_TX_PF_VLAN_TAG_SHIFT);	}	if(skb_tso_size(skb)) {		first_txd->processFlags |= TYPHOON_TX_PF_TCP_SEGMENT;		first_txd->numDesc++;		typhoon_tso_fill(skb, txRing, tp->txlo_dma_addr);	}	txd = (struct tx_desc *) (txRing->ringBase + txRing->lastWrite);	typhoon_inc_tx_index(&txRing->lastWrite, 1);	/* No need to worry about padding packet -- the firmware pads	 * it with zeros to ETH_ZLEN for us.	 */	if(skb_shinfo(skb)->nr_frags == 0) {		skb_dma = pci_map_single(tp->tx_pdev, skb->data, skb->len,				       PCI_DMA_TODEVICE);		txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;		txd->len = cpu_to_le16(skb->len);		txd->addr = cpu_to_le32(skb_dma);		txd->addrHi = 0;		first_txd->numDesc++;	} else {		int i, len;		len = skb_headlen(skb);		skb_dma = pci_map_single(tp->tx_pdev, skb->data, len,				         PCI_DMA_TODEVICE);		txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;		txd->len = cpu_to_le16(len);		txd->addr = cpu_to_le32(skb_dma);		txd->addrHi = 0;		first_txd->numDesc++;		for(i = 0; i < skb_shinfo(skb)->nr_frags; i++) {			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];			void *frag_addr;			txd = (struct tx_desc *) (txRing->ringBase +						txRing->lastWrite);			typhoon_inc_tx_index(&txRing->lastWrite, 1);			len = frag->size;			frag_addr = (void *) page_address(frag->page) +						frag->page_offset;			skb_dma = pci_map_single(tp->tx_pdev, frag_addr, len,					 PCI_DMA_TODEVICE);			txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;			txd->len = cpu_to_le16(len);			txd->addr = cpu_to_le32(skb_dma);			txd->addrHi = 0;			first_txd->numDesc++;		}	}	/* Kick the 3XP	 */	wmb();	writel(txRing->lastWrite, tp->tx_ioaddr + txRing->writeRegister);	dev->trans_start = jiffies;	/* If we don't have room to put the worst case packet on the	 * queue, then we must stop the queue. We need 2 extra	 * descriptors -- one to prevent ring wrap, and one for the	 * Tx header.	 */	numDesc = MAX_SKB_FRAGS + TSO_NUM_DESCRIPTORS + 1;	if(typhoon_num_free_tx(txRing) < (numDesc + 2)) {		netif_stop_queue(dev);		/* A Tx complete IRQ could have gotten inbetween, making		 * the ring free again. Only need to recheck here, since		 * Tx is serialized.		 */		if(typhoon_num_free_tx(txRing) >= (numDesc + 2))			netif_wake_queue(dev);	}	return 0;}static voidtyphoon_set_rx_mode(struct net_device *dev){	struct typhoon *tp = (struct typhoon *) dev->priv;	struct cmd_desc xp_cmd;	u32 mc_filter[2];	u16 filter;	filter = TYPHOON_RX_FILTER_DIRECTED | TYPHOON_RX_FILTER_BROADCAST;	if(dev->flags & IFF_PROMISC) {		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n",		       dev->name);		filter |= TYPHOON_RX_FILTER_PROMISCOUS;	} else if((dev->mc_count > multicast_filter_limit) ||		  (dev->flags & IFF_ALLMULTI)) {		/* Too many to match, or accept all multicasts. */		filter |= TYPHOON_RX_FILTER_ALL_MCAST;	} else if(dev->mc_count) {		struct dev_mc_list *mclist;

⌨️ 快捷键说明

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