如何编写Linux设备驱动程序

有很多朋友关心驱动,但更菜的菜鸟居多:)总结了一下 I/O驱动改成个更简单的LED驱动吧
做的工作非常简单,就是让连在GPC0-GPC2上的LED顺序闪10
目的就是演示一下驱动过程。
先补充一下基础知识 懂的朋友就不用看了

嵌入式驱动的概念

设备驱动程序是操作系统内核和机器硬件之间的接口,设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它主要完成的功能有:对设备进行初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据、回送应用程序请求的数据以及检测和处理设备出现的错误。

Linux
将设备分为最基本的两大类:一类是字符设备,另一类是块设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了。字符设备以单个字节为单位进行顺序读写操作,通常不使用缓冲技术;块设备则是以固定大小的数据块进行存储和读写的,如硬盘、软盘等,并利用一块系统内存作为缓冲区。为提高效率,系统对于块设备的读写提供了缓存机制,由于涉及缓冲区管理、调度和同步等问题,实现起来比字符设备复杂得多。LCD是以字符设备方式加以访问和管理的,Linux把显示驱动看做字符设备,把要显示的数据一字节一字节地送往LCD驱动器。

Linux
的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。为了管理这些设备,系统为设备编了号,每个设备号又分为主设备号和次设备号。主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。对于常用设备,Linux有约定俗成的编号,如硬盘的主设备号是3Linux为所有的设备文件都提供了统一的操作函数接口,方法是使用数据结构struct file_operations。这个数据结构中包括许多操作函数的指针,如open()close()read()write()等,但由于外设的种类较多,操作方式各不相同。Struct file_operations结构体中的成员为一系列的接口函数,如用于读/写的read/write函数和用于控制的ioctl等。打开一个文件就是调用这个文件file_operations中的open操作。不同类型的文件有不同的file_operations成员函数,如普通的磁盘数据文件,接口函数完成磁盘数据块读写操作;而对于各种设备文件,则最终调用各自驱动程序中的I/O函数进行具体设备的操作。这样,应用程序根本不必考虑操作的是设备还是普通文件,可一律当作文件处理,具有非常清晰统一的I/O接口。所以file_operations是文件层次的I/O接口。

开始写了
采用了在代码里加注释的方法,同时把几个文件上传了一下,喜欢的朋友可以下载当作模板。每个文件以==隔开
一共需要写写3个文件,1个驱动头文件,1个驱动文件,一个驱动测试用程序文件
分别是test.htest.cledtest.c

简单说说驱动都做什么,怎么做
1
系统加载驱动
2
应用程序里打开设备(文件)
3
应用程序对设备操作
4
应用程序关闭设备(文件)
5
系统关闭设备

应用程序如何对设备操作?
记得C语言里怎么写文件吗?这里很相象的。对于一般的字符设备(还有块设备,网络设备等等)主要有3个函数(还有很多,可以看)llseek read: write: ioctl: 这里只用ioctl:控制函数,当然也可以使用读写函数操作IO 口,但ioctl:似乎更适合。
具体实现可以看ledtest.c文件了。

test.c
中主要有几个函数 分别负责初始化和清除,打开和关闭。以及ioctl对串口寄存器写一些数据。
初始化和清除,打开和关闭函数里都各有一句主要句,已经分别作了注释。只要记住就好了。

对寄存器操作就不单独说了,需要看44B0数据手册了。好了 剩下的看代码吧。
==============================
==========test.h==================
==============================

/****************************************Copyright (c)*****************************
*********************
** FREE
**
**--------------File Info-------------------------------
** File Name: config.h
** Last modified Date: 2006-9-9
** Last Version: 1.0
** Descriptions: User Configurable File
**
**----------------------------------------------------
** Created By: ZLG CHENMINGJI
** Created date: 2006-9-9
** Version: 1.0
** Descriptions: First version
**
**-------------------------------------------------
** Modified by:MAMAJINCO
** Modified date:2006-9-9
** Version:1.0
** Descriptions:
在此忠心感谢ZLG的模版 我的高质量编程意识起源于此
**
*****************************************************/
//
防止重复包含此文件而设置的宏
#ifndef __CONFIG_H
#define __CONFIG_H

