linux添加led驱动程序,Helper2416-35——Linux驱动——第一个驱动程序(点亮LED)

Helper2416-35——Linux驱动——第一个驱动程序(点亮LED)

[复制链接]

本帖最后由 yuanlai2010 于 2014-8-26 14:29 编辑

Linux驱动——第一个驱动程序(点亮LED)

参与Helper2416开发板助学计划心得

经过几天的琢磨,今天终于完成了我的第一个驱动,也算是敲开了驱动编写的大门

描述:

Linux驱动程序大致分为三种:字符设备驱动、块设备驱动、网络驱动,.这个LED驱动程序就是最简单的字符驱动程序中的最简单的一个实例,不过却也涵盖了驱动程序开发的基本步骤。

先上源码,然后再一点点解剖:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

static int LED_Major = 0;

static struct class *led_driver_class;

volatile unsigned long *GPBCON, *GPBDAT;

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

{

printk("s3c24xx_led_open\n");

(*GPBCON) |= 1<<2;

return 0;

}

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

{

printk("s3c24xx_led_close\n");

return 0;

}

static int s3c24xx_led_write(struct file *file, char __user *buff, size_t count, loff_t *offp)

{

int  value;

copy_from_user(&value, buff, count);

if(value == 1){

(*GPBDAT) &= ~(1<<1);

}else if(value == 0){

(*GPBDAT) |= 1<<1;

}else{

printk("zhe writed value must be 1 or 0\n");

return 1;

}

return 0;

}

static struct file_operations led_fops = {

.owner   =   THIS_MODULE,

.open    =   s3c24xx_led_open,

.write         =         s3c24xx_led_write,

.release =   s3c24xx_led_close,

};

static int __init led_init(void)

{

LED_Major = register_chrdev(LED_Major, "led_driver", &led_fops);

led_driver_class = class_create(THIS_MODULE, "led_driver");

device_create(led_driver_class, NULL, MKDEV(LED_Major, 0), NULL, "led_driver");

GPBCON = (volatile unsigned long*)ioremap(0x56000010,8);

GPBDAT = GPBCON + 1;

printk("led_init\n");

return 0;

}

static void __exit led_exit(void)

{

unregister_chrdev(LED_Major, "led_driver");

device_destroy(led_driver_class, MKDEV(LED_Major, 0) );

class_destroy(led_driver_class);

iounmap(GPBCON);

printk("led_exit\n");

}

module_init(led_init);

module_exit(led_exit);

MODULE_AUTHOR("yuanlai");

MODULE_DESCRIPTION("helper2416 led Driver");

MODULE_LICENSE("GPL");复制代码

第一步:搭好驱动程序的框架

首先就是头文件:

头文件比较多,暂且还不知道这些头文件是不是都有用到,但是至少驱动能够编译成功。

接着就是structfile_operations 这个结构体及驱动处理函数:

加载驱动的时候,就需要把这个struct file_operations的结构体注册进内核,该结构体在头文件linux/fs.h定义,应该是告诉系统这个驱动程序具体有哪些函数用来操作硬件的,然后内核就是通过这个结构体的成员(函数指针)来找到这些函数来操作硬件的,以下便是这个结构体看起来的样子:

struct file_operations {

struct module *owner;

loff_t(*llseek) (struct file *, loff_t, int);

ssize_t(*read) (struct file *, char __user *, size_t, loff_t *);

ssize_t(*aio_read) (struct kiocb *, char __user *, size_t, loff_t);

ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *);

ssize_t(*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);

int (*readdir) (struct file *, void *, filldir_t);

unsigned int (*poll) (struct file *, struct poll_table_struct *);

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

int (*mmap) (struct file *, struct vm_area_struct *);

int (*open) (struct inode *, struct file *);

int (*flush) (struct file *);

int (*release) (struct inode *, struct file *);

int (*fsync) (struct file *, struct dentry *, int datasync);

int (*aio_fsync) (struct kiocb *, int datasync);

int (*fasync) (int, struct file *, int);

int (*lock) (struct file *, int, struct file_lock *);

