頭文件:
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
相關內容
2.6內核中使用cdev結構描述一個字符設備。
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
從dev_t獲取主次設備號
MAJOR(dev_t dev)
MINOR(dev_t dev)
#define MINORBITS
20
#define MINORMASK ((1U << MINORBITS) - 1)
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
由主設備號和此設備號生成設備號dev_t
MKDEV(int major,int minor)
#define MKDEV(ma,mi)
(((ma) << MINORBITS) | (mi))
申請設備號
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);
2.6內核操作cdev的一組函數
void cdev_init(struct cdev *,struct file_operations *);
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *,dev_t,unsigned);
void cdev_del(struct cdev *);
struct file_operations {
struct module *owner;//擁有該模塊的指針,一般賦值爲THIS_MODULES
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 (*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 (*readdir) (struct file *, void *, filldir_t);//僅用於讀取目錄,對於設備文件,該字段爲NULL
unsigned int (*poll) (struct file *, struct poll_table_struct *); //輪訓函數,判斷目前是否可以以非阻塞方式讀/寫
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
//不適用BLK,的文件系統,用該函數代替ioctl
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
//64爲系統的32位ioctl將用此函數指針代替
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 *, struct dentry *, int datasync);//刷新待處理的數據
int (*aio_fsync) (struct kiocb *, int datasync);//異步 fsync
int (*fasync) (int, struct file *, int);//通知FASYNC標誌發生變化
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);
//通常爲NULL
int (*check_flags)(int);
//允許模塊檢查傳遞給fcntl(F_SETEL...)調用的標誌
int (*dir_notify)(struct file *filp, unsigned long arg);
//對文件系統有效,驅動程序不必實現
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
//VFS調用,將管道數據粘接到文件
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
//VFS調用,將文件數據粘接到管道
int (*setlease)(struct file *, long, struct file_lock **);
};
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}
struct cdev *cdev_alloc(void) { struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL); if (p) { INIT_LIST_HEAD(&p->list); kobject_init(&p->kobj, &ktype_cdev_dynamic); } return p; }
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
p->dev = dev;
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}
void cdev_del(struct cdev *p)//調用該函數後需調用unregister_chrdev_region() 釋放申請到的設備號
{
cdev_unmap(p->dev, p->count);
kobject_put(&p->kobj);
}
void unregister_chrdev_region(dev_t from,unsigned count);
字符驅動模塊加載與卸載函數模板
/*設備結構體*/
struct xxx_dev_t{
struct cdev cdev;
...
} xxx_dev;
//驅動模塊加載函數
static int __init xxx_init(void)
{
...
cdev_init(&xxx_dev.cdev,&xxx_fops); // 初始化
xxx_dev.owmer = THIS_MODULE;
if(xxx_major){ // 申請設備號
register_chdev_region(xxx_dev_no,num,DEV_NAME);
}else{
alloc_chrdev_region(&xxx_dev_no,base,num,DEV_NAME);
}
ret = cdev_add(&xxx_dev.cdev,xxx_dev_no,num); // 註冊設備號
}
//模塊卸載函數
static void __exit xxx_exit(void)
{
unregister_chrdev_region(xxx_dev_no,num);
cdev_del(&xxx_dev.cdev);
...
}
字符設備驅動讀,寫,IO控制模板
ssize_t xxx_read(struct file *filp,char __user *buf,szie_t count,loff_t *f_pos)
{
...
copy_to_user(buf,...,...);
...
}
ssize_t xxx_write(struct file *filp,const char __user *buf,szie_t count,loff_t *f_pos)
{
...
copy_from_user(...,buf,...);
...
}
/*內核空間與用戶空間不能直接訪問,續借助一下兩個函數,返回值爲不能被賦值的字節數*/
unsigned long 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)
//複製的是char、int、long可使用put_user()get_user()
int xxx_ioctl(struct inode *inode,struct file *filep,unsigned int cmd,unsigned long arg)
{
命令有效性檢測
依據命令類型,檢測參數空間是否可以訪問
switch(cmd){
case XXX_CMD1:
...
break;
case XXX_CMD1:
...
break;
default://不支持的命令
return -ENOTTY;
}
return 0;
}
文件操作函數模板
struct file_operations xxx_fops = {
.owner = THIS_MODULE,
.read = xxx_read,
.write = xxx_write,
.iotcl = xxx_ioctl,
...
};
驅動中同時包含兩個以上的設備在xxxopen中通過container_of通過結構體成員的指針找到對應結構體的指針
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
當然也可以通過inode結構體中的i_rdev成員獲取其次設備號