guowenxue
2024-04-04 f5c330abb65b94a5fbdcd1e09977b9269402801c
Driver:IGKBoard-All: Add x86_64 linux driver

Signed-off-by: guowenxue <guowenxue@gmail.com>
7 files added
744 ■■■■■ changed files
kernel/drivers/x86_64/Makefile 25 ●●●●● patch | view | raw | blame | history
kernel/drivers/x86_64/ldd1_hello.c 20 ●●●●● patch | view | raw | blame | history
kernel/drivers/x86_64/ldd2_chrdev.c 230 ●●●●● patch | view | raw | blame | history
kernel/drivers/x86_64/ldd3_ioctl.c 295 ●●●●● patch | view | raw | blame | history
kernel/drivers/x86_64/testapp/app2_chrdev.c 74 ●●●●● patch | view | raw | blame | history
kernel/drivers/x86_64/testapp/app3_ioctl.c 68 ●●●●● patch | view | raw | blame | history
kernel/drivers/x86_64/testapp/makefile 32 ●●●●● patch | view | raw | blame | history
kernel/drivers/x86_64/Makefile
New file
@@ -0,0 +1,25 @@
# Linux driver build kernel source code path on X86 server
KERNAL_DIR ?= /lib/modules/$(shell uname -r)/build
ARCH=${shell uname -p}
# Fix bug: module verification failed: signature and/or required key missing - tainting kernel
CONFIG_MODULE_SIG=n
# Linux kernel modules
obj-m += ldd1_hello.o
obj-m += ldd2_chrdev.o
obj-m += ldd3_ioctl.o
modules:
    $(MAKE) -C $(KERNAL_DIR) M=$(shell pwd) modules
    @make clear
clear:
    @rm -f *.o .*.cmd *.cmd *.mod *.mod.c
    @rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f
    @rm -f .*ko.cmd .*.o.cmd .*.o.d
    @rm -f *.unsigned
clean:
    @rm -f *.ko
