2010年10月31日 星期日

嵌入式linux下dm9000网卡驱动的移植与实现

網址:http://www.doc88.com/p-19430099901.html

PXA270 ARM 板實現 dm9000a 網路驅動程式

轉載自:http://tw.myblog.yahoo.com/yh-chiang/article?mid=213&prev=391&next=126&l=f&fid=9

於PXA270 ARM 板實現 dm9000a 網路驅動程式在 Linux 2.6 版本的過程 <-97年年初完成---含完整 dm9000 網卡驅動程式

2008/05/26 00:32

首先先完成硬體線路,PXA270 ARM 板上的dm9000a 網路硬體線路如下所示:

1. 首先必須了解硬體線路及DM9000AEP 的SPEC,接著找到 davicom 上的 dm9000 驅動程式SOURCE CODE。

2. 修改 u-boot 上 dm9000 驅動部分。

3. 將 dm9000 驅動程式SOURCE CODE( dm9ks.c 及dm9ks.h)拷貝至 drivers/net 目錄,修改Linux 2.6 上對應的 kconfig 及 makefile 檔,接著依照上面硬體線路部份,修改 dm9ks.c 及dm9ks.h 驅動程式。最後 MAKE KERNEL 產生 zImage,將 zImage 透過 u-boot 燒寫至 PXA270 板,PXA270開機測試...完成^^。

#####晚了。。。先休息去,其它的細部部分有空再完成了( 用寫的過程很簡單,但想起年初時,整個實作過程蠻累人的 ^^ )。#####^^

我修改後可用於此 ARM-PXA270 開發板的網卡驅動程式如下:

/*
dm9ks.c: Version 2.04 2008/03/09
A Davicom DM9000A/DM9010 ISA NIC fast Ethernet driver for Linux.
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
of the License, 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.

(C)Copyright 1997-2005 DAVICOM Semiconductor,Inc. All Rights Reserved.

V1.00 10/13/2004 Add new function Early transmit & IP/TCP/UDP Checksum
offload enable & flow control is default
V1.1 12/29/2004 Add Two packet mode & modify RX function
V1.2 01/14/2005 Add Early transmit mode
V1.3 03/02/2005 Support kernel 2.6
v1.33 06/08/2005 #define DM9KS_MDRAL 0xf4
#define DM9KS_MDRAH 0xf5
V2.00 Spenser - 01/10/2005
- Modification for PXA270 MAINSTONE.
- Modified dmfe_tx_done().
- Add dmfe_timeout().
V2.01 10/07/2005 Modified dmfe_timer()
Dected network speed 10/100M
V2.02 10/12/2005 Use link change to chage db->Speed
dmfe_open() wait for Link OK
V2.03 11/22/2005 Power-off and Power-on PHY in dmfe_init()
support IOL

Last modified by YiHua,Chiang/江義華.<microcyh@seed.net.tw> to support DM9000 on
ARM-PXA270 development board.
*/

#if defined(MODVERSIONS)
#include
#endif

#include "dm9ks.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define CONFIG_ARCH_MAINSTONE
#ifdef CONFIG_ARCH_MAINSTONE
#include
#include
#include
#endif

typedef struct _RX_DESC
{
u8 rxbyte;
u8 status;
u16 length;
}RX_DESC;

typedef union{
u8 buf[4];
RX_DESC desc;
} rx_t;
#ifndef CONFIG_ARCH_MAINSTONE
#pragma pack(pop)
#endif

enum DM9KS_PHY_mode {
DM9KS_10MHD = 0,
DM9KS_100MHD = 1,
DM9KS_10MFD = 4,
DM9KS_100MFD = 5,
DM9KS_AUTO = 8,
};

/* Structure/enum declaration ------------------------------- */
typedef struct board_info {

u32 reset_counter; /* counter: RESET */
u32 reset_tx_timeout; /* RESET caused by TX Timeout */

u32 io_addr; /* Register I/O base address */
u32 io_data; /* Data I/O address */
int tx_pkt_cnt;

u8 op_mode; /* PHY operation mode */
u8 io_mode; /* 0:word, 2:byte */
u8 device_wait_reset; /* device state */
u8 Speed; /* current speed */

int cont_rx_pkt_cnt;/* current number of continuos rx packets */
struct timer_list timer;
struct net_device_stats stats;
unsigned char srom[128];
spinlock_t lock;

} board_info_t;
/* Global variable declaration ----------------------------- */
static struct net_device * dmfe_dev = NULL;
/* For module input parameter */
static int mode = DM9KS_AUTO;
static int media_mode = DM9KS_AUTO;
static u8 irq = DM9K_IRQ;
static u32 iobase = DM9KS_MIN_IO;
/* function declaration ------------------------------------- */
int dmfe_probe(struct net_device *);
static int dmfe_open(struct net_device *);
static int dmfe_start_xmit(struct sk_buff *, struct net_device *);
static void dmfe_tx_done(unsigned long);
static void dmfe_packet_receive(struct net_device *);
static int dmfe_stop(struct net_device *);
static struct net_device_stats * dmfe_get_stats(struct net_device *);
static int dmfe_do_ioctl(struct net_device *, struct ifreq *, int);
#if LINUX_VERSION_CODE <>

#if defined(CHECKSUM)
static u8 check_rx_ready(u8);
#endif
/*
Search DM9000 board, allocate space and register it
*/
struct net_device * __init dmfe_probe1(void)
{
struct net_device *dev;
int err;

#if LINUX_VERSION_CODE < dev =" init_etherdev(NULL," dev=" alloc_etherdev(sizeof(struct">

err = dmfe_probe(dev);
if (err)
goto out;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
err = register_netdev(dev);
if (err)
goto out1;
#endif
return dev;
out1:
release_region(dev->base_addr,2);
out:
#if LINUX_VERSION_CODE <>

int __init dmfe_probe(struct net_device *dev)
{
struct board_info *db; /* Point a board information structure */
u32 id_val;
u16 i, dm9000_found = FALSE;
u32 iobase_max = iobase + 0x70;

DMFE_DBUG(0, "dmfe_probe()",0);
/* Search All DM9000 serial NIC */
do {
outb(DM9KS_VID_L, iobase);
id_val = inb(iobase + 0x4000);
outb(DM9KS_VID_H, iobase);
id_val |= inb(iobase + 0x4000) << id_val ="="" id_val ="="">name))
return -ENODEV;
printk(" I/O: %x, VID: %x \n",iobase, id_val);
dm9000_found = TRUE;
/* Allocated board information structure */
memset(dev->priv, 0, sizeof(struct board_info));
db = (board_info_t *)dev->priv;
dmfe_dev = dev;
db->io_addr = iobase;
db->io_data = iobase + 0x4000;
/* driver system function */
dev->base_addr = iobase;
dev->irq = irq;
dev->open = &dmfe_open;
dev->hard_start_xmit = &dmfe_start_xmit;
dev->watchdog_timeo = 3*HZ;
dev->tx_timeout = dmfe_timeout;
dev->stop = &dmfe_stop;
dev->get_stats = &dmfe_get_stats;
dev->set_multicast_list = &dm9000_hash_table;
dev->do_ioctl = &dmfe_do_ioctl;
#if defined(CHECKSUM)
dev->features = dev->features | NETIF_F_NO_CSUM;
#endif
#ifndef CONFIG_DM9000_NO_EEPROM
/* Read SROM content */
for (i=0; i<64;>srom)[i] = read_srom_word(db, i);
#else
((u8 *)db->srom)[0] = 0x00;
((u8 *)db->srom)[1] = 0x80;
((u8 *)db->srom)[2] = 0x48;
((u8 *)db->srom)[3] = 0x12;
((u8 *)db->srom)[4] = 0x34;
((u8 *)db->srom)[5] = 0x56;
#endif
/* Set Node Address */
for (i=0; i<6;>dev_addr[i] = db->srom[i];
}//end of if()
iobase += 0x10;
}while(!dm9000_found && iobase <= (0xf1000000+DM9KS_MAX_IO)); if(!dm9000_found) printk("dm9000 device wrong id!"); return dm9000_found ? 0:-ENODEV; } /* Open the interface. The interface is opened whenever "ifconfig" actives it. */ static int dmfe_open(struct net_device *dev) { board_info_t *db = (board_info_t *)dev->priv;
u8 reg_nsr;
int i;
DMFE_DBUG(0, "dmfe_open", 0); set_irq_type(dev->irq, IRQT_RISING);

if (request_irq(dev->irq,&dmfe_interrupt,SA_SHIRQ,dev->name,dev))
return -EAGAIN;

/* Initilize DM910X board */
dmfe_init_dm9000(dev);

/* Init driver variable */
db->reset_counter = 0;
db->reset_tx_timeout = 0;
db->cont_rx_pkt_cnt = 0;

/* check link state and media speed */
db->Speed =10;
i=0;
do {
reg_nsr = ior(db,0x1);
if(reg_nsr & 0x40) /* link OK!! */
{
/* wait for detected Speed */
mdelay(200);
reg_nsr = ior(db,0x1);
if(reg_nsr & 0x80)
db->Speed =10;
else
db->Speed =100;
break;
}
i++;
mdelay(1);
}while(i<3000);>timer);
db->timer.expires = DMFE_TIMER_WUT * 2;
db->timer.data = (unsigned long)dev;
db->timer.function = &dmfe_timer;
add_timer(&db->timer); //Move to DM9000 initiallization was finished.

