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");

文章说明:
此博文只为总结分享学习经验,许多内容不能也并未详细描述,如每个函数的参数、返回值,何为内核空间、用户空间,怎么操作寄存器等等,这些知识点都在相关资料中能得到更专业、详细的解答。

0 0
原创粉丝点击