//
包含必要的头文件
i nclude <linux/config.h>
i nclude <linux/module.h> //模块必须包含的头文件
i nclude <linux/kernel.h> /* printk()函数,用于向内核输出信息 */
i nclude <linux/fs.h> /* 很重要 其中包含了如file_opration等结构 此结构用于文件层接口 */
i nclude <linux/errno.h> /* 错误代码*/
i nclude <asm/uaccess.h>
i nclude <linux/types.h>
i nclude <linux/mm.h>
i nclude <asm/arch/s3c44b0x.h>

/
********************************/
/*
应用程序配置 */
/********************************/
//
以下根据需要改动
//
定义主设备号 设备名称

#define LED_MAJOR_NR 231 //231~239 240~255
都可以

#define DEVICE_NAME "led" /* name for messaging */

#define SET_LED_OFF 0
#define SET_LED_ON
1

#endif
/************************ End Of File
*********************************************************/
=============END===============


==============================
============test.c================
==============================

/*************Copyright (c)**************************
** FREE
**
**--------------File Info-----------------------------------------
** File Name: test.c
** Last modified Date: 2006-9-9
** Last Version: 1.0
** Descriptions: User Configurable File
**
**----------------------------------------------------
** Created By: ZLG CHENMINGJI
** Created date: 2006-9-9
** Version: 1.0
** Descriptions: First version
**
**--------------------------------------------------------
** Modified by:MAMAJINCO
** Modified date:2006-9-9
** Version:1.0
** Descriptions:
在此忠心感谢ZLG的模版 我的高质量编程意识起源于此
**
***********************************************************/
i nclude "test.h"//包含驱动头文件


/************************************************************
function announce

******************************************************/
//
以下是关键函数的声明
static int led_open(struct inode *inode, struct file *filp);
//
打开设备时用的 linux把设备当作文件管理 设备最好在用的时候再打开 尽量不要提前
static int led_release(struct inode *inode, struct file *filp);
static int
led_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
unsigned long param);
//
控制函数 这里用于控制LED亮与灭
int led_init(void);//
注册时用的 注意 模块注册的越早越好
void led_cleanup(void);//
卸载时用的

/*************************************************************
** "
全局和静态变量在这里定义"
** global variables and static variables define here

/****************************************************************/

static struct file_operations LED_fops = /*
前面基础部分提到的重要结构体了*/
{
owner: THIS_MODULE,

#if 0/*
注意:#if 0-#endif里的代码是不编译的,但这里为了驱动模板讲解上的完整性仍然加上了*/
llseek: gpio_llseek,
read: gpio_read,
write: gpio_write,
#endif
ioctl: led_ioctl,//
控制函数
open: led_open, //
打开函数,打开文件时做初始化的
release: led_release,//
释放函数,关闭时调用
};


/***********************************************************
***
** Function name: led_open
** Descriptions: open device
** Input:inode: information of device
** filp: pointer of file
** Output 0: OK
** other: not OK
** Created by: Chenmingji
** Created Date: 2006-9-9
**-------------------------------------------------------------
** Modified by:mamajinco
** Modified Date: 2006-9-9
**--------------------------------------------------------------
**************************************************************/
static int led_open(struct inode *inode, struct file *filp)
{
/*
初始化放在OPEN*/
(*(volatile unsigned *)S3C44B0X_PCONC) &= 0xffffffc0;
(*(volatile unsigned *)S3C44B0X_PCONC) |= 0xffffffd5; /*GPIO C
0~2 设置为输出*/

(*(volatile unsigned *)S3C44B0X_PUPC) &= 0xffffffc0;/*GPIO C
0~2 设置为上拉*/

MOD_INC_USE_CO
UNT;
return 0;
}