netif_start_queue(dev);

return 0;
}

/* Set PHY operationg mode
*/
static void set_PHY_mode(board_info_t *db)
{
u16 phy_reg0 = 0x1200; /* Auto-negotiation & Restart Auto-negotiation */
u16 phy_reg4 = 0x01e1; /* Default flow control disable*/

if ( !(db->op_mode & DM9KS_AUTO) ) // op_mode didn't auto sense */
{
switch(db->op_mode) {
case DM9KS_10MHD: phy_reg4 = 0x21;
phy_reg0 = 0x1000;
break;
case DM9KS_10MFD: phy_reg4 = 0x41;
phy_reg0 = 0x1100;
break;
case DM9KS_100MHD: phy_reg4 = 0x81;
phy_reg0 = 0x3000;
break;
case DM9KS_100MFD: phy_reg4 = 0x101;
phy_reg0 = 0x3100;
break;
default:
break;
} // end of switch
} // end of if
phy_write(db, 0, phy_reg0);
phy_write(db, 4, phy_reg4);
}

/*
Initilize dm9000 board
*/
static void dmfe_init_dm9000(struct net_device *dev)
{
board_info_t *db = (board_info_t *)dev->priv;
DMFE_DBUG(0, "dmfe_init_dm9000()", 0);

/* set the internal PHY power-on, GPIOs normal, and wait 2ms */
iow(db, DM9KS_GPR, 1); /* Power-Down PHY */
udelay(500);
iow(db, DM9KS_GPR, 0); /* GPR (reg_1Fh)bit GPIO0=0 pre-activate PHY */
udelay(20); /* wait 2ms for PHY power-on ready */

/* do a software reset and wait 20us */
iow(db, DM9KS_NCR, 3);
udelay(20); /* wait 20us at least for software reset ok */
iow(db, DM9KS_NCR, 3); /* NCR (reg_00h) bit[0] RST=1 & Loopback=1, reset on */
udelay(20); /* wait 20us at least for software reset ok */

udelay(1000); /* wait 4ms linking PHY (AUTO sense) if RX/TX */
udelay(1000);
udelay(1000);
udelay(1000);
/* I/O mode */
db->io_mode = ior(db, DM9KS_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */

/* Set PHY */
db->op_mode = media_mode;
set_PHY_mode(db);

/* Program operating register */
iow(db, DM9KS_NCR, 0);
iow(db, DM9KS_TCR, 0); /* TX Polling clear */
iow(db, DM9KS_BPTR, 0x3f); /* Less 3kb, 600us */
iow(db, DM9KS_SMCR, 0); /* Special Mode */
iow(db, DM9KS_NSR, 0x2c); /* clear TX status */
iow(db, DM9KS_ISR, 0x0f); /* Clear interrupt status */

/* Added by jackal at 03/29/2004 */
#if defined(CHECKSUM)
iow(db, DM9KS_TCCR, 0x07); /* TX UDP/TCP/IP checksum enable */
iow(db, DM9KS_RCSR, 0x02); /*Receive checksum enable */
#endif

#if defined(ETRANS)
iow(db, DM9KS_ETXCSR, 0x83);
#endif

/* Set address filter table */
dm9000_hash_table(dev);

/* Activate DM9000A/DM9010 */
iow(db, DM9KS_RXCR, DM9KS_REG05 | 1); /* RX enable */
iow(db, DM9KS_IMR, DM9KS_REGFF); // Enable TX/RX interrupt mask

/* Init Driver variable */
db->tx_pkt_cnt = 0;

netif_carrier_on(dev);
spin_lock_init(&db->lock);

}

/*
Hardware start transmission.
Send a packet to media from the upper layer.
*/
static int dmfe_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
board_info_t *db = (board_info_t *)dev->priv;
char * data_ptr;
int i, tmplen;
if(db->Speed == 10)
{if (db->tx_pkt_cnt >= 1) return 1;}
else
{if (db->tx_pkt_cnt >= 2) return 1;}

/* packet counting */
db->tx_pkt_cnt++;

db->stats.tx_packets++;
db->stats.tx_bytes+=skb->len;
if (db->Speed == 10)
{if (db->tx_pkt_cnt >= 1) netif_stop_queue(dev);}
else
{if (db->tx_pkt_cnt >= 2) netif_stop_queue(dev);}

/* Disable all interrupt */
iow(db, DM9KS_IMR, DM9KS_DISINTR);

/* Set TX length to reg. 0xfc & 0xfd */
iow(db, DM9KS_TXPLL, (skb->len & 0xff));
iow(db, DM9KS_TXPLH, (skb->len >> 8) & 0xff);

/* Move data to TX SRAM */
data_ptr = (char *)skb->data;

outb(DM9KS_MWCMD, db->io_addr); // Write data into SRAM trigger
switch(db->io_mode)
{
case DM9KS_BYTE_MODE:
for (i = 0; i <>len; i++)
outb((data_ptr[i] & 0xff), db->io_data);
break;
case DM9KS_WORD_MODE:
tmplen = (skb->len + 1) / 2;
for (i = 0; i <>io_data);
break;
case DM9KS_DWORD_MODE:
tmplen = (skb->len + 3) / 4;
for (i = 0; i<>io_data);
break;
}

#if !defined(ETRANS)
/* Issue TX polling command */
iow(db, DM9KS_TCR, 0x1); /* Cleared after TX complete*/
#endif

/* Saved the time stamp */
dev->trans_start = jiffies;
db->cont_rx_pkt_cnt =0;

/* Free this SKB */
dev_kfree_skb(skb);

/* Re-enable interrupt */
iow(db, DM9KS_IMR, DM9KS_REGFF);

return 0;
}

