Chinaunix首页 | 论坛 | 博客
  • 博客访问: 331087
  • 博文数量: 61
  • 博客积分: 1400
  • 博客等级: 上尉
  • 技术积分: 789
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-05 22:48
文章分类
文章存档

2012年(1)

2011年(2)

2010年(22)

2009年(36)

我的朋友

分类: LINUX

2009-08-09 16:50:33

在看宋宝华出的驱动编程的书第六章里面将的字符设备驱动的一个实例:globalmem
按照书中的程序敲进去进行编译,下载到开发板正常运行。
对程序做了一些注释。


#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>

#define GLOBALMEM_SIZE     0x1000 // 设置全局内存大小为4K

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

#define GLOBALMEM_MAJOR        250 // 预设golbalmem的主设备号


static int globalmem_major = GLOBALMEM_MAJOR;

// globalmem 结构体

struct globalmem_dev
{
    struct cdev cdev;                    // cdev结构体

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

};

//******************************************************************************

//在执行insmod的时候调用init函数会进行kmalloc申请一块内存,会对这个指针进行初始化

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

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:
            memset(dev->mem, 0, GLOBALMEM_SIZE);
            printk(KERN_INFO "globalmem is set to zero\n");
            break;

        default:
            return - EINVAL;
    }
    return 0;
}

//*******************************************************************************************

//filp:文件指针 buf:缓冲区 size:读取的字节数 ppos:相对文件开头的偏移量

//*******************************************************************************************

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

    //from kernel to user

    //copy_to_user和copy_from_user函数的第一个参数都是目标,第二个参数是源

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

//*******************************************************************************************

//filp:文件指针 buf:缓冲区 size:写入的字节数 ppos:相对文件开头的偏移量

//*******************************************************************************************

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

    //from kernel to user

    //copy_to_user和copy_from_user函数的第一个参数都是目标,第二个参数是源

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

//************************************************************************

// llseek函数对文件定位的起始地址可以是:

// 文件开头(SEEK_SET,0)

// 当前位置 (SEEK_CUR,1)

// 文件尾 (SEEK_END,2)

// 函数参数:filp 文件指针 offset 偏移量 orig 偏移量的参考地址

//************************************************************************

static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
    loff_t ret;
    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;
}

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

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

    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 globalmem", err);
    }
}

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

int globalmem_init(void)
{
    int result;
    dev_t devno = MKDEV(globalmem_major,0);    // 通过主设备号和次设备号生成dev_t


    //向系统申请设备号

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

    //*******************************************************************************************

    //在设备驱动程序里面申请动态内存不是用malloc,而是kmalloc,或者用get_free_pages直接申请页。

    //释放内存用的是kfree,或free_pages. 请注意,kmalloc等函数返回的是物理地址!

    //而malloc等返回的是线性地址!要注意kmalloc最大只能开辟128k-16,16个字节是被页描述符结构占用了。

    //内存映射的I/O口,寄存器或者是硬件设备的RAM(如显存)一般占用F0000000以上的地址空间。

    //在驱动程序中不能直接访问,要通过kernel函数vremap获得重新映射以后的地址*/

    //*******************************************************************************************

    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设备驱动模块卸载函数

int globalmem_exit(void)
{
    cdev_del(&globalmem_devp->cdev); // 删除cdev结构

    kfree(globalmem_devp);
    unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); //从系统注销设备号

}
    
MODULE_AUTHOR("Benson");
MODULE_LICENSE("Dual BSD/GPL");

module_param(globalmem_major, int, S_IRUGO);

module_init(globalmem_init);
module_exit(globalmem_exit);





























阅读(2134) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~