一个简单的字符驱动程序

来源:互联网 发布:淮北微商自然知彼 编辑:程序博客网 时间:2024/05/24 15:42

一、背景

    为了了解设备驱动程序的框架,在此编写一个简单的字符驱动程序,以此来对驱动程序的框架进行一个简单的了解。

二、设备驱动程序

    所谓设备驱动程序,其实就是计算机硬件与外部设备进行通信的接口。由于硬件设备各式各样,有了设备驱动程序,应用程序就可以不用在意设备的具体细节,而方便的与外部设备进行通信。从外部设备读取数据,或是将数据写入外部设备,即对设备进行控制。

三、设备驱动程序框架

    设备的种类繁多是可想而知的,所以设备的驱动程序也是各式各样。由此需要建立一个统一的规范:SVR4(Unix System V Rlease 4)提出了DDI/DKI(Driver-Device Interface/Driver-Kernel Interface)规范。这个SVR4是UNIX操作系统的一种内核标准。

    规范分为以下三个部分:

    1、驱动程序与内核的接口

    2、驱动程序与设备的接口

    3、驱动程序与系统引导的接口

    其中,驱动程序与内核的接口是通过数据结构file_opration完成的。驱动程序与设备的接口描述了驱动程序如何与设备交互,这与具体的设备是密切相关的。驱动程序与系统引导的接口其实就是驱动程序对设备进行初始化。

四、简单的字符驱动程序

    我这里直接将我编写的字符驱动程序展示出来,然后对其进行分析:

#include<linux/init.h>
#include<linux/module.h>
#include<linux/types.h>
#include<linux/fs.h>
#include<linux/mm.h>
#include<linux/sched.h>
#include<linux/cdev.h>
#include<asm/io.h>
#include<asm/switch_to.h>
#include<asm/uaccess.h>
#include<linux/kernel.h>

MODULE_LICENSE("GPL");

#define MYCDEV_MAJOR 231
#define MYCDEV_SIZE 1024

static int mycdev_open(struct inode *inode,struct file *fp)
{
  return 0;
}

static int mycdev_release(struct inode *inode,struct file *fp)
{
  return 0;
}

static ssize_t mycdev_read(struct file *fp,char __user *buf,size_t size,loff_t *pos)
{
  unsigned long p = *pos;
  unsigned int count = size;
  char kernel_buf[MYCDEV_SIZE] = "This is mycdev!";
  int i;

  if(p >= MYCDEV_SIZE)
    return -1;
  if(count > MYCDEV_SIZE)
    count = MYCDEV_SIZE - p;
  if(copy_to_user(buf,kernel_buf,count) != 0){
      printk("read error!\n");

      return -1;
    }

  printk("reader:%d bytes was read...\n",count);
  return count;
}

static ssize_t mycdev_write(struct file *fp,const char __user *buf,size_t size,loff_t *pos)
{
  return size;
}

static const struct file_operations mycdev_fops =
{
  .owner = THIS_MODULE,
  .read = mycdev_read,
  .write = mycdev_write,
  .open = mycdev_open,
  .release = mycdev_release,
};

static int __init mycdev_init(void)
{
  int ret;

  printk("mycdev module is starting..\n");

  ret = register_chrdev(MYCDEV_MAJOR,"my_cdev",&mycdev_fops);
  if(ret < 0)
  {
    printk("register failed..\n");
    return 0;
  }
  else
  {
    printk("register success..\n");
  }
  return 0;
}

static void __exit mycdev_exit(void)
{
  printk("mycdev module is leaving..\n");
  unregister_chrdev(MYCDEV_MAJOR,"my_cdev");
}

module_init(mycdev_init);
module_exit(mycdev_exit);

    首先看一下file_operation结构体,此结构体是该驱动程序的核心。它给出了对文件操作的函数的定义。当然,具体的实现函数是留给驱动程序编写的。

struct file_operations {
 struct module *owner;
 loff_t (*llseek) (struct file *, loff_t, int);
 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
 ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 int (*readdir) (struct file *, void *, filldir_t);
 unsigned int (*poll) (struct file *, struct poll_table_struct *);
 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
 long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
 int (*mmap) (struct file *, struct vm_area_struct *);
 int (*open) (struct inode *, struct file *);
 int (*flush) (struct file *, fl_owner_t id);
 int (*release) (struct inode *, struct file *);
 int (*fsync) (struct file *, loff_t, loff_t, int datasync);
 int (*aio_fsync) (struct kiocb *, int datasync);
 int (*fasync) (int, struct file *, int);
 int (*lock) (struct file *, int, struct file_lock *);
 ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
 unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
 int (*check_flags)(int);
 int (*flock) (struct file *, int, struct file_lock *);
 ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
 ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
 int (*setlease)(struct file *, long, struct file_lock **);
 long (*fallocate)(struct file *file, int mode, loff_t offset,
     loff_t len);
 int (*show_fdinfo)(struct seq_file *m, struct file *f);
};

    此结构体对文件操作的函数给出了定义。种类繁多。

    我们这里的file_operation结构体进行初始化时仅初始化了4个函数。这些使用的函数在程序的前半部分已经给出了定义。

五、调试程序

    由于编写的是内核模块,所以需要用make进行编译。这个我在之前的博客已经写过如何编写Makefile文件。编译完毕后将模块插入。

    然后,通过cat /proc/devices来看系统中未使用的字符设备主设备号,我这里看到的是my_cdev,对应的是231号。

    接下来使用mknod命令创建设备文件结点,然后用chmod命令修改权限为777。此时设备就可以使用了。

    这里我们需要注意一下/proc/devices与/dev下的显示的设备的不同之处。

    在/proc/devices下,显示的是驱动程序生成的设备及其主设备号。其中主设备号可用来让mknod作为参数。

    在/dev下的设备是mknod生成的设备,其中,用户通过使用/dev下的设备名来使用设备。

六、编写用户态测试程序

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
  int testdev;
  int i,ret;
  char buf[10];

  testdev = open("/dev/mycdev",O_RDWR);
  if(testdev == -1){
    printf("connot open file..\n");
    exit(1);
  }
  if((ret = read(testdev,buf,10)) <10){
    printf("read error!\n");
    exit(1);
  }
  for(i=0;i<10;i++)
    printf("%d\n",buf[i]);

  close(testdev);

  return 0;
}
    这个程序没什么好说的。

七、运行结果

插入模块后查看日志信息:

运行测试程序:

    这里我们看到,它输出了数组的前十个字节,对应着:This is my

查看日志信息:

    这个我们看到,驱动也打印出了一段文字,十个字节被读取。

卸载模块:



八、总结

    这里只是简单的介绍了一下设备驱动程序的框架,作为一个对驱动程序的简单了解。


   

0 0