/*
Stop the interface.
The interface is stopped when it is brought.
*/
static int dmfe_stop(struct net_device *dev)
{
board_info_t *db = (board_info_t *)dev->priv;
DMFE_DBUG(0, "dmfe_stop", 0);

/* deleted timer */
del_timer(&db->timer);

netif_stop_queue(dev);

/* free interrupt */
free_irq(dev->irq, dev);

/* RESET devie */
phy_write(db, 0x00, 0x8000); /* PHY RESET */
iow(db, DM9KS_GPR, 0x01); /* Power-Down PHY */
iow(db, DM9KS_IMR, DM9KS_DISINTR); /* Disable all interrupt */
iow(db, DM9KS_RXCR, 0x00); /* Disable RX */

/* Dump Statistic counter */
#if FALSE
printk("\nRX FIFO OVERFLOW %lx\n", db->stats.rx_fifo_errors);
printk("RX CRC %lx\n", db->stats.rx_crc_errors);
printk("RX LEN Err %lx\n", db->stats.rx_length_errors);
printk("RESET %x\n", db->reset_counter);
printk("RESET: TX Timeout %x\n", db->reset_tx_timeout);
printk("g_TX_nsr %x\n", g_TX_nsr);
#endif

return 0;
}

static void dmfe_tx_done(unsigned long unused)
{
struct net_device *dev = dmfe_dev;
board_info_t *db = (board_info_t *)dev->priv;
int nsr;

DMFE_DBUG(0, "dmfe_tx_done()", 0);

nsr = ior(db, DM9KS_NSR);
if(nsr & 0x04) db->tx_pkt_cnt--;
if(nsr & 0x08) db->tx_pkt_cnt--;

if (db->tx_pkt_cnt <>tx_pkt_cnt =0;
}
if (db->Speed == 10)
{if(db->tx_pkt_cnt <>tx_pkt_cnt <>

return;
}

/*
DM9000 insterrupt handler
receive the packet to upper layer, free the transmitted packet
*/
#if LINUX_VERSION_CODE < dev =" dev_id;">

DMFE_DBUG(0, "dmfe_interrupt()", 0);

/* A real interrupt coming */
db = (board_info_t *)dev->priv;
spin_lock(&db->lock);

/* Save previous register address */
reg_save = inb(db->io_addr);

/* Disable all interrupt */
iow(db, DM9KS_IMR, DM9KS_DISINTR);

/* Got DM9000A/DM9010 interrupt status */
int_status = ior(db, DM9KS_ISR); /* Got ISR */
iow(db, DM9KS_ISR, int_status); /* Clear ISR status */

/* Link status change */
if (int_status & DM9KS_LINK_INTR)
{
netif_stop_queue(dev);
for(i=0; i<500; time ="0.5s" i="0;">Speed =100;
else db->Speed =10;
break;
}
udelay(1000);
}
netif_wake_queue(dev);
//printk("[INTR]i=%d speed=%d\n",i, (int)(db->Speed));
}
/* Received the coming packet */
if (int_status & DM9KS_RX_INTR)
dmfe_packet_receive(dev);

/* Trnasmit Interrupt check */
if (int_status & DM9KS_TX_INTR)
dmfe_tx_done(0);

if (db->cont_rx_pkt_cnt>=CONT_RX_PKT_CNT)
{
iow(db, DM9KS_IMR, 0xa2);
}
else
{
/* Re-enable interrupt mask */
iow(db, DM9KS_IMR, DM9KS_REGFF);
}

/* Restore previous register address */
outb(reg_save, db->io_addr);

spin_unlock(&db->lock);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
return IRQ_HANDLED;
#endif
}

/*
Get statistics from driver.
*/
static struct net_device_stats * dmfe_get_stats(struct net_device *dev)
{
board_info_t *db = (board_info_t *)dev->priv;
DMFE_DBUG(0, "dmfe_get_stats", 0);
return &db->stats;
}

/*
Process the upper socket ioctl command
*/
static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
DMFE_DBUG(0, "dmfe_do_ioctl()", 0);
return 0;
}

/* Our watchdog timed out. Called by the networking layer */
static void
dmfe_timeout(struct net_device *dev)
{
board_info_t *db = (board_info_t *)dev->priv;

DMFE_DBUG(0, "dmfe_TX_timeout()", 0);
printk("TX time-out -- dmfe_timeout().\n");
db->reset_tx_timeout++;
db->stats.tx_errors++;
#if FALSE
printk("TX packet count = %d\n", db->tx_pkt_cnt);
printk("TX timeout = %d\n", db->reset_tx_timeout);
printk("22H=0x%02x 23H=0x%02x\n",ior(db,0x22),ior(db,0x23));
printk("faH=0x%02x fbH=0x%02x\n",ior(db,0xfa),ior(db,0xfb));
printk("feH=0x%02x ffH=0x%02x\n",ior(db,0xfe),ior(db,0xff));
#endif
dmfe_reset(dev);
}
static void dmfe_reset(struct net_device * dev)
{
board_info_t *db = (board_info_t *)dev->priv;
u8 reg_save;
int i;
/* Save previous register address */
reg_save = inb(db->io_addr);

netif_stop_queue(dev);
db->reset_counter++;
dmfe_init_dm9000(dev);

db->Speed =10;
for(i=0; i<1000; time="1">Speed =100;
else db->Speed =10;
break;
}
udelay(1000);
}

netif_wake_queue(dev);

/* Restore previous register address */
outb(reg_save, db->io_addr);

}
/*
A periodic timer routine
*/
static void dmfe_timer(unsigned long data)
{
struct net_device * dev = (struct net_device *)data;
board_info_t *db = (board_info_t *)dev->priv;
DMFE_DBUG(0, "dmfe_timer()", 0);

if (db->cont_rx_pkt_cnt>=CONT_RX_PKT_CNT)
{
db->cont_rx_pkt_cnt=0;
iow(db, DM9KS_IMR, DM9KS_REGFF);
}
/* Set timer again */
db->timer.expires = DMFE_TIMER_WUT;
add_timer(&db->timer);

return;
}

#if !defined(CHECKSUM)
#define check_rx_ready(a) ((a) == 0x01)
#else
inline u8 check_rx_ready(u8 rxbyte)
{
if (!(rxbyte & 0x01))
return 0;
return ((rxbyte >> 4) | 0x01);
}
#endif

