• NeuSoft(4)编写字符设备驱动


    1、要求:实现简单的字符设备驱动程序

    2、源码清单

    #include <linux/module.h>

    #include <linux/types.h>

    #include <linux/fs.h>

    #include <linux/errno.h>

    #include <linux/mm.h>

    #include <linux/sched.h>

    #include <linux/init.h>

    #include <linux/cdev.h>

    #include <asm/io.h>

    #include <asm/system.h>

    #include <asm/uaccess.h>

    #include <linux/slab.h>

    #define GLOBALMEM_SIZE 0x1000 /*全局内存最大4K字节*/

    #define MEM_CLEAR 0x1 /*清0全局内存*/

    #define GLOBALMEM_MAJOR 254 /*预设的globalmem的主设备号*/

    static globalmem_major = GLOBALMEM_MAJOR;

    /*globalmem设备结构体*/

    struct globalmem_dev

    {

    struct cdev cdev; /*cdev结构体*/

    unsigned char mem[GLOBALMEM_SIZE]; /*全局内存*/

    struct semaphore sem; /*并发控制用的信号量*/

    };

    struct globalmem_dev *globalmem_devp; /*设备结构体指针*/

    /*文件打开函数*/

    int globalmem_open(struct inode *inode, struct file *filp)

    {

    /*将设备结构体指针赋值给文件私有数据指针*/

    filp->private_data = globalmem_devp;

    return 0;

    }

    /*文件释放函数*/

    int globalmem_release(struct inode *inode, struct file *filp)

    {

    return 0;

    }

    /* ioctl设备控制函数 */

    static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg)

    {

    struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/

    switch (cmd)

    {

    case MEM_CLEAR:

    if (down_interruptible(&dev->sem))

    {

    return - ERESTARTSYS;

    }

    memset(dev->mem, 0, GLOBALMEM_SIZE);

    up(&dev->sem); //释放信号量

    printk(KERN_INFO "globalmem is set to zero ");

    break;

    default:

    return - EINVAL;

    }

    return 0;

    }

    /*读函数*/

    static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)

    {

    unsigned long p = *ppos;

    unsigned int count = size;

    int ret = 0;

    struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/

    /*分析和获取有效的写长度*/

    if (p >= GLOBALMEM_SIZE)

    return count ? - ENXIO: 0;

    if (count > GLOBALMEM_SIZE - p)

    count = GLOBALMEM_SIZE - p;

    if (down_interruptible(&dev->sem))

    {

    return - ERESTARTSYS;

    }

    /*内核空间->用户空间*/

    if (copy_to_user(buf, (void*)(dev->mem + p), count))

    {

    ret = - EFAULT;

    }

    else

    {

    *ppos += count;

    ret = count;

    printk(KERN_INFO "read %d bytes(s) from %d ", count, p);

    }

    up(&dev->sem); //释放信号量

    return ret;

    }

    /*写函数*/

    static ssize_t globalmem_write(struct file *filp, const char __user *buf,

    size_t size, loff_t *ppos)

    {

    unsigned long p = *ppos;

    unsigned int count = size;

    int ret = 0;

    struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/

    /*分析和获取有效的写长度*/

    if (p >= GLOBALMEM_SIZE)

    return count ? - ENXIO: 0;

    if (count > GLOBALMEM_SIZE - p)

    count = GLOBALMEM_SIZE - p;

    if (down_interruptible(&dev->sem))//获得信号量

    {

    return - ERESTARTSYS;

    }

    /*用户空间->内核空间*/

    if (copy_from_user(dev->mem + p, buf, count))

    ret = - EFAULT;

    else

    {

    *ppos += count;

    ret = count;

    printk(KERN_INFO "written %d bytes(s) from %d ", count, p);

    }

    up(&dev->sem); //释放信号量

    return ret;

    }

    /* seek文件定位函数 */

    static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)

    {

    loff_t ret = 0;

    switch (orig)

    {

    case 0: /*相对文件开始位置偏移*/

    if (offset < 0)

    {

    ret = - EINVAL;

    break;

    }

    if ((unsigned int)offset > GLOBALMEM_SIZE)

    {

    ret = - EINVAL;

    break;

    }

    filp->f_pos = (unsigned int)offset;

    ret = filp->f_pos;

    break;

    case 1: /*相对文件当前位置偏移*/

    if ((filp->f_pos + offset) > GLOBALMEM_SIZE)

    {

    ret = - EINVAL;

    break;

    }

    if ((filp->f_pos + offset) < 0)

    {

    ret = - EINVAL;

    break;

    }

    filp->f_pos += offset;

    ret = filp->f_pos;

    break;

    default:

    ret = - EINVAL;

    break;

    }

    return ret;

    }

    /* http://blog.csdn.net/zhou1232006/article/details/6867584*/

    /*文件操作结构体*/

    static const struct file_operations globalmem_fops =

    {

    .owner = THIS_MODULE,

    .llseek = globalmem_llseek,

    .read = globalmem_read,

    .write = globalmem_write,

    //.compat_ioctl = globalmem_ioctl,

    .unlocked_ioctl = globalmem_ioctl,

    .open = globalmem_open,

    .release = globalmem_release,

    };

    /*初始化并注册cdev*/

    static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)

    {

    int err, devno = MKDEV(globalmem_major, index);

    cdev_init(&dev->cdev, &globalmem_fops);

    dev->cdev.owner = THIS_MODULE;

    dev->cdev.ops = &globalmem_fops;

    err = cdev_add(&dev->cdev, devno, 1);

    if (err)

    printk(KERN_NOTICE "Error %d adding LED%d", err, index);

    }

    /*设备驱动模块加载函数*/

    int globalmem_init(void)

    {

    int result;

    dev_t devno = MKDEV(globalmem_major, 0);

    /* 申请设备号*/

    /*if (globalmem_major)

    result = register_chrdev_region(devno, 1, "globalmem");

    else /* 动态申请设备号 */

    {*/

    result = alloc_chrdev_region(&devno, 0, 1, "globalmem");

    globalmem_major = MAJOR(devno);

    //}

    if (result < 0)

    return result;

    /* 动态申请设备结构体的内存*/

    globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);

    if (!globalmem_devp) /*申请失败*/

    {

    result = - ENOMEM;

    goto fail_malloc;

    }

    memset(globalmem_devp, 0, sizeof(struct globalmem_dev));

    globalmem_setup_cdev(globalmem_devp, 0);

    sema_init(&globalmem_devp->sem,1); /*初始化信号量*/

    return 0;

    fail_malloc: unregister_chrdev_region(devno, 1);

    return result;

    }

    /*模块卸载函数*/

    void globalmem_exit(void)

    {

    cdev_del(&globalmem_devp->cdev); /*注销cdev*/

    kfree(globalmem_devp); /*释放设备结构体内存*/

    unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*释放设备号*/

    }

    MODULE_AUTHOR("Zhao guohui");

    MODULE_LICENSE("Dual BSD/GPL");

    module_param(globalmem_major, int, S_IRUGO);

    module_init(globalmem_init);

    module_exit(globalmem_exit);

    clip_image002

    3、编译用Makefile

    obj-m := globalmem.o

    KERNELBUILD :=/usr/src/linux-headers-3.2.0-23-generic-pae

    default:

    make -C $(KERNELBUILD) M=$(shell pwd) modules

    clean:

    rm -rf *.o *.ko *.mod.c

    clip_image004

    clip_image006

    1)error: unknown field ‘ioctl’ specified in initializer

    这是由于跨版本移植的时候由于内核提供的函数变化而引起的错误:

    解决:http://blog.csdn.net/zhou1232006/article/details/6867584

    clip_image008

    2)error:implicit declaration of function ‘kmalloc’ [-Werror=implicit-function-declaration]

    解决:http://blog.csdn.net/liukun321/article/details/6785608

    clip_image010

    3)error: ‘globalfifo_devp’ undeclared (first use in this function)

    参考:http://www.tuicool.com/articles/6r2M3a

    clip_image012

    error: implicit declaration of function ‘init_MUTEX’ [-Werror=implicit-function-declaration]

    clip_image013

    clip_image015

    clip_image017

    4、验证

    首先加载内核模块insmod gllobalmem.ko

    clip_image019

    设备忙的解决方法

    clip_image021

    让程序一直寻找可以使用的设备号。

    clip_image023

    终端输入 cat /proc/devices

    找到globalmem的设备号

    clip_image025

    建立设备文件:mknod /dev/globalmem c 你找到的设备号 0

    [root@localhost driver_study]# echo 'hello world' > /dev/globalmem

    [root@localhost driver_study]# cat /dev/globalmem

    hello world

    clip_image027

  • 相关阅读:
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    [转载]MySQL5.5 配置文件 my.ini 1067错误
  • 原文地址:https://www.cnblogs.com/shenerguang/p/3398969.html
Copyright © 2020-2023  润新知