第一个驱动--内存分配

    技术2022-05-19  19

    几天前一直在看Linux驱动开发,看了几天,今天才主动动手开始去抄袭代码.第一眼见这东西,我迷糊迷糊的.摸不着头脑,太可怕了..今天抄袭了一边.把Linux驱动的大概给观摩了一遍,总之也就那样吧...

    第一个驱动

    首先实现一个简单的字符型驱动,在内存区分配一块可读可写的内存,通过文件读写接口对这块内核中的内存进行区域操作.

    模块定义:

    struct DEMO_dev { struct cdev cdev; }; //设备结构 struct file_operations DEMO_fops = { .owner = THIS_MODULE, .llseek = DEMO_llseek, .read = DEMO_read, .write = DEMO_write, .ioctl = DEMO_ioctl, .release = DEMO_release, }; //初始化一个系统调用的结构体 struct DEMO_dev *DEMO_devices; static unsigned char demo_inc = 0; static u8 demoBuffer[256]; //分配的内存空间 #define DEMO_MAJOR 224 //主设备号

    模块初始化:

    int DEMO_init_module(void) { int result; dev_t dev = 0; //设备号 dev = MKDEV(DEMO_MAJOR,0); //转换设备号 result = register_chrdev_region(dev,1,"DEMO"); //按照指定的设备号注册设备 if( result < 0 ) { printk(KERN_WARNING "DEMO:can't get major %d/n",DEMO_MAJOR); //注册失败输出错误信息 return result; } //分配DEMO_dev,该结构是自定义的 DEMO_devices = kmalloc(sizeof(struct DEMO_dev),GFP_KERNEL); //分配设备结构内存 if(!DEMO_devices) { result = -ENOMEM; goto fail; } memset( DEMO_devices,0,sizeof( struct DEMO_dev ) ); //初始化一个字符驱动 cdev_init(&DEMO_devices->cdev,&DEMO_fops); //cdev初始化 DEMO_devices->cdev.owner = THIS_MODULE; //写入模块所属者 DEMO_devices->cdev.ops = &DEMO_fops; //模块操作接口 //向内核添加字符设备 result = cdev_add(&DEMO_devices->cdev,dev,1); if( result ) { printk(KERN_NOTICE "Error %d adding DEMO/n",result); goto fail; } return 0; fail: DEMO_cleanup_module(); //失败清除模块 return result; }

    模块卸载:

    void DEMO_cleanup_module(void) { dev_t devno = MKDEV( DEMO_MAJOR,0 ); //删除键盘 if( DEMO_devices ) { cdev_del(&DEMO_devices->cdev); kfree(DEMO_devices); } //释放设备号 unregister_chrdev_region(devno,1); }

    //模块的打开操作

    int DEMO_open(struct inode *inode, struct file *filp) { struct DEMO_dev *dev; //防止多次打开设备 if( demo_inc > 0 )return -ERESTARTSYS; demo_inc++; //获取DEMO_dev结构,并将它赋给filp->private_data,以后可以用filp->private_data访问DEMO_dev结构 dev = container_of(inode->i_cdev,struct DEMO_dev,cdev); //container_of宏 filp->private_data = dev; return 0; }

    //模块的释放操作

    int DEMO_release(struct inode *inode, struct file *filp) { //引用技术减少 demo_inc--; return 0; }

    //模块的读操作

    ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { int result; //得到文件的当前地址 loff_t pos = *f_pos; //判断位置合法性 if(pos >= 256) { result = 0; goto out; } if( count > ( 256 - pos ) ) { count = 256 - pos; } pos += count; if( copy_to_user(buf,demoBuffer + *f_pos,count) ) { count = -EFAULT; goto out; } //改变文件的当前位置 *f_pos = pos; out: return count; }

    //模块的写操作

    ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { ssize_t retval = -ENOMEM; loff_t pos = *f_pos; //判断位置的合法性 if( pos >= 256 ) { goto out; } if( count > ( 256 - pos ) ) { count = 256 - pos; } pos += count; //将数据复制到内核空间 if( copy_from_user(demoBuffer + *f_pos,buf,count) ) { retval = -EFAULT; goto out; } //改变文件的当前位置 *f_pos = pos; return count; out: return retval; }

    //模块的调整操作

    loff_t DEMO_llseek(struct file *filp, loff_t off, int whence) { loff_t pos; pos = filp->f_pos; switch( whence ) { case 0: pos = off; break; case 1: pos += off; break; case 2: default: return -EINVAL; } //超出范围 if( (pos > 256) || (pos < 0) ) { return -EINVAL; } return filp->f_pos = pos; }

    //模块的控制

    int DEMO_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { if( cmd == 0 ) { printk("ioctl command1 successfully/n"); return 0; } if( cmd == 1 ) { printk("ioctl command2 successfully/n"); return 0; } printk("ioctl error/n"); return -EFAULT; }

     

    抱歉.我还不是很会写Makefile所以.我就用很见的方法来编译的.下面给出我的Makefile

    obj-m := Memory.o

     

    编译驱动是需要源代码的.我用的是ubLinux.

    make -C /forlinx/Linux-2.6.28.6/ M$=(pwd) modules

     

    给出测试代码跟驱动代码的完整版:

    驱动代码:

     

    #include <linux/module.h>

    #include <linux/kernel.h>

    #include <linux/fs.h>

    #include <linux/errno.h>

    #include <linux/types.h>

    #include <linux/fcntl.h>

    #include <linux/cdev.h>

    #include <linux/version.h>

    #include <linux/vmalloc.h>

    #include <linux/ctype.h>

    #include <linux/pagemap.h>

     

    MODULE_AUTHOR("fgj");

    MODULE_LICENSE("Dual BSD/GPL");

     

    int DEMO_init_module(void);

    void DEMO_cleanup_module(void);

    int DEMO_release(struct inode *inode, struct file *filp);

    int DEMO_open(struct inode *inode, struct file *filp);

    ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);

    ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);

    loff_t DEMO_llseek(struct file *filp, loff_t off, int whence);

    int DEMO_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);

     

    struct DEMO_dev

    {

    struct cdev cdev;

    };

     

    struct file_operations DEMO_fops = {

    .owner = THIS_MODULE,

    .llseek = DEMO_llseek,

    .read = DEMO_read,

    .write = DEMO_write,

    .ioctl = DEMO_ioctl,

    .release = DEMO_release,

    };

     

    struct  DEMO_dev *DEMO_devices;

    static unsigned char demo_inc = 0;

    static u8 demoBuffer[256]; //分配的内存空间

    #define DEMO_MAJOR 224 //主设备号

     

    int DEMO_init_module(void)

    {

    int result;

    dev_t dev = 0;

    dev = MKDEV(DEMO_MAJOR,0);

    result = register_chrdev_region(dev,1,"DEMO");

    if( result < 0 )

    {

    printk(KERN_WARNING "DEMO:can't get major %d/n",DEMO_MAJOR);

    return result;

    }

    //分配DEMO_dev,该结构是自定义的

    DEMO_devices = kmalloc(sizeof(struct DEMO_dev),GFP_KERNEL);

    if(!DEMO_devices)

    {

    result = -ENOMEM;

    goto fail;

    }

    memset( DEMO_devices,0,sizeof( struct DEMO_dev ) );

    //初始化一个字符驱动

    cdev_init(&DEMO_devices->cdev,&DEMO_fops);

    DEMO_devices->cdev.owner = THIS_MODULE;

    DEMO_devices->cdev.ops = &DEMO_fops;

    //向内核添加字符设备

    result = cdev_add(&DEMO_devices->cdev,dev,1);

    if( result )

    {

    printk(KERN_NOTICE "Error %d adding DEMO/n",result);

    goto fail;

    }

    return 0;

    fail:

    DEMO_cleanup_module();

    return result;

    }

    void DEMO_cleanup_module(void)

    {

    dev_t devno = MKDEV( DEMO_MAJOR,0 );

    //删除键盘

    if( DEMO_devices )

    {

    cdev_del(&DEMO_devices->cdev);

    kfree(DEMO_devices);

    }

    //释放设备号

    unregister_chrdev_region(devno,1);

    }

    module_init(DEMO_init_module);

    module_exit(DEMO_cleanup_module);

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

    {

    struct DEMO_dev *dev;

    //防止多次打开设备

    if( demo_inc > 0 )return -ERESTARTSYS;

    demo_inc++;

    //获取DEMO_dev结构,并将它赋给filp->private_data,以后可以用filp->private_data访问DEMO_dev结构

    dev = container_of(inode->i_cdev,struct DEMO_dev,cdev); //container_of宏

    filp->private_data = dev;

    return 0;

    }

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

    {

    //引用技术减少

    demo_inc--;

    return 0;

    }

    ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)

    {

    int result;

    //得到文件的当前地址

    loff_t pos = *f_pos;

    //判断位置合法性

    if(pos >= 256)

    {

    result = 0;

    goto out;

    }

    if( count > ( 256 - pos ) )

    {

    count = 256 - pos;

    }

    pos += count;

    if( copy_to_user(buf,demoBuffer + *f_pos,count) )

    {

    count = -EFAULT;

    goto out;

    }

    //改变文件的当前位置

    *f_pos = pos;

    out:

    return count;

    }

    ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)

    {

    ssize_t retval = -ENOMEM;

    loff_t pos = *f_pos;

    //判断位置的合法性

    if( pos >= 256 )

    {

    goto out;

    }

    if( count > ( 256 - pos ) )

    {

    count = 256 - pos;

    }

    pos += count;

    //将数据复制到内核空间

    if( copy_from_user(demoBuffer + *f_pos,buf,count) )

    {

    retval = -EFAULT;

    goto out;

    }

    //改变文件的当前位置

    *f_pos = pos;

    return count;

    out:

    return retval;

    }

    loff_t DEMO_llseek(struct file *filp, loff_t off, int whence)

    {

    loff_t pos;

    pos = filp->f_pos;

    switch( whence )

    {

    case 0:

    pos = off;

    break;

    case 1:

    pos += off;

    break;

    case 2:

    default:

    return -EINVAL;

    }

    //超出范围

    if( (pos > 256) || (pos < 0) )

    {

    return -EINVAL;

    }

    return filp->f_pos = pos;

    }

    int DEMO_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)

    {

    if( cmd == 0 )

    {

    printk("ioctl command1 successfully/n");

    return 0;

    }

    if( cmd == 1 )

    {

    printk("ioctl command2 successfully/n");

    return 0;

    }

    printk("ioctl error/n");

    return -EFAULT;

    }

     

    //测试代码

    #include<sys/types.h>

    #include<unistd.h>

    #include<fcntl.h>

    #include<linux/rtc.h>

    #include<linux/ioctl.h>

    #include<stdio.h>

    #include<stdlib.h>

    #define COMMAND1 0

    #define COMMAND2 1

    void main()

    {

    int fd;

    int i;

    char data[256];

    int retval;

    fd = open("/dev/fgj",O_RDWR);

    if(fd==-1)

    {

    perror("error open/n");

    exit(-1);

    }

    printf("open /dev/smbus successfully/n");

    retval=ioctl(fd,COMMAND1,0);

    if(retval==-1)

    {

    perror("ioctl error/n");

    exit(-1);

    }

    printf("send command1 successfully/n");

    retval=write(fd,"LvApp",5);

    if(retval==-1)

    {

    perror("write error/n");

    exit(-1);

    }

    retval=lseek(fd,0,0);

    if(retval==-1)

    {

    perror("lseek error/n");

    exit(-1);

    }

    retval=read(fd,data,5);

    if(retval==-1)

    {

    perror("read error/n");

    exit(-1);

    }

    printf("read successfully:%s/n",data);

    close(fd);

    }

     

     

     


    最新回复(0)