/*
Received a packet and pass to upper layer
*/
static void dmfe_packet_receive(struct net_device *dev)
{
board_info_t *db = (board_info_t *)dev->priv;
struct sk_buff *skb;
u8 rxbyte, val;
u16 i, GoodPacket, tmplen = 0, MDRAH, MDRAL;
u32 tmpdata;

rx_t rx;

u16 * ptr = (u16*)℞
u8* rdptr;

DMFE_DBUG(0, "dmfe_packet_receive()", 0);

do {
/*store the value of Memory Data Read address register*/
MDRAH=ior(db, DM9KS_MDRAH);
MDRAL=ior(db, DM9KS_MDRAL);

ior(db, DM9KS_MRCMDX); /* Dummy read */
rxbyte = inb(db->io_data); /* Got most updated data */

/* packet ready to receive check */
if(!(val = check_rx_ready(rxbyte))) break;

/* A packet ready now & Get status/length */
GoodPacket = TRUE;
outb(DM9KS_MRCMD, db->io_addr);

/* Read packet status & length */
switch (db->io_mode)
{
case DM9KS_BYTE_MODE:
*ptr = inb(db->io_data) +
(inb(db->io_data) <<>io_data) +
(inb(db->io_data) << ptr =" inw(db-">io_data);
*(ptr+1) = inw(db->io_data);
break;
case DM9KS_DWORD_MODE:
tmpdata = inl(db->io_data);
*ptr = tmpdata;
*(ptr+1) = tmpdata >> 16;
break;
default:
break;
}

/* Packet status check */
if (rx.desc.status & 0xbf)
{
GoodPacket = FALSE;
if (rx.desc.status & 0x01)
{
db->stats.rx_fifo_errors++;
printk(" \n");
}
if (rx.desc.status & 0x02)
{
db->stats.rx_crc_errors++;
printk(" \n");
}
if (rx.desc.status & 0x80)
{
db->stats.rx_length_errors++;
printk(" \n");
}
if (rx.desc.status & 0x08)
printk(" \n");
}

if (!GoodPacket)
{
// drop this packet!!!
switch (db->io_mode)
{
case DM9KS_BYTE_MODE:
for (i=0; i inb(db->io_data);
break;
case DM9KS_WORD_MODE:
tmplen = (rx.desc.length + 1) / 2;
for (i = 0; i <>io_data);
break;
case DM9KS_DWORD_MODE:
tmplen = (rx.desc.length + 3) / 4;
for (i = 0; i <>io_data);
break;
}
continue;/*next the packet*/
}

skb = dev_alloc_skb(rx.desc.length+4);
if (skb == NULL )
{
printk(KERN_INFO "%s: Memory squeeze.\n", dev->name);
/*re-load the value into Memory data read address register*/
iow(db,DM9KS_MDRAH,MDRAH);
iow(db,DM9KS_MDRAL,MDRAL);
return;
}
else
{
/* Move data from DM9000 */
skb->dev = dev;
skb_reserve(skb, 2);
rdptr = (u8*)skb_put(skb, rx.desc.length - 4);

/* Read received packet from RX SARM */
switch (db->io_mode)
{
case DM9KS_BYTE_MODE:
for (i=0; i rdptr[i]=inb(db->io_data);
break;
case DM9KS_WORD_MODE:
tmplen = (rx.desc.length + 1) / 2;
for (i = 0; i <>io_data);
break;
case DM9KS_DWORD_MODE:
tmplen = (rx.desc.length + 3) / 4;
for (i = 0; i <>io_data);
break;
}

/* Pass to upper layer */
skb->protocol = eth_type_trans(skb,dev);
#if defined(CHECKSUM)
if (val == 0x01)
skb->ip_summed = CHECKSUM_UNNECESSARY;
#endif
netif_rx(skb);
dev->last_rx=jiffies;
db->stats.rx_packets++;
db->stats.rx_bytes += rx.desc.length;
db->cont_rx_pkt_cnt++;

if (db->cont_rx_pkt_cnt>=CONT_RX_PKT_CNT)
{
dmfe_tx_done(0);
break;
}
}

}while((rxbyte & 0x01) == DM9KS_PKT_RDY);
DMFE_DBUG(0, "[END]dmfe_packet_receive()", 0);

}
/*
Read a word data from SROM
*/
static u16 read_srom_word(board_info_t *db, int offset)
{
iow(db, DM9KS_EPAR, offset);
iow(db, DM9KS_EPCR, 0x4);
udelay(200);
iow(db, DM9KS_EPCR, 0x0);
return (ior(db, DM9KS_EPDRL) + (ior(db, DM9KS_EPDRH) << db =" (board_info_t">priv;
struct dev_mc_list *mcptr = dev->mc_list;
int mc_cnt = dev->mc_count;
u32 hash_val;
u16 i, oft, hash_table[4];

DMFE_DBUG(0, "dm9000_hash_table()", 0);

((u8 *)dev->dev_addr)[0] = 0x00;
((u8 *)dev->dev_addr)[1] = 0x80;
((u8 *)dev->dev_addr)[2] = 0x48;
((u8 *)dev->dev_addr)[3] = 0x12;
((u8 *)dev->dev_addr)[4] = 0x34;
((u8 *)dev->dev_addr)[5] = 0x56;
/* Set Node address */
for (i = 0, oft = 0x10; i <>dev_addr[i]);

/* Clear Hash Table */
for (i = 0; i <>

/* broadcast address */
hash_table[3] = 0x8000;

/* the multicast address in Hash Table : 64 bits */
for (i = 0; i < mcptr =" mcptr-">next) {
hash_val = cal_CRC((char *)mcptr->dmi_addr, 6, 0) & 0x3f;
hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16); } /* Write the hash table to MAC MD table */ for (i = 0, oft = 0x16; i <>> 8) & 0xff);
}
}

/*
Calculate the CRC valude of the Rx packet
flag = 1 : return the reverse CRC (for the received packet CRC)
0 : return the normal CRC (for Hash Table index)
*/
static unsigned long cal_CRC(unsigned char * Data, unsigned int Len, u8 flag)
{

u32 crc = ether_crc_le(Len, Data);

if (flag)
return ~crc;

return crc;

}

/*
Read a byte from I/O port
*/
static u8 ior(board_info_t *db, int reg)
{
outb(reg, db->io_addr);
return inb(db->io_data);
}

/*
Write a byte to I/O port
*/
static void iow(board_info_t *db, int reg, u8 value)
{
outb(reg, db->io_addr);
outb(value, db->io_data);
}

/*
Read a word from phyxcer
*/
static u16 phy_read(board_info_t *db, int reg)
{
/* Fill the phyxcer register into REG_0C */
iow(db, DM9KS_EPAR, DM9KS_PHY | reg);

iow(db, DM9KS_EPCR, 0xc); /* Issue phyxcer read command */
udelay(100); /* Wait read complete */
iow(db, DM9KS_EPCR, 0x0); /* Clear phyxcer read command */

/* The read data keeps on REG_0D & REG_0E */
return ( ior(db, DM9KS_EPDRH) <<>

}

/*
Write a word to phyxcer
*/
static void phy_write(board_info_t *db, int reg, u16 value)
{
/* Fill the phyxcer register into REG_0C */
iow(db, DM9KS_EPAR, DM9KS_PHY | reg);

/* Fill the written data into REG_0D & REG_0E */
iow(db, DM9KS_EPDRL, (value & 0xff));
iow(db, DM9KS_EPDRH, ( (value >> 8) & 0xff));

iow(db, DM9KS_EPCR, 0xa); /* Issue phyxcer write command */
udelay(500); /* Wait write complete */
iow(db, DM9KS_EPCR, 0x0); /* Clear phyxcer write command */
}
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Davicom DM9000A/DM9010 ISA/uP Fast Ethernet Driver");
MODULE_PARM(mode, "i");
MODULE_PARM(irq, "i");
MODULE_PARM(iobase, "i");
MODULE_PARM_DESC(mode,"Media Speed, 0:10MHD, 1:10MFD, 4:100MHD, 5:100MFD");
MODULE_PARM_DESC(irq,"EtherLink IRQ number");
MODULE_PARM_DESC(iobase, "EtherLink I/O base address");

/* Description:
when user used insmod to add module, system invoked init_module()
to initilize and register.
*/
int init_module(void)
{
//set GPIO13 input, 2006-08-03
pxa_gpio_mode(9 | GPIO_IN);
set_irq_type(IRQ_GPIO(9), IRQT_BOTHEDGE);
printk("MSC0 = %x\n",MSC0);
printk("MSC1= %x\n",MSC1);
printk("MSC2 = %x\n",MSC2);
iobase = (unsigned long)ioremap(DM9KS_MIN_IO, 0x00100000);
switch(mode) {
case DM9KS_10MHD:
case DM9KS_100MHD:
case DM9KS_10MFD:
case DM9KS_100MFD:
media_mode = mode;
break;
default:
media_mode = DM9KS_AUTO;
}

dmfe_dev = dmfe_probe1();
if(IS_ERR(dmfe_dev))
return PTR_ERR(dmfe_dev);
return 0;
}
/* Description:
when user used rmmod to delete module, system invoked clean_module()
to un-register DEVICE.
*/
void cleanup_module(void)
{
struct net_device *dev = dmfe_dev;
DMFE_DBUG(0, "clean_module()", 0);

unregister_netdev(dmfe_dev);
release_region(dev->base_addr, 2);
#if LINUX_VERSION_CODE <>

DMFE_DBUG(0, "clean_module() exit", 0);
}
//#endif

module_init(init_module);
module_exit(cleanup_module);

2010年10月28日 星期四

网卡驱动(CS8900)在ARM上的移植和测试

1.先以模块的方式
    驱动源码如下cs8900new.c(自己由源代码修改的,只保留)
#define VERSION_STRING "Cirrus Logic CS8900A driver for Linux (Modified for SMDK2410//SMDK2440)"

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/pm.h>
#include <linux/irq.h>
#include <linux/fs.h>
#include <asm/irq.h>

#include <mach/hardware.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/mach-types.h>
//#include "asm/arch/smdk2410.h" /* ++ */
#include "cs8900.h"
static struct net_device *dev_cs8900;
spinlock_t mylock;
#define MAX_EEPROM_SIZE        256
static inline u16 cs8900_read (struct net_device *dev,u16 reg)
{
    outw (reg,dev->base_addr + PP_Address);
    return (inw (dev->base_addr + PP_Da
ta));
}

static inline void cs8900_write (struct net_device *dev,u16 reg,u16 value)
{
    outw (reg,dev->base_addr + PP_Address);
    outw (value,dev->base_addr + PP_Data);
}

static inline void cs8900_set (struct net_device *dev,u16 reg,u16 value)
{
    cs8900_write (dev,reg,cs8900_read (dev,reg) | value);
}

static inline void cs8900_clear (struct net_device *dev,u16 reg,u16 value)
{
    cs8900_write (dev,reg,cs8900_read (dev,reg) & ~value);
}

static inline void cs8900_frame_read (struct net_device *dev,struct sk_buff *skb,u16 length)
{
    insw (dev->base_addr,skb_put (skb,length),(length + 1) / 2);
}

static inline void cs8900_frame_write (struct net_device *dev,struct sk_buff *skb)
{
    outsw (dev->base_addr,skb->data,(skb->len + 1) / 2);
}

static void cs8900_receive (struct net_device *dev)
{   
    u16 status,length;
    struct sk_buff *skb;

    status = cs8900_read (dev,PP_RxStatus);
    length = cs8900_read (dev,PP_RxLength);
    if (!(status & RxOK))    
        return;
    if ((skb = dev_alloc_skb (length + 2)) == NULL)
        return;
    skb->dev = dev;
    skb_reserve (skb,2);
   
    cs8900_frame_read (dev,skb,length);
    skb->protocol = eth_type_trans (skb,dev);
    netif_rx (skb);
}

static int cs8900_send_start (struct sk_buff *skb,struct net_device *dev)
{
    u16 status;   

    spin_lock_irq(&mylock);
    netif_stop_queue (dev);

    cs8900_write (dev,PP_TxCMD,TxStart (After5));
    cs8900_write (dev,PP_TxLength,skb->len);

    status = cs8900_read (dev,PP_BusST);

    if ((status & TxBidErr)) {
        spin_unlock_irq(&mylock);
        printk (KERN_WARNING "%s: Invalid frame size %d!\n",dev->name,skb->len);       
        return (1);
    }

    if (!(status & Rdy4TxNOW)) {
        spin_unlock_irq(&mylock);
        printk (KERN_WARNING "%s: Transmit buffer not free!\n",dev->name);
        return (1);
    }

    cs8900_frame_write (dev,skb);
    spin_unlock_irq(&mylock);
    dev_kfree_skb (skb);
    return (0);
}

static irqreturn_t cs8900_interrupt (int irq,void *id) /* ++ */
{   
    volatile u16 status;
    struct net_device *dev = (struct net_device *) id;
    while ((status = cs8900_read (dev, PP_ISQ))) {
        switch (RegNum (status)) {
        case RxEvent:
            cs8900_receive (dev);
            break;
        case TxEvent:
            netif_wake_queue (dev);
            break;
        case BufEvent:
            if ((RegContent (status) & TxUnderrun)) {
                netif_wake_queue (dev);
            }
            break;
        }
    }
    return IRQ_HANDLED;
}

static int cs8900_start (struct net_device *dev)
{
    int result;
    set_irq_type(dev->irq, IRQF_TRIGGER_RISING);
    cs8900_set (dev,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE);
    cs8900_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA);
    cs8900_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE);
    cs8900_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE);
    cs8900_set (dev,PP_LineCTL,SerRxON | SerTxON);
    cs8900_set (dev,PP_BusCTL,EnableRQ);
    udelay(200);
    if ((result = request_irq (dev->irq, &cs8900_interrupt, 0, dev->name, dev)) < 0) {
        printk (KERN_ERR "%s: could not register interrupt %d\n",dev->name, dev->irq);
        return (result);
    }
    netif_start_queue (dev);   
    return (0);
}

