第一个字符设备驱动

转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/45054183

linux驱动分为字符设备、块设备驱动、网络驱动三种,其中以字符驱动最为简单。说起要写驱动自然想到从字符设备驱动写起。看了开发板官方的驱动代码,对新手来说简直是噩梦。新手来说要看懂,实在不容易。其中包含了很多知识和设计思想。所以我想还是尽可能从易到难来写这个系列,相信我,我会努力把我知道的都给大家讲清楚。

一、驱动代码

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>  

#define HELLO_CNT   1       //所请求连续设备编号个数

static int hello_major = 0; //默认内核自动分配
static int hello_minor = 0; //默认此设备号从0开始
static struct cdev hello_cdev;
static struct class *hello_class;

static int hello_open(struct inode *inode, struct file *file)
{
	printk("hello open\n");
	return 0;
}

static struct file_operations hello_fops = {
	.owner = THIS_MODULE,
	.open  = hello_open,
};

static int hello_char_init(void)
{
	dev_t devid;
	int err,result;

	/*1.分配主设备号*/
	if(hello_major)
	{
		devid = MKDEV(hello_major, hello_minor);
		result = register_chrdev_region(devid,HELLO_CNT,"hello");
	}
	else
	{
		result = alloc_chrdev_region(&devid,hello_minor,HELLO_CNT,"hello");
		hello_major = MAJOR(devid);
	}
	if(result < 0)
	{
		printk(KERN_WARNING"hello:can't get major %d\n", hello_major);
		return result;
	}

	/*2.注册字符设备驱动*/
	cdev_init(&hello_cdev, &hello_fops);
	err = cdev_add(&hello_cdev, devid, HELLO_CNT);
	if(err)
	printk(KERN_WARNING"Error %d adding hello",err);

	/*3.创建设备*/
	hello_class = class_create(THIS_MODULE, "hello");
	if (IS_ERR(hello_class))
	{
		printk(KERN_WARNING "class_create() failed for hello_class\n");
	}
	device_create(hello_class, NULL, MKDEV(hello_major, 0), NULL, "hello0"); /* /dev/hello0 */
	return 0;
}