*********************************************************
** Function name: led_release
** Descriptions: release device
** Input:inode: information of device
** filp: pointer of file
** Output 0: OK
** other: not OK
** Created by: Chenmingji
** Created Date: 2006-9-9
**-----------------------------------------------------
** Modified by: mamajinco
** Modified Date: 2006-9-9
**--------------------------------------------------------
****************************************************/
static int led_release(struct inode *inode, struct file *filp)
{
MOD_DEC_USE_COUNT;
return(0);
}

/****************************************************
** Function name: led_ioctl
** Descriptions: IO control function
** Input:inode: information of device
** filp: pointer of file
** cmd: command
** arg: additive parameter
** Output 0: OK
** other: not OK
** Created by: Chenmingji
** Created Date: 2006-9-9
**-------------------------------------------------------
** Modified by: mamajinco
** Modified Date: 2006-9-9
**----------------------------------------------------------
***********************************************************/
static int led_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
if( arg > 2 )//
判断IO是否属于0-2
return -1;

switch(cmd)
{
case
0://ARG传来的IO口编号转换成寄存器数据,把相应IO口置低
(*(volatile unsigned *)S3C44B0X_PDATC) |= 0x1 << arg;

break;

case
1://ARG传来的IO口编号转换成寄存器数据,把相应IO口置高
(*(volatile unsigned *)S3C44B0X_PDATC) &= 0x0 << arg;
break;

default:
return -1;
break;
}
return 0;
}

/**************************************************
** Function name:
led_init
** Descriptions: init driver
** Input:none
** Output 0: OK
** other: not OK
** Created by: Chenmingji
** Created Date: 2006-9-9
**------------------------------------------------
** Modified by: mamajinco
** Modified Date: 2006-9-9
**-----------------------------------------------------
*************************************************/
int led_init(void)
{
int result;

result = register_chrdev(231,"led", &LED_fops);
/*
关键语句 用于注册
**
注意!这是传统的注册方法 2.4以上的linux版本中 加入了devfs设备文件系统 使注册更容易 但为了与大部分资料相** 大家看的方便 这里仍然使用老方法*/
if (result < 0) //
用于异常检测
{
printk(KERN_ERR DEVICE_NAME ": Unable to get major %d/n", LED_MAJOR_NR ); //printk
用于向内核输出信息
return(result);
}

printk(KERN_INFO DEVICE_NAME ": init OK/n");

return(0);
}

/************
*************************************************
** Function name: led_cleanup
** Descriptions: exit driver
** Input:none
** Output none
** Created by: Chenmingji
** Created Date: 2006-9-9
**--------------------------------------------------------
** Modified by: mamajinco
** Modified Date: 2006-9-9
**-----------------------------------------------------
**************************************************/
void led_cleanup(void)
{
unregister_chrdev(231, "led"); //
register_chrdev配对使用 用于清楚驱动
}


/***********************************************************
** End Of File
****************************************************/
===============END=============


=====================================
=============ledtest.c===============
===================================

/*********************Copyright (c)**************************
** FREE
**
**--------------File Info----------------------------
** File Name: led.c
** Last modified Date: 2006-9-9
** Last Version: 1.0
** Descriptions: User Configurable File
**
**-------------------------------------------------------
** Created By: ZLG CHENMINGJI
** Created date: 2006-9-9
** Version: 1.0
** Descriptions: First version
**
**--------------------------------------------------------
** Modified by:MAMAJINCO
** Modified date:2006-9-9
** Version:1.0
** Descriptions:
在此忠心感谢ZLG的模版 我的高质量编程意识起源于此
**
********************************************************/

i nclude <stdio.h>
i nclude <stdlib.h>
i nclude <unistd.h>
i nclude <fcntl.h>
i nclude <sys/types.h>
i nclude <sys/stat.h>

void delay(int delay)//
延时用函数
{
int i;
for(;delay>0;delay--)
{
for(i=0 ; i < 5000 ; i ++);

}
}

