大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
说是网络,其实是网卡驱动。而且是针对于FREESCALE芯片的FEC端的驱动,我不知道别的芯片厂商的FEC模块是怎么样的,
但就我接触过的几款FREESCALE的芯片来看,比如基于POWERPC的860T和ARM系列的MX27等,他们的FEC有一个明显的特点就是
都是由BD和一个DMA缓冲组成,而这个DMA是专用的,也就是只是给FEC使用,区别于芯片内的DMAC模块。我们先来从fec.c这
个与硬件直接相关的看起:
首先找到module_init(fec_enet_module_init);这里fec_enet_module_init为入口点
fec_enet_module_init()
首先调用fec_arch_init,它调用gpio_fec_active设置GPIO为FEC模式,然后如果有电源管理的话,就调用
mxc_fec_power_on开启电源。接着调用clk_get,clk_enable, clk_put设置FEC的CLOCK,这里退出fec_arch_init函数,接
着循环FEC_MAX_PORTS次,也就是有几个FEC就循环几次,在这里我们只有一个FEC,所以这个循环不用管。接下来因为我们
用的是以太网,所以调用dev = alloc_etherdev(sizeof(struct fec_enet_private));申请一个以太网设备描述,其中
struct fec_enet_private是用来描述FEC专有的数据结构。如下:
/* The FEC buffer descriptors track the ring buffers. The rx_bd_base and
* tx_bd_base always point to the base of the buffer descriptors. The
* cur_rx and cur_tx point to the currently available buffer.
* The dirty_tx tracks the current buffer that is being sent by the
* controller. The cur_tx and dirty_tx are equal under both completely
* empty and completely full conditions. The empty/ready indicator in
* the buffer descriptor determines the actual condition.
*/
struct fec_enet_private {
/* Hardware registers of the FEC device */
volatile fec_t *hwp;
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
unsigned char *tx_bounce[TX_RING_SIZE];
struct sk_buff* tx_skbuff[TX_RING_SIZE];
struct sk_buff* rx_skbuff[RX_RING_SIZE];
ushort skb_cur;
ushort skb_dirty;
/* CPM dual port RAM relative addresses.
*/
void * cbd_mem_base; /* save the virtual base address of rx&tx buffer descripter */
cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */
cbd_t *tx_bd_base;
cbd_t *cur_rx, *cur_tx; /* The next free ring entry */
cbd_t *dirty_tx; /* The ring entries to be free()ed. */
struct net_device_stats stats;
uint tx_full;
spinlock_t lock;
uint phy_id;
uint phy_id_done;
uint phy_status;
uint phy_speed;
phy_info_t const *phy;
struct work_struct phy_task;
uint sequence_done;
uint mii_phy_task_queued;
uint phy_addr;
int index;
int opened;
int link;
int old_link;
int full_duplex;
struct clk *clk;
};
调用完这个函数后我们就有eth0这人设备了,其中一些结构的初始化是在这个函数里的一个回调ether_setup里设
置的,在这里我也把它帖出来吧。
/**
* ether_setup – setup Ethernet network device
* @dev: network device
* Fill in the fields of the device structure with Ethernet-generic values.
*/
void ether_setup(struct net_device *dev)
{
dev->change_mtu = eth_change_mtu;
dev->hard_header = eth_header;
dev->rebuild_header = eth_rebuild_header;
dev->set_mac_address = eth_mac_addr;
dev->hard_header_cache = eth_header_cache;
dev->header_cache_update= eth_header_cache_update;
dev->hard_header_parse = eth_header_parse;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
dev->addr_len = ETH_ALEN;
dev->tx_queue_len = 1000; /* Ethernet wants good queues */
dev->flags = IFF_BROADCAST|IFF_MULTICAST;
memset(dev->broadcast, 0xFF, ETH_ALEN);
}
alloc_etherdev完了之后,调用fec_enet_init函数,这里才是真正芯片级的代码:
fec_enet_init()
上锁spin_lock_init(&(fep->lock)),调用__get_free_page申请一页的内存,这个返回的地址为虚拟地
址,然后将这个地址值赋给fep->cbc_mem_base,然后将fecp和fep->hwp指向FEC硬件寄存器,fecp->fec_ecntrl = 1,复位
FEC模块,等待10us后调用fec_get_mac从FLASH中读取MAC地址,调用cbd_base = (cbd_t *)fec_map_uncache(mem_addr,
PAGE_SIZE);将刚才申请的一页进行映射(mem_addr不是虚拟地址么?为什么这里还要映射??)作为BD的初始地址并将FEC
的rx_bd_base指向该地址,同时将tx_bd_base指向RX_RING_SIZE之后的地址,这个RX_RING_SIZE定义如下:
/* The number of Tx and Rx buffers. These are allocated from the page
* pool. The code may assume these are power of two, so it it best
* to keep them that size.
* We don’t need to allocate pages for the transmitter. We just use
* the skbuffer directly.
*/
#define FEC_ENET_RX_PAGES 8
#define FEC_ENET_RX_FRSIZE 2048
#define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE)
#define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)
#define FEC_ENET_TX_FRSIZE 2048
#define FEC_ENET_TX_FRPPG (PAGE_SIZE / FEC_ENET_TX_FRSIZE)
#define TX_RING_SIZE 16 /* Must be power of two */
#define TX_RING_MOD_MASK 15 /* for this to work */
#if (((RX_RING_SIZE + TX_RING_SIZE) * 8) > PAGE_SIZE)
#error “FEC: descriptor ring size constants too large”
#endif
其中PAGE_SIZE是为4096,所以这个RX_RING_SIZE的值也为16,意思好像是说有16个FEC_ENET_RX_FRSIZE?
接下来循环初始化RX_RING_SIZE个frame,循环中,调用dev_alloc_skb(FEC_ENET_RX_FRSIZE);初始化skb,并将赋给fep-
>rx_skbuff[i],接下来将pskb->data进行对齐,这个对齐要求是通过
#define FEC_ADDR_ALIGNMENT(x) ((unsigned char *)(((unsigned long )(x) +
(FEC_ALIGNMENT)) & (~FEC_ALIGNMENT)))
来实现的,也就是说这个对齐要求是由datasheet里的FEC模块规定的。注意的是最后将这个地址进行物理
地址变换后赋给cbd_bufaddr的,即cbd_bufaddr = __pa(pskb->data);然后将BD的cbd_sc状态描述置为BD_ENET_RX_EMPTY空
闲。
接下来将最后一个BD的cbd_sc状态描述置上BD_SC_WRAP标志,这估计是datasheet里规定的吧??接下来
初始化发送BD,这里看不懂,先帖上来先
bdp = fep->tx_bd_base;
for (i=0, j=FEC_ENET_TX_FRPPG; i<TX_RING_SIZE; i++) {
if (j >= FEC_ENET_TX_FRPPG) {
mem_addr = __get_free_page(GFP_KERNEL);
j = 1;
} else {
mem_addr += FEC_ENET_TX_FRSIZE;
j++;
}
fep->tx_bounce[i] = (unsigned char *) mem_addr;
/* Initialize the BD for every fragment in the page.
*/
bdp->cbd_sc = 0;
bdp->cbd_bufaddr = 0;
bdp++;
}
/* Set the last buffer to wrap.
*/
bdp–;
bdp->cbd_sc |= BD_SC_WRAP;
接着调用
/* Set receive and transmit descriptor base.
*/
fecp->fec_r_des_start = __pa((uint)(fep->cbd_mem_base));
fecp->fec_x_des_start = __pa((uint)(fep->cbd_mem_base + RX_RING_SIZE*sizeof
(cbd_t)));
将发送同接收地址值写入寄存器中。下面调用fec_request_intrs申请两个中断,一个为FEC的中
断static irqreturn_t fec_enet_interrupt(int irq, void * dev_id),用作发送接收、MII及出错等中断使用,另一个为
PHY端的中断static irqreturn_t mii_link_interrupt(int irq, void * dev_id),用作PHY芯片检测LINK DOWN或AUTO-
NEGO DONE中断,下面
/* Clear and enable interrupts */
fecp->fec_ievent = FEC_ENET_MASK;
fecp->fec_imask = FEC_ENET_TXF | FEC_ENET_TXB | FEC_ENET_RXF | FEC_ENET_RXB
| FEC_ENET_MII;
fecp->fec_hash_table_high = 0;
fecp->fec_hash_table_low = 0;
fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
fecp->fec_ecntrl = 2;
fecp->fec_r_des_active = 0x01000000;
都是设置硬件寄存器,接着设置
/* The FEC Ethernet specific entries in the device structure. */
dev->open = fec_enet_open;
dev->hard_start_xmit = fec_enet_start_xmit;
dev->tx_timeout = fec_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
dev->stop = fec_enet_close;
dev->get_stats = fec_enet_get_stats;
dev->set_multicast_list = set_multicast_list;
这里是重载DEV的一些实现,当我们敲入ifconfig eth0 up的时候就会调用fec_enet_open。这里
要说明的是fec_timeout这个函数,这个函数运行在SOFTIRQ上下文,被当作网络的watchdog超时函数。而
set_multicast_list函数还不清楚是用来做什么的????接下来
for (i=0; i<NMII-1; i++)
mii_cmds[i].mii_next = &mii_cmds[i+1];
mii_free = mii_cmds;
这是初始化mii的命令队列,这个其实是一个静态数组,然后通过内部的链表变量将这个数组链
起来。mii_free是一个链表头,指向第一个可用的mii_cmd元素。接着调用fec_set_mii初始化MII的速度:
static void __inline__ fec_set_mii(struct net_device *dev, struct
fec_enet_private *fep)
{
u32 rate;
struct clk *clk;
volatile fec_t *fecp;
fecp = fep->hwp;
fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;
fecp->fec_x_cntrl = 0x00;
/*
* Set MII speed to 2.5 MHz
*/
clk = clk_get(NULL, “fec_clk”);
rate = clk_get_rate(clk);
clk_put(clk);
fep->phy_speed =
((((rate / 2 + 4999999) / 2500000) / 2) & 0x3F) << 1;
fecp->fec_mii_speed = fep->phy_speed;
fec_restart(dev, 0);
}
设置完clk后,调用fec_restart重新设置FEC的参数:
/* This function is called to start or restart the FEC during a link
* change. This only happens when switching between half and full
* duplex.
*/
static void
fec_restart(struct net_device *dev, int duplex)
{
struct fec_enet_private *fep;
volatile cbd_t *bdp;
volatile fec_t *fecp;
int i;
OUTDEB(“entry!!!!!\n”);
fep = netdev_priv(dev);
fecp = fep->hwp;
/* Whack a reset. We should wait for this.
*/
fecp->fec_ecntrl = 1;
udelay(10);
/* Enable interrupts we wish to service.
*/
fecp->fec_imask = FEC_ENET_TXF | FEC_ENET_TXB | FEC_ENET_RXF
| FEC_ENET_RXB | FEC_ENET_MII;
/* Clear any outstanding interrupt.
*
*/
fecp->fec_ievent = FEC_ENET_MASK;
#if 0/*modified by Zenith @ 2010.1.21 for int bug*/
fec_enable_phy_intr();
#endif
/* Set station address.
*/
fec_set_mac_address(dev);
/* Reset all multicast.
*/
fecp->fec_hash_table_high = 0;
fecp->fec_hash_table_low = 0;
/* Set maximum receive buffer size.
*/
fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
fec_localhw_setup();
/* Set receive and transmit descriptor base.
*/
fecp->fec_r_des_start = __pa((uint)(fep->cbd_mem_base));
fecp->fec_x_des_start = __pa((uint)(fep->cbd_mem_base +
RX_RING_SIZE*sizeof(cbd_t)));
fep->dirty_tx = fep->cur_tx = fep->tx_bd_base;
fep->cur_rx = fep->rx_bd_base;
/* Reset SKB transmit buffers.
*/
fep->skb_cur = fep->skb_dirty = 0;
for (i=0; i<=TX_RING_MOD_MASK; i++) {
if (fep->tx_skbuff[i] != NULL) {
dev_kfree_skb_any(fep->tx_skbuff[i]);
fep->tx_skbuff[i] = NULL;
}
}
/* Initialize the receive buffer descriptors.
*/
bdp = fep->rx_bd_base;
for (i=0; i<RX_RING_SIZE; i++) {
/* Initialize the BD for every fragment in the page.
*/
bdp->cbd_sc = BD_ENET_RX_EMPTY;
bdp++;
}
/* Set the last buffer to wrap.
*/
bdp–;
bdp->cbd_sc |= BD_SC_WRAP;
/* …and the same for transmmit.
*/
bdp = fep->tx_bd_base;
for (i=0; i<TX_RING_SIZE; i++) {
/* Initialize the BD for every fragment in the page.
*/
bdp->cbd_sc = 0;
bdp->cbd_bufaddr = 0;
bdp++;
}
/* Set the last buffer to wrap.
*/
bdp–;
bdp->cbd_sc |= BD_SC_WRAP;
/* Enable MII mode.
*/
if (duplex) {
fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;/* MII
enable */
fecp->fec_x_cntrl = 0x04; /* FD
enable */
}
else {
/* MII enable|No Rcv on Xmit */
fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x06;
fecp->fec_x_cntrl = 0x00;
}
fep->full_duplex = duplex;
/* Set MII speed.
*/
fecp->fec_mii_speed = fep->phy_speed;
/* And last, enable the transmit and receive processing.
*/
fecp->fec_ecntrl = 2;
fecp->fec_r_des_active = 0x01000000;
netif_start_queue(dev);
}
这里的其实是一个内联函数:
static inline void netif_start_queue(struct net_device *dev)
{
clear_bit(__LINK_STATE_XOFF, &dev->state);
}
它清除state中的XOFF位。
下面我们继续回到fec_enet_init函数中:
/* Queue up command to detect the PHY and initialize the
* remainder of the interface.
*/
fep->phy_id_done = 0;
fep->phy_addr = 0;
mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);
这里是将命令mk_mii_read(MII_REG_PHYIR1)插入mii的命令队列,并设置回调函数mii_discover_phy。
end fec_enet_init()
end fec_enet_module_init()
我们来看一下mii_queue的实现:
static int
mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_device *))
{
struct fec_enet_private *fep;
unsigned long flags;
mii_list_t *mip;
int retval;
/* Add PHY address to register command.
*/
fep = netdev_priv(dev);
regval |= fep->phy_addr << 23;
retval = 0;
spin_lock_irqsave(&fep->lock,flags);
if ((mip = mii_free) != NULL) {
mii_free = mip->mii_next;
mip->mii_regval = regval;
mip->mii_func = func;
mip->mii_next = NULL;
if (mii_head) {
mii_tail->mii_next = mip;
mii_tail = mip;
}
else {
mii_head = mii_tail = mip;
fep->hwp->fec_mii_data = regval;
}
}
else {
retval = 1;
}
spin_unlock_irqrestore(&fep->lock,flags);
return(retval);
}
可以看到,它先是读取FEC硬件寄存器phy_addr的低八位,将它放入变量regval中,这里应该是取PHY的地址,然后先看下
mii_free队列中有无元素,也即是判断有无可用的MII命令队列,如果有,则把寄存器的值(即是要读写PHY的哪个寄存器)
及回调函数放入队列中,再判断mii_head有无元素,如果无的话就将mii_head指向当前这个元素,如果有的话,更新
mii_tail指向当前元素(mii_tail始终指向最后一个命令元素),所以当我们向MII命令队列插入第一个元素的时候,它就
直接读写PHY的那个寄存器(fep->hwp->fec_mii_data = regval;)。而我们前面FEC的MII初始化的时候已经置了MII的中断
,所以这里往fep->hwp->fec_mii_data寄存器写数据完后便会产生一个FEC中断,这个中断由fec_enet_interrupt函数里调
用fec_enet_mii来处理。
我们再来看下fec_enet_mii的实现:
/* called from interrupt context */
static void
fec_enet_mii(struct net_device *dev)
{
struct fec_enet_private *fep;
volatile fec_t *ep;
mii_list_t *mip;
uint mii_reg;
fep = netdev_priv(dev);
ep = fep->hwp;
mii_reg = ep->fec_mii_data;
spin_lock(&fep->lock);
if ((mip = mii_head) == NULL) {
printk(“MII and no head!\n”);
goto unlock;
}
if (mip->mii_func != NULL)
(*(mip->mii_func))(mii_reg, dev);
mii_head = mip->mii_next;
mip->mii_next = mii_free;
mii_free = mip;
if ((mip = mii_head) != NULL)
ep->fec_mii_data = mip->mii_regval;
unlock:
spin_unlock(&fep->lock);
}
可以看到,函数首先是读取FEC的硬件寄存器fec_mii_data,这里的值应该是PHY芯片通过MII接口返回上次要求读的寄存器
的值,然后如果有mip->mii_func的话就回调这个函数,接着移动mii_head和mii_free队列,最后再写入下一个要读(写)
的PHY的寄存器。
所以当我们在初始化函数中调用mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);将mii_discover_phy
插入命令队列时,它先将要读的寄存器MII_REG_PHYIR1通过宏mk_mii_read转换成MII寄存器的格式,然后写入fec_mii_data
寄存器,表示我想读取PHY芯片的MII_REG_PHYIR1寄存器的值,而读寄存器写完后,FEC的MII模块会产生一个中断表示说这
个PHY寄存器的读已经完成,然后中断处理函数fec_enet_mii中,会读取PHY返回的刚才我想要读取的寄存器的值给
fec_mii_data寄存器,后来再调用刚才插入的那个mii_discover_phy函数,mii_discover_phy函数就会根据这个返回的ID来
判断是哪个厂家的PHY芯片。这里将mii_discover_phy再帖出来:
/* Scan all of the MII PHY addresses looking for someone to respond
* with a valid ID. This usually happens quickly.
*/
static void
mii_discover_phy(uint mii_reg, struct net_device *dev)
{
struct fec_enet_private *fep;
volatile fec_t *fecp;
uint phytype;
fep = netdev_priv(dev);
fecp = fep->hwp;
if (fep->phy_addr < 32) {
if ((phytype = (mii_reg & 0xffff)) != 0xffff && phytype != 0) {
/* Got first part of ID, now get remainder.
*/
fep->phy_id = phytype << 16;
mii_queue(dev, mk_mii_read(MII_REG_PHYIR2),
mii_discover_phy3);
}
else {
fep->phy_addr++;
mii_queue(dev, mk_mii_read(MII_REG_PHYIR1),
mii_discover_phy);
}
} else {
printk(“FEC: No PHY device found.\n”);
/* Disable external MII interface */
fecp->fec_mii_speed = fep->phy_speed = 0;
fec_disable_phy_intr();
}
}
至于判断fep->phy_addr < 32与否,这个是说一个FEC可以接多个PHY,每个PHY有一个地址,用5个BIT来表示,这里是从
0~31去自加,然后查询对应的PHY地址上是否有PHY设备。
初始化过程中fec_enet_init调用完了之后,dev这个变量已经设置得差不多了,接下来是调用register_netdev(dev) 来注
册一个网络设备,后面fec_device[i] = dev;将这个DEV放入FEC的设备表中。到此,module_init完成。。。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/169594.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...