LED字符设备驱动程序
来源:互联网 发布:删除矩阵a的第7号元素 编辑:程序博客网 时间:2024/04/30 04:01
LED字符设备驱动程序@2440
Step:
1.设备驱动程序通常需要一个入口函数(通俗理解就是加载此驱动程序模块就会执行的函数,通常做一些初始化操作),用module_init(firstdrv_init)修饰。
2.同样的需要一个出口函数(在模块卸载时调用,通常做与初始化相反的卸载工作。用module_exit(firstdrv_exit)修饰。
3.定义初始化函数.
static int firstdrv_init(void)
{
...
}
所做的事情通常为:
a.为一个字符驱动获取一个或多个设备编号。
int register_chrdev_region(dev_t from, unsigned count, const char *name)
int alloc_chrdev_region(dev_t * dev, unsigned baseminor, unsigned count, const char * name)
两者的区别是,前者为静态分配后者为动态分配。
b.初始化字符设备结构体,通常要定义相应的文件操作结构体(稍后介绍),以使它们关联,使用之前必须定义cdev结构体。
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
c.添加字符设备到系统
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
d.为驱动注册一个类,需要先定义这个类结构体,在2.6.32内核里的定义应区别与更早更早的版本
class_register(class);
static struct class xxx_class = {
.name = "xxx",
...
};
e.创建类下的设备
struct device *device_create(struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, ...)
上面五个为基本步骤,在卸载函数里应该做相反的动作,稍后介绍。
因为本程序旨在写一个LED字符驱动程序,所以在初始化函数里应该将
将LED所在IO地址空间映射到内核的虚拟地址空间上去,便于访问,调用
void *ioremap(unsigned long phys_addr, unsigned long size)
4.定义出口函数.
static void firstdrv_exit(void)
{
...
}
通常做与注册相反的操作:
a.去除类下的设备
void device_destroy(struct class *class, dev_t devt)
b.去除类
void class_destroy(struct class *cls)
c.字符设备删除
void cdev_del(struct cdev *p)
d.释放原先申请的设备号
void unregister_chrdev_region(dev_t from, unsigned count)
一般的操作结束之后,本驱动还要去除io映射
void iounmap(*io_addr)
5.定义与字符设备相关联的操作函数结构体
static struct file_operations firstdrv_ops = {
.owner = THIS_MODULE,
.open = xxx_open,
.write = xxx_write,
.read = xxx_read,
...
};
6.实现各xxx函数,其中包括设备文件被打开时会调用的xxx_open函数,被关闭时会调用的xxx_release函数,被读/写数据时的xxx_read/xxx_write函数等。
本驱动程序在打开时,需要配置寄存器为输出模式(实现xxx_open函数),参照数据手册:
因为LED被配置为输入模式,只需要接受用户空间传到内核空间的数据,所以还需要实现xxx_write函数。根据不同的值设置寄存器即可点亮或者关闭LED。其中会用到函数
static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
在像用户空间传数据时还会用到
static inline long copy_to_user(void __user *to, const void *from, unsigned long n)。
7.包含必要的头文件,遵循GPL协议
MODULE_LICENSE("GPL");
具体实现代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#define FIRST_DEV MKDEV(250, 0)
volatile unsigned long *gpbcon = NULL;
volatile unsigned long *gpbdat = NULL;
static struct cdev firstdrv_cdev;
static struct class_device *firstdrv_class_dev;
static int firstdrv_open(struct inode *inode, struct file *file)
{
printk(KERN_NOTICE "Device opened!\n");
/* 配置为输出 */
*gpbcon &= ~((0x3<<(5*2)) |(0x3<<(6*2)) |(0x3<<(7*2)) |(0x3<<(8*2)));
*gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2)));
return 0;
}
static ssize_t firstdrv_write(struct file *file, const char __user *userbuf,
size_t bytes, loff_t *off)
{
int val;
copy_from_user(&val, userbuf, bytes);
if (1 == val) {
*gpbdat &= ~((0x1<<5) | (0x1<<6) | (0x1<<7) | (0x1<<8));
} else {
*gpbdat |= ((0x1<<5) | (0x1<<6) | (0x1<<7) | (0x1<<8));
}
return 0;
}
static struct class firstdrv_class = {
.name = "firstdrv_class",
};
static struct file_operations firstdrv_ops = {
.owner = THIS_MODULE,
.open = firstdrv_open,
.write = firstdrv_write,
};
static int firstdrv_init(void)
{
int ret;
ret = register_chrdev_region(FIRST_DEV, 1, "firstdrv");
if (ret) {
printk(KERN_ERR "Unable to register firstdrv\n");
goto err_reg;
}
cdev_init(&firstdrv_cdev, &firstdrv_ops);
ret = cdev_add(&firstdrv_cdev, FIRST_DEV, 1);
if (ret) {
printk(KERN_ERR "Unable to add cdev\n");
goto err_add_cdev;
}
class_register(&firstdrv_class);
/* /dev/first */
device_create(&firstdrv_class, NULL, FIRST_DEV, NULL, "first");
gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16);
gpbdat = gpbcon + 1;
return 0;
err_add_cdev:
cdev_del(&firstdrv_cdev);
err_reg:
unregister_chrdev_region(FIRST_DEV, 1);
return 0;
}
static void firstdrv_exit(void)
{
device_destroy(&firstdrv_class, FIRST_DEV);
class_destroy(&firstdrv_class);
cdev_del(&firstdrv_cdev);
unregister_chrdev_region(FIRST_DEV, 1);
iounmap(gpbcon);
}
module_init(firstdrv_init);
module_exit(firstdrv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Angrad Young");
文章说明:
此博文只为总结分享学习经验,许多内容不能也并未详细描述,如每个函数的参数、返回值,何为内核空间、用户空间,怎么操作寄存器等等,这些知识点都在相关资料中能得到更专业、详细的解答。
- LED字符设备驱动程序
- 字符设备驱动----LED驱动程序
- 字符设备驱动--LED驱动程序
- 字符设备驱动程序之LED驱动程序
- 基于mini2440的led字符设备驱动程序
- LED驱动程序---字符设备控制技术
- 1_字符设备驱动程序之LED驱动程序
- Linux字符设备驱动程序开发(3)-LED驱动程序设计
- 第12课第2.3节 字符设备驱动程序之LED驱动程序_操作LED
- 普通字符设备LED驱动程序(IO映射内存实现)
- 自制简单字符型设备驱动程序——LED驱动
- 第一篇 字符设备驱动程序之LED流水灯驱动
- 自制简单字符型设备驱动程序——LED驱动
- 字符设备驱动程序的编写_点亮LED灯
- 11.LED驱动程序设计(1)-字符设备控制
- led字符驱动程序
- 字符设备驱动程序之LED驱动程序__韦老师linux视频源码
- 第12课第2.1节 字符设备驱动程序之LED驱动程序_编写编译
- 15 个一定要会的 Windows7 快捷键
- 给VB.NET开发者的46个忠告(转)
- shutdown命令定时关机全攻略
- MapReduce之二次排序
- CSS3实现镂空文字
- LED字符设备驱动程序
- 将文字显示在Flash上的代码示例
- void main与int main之间的区别?
- C/C++ 误区:int mian()和void main() 的区别与争议!
- if...else if...else用法
- C习题:华氏度转摄氏度
- Android开发:用Drawable XML绘制带阴影效果的圆形按钮
- C拓展:利用canf...if...else...printf写的简单加密程序
- C习题:输入三个任意整数将其按从大到小重新排序