ps3_gelic_net.c

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

C
1,580
字号
/* *  PS3 gelic network driver. * * Copyright (C) 2007 Sony Computer Entertainment Inc. * Copyright 2006, 2007 Sony Corporation * * This file is based on: spider_net.c * * (C) Copyright IBM Corp. 2005 * * Authors : Utz Bacher <utz.bacher@de.ibm.com> *           Jens Osterkamp <Jens.Osterkamp@de.ibm.com> * * 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, 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; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#undef DEBUG#include <linux/kernel.h>#include <linux/module.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/if_vlan.h>#include <linux/in.h>#include <linux/ip.h>#include <linux/tcp.h>#include <linux/dma-mapping.h>#include <net/checksum.h>#include <asm/firmware.h>#include <asm/ps3.h>#include <asm/lv1call.h>#include "ps3_gelic_net.h"#define DRV_NAME "Gelic Network Driver"#define DRV_VERSION "1.0"MODULE_AUTHOR("SCE Inc.");MODULE_DESCRIPTION("Gelic Network driver");MODULE_LICENSE("GPL");static inline struct device *ctodev(struct gelic_net_card *card){	return &card->dev->core;}static inline unsigned int bus_id(struct gelic_net_card *card){	return card->dev->bus_id;}static inline unsigned int dev_id(struct gelic_net_card *card){	return card->dev->dev_id;}/* set irq_mask */static int gelic_net_set_irq_mask(struct gelic_net_card *card, u64 mask){	int status;	status = lv1_net_set_interrupt_mask(bus_id(card), dev_id(card),					    mask, 0);	if (status)		dev_info(ctodev(card),			 "lv1_net_set_interrupt_mask failed %d\n", status);	return status;}static inline void gelic_net_rx_irq_on(struct gelic_net_card *card){	gelic_net_set_irq_mask(card, card->ghiintmask | GELIC_NET_RXINT);}static inline void gelic_net_rx_irq_off(struct gelic_net_card *card){	gelic_net_set_irq_mask(card, card->ghiintmask & ~GELIC_NET_RXINT);}/** * gelic_net_get_descr_status -- returns the status of a descriptor * @descr: descriptor to look at * * returns the status as in the dmac_cmd_status field of the descriptor */static enum gelic_net_descr_statusgelic_net_get_descr_status(struct gelic_net_descr *descr){	u32 cmd_status;	cmd_status = descr->dmac_cmd_status;	cmd_status >>= GELIC_NET_DESCR_IND_PROC_SHIFT;	return cmd_status;}/** * gelic_net_set_descr_status -- sets the status of a descriptor * @descr: descriptor to change * @status: status to set in the descriptor * * changes the status to the specified value. Doesn't change other bits * in the status */static void gelic_net_set_descr_status(struct gelic_net_descr *descr,				       enum gelic_net_descr_status status){	u32 cmd_status;	/* read the status */	cmd_status = descr->dmac_cmd_status;	/* clean the upper 4 bits */	cmd_status &= GELIC_NET_DESCR_IND_PROC_MASKO;	/* add the status to it */	cmd_status |= ((u32)status) << GELIC_NET_DESCR_IND_PROC_SHIFT;	/* and write it back */	descr->dmac_cmd_status = cmd_status;	/*	 * dma_cmd_status field is used to indicate whether the descriptor	 * is valid or not.	 * Usually caller of this function wants to inform that to the	 * hardware, so we assure here the hardware sees the change.	 */	wmb();}/** * gelic_net_free_chain - free descriptor chain * @card: card structure * @descr_in: address of desc */static void gelic_net_free_chain(struct gelic_net_card *card,				 struct gelic_net_descr *descr_in){	struct gelic_net_descr *descr;	for (descr = descr_in; descr && descr->bus_addr; descr = descr->next) {		dma_unmap_single(ctodev(card), descr->bus_addr,				 GELIC_NET_DESCR_SIZE, DMA_BIDIRECTIONAL);		descr->bus_addr = 0;	}}/** * gelic_net_init_chain - links descriptor chain * @card: card structure * @chain: address of chain * @start_descr: address of descriptor array * @no: number of descriptors * * we manage a circular list that mirrors the hardware structure, * except that the hardware uses bus addresses. * * returns 0 on success, <0 on failure */static int gelic_net_init_chain(struct gelic_net_card *card,				struct gelic_net_descr_chain *chain,				struct gelic_net_descr *start_descr, int no){	int i;	struct gelic_net_descr *descr;	descr = start_descr;	memset(descr, 0, sizeof(*descr) * no);	/* set up the hardware pointers in each descriptor */	for (i = 0; i < no; i++, descr++) {		gelic_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE);		descr->bus_addr =			dma_map_single(ctodev(card), descr,				       GELIC_NET_DESCR_SIZE,				       DMA_BIDIRECTIONAL);		if (!descr->bus_addr)			goto iommu_error;		descr->next = descr + 1;		descr->prev = descr - 1;	}	/* make them as ring */	(descr - 1)->next = start_descr;	start_descr->prev = (descr - 1);	/* chain bus addr of hw descriptor */	descr = start_descr;	for (i = 0; i < no; i++, descr++) {		descr->next_descr_addr = descr->next->bus_addr;	}	chain->head = start_descr;	chain->tail = start_descr;	/* do not chain last hw descriptor */	(descr - 1)->next_descr_addr = 0;	return 0;iommu_error:	for (i--, descr--; 0 <= i; i--, descr--)		if (descr->bus_addr)			dma_unmap_single(ctodev(card), descr->bus_addr,					 GELIC_NET_DESCR_SIZE,					 DMA_BIDIRECTIONAL);	return -ENOMEM;}/** * gelic_net_prepare_rx_descr - reinitializes a rx descriptor * @card: card structure * @descr: descriptor to re-init * * return 0 on succes, <0 on failure * * allocates a new rx skb, iommu-maps it and attaches it to the descriptor. * Activate the descriptor state-wise */static int gelic_net_prepare_rx_descr(struct gelic_net_card *card,				      struct gelic_net_descr *descr){	int offset;	unsigned int bufsize;	if (gelic_net_get_descr_status(descr) !=  GELIC_NET_DESCR_NOT_IN_USE) {		dev_info(ctodev(card), "%s: ERROR status \n", __func__);	}	/* we need to round up the buffer size to a multiple of 128 */	bufsize = ALIGN(GELIC_NET_MAX_MTU, GELIC_NET_RXBUF_ALIGN);	/* and we need to have it 128 byte aligned, therefore we allocate a	 * bit more */	descr->skb = netdev_alloc_skb(card->netdev,		bufsize + GELIC_NET_RXBUF_ALIGN - 1);	if (!descr->skb) {		descr->buf_addr = 0; /* tell DMAC don't touch memory */		dev_info(ctodev(card),			 "%s:allocate skb failed !!\n", __func__);		return -ENOMEM;	}	descr->buf_size = bufsize;	descr->dmac_cmd_status = 0;	descr->result_size = 0;	descr->valid_size = 0;	descr->data_error = 0;	offset = ((unsigned long)descr->skb->data) &		(GELIC_NET_RXBUF_ALIGN - 1);	if (offset)		skb_reserve(descr->skb, GELIC_NET_RXBUF_ALIGN - offset);	/* io-mmu-map the skb */	descr->buf_addr = dma_map_single(ctodev(card), descr->skb->data,					 GELIC_NET_MAX_MTU,					 DMA_FROM_DEVICE);	if (!descr->buf_addr) {		dev_kfree_skb_any(descr->skb);		descr->skb = NULL;		dev_info(ctodev(card),			 "%s:Could not iommu-map rx buffer\n", __func__);		gelic_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE);		return -ENOMEM;	} else {		gelic_net_set_descr_status(descr, GELIC_NET_DESCR_CARDOWNED);		return 0;	}}/** * gelic_net_release_rx_chain - free all skb of rx descr * @card: card structure * */static void gelic_net_release_rx_chain(struct gelic_net_card *card){	struct gelic_net_descr *descr = card->rx_chain.head;	do {		if (descr->skb) {			dma_unmap_single(ctodev(card),					 descr->buf_addr,					 descr->skb->len,					 DMA_FROM_DEVICE);			descr->buf_addr = 0;			dev_kfree_skb_any(descr->skb);			descr->skb = NULL;			gelic_net_set_descr_status(descr,						   GELIC_NET_DESCR_NOT_IN_USE);		}		descr = descr->next;	} while (descr != card->rx_chain.head);}/** * gelic_net_fill_rx_chain - fills descriptors/skbs in the rx chains * @card: card structure * * fills all descriptors in the rx chain: allocates skbs * and iommu-maps them. * returns 0 on success, <0 on failure */static int gelic_net_fill_rx_chain(struct gelic_net_card *card){	struct gelic_net_descr *descr = card->rx_chain.head;	int ret;	do {		if (!descr->skb) {			ret = gelic_net_prepare_rx_descr(card, descr);			if (ret)				goto rewind;		}		descr = descr->next;	} while (descr != card->rx_chain.head);	return 0;rewind:	gelic_net_release_rx_chain(card);	return ret;}/** * gelic_net_alloc_rx_skbs - allocates rx skbs in rx descriptor chains * @card: card structure * * returns 0 on success, <0 on failure */static int gelic_net_alloc_rx_skbs(struct gelic_net_card *card){	struct gelic_net_descr_chain *chain;	int ret;	chain = &card->rx_chain;	ret = gelic_net_fill_rx_chain(card);	chain->head = card->rx_top->prev; /* point to the last */	return ret;}/** * gelic_net_release_tx_descr - processes a used tx descriptor * @card: card structure * @descr: descriptor to release * * releases a used tx descriptor (unmapping, freeing of skb) */static void gelic_net_release_tx_descr(struct gelic_net_card *card,			    struct gelic_net_descr *descr){	struct sk_buff *skb = descr->skb;	BUG_ON(!(descr->data_status & (1 << GELIC_NET_TXDESC_TAIL)));	dma_unmap_single(ctodev(card), descr->buf_addr, skb->len,			 DMA_TO_DEVICE);	dev_kfree_skb_any(skb);	descr->buf_addr = 0;	descr->buf_size = 0;	descr->next_descr_addr = 0;	descr->result_size = 0;	descr->valid_size = 0;	descr->data_status = 0;	descr->data_error = 0;	descr->skb = NULL;	/* set descr status */	gelic_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE);}/** * gelic_net_release_tx_chain - processes sent tx descriptors * @card: adapter structure * @stop: net_stop sequence * * releases the tx descriptors that gelic has finished with */static void gelic_net_release_tx_chain(struct gelic_net_card *card, int stop){	struct gelic_net_descr_chain *tx_chain;	enum gelic_net_descr_status status;	int release = 0;	for (tx_chain = &card->tx_chain;	     tx_chain->head != tx_chain->tail && tx_chain->tail;	     tx_chain->tail = tx_chain->tail->next) {		status = gelic_net_get_descr_status(tx_chain->tail);		switch (status) {		case GELIC_NET_DESCR_RESPONSE_ERROR:		case GELIC_NET_DESCR_PROTECTION_ERROR:		case GELIC_NET_DESCR_FORCE_END:			if (printk_ratelimit())				dev_info(ctodev(card),					 "%s: forcing end of tx descriptor " \					 "with status %x\n",					 __func__, status);			card->netdev->stats.tx_dropped++;			break;		case GELIC_NET_DESCR_COMPLETE:			if (tx_chain->tail->skb) {				card->netdev->stats.tx_packets++;				card->netdev->stats.tx_bytes +=					tx_chain->tail->skb->len;			}			break;		case GELIC_NET_DESCR_CARDOWNED:			/* pending tx request */		default:			/* any other value (== GELIC_NET_DESCR_NOT_IN_USE) */			if (!stop)				goto out;		}		gelic_net_release_tx_descr(card, tx_chain->tail);		release ++;	}out:	if (!stop && release)		netif_wake_queue(card->netdev);}/** * gelic_net_set_multi - sets multicast addresses and promisc flags * @netdev: interface device structure * * gelic_net_set_multi configures multicast addresses as needed for the * netdev interface. It also sets up multicast, allmulti and promisc * flags appropriately */static void gelic_net_set_multi(struct net_device *netdev){	struct gelic_net_card *card = netdev_priv(netdev);	struct dev_mc_list *mc;	unsigned int i;	uint8_t *p;	u64 addr;	int status;	/* clear all multicast address */	status = lv1_net_remove_multicast_address(bus_id(card), dev_id(card),						  0, 1);	if (status)		dev_err(ctodev(card),			"lv1_net_remove_multicast_address failed %d\n",			status);	/* set broadcast address */	status = lv1_net_add_multicast_address(bus_id(card), dev_id(card),					       GELIC_NET_BROADCAST_ADDR, 0);	if (status)		dev_err(ctodev(card),			"lv1_net_add_multicast_address failed, %d\n",			status);	if (netdev->flags & IFF_ALLMULTI		|| netdev->mc_count > GELIC_NET_MC_COUNT_MAX) { /* list max */		status = lv1_net_add_multicast_address(bus_id(card),						       dev_id(card),						       0, 1);		if (status)			dev_err(ctodev(card),				"lv1_net_add_multicast_address failed, %d\n",				status);		return;	}	/* set multicast address */	for (mc = netdev->mc_list; mc; mc = mc->next) {		addr = 0;		p = mc->dmi_addr;		for (i = 0; i < ETH_ALEN; i++) {			addr <<= 8;			addr |= *p++;		}		status = lv1_net_add_multicast_address(bus_id(card),						       dev_id(card),						       addr, 0);		if (status)			dev_err(ctodev(card),				"lv1_net_add_multicast_address failed, %d\n",				status);	}}/** * gelic_net_enable_rxdmac - enables the receive DMA controller * @card: card structure * * gelic_net_enable_rxdmac enables the DMA controller by setting RX_DMA_EN * in the GDADMACCNTR register */static inline void gelic_net_enable_rxdmac(struct gelic_net_card *card){	int status;	status = lv1_net_start_rx_dma(bus_id(card), dev_id(card),				card->rx_chain.tail->bus_addr, 0);	if (status)		dev_info(ctodev(card),			 "lv1_net_start_rx_dma failed, status=%d\n", status);}/** * gelic_net_disable_rxdmac - disables the receive DMA controller * @card: card structure * * gelic_net_disable_rxdmac terminates processing on the DMA controller by * turing off DMA and issueing a force end */static inline void gelic_net_disable_rxdmac(struct gelic_net_card *card){	int status;	/* this hvc blocks until the DMA in progress really stopped */	status = lv1_net_stop_rx_dma(bus_id(card), dev_id(card), 0);	if (status)		dev_err(ctodev(card),			"lv1_net_stop_rx_dma faild, %d\n", status);}/** * gelic_net_disable_txdmac - disables the transmit DMA controller * @card: card structure

⌨️ 快捷键说明

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