代码清单:
#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