static int cs8900_stop (struct net_device *dev)
{
    /* disable ethernet controller */
    cs8900_write (dev,PP_BusCTL,0);
    cs8900_write (dev,PP_TestCTL,0);
    cs8900_write (dev,PP_SelfCTL,0);
    cs8900_write (dev,PP_LineCTL,0);
    cs8900_write (dev,PP_BufCFG,0);
    cs8900_write (dev,PP_TxCFG,0);
    cs8900_write (dev,PP_RxCTL,0);
    cs8900_write (dev,PP_RxCFG,0);

    /* uninstall interrupt handler */
    free_irq (dev->irq,dev);

    /* stop the queue */
    netif_stop_queue (dev);

    return (0);
}

int __init cs8900_probe(struct net_device *dev)
{
    int i, value;
    printk(VERSION_STRING);
    ether_setup (dev);
    dev->open               = cs8900_start;
    dev->stop               = cs8900_stop;
    dev->hard_start_xmit    = cs8900_send_start;
//define mac_addr  协议栈使用的
    dev->dev_addr[0] = 0x08;
    dev->dev_addr[1] = 0x00;
    dev->dev_addr[2] = 0x3e;
    dev->dev_addr[3] = 0x26;
    dev->dev_addr[4] = 0x0a;
    dev->dev_addr[5] = 0x5b;
    dev->if_port   = IF_PORT_10BASET;
    dev->base_addr = 0xe0000000 + 0x300;
    dev->irq = IRQ_EINT9;
    spin_lock_init(&mylock);
    dev_cs8900 = dev;
    if (request_region(dev->base_addr,16,dev->name) == NULL) {
        printk (KERN_ERR "%s: can't get I/O port address 0x%lx\n",dev->name,dev->base_addr);
         return (-EIO);
     }
    if ((value = cs8900_read (dev,PP_ProductID)) != EISA_REG_CODE) {//判断网卡是否存在 0x630e
        printk (KERN_ERR "%s: incorrect signature 0x%.4x\n",dev->name,value);
        return (-ENXIO);
    }
    cs8900_write (dev,PP_IntNum,0);//选择CS8900的0号中断有效
    for (i = 0; i < ETH_ALEN; i += 2)
            cs8900_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8));//这个MAC地址写到了PP_IA寄存器中,作用是地址过滤。
   
    return (0);
}

static int __init cs8900_init (void)
{
    struct net_device *dev;

    dev = alloc_etherdev(0);
    if(!dev)
    {
        printk("unable to alloc new ethernet\n");
        return -1;
    }
    strcpy(dev->name, "eth%d");
    dev->init = cs8900_probe;
    return(register_netdev(dev));
}

static void __exit cs8900_cleanup (void)
{   
    unregister_netdev(dev_cs8900);
    release_region(dev_cs8900->base_addr, 16);
    free_netdev(dev_cs8900);
}

MODULE_AUTHOR ("zhuwensheng");
MODULE_DESCRIPTION (VERSION_STRING);
MODULE_LICENSE ("GPL");

module_init (cs8900_init);
module_exit (cs8900_cleanup);


cs8900.h内容如下
#ifndef CS8900_H
#define CS8900_H

/*
 * linux/drivers/net/cs8900.h
 *
 * Author: Abraham van der Merwe <abraham at 2d3d.co.za>
 *
 * A Cirrus Logic CS8900A driver for Linux
 * based on the cs89x0 driver written by Russell Nelson,
 * Donald Becker, and others.
 *
 * This source code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 */

/*
 * Ports
 */

