驱动移植
模块
Linux 驱动有两种运行方式,第一种是将驱动编译进 Linux 内核中,当 Linux 内核启动的时就会自动运行驱动程序。第二种是将驱动编译成模块(Linux 下模块扩展名为.ko),在Linux 内核启动以后使用相应命令加载驱动模块。
模块(mod)只允许工作在内核态下
模块在内核中可以动态的(不影响系统的其他功能下)添加和删除
模块可以实现驱动,也可以实现简单的“hello world”(所以模块不一定是驱动)
模块是载体,驱动是功能
模块和应用程序的区别:
1.模块有两个接口(一个进来的入口init()一个是出去的入口exit()),应用只有一个main的入口(从头到尾的单任务)
2.模块一旦调用init()接口,会预先注册进内核,告知内核我要执行程序,但不是瞬间执行,而是由人控制执行时间,当exit()被调用时,才算执行结束
3.模块占用内核空间,权限大,段错误会导致系统崩溃;应用占用应用空间,段错误对系统危害小
模块的好处:
1.减小内核的体积
2.如果驱动有问题可以直接修改驱动,无需更改内核
简单的Linux内核模块
1.头文件:初始化 #include <linux/init.h>
Linux表示include下的一个目录
2.模块加载函数
3.模块卸载函数
4.模块信息描述 LICENSE协议必须要写
#module templet
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /home/hqyj/sys/linux-3.14
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
cp -af *.ko /home/hqyj/sys/newrootfs/
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean
else
obj-m := mod.o
endif
/*===============================================
* 文件名称:mod.c
* 创 建 者: memories
* 创建日期:2023年07月06日
* 描 述:have a nice day
================================================*/
//1.header
#include <linux/init.h>
#include <linux/module.h>
//2.init/exit fun
static int myinit(void)
{
printk("myinit ok\n");
return 0;
}
static int myexit(void)
{
printk("myexit ok\n");
return ;
}
//3.reg kernel
module_init(myinit);
module_exit(myexit);
//4.mod info
MODULE_LICENSE("GPL");
终端上运行:
insmod mod.ko(运行模块)
rmmod mod(结束模块)(如果出现问题 在rootfs/lib/下创建modules目录)
(若继续出现问题,在modules下继续创建目录)
lsmod (查看模块)
外部传参
static int no = 1;
12 static char* name = "my led";
13
14 //外部传参
15 module_param(no,int,0000);
16 module_param(name,charp,0000);
17
18 //2.init/exit fun
19 static int myinit(void)
20 {
21 printk("name is %s,no is %d\n",name,no);
22 printk("myinit ok\n");
23 return 0;
24 }
设备驱动
1.字符设备驱动
一个一个字节,按照字节流的形式进行读写操作的设备
2.块设备驱动
3.网络设备驱动
驱动向上与内核挂钩,向下面对设备硬件
主设备号:哪一类设备
次设备号:类设备中的哪一个
cat proc/devices (查看已经使用了的设备号)
驱动方式:
1.应用程序 2.驱动文件
应用程序运行在用户空间,驱动文件属于内核,用户空间不能直接对内核进行操作,因此必须使用系统调用的方法来实现用户对于内核的控制。
驱动函数(.c)-->驱动模块(.ko)
在/dev下创建一个与之对应的设备节点文件mknod xx(相当于我们的驱动文件)
运行应用程序(./a.out)--->控制驱动文件
驱动函数
.open() .release()
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
/*===============================================
* 文件名称:mod.c
* 创 建 者: memories
* 创建日期:2023年07月06日
* 描 述:have a nice day
================================================*/
//1.header
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#define MA 300
#define MI 0
static dev_t no = 0;
static unsigned count = 1;
static const char *name = "mydev";
static struct cdev mydev;
int myopen(struct inode *pi,struct file *pf)
{
printk("kernel open\n");
return 0;
}
int myrelease(struct inode *pi,struct file *pf)
{
printk("kernel close\n");
return 0;
}
static const struct file_operations myfops={
.release = myrelease,
.open = myopen,
};//文件操作集
//2.init/exit fun
static int myinit(void)
{
int ret = 1;
//up: kernel
//1.registed 把设备对象注册到系统中
no = MKDEV(MA,MI);//组合主次设备号
ret = register_chrdev_region(no,count,name);
if(ret != 0)
{
printk("reg is error\n");
return -1;
}
//2.init 设备初始化,抽象出来一个对象
cdev_init(&mydev,&myfops);
//3.add 添加设备
ret = cdev_add(&mydev,no,count);
if(ret != 0)
{
unregister_chrdev_region(no,count);
return -1;
}
//down:hardware
printk("myinit ok\n");
return 0;
}
static int myexit(void)
{
cdev_del(&mydev);
unregister_chrdev_region(no,count);
printk("myexit ok\n");
return ;
}
//3.reg kernel
module_init(myinit);
module_exit(myexit);
//4.mod info
MODULE_LICENSE("GPL"); //使用GPL协议
调用open()函数的流程
- 应用调用open()函数
- C库中的open函数 (fd = open())
- 内核中的open()调用 (strcut file pf = open())
- 驱动程序中的open()函数 (my open())
应用函数
/*===============================================
* 文件名称:mainopen.c
* 创 建 者: memories
* 创建日期:2023年07月06日
* 描 述:have a nice day
================================================*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd = open("/dev/mydev",O_RDONLY);
return 0;
}
mknod /dev/mydev c 300 0 (创建字符设备文件)
/dev/mydev :创建的一个设备文件
c:设备类型为C字符设备
300:主设备号
0:次设备号