今天第一次自己尝试编写驱动,看了这么长时间视频,第一次动手,编写的是简单的LED的程序。从简单到复杂一个一个来。

编写驱动的顺序是先从注册函数和卸载函数起始:

static int led_init()
{
cdev_init(&cdev, &led_fops);
alloc_chrdev_region(&devno, 0, 1,"led");
cdev_add(&cdev, devno, 1);

return 0;
}
static int led_exit()
{ cdev_del(&cdev);
unregister_chrdev_region(devno,1);
}
module_init(led_init);
module_exit(led_exit);

先把框架撘起来,然后在填充。在模块init的函数中如定义cdev,定义file_operations,初始化cdev,添加cdev,因为添加cdev要用到设备编号,所以在添加cdev前要注册设备号 devno。在模块卸载函数中需要删除cdev,注销设备号。

本次LED驱动十分简单,只用到了open和ioctl函数。建立LED.h 文件,定义ioctl要用的到命令:

#define LED_MAGIC 'L'       //定义L作为幻数
#define LED_ON _IO(LED_MAGIC,1)     //不需要传递参数,所以用_IO来定义两个命令
#define LED_OFF _IO(LED_MAGIC,0)

然后编写led_open 函数和led_ioctl函数,完成驱动

源码:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include "LED.h"


#define LEDCON 0x56000010 
#define LEDDATA 0x56000014
unsigned int *led_config; 
unsigned int *led_data; 


struct cdev cdev;
 dev_t devno;


static int led_open (struct inode *inode, struct file *filp)
{
led_config=ioremap(LEDCON,4);
iowrite32(0x00015400,led_config);
led_data=ioremap(LEDDATA,4);

return 0;
}
static int led_ioctl(struct inode *inode, struct file *flip, unsigned int cmd, unsigned long arg)
{
switch(cmd){
case LED_ON :
iowrite32(0x00,led_data);
return 0;
case LED_OFF :
iowrite32(0xff,led_data);
return 0;
default :
return -EINVAL;}
}


 struct file_operations led_fops=
{
 .open = led_open,
 .ioctl = led_ioctl,
};

static int led_init()
{
cdev_init(&cdev, &led_fops);
alloc_chrdev_region(&devno, 0, 1,"led");
cdev_add(&cdev, devno, 1);

return 0;
}
static int led_exit()
{ cdev_del(&cdev);
unregister_chrdev_region(devno,1);
}
module_init(led_init);
module_exit(led_exit);


其中struct file_operations led_fops的赋值和初始化要放到led_open和led_ioctl的后面,如果放在前面编译时会提示没有定义。如下:


关于ioctl:

ioctl命令:

定义命令
定义ioctl命令的正确方法是使用4 个位段,这个列表中介绍的符号定义在中:
Type
幻数(类型):表明哪个设备的命令,在参考了ioctl-number.txt之后选出,8位宽。
Number
序号,表明设备命令中的第几个,8位宽。
Direction
数据传送的方向,可能的值是_IOC_NONE(没有数据传输),_IOC_READ,_IOC_WRITE。数据传送是从应用程序的观点来看待的,_IOC_READ意思是从设备读。
size
用户数据的大小。(13/14位宽,视处理器而定)
内核提供了下列宏来帮助定义命令:
_IO(type,nr);
没有 参数的命令
_IOR(type,nr,datatype)
从驱动中读取数据
_IOW(type,nr,datatype)
写数据到驱动
_IOWR(type,nr,datatype)
双向传送,type和number成员作为参数被传递。点击打开链接

http://blog.chinaunix.net/uid-25014876-id-59419.html  ioctl的学习

在开发板中运行应用程序时,可能会提示你缺少权限如图:

使用chmod 777 led_app 就可以解决了

在应用程序运行的过程中出现了一下错误


这种错误说是由于内存泄露什么的,在网上百度了几篇关于Oops的文章,大概看懂了,但是对于新手来说,具体操作还是有点难有兴趣的可以看一下这个http://blog.chinaunix.net/uid-14753126-id-2980100.html

由于驱动简单,我选择了一个比较笨又好用的方法,因为我的驱动是根据视频一步一步写的,所以直接和视频中的程序对照,果然发现了,我驱动函数中的open函数和模块初始化函数都少了一句return 0; 加上之后问题就解决了。这里就有疑惑了,为什么这个return 0 必须要?为什么模块卸载函数里面不用要呢?没有百度到结果,先记下来,以后问高手。加上之后

Logo

更多推荐