当前位置:文档之家› 2410平台上dm9000a网卡驱动分析

2410平台上dm9000a网卡驱动分析

2410平台上dm9000a网卡驱动分析
2410平台上dm9000a网卡驱动分析

2410平台上dm9000a网卡驱动分析该驱动基于linux-2.6.24.4内核。

首先,需要在arch/arm/mach-s3c2410/mach-smdk2410.c文件中添加如下代码:

注意上面的start、end等地址是指的网卡的物理地址。然后,还要在该文件中加入如下代码:

需要特别注意上面的name字段,当设备驱动程序寻找设别资源时,会根据该字段对设备进行匹配。另外,该文件中的smdk2410_devices[]数组中,还需要加入s3c_device_dm9000,不然系统启动时没有找到该资源就不会调用相应的probe函数。

下面分析驱动程序的probe函数。若驱动被编译进内核,则在系统启动的时候,该函数会被调用。该函数的源代码如下:

int ret =-ENODEV;

ndev = alloc_etherdev(sizeof(struct board_info));

if(!ndev){

printk("%s: could not allocate device.\n", CARDNAME);

return-ENOMEM;

}

ndev->dma =(unsigned char)-1;

if(pdev->num_resources < 2 || pdev->num_resources > 3){

printk("DM9000: Wrong num of resources %d\n", pdev->num_resources);

ret =-ENODEV;

goto out;

}

base = pdev->resource[0].start;

ndev->irq = pdev->resource[1].start;

/*

* Request the regions.

*/

if(!request_mem_region(base, 4, ndev->name)){

ret =-EBUSY;

goto out;

}

addr = ioremap(base, 4);

if(!addr){

ret =-ENOMEM;

goto release_mem;

}

ret = dm9k_probe(ndev,(unsigned long)addr);

if(ret != 0){

iounmap(addr);

release_mem:

release_mem_region(base, 4);

out:

printk("%s: not found (%d).\n", CARDNAME, ret);

kfree(ndev);

}

return ret;

}

函数首先调用alloc_etherdev,该函数在include/linux/etherdevice.h中声明,其中有如下语句:

而alloc_etherdev_mq函数又定义在net/ethernet/eth.c中,如下:

可见,该函数只是用自己的参数来调用alloc_netdev_mq函数。alloc_netdev_mq函数定义在

net/core/dev.c中,原型如下:

关于该函数的说明:

/**

* alloc_netdev_mq - allocate network device

* @sizeof_priv: size of private data to allocate space for

* @name: device name format string

* @setup: callback to initialize device

* @queue_count: the number of subqueues to allocate

*

* Allocates a struct net_device with private data area for driver use

* and performs basic initialization. Also allocates subquue structs

* for each queue on the device at the end of the netdevice.

*/

可见,alloc_etherdev为设备驱动分配了私有数据空间,并对设备驱动做了一些初始化工作。

接下来,设备驱动将要检查设备的resources的数量,如果数量小于2或者大于3,则初始化函数自动返回,初始化失败。我们的设备驱动中,resources的数量为2:一个表示设备的IO地址,另一个是设备的中断号。

代码

分别得到设备的端口地址和中断号。

接下来,驱动程序将向系统申请io内存,从地址base开始,大小为4个字节。如果申请成功,接下来需要做的就是将地址重新映射,从地址base开始,长度为4个字节。这样做的原因主要是驱动程序一般不直接访问物理地址,而访问虚拟地址。地址重新映射成功后,就调用dm9k_probe函数进行设备初始化。

dm9k_probe函数的全部代码如下

int __init dm9k_probe(struct net_device *dev,unsigned long addr)

{

struct board_info *db;/* Point a board information structure */

u32 id_val;

u16 i, j;

int retval;

/* Search for DM9000 serial NIC */

PUTB(DM9KS_VID_L, addr);

id_val = GETB(addr + 2);/* Change offset to 2 ^^^^^ */

PUTB(DM9KS_VID_H, addr);

id_val |= GETB(addr + 2)<< 8;

PUTB(DM9KS_PID_L, addr);

id_val |= GETB(addr + 2)<< 16;

PUTB(DM9KS_PID_H, addr);

id_val |= GETB(addr + 2)<< 24;

if(id_val != DM9KS_ID && id_val != DM9010_ID){

/* Dm9k chip not found */

printk("dmfe_probe(): DM9000 not found. ID=%08X\n", id_val);

return-ENODEV;

}

printk(" I/O: %lx, VID: %x \n",addr, id_val);

/* Allocated board information structure */

memset(dev->priv, 0,sizeof(struct board_info));

db =(board_info_t *)dev->priv;

dmfe_dev = dev;

db->io_addr = addr;

db->io_data = addr + 2;/* Change offset to 2 ^^^^^ */

/* driver system function */

dev->base_addr = addr;

dev->irq = IRQ_EINT2;

dev->open=&dmfe_open;

dev->hard_start_xmit =&dmfe_start_xmit;

dev->watchdog_timeo = 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;

for(i=0,j=0x10; i<6; i++,j++)

{

db->srom[i]= ior(db, j);

}

/* Set Node Address */

for(i=0; i<6; i++)

dev->dev_addr[i]= db->srom[i];

retval = register_netdev(dev);

if(retval == 0){

/* now, print out the card info, in a short format.. */

printk("%s: at %#lx IRQ %d\n",

dev->name, dev->base_addr, dev->irq);

if(dev->dma !=(unsigned char)-1)

printk(" DMA %d\n", dev->dma);

if(!is_valid_ether_addr(dev->dev_addr)){

printk("%s: Invalid ethernet MAC address. Please "

"set using ifconfig\n", dev->name);

}else{

/* Print the Ethernet address */

printk("%s: Ethernet addr: ", dev->name);

for(i = 0; i < 5; i++)

printk("%2.2x:", dev->dev_addr[i]);

printk("%2.2x\n", dev->dev_addr[5]);

}

}

return 0;

}

函数首先调用PUTB来写dm9000a芯片,来看看PUTB的实现

可见,PUTB是直接使用的指针,而没有使用内核提供的write等函数,同样,GETB函数如下

注意,这里的地址都是虚拟地址,因为前面调用函数dm9k_probe时传递的addr时重新映射后的,而不是直接传送的物理地址。

具体操作涉及到dm9000a的硬件实现,做简单的说明。dm9000a有两个PORT,一个是INDEX PORT,另一个就是DATA PORT。具体访问哪一个是根据CMD引脚的信号来确定的:CMD为0,则访问INDEX,否则,访问DATA。访问寄存器之前,必须将寄存器的地址存放在INDEX PORT。

首先,驱动程序需要读芯片的ID。DM9000A的ID存放在四个不同的字节中,分别叫做Vendor ID和Product ID。将着四个字节读出来,组合后应该得到0x90000A46,如果读出来的ID与该值不相等,说明不是DM9000A 网卡,程序将返回,初始化失败。

读出ID相同后,就可以认为系统中存在dm9000a网卡了,接下来就开始进行其他初始化工作。主要工作

就是为net_device的成员指定功能函数,以便系统需要的时候进行调用。完成这些基本的工作后,就可以向系统注册设备了

注册完成,该函数就返回。probe函数剩下的就是对返回值的判断了,若注册成功,直接推出,probe完成;失败的话,还需要将ioremap过的地方ioumap掉,request_mem_region的地方release掉。

