globalmem设备驱动

基于宋宝华ldd中globalmem修改测试。

1)采用container_of()获取设备结构体数据;

2)mutex实现互斥访问。

3)支持子设备(示例中设定为3)。

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/delay.h>
//#include <linux/ide.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/slab.h>

#define DEVICE_MAJOR 230
#define DEVICE_NUM  3

static int device_major = DEVICE_MAJOR;
module_param(device_major, int, S_IRUGO);
static char *device_name = "globalmem_chrdev";

#define GLOBALMEM_SIZE    0x1000
//#define MEM_CLEAR   0x1
#define GLOBALMEM_MAGIC 'g'
#define MEM_CLEAR _IO(GLOBALMEM_MAGIC, 0)

struct globalmem_dev{
    struct cdev cdev;
    unsigned char mem[GLOBALMEM_SIZE];
    struct mutex mutex;
};

struct globalmem_dev *globalmem_devp;

static int globalmem_open(struct inode *inode, struct file *filp)
{
    struct globalmem_dev *dev = container_of(inode->i_cdev, 
                                    struct globalmem_dev, cdev);
    filp->private_data = dev;
    return 0;
}

static int globalmem_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
    int ret = 0;
    unsigned long pos = *ppos;
    unsigned int count = size;
    struct globalmem_dev *dev = filp->private_data;

    if(pos >= GLOBALMEM_SIZE){
        return 0;
    }

    if(count > (GLOBALMEM_SIZE - pos)){
        count = GLOBALMEM_SIZE - pos;
    }

    if(mutex_lock_interruptible(&dev->mutex)){
        return -ERESTARTSYS;
    }
    msleep(10000);
    if(copy_to_user(buf, dev->mem+pos, count)){
        ret =  -EFAULT;
    } else {
        *ppos += count;
        ret = count;
        printk(KERN_INFO "device %s read %u byte(s) from %lu\n", device_name, count, pos);
    }
    mutex_unlock(&dev->mutex);

    return ret;
}

static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t size , loff_t *ppos)
{
    int ret = 0;
    unsigned long pos = *ppos;
    unsigned int count = size;
    struct globalmem_dev *dev = filp->private_data;

    if(pos >= GLOBALMEM_SIZE){
        return 0;
    }
    if(size > (GLOBALMEM_SIZE-pos)){
        size = GLOBALMEM_SIZE-pos;
    }

    if(mutex_lock_interruptible(&dev->mutex)){
        return -ERESTARTSYS;
    }
    msleep(10000);
    if(copy_from_user(dev->mem+pos, buf, count)){
        ret = -EFAULT;
    } else {
        *ppos += count;
        ret = count;
        printk(KERN_INFO "device %s write %u bytes from %lu\n", device_name, count, pos);
    }
    mutex_unlock(&dev->mutex);

    return ret;
}

static long globalmem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct globalmem_dev *dev = filp->private_data;

    switch(cmd){
    case MEM_CLEAR:
        if(mutex_lock_interruptible(&dev->mutex)){
            return -ERESTARTSYS;
        }
        memset(dev->mem, 0, GLOBALMEM_SIZE);
        mutex_unlock(&dev->mutex);
        printk(KERN_INFO "device %s is set to zero\n", device_name);
        break;
    default:
        return -EINVAL;
    }

    return 0;
}

static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
    loff_t ret = 0;
    switch(orig){
    case SEEK_SET:
        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 SEEK_CUR:
        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;
}

static const struct file_operations globalmem_fops = {
    .owner = THIS_MODULE,
    .llseek = globalmem_llseek,
    .unlocked_ioctl = globalmem_ioctl,
    .read = globalmem_read,
    .write = globalmem_write,
    .open = globalmem_open,
    .release = globalmem_release,
};

static int globalmem_setup_cdev(struct globalmem_dev *dev, int index)
{
    int err, devno = MKDEV(device_major, index);

    cdev_init(&dev->cdev, &globalmem_fops);
    dev->cdev.owner = THIS_MODULE;
    err = cdev_add(&dev->cdev, devno, 1);
    if(err){
        printk(KERN_NOTICE "device %s add %d cdev error %d\n", device_name, index, err);
        return -1;
    }

    return 0;
}

