1、首先找内核中是否已经支持设备的驱动 选配
2、内核中没有的驱动移植进来 --第三方驱动移植
将第三方驱动代码放到linux源码树中的driver目录中
修改 Makefile Kconfig(界面配置)
修改过的和新添加的代码会重新编译
程序需要在板子上运行 就需要使用交叉编译工具编译
mknod /dev/led c或者b 主设备号 次设备号
拷贝LED驱动程序至drivers目录
LED属于字符设备,所以放在drivers/char/目录下
烧写镜像至开发板
驱动程序测试代码
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#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. 作用 容易维护
https://blog.csdn.net/m0_37542524/article/details/86476109