Chinaunix首页 | 论坛 | 博客
  • 博客访问: 120319
  • 博文数量: 61
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 230
  • 用 户 组: 普通用户
  • 注册时间: 2014-04-26 11:35
个人简介

实践Linux的理论

文章存档

2015年(1)

2014年(60)

我的朋友

分类: 嵌入式

2014-04-29 16:46:52

今天,我们打算在此基础上扩充一下内容。基本的思路是这样的:(1)编写字符设备下需要处理的各个函数,包括open、release、read、write、ioctl、lseek函数;(2)编写一个用户侧的程序来验证我们编写的驱动函数是否正确。当然,我们编写的代码部分参考了宋宝华先生的《linux设备驱动开发详解》一书,在此说明一下。
    在开始今天的内容之前,其实有一些题外话可以和大家分享一下。自从工作以来,我个人一直都有一个观点。那就怎么样利用简单的代码来说明开发中的问题,或者是解释软件中的原理,这是一个很高的学问。有些道理看上去云里雾里说不清楚,其实都可以通过编写代码来验证的。os可以、cpu可以、cache可以、编译器可以、网络协议也可以,很多很多的内容完全可以通过几行代码就可以表达得非常清楚,但是事实上我们并没有这么做。我想原因无非是这么几条,一来授业者对相关知识的学习也是停留在概念上而已,二来我们的学习过于死板和教条、太关注知识、不求实践,三就是学习者自身缺少思考的能力、缺少自我反省的能力、对很多东西不求甚解。对于简单的linux设备,我们完全可以通过这几行代码说清楚问题,免得大家还要苦苦追寻,百思而不得入门。

    好了,说了这么多,我们看看现在的驱动代码是怎么修改的把。
[cpp] view plaincopy
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
  
#include  
#include  
#include  
  
#define CHRMEM_SIZE 0x1000  
#define MEM_CLEAR   0x1  
  
static int chr_major;  
  
struct chr_dev  
{  
    struct cdev cdev;  
    unsigned char mem[CHRMEM_SIZE];  
};  
  
struct chr_dev* char_devp;  
  
int chr_open(struct inode* inode, struct file* filp)  
{  
    filp->private_data = char_devp;  
    return 0;  
}  
  
int chr_release(struct inode* inode, struct file* filp)  
{  
    return  0;  
}  
  
static int chr_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg)  
{  
    struct chr_dev* dev = filp->private_data;  
      
    switch(cmd)  
    {  
        case MEM_CLEAR:  
            memset(dev->mem, 0, CHRMEM_SIZE);  
            break;  
              
        default:  
            return -EINVAL;  
    }  
      
    return 0;  
}  
  
static ssize_t chr_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 chr_dev* dev = filp->private_data;  
      
    if(p >= CHRMEM_SIZE)  
    {  
        return 0;  
    }  
      
    if(count > CHRMEM_SIZE - p)  
    {  
        return 0;  
    }  
      
    if(copy_to_user(buf, (void*)(dev->mem + p), count))  
    {  
        return -EINVAL;  
    }  
    else  
    {  
        *ppos += count;  
        ret = count;  
    }  
      
    return ret;  
}  
  
static ssize_t chr_write(struct file* filp, const char __user* buf, ssize_t size, loff_t *ppos)  
{  
    unsigned long p = *ppos;  
    unsigned int count = size;  
    int ret = 0;  
    struct chr_dev* dev = filp->private_data;  
      
    if(p >= CHRMEM_SIZE)  
    {  
        return 0;  
    }  
      
    if(count > CHRMEM_SIZE - p)  
    {  
        count = CHRMEM_SIZE - p;  
    }  
      
    if(copy_from_user(dev->mem + p, buf, count))  
    {  
        ret = -EINVAL;  
    }  
    else  
    {  
        *ppos += count;  
        ret = count;  
    }  
      
    return ret;  
}  
  