前面分析了dm9000a网卡的probe部分,接下来继续其他部分。

当用户在命令行下使用ifconfig等命令的时候,网卡设备将打开,系统将调用open函数。dm9000a的open函数如下

/* 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);/* wait 3 second */

//printk("i=%d Speed=%d\n",i,db->Speed);

/* set and active a timer process */

init_timer(&db->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;

}

函数首先向系统申请中断,利用内核提供的request_irq函数。该函数声明于include/linux/interrupt.h 中,而它的定于位于kernel/irq/manage.c中。关于它的说明和原型如下

* This call allocates interrupt resources and enables the

* interrupt line and IRQ handling. From the point this

* call is made your handler function may be invoked. Since

* your handler function must clear any interrupt the board

* raises, you must take care both to initialise your hardware

* and to set up the interrupt handler in the right order.

*

* Dev_id must be globally unique. Normally the address of the

* device data structure is used as the cookie. Since the handler

* receives this value it makes sense to use it.

*

* If your interrupt is shared you must pass a non NULL dev_id

* as this is required when freeing the interrupt.

*

* Flags:

*

* IRQF_SHARED Interrupt is shared

* IRQF_DISABLED Disable local interrupts while processing

* IRQF_SAMPLE_RANDOM The interrupt can be used for entropy

*

*/

int request_irq(unsigned int irq, irq_handler_t handler,

unsigned long irqflags,const char*devname,void*dev_id)

可见,传递给该函数的第一个参数是申请的中断号;第二个参数是一个函数指针,当发生相应的中断时,系统将调用该函数处理中断;第三个参数是中断的标志,可以是它说明文档中提到的三种中的任何一种,我们用的是IRQF_SHARED,表示中断可以共享;第四个参数就是设备的简称,可以在/proc/interrupts 列表中找到。

申请完成中断后,驱动程序设置了中断的类型,使用set_irq_type函数。该函数声明于

include/linux/interrupt.h,而定义于kernel/irq/chip.c,原型如下

第一个参数是中断号,第二歌参数是中断类型,表示上升沿触发、下降沿触发、高电平触发或者低电平触发。我们使用的是IRQ_TYPE_EDGE_RISING,即上升沿触发。

中断设置完成后,驱动程序需要对dm9000a芯片进行初始化,调用dmfe_init_dm9000函数,如下

static void dmfe_init_dm9000(struct net_device *dev)