static int __init globalmem_init(void)
{
    int ret = 0;
    int i = 0, ii = 0;
    dev_t devno;

    printk(KERN_INFO "device %s probing...\n", device_name);
    if(device_major){
        devno = MKDEV(device_major, 0);
        ret = register_chrdev_region(devno, DEVICE_NUM, device_name);
    } else {
        ret = alloc_chrdev_region(&devno, 0, DEVICE_NUM, device_name);
        device_major = MAJOR(devno);
    }
    if(ret < 0){
        return ret;
    }

    globalmem_devp = kzalloc(sizeof(struct globalmem_dev)*DEVICE_NUM, GFP_KERNEL);
    if(!globalmem_devp){
        ret = -ENOMEM;
        goto fail_malloc;
    }

    for(i = 0; i < DEVICE_NUM; i++){
        ret = globalmem_setup_cdev(globalmem_devp+i, i);
        if(ret){
            ret = -EFAULT;
            goto fail_cdev;
        }
    }
    mutex_init(&globalmem_devp->mutex);

    printk(KERN_INFO "device %s probe successfully!\n", device_name);
    return 0;

fail_cdev:
    ii = i;
    for(i = 0; i < ii; i++){
        cdev_del(&(globalmem_devp+i)->cdev);
    }
    kfree(globalmem_devp);

fail_malloc:
    unregister_chrdev_region(devno, DEVICE_NUM);

    printk(KERN_INFO "device %s probe failed!\n", device_name);
    return ret;
}

static void __exit globalmem_exit(void)
{
    int i  = 0;
    for(i = 0; i < DEVICE_NUM; i++){
        cdev_del(&(globalmem_devp+i)->cdev);
    }
    kfree(globalmem_devp);
    unregister_chrdev_region(MKDEV(device_major, 0), DEVICE_NUM);

    printk(KERN_INFO "device %s exit!\n", device_name);
}

module_init(globalmem_init);
module_exit(globalmem_exit);

MODULE_AUTHOR("wqh");
MODULE_LICENSE("Dual BSD/GPL");

pc测试Makefile:

obj-m := globalmem.o
CURRENT_PATH := $(shell pwd)
LINUX_KERNEL := $(shell uname -r)
LINUX_KERNEL_PATH := /lib/modules/$(shell uname -r)/build
#LINUX_KERNEL_PATH := /home/wang/kernel/kernel/linux-imx-4.1.15

GCC ?= gcc

all:
    #$(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
    $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
    $(GCC) -Wall -o app app.c

clean:
    #$(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
    $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
    -rm app

app测试程序

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>


int main(int argc, char **argv)
{
    int fd, retvalue;
    char *filename = NULL;
    char rbuf[100], wbuf[] = {"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"};
    
    if(argc != 2){
        printf("Usage: %s filename\n", argv[0]);
        exit(0);
    }

    filename = argv[1];
    fd = open(filename, O_RDWR);
    if(fd < 0){
        //printf("device %s open error(%d): %s\n", filename, fd, strerror(-fd));
        printf("device %s open error(%d): %s\n", filename, fd, strerror(errno));
        return fd;
    }

    retvalue = write(fd, wbuf, strlen(wbuf));
    if(retvalue < 0){
        printf("device %s write error(%d): %s\n", filename, retvalue, strerror(errno));
        return retvalue;
    }
    printf("device %s write %d bytes\n", filename, retvalue);

    retvalue = lseek(fd, 0, SEEK_SET);
    if(retvalue < 0){
        printf("device %s seek error(%d): %s\n", filename, retvalue, strerror(errno));
        return -1;
    }

    retvalue = read(fd, rbuf, sizeof(rbuf));
    if(retvalue < 0){
        printf("device %s read error(%d): %s\n", filename, retvalue, strerror(errno));
        return retvalue;
    }
    printf("device %s read %d bytes, and  data: %s\n", filename, retvalue, rbuf);

    close(fd);

    return 0;
}

测试:

## base
sudo mknod /dev/globmem_chrdev0 c 230 0
sudo mknod /dev/globmem_chrdev1 c 230 1
sudo mknod /dev/globmem_chrdev2 c 230 2
sudo mknod /dev/globmem_chrdev3 c 230 3 // DEVICE_NUM=3, total 3, just test error

cat /proc/devices // 230 globalmem_chrdev

dmesg | tail -n 50
cat /var/log/kern.log | tail -n 50


// su root
echo "hello world" > /dev/globalmem_chrdev
cat /dev/globalmem_chrdev

sudo ./app /dev/globalmem_chrdev2
sudo ./app /dev/globalmem_chrdev3 // just test Error

### mutex

同一子设备互斥访问,不同子设备没有影响(不互斥),因为设备文件的inode不同,取出的设备结构体实际不同,即filp->private_data不同子设备不同。

// same device
sudo time ./app /dev/globalmem_chrdev0 // 30s  read(10s) + write(10s) + mutex(10s)
sudo time ./app /dev/globalmem_chrdev0 // 30s  read(10s) + write(10s) + mutex(10s)

// different devices
sudo time ./app /dev/globalmem_chrdev0 // 30s  read(10s) + write(10s) + mutex(10s)
sudo time ./app /dev/globalmem_chrdev0 // 30s  read(10s) + write(10s) + mutex(10s)
同一子设备互斥访问,不同子设备没有影响(不互斥),因为设备文件的inode不同,取出的设备结构体实际不同,即filp->private_data不同子设备不同。

 

参考:

1. learn-ldd github

posted @ 2017-09-07 18:55  yuxi_o  阅读(376)  评论(0编辑  收藏  举报