static void hello_char_exit(void)
{
	device_destroy(hello_class, MKDEV(hello_major, 0));
	class_destroy(hello_class);

	cdev_del(&hello_cdev);
	unregister_chrdev_region(MKDEV(hello_major, 0), HELLO_CNT);

}
module_init(hello_char_init);
module_exit(hello_char_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ruoyunliufeng");

二、测试程序

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

/*
 * hello_test /dev/hello0
 */

void print_usage(char *file)
{
	printf("%s <dev>\n", file);
}

int main(int argc, char **argv)
{
	int fd;
	if (argc != 2)
	{
		print_usage(argv[0]);
		return 0;
	}

	fd = open(argv[1], O_RDWR);
	if (fd < 0)
		printf("can't open %s\n", argv[1]);
	else
		printf("can open %s\n", argv[1]);

	return 0;
}

三、驱动讲解

1.驱动的入口与出口

在用C写的应用程序中,我们程序的入口是main()函数。驱动程序也有入口,那就是module_init();括号里面的函数就是入口函数,有了入口自然就有出口module_exit();每当insmod XXX.ko的时候驱动就会进入入口函数。入口函数主要做一些初始化的工作。rmmod  XXX.ko的时候,驱动就会调用出口函数,出口函数组要做一些注销和清理工作。

2.字符驱动初始化

字符设备初始化的框架大体就是这样,可以直接当做模板来用,分三步:

1.分配诸设备号

主设备号可以自己定义,也可以交给内核帮你分配,但一般都推荐内核分配,所以这里用了一个if语句来分别处理这两种情况。

2.注册字符驱动

你的驱动要让内核知道就必须注册呀。调用cdev_init();cdev_add()两个函数

3.创建设备结点

这里选择自动创建的方式,你也完全可以在驱动中不写,然后自己去手动创建。首先class_create()然后device_create()。这样在/dev/下就能出现你的设备了。这样就可以对你的设备进行一系列的读写等操作了。

3.如何调用驱动

应用程序调用open函数,通过你在驱动中写的file_operations hello_fops去调用其中的hello_open。这样应用和驱动就建立了联系,其他函数也是一样。其余的实现也是一样,比如需要写入数据,就在hello_fops中加入写函数。驱动提供的是机制,不提供策略。策略是应用程序该干的事。“需要提供什么功能”即机制,“如何使用这些功能”即策略。每个函数都有它的功能,这里要注意不要乱写。否则你的驱动程序看上去就像一坨翔。

注:这里我并没有详细的去分析每个函数的参数,意义。我觉得这些东西完全可以自己去内核中看,我不想把我的文章变成翻译内核注释,文档。我希望看我的文章你能领会大概的框架,具体细节自己去看内核吧。源码之前,了无秘密。

参考:ldd3

时间: 2024-08-26 01:03:07

第一个字符设备驱动的相关文章

register_chrdev_region/alloc_chrdev_region和cdev注册字符设备驱动

内核提供了三个函数来注册一组字符设备编号,这三个函数分别是 register_chrdev_region().alloc_chrdev_region() 和 register_chrdev(). (1)register_chrdev  比较老的内核注册的形式   早期的驱动(2)register_chrdev_region/alloc_chrdev_region + cdev  新的驱动形式 (3)register_chrdev()函数是老版本里面的设备号注册函数,可以实现静态和动态注册两种方法

linux 字符设备驱动开发详解

一.设备的分类及特点 1.字符设备 字符设备是面向数据流的设备,没有请求缓冲区,对设备的存取只能按顺序按字节的存取而不能随机访问.    Linux下的大多设备都是字符设备.应用程序是通过字符设备节点来访问字符设备的.通常至少需要实现 open, close, read, 和 write 等系统调用.    设备节点一般都由mknod命令都创建在/dev目录下,包含了设备的类型.主/次设备号以及设备的访问权限控制等,如:crw-rw----  1 root  root 4, 64 Feb 18

linux设备驱动第三篇:写一个简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [plain] vie

Linux字符设备驱动剖析

一.先看看设备应用程序 1.很简单,open设备文件,read.write.ioctl,最后close退出.如下: intmain(int argc ,char *argv[]){ unsigned char val[1] = 1; int fd =open("/dev/LED",O_RDWR);//打开设备 write(fd,val,1);//写入设备,这里代表LED全亮 close(fd);//关闭设备 return 0; } 二./dev目录与文件系统 2./dev是根文件系统下

字符设备驱动编程(一)

当我们对字符设备进行编程的时候,需要做一些常有的准备工作,获取设备号,对设备文件操作函数的注册,文件信息的初始化,文件的内核表现形式,向内核的注册等等. 对字符设备的访问是通过文件系统内的设备名称进行的,通常在/dev目录下.使用ls -l 每行的第一个字符用来识别该文件类型,c就是字符设备驱动文件.b就是块设备驱动文件.内核通过主次设备号来进行管理设备.主设备号表示对应的驱动程序(虽然linux允许多个驱动程序共享主设备号,但是绝大部分的设备还是一个主设备号对应一个驱动程序),次设备号表示具体

Linux字符设备驱动框架

字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l /dev的时候,就能看到大量的设备文件,c就是字符设备,b就是块设备,网络设备没有对应的设备文件.编写一个外部模块的字符设备驱动,除了要实现编写一个模块所需要的代码之外,还需要编写作为一个字符设备的代码. 驱动模型 Linux一切皆文件,那么作为一个设备文件,它的操作方法接口封装在struct fi

linux设备驱动第三篇:如何写一个简单的字符设备驱动?

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [email prot

linux设备驱动第三篇:如何实现简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动 都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [email pro

05 字符设备驱动

一.字符设备驱动函数接口 1.初始化cdev结构体void cdev_init(struct cdev * cdev, const struct file_operations * fops)功能:初始化cdev结构体参数:@cdev cdev结构体 @fops 操作函数的结构体 2.申请设备号int register_chrdev_region(dev_t from, unsigned count, const char * name);参数:@from 包含主设备号的数字 @count 设备