2010年10月31日 星期日
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 網卡驅動程式
首先先完成硬體線路,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_exit(cleanup_module);
2010年10月28日 星期四
网卡驱动(CS8900)在ARM上的移植和测试
驱动源码如下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
}
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_Da
}
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->da
}
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_CO
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 co
* 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_Da
/*
* Registers
*/
#define PP_ProductID 0x0000 /* Section 4.3.1 Product Identification Co
#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 Da
#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_CO
#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 On
#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红色字体部分表示S3C2410的RTC支持已经在kernel中,但是没有将rtc device加入SMDK2410 targetboard的device列表中,所以我们需要将RTC加入初始化的设备列表中。
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,
修改文件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以上信息表示RTC设备已经初始化成功。红色字体问题表示从硬件时钟(hc)处读到的时间转换到系统时钟(sys)的过程发生错误,硬件时钟传递的 时间参数是一个不合法的时间参数(invalid date/time)。从内核函数int rtc_valid_tm(struct rtc_time *tm),可以看出,当year小于1970时,认为是时间 invalid,函数返回-EINVAL。
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,
/*从根文件系统上运行hwclock可以看到,hwclock的初始值是小于1970的,也验证了上面所说出现invalid date/time的原因:
* 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;
}
[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在/drivers/net/arm/Makefile中添加如下内容:
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
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)修改arch/arm/mach-s3c2410/mach-smdk2410.c文件,包含新创建的smdk2410.h头文件,并在map_desc smdk2410_iodesc[]
#define vSMDK2410_ETH_IO 0xE0000000
#define SMDK2410_EHT_IRQ IRQ_EIN
中添加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>执行menu config。 Device drivers->network device support->Ethernet中选择(*)CS8900 support。
#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
编译内核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表示递归查找子目录。
别笑哦,我今天才学会用这个命令查找 ,用着特爽...
------------------------------------------------------------------------------------
2010年9月30日 星期四
安裝JDK & Eclipse
1.下载SDK
到Android Developers下载linux版本的SDK,要用代理,推荐Hotspot Shield,心中再次问候下伟大的GFW。
下载完成后解压,配置android sdk 环境变量:
$ sudo gedit /home/yourusername/.bashrc
添加下面这行代码(路径大家按照自己的存放目录修改):
export PATH=/home/yourusername/android-sdk-linux_x86-1.5_r1/tools:$PATH
2.配置Java开发环境
参见这里
1).下载安装JDK:
$ sudo apt-get install sun-java5-jdk
or
$ sudo apt-get install sun-java6-jdk
版本不同而已。推荐下载JDK5.0,目前系统镜像的编译不支持JDK6.0.
For Ubuntu 9.10(added 20100416)
由于Ubuntu 9.10上没有jdk5的源,所以不能直接安装,对此有两种方法可以解决:
方法1:从sun网站上下载安装
下载地址: http://java.sun.com/javase/downloads/5u21/jdk,个人倾向安装在 ~/bin目录下
安装:
$ cd ~/bin/
$ chmod 777 jdk-1_5_0_21-linux-i586.bin
$ ./jdk-1_5_0_21-linux-i586.bin
添加到环境变量:
$ sudo gedit ~/.bashrc
加入
export PATH=~/bin/jdk1.5.0_21/bin:$PATH
方法2:从Ubuntu 9.04 源安装
$ sudo gedit /etc/apt/sources.list
添加jdk5源:
deb http://us.archive.ubuntu.com/ubuntu/ jaunty multiverse
deb http://us.archive.ubuntu.com/ubuntu/ jaunty-updates multiverse
这里有另外一组源:
deb http://run.hit.edu.cn/ubuntu/ jaunty main restricted universe multiverse
deb-src http://run.hit.edu.cn/ubuntu/ jaunty main restricted universe multiverse
deb http://run.hit.edu.cn/ubuntu/ jaunty-updates main restricted universe multiverse
deb-src http://run.hit.edu.cn/ubuntu/ jaunty-updates main restricted universe multiverse
deb http://run.hit.edu.cn/ubuntu/ jaunty-backports main restricted universe multiverse
deb-src http://run.hit.edu.cn/ubuntu/ jaunty-backports main restricted universe multiverse
deb http://run.hit.edu.cn/ubuntu/ jaunty-security main restricted universe multiverse
deb-src http://run.hit.edu.cn/ubuntu/ jaunty-security main restricted universe multiverse
save the file and close gedit. Next sync your sources by running
更新源,安装:
$ sudo apt-get update
$ sudo apt-get install sun-java5-jdk
2).如果安装有多个JDK,需要设置当前默认Java解释器:
sudo update-alternatives –config java
3).配置环境变量(可选):
sudo gedit /etc/environment
添加如下两行:
CLASSPATH=.:/usr/lib/jvm/java-6-sun/lib
JAVA_HOME=/usr/lib/jvm/java-6-sun
sudo gedit /etc/jvm
在下面的代码添加到顶部:
/usr/lib/jvm/java-6-sun
设置完成后可以在终端输入java -version测试一下。
3.创建AVD(android virtual device)
android 1.5 中,emulator是不可以直接使用的,必须先创建avd才能使用emulator,
1).查看当前支持版本(在列出的版本中我们需要记住id值,这个值在第2步中使用):
android list targets
2).创建AVD(最后的2表示创建1.5版本的avd):
android create avd –name youravdname –target 2
3).查看是否创建成功(如果成功会显示刚才我们创建的avd信息):
android list avd
4).启动模拟器:
emulator-avd youravdname
(在模拟器中打开中文输入法:Menu->Settings->Local & text->android keyboard,将android keyboard后面的勾去掉就可以了)
4.在Eclipse中搭建android开发环境
1).到http://www.eclipse.org/downloads/下载Eclipse IDE,有JavaEE,Java和RCP,这里选择 Eclipse IDE for Java EE Developers for Linux 32bit下载,如果用的是 x86_64系统的,请选择 Linux 64bit,下载完成后解压即可。
2).安装 Android Eclipse开发插件
在Eclipse内依次选择 Help–Software Updates…项,转换到 Available Software标签,然后单击“ Add Site…”项,在地址文本框中输入http://dl-ssl.google.com/Android/eclipse/, 然后单击“Install”按钮。
返 回 到 “Available Software”页签项 , 选 择“Developer Tools”(这应该是自动检查Android Development Tools和Android Editors),然后单击“ Install…”项。
在安装的界面中,确认“Android Development Tools”和“Android Editors”已经检查,然后单击“ Next”按钮,单击“Finish”按钮,完成安装。
5.设置SDK路径:
在Eclipse中导入android程序后,必须设置sdk版本(工程/属性),否则会出现错误。
android sdk linux 1.5 安装与配置
1.下载android sdk
下载地址如下:
http://dl.google.com/android/android-sdk-windows-1.5_r2.zip
http://dl.google.com/android/android-sdk-linux_x86-1.5_r2.zip
2.下载eclipse 3.4.2
按官方文档说,android sdk 可以运行在eclipse 3.3以上的IDE中,ubuntu源中的eclipse版本过低,建议使用eclipse 3.4.2,其下载地址为:
http://download.actuatechina.com/eclipse/eclipse/downloads/drops/R-3.4.2-200902111700/eclipse-SDK-3.4.2-linux-gtk.tar.gz
3.安装eclipse 3.4.2
先将eclipse解压:
$tar xzvf eclipse-SDK-3.4.2-linux-gtk.tar.gz
将解压出的eclipse文件夹移动到目标目录中去,在这里我设定的安装目标为:/usr/local下。
$sudo mv eclipse /usr/local/
4.安装android sdk
解压android sdk包:
$unzip android-sdk-linux_x86-1.5_r2.zip
移动到目标目录:<在这里我将解压出的sdk文件夹目录改成了android-sdk>
$mv android-sdk-linux_x86-1.5_r2 android-sdk
$sudo mv android-sdk /usr/local/
5.配置环境变量:
修改~/.bashrc文件,加入android sdk与eclipse的环境变量。
$vi ~/.bashrc
在文件的最后加入
export PATH=/usr/local/android-sdk/tools:/usr/local/eclipse:$PATH
保存并退出vi
使配置信息生效
$source ~/.bashrc
6.android avd配置信息的生成
android avd的创建格式为:android create avd -n your-avd-name -t your-targets
其中:-n的参数为我们要创建的avd的名字;-t的参数为Available Android targets的id,我们可以使用命令:android list查寻其具体信息。
$android list
Available Android targets:
id: 1
Name: Android 1.1
Type: Platform
API level: 2
Skins: HVGA-P, HVGA (default), QVGA-P, HVGA-L, QVGA-L
id: 2
Name: Android 1.5
Type: Platform
API level: 3
Skins: HVGA-P, HVGA (default), QVGA-P, HVGA-L, QVGA-L
id: 3
Name: Google APIs
Type: Add-On
Vendor: Google Inc.
Description: Android + Google APIs
Based on Android 1.5 (API level 3)
Libraries:
* com.google.android.maps (maps.jar)
API for Google Maps
Skins: QVGA-P, HVGA-L, HVGA (default), QVGA-L, HVGA-P
Available Android Virtual Devices:
下面我们就以创建一个android 1.5的avd为例:
$android create avd -n android-sdk-15 -t 2
Android 1.5 is a basic Android platform.
Do you wish to create a custom hardware profile [no]yes
Device ram size: The amount of physical RAM on the device, in megabytes.
hw.ramSize [96]:128
Touch-screen support: Whether there is a touch screen or not on the device.
hw.touchScreen [yes]:yes
Track-ball support: Whether there is a trackball on the device.
hw.trackBall [yes]:yes
Keyboard support: Whether the device has a QWERTY keyboard.
hw.keyboard [yes]:yes
DPad support: Whether the device has DPad keys
hw.dPad [yes]:yes
GSM modem support: Whether there is a GSM modem in the device.
hw.gsmModem [yes]:yes
Camera support: Whether the device has a camera.
hw.camera [no]:
Camera support: Whether the device has a camera.
hw.camera [no]:yes
Maximum horizontal camera pixels
hw.camera.maxHorizontalPixels [640]:854
Maximum vertical camera pixels
hw.camera.maxVerticalPixels [480]:480
GPS support: Whether there is a GPS in the device.
hw.gps [yes]:yes
Battery support: Whether the device can run on a battery.
hw.battery [yes]:yes
Accelerometer: Whether there is an accelerometer in the device.
hw.accelerometer [yes]:yes
Audio recording support: Whether the device can record audio
hw.audioInput [yes]:yes
Audio playback support: Whether the device can play audio
hw.audioOutput [yes]:yes
SD Card support: Whether the device supports insertion/removal of virtual SD Cards.
hw.sdCard [yes]:yes
Cache partition support: Whether we use a /cache partition on the device.
disk.cachePartition [yes]:yes
Cache partition size
disk.cachePartition.size [66MB]:100MB
Created AVD ''android-sdk-15'' based on Android 1.5
这样我们就创建了一个基于1.5的avd,上面的具体意思我想大家都能看懂,就不多说了。下在我们就可以测试一下android emulator了:
$emulator @android-sdk-15 -show-kernel
这样就可以启动android emulator了:
加入参数-show-kernel是为了在终端上打印android kernel的信息。
2010年9月29日 星期三
參考
SDK preparation
$ cd working-directory-name
$ repo init -u git://android.git.kernel.org/platform/manifest.git (the latest), or
export ANT_HOME=/home/xxx/ Download /apache-ant-1.7.0
export PATH=$ ANDROID_SDK/tools: $JAVA_HOME/bin:$ANT_HOME/bin:$PATH
cd ~/android-kernel
> cd common
> git branch goldfish origin/android-goldfish-2.6.27
> git checkout goldfish
ramdisk.img、system.img、userdata.img、 system、 data、root