s3c2410 cs8900a 网卡驱动程序
/*
CS8900a.h
*/
#define CONFIG_CERF_CS8900A 1
/*
* cs8900a.c: A Crystal Semiconductor (Now Cirrus Logic) CS8900A
driver for SMDK-s3c2410 (based on cs89x0.c)
*
* Author: Yong-iL Joh <tolkien@mizi.com>
* Date : $Date: 2002/10/16 09:08:07 $
*
* $Revision: 1.1.2.5 $
Wed Aug 14 2002 Yong-iL Joh <tolkien@mizi.com>
- initial, based on cs89x0.c
Wed Aug 16 2002 Yong-iL Joh <tolkien@mizi.com>
- working!
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware.h>
lichao-runing@163.com
#define IRQ_LAN IRQ_CS8900
/*
宏IRQ_LAN 定.网卡的中..
宏IRQ_CS8900 在include/asm-arm/arch-arm/smdk.h 中定.
#define IRQ_CS8900 IRQ_EINT9
宏IRQ_EINT9 定.位于include/asm-arm/arch/irqs.h 中
#define IRQ_EINT9 (5 +NORMAL_IRQ_OFFSET)
#define NORMAL_IRQ_OFFSET 32
所以网卡的中...37。
*/
#include "cs89x0.h"
/*
* Set this to zero to remove all the debug statements via
* dead code elimination
*/
#undef DEBUGGING 4
#if DEBUGGING
#define DPRINTK(n, args...) \
if (n <= DEBUGGING) { \
printk(args); \
}
#else
#define DPRINTK(n, args...)
#endif
#if DEBUGGING
static char version[] __initdata =
"cs89x0.c: v2.4.3-pre1 Russell Nelson <nelson@crynwr.com>, Andrew Morton
<andrewm@uow.edu.au>\n";
#endif
/* First, a few definitions that the brave might change.
A zero-terminated list of I/O addresses to be probed. Some special flags..
Addr & 1 = Read back the address port, look for signature and reset
the page window before probing
Addr & 3 = Reset the page window and probe
The CLPS eval board has the Cirrus chip at 0x80090300, in ARM IO space,
but it is possible that a Cirrus board could be plugged into the ISA
slots. */
static unsigned int netcard_portlist[] __initdata =
{ vCS8900_BASE + 0x300, 0};
/*
lichao-runing@163.com
__initdata .志符和.量.明放在一起。表示gcc ..器在..的.候需
要把...量放在.data.init section 中,而..section 在.核完毕初始化
之后,.被.放掉。
netcard_portlist[]..存放网卡端口的.地址,因为网
卡.接例如以下所看到的:
lichao-runing@163.com
如.中..所看到的,表明网卡映射到S3C2410 微.理器的BANK3 .,查.
微.理器手冊可知。.网卡ioport的物理起始地址.0x19000000,因为在Linux 中使用
虚拟地址,所以必须将其转化为虚拟地址,在ARM-LINUX 中。0x19000000相应
虚拟地址是0xd0000000。所以定义例如以下宏。分别表示CS8900 网卡内存物理起
始地址和虚拟起始地址。
vCS8900_BASE 定.例如以下:
/* CS8900a, nGCS3 */
#define pCS8900_BASE 0x19000000
#define vCS8900_BASE 0xd0000000
查.CS8900A 手.可知,其port寄存器偏移量.0x300。所以CS8900a port
起始地址被定..:vCS8900_BASE + 0x300
*/
/* The number of low I/O ports used by the ethercard. */
#define NETCARD_IO_EXTENT 0xfff
/* we allow the user to override various values normally set in the EEPROM */
#define FORCE_RJ45 0x0001 /* pick one of these three */
#define FORCE_AUI 0x0002
#define FORCE_BNC 0x0004
/*
定.网卡接口.型。假设使用RJ45 水晶..接。.使用第一.宏。
*/
#define FORCE_AUTO 0x0010 /* pick one of these three */
#define FORCE_HALF 0x0020
#define FORCE_FULL 0x0030
/*
定.网卡通.使用半.工通信、.是全.工通.,.是自...。
*/
/* Information that need to be kept for each board. */
struct net_local {
struct net_device_stats stats;
int chip_type; /* one of: CS8900, CS8920, CS8920M */
char chip_revision; /* revision letter of the chip ('A'...) */
int send_cmd; /* the proper send command:
TX_NOW, TX_AFTER_381, or TX_AFTER_ALL */
lichao-runing@163.com
int auto_neg_cnf; /* auto-negotiation word from EEPROM */
int adapter_cnf; /* adapter configuration from EEPROM */
int isa_config; /* ISA configuration from EEPROM */
int irq_map; /* IRQ map from EEPROM */
int rx_mode; /* what mode are we in?
0, RX_MULTCAST_ACCEPT, or RX_ALL_ACCEPT */
int curr_rx_cfg; /* a copy of PP_RxCFG */
int linectl; /* either 0 or LOW_RX_SQUELCH,
depending on configuration. */
int send_underrun; /* keep track of how many underruns
in a row we get */
int force; /* force various values; see FORCE* above. */
spinlock_t lock;
};
/* Index to functions, as function prototypes. */
extern int cs89x0_probe(struct net_device *dev);
static int cs89x0_probe1(struct net_device *dev, int ioaddr);
static int net_open(struct net_device *dev);
static int net_send_packet(struct sk_buff *skb, struct net_device *dev);
static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void set_multicast_list(struct net_device *dev);
static void net_timeout(struct net_device *dev);
static void net_rx(struct net_device *dev);
static int net_close(struct net_device *dev);
static struct net_device_stats *net_get_stats(struct net_device *dev);
static void reset_chip(struct net_device *dev);
static int set_mac_address(struct net_device *dev, void *addr);
static void count_rx_errors(int status, struct net_local *lp);
/* Example routines you must write ;->. */
#define tx_done(dev) 1
/* Check for a network adaptor of this type, and return '0' iff one exists.
If dev->base_addr == 0, probe all likely locations.
If dev->base_addr == 1, always return failure.
If dev->base_addr == 2, allocate space for the device and return success
(detachable devices only).
Return 0 on success.
*/
int __init cs89x0_probe(struct net_device *dev) {
int i;
lichao-runing@163.com
SET_MODULE_OWNER(dev);
DPRINTK(1, "cs89x0:cs89x0_probe(0x%x)\n", base_addr);
BWSCON = (BWSCON & ~(BWSCON_ST3 | BWSCON_WS3 | BWSCON_DW3)) |
(BWSCON_ST3 | BWSCON_WS3 | BWSCON_DW(3, BWSCON_DW_16));
BANKCON3= BANKCON_Tacs0 | BANKCON_Tcos4 | BANKCON_Tacc14 |
BANKCON_Toch1 | BANKCON_Tcah4 | BANKCON_Tacp6 | BANKCON_PMC1;
/*
.置BANK3 存.体。使其和CS8900a 网卡的.性相一致。比方BWSCON_DW_16 .
置BANK3 位..16bit,.何...置呢?因.CS8900a 网卡就是工作在16bit 模式下。即
.据.度(由其..使用到的.据..度.定).16bit。
*/
set_external_irq(IRQ_CS8900, EXT_RISING_EDGE, GPIO_PULLUP_DIS);
/*
.置IRQ_CS8900 所占用中...性,上升沿触.,PULLUP .效。
*/
for (i = 0; netcard_portlist[i]; i++) {
if (cs89x0_probe1(dev, netcard_portlist[i]) == 0)
return 0;
/*
探.网卡。假设探.到网卡后,cs89x0_probe1 返回值.0。所以就退出for 循.。
由此
可.,仅仅能探.一.网卡。
*/
}
printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected."
"Be sure to disable PnP with SETUP\n");
return -ENODEV;
/*
假设不能探.到网卡。就.打印..消息。并返回-ENODEV ...。
*/
}
//.取寄存器.容
inline int readreg(struct net_device *dev, int portno) {
outw(portno, dev->base_addr + ADD_PORT);
return inw(dev->base_addr + DATA_PORT);
/*
dev->base_addr 是..的基地址。ADD_PORT 是偏移地址。DATA_PORT 是.据。
#define ADD_PORT 0x000A
#define DATA_PORT 0x000C
因为在此..中。CS8900A 工作在I/O 模式下,查.CS8900A 手.可知。在I/O
模式下,假设想要.取端口..portno 的寄存器(此寄存器系CS8900A .部寄存器).容,
.依照下列步.(.CS8900A 手.的P77 中的4.10.10 .):
.. 首先。.port.(即.部寄存器..).入到偏移量.ADD_PORT 的寄存器
中。即.用outw 函..完毕;
.. 然后,.部寄存器的.容.被映射到偏移量.DATA_PORT 的.据寄存器中。
通.inw 函..其.出。
*/
}
//把value 值.入到端口..portno 的寄存器中
inline void writereg(struct net_device *dev, int portno, int value) {
outw(portno, dev->base_addr + ADD_PORT);
outw(value, dev->base_addr + DATA_PORT);
/*
dev->base_addr 是io的基地址,ADD_PORT 是偏移地址,DATA_PORT 是.据。
#define ADD_PORT 0x000A
#define DATA_PORT 0x000C
因为在此..中,CS8900A 工作在I/O 模式下,查.CS8900A 手.可知,在I/O
模式下。假设想要.置端口..portno 的寄存器(此寄存器系CS8900A .部寄存器).容,
.依照下列步.(.CS8900A 手.的P77 中的4.10.10 .):
.. 首先,.port.(即.部寄存器..).入到偏移量.ADD_PORT 的寄存器
中。即.用outw 函..完毕;
.. 然后,.用outw 函..value .量的值.入到.部寄存器中。
*/
}
/*
.端口..portno 的寄存器中.取一.字.据
*/
inline int readword(struct net_device *dev, int portno) {
return inw(dev->base_addr + portno);
}
/*
.value 值.入到端口..portno 的寄存器中。
*/
inline void writeword(struct net_device *dev, int portno, int value) {
outw(value, dev->base_addr + portno);
}
/*
..度.Length 的一..据.入到网卡..中pData 指.所指向的...
*/
inline void writeblock(struct net_device *dev, char *pData, int Length) {
int i;
lichao-runing@163.com
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);
}
}
/*
.网卡..中pData 指.所指向的....容.出,.出字...Length
*/
inline void readblock(struct net_device *dev, char *pData, int Length) {
u16 InputWord;
int i;
/*
因为每次.取的是一.半字,所以for 循.次..半
*/
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)
*pData = (u8) (readword(dev, RX_FRAME_PORT) & 0xff);
}
/* This is the real probe routine. Linux has a history of friendly device
probes on the ISA bus. A good device probes avoids doing writes, and
verifies that the correct device exists and functions.
Return 0 on success.
*/
static int __init cs89x0_probe1(struct net_device *dev, int ioaddr) {
struct net_local *lp;
#if DEBUGGING
static unsigned version_printed;
#endif
unsigned rev_type = 0;
lichao-runing@163.com
int ret;
/* Initialize the device structure. */
if (dev->priv == NULL) {
dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
if (dev->priv == 0) {
ret = -ENOMEM;
goto before_kmalloc;
}
/*
判.dev->priv 指.是否.空。假设.空。.用kmalloc 函.申.一..存空.。
例如以下.1 所看到的:
.1
*/
lp = (struct net_local *)dev->priv;
memset(lp, 0, sizeof(*lp));
spin_lock_init(&lp->lock);
}
/*
.lp 指向.1 中新申.的...,.用memset 函.....清空。最后新建一把
自旋.。并初始化.把.。
*/
lp = (struct net_local *)dev->priv;
/* Fill in the 'dev' fields. */
dev->base_addr = ioaddr;
/*
priv
dev
lichao-runing@163.com
形.ioaddr 的值由..netcard_portlist[i]..,netcard_portlist ..定.例如以下:
static unsigned int netcard_portlist[] __initdata =
{ vCS8900_BASE + 0x300, 0};
所以ioaddr 的值.vCS8900_BASE + 0x300。即0xd0000300。
.此值填充到dev 的
base_addr 中。
*/
/* Bus Reset Consideration */
ret = readword(dev, ADD_PORT);
if ((ret & ADD_MASK) != ADD_SIG ) {
DPRINTK(1, __FUNCTION__ " 0x%08X\n", ret);
ret = -ENODEV;
goto after_kmalloc;
}
/* 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';
/*
宏PRODUCT_ID_ADD 值定..:
#define PRODUCT_ID_ADD 0x0002 /* Address of product ID */
#define REVISON_BITS 0x1F00
因此上面几..句表示.网卡.部寄存器0x002 中.取值,.寄存器中存放.网卡的.
品的ID .等,例如以下定.(..信息查.CS8900 手.P41 中4.3.1 ..容):
.品的...存放在在.量rev_type 中, 例如以下值:
rev_type=0000 1110 0110 0011
*/
#if DEBUGGING
if (version_printed++ == 0)
printk(version);
#endif
printk(KERN_INFO "%s: cs89%c0%s rev %c(%s) found at %#3lx\n",
dev->name,
lp->chip_type==CS8900 ? '0' : '2',
lichao-runing@163.com
lp->chip_type==CS8920M ? "M" : "",
lp->chip_revision,
readreg(dev, PP_SelfST) & ACTIVE_33V ? "3.3 Volts" : "5 Volts",
dev->base_addr);
/*
打印网卡信息,用.在.核...可看到此.信息。
*/
if (lp->chip_type != CS8900) {
printk(__FILE__ ": wrong device driver!\n");
ret = -ENODEV;
goto after_kmalloc;
}
/* Check the chip type and revision in order to
set the correct send command
CS8900 revision F can use the faster send. */
lp->send_cmd = TX_AFTER_ALL;
if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
lp->send_cmd = TX_NOW;
reset_chip(dev);
lp->adapter_cnf = A_CNF_10B_T | A_CNF_MEDIA_10B_T;
lp->auto_neg_cnf = EE_AUTO_NEG_ENABLE;
printk(KERN_INFO "cs89x0 media %s%s",
(lp->adapter_cnf & A_CNF_10B_T)?"RJ-45":"",
(lp->adapter_cnf & A_CNF_AUI)?"AUI":"");
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);
/*
.置网卡mac 地址.0x08eeffc00000。
*/
dev->irq = IRQ_LAN;
/*
.置网卡的中..
*/
lichao-runing@163.com
printk(", IRQ %d", dev->irq);
dev->open = net_open;
/*
.置open 函.指.指向net_open()。.函.在打.网卡.被使用。在Linux 中打.网
卡能够使用下列命令:
ifconfig eth0 192.168.0.24 up
*/
dev->stop = net_close;
/*
.置stop 函.指.指向net_close()函.,.函.在..网卡.被使用。
在Linux 中
..网卡能够使用下列命令:
ifconfig eth0 down
*/
dev->tx_timeout = net_timeout;
/*
.置tx_timeout 函.指.指向net_timeout 函.,.函.在网...超.后.重.网卡。
并再次打.网卡。
*/
dev->watchdog_timeo = 3 * HZ;
/*
.置超....3 秒.
*/
dev->hard_start_xmit = net_send_packet;
/*
.置hard_start_xmit 函.指.指向net_send_packet 函.,.函.完毕.一.网..据
包.送出去的任.。
*/
dev->get_stats = net_get_stats;
/*
.置get_stats 函.指.指向net_get_stats 函.。.函.收集网...。
*/
dev->set_multicast_list = set_multicast_list;
/*
.置set_multicast_list 函.指.指向set_multicast_list 函.,.函.完毕多播功能。
*/
dev->set_mac_address = set_mac_address;
/*
.置set_mac_address 函.指.指向set_mac_address 函.,.函..置网卡的mac 地址。
*/
/* Fill in the fields of the device structure with ethernet values. */
ether_setup(dev);
/*
lichao-runing@163.com
在.函.中..完毕CS8900 网卡的剩余.置。
*/
printk("\n");
DPRINTK(1, "cs89x0_probe1() successful\n");
return 0;
after_kmalloc:
kfree(dev->priv);
before_kmalloc:
return ret;
}
/*
.函.完毕网卡芯片重.
*/
void __init reset_chip(struct net_device *dev)
{
int reset_start_time;
writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
/*
宏PP_SelfCTL 等宏定.例如以下:
#define PP_SelfCTL 0x0114 /* Self Command Register */
#define POWER_ON_RESET 0x0040
查看CS8900 手.可知在SelfControl 寄存器中.置网卡重.。SelfControl 寄存器定.如
下(CS8900A 手.的P64 中的4.4.18):
能够看出,假设要使网卡重.,仅仅需..寄存器中的bit6 置.1 就可以。
所以writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET)首先.
用readreg .SelfControl 寄存器的.容.出。并.其bit6 位置1 后再.回SelfControl 寄存器。
..就能.完毕网卡芯片重.功能。
*/
/* wait 30 ms */
lichao-runing@163.com
/*
..前.程...定.不可中.,并等待30ms。
*/
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(30*HZ/1000);
/* Wait until the chip is reset */
reset_start_time = jiffies;
while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 &&
jiffies - reset_start_time < 4)
;
}
/* Open/initialize the board. This is called (in the current kernel)
sometime after booting when the 'ifconfig' program is run.
This routine should set everything up anew at each open, even
registers that "should" only need to be set once at boot, so that
there is non-reboot way to recover if something goes wrong.
*/
static int net_open(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ret;
/* Prevent the crystal chip from generating interrupts */
writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ);
ret = request_irq(dev->irq, &net_interrupt, SA_SHIRQ, "cs89x0", dev);
/*
注.网卡中.。中..理程序.net_interrupt 函.。
*/
if (ret) {
printk("%s: request_irq(%d) failed\n", dev->name, dev->irq);
goto bad_out;
}
/* Set up the IRQ - Apparently magic */
if (lp->chip_type == CS8900)
writereg(dev, PP_CS8900_ISAINT, 0);
/*
.置中..。宏PP_CS8900_ISAINT 定..0x22,中..寄存器定.例如以下:
lichao-runing@163.com
..CS8900a .部中..,此...INTRQ0。
*/
else
writereg(dev, PP_CS8920_ISAINT, 0);
/* while we're testing the interface, leave interrupts disabled */
writereg(dev, PP_BusCTL, MEMORY_ON);
/*
#define MEMORY_ON 0x0400
#define PP_BusCTL 0x0116 /* ISA bus control Register */
.置0x400 到BusCTL 寄存器,即.MemoryE 位置.1。表示CS8900A 能够操作在
memory 模式。
*/
/* Set the LineCTL quintuplet */
lichao-runing@163.com
lp->linectl = 0;
/* Turn on both receive and transmit operations */
writereg(dev, PP_LineCTL,
readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
/*
相.宏定.例如以下:
#define PP_LineCTL 0x0112 /* Line Config Register */
#define SERIAL_RX_ON 0x0040
#define SERIAL_TX_ON 0x0080
首先.LineCTL 寄存器.容.出,.置其bit6 和bit7 位,并.改动后的值再.回LineCTL
寄存器。...置后。.送器和接收器被打.。
*/
/* Receive only error free packets addressed to this card */
lp->rx_mode = 0;
writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
/*
#define PP_RxCTL 0x0104 /* Receive Control Register */
#define DEF_RX_ACCEPT (RX_IA_ACCEPT | RX_BROADCAST_ACCEPT |
RX_OK_ACCEPT)
#define RX_OK_ACCEPT 0x0100
#define RX_IA_ACCEPT 0x0400
#define RX_BROADCAST_ACCEPT 0x0800
lichao-runing@163.com
.置RxCTL 寄存器。
*/
lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
if (lp->isa_config & STREAM_TRANSFER)
lp->curr_rx_cfg |= RX_STREAM_ENBL;
writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
/*
#define PP_RxCFG 0x0102 /* Rx Bus config */
#define RX_OK_ENBL 0x0100
#define RX_CRC_ERROR_ENBL 0x1000
lichao-runing@163.com
*/
writereg(dev, PP_TxCFG,
TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
TX_LATE_COL_ENBL | TX_JBR_ENBL |
TX_ANY_COL_ENBL | TX_16_COL_ENBL);
/*
#define PP_TxCFG 0x0106 /* Transmit Config Register */
#define TX_LOST_CRS_ENBL 0x0040
#define TX_SQE_ERROR_ENBL 0x0080
#define TX_OK_ENBL 0x0100
#define TX_LATE_COL_ENBL 0x0200
#define TX_JBR_ENBL 0x0400
#define TX_ANY_COL_ENBL 0x0800
#define TX_16_COL_ENBL 0x8000
lichao-runing@163.com
*/
writereg(dev, PP_BufCFG,
READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
/*
#define PP_BufCFG 0x010A /* Bus configuration Register */
#define READY_FOR_TX_ENBL 0x0100
#define RX_MISS_COUNT_OVRFLOW_ENBL 0x2000
#define TX_COL_COUNT_OVRFLOW_ENBL 0x1000
#define TX_UNDERRUN_ENBL 0x0200
lichao-runing@163.com
*/
/* now that we've got our act together, enable everything */
writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ);
/*
#define PP_BusCTL 0x0116 /* ISA bus control Register */
#define ENABLE_IRQ 0x8000
lichao-runing@163.com
*/
enable_irq(dev->irq);
netif_start_queue(dev);
DPRINTK(1, "cs89x0: net_open() succeeded\n");
return 0;
bad_out:
return ret;
}
static void net_timeout(struct net_device *dev)
{
/* If we get here, some higher level has decided we are broken.
There should really be a "kick me" function call instead. */
DPRINTK(1, "%s: transmit timed out, %s?
\n", dev->name,
tx_done(dev) ?
"IRQ conflict ?" : "network cable problem");
lichao-runing@163.com
/* Try to restart the adaptor. */
//netif_wake_queue(dev);
net_close(dev);
//..网卡..
writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
//.置网卡..重.
net_open(dev);
//打.网卡..
}
/*
.送.据包函.
*/
static int net_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
writereg(dev, PP_BusCTL, 0x0);
writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ);
DPRINTK(3, "%s: sent %d byte packet of type %x\n",
dev->name, skb->len,
(skb->data[ETH_ALEN+ETH_ALEN] << 8) |
(skb->data[ETH_ALEN+ETH_ALEN+1]));
/* keep the upload from being interrupted, since we
ask the chip to start transmitting before the
whole packet has been completely uploaded. */
spin_lock_irq(&lp->lock);
netif_stop_queue(dev);
/* initiate a transmit sequence */
writeword(dev, TX_CMD_PORT, lp->send_cmd);
writeword(dev, TX_LEN_PORT, skb->len);
/*
..送命令.入到...0x004 的命令寄存器中。.要.送的.据.度.入到...
0x006 的命令寄存器中。
lichao-runing @163.com
*/
/* Test to see if the chip has allocated memory for the packet */
if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
/*
* Gasp! It hasn't. But that shouldn't happen since
* we're waiting for TxOk, so return 1 and requeue this packet.
*/
spin_unlock_irq(&lp->lock);
DPRINTK(1, "cs89x0: Tx buffer not free!\n");
return 1;
}
/*
#define PP_BusST 0x0138 /* Bus Status */
#define READY_FOR_TX_NOW 0x0100
lichao-runing@163.com
*/
/* Write the contents of the packet */
writeblock(dev, skb->data, skb->len);
/*
..据.入到dev .送...。
*/
spin_unlock_irq(&lp->lock);
//.放自旋.
dev->trans_start = jiffies;
//...据被.送的..
dev_kfree_skb (skb);
//.据.送后。.放skb 空.
/*
* We DO NOT call netif_wake_queue() here.
* We also DO NOT call netif_start_queue().
*
* Either of these would cause another bottom half run through
* net_send_packet() before this packet has fully gone out. That causes
* us to hit the "Gasp!" above and the send is rescheduled. it runs like
* a dog. We just return and wait for the Tx completion interrupt handler
* to restart the netdevice layer
*/
return 0;
lichao-runing@163.com
}
/* The typical workload of the driver:
Handle the network interface interrupts. */
static void net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
struct net_device *dev = dev_id;
struct net_local *lp;
int ioaddr, status;
ioaddr = dev->base_addr;
lp = (struct net_local *)dev->priv;
/* we MUST read all the events out of the ISQ, otherwise we'll never
get interrupted again. As a consequence, we can't have any limit
on the number of times we loop in the interrupt handler. The
hardware guarantees that eventually we'll run out of events. Of
course, if you're on a slow machine, and packets are arriving
faster than you can read them off, you're screwed. Hasta la
vista, baby! */
/*
首先.出中....列寄存器,在...列寄存器中的低6 位存放.中..生的原因,
.其.出,判.出中.的原因。然后採取相.的步.。
值得注意的是,我.必.把中..列寄存器中所存放的所有事件所有.理完.。所以採
用了while 循.。
相.宏定.例如以下:
#define ISQ_PORT 0x0008
#define ISQ_EVENT_MASK 0x003F /* ISQ mask to find out type of event */
lichao-runing@163.com
*/
while ((status = readword(dev, ISQ_PORT))) {
DPRINTK(4, "%s: event=%04x\n", dev->name, status);
switch(status & ISQ_EVENT_MASK) {
case ISQ_RECEIVER_EVENT:
/* Got a packet(s). */
net_rx(dev);
break;
/*
#define ISQ_RECEIVER_EVENT 0x04
收到.据包,.用net_rx 函.去接收并.理...据包。
*/
lichao-runing@163.com
case ISQ_TRANSMITTER_EVENT:
lp->stats.tx_packets++;
netif_wake_queue(dev); /* Inform upper layers. */
if ((status & ( TX_OK |
TX_LOST_CRS | TX_SQE_ERROR |
TX_LATE_COL | TX_16_COL)) != TX_OK) {
if ((status & TX_OK) == 0) lp->stats.tx_errors++;
if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++;
if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++;
if (status & TX_LATE_COL) lp->stats.tx_window_errors++;
if (status & TX_16_COL) lp->stats.tx_aborted_errors++;
}
break;
/*
.送了一..据包,.tx_packets 成.加1。.用netif_wake_queue 函.通知
上...。
然后判..送.据包的..。并依据不同情...些....下.。
*/
case ISQ_BUFFER_EVENT:
if (status & READY_FOR_TX) {
/* we tried to transmit a packet earlier,
but inexplicably ran out of buffers.
That shouldn't happen since we only ever
load one packet. Shrug. Do the right
thing anyway. */
netif_wake_queue(dev); /* Inform upper layers. */
}
if (status & TX_UNDERRUN) {
DPRINTK(1, "%s: transmit underrun\n", dev->name);
lp->send_underrun++;
if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381;
else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL;
/* transmit cycle is done, although
frame wasn't transmitted - this
avoids having to wait for the upper
layers to timeout on us, in the
event of a tx underrun */
netif_wake_queue(dev); /* Inform upper layers. */
}
break;
case ISQ_RX_MISS_EVENT:
lp->stats.rx_missed_errors += (status >>6);
break;
/*
lichao-runing@163.com
接收包..
*/
case ISQ_TX_COL_EVENT:
lp->stats.collisions += (status >>6);
break;
/*
.送包.突
*/
}
}
}
static void count_rx_errors(int status, struct net_local *lp) {
lp->stats.rx_errors++;
if (status & RX_RUNT) lp->stats.rx_length_errors++;
if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++;
if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT)))
/* per str 172 */
lp->stats.rx_crc_errors++;
if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++;
return;
}
/* ....接收.据包后。....中.之清除*/
static void net_rx(struct net_device *dev) {
struct net_local *lp = (struct net_local *)dev->priv;
struct sk_buff *skb;
int status, length;
int ioaddr = dev->base_addr;
status = inw(ioaddr + RX_FRAME_PORT);
if ((status & RX_OK) == 0) {
count_rx_errors(status, lp);
return;
}
/*
首先.出接收.port寄存器.容。然后判.接收.是否正确,假设..。.用
count_rx_errors 函..理。
*/
length = inw(ioaddr + RX_FRAME_PORT);
/*
.....port.出.的.度
*
lichao-runing@163.com
/* Malloc up new buffer. */
skb = dev_alloc_skb(length + 2);
if (skb == NULL) {
lp->stats.rx_dropped++;
return;
}
skb_reserve(skb, 2); /* longword align L3 header */
skb->len = length;
skb->dev = dev;
readblock(dev, skb->data, skb->len);
/*
分配一.新的...(位于.核空.中),并把位于.....里的..据拷.到..
新的...中。
*/
DPRINTK(3, "%s: received %d byte packet of type %x\n",
dev->name, length,
(skb->data[ETH_ALEN+ETH_ALEN] << 8) |
skb->data[ETH_ALEN+ETH_ALEN+1]);
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
//把接收下.的...(已.拷.到新的...中)交.上......行。
dev->last_rx = jiffies;
//..接收包的..
lp->stats.rx_packets++;
//收到一.包后。.rx_packets 成.加1
lp->stats.rx_bytes += length;
//改动收到的接收包..度
}
/*..网卡..*/
static int net_close(struct net_device *dev)
{
netif_stop_queue(dev);
//停止..上.送和接收.列
writereg(dev, PP_RxCFG, 0);
writereg(dev, PP_TxCFG, 0);
writereg(dev, PP_BufCFG, 0);
writereg(dev, PP_BusCTL, 0);
/*
.相.寄存器置.0
lichao-runing@163.com
*/
free_irq(dev->irq, dev);
//清除相.中.
/* Update the statistics here. */
return 0;
}
/* Get the current statistics. This may be called with the card open or
closed. */
static struct net_device_stats *
net_get_stats(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
unsigned long flags;
spin_lock_irqsave(&lp->lock, flags);
/* Update the statistics from the device registers. */
lp->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
lp->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
spin_unlock_irqrestore(&lp->lock, flags);
return &lp->stats;
}
static void set_multicast_list(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
unsigned long flags;
spin_lock_irqsave(&lp->lock, flags);
if (dev->flags&IFF_PROMISC) {
lp->rx_mode = RX_ALL_ACCEPT;
} else if((dev->flags&IFF_ALLMULTI)||dev->mc_list) {
/* The multicast-accept list is initialized to accept-all,
and we rely on higher-level filtering for now. */
lp->rx_mode = RX_MULTCAST_ACCEPT;
} else
lp->rx_mode = 0;
writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode);
/* in promiscuous mode, we accept errored packets,
so we have to enable interrupts on them also */
writereg(dev, PP_RxCFG, lp->curr_rx_cfg |
lichao-runing@163.com
(lp->rx_mode == RX_ALL_ACCEPT ?
(RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0));
spin_unlock_irqrestore(&lp->lock, flags);
}
/*
.置网卡的物理mac 地址
*/
static int set_mac_address(struct net_device *dev, void *addr)
{
int i;
if (netif_running(dev))
return -EBUSY;
DPRINTK(1, "%s: Setting MAC address to ", dev->name);
for (i = 0; i < 6; i++) {
dev->dev_addr[i] = ((unsigned char *)addr)[i];
DPRINTK(1, " %2.2x", dev->dev_addr[i]);
}
/*
首先.要更改的mac 地址.到dev_addr 成.中
*/
DPRINTK(1, ".\n");
/* set the Ethernet address */
for (i=0; i < ETH_ALEN/2; i++)
writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
/*
.mac 地址.入到网卡中存放物理mac 地址的寄存器中
*/
return 0;
}
#ifdef MODULE
static struct net_device dev_cs89x0 = {
"",
0, 0, 0, 0,
0, 0,
0, 0, 0, NULL, NULL };
/*
* Support the 'debug' module parm even if we're compiled for non-debug to
lichao-runing@163.com
* avoid breaking someone's startup scripts
*/
static int io = 0xd0000300;
static int irq = IRQ_LAN;
static char media[8];
static int duplex= 0;
MODULE_PARM(io, "i");
MODULE_PARM(irq, "i");
MODULE_PARM(media, "c8");
MODULE_PARM(duplex, "i");
MODULE_PARM_DESC(io, "cs89x0 I/O base address");
MODULE_PARM_DESC(irq, "cs89x0 IRQ number");
MODULE_PARM_DESC(media, "Set cs89x0 adapter(s) media type(s) (rj45,bnc,aui)");
/* No other value than -1 for duplex seems to be currently interpreted */
MODULE_PARM_DESC(duplex, "(ignored)");
MODULE_AUTHOR("Mike Cruse, Russwll Nelson <nelson@crynwr.com>, Andrew Morton
<andrewm@uow.edu.au>");
MODULE_LICENSE("GPL");
EXPORT_NO_SYMBOLS;
/*
* media=t - specify media type
or media=2
or media=aui
or medai=auto
* duplex=0 - specify forced half/full/autonegotiate duplex
* debug=# - debug level
* Default Chip Configuration:
* DMA Burst = enabled
* IOCHRDY Enabled = enabled
* UseSA = enabled
* CS8900 defaults to half-duplex if not specified on command-line
* CS8920 defaults to autoneg if not specified on command-line
* Use reset defaults for other config parameters
* Assumptions:
* media type specified is supported (circuitry is present)
* if memory address is > 1MB, then required mem decode hw is present
lichao-runing@163.com
* if 10B-2, then agent other than driver will enable DC/DC converter
(hw or software util)
*/
/*
初始化CS8900 网卡,..就是初始化全局.量dev_cs89x0
*/
static int __init init_cs8900a_s3c2410(void) {
struct net_local *lp;
int ret = 0;
dev_cs89x0.irq = irq;
/*
.置中..。irq 宏定.例如以下:
static int irq = IRQ_LAN;
*/
dev_cs89x0.base_addr = io;
/*
.置网卡基地址
*/
dev_cs89x0.init = cs89x0_probe;
/*
定.初始化函.指.,指向cs89x0_probe 函.。
*/
dev_cs89x0.priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
//申.一..存空.,..存空.用于存放网卡的私有.据
if (dev_cs89x0.priv == 0) {
printk(KERN_ERR "cs89x0.c: Out of memory.\n");
return -ENOMEM;
}
memset(dev_cs89x0.priv, 0, sizeof(struct net_local));
lp = (struct net_local *)dev_cs89x0.priv;
request_region(dev_cs89x0.base_addr, NETCARD_IO_EXTENT, "cs8900a");
spin_lock_init(&lp->lock);
/* boy, they'd better get these right */
if (!strcmp(media, "rj45"))
lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
lichao-runing@163.com
else if (!strcmp(media, "aui"))
lp->adapter_cnf = A_CNF_MEDIA_AUI | A_CNF_AUI;
else if (!strcmp(media, "bnc"))
lp->adapter_cnf = A_CNF_MEDIA_10B_2 | A_CNF_10B_2;
else
lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
if (duplex==-1)
lp->auto_neg_cnf = AUTO_NEG_ENABLE;
if (io == 0) {
printk(KERN_ERR "cs89x0.c: Module autoprobing not allowed.\n");
printk(KERN_ERR "cs89x0.c: Append io=0xNNN\n");
ret = -EPERM;
goto out;
}
/*
注.网卡..
*/
if (register_netdev(&dev_cs89x0) != 0) {
printk(KERN_ERR "cs89x0.c: No card found at 0x%x\n", io);
ret = -ENXIO;
goto out;
}
out:
if (ret)
kfree(dev_cs89x0.priv);
return ret;
}
static void __exit cleanup_cs8900a_s3c2410(void) {
if (dev_cs89x0.priv != NULL) {
/* Free up the private structure, or leak memory :-) */
unregister_netdev(&dev_cs89x0);
outw(PP_ChipID, dev_cs89x0.base_addr + ADD_PORT);
kfree(dev_cs89x0.priv);
dev_cs89x0.priv = NULL; /* gets re-allocated by cs89x0_probe1 */
/* If we don't do this, we can't re-insmod it later. */
release_region(dev_cs89x0.base_addr, NETCARD_IO_EXTENT);
}
}
module_init(init_cs8900a_s3c2410);
module_exit(cleanup_cs8900a_s3c2410);
#endif
/*
| $Id: cs8900a.c,v 1.1.2.5 2002/10/16 09:08:07 tolkien Exp $
|
| Local Variables:
| mode: c
| mode: font-lock
| version-control: t
| delete-old-versions: t
| End:
|
| -*- End-Of-File -*-
*/
博客转自,http://hdzhanghao1989.blog.163.com/blog/static/184977241201132485610585/ 有改变原来。
版权声明:本文博主原创文章,博客,未经同意不得转载。
s3c2410 cs8900a 网卡驱动程序的更多相关文章
- Linux DM9000网卡驱动程序完全分析
Linux DM9000网卡驱动程序完全分析http://blog.csdn.net/ypoflyer/article/details/6209922
- 转:Linux网卡驱动程序编写
Linux网卡驱动程序编写 [摘自 LinuxAID] 工作需要写了我们公司一块网卡的Linux驱动程序.经历一个从无到有的过程,深感技术交流的重要.Linux作为挑战微软垄断的强有力武器,日益受到大 ...
- freebsd网卡驱动程序详解
freebsd网卡驱动程序详解 来源 https://blog.csdn.net/h_cszc/article/details/7776116 /* 注释:xie_minix */ /*此处为BSD申 ...
- 17、网卡驱动程序-DM9000举例
(参考:cs89x0.c可以参考) DM9000 芯片实现网络功能的基础,在接收数据时采用中断方式,即当有数据到来并在 DM9000 内部 CRC 校验通过后会产生一个接收中断: 网卡驱动程序框架: ...
- DM9000C网卡驱动程序编写与测试
一般网卡驱动程序厂商会给我们提供一份模板驱动,我们的工作就是需要根据自己的需要更改这个模板驱动 1.DM9000C的硬件连接 硬件连接图如下所示:它接在S3C2440的BANK4内存控制器上,它只占用 ...
- 嵌入式Linux驱动学习之路(二十五)虚拟网卡驱动程序
一.协议栈层次对比 设备无关层到驱动层的体系结构 1).网络协议接口层向网络层协议提供提供统一的数据包收发接口,不论上层协议为ARP还是IP,都通过dev_queue_xmit()函数发送数据,并通过 ...
- Linux下的网卡驱动程序的编写过程(转)
工作需要写了我们公司一块网卡的linux驱动程序.经历一个从无到有的过程,深感技术交流的重要.Linux作为挑战微软垄断的强有力武器,日益受到大家的喜爱.真希望她能在中国迅速成长.把程序文档贴出来,希 ...
- Linux网卡驱动(4)—DM9000网卡驱动程序完全分析
1.硬件连接 mini2440开发板上DM9000的电气连接和mach-mini2440.c文件的关系 其中片选信号AEN使用了nGCS4,所以网卡的内存区域在BANK4,也就是从地址0x200000 ...
- 嵌入式Linux驱动学习之路(二十六)DM9000C网卡驱动程序
基于DM9000C的原厂代码修改dm9000c的驱动程序. 首先确认内存的基地址 iobase. 确定中断号码. 打开模块的初始化函数定义. 配置内存控制器的相应时序(结合DM9000C.C的手册). ...
随机推荐
- JVM学习:方法重载的优先级
重载:方法名一致,参数长度或者类型不一致. 先放总结,下面为例子 参数具有继承.实现关系,优先考虑子类: 在不考虑对基本类型自动装拆箱(auto-boxing,auto-unboxing),以及可变长 ...
- 【SPOJ 694】Distinct Substrings
[链接]h在这里写链接 [题意] 给你一个长度最多为1000的字符串 让你求出一个数x,这个x=这个字符串的不同子串个数; [题解] 后缀数组题. 把原串复制一份,加在 ...
- [AngularFire2] Pagination
Let's see how to do pagination in Firebase: For the init loading, we only want 3 items: findLessonsK ...
- swift开发网络篇—利用NSURLSession 发送GET和POST请求
说明:本文示例代码发送的请求均为http请求,需要对info.plist文件进行配置.如何配置,请参考https://github.com/HanGangAndHanMeimei/iOS9Adapta ...
- 【例题3-4 UVA - 340】Master-Mind Hints
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 这里出现了没有在相同位置的只能唯一配对. 就是说 3322 2234 这种情况. 只有3个weak pair. 即key[1]=a[ ...
- 在vue中使用font-awesome
1.安装 cnpm i font-awesome -S 2.在main.js中引入 import 'font-awesome/css/font-awesome.min.css'
- PWA之Service work
原文 简书原文:https://www.jianshu.com/p/84a4553d81a8 大纲 1.Service Workers: PWA 的关键 2.理解 Service Workers 3. ...
- iOS开发RunnLoop学习二:GCD中的定时器
#import "ViewController.h" @interface ViewController () /** 注释 */ @property (nonatomic, st ...
- HTTP协议和HTTPS协议初探
概况 HTTP是hypertext transfer protocol(超文本传输协议)的简写.它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEBserver之间交换数据的过程. HT ...
- python implementation for Qt's QDataStream(看一下QDataStream的结构)
#!/usr/bin/env python # -*- coding: utf- -*- from __future__ import print_function from __future__ i ...