【原创】如何编写可动态加载的uClinux驱动程序

前提条件是工作目录在/usr/local/src/下。编写de0_led.c驱动程序,如下:

#include<linux/module.h>

#include<linux/kernel.h>

#include<linux/version.h>

#include<linux/init.h>

#include<linux/types.h>

#include<linux/string.h>

#include<linux/slab.h>

#include</usr/local/src/uClinux-dist/linux-2.6.x/include/asm-nios2nommu/io.h>

#include</usr/local/src/uClinux-dist/linux-2.6.x/include/asm-nios2nommu/page.h>

#include</usr/local/src/uClinux-dist/linux-2.6.x/include/asm-nios2nommu/uaccess.h>

#include<linux/sched.h>

#include<linux/fs.h>

#include<linux/proc_fs.h>

#include</usr/local/src/uClinux-dist/linux-2.6.x/include/linux/sysctl.h>

//#include</usr/local/src/uClinux-dist/linux-2.6.x/include/asm-nios2nommu/nios2.h>

#include </usr/local/src/uClinux-dist/linux-2.6.x/include/nios2_system.h>

 

#define de0_led_major 120    //major number 120

static char de0_led_name[]="de0_led";

static int de0_led_open(struct inode* inode,struct file* file)

{

         printk("open the de0_led device,major:%d,minor:%d\n",MAJOR(inode->i_rdev),MINOR(inode->i_rdev));

         try_module_get(THIS_MODULE);

         return 0;

}

static int de0_led_release(struct inode*inode,struct file*file)

{

         printk("release the de0_led device!\n");

         module_put(THIS_MODULE);

         return 0;

        

}

static ssize_t de0_led_read(struct file *filp,char __user *buff,size_t len,loff_t *ppos)//32

{

         printk("read de0_led device\n");

         int bytes_read=0;

         bytes_read=na_led->np_piodata;//36

         printk("bytes_read=%d\n",bytes_read);

         if(copy_to_user(buff,(char*)&bytes_read,sizeof(int)))

         {

                   return -EFAULT;

         }

         return len;

}

static ssize_t de0_led_write(struct file*file,const char __user*buff,size_t len,loff_t*off)

{

         unsigned int data_write=0;

         if(copy_from_user((char*)&data_write,buff,sizeof(int)))//47

         return -EFAULT;

         na_led->np_piodata=data_write;

         return len;

}

static struct file_operations de0_led_fops={

owner:THIS_MODULE,

open:de0_led_open,

release:de0_led_release,

read:de0_led_read,

write:de0_led_write//57

};

static int __init led_init(void)

{

         printk("Init the module!\n");

         int err;

         if((err=register_chrdev(de0_led_major,de0_led_name,&de0_led_fops))<0)

         {

                   printk("register_chrdev failed!error code=%d\n",err);

                   return err;

         }

         return 0;

}

static void __exit led_exit(void)

{

         printk("exit module!!\n");

         unregister_chrdev(de0_led_major,de0_led_name);

}

module_init(led_init);

module_exit(led_exit);

MODULE_LICENSE("GPL");

 

将编写好的驱动文件放到linux-2.6/drivers/char下,修改该文件夹下的Kconfig和Makefile文件。

Kconfig中添加:

config DE0_LED

              tristate”Nios led support”

              help

                     de0_led driver

 

在Makefile中添加:

obj-$(CONFIG_DE0_LED)               +=de0_led.o

 

然后运行make menuonfig,对内核进行配置。为了让操作系统支持动态加载,必须在Linux Kernel Configuration中选中Loadable module support,然后再进入Linux Kernel Configuration的Device Drivers选项,找到Character Devices,进入,找到新加入的Nios led support,选择为M,可动态加载。然后保存退出。

用户程序的处理跟hello world一样,现在src文件夹下用交叉工具链编译,将生成的可执行文件复制到romfs/bin下。

