1.Linux字符设备注册相关步骤:
1.1 初始化cdev:
一个 cdev 一般有两种定义初始化方式:静态的和动态的。
静态内存定义初始化:
struct cdev my_cdev;
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
动态内存定义初始化:
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &fops;
my_cdev->owner = THIS_MODULE;
1.2 注册设备号:
主要涉及到的函数:
int register_chrdev_region(dev_t, unsigned, const char *); //静态的申请和注册设备号
int alloc_chrdev_region(dev_t, unsigned, const char *); //动态的申请注册一个设备号
int register_chrdev(unsigned int, const char *,struct file_operations *);//int为0时候动态注册,非零时候静态注册。
静态注册:
1.首先需要定义一个dev_t变量来作为一个设备号,
dev_t dev_num;
2.要想注册一个设备则需要一个主设备号。根据主设备号获取设备号。
dev_num=MKDEV(major,minor);
major是一个表示设备号的主设备号,minor次设备号
3.注册:
register_chrdev_region(dev_num,2,"dev_name");
动态注册:
如果我们提前知道设备的编号,那么就用register_chrdev_region(),但是如果我们不知道呢,我们就使用动态申请设备编号。
1.首先需要定义一个dev_t变量来作为一个设备号,
dev_t dev_num;
2.动态分配设备号:
alloc_chrdev_region(&dev_num, minor, 2, "dev_name");
第一个参数保存生成的设备号,第二个参数注册设备的数目,第三个参数表示设备名称。
3.根据设备号获取主设备号:
dev_major = MAJOR(dev_num);
3.向内核增加设备:
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
参数1:字符结构体,参数2:设备号,参数3:个数
返回0表示注册成功,不然表示注册失败
4.注销设备
int unregister_chrdev(unsigned int, const char *); //注销设备号
void unregister_chrdev_region(dev_t, unsigned); //注销设备号
5.app访问设备
运行代码后发现/dev目录下没有长出设备节点,原因如下:
在2.6内核之前通过函数cdev_init和cdev_add添加字符设备,另外还需要手动创建设备节点;
在2.6之后的内核,通过cdev_init和cdev_add添加字符设备,通过class_create和device_create函数往sys文件系统中添加设备,udev检测到/sys目录的变动会根据变化在/dev目录下创建对应的设备节点。
因为在linux里面有两种设备文件系统,devfs和sysfs
devfs
1) 需要手动创建设备节点mknod fasync_dev c 250 0
2) 或者在fasync_dev_init函数中添加:
#ifdef CONFIG_DEVFS_FS //支持devfs文件系统,在内核里面配置
devfs_mk_cdev(设备号, S_IFCHR | S_IRUGO | S_IWUSR, 设备名称)
#endif
sysfs
sysfs通过class_create和device_create在设备树中创建相应的设备,应用层udev会自动根据设备树的变化生成相应的设备节点。
————————————————
版权声明:本文为CSDN博主「luckywang1103」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luckywang1103/article/details/47860805/
2.示例代码:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
int dev1_registers[5];
int dev2_registers[5];
struct cdev cdev;
dev_t devno;
/*文件打开函数*/
int mem_open(struct inode *inode, struct file *filp)
{
/*获取次设备号*/
int num = MINOR(inode->i_rdev);
if (num==0)
filp->private_data = dev1_registers;
else if(num == 1)
filp->private_data = dev2_registers;
else
return -ENODEV; //无效的次设备号
return 0;
}
/*文件释放函数*/
int mem_release(struct inode *inode, struct file *filp)
{
return 0;
}
/*读函数*/
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
int *register_addr = filp->private_data; /*获取设备的寄存器基地址*/
/*判断读位置是否有效*/
if (p >= 5*sizeof(int))
return 0;
if (count > 5*sizeof(int) - p)
count = 5*sizeof(int) - p;
/*读数据到用户空间*/
if (copy_to_user(buf, register_addr+p, count))
{
ret = -EFAULT;
}
else
{
*ppos += count;
ret = count;
}
return ret;
}
/*写函数*/
static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
int *register_addr = filp->private_data; /*获取设备的寄存器地址*/
/*分析和获取有效的写长度*/
if (p >= 5*sizeof(int))
return 0;
if (count > 5*sizeof(int) - p)
count = 5*sizeof(int) - p;
/*从用户空间写入数据*/
if (copy_from_user(register_addr + p, buf, count))
ret = -EFAULT;
else
{
*ppos += count;
ret = count;
}
return ret;
}
/* seek文件定位函数 */
static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)
{
loff_t newpos;
switch(whence) {
case SEEK_SET:
newpos = offset;
break;
case SEEK_CUR:
newpos = filp->f_pos + offset;
break;
case SEEK_END:
newpos = 5*sizeof(int)-1 + offset;
break;
default:
return -EINVAL;
}
if ((newpos<0) || (newpos>5*sizeof(int)))
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
/*文件操作结构体*/
static const struct file_operations mem_fops =
{
.llseek = mem_llseek,
.read = mem_read,
.write = mem_write,
.open = mem_open,
.release = mem_release,
};
/*设备驱动模块加载函数*/
static int memdev_init(void)
{
/*初始化cdev结构*/
cdev_init(&cdev, &mem_fops);
/* 注册字符设备 */
alloc_chrdev_region(&devno, 0, 2, "memdev");
cdev_add(&cdev, devno, 2);
}
/*模块卸载函数*/
static void memdev_exit(void)
{
cdev_del(&cdev); /*注销设备*/
unregister_chrdev_region(devno, 2); /*释放设备号*/
}
MODULE_LICENSE("GPL");
module_init(memdev_init);
module_exit(memdev_exit);
3.其他说明:
3.1错误的定义初始化方式:
struct cdev *ptr_my_cdev = cdev_alloc();//变量在heap中
cdev_init(ptr_my_cdev , &fops);
ptr_my_cdev ->owner = THIS_MODULE;
原因可以参考:
cdev_alloc和cdev_init 的使用_qinrenzhi的博客-CSDN博客