ps3_gelic_net.c

来自「linux 内核源代码」· C语言 代码 · 共 1,580 行 · 第 1/3 页

C
1,580
字号
 * * gelic_net_disable_txdmac terminates processing on the DMA controller by * turing off DMA and issueing a force end */static inline void gelic_net_disable_txdmac(struct gelic_net_card *card){	int status;	/* this hvc blocks until the DMA in progress really stopped */	status = lv1_net_stop_tx_dma(bus_id(card), dev_id(card), 0);	if (status)		dev_err(ctodev(card),			"lv1_net_stop_tx_dma faild, status=%d\n", status);}/** * gelic_net_stop - called upon ifconfig down * @netdev: interface device structure * * always returns 0 */static int gelic_net_stop(struct net_device *netdev){	struct gelic_net_card *card = netdev_priv(netdev);	napi_disable(&card->napi);	netif_stop_queue(netdev);	/* turn off DMA, force end */	gelic_net_disable_rxdmac(card);	gelic_net_disable_txdmac(card);	gelic_net_set_irq_mask(card, 0);	/* disconnect event port */	free_irq(card->netdev->irq, card->netdev);	ps3_sb_event_receive_port_destroy(card->dev, card->netdev->irq);	card->netdev->irq = NO_IRQ;	netif_carrier_off(netdev);	/* release chains */	gelic_net_release_tx_chain(card, 1);	gelic_net_release_rx_chain(card);	gelic_net_free_chain(card, card->tx_top);	gelic_net_free_chain(card, card->rx_top);	return 0;}/** * gelic_net_get_next_tx_descr - returns the next available tx descriptor * @card: device structure to get descriptor from * * returns the address of the next descriptor, or NULL if not available. */static struct gelic_net_descr *gelic_net_get_next_tx_descr(struct gelic_net_card *card){	if (!card->tx_chain.head)		return NULL;	/*  see if the next descriptor is free */	if (card->tx_chain.tail != card->tx_chain.head->next &&	    gelic_net_get_descr_status(card->tx_chain.head) ==	    GELIC_NET_DESCR_NOT_IN_USE)		return card->tx_chain.head;	else		return NULL;}/** * gelic_net_set_txdescr_cmdstat - sets the tx descriptor command field * @descr: descriptor structure to fill out * @skb: packet to consider * * fills out the command and status field of the descriptor structure, * depending on hardware checksum settings. This function assumes a wmb() * has executed before. */static void gelic_net_set_txdescr_cmdstat(struct gelic_net_descr *descr,					  struct sk_buff *skb){	if (skb->ip_summed != CHECKSUM_PARTIAL)		descr->dmac_cmd_status = GELIC_NET_DMAC_CMDSTAT_NOCS |			GELIC_NET_DMAC_CMDSTAT_END_FRAME;	else {		/* is packet ip?		 * if yes: tcp? udp? */		if (skb->protocol == htons(ETH_P_IP)) {			if (ip_hdr(skb)->protocol == IPPROTO_TCP)				descr->dmac_cmd_status =					GELIC_NET_DMAC_CMDSTAT_TCPCS |					GELIC_NET_DMAC_CMDSTAT_END_FRAME;			else if (ip_hdr(skb)->protocol == IPPROTO_UDP)				descr->dmac_cmd_status =					GELIC_NET_DMAC_CMDSTAT_UDPCS |					GELIC_NET_DMAC_CMDSTAT_END_FRAME;			else	/*				 * the stack should checksum non-tcp and non-udp				 * packets on his own: NETIF_F_IP_CSUM				 */				descr->dmac_cmd_status =					GELIC_NET_DMAC_CMDSTAT_NOCS |					GELIC_NET_DMAC_CMDSTAT_END_FRAME;		}	}}static inline struct sk_buff *gelic_put_vlan_tag(struct sk_buff *skb,						 unsigned short tag){	struct vlan_ethhdr *veth;	static unsigned int c;	if (skb_headroom(skb) < VLAN_HLEN) {		struct sk_buff *sk_tmp = skb;		pr_debug("%s: hd=%d c=%ud\n", __func__, skb_headroom(skb), c);		skb = skb_realloc_headroom(sk_tmp, VLAN_HLEN);		if (!skb)			return NULL;		dev_kfree_skb_any(sk_tmp);	}	veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN);	/* Move the mac addresses to the top of buffer */	memmove(skb->data, skb->data + VLAN_HLEN, 2 * ETH_ALEN);	veth->h_vlan_proto = __constant_htons(ETH_P_8021Q);	veth->h_vlan_TCI = htons(tag);	return skb;}/** * gelic_net_prepare_tx_descr_v - get dma address of skb_data * @card: card structure * @descr: descriptor structure * @skb: packet to use * * returns 0 on success, <0 on failure. * */static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,					struct gelic_net_descr *descr,					struct sk_buff *skb){	dma_addr_t buf;	if (card->vlan_index != -1) {		struct sk_buff *skb_tmp;		skb_tmp = gelic_put_vlan_tag(skb,					     card->vlan_id[card->vlan_index]);		if (!skb_tmp)			return -ENOMEM;		skb = skb_tmp;	}	buf = dma_map_single(ctodev(card), skb->data, skb->len, DMA_TO_DEVICE);	if (!buf) {		dev_err(ctodev(card),			"dma map 2 failed (%p, %i). Dropping packet\n",			skb->data, skb->len);		return -ENOMEM;	}	descr->buf_addr = buf;	descr->buf_size = skb->len;	descr->skb = skb;	descr->data_status = 0;	descr->next_descr_addr = 0; /* terminate hw descr */	gelic_net_set_txdescr_cmdstat(descr, skb);	/* bump free descriptor pointer */	card->tx_chain.head = descr->next;	return 0;}/** * gelic_net_kick_txdma - enables TX DMA processing * @card: card structure * @descr: descriptor address to enable TX processing at * */static int gelic_net_kick_txdma(struct gelic_net_card *card,				struct gelic_net_descr *descr){	int status = 0;	if (card->tx_dma_progress)		return 0;	if (gelic_net_get_descr_status(descr) == GELIC_NET_DESCR_CARDOWNED) {		card->tx_dma_progress = 1;		status = lv1_net_start_tx_dma(bus_id(card), dev_id(card),					      descr->bus_addr, 0);		if (status)			dev_info(ctodev(card), "lv1_net_start_txdma failed," \				 "status=%d\n", status);	}	return status;}/** * gelic_net_xmit - transmits a frame over the device * @skb: packet to send out * @netdev: interface device structure * * returns 0 on success, <0 on failure */static int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev){	struct gelic_net_card *card = netdev_priv(netdev);	struct gelic_net_descr *descr;	int result;	unsigned long flags;	spin_lock_irqsave(&card->tx_dma_lock, flags);	gelic_net_release_tx_chain(card, 0);	descr = gelic_net_get_next_tx_descr(card);	if (!descr) {		/*		 * no more descriptors free		 */		netif_stop_queue(netdev);		spin_unlock_irqrestore(&card->tx_dma_lock, flags);		return NETDEV_TX_BUSY;	}	result = gelic_net_prepare_tx_descr_v(card, descr, skb);	if (result) {		/*		 * DMA map failed.  As chanses are that failure		 * would continue, just release skb and return		 */		card->netdev->stats.tx_dropped++;		dev_kfree_skb_any(skb);		spin_unlock_irqrestore(&card->tx_dma_lock, flags);		return NETDEV_TX_OK;	}	/*	 * link this prepared descriptor to previous one	 * to achieve high performance	 */	descr->prev->next_descr_addr = descr->bus_addr;	/*	 * as hardware descriptor is modified in the above lines,	 * ensure that the hardware sees it	 */	wmb();	if (gelic_net_kick_txdma(card, descr)) {		/*		 * kick failed.		 * release descriptors which were just prepared		 */		card->netdev->stats.tx_dropped++;		gelic_net_release_tx_descr(card, descr);		gelic_net_release_tx_descr(card, descr->next);		card->tx_chain.tail = descr->next->next;		dev_info(ctodev(card), "%s: kick failure\n", __func__);	} else {		/* OK, DMA started/reserved */		netdev->trans_start = jiffies;	}	spin_unlock_irqrestore(&card->tx_dma_lock, flags);	return NETDEV_TX_OK;}/** * gelic_net_pass_skb_up - takes an skb from a descriptor and passes it on * @descr: descriptor to process * @card: card structure * * iommu-unmaps the skb, fills out skb structure and passes the data to the * stack. The descriptor state is not changed. */static void gelic_net_pass_skb_up(struct gelic_net_descr *descr,				 struct gelic_net_card *card){	struct sk_buff *skb;	struct net_device *netdev;	u32 data_status, data_error;	data_status = descr->data_status;	data_error = descr->data_error;	netdev = card->netdev;	/* unmap skb buffer */	skb = descr->skb;	dma_unmap_single(ctodev(card), descr->buf_addr, GELIC_NET_MAX_MTU,			 DMA_FROM_DEVICE);	skb_put(skb, descr->valid_size? descr->valid_size : descr->result_size);	if (!descr->valid_size)		dev_info(ctodev(card), "buffer full %x %x %x\n",			 descr->result_size, descr->buf_size,			 descr->dmac_cmd_status);	descr->skb = NULL;	/*	 * the card put 2 bytes vlan tag in front	 * of the ethernet frame	 */	skb_pull(skb, 2);	skb->protocol = eth_type_trans(skb, netdev);	/* checksum offload */	if (card->rx_csum) {		if ((data_status & GELIC_NET_DATA_STATUS_CHK_MASK) &&		    (!(data_error & GELIC_NET_DATA_ERROR_CHK_MASK)))			skb->ip_summed = CHECKSUM_UNNECESSARY;		else			skb->ip_summed = CHECKSUM_NONE;	} else		skb->ip_summed = CHECKSUM_NONE;	/* update netdevice statistics */	card->netdev->stats.rx_packets++;	card->netdev->stats.rx_bytes += skb->len;	/* pass skb up to stack */	netif_receive_skb(skb);}/** * gelic_net_decode_one_descr - processes an rx descriptor * @card: card structure * * returns 1 if a packet has been sent to the stack, otherwise 0 * * processes an rx descriptor by iommu-unmapping the data buffer and passing * the packet up to the stack */static int gelic_net_decode_one_descr(struct gelic_net_card *card){	enum gelic_net_descr_status status;	struct gelic_net_descr_chain *chain = &card->rx_chain;	struct gelic_net_descr *descr = chain->tail;	int dmac_chain_ended;	status = gelic_net_get_descr_status(descr);	/* is this descriptor terminated with next_descr == NULL? */	dmac_chain_ended =		descr->dmac_cmd_status & GELIC_NET_DMAC_CMDSTAT_RXDCEIS;	if (status == GELIC_NET_DESCR_CARDOWNED)		return 0;	if (status == GELIC_NET_DESCR_NOT_IN_USE) {		dev_dbg(ctodev(card), "dormant descr? %p\n", descr);		return 0;	}	if ((status == GELIC_NET_DESCR_RESPONSE_ERROR) ||	    (status == GELIC_NET_DESCR_PROTECTION_ERROR) ||	    (status == GELIC_NET_DESCR_FORCE_END)) {		dev_info(ctodev(card), "dropping RX descriptor with state %x\n",			 status);		card->netdev->stats.rx_dropped++;		goto refill;	}	if (status == GELIC_NET_DESCR_BUFFER_FULL) {		/*		 * Buffer full would occur if and only if		 * the frame length was longer than the size of this		 * descriptor's buffer.  If the frame length was equal		 * to or shorter than buffer'size, FRAME_END condition		 * would occur.		 * Anyway this frame was longer than the MTU,		 * just drop it.		 */		dev_info(ctodev(card), "overlength frame\n");		goto refill;	}	/*	 * descriptoers any other than FRAME_END here should	 * be treated as error.	 */	if (status != GELIC_NET_DESCR_FRAME_END) {		dev_dbg(ctodev(card), "RX descriptor with state %x\n",			status);		goto refill;	}	/* ok, we've got a packet in descr */	gelic_net_pass_skb_up(descr, card);refill:	/*	 * So that always DMAC can see the end	 * of the descriptor chain to avoid	 * from unwanted DMAC overrun.	 */	descr->next_descr_addr = 0;	/* change the descriptor state: */	gelic_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE);	/*	 * this call can fail, but for now, just leave this	 * decriptor without skb	 */	gelic_net_prepare_rx_descr(card, descr);	chain->head = descr;	chain->tail = descr->next;	/*	 * Set this descriptor the end of the chain.	 */	descr->prev->next_descr_addr = descr->bus_addr;	/*	 * If dmac chain was met, DMAC stopped.	 * thus re-enable it	 */	if (dmac_chain_ended) {		card->rx_dma_restart_required = 1;		dev_dbg(ctodev(card), "reenable rx dma scheduled\n");	}	return 1;}/** * gelic_net_poll - NAPI poll function called by the stack to return packets * @netdev: interface device structure * @budget: number of packets we can pass to the stack at most * * returns 0 if no more packets available to the driver/stack. Returns 1, * if the quota is exceeded, but the driver has still packets. * */static int gelic_net_poll(struct napi_struct *napi, int budget){	struct gelic_net_card *card = container_of(napi, struct gelic_net_card, napi);	struct net_device *netdev = card->netdev;	int packets_done = 0;	while (packets_done < budget) {		if (!gelic_net_decode_one_descr(card))			break;		packets_done++;	}	if (packets_done < budget) {		netif_rx_complete(netdev, napi);		gelic_net_rx_irq_on(card);	}	return packets_done;}/** * gelic_net_change_mtu - changes the MTU of an interface * @netdev: interface device structure * @new_mtu: new MTU value * * returns 0 on success, <0 on failure */static int gelic_net_change_mtu(struct net_device *netdev, int new_mtu){	/* no need to re-alloc skbs or so -- the max mtu is about 2.3k	 * and mtu is outbound only anyway */	if ((new_mtu < GELIC_NET_MIN_MTU) ||	    (new_mtu > GELIC_NET_MAX_MTU)) {		return -EINVAL;	}	netdev->mtu = new_mtu;	return 0;}/** * gelic_net_interrupt - event handler for gelic_net */static irqreturn_t gelic_net_interrupt(int irq, void *ptr){	unsigned long flags;	struct net_device *netdev = ptr;	struct gelic_net_card *card = netdev_priv(netdev);	u64 status;	status = card->irq_status;	if (!status)		return IRQ_NONE;	if (card->rx_dma_restart_required) {		card->rx_dma_restart_required = 0;		gelic_net_enable_rxdmac(card);	}	if (status & GELIC_NET_RXINT) {		gelic_net_rx_irq_off(card);		netif_rx_schedule(netdev, &card->napi);	}	if (status & GELIC_NET_TXINT) {		spin_lock_irqsave(&card->tx_dma_lock, flags);		card->tx_dma_progress = 0;		gelic_net_release_tx_chain(card, 0);		/* kick outstanding tx descriptor if any */		gelic_net_kick_txdma(card, card->tx_chain.tail);		spin_unlock_irqrestore(&card->tx_dma_lock, flags);	}	return IRQ_HANDLED;}#ifdef CONFIG_NET_POLL_CONTROLLER/** * gelic_net_poll_controller - artificial interrupt for netconsole etc. * @netdev: interface device structure * * see Documentation/networking/netconsole.txt */static void gelic_net_poll_controller(struct net_device *netdev){	struct gelic_net_card *card = netdev_priv(netdev);	gelic_net_set_irq_mask(card, 0);	gelic_net_interrupt(netdev->irq, netdev);	gelic_net_set_irq_mask(card, card->ghiintmask);}

⌨️ 快捷键说明

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