guowenxue
2024-12-23 b8e5f60912c77d52214c21e67fa91ec5f522c54c
bsp/drivers/x86/driver/chrdev4.c
File was renamed from bsp/drivers/x86_64/ldd3_ioctl.c
@@ -1,50 +1,33 @@
/*
 * Copyright (C) 2024 LingYun IoT System Studio
 * Author: Guo Wenxue <guowenxue@gmail.com>
 *
 * A character skeleton driver example in linux kernel.
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>   /* printk() */
#include <linux/slab.h>     /* kmalloc() */
#include <linux/fs.h>       /* everything... */
#include <linux/errno.h>    /* error codes */
#include <linux/types.h>    /* size_t */
#include <linux/cdev.h>     /* cdev */
#include <linux/fcntl.h>    /* O_ACCMODE */
#include <linux/uaccess.h>  /* copy_*_user */
#include <linux/slab.h>     /* kmalloc() */
#include <linux/version.h>  /* kernel version code */
#include <linux/uaccess.h>  /* copy_from/to_user() */
#include <linux/moduleparam.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
#define access_ok_wrapper(type,arg,cmd) access_ok(type, arg, cmd)
#else
#define access_ok_wrapper(type,arg,cmd) access_ok(arg, cmd)
#endif
/* ioctl definitions, use 'z' as magic number */
#define CHRDEV_IOC_MAGIC  'z'
#define CHRDEV_IOCRESET    _IO(CHRDEV_IOC_MAGIC, 0)
#define CHRDEV_IOCSET      _IOW(CHRDEV_IOC_MAGIC, 1, int)
#define CHRDEV_IOCGET      _IOR(CHRDEV_IOC_MAGIC, 2, int)
#define CHRDEV_IOC_MAXNR   3
/* device name and major number */
#define DEV_NAME         "chrdev"
#define DEV_SIZE         1024
#define CONFIG_AUTODEV   1 /* Auto create device node in driver or not */
//#define DEV_MAJOR  79
#ifndef DEV_MAJOR
#define DEV_MAJOR  0
#endif
int dev_major = DEV_MAJOR;
int dev_major = 0;
module_param(dev_major, int, S_IRUGO);
#define BUF_SIZE         1024
typedef struct chrdev_s
{
    struct cdev    cdev;
#ifdef CONFIG_AUTODEV
    struct class  *class;
    struct device *device;
#endif
    char          *data;   /* data buffer */
    uint32_t       size;   /* data buffer size */
    uint32_t       bytes;  /* data bytes in the buffer */
@@ -109,19 +92,29 @@
    return rv;
}
/* The ioctl() implementation */
long chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
#define access_ok_wrapper(type,arg,cmd) access_ok(type, arg, cmd)
#else
#define access_ok_wrapper(type,arg,cmd) access_ok(arg, cmd)
#endif
/* ioctl definitions, use 'c' as magic number */
#define CHR_MAGIC           'c'
#define CHR_MAXNR           2
#define CMD_READ            _IOR(CHR_MAGIC, 0, int)
#define CMD_WRITE           _IOW(CHR_MAGIC, 1, int)
static long chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct chrdev_s *dev = file->private_data;
    int rv, data;
    size_t  bytes = sizeof(data);
    static int value = 0xdeadbeef;
    int rv = 0;
    /*
     * extract the type and number bitfields, and don't decode
     * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
     */
    if (_IOC_TYPE(cmd) != CHRDEV_IOC_MAGIC) return -ENOTTY;
    if (_IOC_NR(cmd) > CHRDEV_IOC_MAXNR) return -ENOTTY;
    if (_IOC_TYPE(cmd) != CHR_MAGIC) return -ENOTTY;
    if (_IOC_NR(cmd) > CHR_MAXNR) return -ENOTTY;
    /*
     * the direction is a bitmask, and VERIFY_WRITE catches R/W transfers.
@@ -132,32 +125,26 @@
        rv = !access_ok_wrapper(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
    else if (_IOC_DIR(cmd) & _IOC_WRITE)
        rv =  !access_ok_wrapper(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
    if (rv) return -EFAULT;
    if (rv)
        return -EFAULT;
    switch(cmd) {
        case CHRDEV_IOCRESET:
            dev->bytes = 0;
            memset(dev->data, 0, dev->size);
            rv = 0;
        case CMD_READ:
            if (copy_to_user((int __user *)arg, &value, sizeof(value)))
                return -EFAULT;
            break;
        /* Last 4 bytes in the buffer used to save ioctl() data*/
        case CHRDEV_IOCSET:
            rv = __get_user(data, (int __user *)arg);
            if( !rv )
                 memcpy(&dev->data[dev->size-bytes], &data, bytes);
            break;
        case CHRDEV_IOCGET:
            memcpy(&data, &dev->data[dev->size-bytes], bytes);
            rv = __put_user(data, (int __user *)arg);
        case CMD_WRITE:
            if (copy_from_user(&value, (int __user *)arg, sizeof(value)))
                return -EFAULT;
            break;
        default:
            return -ENOTTY;
            return -EINVAL;
    }
    return rv;
    return 0;
}
static int chrdev_open (struct inode *inode, struct file *file)
@@ -183,8 +170,8 @@
    .open    = chrdev_open,          /* open()  implementation */
    .read    = chrdev_read,          /* read()  implementation */
    .write   = chrdev_write,         /* write() implementation */
    .release = chrdev_close,         /* close() implementation */
    .unlocked_ioctl = chrdev_ioctl,  /* ioctl() implementation */
    .release        = chrdev_close, /* close() implementation */
};
static int __init chrdev_init(void)
@@ -193,17 +180,17 @@
    int        rv;
    /* malloc and initial device read/write buffer */
    dev.data = kmalloc(DEV_SIZE, GFP_KERNEL);
    dev.data = kmalloc(BUF_SIZE, GFP_KERNEL);
    if( !dev.data )
    {
        printk(KERN_ERR " %s driver kmalloc() failed\n", DEV_NAME);
        return -ENOMEM;
    }
    dev.size = DEV_SIZE;
    dev.size = BUF_SIZE;
    dev.bytes = 0;
    memset(dev.data, 0, dev.size);
    /* dynamic alloc device node major number if not set */
    /* allocate device number */
    if(0 != dev_major)
    {
        devno = MKDEV(dev_major, 0);
@@ -221,9 +208,11 @@
        return -ENODEV;
    }
    /* setup and register cdev into kernel */
    /* initialize cdev and setup fops */
    cdev_init(&dev.cdev, &chrdev_fops);
    dev.cdev.owner = THIS_MODULE;
    /* register cdev to linux kernel */
    rv = cdev_add(&dev.cdev, devno, 1);
    if( rv )
    {
@@ -232,7 +221,6 @@
        goto failed1;
    }
#ifdef CONFIG_AUTODEV
    /* create device node in user space */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
    dev.class = class_create(DEV_NAME);
@@ -244,25 +232,22 @@
        goto failed2;
    }
    dev.device = device_create(dev.class, NULL, MKDEV(dev_major, 0), NULL, DEV_NAME);
    dev.device = device_create(dev.class, NULL, MKDEV(dev_major, 0), NULL, "%s%d", DEV_NAME, 0);
    if( !dev.device )
    {
        rv = -ENODEV;
        printk(KERN_ERR "%s driver create device failed\n", DEV_NAME);
        goto failed3;
    }
#endif
    printk(KERN_INFO "%s driver on major[%d] installed.\n", DEV_NAME, dev_major);
    return 0;
#ifdef CONFIG_AUTODEV
failed3:
    class_destroy(dev.class);
failed2:
    cdev_del(&dev.cdev);
#endif
failed1:
    unregister_chrdev_region(devno, 1);
@@ -274,10 +259,8 @@
static void __exit chrdev_exit(void)
{
#ifdef CONFIG_AUTODEV
    device_del(dev.device);
    class_destroy(dev.class);
#endif
    cdev_del(&dev.cdev);
    unregister_chrdev_region(MKDEV(dev_major,0), 1);