/*
* 说明:用于演示一个驱动如何管理多个设备。
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "ioctl.h"
#define FSDEV_MAJOR 250
#define FSDEV_MINOR 0
#define FSDEV_NR 2
#define FSDEV_NAME "fsdev"
struct fsdev {
struct cdev cdev;
unsigned char buf[256];
};
static struct fsdev *fsdev[FSDEV_NR];
static int fschr_open(struct inode *inode, struct file *filp)
{
struct fsdev *fsdev;
/* 根据保存在inode节点中的cdev指针,反向查询到包含该cdev的结构体,
* 将该结构体指针保存在打开的文件结构体的私有数据中,可以在之后
* 的read、write以及ioctl当中通过file结构体指针顺利获得反查得到的
* 结构体指针,这是实现一个驱动管理多个设备的关键
*/
fsdev = container_of(inode->i_cdev, struct fsdev, cdev);
filp->private_data = fsdev;
return 0;
}
static int fschr_close(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t fschr_read(struct file *filp, char __user *buf, size_t count, loff_t *fops)
{
int len;
int ret;
struct fsdev *fsdev = filp->private_data;//通过filp的private_data得到是哪一个设备的操作
len = count > 256 ? 256 : count;
ret = copy_to_user(buf, fsdev->buf, len);
return len - ret;
}
static ssize_t fschr_write(struct file *filp, const char __user *buf, size_t count, loff_t *fops)
{
int len;
int ret;
struct fsdev *fsdev = filp->private_data;
len = count > 256 ? 256 : count;
ret = copy_from_user(fsdev->buf, buf, len);
return len - ret;
}
static int fschr_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
struct fsdev *fsdev = filp->private_data;
if (_IOC_TYPE(cmd) != FS_IOC_MAGIC)
return -ENOTTY;
if (_IOC_NR(cmd) > FS_IOC_MAXNR)
return -ENOTTY;
if (_IOC_DIR(cmd) & _IOC_READ)
ret = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
ret = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (ret)
return -EFAULT;
switch (cmd) {
case FS_IOC_SET_BUF:
memset(fsdev->buf, *(char *)arg, sizeof(fsdev->buf));
break;
}
return 0;
}
static struct file_operations fsfops = {
.owner = THIS_MODULE,
.open = fschr_open,
.release = fschr_close,
.read = fschr_read,
.write = fschr_write,
.ioctl = fschr_ioctl
};
static int __init fschr_init(void)
{
int i;
int ret;
int alloc_num = 0;
int add_num = 0;
dev_t devno;
devno = MKDEV(FSDEV_MAJOR, FSDEV_MINOR);
ret = register_chrdev_region(devno, FSDEV_NR, FSDEV_NAME);
if (ret) {
printk(KERN_ERR "fschr: register chrdev region failed\n");
goto reg_err;
}
for (i = 0; i < FSDEV_NR; i++)
{
fsdev[i] = (struct fsdev *)kzalloc(sizeof(struct fsdev), GFP_KERNEL);
if (!fsdev[i]) {
printk(KERN_ERR "fschr: kzalloc failure\n");
alloc_num = i;
ret = -ENOMEM;
goto alloc_err;
}
}
alloc_num = FSDEV_NR;
for (i = 0; i < FSDEV_NR; i++) {
cdev_init(&fsdev[i]->cdev, &fsfops);
fsdev[i]->cdev.owner = THIS_MODULE;
ret = cdev_add(&fsdev[i]->cdev, devno + i, 1);
if (ret) {
printk(KERN_ERR "fschr: add cdev failed");
add_num = i;
goto add_err;
}
}
return 0;
add_err:
for (i = 0; i < add_num; i++)
cdev_del(&fsdev[i]->cdev);
alloc_err:
for (i = 0; i < alloc_num; i++)
kfree(fsdev[i]);
unregister_chrdev_region(devno, FSDEV_NR);
reg_err:
return ret;
}
static void __exit fschr_exit(void)
{
int i;
dev_t devno;
devno = MKDEV(FSDEV_MAJOR, FSDEV_MINOR);
for (i = 0; i < FSDEV_NR; i++)
cdev_del(&fsdev[i]->cdev);
for (i = 0; i < FSDEV_NR; i++)
kfree(fsdev[i]);
unregister_chrdev_region(devno, FSDEV_NR);
}
module_init(fschr_init);
module_exit(fschr_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("xxxx<xxxx@xxxx.com>");
MODULE_DESCRIPTION("Character device driver example for FSC100 development board");
ioctl.h
#ifndef __IOCTL__
#define __IOCTL__
#define FS_IOC_MAGIC 'f'
#define FS_IOC_MAXNR 1
#define FS_IOC_SET_BUF _IOW(FS_IOC_MAGIC, 1, char)
#endif