#define PP_Address        0x0a    /* PacketPage Pointer Port (Section 4.10.10) */
#define PP_Data            0x0c    /* PacketPage Data Port (Section 4.10.10) */

/*
 * Registers
 */

#define PP_ProductID        0x0000    /* Section 4.3.1   Product Identification Code */
#define PP_MemBase            0x002c    /* Section 4.9.2   Memory Base Address Register */
#define PP_IntNum            0x0022    /* Section 3.2.3   Interrupt Number */
#define PP_EEPROMCommand    0x0040    /* Section 4.3.11  EEPROM Command */
#define PP_EEPROMData        0x0042    /* Section 4.3.12  EEPROM Data */
#define PP_RxCFG            0x0102    /* Section 4.4.6   Receiver Configuration */
#define PP_RxCTL            0x0104    /* Section 4.4.8   Receiver Control */
#define PP_TxCFG            0x0106    /* Section 4.4.9   Transmit Configuration */
#define PP_BufCFG            0x010a    /* Section 4.4.12  Buffer Configuration */
#define PP_LineCTL            0x0112    /* Section 4.4.16  Line Control */
#define PP_SelfCTL            0x0114    /* Section 4.4.18  Self Control */
#define PP_BusCTL            0x0116    /* Section 4.4.20  Bus Control */
#define PP_TestCTL            0x0118    /* Section 4.4.22  Test Control */
#define PP_ISQ                0x0120    /* Section 4.4.5   Interrupt Status Queue */
#define PP_TxEvent            0x0128    /* Section 4.4.10  Transmitter Event */
#define PP_BufEvent            0x012c    /* Section 4.4.13  Buffer Event */
#define PP_RxMISS            0x0130    /* Section 4.4.14  Receiver Miss Counter */
#define PP_TxCOL            0x0132    /* Section 4.4.15  Transmit Collision Counter */
#define PP_SelfST            0x0136    /* Section 4.4.19  Self Status */
#define PP_BusST            0x0138    /* Section 4.4.21  Bus Status */
#define PP_TxCMD            0x0144    /* Section 4.4.11  Transmit Command */
#define PP_TxLength            0x0146    /* Section 4.5.2   Transmit Length */
#define PP_IA                0x0158    /* Section 4.6.2   Individual Address (IEEE Address) */
#define PP_RxStatus            0x0400    /* Section 4.7.1   Receive Status */
#define PP_RxLength            0x0402    /* Section 4.7.1   Receive Length (in bytes) */
#define PP_RxFrame            0x0404    /* Section 4.7.2   Receive Frame Location */
#define PP_TxFrame            0x0a00    /* Section 4.7.2   Transmit Frame Location */

/*
 * Values
 */

/* PP_IntNum */
#define INTRQ0            0x0000
#define INTRQ1            0x0001
#define INTRQ2            0x0002
#define INTRQ3            0x0003

/* PP_ProductID */
#define EISA_REG_CODE    0x630e
#define REVISION(x)        (((x) & 0x1f00) >> 8)
#define VERSION(x)        ((x) & ~0x1f00)

#define CS8900A            0x0000
#define REV_B            7
#define REV_C            8
#define REV_D            9

/* PP_RxCFG */
#define Skip_1            0x0040
#define StreamE            0x0080
#define RxOKiE            0x0100
#define RxDMAonly        0x0200
#define AutoRxDMAE        0x0400
#define BufferCRC        0x0800
#define CRCerroriE        0x1000
#define RuntiE            0x2000
#define ExtradataiE        0x4000

/* PP_RxCTL */
#define IAHashA            0x0040
#define PromiscuousA    0x0080
#define RxOKA            0x0100
#define MulticastA        0x0200
#define IndividualA        0x0400
#define BroadcastA        0x0800
#define CRCerrorA        0x1000
#define RuntA            0x2000
#define ExtradataA        0x4000

/* PP_TxCFG */
#define Loss_of_CRSiE    0x0040
#define SQErroriE        0x0080
#define TxOKiE            0x0100
#define Out_of_windowiE    0x0200
#define JabberiE        0x0400
#define AnycolliE        0x0800
#define T16colliE        0x8000

/* PP_BufCFG */
#define SWint_X            0x0040
#define RxDMAiE            0x0080
#define Rdy4TxiE        0x0100
#define TxUnderruniE    0x0200
#define RxMissiE        0x0400
#define Rx128iE            0x0800
#define TxColOvfiE        0x1000
#define MissOvfloiE        0x2000
#define RxDestiE        0x8000

/* PP_LineCTL */
#define SerRxON            0x0040
#define SerTxON            0x0080
#define AUIonly            0x0100
#define AutoAUI_10BT    0x0200
#define ModBackoffE        0x0800
#define PolarityDis        0x1000
#define L2_partDefDis    0x2000
#define LoRxSquelch        0x4000

/* PP_SelfCTL */
#define RESET            0x0040
#define SWSuspend        0x0100
#define HWSleepE        0x0200
#define HWStandbyE        0x0400
#define HC0E            0x1000
#define HC1E            0x2000
#define HCB0            0x4000
#define HCB1            0x8000

/* PP_BusCTL */
#define ResetRxDMA        0x0040
#define DMAextend        0x0100
#define UseSA            0x0200
#define MemoryE            0x0400
#define DMABurst        0x0800
#define IOCHRDYE        0x1000
#define RxDMAsize        0x2000
#define EnableRQ        0x8000

/* PP_TestCTL */
#define DisableLT        0x0080
#define ENDECloop        0x0200
#define AUIloop            0x0400
#define DisableBackoff    0x0800
#define FDX                0x4000

/* PP_ISQ */
#define RegNum(x) ((x) & 0x3f)
#define RegContent(x) ((x) & ~0x3d)

#define RxEvent            0x0004
#define TxEvent            0x0008
#define BufEvent        0x000c
#define RxMISS            0x0010
#define TxCOL            0x0012

/* PP_RxStatus */
#define IAHash            0x0040
#define Dribblebits        0x0080
#define RxOK            0x0100
#define Hashed            0x0200
#define IndividualAdr    0x0400
#define Broadcast        0x0800
#define CRCerror        0x1000
#define Runt            0x2000
#define Extradata        0x4000

#define HashTableIndex(x) ((x) >> 0xa)

/* PP_TxCMD */
#define After5            0
#define After381        1
#define After1021        2
#define AfterAll        3
#define TxStart(x) ((x) << 6)

#define Force            0x0100
#define Onecoll            0x0200
#define InhibitCRC        0x1000
#define TxPadDis        0x2000

/* PP_BusST */
#define TxBidErr        0x0080
#define Rdy4TxNOW        0x0100

/* PP_TxEvent */
#define Loss_of_CRS        0x0040
#define SQEerror        0x0080
#define TxOK            0x0100
#define Out_of_window    0x0200
#define Jabber            0x0400
#define T16coll            0x8000

#define TX_collisions(x) (((x) >> 0xb) & ~0x8000)

/* PP_BufEvent */
#define SWint            0x0040
#define RxDMAFrame        0x0080
#define Rdy4Tx            0x0100
#define TxUnderrun        0x0200
#define RxMiss            0x0400
#define Rx128            0x0800
#define RxDest            0x8000

/* PP_RxMISS */
#define MissCount(x) ((x) >> 6)

/* PP_TxCOL */
#define ColCount(x) ((x) >> 6)

/* PP_SelfST */
#define T3VActive        0x0040
#define INITD            0x0080
#define SIBUSY            0x0100
#define EEPROMpresent    0x0200
#define EEPROMOK        0x0400
#define ELpresent        0x0800
#define EEsize            0x1000

/* PP_EEPROMCommand */
#define EEWriteEnable   0x00F0
#define EEWriteDisable  0x0000
#define EEWriteRegister    0x0100
#define EEReadRegister    0x0200
#define EEEraseRegister    0x0300
#define ELSEL            0x0400

#endif    /* #ifndef CS8900_H */

