linux 3.0.35下globalmem 字符设备驱动实现

1、Makefile

KDIR=/home/xxx/s-linux-3.0.35

PWD:=$(shell pwd)



# kernel modules

obj-m := globalmem.o



modules:

    make -C $(KDIR) M=$(PWD) modules



clean:

    rm -rf *.o *.ko *.mod.c *.markesr *.order *.symvers



.PHONY:modules clean

 

2、globalmem.c

#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 <linux/slab.h>



#include <asm/io.h>

#include <asm/system.h>

#include <asm/uaccess.h>



#define GLOBALMEM_SIZE 0x1000 // 4KB



// create device node in the board side.

// mknod /dev/globalmem c 120 0

#define GLOBALMEM_MAJOR 120 // preset major number



// define ioctl cmd

#define GLOBALMEM_MAGIC 0x01

#define MEM_CLEAR _IO(GLOBALMEM_MAGIC, 0)



static int globalmem_major = GLOBALMEM_MAJOR;



// globalmem struct

struct globalmem_dev {

    struct cdev cdev; // cdev struct

    unsigned char mem[GLOBALMEM_SIZE]; // global memory

};



struct globalmem_dev *globalmem_devp; // device struct instance



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

{

        // set device struct pointer to file privatedata pointer

    filp->private_data = globalmem_devp;



    return 0;

}



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)

{

    unsigned long p = *ppos;

    unsigned int count = size;

    int ret = 0;

    struct globalmem_dev *dev = filp->private_data; // get device struct pointer

        

        // analysis and get valid read length

    if (p >= GLOBALMEM_SIZE) // overflow

        return 0;



    if (count > GLOBALMEM_SIZE - p) // count is too large

        count = GLOBALMEM_SIZE - p;



        // kernel buf -> user buf

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

        ret = -EFAULT;

    else {

        *ppos += count;

        ret = count;

        printk(KERN_INFO "read %d bytes from %ld\n", count, p);

    }



    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; // get device stuct pointer



        // analysis and get valid write length

    if (p >= GLOBALMEM_SIZE) // write overflow

        return 0;



    if (count > GLOBALMEM_SIZE - p) // write count is too large

        count = GLOBALMEM_SIZE - p;



        // user buf -> kernel buf

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

        ret = -EFAULT;

    else {

        *ppos += count;

        ret = count;

        printk(KERN_INFO "written %d bytes from %ld\n", count, p);

    }



    return ret;

}



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

{

    loff_t ret = 0;



    switch (orig) {

        case 0: // from the file head

            if (offset < 0 || ((unsigned int) offset > GLOBALMEM_SIZE)) {

                ret = -EINVAL;

                break;

            }

            filp->f_pos = (unsigned int) offset;

            ret = filp->f_pos;

            break;



        case 1: // from current position

            if ((filp->f_pos + offset) > GLOBALMEM_SIZE || (filp->f_pos + offset) < 0) {

                ret = - EINVAL;

                break;

            }

            filp->f_pos += offset;

            ret = filp->f_pos;

            break;

    }



    return ret;

}





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

{



    struct globalmem_dev *dev = filp->private_data; // get device stuct pointer

    

    switch (cmd) {

        case MEM_CLEAR:

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

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

            break;



        default:

            return -EINVAL; // not supported

    }



    return 0;

}



// file operations struct

static const struct file_operations globalmem_fops = {

    .owner = THIS_MODULE,

    .llseek = globalmem_llseek,

    .read = globalmem_read,

    .write = globalmem_write,

    .unlocked_ioctl = globalmem_ioctl,

    .open = globalmem_open,

    .release = globalmem_release,

};



// init and add cdev struct

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

{

    int err;

    int devno = MKDEV(globalmem_major, 0);



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

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

    if (err)

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

}



// globalmem device init function

int globalmem_init(void)

{

    int result;

    dev_t devno = MKDEV(globalmem_major, 0);



        // apply globalmem device kernel region

    if (globalmem_major)

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

    else {

            // get major no dynamically

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

        globalmem_major = MAJOR(devno);

    }



    if (result < 0)

        return result;



        // apply device struct memory

    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);



    return 0;



  fail_malloc:

    unregister_chrdev_region(devno, 1);

    return result;

}



// globalmem device exit function

void globalmem_exit(void)

{

    // del cdev struct

    cdev_del(&globalmem_devp->cdev); 

    // free device struct memory

    kfree(globalmem_devp); 

    // unregister device region

    unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); 

}



module_param(globalmem_major, int, S_IRUGO);

module_init(globalmem_init);

module_exit(globalmem_exit);



MODULE_AUTHOR("TT <[email protected]>");

MODULE_LICENSE("Dual BSD/GPL");

 

你可能感兴趣的:(global)