字符设备: 传输字符流的设备 不允许随机访问(一般)
块设备:数据块 允许随机访问 例如硬盘,U盘
网络设备:可以使用网络的设备
字符设备驱动:
ctrl+N自动补齐
生成tags
一、模块:
组成:
1、许可证声明
MODULE_LICENSE("GPL");
1) 不写内核会产生抱怨
2) 不写则内核很多函数无法使用
2、初始化函数---加载函数----insmod 时会被调用
int init_module(void)
{
return 0;
}
3、清除函数---卸载函数---rmmod 时会被调用
void cleanup_module(void)
{
}
编译外部模块:
make -C <path-to-kernel-src> M=`pwd`======> 使用path-to-kernel-src目录下的Makefile为主Makefile并传递参数M=`pwd`告诉内核Makefile外部模块所在位置
例:make -C /home/linux/FS4412/linux-3.14-fs4412 M=`pwd`
注意:内核源码有要求
1、必须是配置为针对某个特定平台的内核源码
2、内核源码必须编译过且没有被make clean
测试:
insmod 插入模块---动态加载模块
insmod 模块文件名
dmesg 显示内核启动后所有打印信息
dmesg -c 清除现有信息
lsmod 列出当前内核中所有动态加载模块
rmmod 删除模块----动态卸载模块
rmmod 模块名
注意:根文件系统中需要一个路径:/lib/modules/`uname -r`
modinfo 查看模块信息
modprobe 类似与insmod
module_init
#ifndef MODULE
定义函数指针指向加载函数,且函数指针被链接到initcall段,内核启动时将被统一调用
#else
用来检测加载函数的合法性(只有返回值为int 参数为void的函数才能作为加载函数)
定义别名将加载函数与init_module关联
#endif
组成:
必须有的部分:
1、许可证声明
MODULE_LICENSE("GPL");
1) 不写内核会产生抱怨
2) 不写则内核很多函数无法使用
2、初始化函数---加载函数----insmod 时会被调用
初始化函数为返回值为int 参数为void的函数,并用module_init修饰
3、清除函数---卸载函数---rmmod 时会被调用
卸载函数函数为返回值为void 参数为void的函数,并用module_exit修饰
可选部分:
4、描述类信息
5、模块参数
定义全局变量:
int i = 10;
声明变量可以接受外部传参
module_param(变量名,变量类型,权限)
module_param(i, int, 0444);
模块参数的说明
MODULE_PARM_DESC(变量名, 描述信息)
MODULE_PARM_DESC(i, "...");
传递参数:
insmod hello.ko i=15
6、符号导出
驱动
1、申请设备号
cat /proc/devices 可以查看内核设备号使用情况
<linux/fs.h>
dev_t 32bit数
12(主设备号) | 20 (次设备号)
主设备号表示设备的类型
次设备好表示第几个这种类型的设备
dev_t devno = MKDEV(主, 次);
MAJOR(devno);
MINOR(devno);
静态申请
int register_chrdev_region(dev_t from, unsigned count, const char *name);
参数:
from: 要申请的连续的若干个设备号中的第一个
count:要申请多少个设备号
name:设备或驱动的名字
返回值:
成功 0
失败 -errno
设备号释放
void unregister_chrdev_region(dev_t from, unsinged count);
参数:
from: 要释放的连续的若干个设备号中的第一个,必须和申请的号一直
count:要释放多少个设备号
动态分配
alloc_chrdev_region----课后作业
1.1
定义struct file_operations
struct file_operations hello_fops = {
.owner = THIS_MODULE,
};
2、注册设备
<linux/cdev.h>
定义:struct cdev cdev;
初始化:void cdev_init(struct cdev *cdev, struct file_operations *ops);
cdev_init(&cdev, &hello_fops);
注册:int cdev_add(struct cdev *cdev, dev_t devno, int count); 将设备号devno和cdev关联,并将cdev注册到系统中去(cdev_map)
cdev_add(&cdev, devno, 1);
3、注销设备
cdev_del(struct cdev *cdev);
cdev_del(&cdev);
4、为应用程序提供访问接口
open/release
open:
write:
memcpy(data,buf,size);
1,size合法性验证
2,数据拷贝---copy_from_user(to,from,size)
3,返回实际写入的数据大小
return -EINVAL;//返回无效操作
return -EFAULT;//返回错误地址
read:
memcpy(buf,data,size);
access_ok(buf);//判断buf是否可写
1,size合法性验证
2,数据拷贝---copy_to_user(to,from,size)
3,返回实际写入的数据大小
注意:copy_to_user和copy_to_user是可能引起睡眠的操作
编译
make -C /lib/modules/`uname -r`/build M=`pwd`
测试出错时(缺少文件或目录时)
在 /dev下新建hello
mknod /dev/hello c 250 0
ioctl:
应用:
int ioctl(int fd, int cmd, void *arg)
fd : 文件描述符
cmd: 设备驱动所定义的命令,应用程序根据驱动对每个命令赋予的不同含义,传递不同的命令到驱动中,完成不同的操作
arg: 当某个命令需要传递参数时,就是用这个参数
驱动:
定义命令:
类型为int 32bit
dir(2bit) | size(14bit) | type(8bit) | nr(8bit)
type: 类型表示这个命令属于哪个设备
nr: 序号表示这个这个设备的第几个命令
dir: 数据流向
size: 传递数据的大小
struct A
{ int a;
int b;
};
struct A a;
#define HELLO_CMD1 _IO('H', 0)
#define HELLO_CMD2 _IOW('H', 1, struct A)
编码: _IO(type, nr)
_IOW(type, nr, size)
_IOR(type, nr, size)
_IOWR(type, nr, size)
解码: _IOC_DIR(cmd)
_IOC_SIZE(cmd)
_IOC_TYPE(cmd)
_IOC_NR(cmd)
file-operations ->ioctl(struct inode *indoe, struct file *file, int cmd, unsigned long arg)
unlocked_ioctl(struct file *file, int cmd, unsigned long arg)
识别命令,根据驱动对不同命令的定义,实现不同的操作
switch-case
块设备:数据块 允许随机访问 例如硬盘,U盘
网络设备:可以使用网络的设备
字符设备驱动:
ctrl+N自动补齐
生成tags
一、模块:
组成:
1、许可证声明
MODULE_LICENSE("GPL");
1) 不写内核会产生抱怨
2) 不写则内核很多函数无法使用
2、初始化函数---加载函数----insmod 时会被调用
int init_module(void)
{
return 0;
}
3、清除函数---卸载函数---rmmod 时会被调用
void cleanup_module(void)
{
}
编译外部模块:
make -C <path-to-kernel-src> M=`pwd`======> 使用path-to-kernel-src目录下的Makefile为主Makefile并传递参数M=`pwd`告诉内核Makefile外部模块所在位置
例:make -C /home/linux/FS4412/linux-3.14-fs4412 M=`pwd`
注意:内核源码有要求
1、必须是配置为针对某个特定平台的内核源码
2、内核源码必须编译过且没有被make clean
测试:
insmod 插入模块---动态加载模块
insmod 模块文件名
dmesg 显示内核启动后所有打印信息
dmesg -c 清除现有信息
lsmod 列出当前内核中所有动态加载模块
rmmod 删除模块----动态卸载模块
rmmod 模块名
注意:根文件系统中需要一个路径:/lib/modules/`uname -r`
modinfo 查看模块信息
modprobe 类似与insmod
module_init
#ifndef MODULE
定义函数指针指向加载函数,且函数指针被链接到initcall段,内核启动时将被统一调用
#else
用来检测加载函数的合法性(只有返回值为int 参数为void的函数才能作为加载函数)
定义别名将加载函数与init_module关联
#endif
组成:
必须有的部分:
1、许可证声明
MODULE_LICENSE("GPL");
1) 不写内核会产生抱怨
2) 不写则内核很多函数无法使用
2、初始化函数---加载函数----insmod 时会被调用
初始化函数为返回值为int 参数为void的函数,并用module_init修饰
3、清除函数---卸载函数---rmmod 时会被调用
卸载函数函数为返回值为void 参数为void的函数,并用module_exit修饰
可选部分:
4、描述类信息
5、模块参数
定义全局变量:
int i = 10;
声明变量可以接受外部传参
module_param(变量名,变量类型,权限)
module_param(i, int, 0444);
模块参数的说明
MODULE_PARM_DESC(变量名, 描述信息)
MODULE_PARM_DESC(i, "...");
传递参数:
insmod hello.ko i=15
6、符号导出
驱动
1、申请设备号
cat /proc/devices 可以查看内核设备号使用情况
<linux/fs.h>
dev_t 32bit数
12(主设备号) | 20 (次设备号)
主设备号表示设备的类型
次设备好表示第几个这种类型的设备
dev_t devno = MKDEV(主, 次);
MAJOR(devno);
MINOR(devno);
静态申请
int register_chrdev_region(dev_t from, unsigned count, const char *name);
参数:
from: 要申请的连续的若干个设备号中的第一个
count:要申请多少个设备号
name:设备或驱动的名字
返回值:
成功 0
失败 -errno
设备号释放
void unregister_chrdev_region(dev_t from, unsinged count);
参数:
from: 要释放的连续的若干个设备号中的第一个,必须和申请的号一直
count:要释放多少个设备号
动态分配
alloc_chrdev_region----课后作业
1.1
定义struct file_operations
struct file_operations hello_fops = {
.owner = THIS_MODULE,
};
2、注册设备
<linux/cdev.h>
定义:struct cdev cdev;
初始化:void cdev_init(struct cdev *cdev, struct file_operations *ops);
cdev_init(&cdev, &hello_fops);
注册:int cdev_add(struct cdev *cdev, dev_t devno, int count); 将设备号devno和cdev关联,并将cdev注册到系统中去(cdev_map)
cdev_add(&cdev, devno, 1);
3、注销设备
cdev_del(struct cdev *cdev);
cdev_del(&cdev);
4、为应用程序提供访问接口
open/release
open:
write:
memcpy(data,buf,size);
1,size合法性验证
2,数据拷贝---copy_from_user(to,from,size)
3,返回实际写入的数据大小
return -EINVAL;//返回无效操作
return -EFAULT;//返回错误地址
read:
memcpy(buf,data,size);
access_ok(buf);//判断buf是否可写
1,size合法性验证
2,数据拷贝---copy_to_user(to,from,size)
3,返回实际写入的数据大小
注意:copy_to_user和copy_to_user是可能引起睡眠的操作
编译
make -C /lib/modules/`uname -r`/build M=`pwd`
测试出错时(缺少文件或目录时)
在 /dev下新建hello
mknod /dev/hello c 250 0
ioctl:
应用:
int ioctl(int fd, int cmd, void *arg)
fd : 文件描述符
cmd: 设备驱动所定义的命令,应用程序根据驱动对每个命令赋予的不同含义,传递不同的命令到驱动中,完成不同的操作
arg: 当某个命令需要传递参数时,就是用这个参数
驱动:
定义命令:
类型为int 32bit
dir(2bit) | size(14bit) | type(8bit) | nr(8bit)
type: 类型表示这个命令属于哪个设备
nr: 序号表示这个这个设备的第几个命令
dir: 数据流向
size: 传递数据的大小
struct A
{ int a;
int b;
};
struct A a;
#define HELLO_CMD1 _IO('H', 0)
#define HELLO_CMD2 _IOW('H', 1, struct A)
编码: _IO(type, nr)
_IOW(type, nr, size)
_IOR(type, nr, size)
_IOWR(type, nr, size)
解码: _IOC_DIR(cmd)
_IOC_SIZE(cmd)
_IOC_TYPE(cmd)
_IOC_NR(cmd)
file-operations ->ioctl(struct inode *indoe, struct file *file, int cmd, unsigned long arg)
unlocked_ioctl(struct file *file, int cmd, unsigned long arg)
识别命令,根据驱动对不同命令的定义,实现不同的操作
switch-case