kernel/drivers/x86_64/ldd1_hello.c
New file
@@ -0,0 +1,20 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
static __init int hello_init(void)
{
    printk(KERN_ALERT "hello module installed.\n");
    return 0;
}
static __exit void hello_exit(void)
{
    printk(KERN_ALERT "hello module removed.\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("GuoWenxue <guowenxue@gmail.com>");
kernel/drivers/x86_64/ldd2_chrdev.c
New file
@@ -0,0 +1,230 @@
#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/version.h>  /* kernel version code */
#include <linux/moduleparam.h>
/* 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;
module_param(dev_major, int, S_IRUGO);
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 */
} chrdev_t;
static struct chrdev_s   dev;
static ssize_t chrdev_read (struct file *file, char __user *buf, size_t count, loff_t *f_pos)
{
    struct chrdev_s   *dev = file->private_data;
    ssize_t nbytes;
    ssize_t rv = 0;
    /* no data in buffer  */
    if( !dev->bytes )
        return 0;
    /* copy data to user space */
    nbytes = count>dev->bytes ? dev->bytes : count;
    if( copy_to_user(buf, dev->data, nbytes) )
    {
        rv = -EFAULT;
        goto out;
    }
    /* update return value and data bytes in buffer */
    rv = nbytes;
    dev->bytes -= nbytes;
out:
    return rv;
}
static ssize_t chrdev_write (struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
{
    struct chrdev_s   *dev = file->private_data;
    ssize_t nbytes;
    ssize_t rv = 0;
    /* no space left */
    if( dev->bytes >= dev->size )
        return -ENOSPC;
    /* check copy data bytes */
    if( dev->size - dev->bytes < count)
        nbytes = dev->size - dev->bytes;
    else
        nbytes = count;
    /* copy data from user space  */
    if( copy_from_user(&dev->data[dev->bytes], buf, nbytes) )
    {
        rv = -EFAULT;
        goto out;
    }
    /* update return value and data bytes in buffer */
    rv = nbytes;
    dev->bytes += nbytes;
out:
    return rv;
}
static int chrdev_open (struct inode *inode, struct file *file)
{
    struct chrdev_s    *dev; /* device struct address */
    /* get the device struct address by container_of() */
    dev = container_of(inode->i_cdev, struct chrdev_s, cdev);
    /* save the device struct address for other methods */
    file->private_data = dev;
    return 0;
}
static int chrdev_close (struct inode *node, struct file *file)
{
    return 0;
}
static struct file_operations chrdev_fops = {
    .owner   = THIS_MODULE,
    .open    = chrdev_open,          /* open()  implementation */
    .read    = chrdev_read,          /* read()  implementation */
    .write   = chrdev_write,         /* write() implementation */
    .release = chrdev_close,         /* close() implementation */
};
static int __init chrdev_init(void)
{
    dev_t      devno;
    int        rv;
    /* malloc and initial device read/write buffer */
    dev.data = kmalloc(DEV_SIZE, GFP_KERNEL);
    if( !dev.data )
    {
        printk(KERN_ERR " %s driver kmalloc() failed\n", DEV_NAME);
        return -ENOMEM;
    }
    dev.size = DEV_SIZE;
    dev.bytes = 0;
    memset(dev.data, 0, dev.size);
    /* dynamic alloc device node major number if not set */
    if(0 != dev_major)
    {
        devno = MKDEV(dev_major, 0);
        rv = register_chrdev_region(devno, 1, DEV_NAME);
    }
    else
    {
        rv = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
        dev_major = MAJOR(devno);
    }
    if(rv < 0)
    {
        printk(KERN_ERR "%s driver can't use major %d\n", DEV_NAME, dev_major);
        return -ENODEV;
    }
    /* setup and register cdev into kernel */
    cdev_init(&dev.cdev, &chrdev_fops);
    dev.cdev.owner = THIS_MODULE;
    rv = cdev_add(&dev.cdev, devno, 1);
    if( rv )
    {
        rv = -ENODEV;
        printk(KERN_ERR "%s driver regist failed, rv=%d\n", DEV_NAME, rv);
        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);
#else
    dev.class = class_create(THIS_MODULE, DEV_NAME);
#endif
    if (IS_ERR(dev.class)) {
        rv = PTR_ERR(dev.class);
        goto failed2;
    }
    dev.device = device_create(dev.class, NULL, MKDEV(dev_major, 0), NULL, DEV_NAME);
    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);
    kfree(dev.data);
    printk(KERN_ERR "%s driver installed failed.\n", DEV_NAME);
    return rv;
}
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);
    kfree(dev.data);
    printk(KERN_INFO "%s driver removed!\n", DEV_NAME);
    return;
}
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("GuoWenxue <guowenxue@gmail.com>");
kernel/drivers/x86_64/ldd3_ioctl.c
New file
@@ -0,0 +1,295 @@
#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/version.h>  /* kernel version code */
#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;
module_param(dev_major, int, S_IRUGO);
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 */
} chrdev_t;
static struct chrdev_s   dev;
static ssize_t chrdev_read (struct file *file, char __user *buf, size_t count, loff_t *f_pos)
{
    struct chrdev_s   *dev = file->private_data;
    ssize_t nbytes;
    ssize_t rv = 0;
    /* no data in buffer  */
    if( !dev->bytes )
        return 0;
    /* copy data to user space */
    nbytes = count>dev->bytes ? dev->bytes : count;
    if( copy_to_user(buf, dev->data, nbytes) )
    {
        rv = -EFAULT;
        goto out;
    }
    /* update return value and data bytes in buffer */
    rv = nbytes;
    dev->bytes -= nbytes;
out:
    return rv;
}
static ssize_t chrdev_write (struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
{
    struct chrdev_s   *dev = file->private_data;
    ssize_t nbytes;
    ssize_t rv = 0;
    /* no space left */
    if( dev->bytes >= dev->size )
        return -ENOSPC;
    /* check copy data bytes */
    if( dev->size - dev->bytes < count)
        nbytes = dev->size - dev->bytes;
    else
        nbytes = count;
    /* copy data from user space  */
    if( copy_from_user(&dev->data[dev->bytes], buf, nbytes) )
    {
        rv = -EFAULT;
        goto out;
    }
    /* update return value and data bytes in buffer */
    rv = nbytes;
    dev->bytes += nbytes;
out:
    return rv;
}
/* The ioctl() implementation */
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);
    /*
     * 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;
    /*
     * the direction is a bitmask, and VERIFY_WRITE catches R/W transfers.
     * `Type' is user-oriented, while access_ok is kernel-oriented,
     * so the concept of "read" and "write" is reversed
     */
    if (_IOC_DIR(cmd) & _IOC_READ)
        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;
    switch(cmd) {
        case CHRDEV_IOCRESET:
            dev->bytes = 0;
            memset(dev->data, 0, dev->size);
            rv = 0;
            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);
            break;
        default:
            return -ENOTTY;
    }
    return rv;
}
static int chrdev_open (struct inode *inode, struct file *file)
{
    struct chrdev_s    *dev; /* device struct address */
    /* get the device struct address by container_of() */
    dev = container_of(inode->i_cdev, struct chrdev_s, cdev);
    /* save the device struct address for other methods */
    file->private_data = dev;
    return 0;
}
static int chrdev_close (struct inode *node, struct file *file)
{
    return 0;
}
static struct file_operations chrdev_fops = {
    .owner   = THIS_MODULE,
    .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 */
};
static int __init chrdev_init(void)
{
    dev_t      devno;
    int        rv;
    /* malloc and initial device read/write buffer */
    dev.data = kmalloc(DEV_SIZE, GFP_KERNEL);
    if( !dev.data )
    {
        printk(KERN_ERR " %s driver kmalloc() failed\n", DEV_NAME);
        return -ENOMEM;
    }
    dev.size = DEV_SIZE;
    dev.bytes = 0;
    memset(dev.data, 0, dev.size);
    /* dynamic alloc device node major number if not set */
    if(0 != dev_major)
    {
        devno = MKDEV(dev_major, 0);
        rv = register_chrdev_region(devno, 1, DEV_NAME);
    }
    else
    {
        rv = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
        dev_major = MAJOR(devno);
    }
    if(rv < 0)
    {
        printk(KERN_ERR "%s driver can't use major %d\n", DEV_NAME, dev_major);
        return -ENODEV;
    }
    /* setup and register cdev into kernel */
    cdev_init(&dev.cdev, &chrdev_fops);
    dev.cdev.owner = THIS_MODULE;
    rv = cdev_add(&dev.cdev, devno, 1);
    if( rv )
    {
        rv = -ENODEV;
        printk(KERN_ERR "%s driver regist failed, rv=%d\n", DEV_NAME, rv);
        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);
#else
    dev.class = class_create(THIS_MODULE, DEV_NAME);
#endif
    if (IS_ERR(dev.class)) {
        rv = PTR_ERR(dev.class);
        goto failed2;
    }
    dev.device = device_create(dev.class, NULL, MKDEV(dev_major, 0), NULL, DEV_NAME);
    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);
    kfree(dev.data);
    printk(KERN_ERR "%s driver installed failed.\n", DEV_NAME);
    return rv;
}
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);
    kfree(dev.data);
    printk(KERN_INFO "%s driver removed!\n", DEV_NAME);
    return;
}
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("GuoWenxue <guowenxue@gmail.com>");
kernel/drivers/x86_64/testapp/app2_chrdev.c
New file
@@ -0,0 +1,74 @@
/*********************************************************************************
 *      Copyright:  (C) 2023 Avnet
 *                  All rights reserved.
 *
 *       Filename:  file.c
 *    Description:  This file
 *
 *        Version:  1.0.0(2023年11月02日)
 *         Author:  Guo Wenxue <guowenxue@avnet.com>
 *      ChangeLog:  1, Release initial version on "2023年11月02日 14时47分52秒"
 *
 ********************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
/* 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)
int main (int argc, char **argv)
{
    char      *devname = "/dev/chrdev";
    char       buf[1024];
    int        rv, fd;
    int        value = 0x12345678;
    fd = open(devname, O_RDWR|O_TRUNC);
    if( fd < 0 )
    {
        printf("Open device %s failed: %s\n", devname, strerror(errno));
        return 1;
    }
    rv = write(fd, "Hello", 5);
    if( rv< 0)
    {
        printf("Write data into device failed, rv=%d: %s\n", rv, strerror(errno));
        return 2;
    }
    printf("Write %d bytes data okay\n", rv);
#if 0
    lseek(fd, 3, SEEK_SET);
#endif
    memset(buf, 0, sizeof(buf));
    rv = read(fd, buf, sizeof(buf));
    if( rv< 0)
    {
        printf("Read data from device failed, rv=%d: %s\n", rv, strerror(errno));
        return 3;
    }
    printf("Read %d bytes data: %s\n", rv, buf);
#if 0
    ioctl(fd, CHRDEV_IOCSET, &value);
    value = 0;
    ioctl(fd, CHRDEV_IOCGET, &value);
    printf("ioctl() read value=0x%0x\n", value);
#endif
    close(fd);
    return 0;
}
kernel/drivers/x86_64/testapp/app3_ioctl.c
New file
@@ -0,0 +1,68 @@
/*********************************************************************************
 *      Copyright:  (C) 2023 Avnet
 *                  All rights reserved.
 *
 *       Filename:  file.c
 *    Description:  This file
 *
 *        Version:  1.0.0(2023年11月02日)
 *         Author:  Guo Wenxue <guowenxue@avnet.com>
 *      ChangeLog:  1, Release initial version on "2023年11月02日 14时47分52秒"
 *
 ********************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
/* 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)
int main (int argc, char **argv)
{
    char      *devname = "/dev/chrdev";
    char       buf[1024];
    int        rv, fd;
    int        value = 0x12345678;
    fd = open(devname, O_RDWR|O_TRUNC);
    if( fd < 0 )
    {
        printf("Open device %s failed: %s\n", devname, strerror(errno));
        return 1;
    }
    rv = write(fd, "Hello", 5);
    if( rv< 0)
    {
        printf("Write data into device failed, rv=%d: %s\n", rv, strerror(errno));
        return 2;
    }
    printf("Write %d bytes data okay\n", rv);
    memset(buf, 0, sizeof(buf));
    rv = read(fd, buf, sizeof(buf));
    if( rv< 0)
    {
        printf("Read data from device failed, rv=%d: %s\n", rv, strerror(errno));
        return 3;
    }
    printf("Read %d bytes data: %s\n", rv, buf);
    ioctl(fd, CHRDEV_IOCSET, &value);
    value = 0;
    ioctl(fd, CHRDEV_IOCGET, &value);
    printf("ioctl() read value=0x%0x\n", value);
    close(fd);
    return 0;
}
kernel/drivers/x86_64/testapp/makefile
New file
@@ -0,0 +1,32 @@
#********************************************************************************
#      Copyright:  (C) 2023 LingYun IoT System Studio
#                  All rights reserved.
#
#       Filename:  Makefile
#    Description:  This file used to compile all the C file to respective binary,
#                  and it will auto detect cross compile or local compile.
#
#        Version:  1.0.0(11/08/23)
#         Author:  Guo Wenxue <guowenxue@gmail.com>
#      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
#
#*******************************************************************************
BUILD_ARCH=$(shell uname -m)
ifneq ($(findstring "x86_64" "i386", $(BUILD_ARCH)),)
    CROSS_COMPILE?=arm-linux-gnueabihf-
endif
CC=${CROSS_COMPILE}gcc
SRCFILES = $(wildcard *.c)
BINARIES=$(SRCFILES:%.c=%)
all: ${BINARIES}
%: %.c
    $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
clean:
    rm -f ${BINARIES}