2020-04-08 17:00:51 +08:00
|
|
|
|
/**
|
2020-04-28 14:46:23 +08:00
|
|
|
|
* @file demo_misc.c
|
2020-04-08 17:00:51 +08:00
|
|
|
|
* @author Rick Chan (cy187lion@sina.com)
|
2020-04-28 14:46:23 +08:00
|
|
|
|
* @brief Linux misc driver demo.
|
|
|
|
|
在 Linux 驱动中,会把一些无法归类的设备定义为混杂设备,它们是拥
|
|
|
|
|
有着共同的特性的简单字符设备。它们的特点是共享统一的主设备号 10,
|
|
|
|
|
但每个设备可以选择一个单独的次设备号。
|
|
|
|
|
|
2020-04-28 15:53:24 +08:00
|
|
|
|
如果一个字符设备驱动要驱动多个设备,那么它就不应该用 misc 设备来
|
|
|
|
|
实现。
|
|
|
|
|
|
2020-04-28 14:46:23 +08:00
|
|
|
|
通常情况下,一个字符设备都不得不在初始化的过程中进行下面的步骤:
|
|
|
|
|
|
|
|
|
|
1.通过alloc_chrdev_region()分配主/次设备号
|
|
|
|
|
2.使用cdev_init()和cdev_add()来以一个字符设备注册自己
|
|
|
|
|
|
|
|
|
|
而一个misc驱动,则可以只用一个调用misc_register()来完成这所有
|
|
|
|
|
的步骤。
|
|
|
|
|
|
|
|
|
|
所有的 miscdevice 设备形成一个链表,对设备访问时,内核根据次设
|
|
|
|
|
备号查找对应的 miscdevice 设备,然后调用其 file_operations
|
|
|
|
|
中注册的文件操作方法进行操作。
|
|
|
|
|
* @version 0.1.1
|
2020-04-27 11:12:46 +08:00
|
|
|
|
* @date 2020-04-27
|
2020-04-08 17:00:51 +08:00
|
|
|
|
*
|
2020-04-27 11:12:46 +08:00
|
|
|
|
* @copyright Copyright (c) 2020
|
2020-04-08 17:00:51 +08:00
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
#include <linux/init.h>
|
2020-04-28 14:46:23 +08:00
|
|
|
|
#include <linux/miscdevice.h>
|
2020-04-08 17:00:51 +08:00
|
|
|
|
#include <linux/uaccess.h>
|
2020-04-27 14:51:40 +08:00
|
|
|
|
#include <linux/fs.h>
|
2020-04-08 17:00:51 +08:00
|
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Rick Chan");
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
2020-04-27 11:12:46 +08:00
|
|
|
|
#define DEMO_MAGIC 'R'
|
|
|
|
|
#define DEMO_IOC_SIZE 8
|
|
|
|
|
#define DEMO_CTL_IOC _IOC(_IOC_READ|_IOC_WRITE, DEMO_MAGIC, 0, DEMO_IOC_SIZE)
|
|
|
|
|
#define DEMO_CTL_IO _IO(DEMO_MAGIC, 1)
|
|
|
|
|
#define DEMO_CTL_IOR _IOR(DEMO_MAGIC, 2, uint16_t)
|
|
|
|
|
#define DEMO_CTL_IOW _IOW(DEMO_MAGIC, 3, int32_t)
|
|
|
|
|
#define DEMO_CTL_IOWR _IOWR(DEMO_MAGIC, 4, uint32_t)
|
|
|
|
|
#define DEMO_CTL_MAX 5
|
2020-04-08 17:00:51 +08:00
|
|
|
|
|
|
|
|
|
static int demo_open(struct inode *inode, struct file* filp)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int demo_release(struct inode *inode, struct file* filp)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static loff_t demo_llseek(struct file *filp, loff_t offset, int origin)
|
|
|
|
|
{
|
|
|
|
|
return filp->f_pos;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-28 15:48:32 +08:00
|
|
|
|
static ssize_t demo_read(struct file *filp, char __user *buffer, size_t count, loff_t *position)
|
2020-04-08 17:00:51 +08:00
|
|
|
|
{
|
2020-04-28 14:46:23 +08:00
|
|
|
|
const char __user *p = buffer;
|
|
|
|
|
return p-buffer;
|
2020-04-08 17:00:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-28 15:48:32 +08:00
|
|
|
|
static ssize_t demo_write(struct file *filp, const char __user *buffer, size_t count, loff_t *position)
|
2020-04-08 17:00:51 +08:00
|
|
|
|
{
|
|
|
|
|
const char __user *p = buffer;
|
|
|
|
|
return p-buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static long demo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
|
|
|
{
|
2020-04-28 14:46:23 +08:00
|
|
|
|
static int err = 0;
|
2020-04-27 11:12:46 +08:00
|
|
|
|
|
2020-04-28 14:46:23 +08:00
|
|
|
|
// 检测 cmd 合法性.
|
|
|
|
|
if(DEMO_MAGIC!=_IOC_TYPE(cmd))
|
2020-04-27 11:12:46 +08:00
|
|
|
|
return -EINVAL;
|
2020-04-28 14:46:23 +08:00
|
|
|
|
if(DEMO_CTL_MAX<_IOC_NR(cmd))
|
2020-04-27 11:12:46 +08:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
2020-04-28 14:46:23 +08:00
|
|
|
|
// 根据命令类型, 检测参数空间是否可以访问.
|
|
|
|
|
if(_IOC_DIR(cmd)&_IOC_READ)
|
|
|
|
|
err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
|
|
|
|
|
else if(_IOC_DIR(cmd)&_IOC_WRITE)
|
|
|
|
|
err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
|
|
// 根据命令, 来选择执行哪个分支.
|
2020-04-08 17:00:51 +08:00
|
|
|
|
switch(cmd)
|
|
|
|
|
{
|
2020-04-27 11:12:46 +08:00
|
|
|
|
case DEMO_CTL_IOC:
|
|
|
|
|
{
|
|
|
|
|
uint8_t tmp[DEMO_IOC_SIZE];
|
2020-04-08 17:00:51 +08:00
|
|
|
|
copy_from_user(&tmp, (void*)arg, _IOC_SIZE(cmd));
|
|
|
|
|
copy_to_user((void*)arg, &tmp, _IOC_SIZE(cmd));
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-04-27 11:12:46 +08:00
|
|
|
|
case DEMO_CTL_IO:
|
2020-04-08 17:00:51 +08:00
|
|
|
|
break;
|
2020-04-27 11:12:46 +08:00
|
|
|
|
case DEMO_CTL_IOR:
|
2020-04-08 17:00:51 +08:00
|
|
|
|
{
|
|
|
|
|
uint16_t tmp = 0x55AA;
|
|
|
|
|
copy_to_user((void*)arg, &tmp, _IOC_SIZE(cmd));
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-04-27 11:12:46 +08:00
|
|
|
|
case DEMO_CTL_IOW:
|
2020-04-08 17:00:51 +08:00
|
|
|
|
{
|
|
|
|
|
int32_t tmp = 0;
|
|
|
|
|
copy_from_user(&tmp, (void*)arg, _IOC_SIZE(cmd));
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-04-27 11:12:46 +08:00
|
|
|
|
case DEMO_CTL_IOWR:
|
2020-04-08 17:00:51 +08:00
|
|
|
|
{
|
|
|
|
|
uint32_t tmp = 0;
|
|
|
|
|
copy_from_user(&tmp, (void*)arg, _IOC_SIZE(cmd));
|
|
|
|
|
copy_to_user((void*)arg, &tmp, _IOC_SIZE(cmd));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return -ENOIOCTLCMD;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int demo_mmap(struct file* filp, struct vm_area_struct* vma)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct file_operations demo_fops = {
|
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
|
.open = demo_open,
|
|
|
|
|
.release = demo_release,
|
|
|
|
|
.llseek = demo_llseek,
|
|
|
|
|
.read = demo_read,
|
|
|
|
|
.write = demo_write,
|
|
|
|
|
.unlocked_ioctl = demo_ioctl,
|
|
|
|
|
.mmap = demo_mmap
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-28 14:46:23 +08:00
|
|
|
|
static struct miscdevice demo_dev = {
|
|
|
|
|
.minor = MISC_DYNAMIC_MINOR, // Dynamically allocate minor.
|
|
|
|
|
.name = "demo_misc",
|
|
|
|
|
.fops = &demo_fops,
|
|
|
|
|
};
|
2020-04-08 17:00:51 +08:00
|
|
|
|
|
|
|
|
|
static int __init demo_init(void)
|
|
|
|
|
{
|
2020-04-28 14:46:23 +08:00
|
|
|
|
misc_register(&demo_dev); // 注册混杂设备驱动.
|
2020-04-08 17:00:51 +08:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __exit demo_exit(void)
|
|
|
|
|
{
|
2020-04-28 14:46:23 +08:00
|
|
|
|
misc_deregister(&demo_dev); // 撤销混杂设备驱动.
|
2020-04-08 17:00:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module_init(demo_init);
|
2020-04-27 11:12:46 +08:00
|
|
|
|
module_exit(demo_exit);
|