CS8900A 驱动程序分析(国嵌)

CS8900A 驱动程序分析

(国嵌)

 

1.   寄存器

 

·LINECTL0112H

LINECTL决定CS8900的基本配置和物理接口。例如:设置初始值为00d3H,选择物理接口为10BASE-T,并使能设备的发送和接收控制位。

·RXCTL0104H

RXCTL控制CS8900接收特定数据报。设置RXTCL的初始值为0d05H,表示接收网络上的广播或者目标地址同本地物理地址相同的正确数据包。

·RXCFG0102H

RXCFG控制CS8900接收到特定数据报后会引发接收中断。RXCFG可设置为0103H,这样当收到一个正确的数据报后,CS8900会产生一个接收中断。

·BUSCT0116H

BUSCT可控制芯片的I/O接口的一些操作。设置初始值为8017H,打开CS8900的中断总控制位。

·ISQ0120H

ISQ是网卡芯片的中断状态寄存器,内部映射接收中断状态寄存器和发送中断状态寄存器的内容。


·PORT00000H
发送和接收数据时,CPU通过PORT0传递数据。

·TXCMD0004H
发送控制寄存器,如果写入数据00C0H,那么网卡芯片在全部数据写入后开始发送数据。

·TXLENG0006H
发送数据长度寄存器,发送数据时,首先写入发送数据长度,然后将数据通过PORT0写入芯片。

以上为几个最主要的工作寄存器(为16位)。
系统工作时,应首先对网卡芯片进行初始化,即写寄存器LINECTLRXCTL、 RCCFGBUSCT

发数据时,写控制寄存器TXCMD,并将发送数据长度写入TXLENG,然后将数据依次写入PORT0口,网卡芯片将数据组织为链路层类型并添加填充位和CRC校验送到网络。

 

 

 

 

 

 

 

 

 

2.   程序框架

 

2.1 模块注册

 

static int __init init_cs8900a_s3c2410(void) 

{

    struct net_local *lp;

    int ret = 0;

//初始化

    dev_cs89x0.irq = irq;// dev_cs89x0是struct net_device

    dev_cs89x0.base_addr = io;

dev_cs89x0.init = cs89x0_probe;// register_netdev时被调用来完成对net_device结构的初始化

// 申请使用IO端口。IO访问步骤:1.申请2.访问3.释放

    request_region(dev_cs89x0.base_addr, NETCARD_IO_EXTENT, "cs8900a");

//注册驱动

if (register_netdev(&dev_cs89x0) != 0) 

//其他初始化工作在cs89x0_probecs89x0_probe又调用cs89x0_probe1

}

 

 

2.2 设备检测

 

static int __init cs89x0_probe1(struct net_device *dev, int ioaddr)

{

  //判断是否是CS89x0芯片,通过读取寄存器。判断型号

    /* get the chip type */

    rev_type = readreg(dev, PRODUCT_ID_ADD);

    lp->chip_type = rev_type &~ REVISON_BITS;

    lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';

 

    if (lp->chip_type != CS8900) 

   {

      printk(__FILE__ ": wrong device driver!\n");

      ret = -ENODEV;

      goto after_kmalloc;

    }

 

 

//设置mac地址

    dev->dev_addr[0] = 0x00;

    dev->dev_addr[1] = 0x00;

    dev->dev_addr[2] = 0xc0;

    dev->dev_addr[3] = 0xff;

    dev->dev_addr[4] = 0xee;

    dev->dev_addr[5] = 0x08;

    set_mac_address(dev, dev->dev_addr);

 

    dev->irq = IRQ_LAN;

    printk(", IRQ %d", dev->irq);

//net_device进一步初始化

    dev->open= net_open;//open中注册中断

    dev->stop= net_close;

    dev->tx_timeout= net_timeout;

    dev->watchdog_timeo= 3 * HZ;

    dev->hard_start_xmit= net_send_packet;

    dev->get_stats= net_get_stats;

    dev->set_multicast_list= set_multicast_list;

    dev->set_mac_address = set_mac_address;

 

/* Fill in the fields of the device structure with ethernet values. */

//针对以太网卡的函数。初始化net_device设置为以太网卡的参数

    ether_setup(dev);

}

 

 

