代码清单:
//复杂的杂项设备例子可以参考内核apm-emulation.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
//ioctl cmd <->应用空间需要和内核定义一致
#define MISCDEV_MAGIC_CODE 'M'
#define MISCDEV_CLEAR_DATA _IO(MISCDEV_MAGIC_CODE,1)
#define MISCDEV_READ_DATA _IOR(MISCDEV_MAGIC_CODE,2,int)
#define MISCDEV_WRITE_DATA _IOW(MISCDEV_MAGIC_CODE,3,int)
#define MISCDEV_NAME "misc_panda"
#define MISCDEV_DATA_SIZE 0x2000
struct Miscdev_User
{
char data[MISCDEV_DATA_SIZE];
};
static int miscdev_open(struct inode *inode, struct file *file)
{
struct Miscdev_User *usr_data = NULL;
printk("%s\n",__func__);
usr_data = kzalloc(sizeof(struct Miscdev_User), GFP_KERNEL);
if(!usr_data)
{
return -ENOMEM;
}
file->private_data = usr_data;
return 0;
}
static int miscdev_release(struct inode *inode, struct file *file)
{
struct Miscdev_User *usr_data = file->private_data;
file->private_data = NULL;
kfree(usr_data);
printk("%s\n",__func__);
return 0;
}
static loff_t miscdev_llseek(struct file *file, loff_t offset, int whence)
{
loff_t ret = offset;
loff_t pos = 0;
printk("%s\n",__func__);
if(!file->private_data)
{
return -EINVAL;
}
switch(whence){
case SEEK_SET:
if(offset < 0 || offset > MISCDEV_DATA_SIZE ){
ret = -EINVAL;
break;
}
file->f_pos = offset;
ret = file->f_pos;
break;
case SEEK_CUR:
case SEEK_END:
pos = file->f_pos + offset;
if(pos < 0 || pos > MISCDEV_DATA_SIZE ){
ret = -EINVAL;
break;
}
file->f_pos = pos;
ret = file->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static ssize_t miscdev_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{
int ret = 0;
loff_t offset = *ppos;
size_t size = count;
struct Miscdev_User *usr_data = file->private_data;
if(!usr_data)
{
return -EINVAL;
}
printk("%s\n",__func__);
if(offset >= MISCDEV_DATA_SIZE|| offset < 0 || size < 0){
return 0;
}
if(size > (MISCDEV_DATA_SIZE - offset)){
size = MISCDEV_DATA_SIZE - offset;
}
ret = copy_to_user(buf, usr_data->data + offset, size);//成功,返回0,失败返回未copy成功的数据字节数
if(ret){
return -EFAULT;
}
else {
*ppos += size;
ret = size;
}
return ret;
}
static ssize_t miscdev_write(struct file *file, const char __user *buf,size_t count, loff_t * ppos)
{
int ret = 0;
loff_t offset = *ppos;
size_t size = count;
struct Miscdev_User *usr_data = file->private_data;
if(!usr_data)
{
return -EINVAL;
}
printk("%s\n",__func__);
if(offset >= MISCDEV_DATA_SIZE || offset < 0 || size < 0){
return 0;
}
if(size > (MISCDEV_DATA_SIZE - offset)){
size = MISCDEV_DATA_SIZE - offset;
}
ret = copy_from_user(usr_data->data + offset,buf, size);//成功,返回0,失败返回未copy成功的数据字节数
if(ret)
{
return -EFAULT;
}
else
{
*ppos += size;
ret = size;
}
return ret;
}
static long miscdev_ioctl(struct file *file, unsigned int cmd,unsigned long arg)
{
int err = 0;
void __user *argp = (void __user *)arg;
struct Miscdev_User *usr_data = file->private_data;
if(!usr_data)
{
return -EINVAL;
}
printk("%s\n",__func__);
/* Access check of the argument. Some commands, e.g. create session and process op,
needs additional checks. Those are handled in the command handling functions. */
if(_IOC_DIR(cmd) & _IOC_READ)
{
err = !access_ok(VERIFY_WRITE,argp,_IOC_SIZE(cmd));
}
else if(_IOC_DIR(cmd) & _IOC_WRITE)
{
err = !access_ok(VERIFY_READ,argp,_IOC_SIZE(cmd));
}
if(err)
{
return -EFAULT;
}
switch(cmd)
{
case MISCDEV_CLEAR_DATA:
memset(usr_data->data,0,MISCDEV_DATA_SIZE);
break;
case MISCDEV_READ_DATA:
err = copy_to_user(argp, usr_data->data, 100);
break;
case MISCDEV_WRITE_DATA:
err = copy_from_user(usr_data->data, argp, 100);
break;
default:
return -EINVAL;
}
if(err)
{
return -EFAULT;
}
return 0;
}
static const struct file_operations miscdev_fops ={
.owner = THIS_MODULE,
.open = miscdev_open,
.release = miscdev_release,
.llseek = miscdev_llseek,
.read = miscdev_read,
.write = miscdev_write,
.unlocked_ioctl = miscdev_ioctl,
};
static struct miscdevice miscdev = {
.minor = MISC_DYNAMIC_MINOR, //动态分配次设备号
.name = MISCDEV_NAME,
.fops = &miscdev_fops,
};
static int __init miscdev_init(void)
{
int ret = 0;
//注册一个struct miscdevice 实例,申请次设备号,创建设备文件
ret = misc_register(&miscdev);
if(ret < 0)
{
printk(KERN_ERR "miscdev:register misc_device failed\n");
goto out;
}
printk(KERN_INFO "miscdev:register misc_device ok\n");
out:
return ret;
}
static void __exit miscdev_exit(void)
{
misc_deregister(&miscdev);
printk(KERN_INFO "miscdev:deregister misc_device ok\n");
}
module_init(miscdev_init);
module_exit(miscdev_exit);
MODULE_DESCRIPTION("MISCDEV Driver");
MODULE_AUTHOR("Kevin D");
MODULE_LICENSE("GPL");
Makefile清单:
ifneq ($(KERNELRELEASE),)
obj-m:=miscdev.o
#miscdev-objs:=file1.o file2.o ##多文件
else
PWD:=$(shell pwd) #当前目录
KERNEL_DIR:=/home/kevin/workspace/driver_test/Hi3519AV100_SDK_V2.0.1.0/osdrv/opensource/kernel/linux-4.9.y/ ##内核绝对路径
ARCH_TYPE=arm //指定soc架构
CC:=arm-himix200-linux- //指定交叉编译链
all:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) ARCH=$(ARCH_TYPE) CROSS_COMPILE=$(CC) modules
@mkdir -p ./ko
@cp ./*.ko ./ko/
clean:
@rm *.symvers *.ko *.o *.mod.c *.order
endif