linux 驱动模块参考

#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  //全局内存大小
#define MEM_CLEAR 0x1            //清零全局内存
#define GLOBALMEM_MAJOR   250  //主设备号

static int globalmem_major = GLOBALMEM_MAJOR;
//设备结构体
struct globalmem_dev{
    struct cdev cdev; //cdev 结构体
    unsigned char mem[GLOBALMEM_SIZE];
    struct semaphore sem; //并发控制用的信号量,由于要调用copy_from_user这些函数,可能导致阻塞的函数,不能用自旋锁,用信号量
};

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

//文件打开
int globalmem_open(struct inode *inode, struct file *filp)
{
    struct globalmem_dev *dev;

    dev = container_of(inode->i_cdev, struct globalmem_dev, cdev);
    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);
        printk(KERN_INFO "globalmem is set to zero\n");

        up(&dev->sem);//释放信号量
        break;
    default:
        return -EINVAL;
    }
    return 0;
}

//read
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 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 %u bytes(s) from %lu \n", 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 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 %u bytes(s) from %lu\n", 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;
        }
    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,
};

// 初始化并注册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;
    err = cdev_add(&dev->cdev, devno, 1);
    if(err)
        printk(KERN_NOTICE "error %d adding globalmem %d", err , index);
}

//设备加载
int globalmem_init()
{
    int result ;
    dev_t devno = MKDEV(globalmem_major, 0);

    //申请设备号
    if(globalmem_major)
        result = register_chrdev_region(devno, 2, "globalmem");
    else{ // 动态分配
        result = alloc_chrdev_region(&devno, 0, 2, "globalmem");
        globalmem_major = MAJOR(devno);
    }
    if(result <0)
        return result;

    //动态申请设备结构体的内体
    globalmem_devp = kmalloc(2*sizeof(struct globalmem_dev), GFP_KERNEL);
    if(!globalmem_devp){//申请失败
        result = -ENOMEM;
        goto fail_malloc;
    }

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

    globalmem_setup_cdev(&globalmem_devp[0], 0);
    globalmem_setup_cdev(&globalmem_devp[1], 1);

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

    return 0;

fail_malloc:
    unregister_chrdev_region(devno, 1);
    return result;
}

//模块卸载函数
void globalmem_exit()
{
    cdev_del(&(globalmem_devp[0].cdev)); // 注销cdev
    cdev_del(&(globalmem_devp[1].cdev)); // 注销cdev
    kfree(globalmem_devp); // 释放设备结构体内存
    unregister_chrdev_region(MKDEV(globalmem_major, 0), 2); //释放设备号
}

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

module_param(globalmem_major, int, S_IRUGO);//模块参数   insmod xxx.ko globalmem_major= 1000
module_init(globalmem_init);
module_exit(globalmem_exit);



from: 设备驱动开发详解2,宋宝华
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值