tiny6410 蜂鸣器字符设备驱动<1>
来源:互联网 发布:手机记工天软件 编辑:程序博客网 时间:2024/05/22 15:57
这两周主要学习了字符设备驱动、杂项设备驱动以及驱动程序设计的核心理论与技巧。完成了一个蜂鸣器的字符设备驱动和一个LED杂项设备驱动。做一下总结。
字符设备驱动:字符设备驱动是基本的串行输入输出驱动,将一些按字节、字进行读取、写入的设备做成驱动模块。Insmod装载驱动模块到内核,则用户空间与内核空间的交互就可以达成控制设备的目的。为了方便驱动的设计与调试,将驱动做成模块obj-m。
字符设备设计模式:内核提供了一套字符设备的接口,利用这些接口完成字符设备的注册和注销。
注册:
1、获取设备号:字符设备的设备后数据类型为dev_t,用函数MKDEV(MAJOR,MINOR)获得设备号。但还要向内核申请到这一个设备号(想要的设备号与实际可以得到的设备号的区别)。因此还要通过静态获取设备号或者动态获取设备号(区别是,主设备号是否为0)。静态申请获取设备号的前提是已知设备号的情况。动态申请获取设备号则是未知的,但可以避免设备号冲突。在使用动态申请获取设备号时,函数调用成功会将设备号放入到参数dev_t dev中这时应该用MAJOR(dev)获得主设备号并在成功注册字符设备后将信息打印出来,利于创建设备结点。
int register_chrdev_region(dev_tfrom,unsigned count,const char *name);
intalloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char*name);
如果函数调用发生错误,返回小于0的值。
2、初始化化字符设备:字符设备结构体(struct cdev)的初始化包括file_operations、owner。file_oerations包含有字符设备的控制方法。owner为驱动模块的拥有者,为权限设置,一般设置为THIS_MODULE。
voidcdev_init(struct cdev *cdevp,struct file_operations *ops);
cdevp->owner= THIS_MODULE;
cdevp->fops= ops;
3、添加字符设备:将初始化的字符设备添加到字符设备队列。完成这一步,字符设备的注册就全部做完了。参数unsigned count表明添加到设备队列的字符设备个数。
voidcdev_add(struct cdev *cdevp,dev_t devn,unsigned count);
注销:
1、 从字符设备队列删除字符设备:将字符设备结构体从设备队列删除。
voidcdev_del(struct cdev *cdevp);
2、注销字符设备号:释放掉设备号(设备号属于有限资源,主设备号为212个,次设备号为220个)。参数unsigned count为要注销的设备号个数。
voidunregister_chrdev_region(dev_t from,unsigned count);
字符设备基本的注册、注销过程就是这个模式。如果加入了私有数据filp->private_data、信号量或者其他的驱动程序设计的核心理论,则是在这个基础上进行添加。这里注明一下在显示的定义了设备结构体的情况下,进行字符设备注销注册的不同的地方。
定义了设备结构体dev_name_t及其变量dev
struct dev_name_t ={
struct cdev cdev;
char buf[1024];
}dev;
那么在字符设备的初试化函数****_init(void)中,则需要给该自定义的设备结构体分配内核空间。调用kmalloc(size,GFP_KERNEL)分配内核空间,成功则返回所分配的内核空间的首地址。再调用初始化函数memset(addr,0,size);清空addr地址的内核空间。需要注意的是,在ARM的设计中需要对ARM寄存器进行设置,使用的是寄存器的物理地址。而在Linux系统上进行设备的驱动设计时,内核是不能识别物理地址的,需要将设备的物理地址映射为内核空间的虚拟地址。调用ioremap(PADDR,int);将物理地址PADDR,映射为长度为int字节的虚拟地址。然互内核空间就可以通过ioread32()\iowrite32()对设备进行读写操作。结合蜂鸣器的字符设备驱动做进一步的解释。
蜂鸣器驱动程序源码:
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 名称: beep_modu.c
// 工程: beep
// 作者: Nozuowilldie
// 描述:
// 蜂鸣器驱动,打开、关闭蜂鸣器。
// 主要函数:
// beep_init()\beep_exit()
// 版本: V1.0 2014/05/03
// 修改记录:
// 1 2014/5/3
//////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#define BEEP_MAJOR 0
#define MAGIC 'f'
#define START_CMD0 _IO(MAGIC,0)
#define STOP_CMD1 _IO(MAGIC,1)
#define GPFCON 0x7F0080A0
#define GPFDAT 0x7F0080A4
#define REG_AND_VAL 0xc0000000
#define REG_OR_VAL 0x10000000
unsigned int unContrlReg,unDataReg;
unsigned long *beepCtrlAddr;
unsigned long *beepDataAddr;
static int beep_major = BEEP_MAJOR;
static char *beep_dev_name = "beep_modu";
struct beep_dev{
struct cdevcdev;
};
struct beep_dev dev;
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数名称: start_beep
// 函数功能:
// 打开蜂鸣器
// 参数说明:无
// 返回值:无
// 全局变量:
beepDataAddr GPFDAT寄存器物理地址映射后的虚拟地址
unDataReg 写入GPFDAT中的暂存值
// 备注:无
// 修改记录:
// 1: Nozuowilldie 2014.05.03撰写
//////////////////////////////////////////////////////////////////////////////////////////////////////////
static void start_beep(void)
{
printk("Startbeep\n");
unDataReg =ioread32(beepDataAddr);
unDataReg &=0xBFFF;
unDataReg |=0x4000;
iowrite32(unDataReg,beepDataAddr);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数名称: stop_beep
// 函数功能:
// 关闭蜂鸣器
// 参数说明:无
// 返回值:无
// 全局变量:
beepDataAddr GPFDAT寄存器物理地址映射后的虚拟地址
unDataReg 写入GPFDAT中的暂存值
// 备注:无
// 修改记录:
// 1: Nozuowilldie 2014.05.03撰写
//////////////////////////////////////////////////////////////////////////////////////////////////////////
static void stop_beep(void)
{
printk("Stopbeep\n");
unDataReg =ioread32(beepDataAddr);
unDataReg &=0xBFFF;
iowrite32(unDataReg,beepDataAddr);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数名称: beep_ioctl
// 函数功能:
// 蜂鸣器I/O控制函数,打开、关闭蜂鸣器
// 参数说明:
// filp: 输入参数,设备句柄
// cmd: 输入参数,I/O控制命令
// arg: 输入参数,补充I/O控制命令
// 返回值:
// 0: 正确
// -EINVAL: 错误的I/O命令
// 全局变量:
// START_CMD0 打开蜂鸣器
// STOP_CMD1 关闭蜂鸣器
// 备注:在linux-2.6.38内核上加载编译驱动时,出现error:unknown field 'ioctl'specified in initializer
// 原因是:在2.6.38内核上file_operations发生了重大的改变:
// 原先的int (*ioctl)(struct inode*,struct file*, unsigned int, unsigned long);被改为了
// long(*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
// long(*compat_ioctl) (struct file *, unsigned int, unsigned long);
// 因而在实际驱动中,我们需要将原先的写的ioctl函数头给改成下面的unlocked_ioctl,在file_operations结构
// 体的填充中也是一样。
// 修改记录:
// 1: Nozuowilldie 2014.05.03撰写
//////////////////////////////////////////////////////////////////////////////////////////////////////////
static long beep_ioctl(struct file *filp,unsigned intcmd,unsigned long arg)
{
switch(cmd)
{
case START_CMD0:
start_beep();
break;
case STOP_CMD1:
stop_beep();
break;
default:
return-EINVAL;
}
return 0;
}
struct file_operations fops = {
.owner =THIS_MODULE,
.unlocked_ioctl= beep_ioctl,
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数名称: setup_chrdev
// 函数功能:
// 初试化字符设备,添加字符设备
// 参数说明:
// devn: 输入参数,设备号
// 返回值:无
// 全局变量:
// dev 蜂鸣器设备结构体
// 备注:无
// 修改记录:
// 1: Nozuowilldie 2014.05.03撰写
//////////////////////////////////////////////////////////////////////////////////////////////////////////
static void setup_chrdev(dev_t devn)
{
int err;
cdev_init(&dev.cdev,&fops);
dev.cdev.owner =THIS_MODULE;
dev.cdev.ops =&fops;
err =cdev_add(&dev.cdev,devn,1);
if(err)
printk("Error %d adding beepmem\n",err);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数名称: beep_init
// 函数功能:
// 完成蜂鸣器设备的注册
// 参数说明:无
// 返回值:
// 0: 正确
// 其他: 错误号
// 全局变量:
// beep_major 主设备号,初始值为0
// beep_dev_name 设备名,beep_modu
// GPFCON GPIO F端的控制寄存器物理地址
// GPFDAT GPIO F端的数据寄存器物理地址
// beepCtrAddr GPFCON映射到内核空间的虚拟地址
// beepDataAddr GPFDAT映射到内核空间的虚拟地址
// unContrlReg 读写寄存器值的暂存空间
// REG_AND_VAL 清除GPFCON寄存器的值,0xcfffffff
// REG_OR_VAL 设置GPFCON寄存器的值,0x10000000
// 备注:
// 1: MKDEV()得到设备号
// 2: register_chrdev_region()静态申请设备号,alloc_chrdev_region()动态申请设备号
// 3: setup_chrdev()初始化字符设备,添加字符设备,完成字符设备的注册
// 4: ioremap()完成物理地址到虚拟地址的映射,ioread32()/iowrite32()虚拟地址读写操作
// 修改记录:
// 1: Nozuowilldie 2014.05.03撰写
//////////////////////////////////////////////////////////////////////////////////////////////////////////
static int beep_init(void)
{
int ret = 0;
dev_t devn = MKDEV(beep_major,0);
if(BEEP_MAJOR)
register_chrdev_region(devn,1, beep_dev_name);
else
{
ret = alloc_chrdev_region(&devn,0,1, beep_dev_name);
beep_major = MAJOR(devn);
}
if(ret < 0)
{
printk("failed register\n");
return ret;
}
setup_chrdev(devn);
beepCtrlAddr =ioremap(GPFCON,4);
beepDataAddr =ioremap(GPFDAT,4);
unContrlReg =ioread32(beepCtrlAddr);
unContrlReg&= REG_AND_VAL;
unContrlReg |=REG_OR_VAL;
iowrite32(unContrlReg,beepCtrlAddr);
printk("Deviceinstall success!\n");
printk("DeviceMajor :%d\n",beep_major);
printk("Devicename :%s\n",beep_dev_name);
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数名称: beep_exit
// 函数功能:
// 完成蜂鸣器设备的注销
// 参数说明:无
// 返回值:无
// 全局变量:
// beep_major 主设备号
// dev 设备结构体
// 备注:
// 1: cdev_del删除字符设备
// 2: unregister_chrdev_region()注销字符设备
// 修改记录:
// 1: Nozuowilldie 2014.05.03撰写
//////////////////////////////////////////////////////////////////////////////////////////////////////////
static void __exit beep_exit(void)
{
cdev_del(&dev.cdev);
unregister_chrdev_region(MKDEV(beep_major,0),1);
}
module_init(beep_init);
module_exit(beep_exit);
/*beep_major设置为驱动模块参数,手动设置主设备号*/
module_param(beep_major,int,S_IRUGO);
MODULE_AUTHOR("Nozuowilldie");
MODULE_LICENSE("Dual BSD/GPL");
- tiny6410 蜂鸣器字符设备驱动<1>
- tiny6410 蜂鸣器字符设备驱动<2>
- Tiny6410 简单的LED字符设备驱动
- Tiny6410 简单的LED字符设备驱动
- Linux字符设备驱动之Tiny6410 LED驱动分析
- 字符设备驱动之蜂鸣器与PWM——FS2410
- 字符设备驱动1
- 字符设备驱动(1)
- 字符设备驱动1
- 1、字符设备驱动
- mini6410蜂鸣器驱动学习(混杂设备)
- misc类设备与蜂鸣器驱动
- OK6410之蜂鸣器buzzer字符驱动
- Tiny6410 简单的设备驱动helloworld_driver
- tiny6410 linux混杂设备 led驱动
- 字符设备驱动分析(1)
- 驱动开发-字符设备1
- 字符设备驱动框架1
- 写个关于使用cocostudio Armature实现动画自由切换的小demo
- HTTP协议详解
- 关于在Box2dTest中开启调试打印
- [VisualStudio]_[增加自定义宏,自定义属性键值]
- jQuery 选择器
- tiny6410 蜂鸣器字符设备驱动<1>
- jQuery 参考手册 - 选择器
- <PY><core python programming笔记>C6 序列:字符串和列表和元组(一)
- 安装ArcGISServer 时,出现"Internal Error 2878, Existing_Web_Site, ListBox, Default Web Site,80,1"解决
- 常用的SVN命令
- 使用Java Base64解密算对openssl的base64加密字符串进行解密
- Android中onTouch返回值含义
- 计算机视觉 资源 链接
- <PY><core python programming笔记>C6 序列:字符串和列表和元组(二)