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 + -
显示快捷键?