Makefile 内容如下:
ifneq ($(KERNELRELEASE),)
obj-m :=drive.o
else
KERNEL SRC := /usr/src/linux-headers-2.6.28-18-generic
modules:
    make -C $(KERNEL SRC) SUBDIRS=$(PWD) $@
clean:
    rm -f *.o *.ko *.mod.c .*.cmd  *~ *.order *.symvers *.markers
endif
SRC 路径说明:以上的路径是PC机上LINUX源代码路径,这样编译出来的是在PC上运行的(其他驱动一样),要编译成在ARM上运行的模块,SRC改为 /xxx/xxx,代表的是编译成能在ARM上运行的kernel后的内核源代码目录,如我的是/home/akaedu/linux-2.6.27,这 样再执行make编译驱动模块,生成可以插入ARM上内核的模块cs8900.ko
 (2)安装文件包lrzsz,  sudo apt-get install lrzsz
配置minicom,启动开发板,从nand 启动,首先要保证文件nand上的文件系统是可写的,如yaffs,而cramfs文件系统是只读文件系统。把cs8900.ko复制到家目录下(我的是/home/akaedu)

输入命令:rx cs8900.ko
此时minicom等待文件的输入,按ctrl+a,然后按z,再按s,选择xmodem,此时打开的是家目录,光标移到cs8900.ko,然后按空格键选中文件,再回车键,文件开始传输。传输结束后,在minicon上ls下,看到了文件cs8900.ko。
在minicom上执行命令 insmod cs8900.ko,打印了驱动程序第一行定义的宏,说明插入成功。

启动网卡并为它设置个IP,输入ifconfig eth0 192.168.0.210,此时网卡被激活,可输入ifconfig 查看是否正确。

测试能否ping通主机,
ping 192.168.0.5(主机IP)

(3)挂载主机上的目录到开发板
主机上安装nfs网络服务,sudo aot-get install nfs-kernel-server
重启nfs服务 ,sudo /etc/init.d/nfs-kernel-server restart

编辑主机上的 /etc/exports文件,加入主机上被挂载的目录,如/home/akaedu/root,格式如下:
/home/akaedu/root   *(rw,sync,no_root_squash)
*表示允许所有IP访问,括号里内容表示拥有读写,同步,和root权限。
然后在minicom上输入命令 :mount -t nfs -o nolock  192.168.0.5:/home/akaedu/root  /mnt
                                                                                      (主机IP)
这样挂载到了目标板上的mnt目录下,可以进入mnt目录查看是否成功。

(2)将驱动加入内核源代码并静态编译
将 cs8900new.c和cs8900.h(由于cs8900.h就来自这个目录,可以不用拷贝)放入内核源代码目录,如/home/akaedu /linux-2.6.27/drivers/net/arm,编辑这个目录下的Kconfig 和Makefile,参照原有的cs8900配置,在Kconfig 加入

config MY_OPTION
    tristate "my option"

config CS8900
    tristate "CS8900A support"
    depends on NET_PCI && (ISA || MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X || MACH_AKAE2440)
    ---help---
      Support for CS8900A chipset based Ethernet cards. If you have a
      network (Ethernet) card of this type, say Y and read the
      Ethernet-HOWTO, available from
      <http://www.tldp.org/docs.html#howto> as well as
      <file:Documentation/networking/cs89x0.txt>.

      To compile this driver as a module, choose M here. The module
      will be called cs89x0.

config NEW_CS8900
    tristate "my CS8900 support"
    depends on NET_PCI && (ISA || MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X || MACH_AKAE2440)&&MY_OPTION



嵌入式Linux系统移植 – RTC

移植步骤:
将前面生成的内核加载到开发板中的时候,我们可以发现以下的内容:
s3c2410-wdt s3c2410-wdt: watchdog inactive, reset disabled, irq enabled
TCP cubic registered
NET: Registered protocol family 1
drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
IP-Config: Complete:
device=eth0, addr=10.12.33.38, mask=255.255.255.0, gw=10.12.33.254,
红色字体部分表示S3C2410的RTC支持已经在kernel中,但是没有将rtc device加入SMDK2410 targetboard的device列表中,所以我们需要将RTC加入初始化的设备列表中。
修改文件arch/arm/mach-s3c2410/mach-smdk2410.c,在数组smdk2410_devices[]中添加&s3c_device_rtc:
rookiesean@rookiesean-desktop:~/workspace/linux-2.6.23.8$ cd arch/arm/mach-s3c2410/
rookiesean@rookiesean-desktop:~/workspace/linux-2.6.23.8/arch/arm/mach-s3c2410$ vim mach-smdk2410.c
static struct platform_device *smdk2410_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
&s3c_device_rtc, // sean chi 20Aug2010, added.
};
static void __init smdk2410_map_io(void)
{
s3c24xx_init_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc));
重新编译内核后,加载内核到开发板,发现以下内容:
S3C24XX RTC, (c) 2004,2006 Simtec Electronics
s3c2410-rtc s3c2410-rtc: rtc disabled, re-enabling
s3c2410-rtc s3c2410-rtc: rtc core: registered s3c as rtc0
…………
NET: Registered protocol family 1
s3c2410-rtc s3c2410-rtc: hctosys: invalid date/time
IP-Config: Complete:
device=eth0, addr=10.12.33.38, mask=255.255.255.0, gw=10.12.33.254,
以上信息表示RTC设备已经初始化成功。红色字体问题表示从硬件时钟(hc)处读到的时间转换到系统时钟(sys)的过程发生错误,硬件时钟传递的 时间参数是一个不合法的时间参数(invalid date/time)。从内核函数int rtc_valid_tm(struct rtc_time *tm),可以看出,当year小于1970时,认为是时间 invalid,函数返回-EINVAL。
/*
* Does the rtc_time represent a valid date/time?
*/
int rtc_valid_tm(struct rtc_time *tm)
{
if (tm->tm_year < 70
|| ((unsigned)tm->tm_mon) >= 12
|| tm->tm_mday < 1
|| tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900)
|| ((unsigned)tm->tm_hour) >= 24
|| ((unsigned)tm->tm_min) >= 60
|| ((unsigned)tm->tm_sec) >= 60)
return -EINVAL;
return 0;
}
从根文件系统上运行hwclock可以看到,hwclock的初始值是小于1970的,也验证了上面所说出现invalid date/time的原因:
[root@armsys /]# hwclock
Wed Dec 31 23:59:59 1969 0.000000 seconds
所以我们需要设置正确的系统时间,然后再把系统时间传递给RTC:
[root@armsys /]# date 082013342010.0
Fri Aug 20 13:34:00 UTC 2010
[root@armsys /]# hwclock -w
[root@armsys /]# hwclock
Fri Aug 20 13:34:42 2010 0.000000 seconds
为了使系统时间和RTC时间同步,可以在初始化文件中添加命令
hwclock –s
使每次开机时读取RTC时间,并同步给系统时间。
[root@armsys /]# vi /etc/init.d/rcS
#! /bin/sh
echo “Processing etc/init.d/rc.s”
#hostname ${HOSTNAME}
echo “Mount all”
/bin/mount -a
echo “Start mdev….”
/bin/echo /sbin/mdev >proc/sys/kernel/hotplug
mdev -s
echo “Set RTC…”
/sbin/hwclock -s
echo “******************************************”
echo ” rookiesean’s rootfs ”
echo ” 8Aug2010 ”
echo “******************************************”
echo
搞定。

引用自
http://blog.rookiesean.com/2010/08/29/%E5%B5%8C%E5%85%A5%E5%BC%8Flinux%E7%B3%BB%E7%BB%9F%E7%A7%BB%E6%A4%8D-rtc/

這個網站裡的東西都可以參考
http://blog.rookiesean.com/

嵌入式Linux系统移植-添加CS8900支持

