logo资料库

tcp/ip在linux下得实现.doc

第1页 / 共6页
第2页 / 共6页
第3页 / 共6页
第4页 / 共6页
第5页 / 共6页
第6页 / 共6页
资料共6页,全文预览结束
CS8900A 驱动程序分析 (国嵌) 1. 寄存器 ·LINECTL(0112H) LINECTL 决定 CS8900 的基本配置和物理接口。例如:设置初始值为 00d3H,选择物理接口为 10BASE-T,并使能设备的发送和接收控制 位。 ·RXCTL(0104H) RXCTL 控制 CS8900 接收特定数据报。设置 RXTCL 的初始值为 0d05H,表示接收网络上的广播或者目标地址同本地物理地址相同的正 确数据包。 ·RXCFG(0102H) RXCFG 控制 CS8900 接收到特定数据报后会引发接收中断。RXCFG 可 设置为 0103H,这样当收到一个正确的数据报后,CS8900 会产生一个 接收中断。 ·BUSCT(0116H) BUSCT 可控制芯片的 I/O 接口的一些操作。设置初始值为 8017H,打开 CS8900 的中断总控制位。 ·ISQ(0120H) ISQ 是网卡芯片的中断状态寄存器,内部映射接收中断状态寄存器和发 送中断状态寄存器的内容。 ·PORT0(0000H) 发送和接收数据时,CPU 通过 PORT0 传递数据。 ·TXCMD(0004H) 发送控制寄存器,如果写入数据 00C0H,那么网卡芯片在全部数据写入 后开始发送数据。 ·TXLENG(0006H) 发送数据长度寄存器,发送数据时,首先写入发送数据长度,然后将数 据通过 PORT0 写入芯片。
以上为几个最主要的工作寄存器(为 16 位)。 系统工作时,应首先对网卡芯片进行初始化,即写寄存器 LINECTL、 RXCTL、 RCCFG、BUSCT。 发数据时,写控制寄存器 TXCMD,并将发送数据长度写入 TXLENG, 然后将数据依次写入 PORT0 口,网卡芯片将数据组织为链路层类型并添 加填充位和 CRC 校验送到网络。 网卡:可以接收与 MAC 地址相同的包或广播包 网卡:可以用中断向上层发送数据 2. 程序框架 2.1 模块注册 static int __init init_cs8900a_s3c2410(void) { struct net_local *lp; int ret = 0; dev_cs89x0.irq = irq; dev_cs89x0.base_addr = io; dev_cs89x0.init = cs89x0_probe; request_region(dev_cs89x0.base_addr, NETCARD_IO_EXTENT, "cs8900a"); } if (register_netdev(&dev_cs89x0) != 0) 2.2 设备检测 static int __init cs89x0_probe1(struct net_device *dev, int ioaddr) { /* 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; 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_open; = net_close; dev->open dev->stop dev->tx_timeout = net_timeout; dev->watchdog_timeo dev->hard_start_xmit dev->get_stats dev->set_multicast_list dev->set_mac_address = 3 * HZ; = net_send_packet; = net_get_stats; = set_multicast_list; = set_mac_address; /* Fill in the fields of the device structure with ethernet values. */ 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) { //协议栈发给网卡的是 sk_buff netif_stop_queue(dev);//暂停协议栈发送数据 如何发送? 1. 写发送命令 2. 写报文长度 3. 判断硬件是否准备好发送 readreg(dev, PP_BusST) & READY_FOR_TX_NOW) 4. 写数据 sk_buff :writeblock(dev, skb->data, skb->len);
/* initiate a transmit sequence */ 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 */ 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); /* skb->data:而不是 skb->head */ return 0; } inline void writeblock(struct net_device *dev,char *pdata,int length) { int i; for(i=0;i<(length/2);i++) { 为什么 length/2? 因为寄存器 16 位,writeword 每次从起始地址传两个字节 writeword(dev,TX_FRAME_PORT,*(u16 *)pdata);//寄存器 16 位 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. */唤醒协议栈 } length = inw(ioaddr + RX_FRAME_PORT); /* Malloc up new buffer. */ skb = dev_alloc_skb(length + 2); //分配 skb 在网卡驱动这一层并没有剥去 mac 头部,相反用 readblock(dev, skb->data, skb->len); //读取数据 读取的数据含有 mac 地址,或还有其他协议头比如 IP 协议头 /* 为什么加 2? mac 头不需要对齐 ip 头的开始也是四字节对齐地的,始地址 是 4 的倍数 分配时 Skb_buff 是按 4 字节对齐的,即 Skb_buff 的开始点是在 0 或 4 /* netif_stop_queue(dev); 发送函数暂停协议栈发送数据 */ break; } 2.5 接收 :从网络接收 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;
*/ if (skb == NULL) { lp->stats.rx_dropped++; return; } skb_reserve(skb, 2); /* mac 头是 14 个字节,一开始保留两个字节,正是为了保证 ip 头的开始是四字节 对齐的。*/ skb->len = length; skb->dev = dev; readblock(dev, skb->data, skb->len); //读取数据 skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); } 好东东,认真理解,深刻体会!祝各位好运!
分享到:
收藏