Fork Me on GitHub

源码开放学ARM

LASO - Learn ARM with Source Open

首页         目录索引         资料下载         代码下载         联系作者        
下载PDF打印版本

Linux 字符设备驱动

字符设备驱动结构

设备分类

字符设备

一个字符( char ) 设备是一种可以当作一个字节流来访问存取的设备( 如同一个文件 ); 一个字符驱动负责实现这种行为. 这样的驱动常常至少实现 open, close, read, 和 write 系统调用. 文本控制台( /dev/console )和串口( /dev/ttyS0 及其类似的设备 )就是两个字符设备的例子, 因为它们很好地展现了流的抽象. 字符设备通过文件系统结点来存取, 例如 /dev/tty1 和 /dev/lp0. 在一个字符设备和一个普通文件之间唯一有关的不同就是, 你经常可以在普通文件中移来移去, 但是大部分字符设备仅仅是数据通道, 你只能顺序存取.然而, 存在看起来象数据区的字符设备, 你可以在里面移来移去. 例如, frame grabber 经常这样, 应用程序可以使用 mmap 或者 lseek 存取整个要求的图像.

块设备

如同字符设备, 块设备通过位于 /dev 目录的文件系统结点来存取. 一个块设备(例如一个磁盘)应该是可以驻有一个文件系统的. 在大部分的 Unix 系统, 一个块设备只能处理这样的 I/O 操作, 传送一个或多个长度经常是 512 字节( 或一个更大的 2 的幂的数 )的整块. Linux, 相反, 允许应用程序读写一个块设备象一个字符设备一样 – 它允许一次传送任意数目的字节. 结果就是, 块和字符设备的区别仅仅在内核在内部管理数据的方式上, 并且因此在内核/驱动的软件接口上不同. 如同一个字符设备, 每个块设备都通过一个文件系统结点被存取的, 它们之间的区别对用户是透明的. 块驱动和字符驱动相比, 与内核的接口完全不同.

网络接口

任何网络事务都通过一个接口来进行, 就是说, 一个能够与其他主机交换数据的设备. 通常, 一个接口是一个硬件设备, 但是它也可能是一个纯粹的软件设备, 比如环回接口. 一个网络接口负责发送和接收数据报文, 在内核网络子系统的驱动下, 不必知道单个事务是如何映射到实际的被发送的报文上的. 很多网络连接( 特别那些使用 TCP 的)是面向流的, 但是网络设备却常常设计成处理报文的发送和接收. 一个网络驱动对单个连接一无所知; 它只处理报文.

字符设备驱动结构

字符设备是指发送或者接受数据按照字符方式进行,一般应用程序都通过设备文件来访问字符设备。

字符设备的驱动一般在 kernel-src/drivers/char 目录下。常见的字符设备有:鼠标,控制台,声卡,显示设备,touch panel,串口,并口等等。

我们可以在Linux源代码目录通过 make menuconfig来看到字符设备。

字符设备管理

我们都知道应用程序是通过设备文件来访问字符设备的。那么设备文件通过什么标示来对应相关的驱动呢?这就是我们前面提到的设备号。

因为应用程序要与字符设备进行数据交互(read,write)。那么驱动还要提供读写函数。 并且要求将读写函数与设备号连接起来。这就是我们下面要讲的应用和驱动的关联。

应用和驱动关联

在前面的课程我们谈到了设备文件, 给出了设备文件和设备号的概念。 这节课我们先看一个例子:

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

#define filename "/dev/akae_c"

int main (void)
{
	int fd;
	char buf[10];
	int ret;
	
	fd = open (filename,O_RDWR);
	
	if (fd < 0)
	{
	    printf ("Open %s file error,please use sudo !",filename);
	    return -1;
	}

	ret = read (fd,buf,10);
	if (ret != 10)
	{
	    printf ("NOTICE: read %s file,get %d Bytes\n",filename,ret);
	}
    
	ret = write (fd,buf,10);
	if (ret != 10)
	{
	    printf ("NOTICE: write %s file,get %d Bytes\n",filename,ret);
	}
	
	close (fd);
	
	return 0;
}

/* mychar_test.c */

#include <stdio.h>
#include <fcntl.h>

#include <sys/types.h>
#include <unistd.h>

int main(void)
{
	int fd;
	int local = 100;

	printf("hello, test char drv\n");
	printf("<main> &local = %p\n", &local);

	printf("current pid = %d\n", getpid());

	fd = open("mychar", O_RDWR); 
	printf("fd = %d\n", fd);

	if (fd < 0)
	{
		perror("open mychar failed!\n");
		return -1;
	}

	return 0;
}

/* mychar_drv.c */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/sched.h>

MODULE_LICENSE("GPL");

int mychar_open(struct inode * in, struct file * filp)
{
	int local = 100;

	printk("mychar open called!\n");

	printk("current pid = %d\n", current->pid);
	printk("current parent pid = %d\n", current->parent->pid);
	printk("current process name = %s\n", current->comm);
	printk("current open function = %p\n", mychar_open);

	printk("<driver> &local = %p\n", &local);
	printk("task struct size = %d\n", sizeof(*current));

//	while (1);

	printk("mychar open called finished!\n");
	
	return 0;
}

struct file_operations mychar_fops =
{
	.open = mychar_open,
};

static __init int mychar_init(void)
{
	int rc;

	printk("mychar init\n");

	// register mychar_drv 
	rc = register_chrdev(240, "this is my first char drv", &mychar_fops);

	printk("rc = %d\n", rc);

	return 0;
}


static __exit void mychar_exit(void)
{
	printk("mychar exit\n");

	// unregister mychar_drv 
	unregister_chrdev(240, "this is my first char drv");

	return;
}

module_init(mychar_init);
module_exit(mychar_exit);


/* Makefile */
CC = arm-linux-gcc

obj-m := mychar_drv.o
KDIR := /home/akaedu/teacher_li/linux-2.6.35.7/

all:
	make mychar_test
	make -C $(KDIR)	SUBDIRS=$(PWD) 	modules
	ls -l *.ko mychar_test

clean:
	-rm *.ko *.o *.order *.mod.c *.symvers
	-rm mychar_test

从上例中我们可以看出:

* 应用程序可以通过打开设备文件访问设备;
* 可以通过read, write函数访问设备驱动;
* 设备号是设备的唯一重要标示;
* 多个设备文件可以对应到一个设备号,反之则不行;
* 设备文件名可以根据需要命名,没有硬性规定;
* 设备驱动和设备文件之间,通过设备节点的设备号(id)关联起来,和取名(name)无关,和存放位置(/dev)无关;

上一节 | 目录索引 | 下一节

blog comments powered by Disqus