鸿 网 互 联 www.68idc.cn

当前位置 : 服务器租用 > cms安装教程 > 新云cms > >

动手写led字符驱动模块

来源:互联网 作者:佚名 时间:2015-08-11 09:12
刚开始学写驱动,光看看听听,感觉都对;但是动手的时候发现困难重重 , 这算是自己动手写成的第一个驱动模块(helloworld就不算了)。 首先头文件也是一个重点,至少我觉得是,往往写写就会漏,说明自己对驱动的函数还不够了解 #includelinux/module.h#incl

刚开始学写驱动,光看看听听,感觉都对;但是动手的时候发现困难重重这算是自己动手写成的第一个驱动模块(helloworld就不算了)。

首先头文件也是一个重点,至少我觉得是,往往写写就会漏,说明自己对驱动的函数还不够了解

#include<linux/module.h>
#include<linux/kernel.h>               //前2个是写驱动必备的
#include<linux/cdev.h>                 //这个毫无疑问写字符驱动要用,其中用到cdev结构体
#include<linux/fs.h>                   //fs中定义了第二个重要的数据struct file
#include<linux/device.h>
#include<mach/gpio.h>
#include<linux/gpio.h>
#include<asm/io.h>
#include<linux/ioctl.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LJJ EMAIL:346619201@QQ.COM");

unsigned int *GPM4CON;
unsigned int *GPM4DAT;

dev_t ljj_dev;
struct cdev ljj_cdev;           //这2个变量,我自己写着就混淆了,比较重要。
                                //dev_t 用在设备编号的分配和释放,动态设备创建中。
                                //cdev结构用在其cdev_init,以及add到设备中。

struct class* ljj_class;
struct device *parent;        //这是用来动态创建设备的变量,暂时不清楚为什么

//fops中自定义的ioctl的实现
static long ljj_ioctl(struct file *fp,unsigned int arg,unsigned long value)
{
    int tmp;
    if(value == 0)
    {
        tmp = ioread32(GPM4DAT);                   //使用ioread32来读取32位的数据
        tmp &= ~(1<<arg);
        iowrite32(tmp,GPM4DAT);                    //iowrite32时用来写入32位的数据
        printk(KERN_WARNING"led%d on\n",arg);      //KERN_WARING的警告级别可以在终端中显示信息
    }else{
        tmp = ioread32(GPM4DAT);
        tmp |= (1<<arg);
        iowrite32(tmp,GPM4DAT);
        printk(KERN_WARNING"led%d off\n",arg);        
    }    
    return 0;
}
//fops的定义
struct file_operations ljj_fops={
    .owner =     THIS_MODULE,
    //.open =     ljj_open,
    //.release =     ljj_close,
    .unlocked_ioctl = ljj_ioctl,
};
//初始化
static int __init init_led(void)
{
    int ret = 0;
    unsigned int tmp;
    ret = alloc_chrdev_region(&ljj_dev,0,5,"ljj_cdev"); //设备编号的申请
    if(ret!=0)
    {
        goto error1;
    }
    cdev_init(&ljj_cdev,&ljj_fops);                 //初始化亦分配到的结构(LDD中这么解释)
    ljj_cdev.owner = THIS_MODULE;                   //cdev结构的owner需要单独初始化!
    ljj_cdev.ops = &ljj_fops;                       //把自定义的fops结构,传给cdev结构
    ret = cdev_add(&ljj_cdev,ljj_dev,1);            //添加字符设备
    if(ret!=0)
    {
        goto error2;
    }
    ljj_class = class_create(THIS_MODULE,"ljj_cdev");   //这其实是添加udev动态创建设备的,在驱动初始化的代码里,调用class_create为该设备创建一个class,再为每个设备调用 class_device_create创建对应的设备,在模块加载的时候udev就会自动在/dev目录下创建一个"ljj_cdev"的设备文件
    if(IS_ERR((void*)ljj_class))
    {
        goto error3;
    }
    parent = device_create(ljj_class,NULL,ljj_dev,NULL,"ljj_cdev");
    if(IS_ERR((void*)parent))
    {
        goto error4;
    }
    GPM4CON = ioremap(0x110002E0,4);       //io口的重映射,(地址,长度),从而使我们可以操作该io口
    GPM4DAT = ioremap(0x110002E4,4);       //地址信息根据datasheet,和原理图来确定
    tmp = ioread32(GPM4CON);
    tmp |= 0x1111;
    iowrite32(tmp,GPM4CON);
    printk("I am ljj's led.ko ! GPM4CON:%x \n",tmp);

    return 0;
error4:
    class_destroy(ljj_class);
error3:
    cdev_del(&ljj_cdev);
error2:
    unregister_chrdev_region(ljj_dev,5);
error1:
    printk(KERN_WARNING"ljj_cdev error!\n");
    return -1;
}



static void __exit clean_led(void)
{
    iounmap(GPM4DAT);                 //解除重映射,释放io资源
    iounmap(GPM4CON);
    class_destroy(ljj_class);         //摧毁这个东西,反正是用来动态创建设备的
    cdev_del(&ljj_cdev);              //移除字符设备
    unregister_chrdev_region(0,5);    //释放设备编号
    printk("ljj's led.ko is cleaned! \n");
}

module_init(init_led);              //提醒内核,哪个是insmod调用的函数
module_exit(clean_led);             //同上,这个时rmmod调用的函数



最后也写一段应用程序在自己友善的Tiny4412(A9)开发板,open设备ljj_dev,用ioctl函数来控制led亮灭。其中发现一个小bug,led0,led1,led3都能正常控制,而led2,无法控制,开始以为是硬件问题,后来重启A9开发板,启动rcS中系统自带的led跑马灯,发现led2能够正常亮灭。目前仍然在试图解决该bug。

网友评论
<