Chinaunix首页 | 论坛 | 博客
  • 博客访问: 12302
  • 博文数量: 4
  • 博客积分: 25
  • 博客等级: 民兵
  • 技术积分: 70
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-08 19:32
文章分类
文章存档

2013年(4)

我的朋友

分类: LINUX

2013-01-16 21:57:54


现实世界中存在着大量的设备,这些设备在电气特性和I/O方式都各不相同。 为了简化设备驱动程序员的工作. Linux系统从这些各异的设备中提取出了共性的特征,将其划分为

1.字符设备(I/O传输过程以字符为单位,传输速率比较缓慢因而内核设施不提供缓存机制)比如鼠标,键盘和打印机。

2.块设备

3.网络设备

针对每一类设备都提供了对应的驱动模型框架,包括

1.基本的内核设施

2.文件系统接口

 
 

这节将详细讨论

  1.构成字符设备驱动程序的内核设施的幕后机制

  2.应用程序与字符设备驱动程序进行交互,也即应用程序如何使用字符设备驱动程序提供的服务。 字符设备驱动程序所提供的功能是以设备文件的形式提供给用户空间程序使用。










#include
#include
#include
#include

static struct cdev chr_dev;//定义一个字符设备对象
static dev_t ndev; //字符设备节点的设备号

static int chr_open(struct inode *nd, struct file *filp)
{
     int major=MAJOR(nd->i_rdev);
     int minor=MINOR(nd->i_rdev);
     printk("chr_open,major=%d,minor=%d\n",major,minor);
     return 0;
}


static ssize_t chr_read(struct file *f,char __user *u,size_t sz,loff_t *off)
{
     printf("In the chr_read() function!\n");
     return 0;
}

//字符设备驱动程序中非常关键的一个数据结构struct file_operations
struct file_operations chr_ops=
{
      .owner=THIS_MODULE,
      .open  =chr_open,
      .read   =chr_read,
}


//模块的初始化函数
static int demo_init(void)
{
      int ret;
      cdev_init(&chr_dev,&chr_ops);  //初始化字符设备对象
      ret=alloc_chrdev_region(&ndev,0,1,"chr_dev");//分配设备号
      if(ret<0)
                 return ret;  printk("demo_init():major=%d,minor=%d\n",MAJOR(ndev),MINOR(ndev));
      ret=cdev_add(&chr_dev,ndev,1);//将字符设备对象chr_dev注册进系统
     if(ret<0)
           return ret;
      return 0;
}


static void demo_exit(void)
{
        printk("Removing chr_dev module...\n");
        cdev_del(&chr_dev);//将字符设备对象chr_dev从系统中注销掉
        unregister_chrdev_region(ndev,1); //释放分配的设备号
}

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Dennis @AMDLinuxFGL");
MODULE_DESCRIPTION("A char device driver as an example");
 
obj-m :=demo_chr_dev.o
KERNELDIR:=/lib/module/$(shell uname -r)/build 
PWD :=$(shell pwd)

default:
     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
     rm -f *.o *.ko *.mod.c  





#include
#include
#include

#define CHR_DEV_NAME "/dev/chr_dev"

int main()
{
     int ret;
     char buf[32];
     int fd=open(CHR_DEV_NAME,O_RDONLY|O_NDELAY);
     if(fd<0)
     {
          printf("open file %s failed\n",CHR_DEV_NAME);
          return -1;
     }
     read(fd,buf,32);
     close(fd);


     return 0;
} 

使用gcc来生成应用程序的可执行文件main:


gcc main.c -o main

主要是用open打开一个设备文件节点,然后在打开的设备文件描述符fd上调用read函数。

将展示应用程序如何通过文件系统调用,穿越内核空间,呼叫到设备驱动程序实现的各种接口函数。


使用insmod把demo_chr_dev.ko加入到系统:

insmod demo_chr_dev.ko


使用dmesg查看insmod的输出信息


可以看到alloc_chrdev_region函数给内核模块demo_chr_dev.ko分配的主设备号major=248,minor=0。

根据这个设备号信息用mknod命名在系统的/dev目录下为该模块生成一个新的设备文件节点:

mknod /dev/chr_dev c 248 0

ls -l /dev/chr_dev

有了对应的设备文件后,现在我们可以运行我们的应用程序了:

./main

查看dmesg对此的输出信息:

我们见证了应用程序成功调用到了设备驱动程序实现的函数。

 
 

struct file_operations

其成员变量基本上都是函数指针

现实中字符设备驱动程序的编写,其实基本上是围绕着如何实现struct file_operations中的那些函数指针成员而展开的。


                                   
阅读(1265) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~