字符设备实例及Makefile

代码清单:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>

//ioctl cmd <->应用空间需要和内核定义一致
#define CHARDEV_MAGIC_CODE  'C'
#define CHARDEV_CLEAR_DATA  _IO(CHARDEV_MAGIC_CODE,1)
#define CHARDEV_READ_DATA  _IOR(CHARDEV_MAGIC_CODE,2,int)
#define CHARDEV_WRITE_DATA  _IOW(CHARDEV_MAGIC_CODE,3,int)

//设备信息
#define CHARDEV_MAJOR 220
#define CHARDEV_MINOR 0
#define CHARDEV_MINOR_NUM 2
#define CHARDEV_NAME "PANDA"
#define CHARDEV_NODE_NAME "panda_%d"
#define CHARDEV_PRI_DATA_SIZE   0x2000
struct CharDev
{
    struct cdev cdev;  
    char data[CHARDEV_PRI_DATA_SIZE];
};

static struct CharDev *chardev;
static struct class *cls;
static int major = CHARDEV_MAJOR;
static int minor = CHARDEV_MINOR;
static int minor_nums = CHARDEV_MINOR_NUM; //次设备号数

module_param(major,int,S_IRUSR);
module_param(minor,int,S_IRUSR);
module_param(minor_nums,int,S_IRUSR);



static int chardev_open(struct inode *inode, struct file *file)
{
	struct CharDev *dev = container_of(inode->i_cdev,struct CharDev,cdev);
	file->private_data = dev;
	printk("%s\n",__func__);
	return 0;
	
}
static int chardev_release(struct inode *inode, struct file *file)
{
	printk("%s\n",__func__);	
	return 0;
}
static loff_t chardev_llseek(struct file *file, loff_t offset, int whence)
{
	loff_t ret = offset;
	loff_t pos = 0;
	printk("%s\n",__func__);
	switch(whence){
	case SEEK_SET:
		if(offset < 0 || offset > CHARDEV_PRI_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 > CHARDEV_PRI_DATA_SIZE ) {
			ret = -EINVAL; 
			break;
		}
		file->f_pos = pos;
		ret = file->f_pos;
		break;
	default:
		ret = -EINVAL;
		break;
	}
	return ret;
}

static ssize_t chardev_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 CharDev *dev = file->private_data;
	printk("%s\n",__func__);
	if(offset >= CHARDEV_PRI_DATA_SIZE || offset < 0 || size < 0) {
		return 0;
	}

	if(size > (CHARDEV_PRI_DATA_SIZE - offset)) {
		size = CHARDEV_PRI_DATA_SIZE - offset;
	}

	ret = copy_to_user(buf, dev->data + offset, size);//成功,返回0,失败返回未copy成功的数据字节数
	if(ret) {
		return -EFAULT;
	} else {
		*ppos += size;
		ret = size;
	}	
	return ret;
}

static ssize_t chardev_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 CharDev *dev = file->private_data;
	printk("%s\n",__func__);
	if(offset >= CHARDEV_PRI_DATA_SIZE || offset < 0 || size < 0) {
		return 0;
	}

	if(size > (CHARDEV_PRI_DATA_SIZE - offset)) {
		size = CHARDEV_PRI_DATA_SIZE - offset;
	}

	ret = copy_from_user(dev->data + offset,buf, size);//成功,返回0,失败返回未copy成功的数据字节数
	if(ret) {
		return -EFAULT;
	} else {
		*ppos += size;
		ret = size;
	}
	return ret;
}

static long chardev_ioctl(struct file *file, unsigned int cmd,unsigned long arg)
{
	int err = 0;
	void __user *argp = (void __user *)arg;
	struct CharDev *dev = file->private_data;
	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 CHARDEV_CLEAR_DATA:
		memset(dev->data,0,CHARDEV_PRI_DATA_SIZE);
		break;
	case CHARDEV_READ_DATA:
		err = copy_to_user(argp, dev->data, 100);		
		break;
	case CHARDEV_WRITE_DATA:
		err = copy_from_user(dev->data, argp, 100);	
		break;
	default:
		return -EINVAL;
	}
	if(err) {
		return -EFAULT;
	}
	return 0;
}

