字符设备
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;
}