ssize_t(*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t(*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t(*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void __user *);

ssize_t(*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

unsigned long (*get_unmapped_area) (struct file *, unsigned long,

unsigned long, unsigned long,

unsigned long);

};复制代码

对于具体要用到哪些成员,就需要根据驱动程序的具体情况来决定了。不过.owner   =   THIS_MODULE, 这条是一定要有的。由于只是控制一盏LED的亮灭状态,所以我也只用到了open、write、close这几个成员。

最后就是模块初始化与卸载处理函数:

模块的初始化这个函数(led_init)是在insmod的时候自动运行的,在代码中还需要用module_init()这个宏来包装一下。在这个函数中就需要把上面提到的structfile_operations这个结构体注册到内核里面去,这里使用register_chrdev()函数完成注册。

接着还需要在/dev/目录下创建相应设备节点,尽管可以通过shell用命令手工创建,但那是在太麻烦了点,所以通过先创建一个类,然后再在这个类下创建一个所需要的设备文件。

同样的模块退出函数(led_exit())是在rmmod的时候自动运行的,同样也需要module_exit()这个宏来包装一下。这里需要注销之前注册的structfile_operations结构体,然后销毁之前创建的类和设备(删除/dev/目录下的设备节点)

忘了,在最后的最后,还需要加上这些信息,尤其是MODULE_LICENSE("GPL");这条,不然很多时候编译会出错。

MODULE_AUTHOR("yuanlai");

MODULE_DESCRIPTION("helper2416 led Driver");

MODULE_LICENSE("GPL");复制代码

第二部:完善硬件操作

由于这仅仅是一个最简单的一盏LED的驱动,所以硬件操作比较简单,不过由于Linux操作的都是虚拟地址,所以当操作实际硬件的时候,就需要把物理地址映射成虚拟地址,具体的虚拟地址是多少这个是由系统来决定的,我们可以通过ioremap()这个函数来获得硬件物理地址所对应的虚拟地址,当然在驱动卸载时也记得用iounmap()解除此次映射。为了方便,我在驱动加载的时候,就用ioremap完成了IO的映射,并在led_exit()中用iounmap取消映射。然后具体的硬件操作都体现在s3c24xx_led_open()、s3c24xx_led_write()这两个函数里面了,怎么样,是不是和裸机差不多?

第三步:编译模块(驱动)

编译模块是一定需要有内核源码树的,我这里直接使用的是BOSS提供的Fedora18虚拟机镜像里的/home/jyxtec/workspace/kernel/s3c-linux

下面是Makefile文件具体内容,直接执行make后就在当前目录生成了我期待已久的.ko文件了。

obj-m := led_module.o

KERNEL_DIR :=/home/jyxtec/workspace/kernel/s3c-linux

PWD := $(shell pwd)

all:

make ARCH=arm CROSS_COMPILE=arm-linux- -C /home/jyxtec/workspace/kernel/s3c-linux M=$(PWD) modules

clean:

rm *.o *.ko复制代码

驱动测试

拿到.ko文件后当然就是急着去测试咯,只有测试成功了才是真正值得兴奋的时刻。

测试程序代码如下,实现频率为2秒的LED闪烁。

#include

#include

#include

#include

#include

#include

int main(int argc, char **argv)

{

int fd;

int value;

fd = open("/dev/led_driver", O_WRONLY);

if(fd < 0)

{

perror("cannot open device led");

exit(1);

}

while(1)

{

/* 点亮 LED */

value = 1;

write(fd, &value, 4);

sleep(1);

/* 熄灭 LED */

value = 0;

write(fd, &value, 4);

sleep(1);

}

close(fd);

return 0;

}复制代码

首先需要把.ko 文件和测试程序的可执行文件发送到目标版(Helper2416)

[root@jyxtec Linux]# ls

led_module.ko  led_test

[root@jyxtec Linux]#复制代码

然后执行 insmod led_module.ko 打印出以下信息,模块应该是已经加载成功了。

[root@jyxtec Linux]# insmod led_module.ko

led_init

[root@jyxtec Linux]#复制代码

为了验证是否整的已经加载,我们再来查看/dev/目录是否有我们所期待的设备文件 led_driver ., 确实是有的。

[root@jyxtec Linux]# ls -l /dev/led*

crw-rw----    1 root     root      252,   0 Jan  1 03:40 /dev/led_driver

[root@jyxtec Linux]#复制代码

接着就是运行测试程序了,由于我在驱动程序的open函数中加入打印信息的语句,所以会打印下面这些信息,然后开发板上的LED已经闪烁起来,由于测试程序是个无限循环,所以最后只能通过Ctle + C 结束程序。

a26d678767e8b431ecd7a25f1936bdc5.gif

minicom.png (64.02 KB, 下载次数: 0)

2014-8-26 14:24 上传

forum.php?mod=viewthread&tid=446282&page=1&authorid=423168

测试成功,第一个驱动程序成功的运行起来了!

论坛ID:yuanlai2010

发表时间:2014-08-26

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值