接下来就是编译内核了,make。

这里总结一下写字符型驱动程序的步骤:

  1. 定义主设备号和设备名称
  2. 定义驱动程序实现的功能,依照定义的功能定义并初始化file_operations结构体变量。
  3. 依照定义的功能,编写相应的函数体,例如读写函数,打开关闭函数等等。打开函数执行时首先要将设备计数器加一,告知系统设备已经被使用。也可以在打开函数里完成一些对硬件寄存器的设置,一般是第一次打开设备时才这样做。关闭函数主要是将设备计数器减一,告知内核这个进程已经不使用这个设备了。
  4. 编写设备加载函数,也叫初始化函数。在初始化函数里,主要完成设备注册和一些硬件相关寄存器的设置。
  5. 编写设备卸载函数,它的作用于加载函数正好相反。
  6. 用宏定义指定模块的加载函数和卸载函数。
  7. 其他可选择的宏定义。

驱动程序编写完成后,要用用户程序验证其是否正常工作。用户程序中不会再出现硬件寄存器,而是打开文件,读写文件,关闭文件等操作。用户把所有设备都当做文件来处理。Led驱动的测试程序如下:

#include <stdio.h>

//#include <linux-2.6.x/include/asm/nios2.h>

#include <string.h>

#include <malloc.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

#include <signal.h>

#include <sys/ioctl.h>

 

void mydelay(int count)

{

int i;

int j;

j=0;

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

    {

    j=j+i;   

    }

}

 

int main()

{

int fd;

int count;

char i=0;

unsigned char buff[]={0xfe,0xfe,0xfe,0xfe};

 

if((fd=open("/dev/de0_led",O_RDWR))==-1)

{

perror("open eror");

exit(1);

}

while(1)

{

/*if(count=read(fd,(char *)buff,4)!=4)

{

perror("read error");

exit(1);

}*/

 

mydelay(100000);

 

if(count=write(fd,(char *)buff,4)!=4)

{

perror("write error");

exit(1);

}

 

mydelay(100000);

i++;

buff[4]=i;

 

 

}

}

运行uclinux系统后,运行Altera Nios II EDS 11.0,与系统通信。可加载模块在uclinux下的路径为:/lib/modules/2.6.19-uc1/kernel/drivers/char/

进入到这个目录下,运行insmod de0_led.ko安装内核模块

然后建立设备节点,运行mknod /dev/de0_led c 120 0

其中120是主设备号,0是次设备号,c代表char字符设备。

成功注册设备并建立设备节点后,返回到根目录:cd  /

运行用户程序,输入命令:led_app

可以看到LED灯变亮,证明驱动正常工作。

后记:用户测试程序的功能是死循环,灯应该闪烁,但是实际效果并不是闪烁,而是一直亮。后来仔细检查了驱动程序,发现了问题,将设备写函数改为下面的行驶后,LED就会按照用户程序规定的那样闪烁了。

static ssize_t de0_led_write(struct file*file,const char __user*buff,size_t len,loff_t*off)

{

         char data_write[4]={0};

         if(copy_from_user(data_write,buff,sizeof(int)))//47

         return -EFAULT;

         na_led->np_piodata=(data_write[0]|(data_write[1]<<8)|(data_write[2]<<16)|(data_write[3]<<24));

         return len;

}

先前的驱动程序是照搬别人的,那个驱动有问题,强制将int变量的地址转化为char*型,是会出现问题的,如果int变量的地址值大于8位,强制转化为char*后,只保留了后8位地址值,前面的都没了,像这个地址写入数据,不会反应到LED上。而且还有可能会使系统程序崩溃。Uclinux系统是没有内存保护的,就是说用户一不小心可能访问了系统的内存空间,修改了系统的代码,内存都是不管的,所以编写程序时要格外小心。

 

 

 

转载于:https://www.cnblogs.com/lamapig/archive/2012/08/04/2622503.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值