赞
踩
1、首先找内核中是否已经支持设备的驱动 选配
2、内核中没有的驱动移植进来 --第三方驱动移植
将第三方驱动代码放到linux源码树中的driver目录中
修改 Makefile Kconfig(界面配置)
修改过的和新添加的代码会重新编译
程序需要在板子上运行 就需要使用交叉编译工具编译
mknod /dev/led c或者b 主设备号 次设备号
拷贝LED驱动程序至drivers目录
LED属于字符设备,所以放在drivers/char/目录下
烧写镜像至开发板
驱动程序测试代码
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <sys/ioctl.h> #define LED_MAGIC 'L' #define LED_ON _IOW(LED_MAGIC, 1, int) #define LED_OFF _IOW(LED_MAGIC, 2, int) int main(int argc, char **argv) { int fd; fd = open("/dev/led", O_RDWR); if (fd < 0) { perror("open"); exit(1); } printf("open led ok\n"); //实现LED灯闪烁 while(1) { ioctl(fd, LED_ON); //点亮灯 usleep(100000); ioctl(fd, LED_OFF); //灭灯 usleep(100000); } return 0; }
编译并拷贝至根文件系统
在开发板上运行该测试程序
分析这个打印信息的来源
需要创建设备文件
mknod /dev/led c 501 0
创建好设备文件后,再运行测试程序,就可以看到LED灯在闪烁了
menuconfig 运行的时候,会读取内核下面所有的Kconfig,根据Kconfig生成对应的图形界面
Makfile 具体编译文件
Kconfig 选配,生成图形化界面(在界面中显示一些选项)
在Kconfig中添加一个灯的选项
界面修改好了, 还需要再次修改 Makfile
[] 只有两种选项编译(y)或者不编译(n)
<> 三种选项 编译(y)、不编译(n)或者编译成模块(m)
obj-y obj-n obj-$(CONFIG_XXX)
方便驱动管理
linux@linux:~/linux-3.14.79$ make menuconfig
可以看到图形界面中多了一个对应的选项
没有编译LED驱动
开发板上电重启,也可以测试出LED驱动没有被编译进来
驱动模块学习 详细视频
linux 内核模块 http://www.makeru.com.cn/live/1392_586.html
驱动初级也会详细讲解
创建驱动程序的文件夹,将文件拷贝进来,并为之编写Makfile
卸载是rmmod
黑盒移植 两种方式 1. 驱动编译进内核 选则源码目录 修改Makefile 修改Kconfig 2. 将驱动编译成独立的模块 配置为模块方式 通过修改Kconfig 使用make modules 编译为模块 装载模块 insmod xxx.ko 创建设备结点 mknod /dev/xxx c xx xx 运行测试驱动的应用程序 白盒移植 需要阅读阅读源码,熟悉驱动框架 1. 字符设备 2. 平台设备 字符设备框架 application User Mode || \/ -------------------------------------------------- System Call Interface || Kernel Mode \/ Virture File System(VFS) | | | Character Block Network | | | Device interface || \/ ------------------------------------------------- Hardware Physical Device (Hardware)
1.一切设备皆文件
对设备的操作的就是 对设备文件的 read write
2. open read write ioctl
3. 将设备进行编号 设备号(主次设备号组成)
1. 注册获取设备号
2. 初始化设备
3. 操作设备 file_operations -- open release read write ioctl...
4. 两个宏定义 module_init module_exit 两个命令 insmod rmmod
5. 注册设备号 register_chrdev_region
6. cdev_init 初始化字符设备
7. cdev_add 添加字符设备到系统
驱动是被动调用的,是被应用程序触发的
应用程序的 open 调用到驱动的file_operations 的 open
应用程序的 read 调用到驱动的file_operations 的 read
应用程序的 write 调用到驱动的file_operations 的 write
应用程序的 ioctl 调用到驱动的file_operations 的 ioctl
应用程序的 close 调用到驱动的file_operations 的 release
#include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <asm/io.h> #define LED_MAGIC 'L' #define LED_ON _IOW(LED_MAGIC, 1, int) #define LED_OFF _IOW(LED_MAGIC, 2, int) #define LED_MA 501 #define LED_MI 0 #define LED_NUM 1 #define LED_CON 0x11000C20 #define LED_DAT 0x11000C24 struct cdev cdev; unsigned int *ledcon; unsigned int *leddat; int led_open (struct inode *inode, struct file *file) { printk("led_open\n"); ledcon = ioremap(LED_CON, 4); //内核访问硬件需要地址映射 if(ledcon == NULL) { printk("ioremap LED_CON error\n"); return -1; } writel(0x01, ledcon); //设置LED3 GPX1_0 为输出模式 leddat = ioremap(LED_DAT, 4); //内核访问硬件需要地址映射 if(leddat == NULL) { printk("ioremap LED_DAT error\n"); return -1; } writel(1, leddat); //设置LED3 GPX1_0 输出高电平 return 0; } int led_release(struct inode *inode, struct file *file) { printk("led_close\n"); return 0; } long led_ioctl(struct file *file, unsigned int cmd, unsigned long args) { switch(cmd) { case LED_ON: printk("led on ..\n"); writel(1, leddat); //设置LED3 GPX1_0 输出高电平 break; case LED_OFF: printk("led off ..\n"); writel(0, leddat); //设置LED3 GPX1_0 输出高电平 break; default: printk("no command\n"); break; } return 0; } struct file_operations led_fops = { //文件操作 .owner = THIS_MODULE, .open = led_open, .release =led_release, .unlocked_ioctl = led_ioctl, }; static led_init(void) { int ret; dev_t devno = MKDEV(LED_MA, LED_MI); ret= register_chrdev_region(devno, LED_NUM, "newled"); //注册设备号 if(ret) { printk("register_chrdev_region error\n"); return -1; } cdev_init(&cdev, &led_fops); //初始化字符设备 ret = cdev_add(&cdev, devno, LED_NUM); //添加字符设备到系统中 if (ret < 0) { printk("cdev_add error\n"); return -1; } printk("Led init 5 \n"); return 0; } static void led_exit(void) { dev_t devno = MKDEV(LED_MA, LED_MI); cdev_del(&cdev); unregister_chrdev_region(devno, LED_NUM); //取消注册 printk("Led exit\n"); } module_init(led_init); //模块加载入口 对应命令insmod module_exit(led_exit); //模块卸载入口 对应命令rmmod MODULE_LICENSE("Dual BSD/GPL"); //免费开源声明
将驱动中的硬件信息剥离出来,放到设备树文件中
1. 寄存器的地址值是根据芯片手册 和原理图找到的
2. 对寄存器地址内容操作不能直接使用物理地址,需要ioremap
3. ioremap 将物理地址 映射到虚拟地址
4. platform 用于将硬件信息 和驱动代码做分离
5. 通过名字匹配,匹配成功执行 probe函数
6. 驱动通过 platform_get_resource 获取硬件设备资源
7. 作用 容易维护
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。