字符设备
cdev结构体
Linux中使用cdev结构体描述一个字符设备。结构体定义在include/linux/cdev.h 文件中,
struct cdev{struct kobject kobj;struct module *owner; //所属模块const struct file_operations *ops; //文件操作结构体struct list_head list;dev_t dev; //设备号unsigned int count;
}
字符设备相关函数
使用 cdev_init 函数初始化 cdev结构体,成员变量,建立cdev 和file_operations 之间的联系
使用cdev_add 函数向系统添加一个cdv结构体,也就是添加一个字符设备。
使用cdev_delete函数删除一个字符设备;
file_operations结构体:
对设备节点进行文件操作时,最终会调用 设备驱动里的 file_operations 结构体里的文件操作函数:
例如:open、 release、read、write、ioctl、prode;
file_operations 结构体定义在include/linux/fs.h文件中
一个简化的 file_operations
结构体的定义示例:
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, loff_t, loff_t, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); void (*show_fdinfo)(struct seq_file *m, struct file *f); #ifdef CONFIG_AIO ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*aio_fsync) (struct kiocb *, int datasync); #endif /* ... 其他可能的成员函数 ... */
};
设备节点
每个设备在Linux系统中都有一个设备文件,这个设备文件就是通常所说的设备节点,应用程序通过操作这个设备文件,便可以操作相对应的硬件;
设备节点创建在、dev目录下;
创建设备节点的方式
1、手动创建
mknod /dev/test c 236 0
2、函数中自动创建
可以通过mdev机制自动创建
udev机制:
Linux中通过udev来实现设备节点的创建和删除,udev是一个用户程序,可以根据系统中设备的状态来创建或者删除设备节点,比如说,如果驱动程序成功加载到Linux时,会自动在/dev目录下创建相对应的设备节点,当驱动程序卸载时,会自动删除/dev目录下的设备节点,
在嵌入式linux系统中使用mdev,mdev时udev的简化板本,在使用busybox构建根文件系统时,busybox会自动创建mdev
相关函数
class_create函数
函数定义在 include/linux/device.h文件当中,使用这个函数,将会在系统的/sys/class 下创建一个文件;
#include <linux/device.h> struct class *class_create(struct module *owner, const char *name);
device_create 函数
使用class_create 创建好类之后,还需要 device_create 函数在类下面创建一个设备。定义在include/linux/device.h文件中
#include <linux/device.h> struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
device_destroy 函数
删掉创建的设备
class_destroy 函数
删掉创建的类
内核空间和用户空间的数据交换
应用层和内核不能直接进行数据传输,可以使用 copy_from_user,copy_to_user,需要包含头文件 Linux/uaccess.h,这些函数提供了对访问违规(如地址无效或越界)的检查,从而增加了内核的稳定性。
copy_to_user
内核空间数据copy到用户空间
#define copy_to_user(to, from, n) _copy_to_user((to), (from), (n))
copy_from_user
用户空间copy到内核空间
例程
#include <linux/module.h>
#include <linux/init.h>#include <linux/fs.h>
#include <linux/modulepram.h>
#include <linux/kdev_t.h>#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/asm-generic/uaccess.h> static int major;
static int minor; module_param(major,int,S_IRUGO);
MODULE_PARAM_DESC(major,"e.g:major=1");module_param(minor,int,S_IRUGO);
MODULE_PARAM_DESC(minor,"e.g:minor=1");static struct cdev cdev_test;struct class *class;
struct device *device;static int cdev_test_open(struct inode *node, struct file *file)
{printk("cdev_test_open \n");return 0;
}static int cdev_test_release(struct inode *node, struct file *file)
{printk("cdev_test_release \n");return 0;}static ssize_t cdev_test_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{char kbuf[64]="123456hello";copy_to_user(buf,kbuf,strlen(kbuf));printk("cdev_test_read \n");} static ssize_t cdev_test_write (struct file *file, const char __user *buf, size_t size, loff_t *off)
{char kbuf[64];copy_from_user(kbuf,buf,size);printk("write buf[%s]\n",kbuf);printk("cdev_test_write\n");}struct file_operations cdev_test_ops={.owner=THIS_MODULE,.open=cdev_test_open,.release=cdev_test_release,.read=cdev_test_read,.write=cdev_test_write,.ioctl=cdev_test_ioctl
};static int cdv_init(void)
{dev_t dev_num;int ret;printk("input major=%d minor=%d \n",major,minor);if(major == 0){//静态申请dev_num=MKDEV(major,minor);ret=register_chrdev_region(dev_num,1,"chrdev_num");if(0 != ret){printk("register_chrdev_region err %d\n",ret);return ret;}}else{//动态申请ret=alloc_chrdev_region(&dev_num,0,1,"chrdev_num");if(0 != ret){printk("alloc_chrdev_regionerr %d\n",ret);return ret;}printk("dev_num=%d \n",dev_num);major = MAJOR(dev_num);minor = MINOR(dev_num);printk("input major=%d minor=%d \n",major,minor);}cdev_test.owner= THIS_MODULE;cdev_init(&cdev_test,&&cdev_test_ops); cdev_add(&cdev_test,dev_num,1);class = class_create(THIS_MODULE,"test");device = device_create(class,NULL,dev_num,NULL,"/dev/test");printk("cdv init\n");return 0;
}static void cdv_exit(void)
{//释放设备号unregister_chrdev_region(dev_num,1);cdev_del(&cdev_test);device_destroy(class,dev_num);classs_destroy(class);printk("cdv exit\n");return 0;
}module_init(cdv_init);
module_exit(cdv_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("SONG");
MODULE_VERSION("v1.0");
APP应用程序
app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int arc, char *argv[])
{int fd;char buf[64]={0};fd = open("/dev/test",O_RDWR);if(fd < 0){printf("open err\n");}read(fd,buf,sizeof(buf));printf("read [%s]\n",buf);char buf1[64]="4578kkk";write(fd,buf1,strlen(buf1));close(fd);return 0;}