一 模块化编程
1.如何编写模块
#include <linux/init.h>
#include <linux/module.h>
MODULE_LISCENSE("GPL");
int xxx_init(void)
{
.....
return 0;
}
void xxx_exit(void)
{
....
return;
}
module_init(xxx_init);//入口,加载模块的时候,调用xxx_init
module_exit(xxx_exit);//出口,卸载模块的时候, 调用xxx_exit
2.如何编译模块
第一种方法:
将编写好的模块,放在Linux 源代码下编译
第二种方法:
自己编写Makefile,调用Linux 源代码下的编译系统完成模块的编译
ifeq ($(KERNELRELEASE),)
问:如何让编译好的模块可以在开发板上运行?
[1]KERNEL_DIR指定开发板的Linux源代码的路径
[2]Linux 源代码一定是针对当前开发板配置过
KERNEL_DIR=/lib/modules/$(shell uname -r)/build
PWD=$(shell pwd)
module:
make -C $(KERNEL_DIR) M=$(PWD) modules
clean:
make -C $(KERNEL_DIR) M=$(PWD) clean
else
obj-m = xxx.o
endif
3.模块的动态加载
[1]如何加载模块
sudo insmod xxx.ko
[2]如何查看系统中的模块
lsmod | grep 模块名
[3]如何卸载指定的模块
sudo rmmod 模块名
4.查看内核空间打印的信息
[1]dmesg
[2]dmesg -c 清除的打印的信息
二 加载模块时参数传递和模块符号导出
1.加载模块的时候参数传递
module_param(变量名,类型,权限);
2.模块符号导出
EXPORT_SYMBOL(符号名);
EXPORT_SYMBOL_GPL(符号名);
三 Linux 设备分类
[1]字符设备 : 按字节操作
[2]块设备 : 按数据块操作
[3]网络设备 : 按网络接口访问(eth0)
注意:
字符设备和块设备都有对应的设备文件(/dev),网络设备没有
四 字符驱动
思考:Linux 驱动作用?
驱动程序向应用层提供函数接口,这些函数接口可以用来操作实际的硬件设备
思考:应用程序是如何访问底层驱动程序?
[1]字符设备和块设备驱动在应用层的/dev存在设备文件
[2]设备文件中包含了驱动的设备号信息
[3]在Linux下每个驱动有唯一的设备号和它关联
设备文件->设备号->设备驱动->操作设备函数接口
思考:Linux 编写字符驱动流程?
[1]封装结构体对自己的设备进行描述
struct xxxx_device
{
自己的设备的属性;
struct cdev cdev;
};
[2]实现操作设备的函数接口
struct file_operations xxx_fops= {
.open = xxxx_open,
.read = xxx_read,
.write = xxx_write,
....
}
[3]模块的入口的函数需要做事情
<1>分配空间
<2>初始化cdev -> cdev_init
<3>注册设备号 -> register_chrdev_region
<4>添加字符设备-> cdev_add
[4]模块的出口函数需要做的事情
<1>删除字节设备->cdev_del
<2>删除设备号 ->unregister_chrdev_region
<3>释放分配的空间
------------------------------------------------------------------------------
设备号 = 主设备号(12bit) | 次设备号(20bit)
主设备号:表示一个驱动程序
次设备号:一个开发板上有多个同类设备,此时只需要一个驱动程序,通过
不同的次设备号区分不同的设备。
注意:同一类中不同的设备主设备号一样,次设备号不同
1.如何编写模块
#include <linux/init.h>
#include <linux/module.h>
MODULE_LISCENSE("GPL");
int xxx_init(void)
{
.....
return 0;
}
void xxx_exit(void)
{
....
return;
}
module_init(xxx_init);//入口,加载模块的时候,调用xxx_init
module_exit(xxx_exit);//出口,卸载模块的时候, 调用xxx_exit
2.如何编译模块
第一种方法:
将编写好的模块,放在Linux 源代码下编译
第二种方法:
自己编写Makefile,调用Linux 源代码下的编译系统完成模块的编译
ifeq ($(KERNELRELEASE),)
问:如何让编译好的模块可以在开发板上运行?
[1]KERNEL_DIR指定开发板的Linux源代码的路径
[2]Linux 源代码一定是针对当前开发板配置过
KERNEL_DIR=/lib/modules/$(shell uname -r)/build
PWD=$(shell pwd)
module:
make -C $(KERNEL_DIR) M=$(PWD) modules
clean:
make -C $(KERNEL_DIR) M=$(PWD) clean
else
obj-m = xxx.o
endif
3.模块的动态加载
[1]如何加载模块
sudo insmod xxx.ko
[2]如何查看系统中的模块
lsmod | grep 模块名
[3]如何卸载指定的模块
sudo rmmod 模块名
4.查看内核空间打印的信息
[1]dmesg
[2]dmesg -c 清除的打印的信息
二 加载模块时参数传递和模块符号导出
1.加载模块的时候参数传递
module_param(变量名,类型,权限);
2.模块符号导出
EXPORT_SYMBOL(符号名);
EXPORT_SYMBOL_GPL(符号名);
三 Linux 设备分类
[1]字符设备 : 按字节操作
[2]块设备 : 按数据块操作
[3]网络设备 : 按网络接口访问(eth0)
注意:
字符设备和块设备都有对应的设备文件(/dev),网络设备没有
四 字符驱动
思考:Linux 驱动作用?
驱动程序向应用层提供函数接口,这些函数接口可以用来操作实际的硬件设备
思考:应用程序是如何访问底层驱动程序?
[1]字符设备和块设备驱动在应用层的/dev存在设备文件
[2]设备文件中包含了驱动的设备号信息
[3]在Linux下每个驱动有唯一的设备号和它关联
设备文件->设备号->设备驱动->操作设备函数接口
思考:Linux 编写字符驱动流程?
[1]封装结构体对自己的设备进行描述
struct xxxx_device
{
自己的设备的属性;
struct cdev cdev;
};
[2]实现操作设备的函数接口
struct file_operations xxx_fops= {
.open = xxxx_open,
.read = xxx_read,
.write = xxx_write,
....
}
[3]模块的入口的函数需要做事情
<1>分配空间
<2>初始化cdev -> cdev_init
<3>注册设备号 -> register_chrdev_region
<4>添加字符设备-> cdev_add
[4]模块的出口函数需要做的事情
<1>删除字节设备->cdev_del
<2>删除设备号 ->unregister_chrdev_region
<3>释放分配的空间
------------------------------------------------------------------------------
设备号 = 主设备号(12bit) | 次设备号(20bit)
主设备号:表示一个驱动程序
次设备号:一个开发板上有多个同类设备,此时只需要一个驱动程序,通过
不同的次设备号区分不同的设备。
注意:同一类中不同的设备主设备号一样,次设备号不同