用户层调用open函数时,内核层的sys_open()会根据用户层传递的文件路径参数找到该文件的文件信息结构体struct inode{},这个文件信息结构体存放的是该文件的相关信息,里面有一个成员是字符设备驱动结构体struct cdev{},这个字符设备驱动结构体struct cdev{}里面有一个操作方法结构体指针struct file_openations,基于这个操作方法结构体指针struct file_openations就可以找到操作方法mycdev_open()然后回调用户层的open函数。
①struct cdev cdev;//直接分配一个变量空间
②struct cdev *cdev=cdev_alloc();
struct cdev *cdev_alloc(void);//手动申请字符设备驱动对象空间
①void cdev_init(struct cdev *cdev, const struct file_operations *fops);
cdev:字符设备驱动对象指针
fops:操作方法结构体指针
②申请设备号 int register_chrdev_region(dev_t from, unsigned count, const char *name)
from:要申请的设备号
count:要申请的设备资源的数量
name:驱动名字
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
dev:申请的设备号填充在这个变量中
baseminor:次设备号的起始值
count:要申请的设备资源的数量
name:驱动名字
cdev:字符设备驱动对象指针
dev:申请的设备号的起始值
count:设备数量
scdev *字符设备驱动对象指针
from:要释放的设备号 count:设备的数量
p :对象空间首地址
#include
#include
#include
#include
#include
#include
#include
struct cdev *cdev;
unsigned int minor = 0;
unsigned int major = 500;
dev_t devno;
char kbuf[128] = {0};
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
unsigned long ret;
// 向用户空间读取拷贝
if (size > sizeof(kbuf)) // 用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
size = sizeof(kbuf);
ret = copy_to_user(ubuf, kbuf, size);
if (ret) // 拷贝失败
{
printk("copy_to_user filed\n");
return ret;
}
return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
unsigned long ret;
// 从用户空间读取数据
if (size > sizeof(kbuf)) // 用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret) // 拷贝失败
{
printk("copy_to_user filed\n");
return ret;
}
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
// 定义操作方法结构体变量并赋值
struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
static int __init mycdev_init(void)
{
int ret, i;
// 1.分配对象空间
cdev = cdev_alloc();
if (cdev == NULL)
{
ret = -EFAULT;
goto OUT1;
}
printk("字符设备驱动对象实例化成功\n");
// 2.部分初始化字符设备驱动对象
cdev_init(cdev, &fops);
// 3.申请设备号
if (major == 0)
{
ret = alloc_chrdev_region(&devno, minor, 3, "mycdev");
if (ret)
{
printk("动态申请设备号失败\n");
goto OUT2;
}
major = MAJOR(devno);
minor = MINOR(devno);
}
else
{
devno = MKDEV(major, 0);
ret = register_chrdev_region(devno, 3, "mycdev");
if (ret)
{
printk("静态指定设备号失败\n");
goto OUT2;
}
}
printk("设备号申请成功\n");
// 4.将字符设备驱动对象注册进内核
ret = cdev_add(cdev, devno, 3);
if (ret)
{
printk("字符设备驱动对象注册内核失败\n");
goto OUT3;
}
printk("注册进内核成功\n");
// 5.自动创建设备节点
cls = class_create(THIS_MODULE, "mycdev");
if (IS_ERR(cls))
{
printk("向上提交目录失败\n");
ret = -PTR_ERR(cls);
goto OUT4;
}
printk("向上提交目录成功\n");
// 向上提交设备节点
for (i = 0; i < 3; i++)
{
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
if (IS_ERR(dev))
{
printk("向上提交设备信息失败\n");
ret = PTR_ERR(dev);
goto OUT5;
}
}
printk("自动创建设备节点成功\n");
return 0;
OUT5:
for (--i; i >= 0; i--)
{
device_destroy(cls, MKDEV(major, i));
}
// 销毁目录
class_destroy(cls);
OUT4:
cdev_del(cdev);
OUT3:
unregister_chrdev_region(devno, 3);
OUT2:
kfree(cdev);
OUT1:
return ret;
}
static void __exit mycdev_exit(void)
{
// 销毁设备节点信息
int i;
for (i = 0; i < 3; i++)
{
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
// 注销字符设备驱动对象
cdev_del(cdev);
// 释放设备号
unregister_chrdev_region(devno, 3);
// 释放对象空间
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");