static const struct file_operations chardev_fops = {
	.owner = THIS_MODULE,
	.open  = chardev_open,
	.release = chardev_release,	
	.llseek = chardev_llseek,
	.read = chardev_read,
	.write = chardev_write,
	.unlocked_ioctl = chardev_ioctl,
};

static int cdev_init_add_bundle(struct CharDev *pdev)
{
	int ret = 0,i = 0;
	struct CharDev *dev;
	for(i = 0; i < minor_nums; i++) {
		dev = (pdev + i);
		cdev_init(&dev->cdev, &chardev_fops);
		ret = cdev_add(&dev->cdev, MKDEV(major, minor+i), 1);
		if(ret < 0) {
			printk(KERN_ERR "chardev: unable register character device%d\n",minor+i);
			return ret;		
		}
	}
	return 0;
}
static void cdev_del_bundle(struct CharDev *pdev)
{
	int i = 0;
	struct CharDev *dev;
	for(i = 0; i < minor_nums; i++) {
		dev = (pdev + i);
		cdev_del(&dev->cdev);
	}
}
static int device_create_bundle(struct class *p_cls)
{
	int i = 0;	
	struct device *clsdev;
	for(i = 0; i < minor_nums; i++) {
		clsdev = device_create(p_cls, NULL, MKDEV(major, minor+i),NULL, CHARDEV_NODE_NAME, i);
		if (IS_ERR(clsdev)) {
			printk(KERN_ERR "chardev: failed to create device%d,error%ld\n",minor+i,PTR_ERR(clsdev));
			return -1;
		}			
	}
	return 0;	
}
static void device_destroy_bundle(struct class *p_cls)
{
	int i = 0;	
	for(i = 0; i < minor_nums; i++) {
		device_destroy(p_cls,MKDEV(major, minor+i));		
	}
}

static int __init chardev_init(void)
{
	int retval = 0;
    dev_t devno;
	printk(KERN_ERR "chardev: major: %d,minor:%d,minor_nums:%d\n", major,minor,minor_nums);
	if(minor_nums < 1) {
		minor_nums = 1;
	}
	//1.注册设备号
	if(major) {
		devno = MKDEV(major,minor);
		retval = register_chrdev_region(devno, minor_nums, CHARDEV_NAME);
	} else {
		retval = alloc_chrdev_region(&devno, minor, minor_nums, CHARDEV_NAME);
		major = MAJOR(devno);
	}
	if(retval < 0) {
		printk(KERN_ERR "chardev: register dev number failed!\n");
		return retval;
	}
	
	//2.分配内存给自定义设备结构体内存
	chardev = kzalloc(sizeof(struct CharDev) * minor_nums,GFP_KERNEL);
	if(!chardev) {
		retval = -ENOMEM;
		goto error_malloc;
	}

	//3.初始化cdev结构体,添加到系统
	retval = cdev_init_add_bundle(chardev);
	if(retval < 0) {
		goto error_cdev_add;
	}
	//4.创建设备类及设备文件
	cls = class_create(THIS_MODULE, CHARDEV_NAME);
	if (IS_ERR(cls)) {
		retval = PTR_ERR(cls);
		goto error_class_create;
	}
	retval = device_create_bundle(cls);
	if (retval < 0) {
		goto error_device_create;
	}
	printk(KERN_INFO "chardev: init finish\n");
	return retval;

error_device_create:
	class_destroy(cls);
error_class_create:
	cdev_del_bundle(chardev);
error_cdev_add:
	kfree(chardev);
error_malloc:
	unregister_chrdev_region(MKDEV(major, minor), minor_nums);
	return retval;

}

static void __exit chardev_exit(void)
{
	device_destroy_bundle(cls);
	class_destroy(cls);
	cdev_del_bundle(chardev);
	kfree(chardev);
	unregister_chrdev_region(MKDEV(major, minor), minor_nums);
}

module_init(chardev_init);
module_exit(chardev_exit);
MODULE_DESCRIPTION("CHARDEV Driver");
MODULE_AUTHOR("Kevin D");
MODULE_LICENSE("GPL");

Makefile清单:

ifneq ($(KERNELRELEASE),)

obj-m:=chardev.o

#chardev-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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值