static loff_t chr_llseek(struct file* filp, loff_t offset, int orig)  
{  
    loff_t ret = 0;  
      
    /* orig can be SEEK_SET, SEEK_CUR, SEEK_END */  
    switch(orig)  
    {  
        case 0:  
            if(offset < 0)  
            {  
                ret = -EINVAL;  
                break;  
            }  
              
            if((unsigned int) offset > CHRMEM_SIZE)  
            {  
                ret = -EINVAL;  
                break;  
            }  
              
            filp->f_pos = (unsigned int) offset;  
            ret = filp->f_pos;  
            break;  
              
        case 1:  
            if((filp->f_pos + offset) > CHRMEM_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 chr_ops =   
{  
    .owner    = THIS_MODULE,  
    .llseek   = chr_llseek,  
    .read     = chr_read,  
    .write    = chr_write,  
    .ioctl    = chr_ioctl,  
    .open     = chr_open,  
    .release  = chr_release  
};  
  
static void chr_setup_cdev(struct chr_dev* dev, int index)  
{  
    int err;  
    int devno = MKDEV(chr_major, index);  
      
    cdev_init(&dev->cdev, &chr_ops);  
    dev->cdev.owner = THIS_MODULE;  
      
    err = cdev_add(&dev->cdev, devno, 1);  
    if(err)  
    {  
        printk(KERN_NOTICE "Error happend!\n");  
    }  
}  
  
int chr_init(void)  
{  
    int result;  
    dev_t ndev;  
  
    result = alloc_chrdev_region(&ndev, 0, 1, "chr_dev");    
    if(result < 0 )    
    {    
        return result;    
    }     
      
    printk("chr_init(): major = %d, minor = %d\n", MAJOR(ndev), MINOR(ndev));  
    chr_major = MAJOR(ndev);  
      
    char_devp = kmalloc(sizeof(struct chr_dev), GFP_KERNEL);  
    if(!char_devp)  
    {  
        result = -ENOMEM;  
        goto final;  
    }  
      
    memset(char_devp, 0, sizeof(struct chr_dev));  
    chr_setup_cdev(char_devp, 0);  
    return 0;  
      
final:  
    unregister_chrdev_region(ndev, 1);  
    return 0;  
}  
  
void chr_exit()  
{  
    cdev_del(&char_devp->cdev);  
    kfree(char_devp);  
    unregister_chrdev_region(MKDEV(chr_major, 0), 1);  
}  
  
module_init(chr_init);  
module_exit(chr_exit);  
  
MODULE_LICENSE("GPL");  
MODULE_AUTHOR("feixiaoxing!163.com");  
MODULE_DESCRIPTION("A simple device example!");  
    不可否认,我们的代码出现了更多的内容,但是基本框架还是一致的。要是说区别,无非就是我们在原来的基础上添加了新的处理函数而已。说起来,我们对于设备的主要操作也就是这么几种,大家如果对此的概念已经非常成熟了,那么后面的学习就会轻松很多。当然和之前的驱动一样,我们也需要make &  insmod char.ko & mknod /dev/chr_dev c 249 0。接下来,为了验证上述的内容是否正确,编写一段简单的测试代码是必不可少的。
[cpp] view plaincopy
#include  
#include  
#include  
  
#define MEM_CLEAR 0x01  
#define CHAR_DEV_NAME "/dev/chr_dev"  
  
int main()  
{  
        int ret;  
        int fd;  
        int index;  
        char buf[32];  
  
        /* open device */  
        fd = open(CHAR_DEV_NAME, O_RDWR | O_NONBLOCK);  
        if(fd < 0)  
        {  
                printf("open failed!\n");  
                return -1;  
        }  
  
        /* set buffer data, which will be stored into device */  
        for(index = 0; index < 32; index ++)  
        {  
                buf[index] = index;  
        }  
  
        /* write data */  
        write(fd, buf, 32);  
        memset(buf, 0, 32);  
  
        /* read data */  
        lseek(fd, 0, SEEK_SET);  
        read(fd, buf, 32);  
        for(index = 0; index < 32; index ++)  
        {  
                printf("data[%d] = %d\n", index, buf[index]);  
        }  
  
        /* reset all data to zero, read it and check whether it is ok */  
        ioctl(fd, MEM_CLEAR, NULL);  
        lseek(fd, 0, SEEK_SET);  
        read(fd, buf, 32);  
        for(index = 0; index < 32; index ++)  
        {  
                printf("data[%d] = %d\n", index, buf[index]);  
        }  
  
        close(fd);  
        return 0;  
}  
    细心的朋友可能发现了,我们在用户侧代码中使用了很多的处理函数,基本上从open、release、read、write、lseek、ioctl全部包括了。测试代码处理的流程也非常简单,首先打开设备,接着写数据,后面就是读取数据,最后利用ioctl清除数据,程序返回。因为代码中包含了注释的内容,在此我们就不过多赘述了。大家慢慢看代码,应该都会了解和明白的。


    希望以上的这段内容对大家有所帮助。
阅读(659) | 评论(0) | 转发(0) |
0

上一篇:块驱动范例1

下一篇:ubuntu 12.04 安装wps

给主人留下些什么吧!~~