宿主机环境:
Linux version 2.6.32-24, Ubuntu10.04
gcc version 4.1.3, Thread model: posix;
GNU Make 3.81
arm-linux-gcc 3.4.5

开发板环境:
CPU: S3C2410X
SDRAM: HY57V561620(32MB)
FLASH: K9F1208(64MB)
NET: CS8900
Linux Kernel: 2.6.23.8

所需源文件:
cs8900.tar.gz

移植步骤:
下载上面所给出的cs8900.tar.bz2文件,解压缩到drivers/net/arm/目录下面。
rookiesean@rookiesean-desktop:~/workspace/linux-2.6.23.8$ cd drivers/net/arm/
rookiesean@rookiesean-desktop:~/workspace/linux-2.6.23.8/drivers/net/arm$ tar xvf /home/rookiesean/Downloads/cs8900.tar.bz2
在/drivers/net/arm/Kconfig中增加menu config中CS8900编译选项。
rookiesean@rookiesean-desktop:~/workspace/linux-2.6.23.8/drivers/net/arm$ vim Kconfig
config ARM_CS8900
tristate “CS8900 support (sean chi 18Aug2010, added)”
depends on NET_ETHERNET && ARM && ARCH_SMDK2410
help
Support for CS8900A chipset based Ethernet cards. If you have a
network card of this type, say Y and read the EthernetHOWTO, available
from as well as. To compile this driver as a module, choose M here and
read. The module will be called cs8900.o
在/drivers/net/arm/Makefile中添加如下内容:
rookiesean@rookiesean-desktop:~/workspace/linux-2.6.23.8/drivers/net/arm$ vim Makefile
obj-$(CONFIG_ARM_CS8900) += cs8900.o#sean chi 18Aug2010, added.
然后我们在include/asm-arm/arch-s3c2410/目录下面创建一个smdk2410.h的头文件。
rookiesean@rookiesean-desktop:~/workspace/linux-2.6.23.8$ cd include/asm-arm/arch-s3c2410/
rookiesean@rookiesean-desktop:~/workspace/linux-2.6.23.8/include/asm-arm/arch-s3c2410$ vim smdk2410.h
添加如下代码:
#define pSMDK2410_ETH_IO __phys_to_pfn(0×19000000)
#define vSMDK2410_ETH_IO 0xE0000000
#define SMDK2410_EHT_IRQ IRQ_EIN
修改arch/arm/mach-s3c2410/mach-smdk2410.c文件,包含新创建的smdk2410.h头文件,并在map_desc smdk2410_iodesc[]
中添加cs8900的对于的io空间映射:
rookiesean@rookiesean-desktop:~/workspace/linux-2.6.23.8$ cd arch/arm/mach-s3c2410/
rookiesean@rookiesean-desktop:~/workspace/linux-2.6.23.8/arch/arm/mach-s3c2410$ vi mach-smdk2410.c
#include <asm/plat-s3c24xx/devs.h>
#include <asm/plat-s3c24xx/cpu.h>
#include <asm/plat-s3c24xx/common-smdk.h>
#include <asm/arch/smdk2410.h> // sean chi 18Aug2010, added.
static struct map_desc smdk2410_iodesc[] __initdata = {
{ vSMDK2410_ETH_IO , pSMDK2410_ETH_IO, SZ_1M, MT_DEVICE }, // sean chi 18Aug2010, added.
/* nothing here yet */
};
#define UCON S3C2410_UCON_DEFAULT
#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
执行menu config。 Device drivers->network device support->Ethernet中选择(*)CS8900 support。
编译内核make zImage。
通过uboot把zImage加载到SDRAM的0×30008000处,接着运行新的内核。
SMDK2410 # tftp 30008000 zImage.sean.cs8900
SMDK2410 # g 30008000
可以看到如下结果,表示添加对cs8900的支持成功。
dm9000 Ethernet Driver
Cirrus Logic CS8900A driver for Linux (Modified for SMDK2410)
eth0: CS8900A rev E at 0xe0000300 irq=53, addr: 00: 0:3E:26:0A: 0
Uniform Multi-Platform E-IDE driver Revision: 7.00alpha2
ide: Assuming 50MHz system bus speed for PIO modes; override with idebus=xx

引用自 
http://blog.rookiesean.com/2010/08/18/%E5%B5%8C%E5%85%A5%E5%BC%8Flinux%E7%B3%BB%E7%BB%9F%E7%A7%BB%E6%A4%8D-%E6%B7%BB%E5%8A%A0cs8900%E6%94%AF%E6%8C%81/

網卡驅動debug相關資料

驱动接口定义的变化

如:2.4内核中字符设备驱动的注册接口是

int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)

而2.6内核中已经不建议使用这种方法了,改为:

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

又如:2.6.27内核中网卡接口的net_device结构成员和低版本的net_device结构成员也发生了一些变化。

这种接口定义及注册方法带来的变化,发生的并不频繁。解决方案是:参考内核中的代码。这种接口定义及注册方法在内核中非常容易找到,如:字符设备驱动的注册方法及接口定义可以参照内核driver/char/目录下的很多实例。

http://blog.csdn.net/hongtao_liu/archive/2009/07/28/4386375.aspx

--------------------------------------------------------------------------------------

2、移植DM9000遇到的问题1(我已参照相关资料介绍添加了DM9000的驱动):

     前期移植是参照<嵌入式linux应用开发完全手册>441页来改的,

错误1:

     编译内核,下载内核启动后出现“IP-Config: Device `eth0' not found. ”信息。

     分析与解决:此错误说明dm9000未工作。可我确认已经添加驱动了呀,在这晕了半天,后来查资料得知可能是DM9000基地址设置有问题,原来我在移植时书上介绍的说是片选CS4的基地址就是DM9000的基地址。经查资料我的阳初2440开发板DM9000基地址为0x19000300与CS4基地址 0x20000000不同哦.问题根源找到了,修改结构体s3c_dm9k_resource中的DM9000的基址址即可....    这个结构体可能在arch/arm/plat-s3c24xx/common-smdk.c中(我的是这个位置,可用grep命令进行搜索)。

错误2:


       上面问题解决后又出现了“NETDEV WATCHDOG: eth0: transmit timed out” 错误(网卡已工作了),

       分析与解决:刚开始怀疑是NETDEV WATCHDOG的问题,于是把它关了,关后网卡就不工作了,更惨.....

于是就上网查找书查也没解决,后来想到我以前用别人配置的内核能运行 成功的,于是就拿来参照着看,原来是中断号设置有误,晕呀...

把中断改成IRQ_EINT9,OK啦哈!  通过 nfs挂我制作好的根文件系统 ,成功啦,好高兴,毕竟这次是我自己配置的哦。

下面是以上两个问题修改的数据结构s3c_dm9k_resource:

/* DM9000 */
static struct resource s3c_dm9k_resource[] = {
    [0] = {
        .start = S3C2410_CS4,       /* ADDR2=0,发送地址时使用这个地址 */
        .end   = S3C2410_CS4 + 3,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = S3C2410_CS4 + 4,   /* ADDR2=1,传输数据时使用这个地址 */
        .end   = S3C2410_CS4 + 4 + 3,
        .flags = IORESOURCE_MEM,
    },
    [2] = {
        .start = IRQ_EINT9,         /* 中断号 */
        .end   = IRQ_EINT9,
        .flags = IORESOURCE_IRQ,
    }

};

我已把S3C2410_CS4改为0x19000300了。

附加:

S3C2410_CS4在内核中的位置可通过 grep命令来查找.

方法:在终端通过cd命令进到内核目录,然后执行如下命令即可找到包含该字样的所有文件。

$grep  "S3C2410_CS4" * -R   //*表示查找当前目录下的所有文件、目录,-R表示递归查找子目录。


别笑哦,我今天才学会用这个命令查找 ,用着特爽...

------------------------------------------------------------------------------------