int main()
{
int fd1;
int j;

fd1= open("/dev/led" , O_RDWR);/*
打开设备,就象打开文件一样简单*/

if(fd1
== -1)/*异常处理*/
{
printf ( "file can not be open" );
return -1;
}

for (j =0 ; j< 10 ; j ++)/*
重复10*/
{
ioctl(fd1 , 1 , 0);/*GPC0
LED*/
delay(1000);
ioctl(fd1 , 0 , 0);/*GPC0
LED*/
ioctl(fd1 , 1 , 1);/*GPC1
LED*/
delay(1000);
ioctl(fd1 , 0 ,
1);/*GPC1LED*/
ioctl(fd1 , 1 , 2);/*GPC2
LED*/
delay(1000);
ioctl(fd1 , 0 , 2);/*GPC2
LED*/
delay(1000);
}

close (fd1);/*
关闭设备(文件)*/
return 0;

}

===============END===============

驱动编译进内核
编译的中对于菜鸟来说需要需要注意几点
1
被打错字,包括上面的函数中也是!
就算各位扔砖头我也得说,因为编译进内核是很费时间的~~而且最重要的是对于菜鸟来说make的错误提示都是一道关,绝对不要自己给自己设置障碍!我们团队里就常有兄弟姐妹出现这样的错误,怎么看怎么对,尤其是从书上抄下来的命令和字符,l1还有I你怎么分?最后一个是大写的i :)
2
不要用中文文件名 包括ABC(复件)
要不然MAKE出错
3
在各个现成的文件里修改的时候按照原有的格式修改 要不然菜鸟很难保证不犯低级错误
让我想起来了IBM的规律总结测试题:6 13 7 14 8 下一个数字是什么?

好了 开始修改!
===============START==============
uClinux-dist/linux-2.4.x/drivers/char/Makefile
----------------------------------------------
obj-$(CONFIG_C5471_WDT) += wdt_c5471.o
之后加
obj-$(CONFIG_TEST) += led.o
================END============

==============
===START===========
uClinux-dist/linux-2.4.x/drivers/char/Config.in
-----------------------------------------
if [ "$CONFIG_CPU_S3C44B0X" = "y" ]; then
bool 'Samsung S3C44B0X serial ports support' CONFIG_SERIAL_S3C44B0X
之后加
bool 'Test LED Driver' CONFI
G_TEST
================END=================
=================START=============
uClinux-dist/linux-2.4.x/drivers/char/mem.c
-----------------------------------------
开头的地方扎堆加
#ifdef CONFIG_LEDTEST
extern void led_init(void);
#endif

int __init chr
_dev_init(void)之后加
#ifdef CONFIG_TEST
led_init();
#endif
================END==============
=================START============
uClinux-dist/vendors/Samsung/44B0/Makefile
-----------------------------------------
ttypc,c,3,12 ttypd,c,3,13 ttype,c,3,1
4 ttypf,c,3,15/之后加
/
led,c,231,0 /
================END==============


把程序编译进内核
没什么说的了,和过去写的简单的程序一样加 但这里再重复一次

================START============
uClinux-dist/user/Makefile
-----------------------------------------
扎堆加个下面
dir_$(CONFIG_USER_LEDTE
ST) += LEDtest
=================END===============
================START============
uClinux-dist/config/Configure.help
-----------------------------------------
扎堆加个下面
CONFIG_USER_LEDTEST
Test the LED driver
=================END=============
====
============START================
uClinux-dist/config/Configure.in
-----------------------------------------
##############################
mainmenu_option next_comment
comment 'LED driver test PG'

bool 'LEDtest' CONFIG_USER_LEDTEST
endmenu

###############################
=================END=============

编译 烧写……省略200 想看的看我写的helloworld编译笔记吧
下面的操作在板子上执行
1 cd /dev
2 ls
看见里面有个LED了吧?
3 cd /proc
4 cat devices
看见驱动列表吧?
led 231
也应该在里面
5 LEDtest
在任何地方执行这个语句 就可以
之后看GPIOC口电平吧:)

原文地址 http://forum.ubuntu.org.cn/trackback.php?e=6647

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值