2.3 数据发送

 

dev->hard_start_xmit= net_send_packet;//所以下面这个函数是发送函数

 

static int net_send_packet(struct sk_buff *skb, struct net_device *dev)

{

 

    netif_stop_queue(dev);//停止发送队列,暂停送数据

 

/* initiate a transmit sequence */

//配置发送命令和发送长度。Skb是发送的报文,不能改的

    writeword(dev, TX_CMD_PORT, lp->send_cmd);

    writeword(dev, TX_LEN_PORT, skb->len);

 

/* Test to see if the chip has allocated memory for the packet */

//判断准备发送是否OK

    if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) 

   {

      spin_unlock_irq(&lp->lock);

      DPRINTK(1, "cs89x0: Tx buffer not free!\n");

      return 1;

    }

/* Write the contents of the packet */

//发送数据

    writeblock(dev, skb->data, skb->len);

 

    return 0;

}

//pDataskb数据首地址即skbdata

inline void writeblock(struct net_device *dev, char *pData, int Length) 

{

    int i;

//length是一个字节一个字节统计出来的

//芯片是16位的,每次发送两个字节,所以除以2

    for (i = 0 ; i < (Length/2); i++) {

        writeword(dev, TX_FRAME_PORT, *(u16 *)pData );

        pData += 2;

    }

//取余,是奇数的情况下发送最后一个字节

    if (Length % 2) {

        u16 OddWordValue = *pData;

        writeword(dev, TX_FRAME_PORT, OddWordValue);

    }

}

 

2.4 中断

 

 

static void net_interrupt(int irq, void *dev_id, struct pt_regs * regs)

{

//读取中断状态寄存器,根据中断类型和状态进行接受

    while ((status = readword(dev, ISQ_PORT))) 

{

      switch(status & ISQ_EVENT_MASK) {

      case ISQ_RECEIVER_EVENT:

/* Got a packet(s). */

net_rx(dev);//接收

break;

      

      case ISQ_TRANSMITTER_EVENT:

//发送完成

lp->stats.tx_packets++;//发送计数加一

netif_wake_queue(dev);/* Inform upper layers. *///重新启动发送队列

break;

}

 

 

2.5 接收

接收流程

1.分配skb

skb=dev_alloc_skb(pkt->datalen+2)

2.从硬件中读取数据到skb

3.调用netif_rx将数据交给协议栈

netif_rx(skb)

 

 

static void net_rx(struct net_device *dev) {

//读接收状态和长度

    status = inw(ioaddr + RX_FRAME_PORT);

    if ((status & RX_OK) == 0) {

      count_rx_errors(status, lp);

      return;

    }

 

    length = inw(ioaddr + RX_FRAME_PORT);

 

/* Malloc up new buffer. */

//分配skb

//ARM采用4字节对齐,skb的开始点可能是0也可能是4等,

//若在0,从0开始放数据为14(6+6+2),差2不能满足对齐要求

    skb = dev_alloc_skb(length + 2);

    

if (skb == NULL) {

      lp->stats.rx_dropped++;

      return;

    }

    skb_reserve(skb, 2);

/*  mac头是14个字节,一开始保留两个字节,正是为了保证ip头的开始是四字节对齐的。*/

    skb->len = length;

skb->dev = dev;

//读数据到skb

    readblock(dev, skb->data, skb->len);

 

 

    skb->protocol=eth_type_trans(skb,dev);

    netif_rx(skb);

}

inline void readblock(struct net_device *dev, char *pData, int Length) 

{

    u16 InputWord;

    int i;

 

    for (i=0; i < (Length/2); i++) {

        InputWord = readword(dev, RX_FRAME_PORT);

        *(u8*)pData++ = (u8) InputWord & 0xFF;

        *(u8*)pData++ = (u8) (InputWord >> 8) & 0xFF;

    }

 

    if (Length & 0x1)//判断最低位是否为1  等价if (Length % 2) ,判断是否为奇数

      *pData = (u8) (readword(dev, RX_FRAME_PORT) & 0xff);

}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值