1. cdev结构体
struct cdev {
struct kobject kobj; //内嵌的kobject对象
struct module *owner; //所属模板
const struct file_operations *ops; //文件操作的结构体
struct list_head list;
dev_t dev; //设备号
unsigned int count;
};
1.1 cdev的相关操作
void cdev_init(struct cdev *, const struct file_operations *); //初始化使其和文件操作结构相连接
struct cdev *cdev_alloc(void); //为cdev分配内存
int cdev_add(struct cdev *, dev_t, unsigned); //向内核注册一个设备
void cdev_del(struct cdev *); //从内核删除一个设备
1.2 设备号的分配
1.21 主次设备号和dev_t的相互转换
由dev_t号获取主次设备号
MAJOR(dev_t dev)
MINOR(dev_t dev)
由主次设备号获得dev_t
MKDEV(int major,int minor)
1.22 获取及注销设备号
int register_chrdev_region(dev_t from,unsigned count,const char *name); //手动注册
int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name); //由内核分配
void unregion_chrdev_region(dev_t from,unsigned count); //注销设备号
2. 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(*aio_read) (struct kiocb *, char __user *, size_t, loff_t); //异步读
ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *); //写操作
ssize_t(*aio_write) (struct kiocb *, const char __user *, size_t, loff_t); //异步写
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *); //设备是否可以被非阻塞的读写
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); //文件相关控制命令
int (*mmap) (struct file *, struct vm_area_struct *); //文件映射
int (*open) (struct inode *, struct file *); //打开文件
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *); //关闭文件
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t(*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t(*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t(*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void __user *);
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);
};
3. inode结构体
struct inode{
umode_t i_mode; /* 访问权限控制 */
uid_t i_uid; /* 使用者id */
gid_t i_gid; /* 使用者id组 */
kdev_t i_rdev; /* 实设备标识符 */
loff_t i_size; /* 以字节为单位的文件大小 */
struct timespec i_atime; /* 最后访问时间 */
struct timespec i_mtime; /* 最后修改(modify)时间 */
struct timespec i_ctime; /* 最后改变(change)时间 */
unsigned int i_blkbits; /* 以位为单位的块大小 */
unsigned long i_blocks; /* 文件的块数 */
};
4. file 结构体
struct file {
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
const struct file_operations *f_op;
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode;
loff_t f_pos;
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
struct file_ra_state f_ra;
unsigned long f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
void *private_data; //私有数据
#ifdef CONFIG_EPOLL
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif
struct address_space *f_mapping;
};
5. 自动创建设备文件
class_create(owner, name);//创建设备类
struct device *device_create(struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, ...)//创建文件
6. 内核空间与用户空间的数据的读写
copy_from_user(void *to, const void __user *from, unsigned long n); //用户区到内核区
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);//内核区到用户区
7. 实例
在内核区开辟256字节的内存为虚拟的设备,然后进行读写操作(参看宋宝华的linux驱动开发详解)
/*
* globamem.c
*
* Created on: 2016年11月17日
* Author: chy
*/
#include <linux/stat.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/gfp.h>
#include <linux/io.h>
#define buff_size 256 //设备缓冲区的大小
#define dev_num 2 //设备的个数
//#define DEBUG //若打印调试信息则开启
struct globamem{ //设备描述
char buff[buff_size];
ssize_t size;
};
struct globamem *mem = NULL;
static int open_file(struct inode *node,struct file *g_file) //打开文件
{
int minor = MINOR(node->i_rdev); //获取此设备号
struct globamem *dev = &mem[minor];
g_file->private_data = dev;
return 0;
}
static int close_file(struct inode *node,struct file *g_file) //关闭
{
return 0;
}
static ssize_t read_file(struct file *g_file,char __user *buf,size_t size,loff_t *pos)//读
{
loff_t port = *pos;
int ans = 0;
struct globamem *temp = g_file->private_data;
ssize_t count = size;
#ifdef DEBUG
printk(KERN_INFO "read file = %s pos=%ld %ld\n",temp->buff,port,g_file->f_pos);
#endif
if(port >= strlen(temp->buff) || port > buff_size)
return ans;
if(port + count > strlen(temp->buff))
count = strlen(temp->buff) - port;
else count = strlen(temp->buff);
if(copy_to_user(buf,temp->buff + port,count + 1))
ans = -EFAULT;
else{
*pos += count;
ans = count;
}
#ifdef DEBUG
printk(KERN_INFO "read over %d %s\n",ans,temp->buff);
#endif
return ans;
}
static ssize_t write_file(struct file *g_file,const char __user *buf,size_t size,loff_t *pos) //写
{
unsigned long port = *pos;
int ans = 0;
struct globamem *temp = g_file->private_data;
ssize_t file_size = size;
#ifdef DEBUG
printk(KERN_INFO "write file\n");
#endif
if(port >= buff_size)
return ans;
if(strlen(buf) > buff_size - port)
file_size = buff_size - port;
else file_size = strlen(buf);
if(copy_from_user(temp->buff + port,buf,file_size + 1))
ans = -EFAULT;
else{
*pos += file_size;
ans = file_size;
}
return ans;
}
static long g_ioctl(struct file *g_file,unsigned int cmd,unsigned long arg) //IO控制命令
{
struct globamem *g = g_file->private_data;
switch(cmd){
case 0: memset(g->buff,0,buff_size); break;
default: return -EINVAL;
}
return 0;
}
static const struct file_operations fos = { //文件操作
.owner = THIS_MODULE,
.read = read_file,
.write = write_file,
.open = open_file,
.release = close_file,
.unlocked_ioctl = g_ioctl,
};
dev_t dev_no;
struct cdev dev;
struct class *dev_class = NULL;
static int globamem_init(void) //加载
{
int ret;
ret = alloc_chrdev_region(&dev_no,0,2,"globamem_s"); //自动分配设备号
if(ret < 0)
return ret;
mem = (struct globamem*)kmalloc(sizeof(struct globamem) * dev_num,GFP_KERNEL);
dev.owner = THIS_MODULE;
cdev_init(&dev,&fos); //初始化设备
cdev_add(&dev,dev_no,2); //向内核注册设备
/*生成设备文件*/
dev_class = class_create(THIS_MODULE,"globamem_class");
device_create(dev_class, NULL, MKDEV(MAJOR(dev_no),0), NULL, "globamem_1_dev");
device_create(dev_class, NULL, MKDEV(MAJOR(dev_no),1), NULL, "globamem_2_dev");
return 0;
}
static void globamem_exit(void) //卸载
{
int i;
for(i = 0; i < dev_num; i++)
kfree(&mem[i]);
cdev_del(&dev); //删除设备
/*删除设备文件*/
device_destroy(dev_class, MKDEV(MAJOR(dev_no),0));
device_destroy(dev_class,MKDEV(MAJOR(dev_no),1));
class_destroy(dev_class);
unregister_chrdev_region(dev_no,2); //释放设备号
return;
}
MODULE_LICENSE("GPL v2");
module_init(globamem_init);
module_exit(globamem_exit);