文章目录
- linux 驱动中的私有数据
- container_of
- 驱动程序
- 数据结构定义
- 应用程序
- 模块使用
linux 驱动中的私有数据
前面的程序中,都只申请了一个从设备号,这里使用 alloc_chrdev_region
分配两个设备号,这两个设备共用 ops
方法。
所以需要在 ops
方法中区分两个设备
container_of
参考:linux——宏 list_entry/container_of
驱动程序
数据结构定义
typedef struct {dev_t dev_id; /* 设备号 */struct cdev c_dev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */int minor; /* 次设备号 */char write_buf[100];char read_buf[100];
} new_chrdev_t;static new_chrdev_t new_chrdev1;
static new_chrdev_t new_chrdev2;
这里定义了两个设备,分别为 new_chrdev1
和 ·new_chrdev2·
- new_chrdev1
- 设备号:通过
alloc_chrdev_region
分配; - 主设备号:major = MAJOR(dev_id);
- 次设备号:minor = MINOR(dev_id);
- 设备号:通过
- new_chrdev2
- 主设备号:major = MAJOR(new_chrdev1.dev_id);
- 次设备号:minor = MINOR(new_chrdev1.dev_id) + 1;
- 设备号: dev_id = MKDEV(new_chrdev2.major, new_chrdev2.minor);
程序源码
#include "linux/device/class.h"
#include "linux/export.h"
#include "linux/uaccess.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>#define CHRDEVBASE_NAME "chrdevbase" /* 设备名 */
#define CHRDEVBASE_NUM 2 /* 设备数目 */static char *string_test = "kernel data this tyustli test";typedef struct {dev_t dev_id; /* 设备号 */struct cdev c_dev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */int minor; /* 次设备号 */char write_buf[100];char read_buf[100];
} new_chrdev_t;static new_chrdev_t new_chrdev1;
static new_chrdev_t new_chrdev2;static int chrdevbase_open(struct inode *inode, struct file *file)
{new_chrdev1.minor = 0;new_chrdev2.minor = 1;file->private_data =container_of(inode->i_cdev, new_chrdev_t, c_dev); /* 设置私有数据 */printk("k: chrdevbase open\r\n");return 0;
}static ssize_t chrdevbase_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{unsigned long ret = 0;new_chrdev_t *dev = (new_chrdev_t *)file->private_data;printk("k: chrdevbase read major %d\r\n", dev->major);memcpy(dev->read_buf, string_test, strlen(string_test));ret = copy_to_user(buf, dev->read_buf, count);if (ret == 0) {printk("k: read data success\r\n");} else {printk("k: read data failed ret = %ld\r\n", ret);}return ret;
}static ssize_t chrdevbase_write(struct file *file, const char __user *buf,size_t count, loff_t *ppos)
{unsigned long ret = 0;printk("k: chrdevbase write\r\n");new_chrdev_t *dev = (new_chrdev_t *)file->private_data;printk("k: chrdevbase read minor %d\r\n", dev->minor);ret = copy_from_user(dev->write_buf, buf, count);if (ret == 0) {printk("k: write data success write data is: %s\r\n", dev->write_buf);} else {printk("k: write data failed ret = %ld\r\n", ret);}return count;
}static int chrdevbase_release(struct inode *inode, struct file *file)
{printk("k: chrdevbase release\r\n");return 0;
}static struct file_operations chrdevbase_fops = {.owner = THIS_MODULE,.open = chrdevbase_open,.read = chrdevbase_read,.write = chrdevbase_write,.release = chrdevbase_release,
};static int __init chrdevbase_init(void)
{int err = 0;err = alloc_chrdev_region(&new_chrdev1.dev_id, 0, CHRDEVBASE_NUM,CHRDEVBASE_NAME);if (err < 0) {printk("k: alloc chrdev region failed err = %d\r\n", err);goto err_chrdev;}/* get major 1 and minor 1 */new_chrdev1.major = MAJOR(new_chrdev1.dev_id);new_chrdev1.minor = MINOR(new_chrdev1.dev_id);printk("k: newcheled major=%d,minor=%d\r\n", new_chrdev1.major,new_chrdev1.minor);new_chrdev1.c_dev.owner = THIS_MODULE;cdev_init(&new_chrdev1.c_dev, &chrdevbase_fops);err = cdev_add(&new_chrdev1.c_dev, new_chrdev1.dev_id, 1);if (err < 0) {printk("k: cdev add failed err = %d\r\n", err);goto err_cdev_add;}new_chrdev1.class = class_create("chr_test1");if (IS_ERR(new_chrdev1.class)) {err = PTR_ERR(new_chrdev1.class);goto err_class_create;}new_chrdev1.device = device_create(new_chrdev1.class, NULL,new_chrdev1.dev_id, NULL, "chr_test1");if (IS_ERR(new_chrdev1.device)) {err = PTR_ERR(new_chrdev1.device);goto err_device_create;}/* get major 2 and minor 2 */new_chrdev2.major = MAJOR(new_chrdev2.dev_id);new_chrdev2.minor = MINOR(new_chrdev2.dev_id) + 1;new_chrdev2.dev_id = MKDEV(new_chrdev2.major, new_chrdev2.minor);printk("k: newcheled major=%d,minor=%d\r\n", new_chrdev2.major,new_chrdev2.minor);new_chrdev2.c_dev.owner = THIS_MODULE;cdev_init(&new_chrdev2.c_dev, &chrdevbase_fops);err = cdev_add(&new_chrdev2.c_dev, new_chrdev2.dev_id, 1);if (err < 0) {printk("k: cdev add failed err = %d\r\n", err);goto err_cdev_add;}new_chrdev2.class = class_create("chr_test2");if (IS_ERR(new_chrdev2.class)) {err = PTR_ERR(new_chrdev2.class);goto err_class_create;}new_chrdev2.device = device_create(new_chrdev2.class, NULL, new_chrdev2.dev_id, NULL, "chr_test2");if (IS_ERR(new_chrdev2.device)) {err = PTR_ERR(new_chrdev2.device);goto err_device_create;}printk("k: base module init\r\n");return 0;err_device_create:class_destroy(new_chrdev1.class);class_destroy(new_chrdev2.class);
err_class_create:cdev_del(&new_chrdev1.c_dev);cdev_del(&new_chrdev2.c_dev);
err_cdev_add:unregister_chrdev_region(new_chrdev1.dev_id, CHRDEVBASE_NUM);
err_chrdev:return err;
}static void __exit chrdevbase_exit(void)
{device_destroy(new_chrdev1.class, new_chrdev1.dev_id);device_destroy(new_chrdev2.class, new_chrdev2.dev_id);class_destroy(new_chrdev1.class);class_destroy(new_chrdev2.class);cdev_del(&new_chrdev1.c_dev);cdev_del(&new_chrdev2.c_dev);unregister_chrdev_region(new_chrdev1.dev_id, CHRDEVBASE_NUM);printk("k: base module exit!\r\n");
}module_init(chrdevbase_init);
module_exit(chrdevbase_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("tyustli");
MODULE_INFO(intree, "Y"); /* loading out-of-tree module taints kernel */
应用程序
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"static char usrdata[] = { "user data!" };int main(int argc, char *argv[])
{int fd, retvalue;char *filename;char readbuf[100], writebuf[100];if (argc != 3) {printf("u: error Usage!\r\n");return -1;}filename = argv[1];/* 打开驱动文件 */fd = open(filename, O_RDWR);if (fd < 0) {printf("u: can't open file %s\r\n", filename);return -1;}/* 从驱动文件读取数据 */if (atoi(argv[2]) == 1) {retvalue = read(fd, readbuf, 50);if (retvalue < 0) {printf("u: read file %s failed!\r\n", filename);} else {/* 读取成功,打印出读取成功的数据 */printf("u: read data:%s\r\n", readbuf);}}/* 向设备驱动写数据 */if (atoi(argv[2]) == 2) {memcpy(writebuf, usrdata, sizeof(usrdata));retvalue = write(fd, writebuf, 50);if (retvalue < 0) {printf("u: write file %s failed!\r\n", filename);}}/* 关闭设备 */retvalue = close(fd);if (retvalue < 0) {printf("u: can't close file %s\r\n", filename);return -1;}return 0;
}
模块使用
查看设备节点
ls /dev/
模块安装
modprobe my_module
模块使用
#设备1
lib/modules/6.5.7+/my_app /dev/chr_test1 1
lib/modules/6.5.7+/my_app /dev/chr_test1 2
#设备2
lib/modules/6.5.7+/my_app /dev/chr_test2 1
lib/modules/6.5.7+/my_app /dev/chr_test2 2
模块打印日志
~ # lib/modules/6.5.7+/my_app /dev/chr_test1 1
k: chrdevbase open
k: chrdevbase read major 248
k: read data success
u: read data:kernel data this tyustli test
k: chrdevbase release
~ # lib/modules/6.5.7+/my_app /dev/chr_test1 2
k: chrdevbase open
k: chrdevbase write
k: chrdevbase read minor 0
k: write data success write data is: user data!
k: chrdevbase release
~ # lib/modules/6.5.7+/my_app /dev/chr_test2 1
k: chrdevbase open
k: chrdevbase read major 248
k: read data success
u: read data:kernel data this tyustli test
k: chrdevbase release
~ # lib/modules/6.5.7+/my_app /dev/chr_test2 2
k: chrdevbase open
k: chrdevbase write
k: chrdevbase read minor 1
k: write data success write data is: user data!
k: chrdevbase release
~ #