LED驱动分析

第一次玩驱动,就来分析老师给的源码

#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/uaccess.h>




#include "led.h"




/* led对应的GPIOM虚拟地址*/
unsigned long GPIOM_VA_BASE;




/*led对应GPIOM各寄存器虚拟地址*/
#define GPIOM_CON_VA GPIOM_VA_BASE
#define GPIOM_DAT_VA GPIOM_VA_BASE+0x4
#define GPIOM_PUD_VA GPIOM_VA_BASE+0x8




/*led对应GPIOM物理地址*/
#define GPIOM_PA_BASE 0x7f008820




struct resource ok6410_led_resource = {
.name  = "led io-mem",
.start = GPIOM_PA_BASE,
.end   = GPIOM_PA_BASE + 0xc,
.flags = IORESOURCE_MEM,
};




/* 完成申请、映射,映射获得的虚拟地址由全局变量GPIOM_VA_BASE获得。
     同时,完成GPIOM的配置寄存器进行初始化,设置led对应端口为输出 */
static void ok6410_led_pin_setup(void)
{

       //这里获得内存的起始地址与大小
unsigned long start = ok6410_led_resource.start; 
unsigned long size  = ok6410_led_resource.end - start; 
unsigned long tmp; 

/* 申请IO内存*/
request_mem_region(start, size, ok6410_led_resource.name);

/* 映射io内存*/
GPIOM_VA_BASE = (unsigned long)ioremap(start, size);

printk ("<1> [GPIOM_VA_BASE = 0x%lx]\n", GPIOM_VA_BASE);


  
/*对应管脚设置为输出*/
tmp = readl(GPIOM_CON_VA);
tmp = (tmp & ~(0xffffU))|(0x1111U);
writel(tmp, GPIOM_CON_VA);

/*对应管脚置高,使led全灭*/
tmp = readl(GPIOM_DAT_VA);
tmp |= 0xF;
writel(tmp, GPIOM_DAT_VA);
}


/* led端口释放函数,进行申请和映射的逆过程*/
static void ok6410_led_pin_release(void)
{
iounmap((void*)GPIOM_VA_BASE);
release_mem_region(ok6410_led_resource.start,
  ok6410_led_resource.end - ok6410_led_resource.start);
}


/* 读写led对应的GPIO数据寄存器值,writel(),readl()函数分别
     读写对应寄存器,也就是在访问映射过来的IO内存 */
static unsigned long ok6410_led_getdat(void)
{
return (readl(GPIOM_DAT_VA)& 0xF);
}




static void ok6410_led_setdat(int dat)
{
unsigned long tmp; 

tmp = readl(GPIOM_DAT_VA);
tmp = (tmp & ~0xF) | (dat&0xF);
writel(tmp, GPIOM_DAT_VA);
}


/* 实现led设备的ioctl操作,对于命令LED_IOCGETDAT,
     调用函数ok6410_led_getdat()
     获得led对应端口寄存器的值,并通过put_user函数将它传回用户空间。
     对于命令LED_IOCSETDAT,则将用户空间传过来的值,
     通过函数ok6410_led_setdat()设置对应的寄存器。*/
static long led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ioarg, ret;

    /* 检测命令的有效性*/
    if (_IOC_TYPE(cmd) != LED_IOC_MAGIC) 
        return -EINVAL;
    if (_IOC_NR(cmd) > LED_IOC_MAXNR) 
        return -EINVAL;
    
    /* 根据命令,执行相应的操作*/
    switch(cmd) {
        case LED_IOCGETDAT:  
            ioarg = ok6410_led_getdat();
            ret = put_user(ioarg, (int *)arg);
            break;
        case LED_IOCSETDAT:  
            ret = get_user(ioarg, (int *)arg);
    ok6410_led_setdat(ioarg);
            break;
        default:  
            return -EINVAL;
    }
return ret;
}
/* 将该设备作为混杂设备方式注册,因此主次设备号都不需要考虑,
     而只需要实现miscdevice结构,
    以及在设备初始化函数dev_init()中调用misc_register()函数注册即可 */
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl= led_ioctl,
};


static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
/* 将MISC_DYNAMIC_MINOR赋值给miscdevice结构的minor成员,
     表示自动分配次设备号,
     在使用misc_register()注册混杂设备后,
     还会在/dev目录下自动创建设备节点,节点名称由
     设备名称DEVICE_NAME指定。在模块初始化函数中,
     我们还需要对设备进行必要的初始化。
     这里设计一个用于初始化设备的函数接口ok6410_led_pin_setup(),
     后面再来实现它。
     模块卸载函数完成与初始化函数相反的工作。*/
static int __init dev_init(void)
{
int ret;


ok6410_led_pin_setup();
ret = misc_register(&misc);

printk (DEVICE_NAME" initialized minor=%d\n", misc.minor);


return ret;
}


static void __exit dev_exit(void)
{
ok6410_led_pin_release();
misc_deregister(&misc);
}


module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.enjoylinux.cn");

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值