Linux驱动编写(字符设备编写框架)

发表于:2013-1-29 09:54

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:feixiaoxing    来源:51Testing软件测试网采编

  上次我们编写了一个简单的字符设备,但是涉及的内容比较少,只有open和read两个函数。今天,我们打算在此基础上扩充一下内容。基本的思路是这样的:(1)编写字符设备下需要处理的各个函数,包括open、release、read、write、ioctl、lseek函数;(2)编写一个用户侧的程序来验证我们编写的驱动函数是否正确。当然,我们编写的代码部分参考了宋宝华先生的《linux设备驱动开发详解》一书,在此说明一下。

  在开始今天的内容之前,其实有一些题外话可以和大家分享一下。自从工作以来,我个人一直都有一个观点。那就怎么样利用简单的代码来说明开发中的问题,或者是解释软件中的原理,这是一个很高的学问。有些道理看上去云里雾里说不清楚,其实都可以通过编写代码来验证的。os可以、cpu可以、cache可以、编译器可以、网络协议也可以,很多很多的内容完全可以通过几行代码就可以表达得非常清楚,但是事实上我们并没有这么做。我想原因无非是这么几条,一来授业者对相关知识的学习也是停留在概念上而已,二来我们的学习过于死板和教条、太关注知识、不求实践,三就是学习者自身缺少思考的能力、缺少自我反省的能力、对很多东西不求甚解。对于简单的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 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!");

21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号