{

board_info_t *db =(board_info_t *)dev->priv;

/* 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 */

/* 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 */

/* 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);

}

该函数中使用了iow函数,看一看它的实现

即将value写入reg表示的寄存器中。dmfe_init_dm9000函数的具体功能函数里面已经有注释,更详细的可以查看datasheet。dmfe_init_dm9000函数返回后,open函数还做了一些工作。首先,初始化一些设备变量

这些值在发送和接收数据的时候将会使用到,到讨论那些函数的时候将详细介绍。接下来,驱动程序需要为自己增加一个timer

timer的expires变量决定定时时间,当定时时间到的时候,就会执行function指定的函数。最后,使用add_timer()函数将初始化的timer插入挂起定时器全局队列。关于function指定的函数,将在后面说明。

最后,open函数调用netif_start_queue,该函数的定义位于include/linux/netdevice.h中

可见,该函数告诉系统可以使用hard_start_xmit进行数据发送了。

open函数到此就结束了。

前面讨论了probe函数和open函数,下面继续。

内核发送数据在底层是通过dmfe_start_xmit函数来实现的

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;

PUTB(DM9KS_MWCMD, db->io_addr);// Write data into SRAM trigger

switch(db->io_mode)

{

case DM9KS_BYTE_MODE:

for(i = 0; i < skb->len; i++)

PUTB((data_ptr[i]& 0xff), db->io_data);

break;

case DM9KS_WORD_MODE:

tmplen =(skb->len + 1)/ 2;

for(i = 0; i < tmplen; i++)

PUTW(((u16 *)data_ptr)[i], db->io_data);

break;

case DM9KS_DWORD_MODE:

tmplen =(skb->len + 3)/ 4;

该函数首先判断设备使用的模式,若speed为10,则发送的数据包个数最大为1,若speed为100,则最大个数为2.超过这两个值,函数立即返回。若不超过,则说明可以进行数据发送。然后是更新系统的统计信息。如果待发送包达到上限,则调用netif_stop_queue,告诉内核暂时停止内核与驱动程序间的数据传递。

这些完成后,就可以开始真正的数据传输了。先禁止dm9000a的所有中断,通过写它的Interrupt Mask Register来实现。然后将要传递的数据的长度信息写入TX Packet Length Register中。

需要注意的是char* data_ptr在这里不能理解为一个指向char变量的指针,而应该理解为一个char数组。根据芯片的硬件连接方式,选择字节、半字或者字的方式对数据进行发送。发送完成后,记录下时间戳,并释放skb的空间,然后允许dm9000a的中断,以便继续进行发送或者接收。

stop方法和open函数的方法作用相反,即停止网络设备

完成的工作主要有删除定时器、释放中断、调用netif_stop_queue()告诉内核停止内核与驱动程序之间的数据交换,最后使dm9000a网卡处于power-down模式。

下面分析一个重要的函数--中断处理函数

static irqreturn_t dmfe_interrupt(int irq,void*dev_id)

{

struct net_device *dev = dev_id;

board_info_t *db;

int int_status,i;

u8 reg_save;

/* A real interrupt coming */

db =(board_info_t *)dev->priv;

spin_lock(&db->lock);

/* Save previous register address */

reg_save = GETB(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; i++)/*wait link OK, waiting time =0.5s */

{

phy_read(db,0x1);

if(phy_read(db,0x1)& 0x4)/*Link OK*/

{

/* wait for detected Speed */

for(i=0; i<200;i++)

udelay(1000);

/* set media speed */

if(phy_read(db,0)&0x2000) db->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 */

PUTB(reg_save, db->io_addr);

spin_unlock(&db->lock);

return IRQ_HANDLED;

}

前面已经提到,注册中断的时候该函数作为request_irq()函数的第二个参数。发生中断时,系统将调用该函数进行相关处理。

函数首先需要获得自旋锁,然后将当前的寄存器地址保存下来,以便返回的时候继续进行被打断的作业;接着就是屏蔽所有的中断,读取中断状态寄存器并清除中断状态寄存器,然后就开始真正的中断处理了。

在进行中断处理之前,需要首先判断是发生了什么中断。有如下几种可能:连接状态改变、数据接收中断或者数据发送中断。连接状态改变的处理比较简单,就不讨论了。

首先看数据接收中断。当发生接收中断时,中断函数调用dmfe_packet_receive()函数

u8 rxbyte, val;

u16 i, GoodPacket, tmplen = 0, MDRAH, MDRAL;

u32 tmpdata;

rx_t rx;

u16 * ptr =(u16*)℞

u8* rdptr;

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 = GETB(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;

PUTB(DM9KS_MRCMD, db->io_addr);

/* Read packet status & length */

switch(db->io_mode)

{

case DM9KS_BYTE_MODE:

*ptr = GETB(db->io_data)+

(GETB(db->io_data)<< 8);

*(ptr+1)= GETB(db->io_data)+

(GETB(db->io_data)<< 8);

break;

case DM9KS_WORD_MODE:

*ptr = GETW(db->io_data);

*(ptr+1)= GETW(db->io_data);

break;

case DM9KS_DWORD_MODE:

tmpdata = GETL(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; iio_data);

break;

case DM9KS_WORD_MODE:

tmplen =(rx.desc.length + 1)/ 2;

for(i = 0; i < tmplen; i++)

GETW(db->io_data);

break;

case DM9KS_DWORD_MODE:

tmplen =(rx.desc.length + 3)/ 4;

for(i = 0; i < tmplen; i++)

GETL(db->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]=GETB(db->io_data);

break;

case DM9KS_WORD_MODE:

tmplen =(rx.desc.length + 1)/ 2;

for(i = 0; i < tmplen; i++)

((u16 *)rdptr)[i]= GETW(db->io_data);

break;

case DM9KS_DWORD_MODE:

tmplen =(rx.desc.length + 3)/ 4;

for(i = 0; i < tmplen; i++)

((u32 *)rdptr)[i]= GETL(db->io_data);

break;

}

/* Pass to upper layer */

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

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;

函数首先读出并保存Memory Data Read Address Register的值。然后有一个空的读操作,具体是为什么要加入这个操作我也不太清楚,希望知道的朋友说一下。在这个读操作后,读取数据端口的第一个字节。根据datasheet,数据包的开始四个字节分别是0x01、status、BYTE_COUNT low和BYTE_COUNT high。读出的第一个字节应该是0x01,为了验证,交给check_rx_ready函数处理,如果不相等,则break,函数退出;若相等则继续对数据包进行处理。

初步验证后,需要继续读取包的前四个字节的信息。由于操作模式不同,需要选择相应的操作模式进行读取。需要注意的是,在该函数的开始的声明部分,有u16 * ptr = (u16*)℞,即ptr是指向无符号16

位数的指针,故在读取的时候根据8位、16位或者32位方式,对结果需进行不同的移位处理,具体见源代码。

如上,ptr是指向rx这个变量的指针,rx是rx_t类型的结构

RX_DESC刚好四个字节,与网卡数据包头信息长度是一致的。读出的四个字节信息中,位于第二字节的status刚好存放到RX_DESC的status变量中。注意这里的status是RX Status Register中的值,RX Status Register的质地为0x06。所以,在接下来的代码中,程序首先是判断状态,看有没有错误。

若RX Status Register中有一位置位,则说明存在错误,故把标志GoodPacket设置位false,然后再具体检查错误原因。各个位表示的错误信息可以查看datasheet。

上面的检查决定了GoodPacket的值为true或者false,所以如果GoodPacket的值位false,函数直接将不需要的值读出来,然后调用continue,使函数退出do...while循环,开始下一个包的处理,否则,说明是GoodPacket,进行正常的读取操作。

到此,已经确定所接收到的包含有有效信息,可以向内核传送数据了。首先使用函数dev_alloc_skb函数来分配一个skbuff,dev_alloc_skb函数定义于include/linux/skbuff.h中

可见,函数分配了一段内存给用户。分配完成,就开始从网卡读取数据,并将数据传送给上层。代码比较简单,就不在逐一分析了。

上面已经分析到中断处理的接收中断处理函数,下面分析发送中断处理函数。

在前面,我们说到系统调用dmfe_start_xmit来进行数据发送,当数据发送完成后,将产生一个数据发送中断,由interrupt函数检测到该中断后,将调用dmfe_tx_done()进行处理

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;

nsr = ior(db, DM9KS_NSR);

if(nsr & 0x04) db->tx_pkt_cnt--;

if(nsr & 0x08) db->tx_pkt_cnt--;

if(db->tx_pkt_cnt < 0)

{

printk("[dmfe_tx_done] tx_pkt_cnt ERROR!!\n");

db->tx_pkt_cnt =0;

}

if(db->Speed == 10)

{if(db->tx_pkt_cnt < 1 ) netif_wake_queue(dev);}

else

{if(db->tx_pkt_cnt < 2 ) netif_wake_queue(dev);}

return;

该函数主要完成两个动作:一是把tx_pkt_cnt成员的值减少1,表示已经完成一个数据包的发送了;二是判断tx_pkt_cnt的值,根据speed的值,判断是否可以调用netif_wake_queue()函数来通知内核网卡可以接受新的数据发送任务了。

Android源代码结构分析

目录 一、源代码结构 (2) 第一层次目录 (2) bionic目录 (3) bootloader目录 (5) build目录 (7) dalvik目录 (9) development目录 (9) external目录 (13) frameworks目录 (19) Hardware (20) Out (22) Kernel (22) packages目录 (22) prebuilt目录 (27) SDK (28) system目录 (28) Vendor (32)

一、源代码结构 第一层次目录 Google提供的Android包含了原始Android的目标机代码,主机编译工具、仿真环境,代码包经过解压缩后,第一级别的目录和文件如下所示: . |-- Makefile (全局的Makefile) |-- bionic (Bionic含义为仿生,这里面是一些基础的库的源代码) |-- bootloader (引导加载器),我们的是bootable, |-- build (build目录中的内容不是目标所用的代码,而是编译和配置所需要的脚本和工具) |-- dalvik (JAVA虚拟机) |-- development (程序开发所需要的模板和工具) |-- external (目标机器使用的一些库) |-- frameworks (应用程序的框架层) |-- hardware (与硬件相关的库) |-- kernel (Linux2.6的源代码) |-- packages (Android的各种应用程序) |-- prebuilt (Android在各种平台下编译的预置脚本) |-- recovery (与目标的恢复功能相关) `-- system (Android的底层的一些库)

yaffs2文件系统制作

交叉编译器ARM-Linux-gcc4.1.2 开发板TX2440A Busybox-1.15.1.tar.bz2(在Linux中被称为瑞士军刀) mkyaffs2image工具 首先创建一个名字为root_2.6.31的文件夹,在其中创建如下文件夹 etc bin var dev home lib mnt proc root sbin sys tmp usr opt共14个文件夹 解压Busybox tar xjvf busybox 进入源目录,修改Makefile 第164行,CROSS_COMPILE=arm-linux- 第190行,ARCH=arm 执行#make men onfig进行配置 配置选项大部分都是保持默认的,只需要注意选择以下这几个选项,其他的选项都不用动:Busybox Setting---> Build Options---> [*]Build Busybox as a static binary(no shared libs) [*]Build with Large File Support(for accessing files>2GB) Installation Options--->

(./_install)Busybox installation prefix 进入这个选项,输入busybox的安装路径,如:../rootfs Busybox Library Tuning---> [*]vi-style line editing commands [*]Fancy shell prompts 要选择这个选项:“Fancy shell prompts”,否则挂载文件系统后,无法正常显示命令提示符:“[\u@\h\W]#” 配置完成以后 执行#make #make install 然后就会在上一级目录下生成rootfs文件夹,里面包含几个文件夹/bin/sbin/usr linuxrc 把这些文件全部复制到刚建好的root_2.6.31目录下, #cp–rf*../root_2.6.31 在dev目录下,创建两个设备节点: #mknod console c51 #mknod null c13 然后进入自己建立的etc目录 拷贝Busybox-1.15.2/examples/bootfloopy/etc/*到当前目录下。 #cp-r../../busybox-1.15.2/examples/bootfloopy/etc/*./ 包括文件:fstab init.d inittab profile

linux-2.6.18移植

Linux-2.6.18移植 有了我们的交叉编译环境和我们先前学的内核基础知识,下面我们就开始我们的内核移植了,我们所用的是博创的 S3C2410 。 关于 linux-2.6.18.tar.bz2 的下载网站先前我们说过,我们要先到该官方网站上去下载一个全新的内核。 [root@Binnary ~ ]# tar –jxvf linux-2.6.18.tar.bz2 [root@Binnary ~ ]# make mrproper 如果你是新下载的内核,那这一步就不用了。但如果你用的是别人移植好的内核,那最好在编译内核之前先清除一下中间文件,因为你们用来编译内核的交叉编译工具可能不同。 第一步:修改Makefile文件 将 改为 第二步:修改分区设置信息 我们要先在BootLoader中查看相应的分区信息 vivi>help 然后修改内核源码中的分区信息。分区信息文件在 a rch/arm/mach-s3c2410/common-smdk.c 将其中的

改为如下内容:

第三步:内核通过 BootLoader把数据写入NAND Flash,而vivi的ECC效验算法和内核的不同,内核的效验码是由NAND Flash控制器产生的,所以在此必须禁用NAND Flash ECC。所以我们就要修改 drivers/mtd/nand/s3c2410.c 这个文件。将 中的 chip->ecc.mode = NAND_ECC_SOFT ,改为如下 chip->ecc.mode = NAND_ECC_NONE。

只此一处。 第四步:下面是devfs的问题,因为2.6.12内核以后取消了devfs的配置选项,缺少了它内核会找不到mtdblock设备。所以我们需要修改 fs/Kconfig 文件,或者是从2.6.12的fs/Kconfig中拷贝下面几项到2.6.18的fs/Kconfig中去,我们采用修改的方法来完成。 修改 fs/Kconfig支持devfs 。 在Pseudo filesystems 主菜单的最后添加我们所要的内容。 第五步:文件系统的支持 Yaffs 文件系统 YAFFS文件系统简介 YAFFS,Yet Another Flash File System,是一种类似于JFFS/JFFS2的专门为Flash设计 的嵌入式文件系统。与JFFS相比,它减少了一些功能,因此速度更快、占用内存更少。 YAFFS和JFFS都提供了写均衡,垃圾收集等底层操作。它们的不同之处在于: (1)、JFFS是一种日志文件系统,通过日志机制保证文件系统的稳定性。YAFFS仅仅 借鉴了日志系统的思想,不提供日志机能,所以稳定性不如JAFFS,但是资源占用少。 (2)、JFFS中使用多级链表管理需要回收的脏块,并且使用系统生成伪随机变量决定 要回收的块,通过这种方法能提供较好的写均衡,在YAFFS中是从头到尾对块搜索, 所以在垃圾收集上JFFS的速度慢,但是能延长NAND的寿命。 (3)、JFFS支持文件压缩,适合存储容量较小的系统;YAFFS不支持压缩,更适合存 储容量大的系统。 YAFFS还带有NAND芯片驱动,并为嵌入式系统提供了直接访问文件系统的API,用 户可以不使用Linux中的MTD和VFS,直接对文件进行操作。NAND Flash大多采用 MTD+YAFFS的模式。MTD( Memory Technology Devices,内存技术设备)是对Flash 操作的接口,提供了一系列的标准函数,将硬件驱动设计和系统程序设计分开。 Yaffs 文件系统内核没有集成,可以对其主页下载: https://www.doczj.com/doc/fe18618879.html,/cgi-bin/viewcvs.cgi/#dirlist

Yaffs2文件系统中对NAND Flash磨损均衡的改进

Yaffs2文件系统中对NAND Flash磨损均衡的改进 摘要:针对以NAND Flash为存储介质时Yaffs2文件系统存在磨损均衡的缺陷,通过改进回收块选择机制,并在数据更新中引入冷热数据分离策略,从而改善NAND Flash的磨损均衡性能。实验借助Qemu软件建立Linux嵌入式仿真平台,从总擦除次数、最大最小擦除次数差值和块擦除次数标准差等方面进行对比。实验结果表明,在改进后的Yaffs2文件系统下NAND Flash的磨损均衡效果有明显提升,这有益于延长NAND Flash的使用寿命。 关键词: Yaffs2文件系统;NAND Flash;垃圾回收;冷热数据 0 引言 NAND Flash存储设备与传统机械磁盘相比,具有体积小、存储密度高、随机存储和读写能力强、抗震抗摔、功耗低等特点[1]。它被广泛用于智能手机、车载智能中心、平板电脑等智能终端中。近年来,以NAND Flash为存储介质的固态硬盘也得到越来越多的应用。目前Yaffs2文件系统(Yet Another Flash File System Two,Yaffs2)[1]是使用最多、可移植性最好的专用文件系统,在安卓、阿里云OS、Linux等嵌入式系统中都有使用。在Yaffs2文件系统下以NAND Flash为存储介质时存在磨损均衡的缺陷,可通过对回收块选择机制作改进和引入冷热数据分离策略来提高磨损均衡的效果。 1 Yaffs2和Nand Flash关系 这里以使用最多的Linux操作系统为实践,将Yaffs2文件系统移植到Linux操作系统中。Linux系统通常可以分为3层:应用层、内核层和设备层,其中支持NAND Flash设备的Yaffs2文件系统属于内核层,。 最上层用户应用程序通过VFS(Virtual File System)提供的统一接口,将数据更新等文件操作传递给Yaffs2。VFS代表虚拟文件系统,它为上层应用提供统一的接口。有了这些接口,应用程序只用遵循抽象后的访问规则,而不必理会底层文件系统和物理构成上的差异。然后Yaffs2通过MTD(Memory Technology Device)提供的统一访问接口对NAND Flash进行读、写和擦除操作,从而完成数据的更新或者存储操作。MTD代表内存技术设备,它为存储设备提供统一访问的接口。最终,在NAND Flash上以怎样的格式组织和存储数据由Yaffs2文件系统决定。 NAND Flash由若干块(block)组成,每个块又是由若干页(page)组成,页中含有数据区和附加区。NAND Flash的页根据状态不同,可以分为有效页、脏页、空闲页。有效页中存放有效数据,脏页中存放无效数据,空闲页是经过擦除后可以直接用于写入数据的页。NAND Flash在写入数据前需要执行擦除操作,因此数据不能直接在相同的位置更新。当一个页中数据需要更新时,必须将该页中有效数据拷贝到其他空闲页上再更新,并将原来页上的数据置为无效。随着时间的推移,许多无效页累积在存储器中使得空闲页逐渐减少。当存储器中的空闲空间不足时,启动垃圾回收操作,利用回收块选择机制从待回收块中选取满足要求的块来擦除,从而得到足够的空闲空间。NAND Flash中块的擦除次数有限,通常为10 000次~100 000次[2]。当某个块的擦除次数超过使用寿命时,该块将无法正常用于数据存储。因此,垃圾回收应利用合理的回收块选择机制,从待回收块中找到回收后能产生良好磨损均衡效果且付出较少额外代价的块来回收,从而获得足够的空闲空间用于数据更新操作。 2 Yaffs2在磨损均衡方面的缺陷 Yaffs2中回收块的选择机制[3]是从待回收块中找到有效数据最少的块来回收。回收过程中,Yaffs2能够减少有效数据的额外读和写操作。当数据更新处于均匀分布的情况下,Yaffs2表现出较好的磨损均衡效果。 但是,通常情况下数据的更新频率不同,有些数据经常更新,而有些数据很少更新。经

2-Linux

Linux-2.6.32.2内核在mini2440上的移植(二)---yaffs2文件系统移植 移植环境(红色粗字体字为修改后内容,蓝色粗体字为特别注意内容) 2.1, yaffs2文件系统移植 【1】获取yaffs2 源代码 现在大部分开发板都可以支持yaffs2 文件系统,它是专门针对嵌入式设备,特别是使用nand flash 作为存储器的嵌入式设备而创建的一种文件系统,早先的yaffs 仅支持小页(512byte/page)的nand flash,现在的开发板大都配备了更大容量的nand flash,它们一般是大页模式的(2K/page),使用yaffs2 就可以支持大页的nand flash,下面是yaffs2 的移植详细步骤。 在https://www.doczj.com/doc/fe18618879.html,/node/346可以下载到最新的yaffs2 源代码,需要使用git工具( 安装方法见Git版本控制软件安装与使用),在命令行输入: [root@localhost ~]# cd ./linux-test [root@localhost linux-test]# git clone git://https://www.doczj.com/doc/fe18618879.html,/ya ffs2 Cloning into yaffs2... remote: Counting objects: 6592, done. remote: Compressing objects: 100% (3881/3881), done. remote: Total 6592 (delta 5237), reused 3396 (delta 2642) Receiving objects: 100% (6592/6592), 3.34 MiB | 166 KiB/s, d one. Resolving deltas: 100% (5237/5237), done.

使用yaffs2img工具制作Android刷机包教程

制作刷机包 打开‘yaffs2img浏览器’,点击左上角的‘选取yaffs2文件’选择你刚刚复制出来的 files文件夹里的system.img 先来认识一下这个软件 1.定制软件的提取(此部和制作刷机包没关系,可以不做,想用官方软件的同学可以 看看) 选择app,右键你想要提取软件,提取就可以了,我是把整个app文件夹提取出来了,不用 的软件直接删掉好了 2.定制软件的精简 在你不想要用的软件上直接右键,删除,就好了,你也可以右键添加你想要用的软件,得把

软件改成比较简短的英文名,否则有可能不能用 秀一下我精简后的列表,大家可以参照着精简 https://www.doczj.com/doc/fe18618879.html,uncher文件的替换 下载好你想要用的桌面软件,改名为‘Launcher’,删掉app中的‘Launcher2’,添加进去你改好名字的‘Launcher’就好了,我比较喜欢ADW,所以我把ADW的文件名改为 Launcher,替换掉原来的Launcher2就好了 4.破音问题的解决 在左边导航点选‘etc’,右键添加文件,把附件中的声音配置文件解压出来 ‘AudioFilter.csv’添加进去就好了 AudioFilter.rar (355 Bytes)

5.字体的更改 下载字体文件,中文字体库一律把名字改名为‘DroidSans Fallback.ttf’,英文字体改为‘DroidSans.ttf ’,加粗的英文字体改为‘DroidSans-Bold.ttf ’然后再左边导航栏点选‘fonts’,把之前自带的字体删除,然后把你改好名字的字体添加进去就好了把国产机皇的字体也分享给大家,中文+英文+英文加粗 6.开机音乐和照相机音乐的删除 在导航栏点选‘media’,在audio/ui文件夹下,删除‘Bootsound.mp3’(开机音乐)和

第2天 linux系统的编译及镜像文件的制作

第2天linux系统的编译及镜像文件的制作 一般来说,linux系统分为几个映像。 一:bootload :一般常用的是 U-boot 二:内核映像:主要是linux内核编译成的映像比如TQ210开发板使用的zImage.bin 三:文件系统:有很多格式,比如 下面根据TQ210说明书进行讲解,大部分参考官方手册。以后自己修改源码可以在此基础上进行修改,修改完以后按照此步骤进行编译,编译完成后进行下载到开发板进行运行 1编译bootloader 1.1光盘中的 u-boot 源码的解压 先将光盘中的 u-boot 源码 ( 在光盘的“ TQ210_CD\bootloader\ ” 目录下 , 名为 uboot_TQ210_1.3.4_V1.1.tar.bz2)拷贝到 PC 的linux系统的根目录(这里说的根目录是本手册编写者的操 作和截图所拷贝的地方, 实际操作可以拷贝到任意目录下) 下, 然后使用命令#tar xvfjuboot_TQ210_1.3.4_V1.1.tar.bz2 -C /,解压源码,如下图所示 源码解压后,会在“/opt/EmbedSky/TQ210/uboot_TQ210_1.3.4/”目录下得到刚刚解压的源码。 1.2 光盘中的u-boot源码的编译 解压完成后,使用命令#make TQ210_config,配置u-boot,如下图所示:

使用命令#make,编译u-boot。编译结束后,在/opt/EmbedSky/TQ210/uboot_TQ210_1.3.4/目录下会得 到一个名字u-boot.bin的镜像,将其拷贝到Windows 或者拷贝到TFTP 服务器发送文件指定的目录中,就 可以烧写到开发板上面进行测试了(或者制作成SD 启动卡也可以测试)。如下两图所示:

S3C2440的Linux内核移植和yaffs2文件系统制作

L i n u x内核移植和根文件系统制作 第一章移植内核 (2) 1.1 Linux内核基础知识 (2) 1.1.1 Linux版本 (2) 1.1.2 什么是标准内核 (2) 1.1.3 Linux操作系统的分类 (3) 1.1.4 linux内核的选择 (4) 1.2 Linux内核启动过程概述 (5) 1.2.1 Bootloader启动过程 (5) 1.2.2 Linux启动过程 (7) 1.3 Linux内核移植 (10) 1.3.1 移植内核和根文件系统准备工作 (10) 1.3.2 修改Linux源码中参数 (11) 1.3.3 配置Linux内核 (15) 1.3.4、编译内核 (17) 第二章制作根文件系统 (19) 2.1 根文件系统预备知识 (19) 2.2、构建根文件按系统 (19) 2.2.1、建立根文件系统目录 (19) 2.2.2、建立动态链接库 (21) 2.2.3 交叉编译Bosybox (21) 2.2.4 建立etc目录下的配置文件 (24) 2.2.5 制作根文件系统映像文件 (26) 第三章启动系统 (27) 第四章总结 (34)

第一章移植内核 1.1 Linux内核基础知识 在动手进行Linux内核移植之前,非常有必要对Linux内核进行一定的了解,下面从Linux内核的版本和分类说起。 1.1.1 Linux版本 Linux内核的版本号可以从源代码的顶层目录下的Makefile中看到,比如2.6.29.1内核的Makefile中: VERSION = 2 PA TCHLEVEL = 6 SUBLEVEL = 29 EXTRA VERSION = .1 其中的“VERSION”和“PA TCHLEVEL”组成主版本号,比如 2.4、2.5、2.6等,稳定版本的德主版本号用偶数表示(比如2.6的内核),开发中的版本号用奇数表示(比如2.5),它是下一个稳定版本内核的前身。“SUBLEVEL”称为次版本号,它不分奇偶,顺序递增,每隔1~2个月发布一个稳定版本。“EXTRA VERSION”称为扩展版本号,它不分奇偶,顺序递增,每周发布几次扩展本版号。 1.1.2 什么是标准内核 按照资料上的习惯说法,标准内核(或称基础内核)就是指主要在https://www.doczj.com/doc/fe18618879.html,/维护和获取的内核,实际上它也有平台属性的。这些linux 内核并不总是适用于所有linux支持的体系结构。实际上,这些内核版本很多时候并不是为一些流行的嵌入式linux系统开发的,也很少运行于这些嵌入式linux 系统上,这个站点上的内核首先确保的是在Intel X86体系结构上可以正常运行,它是基于X86处理器的内核,如对linux-2.4.18.tar.bz2的配置make menuconfig 时就可以看到,Processor type and features--->中只有386、486、586/K5/5x86/6x86/6x86MX、Pentium-Classic、Pentium-MMX、Pentium-Pro/Celeron/Pentium-II、Pentium-III/Celeron(Coppermine)、Pentium-4、K6/K6-II/K6-III 、Athlon/Duron/K7 、Elan 、Crusoe、Winchip-C6 、Winchip-2 、

(课程总结)

《嵌入式系统案例分析与设计》 课程实验报告 班级: 学号: 姓名: 指导老师: 成绩:

嵌入式系统案例分析与设计课程实验报告 一、开发环境的构建 1. 交叉工具链arm-linux-gcc安装与设置 (1)首先在家目录下创建一个文件夹,然后继续在该文件夹目录下创建一个test文件夹,如下图所示: (2)将交叉编译工具链Linux4.6.1拷到test目录下并解压 解压: tar -xvf arm-linux-gcc-4.5.1-v6-vfp-20120301.tgz (3)添加环境变量 PATH 决定了shell将到哪些目录中寻找命令或程序 配置编译环境路径在控制台下输入vim /etc/profile 在文件最后一行添加下面语句:export PATH=$PATH:/opt/FriendlyARM/toolschain/4.5.1/bin (4)保存关闭后,重启,用root账号重新登录系统,刚刚添加的环境变量生效,在控制台输入:arm-linux-gcc -v 如果安装成功将会输出arm-linux-gcc的版本号。如下图所示,显示gcc version 4.5.1 ,表示交叉编译工具链安装成功。

2. 设置TFTP服务器 (1)运行下面的命令,安装TFTP服务器和客户端# apt-get install tftpd-hpa tftp-hpa (2)运行下面的命令,重启TFTP服务器 # service tftpd-hpa restart

(3)新建一个文件,并将其移动到TFTP服务器的默认上传下载目录 (4)从服务器上下载test.txt文件,并退出tftp程序 (5)运行如下命令,确认下载的文件内容正确 3. 设置NFS服务器 (1)安装NFS服务器 # apt-get install nfs-kernel-server (2)运行下面的命令,创建一个目录,并在该目录下创建一个文件mkdir /nfs chown root /nfs chgrp root /nfs mkdir /nfs/rootfs echo "nfs test" > /nfs/rootfs/test.txt (3)编辑/ect/exports配置文件。 gedit /etc/exports

yaffs2制作教程

Yaffs2根文件系统制作 环境: 交叉编译环境:4.3.3 (天嵌科技提供,存放路径/opt/EmbedSky/4.3.3)开发平台:TQ2440 1,编译busybox 获取busybox源码 busybox-1.17.2.tar (https://www.doczj.com/doc/fe18618879.html,/downloads/)置于目录/opt/embed下 #tar jxvf busybox-1.17.2.tar.bz2 #cd busybox-1.17.2 #vim Makefile 将164行改为CROSS_COMPILE = arm-linux- 将190行改为ARCH = arm 保存推出进入配置菜单 #make menuconfig 采用默认配置保存推出 #make #make install 在busybox-1.17.2的根目录下出现了一个_install目录在该目录下又有三个目录文件bin sbin usr 和一个链接文件 linuxrc 。 2,创建根文件系统必要的目录 回到/opt/embed目录下创建根文件系统必要的目录 #mkdir root_fs #cd root_fs 将刚才生成的三个目录bin sbin usr和一个链接文件linuxrc考到目录root_fs下

#cp -rf ../busybox-1.17.2/_install/* ./ #mkdir dev etc home lib mnt opt proc root sys tmp var 创建几个必要的二级目录 #mkdir usr/lib usr/share #mkdir etc/rc.d #mkdir var/lib var/lock var/run var/tmp 3,创建必要文件 (1), 获取库文件 (我的交叉编译工具链放在目录 /opt/EmbedSky/下的) #cp -rf /opt/EmbedSky/4.3.3/arm-none-linux-gn?i/libc/armv4t/lib/* so* lib -a (2),将主机 etc 目录下的passwd、group、shadow文件拷贝到 root_fs/etc 目录下 #cp -f /etc/passwd /etc/group /etc/shadow etc 将目录/opt/embed/busybox-1.17.2/examples/bootfloppy/etc下的所有文件拷贝到 root_fs/etc下。在这个目录下有三个文件fstab, inittab, profile和一个目录init.d, 在目录init.d中有一个文件rcS。 #cp -rf ../busybox-1.17.2/examples/bootfloppy/etc/* etc 在目录etc下创建文件mdev.conf。mdev是?v的一个简化版本,我们可以通过文件mdev.conf自定义一些设备节点的名称或链接来满足特定的需要,但在此处让它为空。 #touch etc/mdev.conf (3)创建两个设备文件dev/console dev/null。 在linux内核源码文件init/main.c中有打开设备文件dev/console 的操作如下:

Android 2.1 源码结构分析

Android 2.1 |-- Makefile |-- bionic (bionic C库) |-- bootable (启动引导相关代码) |-- build (存放系统编译规则及generic等基础开发包配置) |-- cts (Android兼容性测试套件标准) |-- dalvik (dalvik JAVA虚拟机) |-- development (应用程序开发相关) |-- external (android使用的一些开源的模组) |-- frameworks (核心框架——java及C++语言) |-- hardware (主要保护硬解适配层HAL代码) |-- out (编译完成后的代码输出与此目录) |-- packages (应用程序包) |-- prebuilt (x86和arm架构下预编译的一些资源) |-- sdk (sdk及模拟器) |-- system (文件系统库、应用及组件——C语言) `-- vendor (厂商定制代码) bionic 目录 |-- libc (C库) | |-- arch-arm (ARM架构,包含系统调用汇编实现) | |-- arch-x86 (x86架构,包含系统调用汇编实现) | |-- bionic (由C实现的功能,架构无关) | |-- docs (文档) | |-- include (头文件) | |-- inet (?inet相关,具体作用不明) | |-- kernel (Linux内核中的一些头文件) | |-- netbsd (?nesbsd系统相关,具体作用不明) | |-- private (?一些私有的头文件) | |-- stdio (stdio实现) | |-- stdlib (stdlib实现) | |-- string (string函数实现) | |-- tools (几个工具) | |-- tzcode (时区相关代码) | |-- unistd (unistd实现) | `-- zoneinfo (时区信息) |-- libdl (libdl实现,dl是动态链接,提供访问动态链接库的功能)|-- libm (libm数学库的实现,) | |-- alpha (apaha架构) | |-- amd64 (amd64架构) | |-- arm (arm架构) | |-- bsdsrc (?bsd的源码) | |-- i386 (i386架构) | |-- i387 (i387架构?)

《嵌入式操作系统》课程设计(DOC)

《嵌入式操作系统》课程设计指导书 专业:计算机科学与技术专业方向:计算机科学与技术 计算机科学与工程学院

第一章课程设计指导书 1、目的任务 本设计的目的在于使学生全面理解实践已学的相关课程内容,深刻理解嵌入式系统开发的全过程。从硬件的角度掌握嵌入式开发板的组成,接口部件的结构和与宿主机间的连接,从软件的角度掌握嵌入式系统软件开发的全过程。使学生通过自己的实践,初步了解和掌握一个实用嵌入式系统的开发步骤,综合应用所学的基础知识和编程手段独立完成嵌入式系统开发的基础内容。 2、设计内容 基础实验(必做)——嵌入式系统开发环境搭建 扩展实验(从以下三个实验中任选一个) 实验一——利用Autotools工具自动生成Makefile文件 实验二——使用BusyBox制作根文件系统 实验三——使用GTK+进行图形界面编程 3、时间安排 时间:第17周 第一天确定题目、查找相关资料,安装系统;第二到第四天为实验和程序设计、完善总结、撰写报告,第五天答辩。 4、工作要求 (1)爱护实验开发板,为避免烧坏开发板,对开发板的任何插拔工作,都必须在断电之后进行。 (2)明确实验要求和步骤,在进行实验之前详细阅读开发板配套手册和相关资料。

(3)在本设计过程中,学生应随时做实习笔记,记录每天的工作内容及结果,同时还应规划出次日的实习计划与解决问题的方案。 5、成绩评定 设计结束时,由指导老师对学生进行全面考核,评分按五级分制(优、良、中、及格、不及格)评定成绩,评分依据以下几个方面。 (1)平时成绩 包括遵守纪律情况,实习中的工作态度,实习日记的记录情况等。 (2)设计中实验结果的成绩 教师要把关,确认实验结果是由实验人做出的。如不能按设计要求做出实验结果的,要予以扣分;对于有创新的实验及结果者,应给予好的成绩。对于以下情况之一者,要严格进行处理。 ①. 照抄他人,自己没有消化者,应给予“不及格”。 ②. 严重迟到早退,应给予“不及格”。 ③. 实习不认真,违反实验室规定者,应给予“不及格”。 ④. 缺勤大于整个工作时间的25%以上者,应给予“不及格”。 ⑤. 实习报告不认真者,至少不能给“优”和“良”的成绩。 6、参考资料 (1)熊茂华、熊昕编著.嵌入式Linux实时操作系统及应用编程.清华大学出版社.2011年5月第1版 (2)深圳友坚恒天科技公司开发板配套光盘中用户手册: idea6410开发板linux使用手册 ubuntu-UserManual_v0.18 UT6410-Android2.1_manual (3)相关国嵌、申嵌视频资料

《嵌入式操作系统》实验报告

《嵌入式操作系统》实验报告 班级计算机 学号 姓名 指导教师庄旭菲

工业大学信息工程学院计算机系 2018年6月 实验一 Linux核移植与编译实验 1. 实验目的 ?了解Linux 核相关知识与核结构 ?了解Linux 核在ARM 设备上移植的基本步骤和方法 ?掌握Linux 核裁剪与定制的基本方法 2. 实验容 ?分析Linux 核的基本结构,了解Linux 核在ARM 设备上移植的一些基本步骤及常识。 ?学习Linux 核裁剪定制的基本配置方法,利用UP-Magic210 型设备配套Linux 核进行 自定义功能(如helloworld 显示)的添加,并重新编译核源码,生成核压缩文件zImage,下载到UP-Magic210 型设备中测试。 3. 实验步骤

实验目录:/UP-Magic210/SRC/kernel/linux-2.6.35.7/ 编译核:在宿主机端为UP-Magic210 设备的Linux 2.6.35.7 核编写简单的测试驱动(核)程序helloworld.c 并修改核目录中相关文件,添加对测试驱动程序的支持。 (1)、使用vim 编辑器手动编写实验代码helloworld.c helloworld.c 如如下: #include #include MODULE_LICENSE("Dual BSD/GPL"); //驱动程序入口函数 static int hello_init(void) { printk(KERN_ALERT "##############Hello, world############\n"); return 0; } //驱动程序出口函数 static void hello_exit(void) { printk(KERN_ALERT "###############Goodbye, world#########\n"); } module_init(hello_init); module_exit(hello_exit);

YAFFS2启动坏块问题

YAFFS2启动坏块问题 动的时候出现一大串: block 2 is bad block 3 is bad block 4 is bad block 5 is bad block 6 is bad block 7 is bad block 8 is bad block 9 is bad ... VFS:Mounted root yaffs filesystem. Freeing init memory: 120K Warning: unable to open an initial console. Failed to execute /linuxrc. Attempting defaults... Kernel panic - not syncing: No init found. Try passing init option to kernel. 或者block 2 is bad 没有出现直接显示 VFS: Mounted root yaffs filesystem. Freeing init memory: 120K Warning: unable to open an initial console. Failed to execute /linuxrc. Attempting defaults... Kernel panic - not syncing: No init found. Try passing init option to kernel. 其实都是 MTD没有正确地读取到 root 可以在 u-boot 中执行 nand bad 查看 bad block 的信息如果从0x200000 - 0x1200000 之间有很多bad block说明nand write.yaffs 这个命令还有问题 OOB 的信息没有正确地写入在 NANDFLASH 只写入了 data 而 OOB 没有写进去. 下面说说如果解决这个问题:在 NANDFLASH 的写入过程中其中会调用 nand_do_write_ops 在这个函数中 if unlikelyoob oob nand_fill_oobchip oob ops 这两行要注意在这两行之前 OOB buf一直都是正解的但这两行之后 OOB 的数据就变成了 0xFF了在靠前的一些 block OOB 都是 0xFF但是到了后面 OOB的数据就变得杂乱无章了所以就会出现一大串的 bad flash为什么会变成没有规律原因还没有找到将这两行MASK改为memcpychip-oob_poi oob ops-ooblen 问题就解决了或者在nand_write_opts 函数中将 oob_ops.mode MTD_OOB_AUTO 改为 oob_ops.mode MTD_OOB_RAW 问题同样可以解决.

Yaffs功能说明

? 1 背景 ? 2 发牌 ? 3 Yaffs 和 Yaffs 直接接口有哪些? ? 4 为什么使用 Yaffs 吗? ? 5 源代码和 Yaffs 资源 ? 6 系统要求 ?7 如何与实时操作系统/Embedded 系统集成 Yaffs ?7.1 源文件 ?7.2 整合POSIX应用程序接口 ?7.3 RTO集成接口 ?8 Yaffs NAND模型 ?Yaffs1 8.1 NAND模型注意事项 ?Yaffs2 8.2 NAND模型 ?9 NAND配置和访问接口 ?9.1 常见配置项 (Yaffs1 和 Yaffs2) ?9.2 共同访问函数 (Yaffs1 和 Yaffs2) ?9.3 Yaffs1 访问函数 ?9.4 Yaffs2 访问函数 ?10 使用POSIX文件系统接口 ?10.1 向 Windows POSIX差异-喜欢接口 ?10.2 基本概念 ?10.3 错误代码 ?10.4 链接——硬种 (不是符号链接) ?10.5 符号链接 ?10.6 基于句柄的文件处理 ?10.7 更改文件大小 ?10.8 获取/设置有关文件的信息 ?10.9 更改目录结构和名称 ?10.10 搜索目录 ?10.11 装载控制 ?10.12 其他 ?11 示例: yaffs_readdir() 和 yaffs_stat() 1 背景 本文档的目的是描述接口的Yaffs 直接接口(YDI) 以及提供足够的信息,以允许Yaffs 初步评价。这份文件试图把重点放在重要的系统集成商的问题没有得到Yaffs 是如何工作的太详细。其他文件提供Yaffs 的工作原理进行深入的讨论。 2 发牌

yaffs2文件系统分析

yaffs2文件系统分析 作者:dreamice 1.前言 略。 2.yaffs文件系统简介 按理说这里应该出现一些诸如“yaffs是一种适合于NAND Flash的文件系统XXXXX”之类的字眼,不过考虑到网络上关于yaffs/yaffs2的介绍已经多如牛毛,所以同上,略。 3.本文内容组织 本文将模仿《linux内核源代码情景分析》一书,以情景分析的方式对yaffs2文件系统的 源代码进行分析。首先将分析几组底层函数,如存储空间的分配和释放等;其次分析文件逻辑地址映射;然后是垃圾收集机制;接下来……Sorry,本人还没想好。:-) 4.说明 因为yaffs2貌似还在持续更新中,所以本文所列代码可能和读者手中的代码不完全一致。另外,本文读者应熟悉C语言,熟悉NAND Flash的基本概念(如block和page)。 Ok,步入正题。首先分析存储空间的分配。 5.NAND Flash存储空间分配和释放 我们知道,NAND Flash的基本擦除单位是Block,而基本写入单位是page。yaffs2在分配存储空间的时候是以page为单位的,不过在yaffs2中把基本存储单位称为chunk,和page是一样的大小,在大多数情况下和page是一个意思。在下文中我们使用chunk这个词,以保持和yaffs2的源代码一致。 我们先看存储空间的分配(在yaffs_guts.c中。这个文件也是yaffs2文件系统的核心部分):Yaffs2中将该函数更名为yaffs_alloc_chunk。 static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr) { int retVal; yaffs_BlockInfo *bi; if (dev->allocationBlock < 0) { /* Get next block to allocate off */ dev->allocationBlock = yaffs_FindBlockForAllocation(dev); dev->allocationPage = 0; } 函数有三个参数,dev是yaffs_Device结构的指针,yaffs2用这个结构来记录一个NAND 器件的属性(如block和page的大小)和系统运行过程中的一些统计值(如器件中可用chunk 的总数),还用这个结构维护着一组NAND操作函数(如读、写、删除)的指针。 整个结构体比较大,我们会按情景的不同分别分析。useReserve表示是否使用保留空间。yaffs2文件系统并不会将所有的存储空间全部用于存储文件系统数据,而要空出部分block用于垃圾收集时使用。一般情况下这个参数都是0,只有在垃圾收集时需要分配存 储空间的情况下将该参数置1。yaffs_BlockInfo 是描述block属性的结构,主要由一些统计变量组成,比如该block内还剩多少空闲page等。我们同样在具体情景中再分析这个结构中的字段含义。 函数首先判断dev->allocationBlock的值是否小于0。yaffs_Device结构内的allocationBlock字段用于记录当前从中分配chunk(page)的那个block的序号。当一 个block内的所有page全部分配完毕时,就将这个字段置为-1,下次进入该函数时就会

深入理解yaffs2文件系统(一)

深入理解yaffs2文件系统(一) 1、Flash文件系统 1.1、背景 已经有多种flash文件系统(FFSs)或flash块驱动(在之上运行一个常规的FS),同时都有优点或缺点。 Flash存储器有非常多的限制,这里就不一一列举了。已经有各种方法解决这些限制,以提供一个文件系统。必须认识到,“flash”,包括NOR和NAND,各自有不同的限制。很容易被专业术语“flash”误导,误以为用于NorFlash的方法也立即适用于NandFlash。 Nand块驱动一般采用FAT16作为文件系统,但不够健壮,也不够贴近Flash的特性。这些块驱动通过一个“本地--物理”的映射层来仿真可写的、类似于磁盘扇区的块。当使用FAT16时,这些文件系统工作的相当好,它们内存消耗小,代码尺寸也很小。但就像所有基于FAT 的系统一样,它们很容易损坏(如,丢失簇)。 其他的途径则是设计整个文件系统,不是基于块驱动,而且是flash友好的,这允许更多的余地来解决上述所提到的问题。 当前有两个linux文件系统能非常好的支持NorFLash,那就是JFFS以及它的升级版本JFFS2。这两者都提供日志机制,大大的提升了健壮性,这也是嵌入式系统特别重要的一个特性。不幸的是,它们在RAM消耗和启动时间方面都不是很好。 JFFS在flash中的每一个journalling日志节点,需要一个基于RAM的jffs_node结构,每一个节点为48字节。JFFS2做了一个大改进,通过剪裁相关的结构体(jffs2_raw_node_ref)而减少到16字节。即使如此,在512字节页大小128M的NandFlash,按平均节点大小来算,也需要250000字节约4M大小。 JFFS和JFFS2在启动时,需要扫描整个flash阵列来查找journaling节点,并决定文件结构。由于NAND容量大、慢、连续访问、需要ECC校验,这些特性将导致不可接受的、很长的启动时间。随便掐指一算,扫描128M字节的Nand阵列大小需要25秒钟。 设计yaffs2的目的就是:NandFlash友好的、通过提供日志机制达到健壮的、大大减少JFFSx 所具有的RAM消耗和启动时间。Yaffs主要是用于内部Nand而不是可移动的Nand(SM卡)。在可移动的SM智能卡,兼容性显得更重要,一般使用FAT文件系统。当然,yaffs也做了深思熟虑,认为稳定性比兼容性更重要。 1.2、Yaffs文件系统特性 YAFFS是一个专为NandFlash特性设计的文件系统。它已经被证实的好特性有: (1)fast –快速,比其他Flash文件系统要快很多。 (2)Easily ported –易于移植,已经移植到GNU/Linux,WinCE,eCOS,pSOS,VxWorks,以及其他各种系统。 (3)Log structured –日志结构,提供均衡负载,使得它非常健壮。 (4)支持多种类型的NandFlash芯片,如页大小为512B、1KB、2KB的NnadFlash等等。(5)Very fast mount –非常快速的文件系统挂载速度,几乎是立即启动的。 (6)非常少的RAM消耗。 (7)灵活的Licensing授权机制,适合许多情况。 YAFFS当前版本为v2,yaffs2除了支持512字节页大小的flash,还支持2K字节页大小的flash (YAFFS1仅仅支持原先的512字节页大小的flash)。YAFFS 1 和2已经被众多的商业产品所采用。 2、关于yaffs1文件系统

相关主题
文本预览
相关文档 最新文档