LINUX下一个LED驱动程序的详细分析

LINUX下一个LED驱动程序的详细分析  

2011-03-25 15:51:25|  分类: Linux + ARM9 +LP |  标签:linux下一个led驱动程序的详细分析  |字号 订阅

/* led.c

   By Genghao  

*/

 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

#include

#include

#include

#include

#include

#include

 

#include

#include

 

#include

 

#include "led.h"

 

#define DEV_NAME        "led"

#define GPIO_IOBASE io_p2v(GPIO_BASE)

/*

io_p2v(x) 这个函数的功能是物理地址(physical)向虚拟地址(virtual)的转换,对于有MMUARM9来说这个很重要,有p to v那么就一定有v to p,根据不同的映射关系,映射函数自然也就不同,在linux中该公式是以宏的方式给出的。

*/

static struct semaphore led_sem;

 

static int tp_led_Istruct inode *inode, struct file *I)

{

    __raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05

         try_module_get(THIS_MODULE);

/*

try_module_get(THIS_MODULE);函数的功能是使得该模块的引用计数加1,相反既然有加一,那么自然也就有减一,下面就会看到。

*/

         printk( KERN_INFO DEV_NAME " opened!\n");

         return 0;

}

 

static int tp_led_release(struct inode *inode, struct file *filp)

{

__raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05

         module_put(THIS_MODULE);

/*

模块的引用计数减一

*/

         printk(KERN_INFO DEV_NAME " released!\n");

         return 0;

}

 

static ssize_t tp_led_write(struct file *filp, const char __user *buff, size_t count, loff_t *ppos)

{

         int i;

         unsigned char ctrl=0;

         if (count > 1) {

                   return -EFBIG;

         }

        

         if (down_interruptible(&led_sem))

                   return -ERESTARTSYS;

 

         get_user(ctrl, (u8 *)buff);

/*

get_user

Name

get_user --  Get a simple variable from user space.

Synopsis

get_user ( x, ptr);

Arguments

x

Variable to store result.

ptr

Source address, in user space.

Context

User context only. This function may sleep.

Description

This macro copies a single simple variable from user space to kernel space. It supports simple types like char and intbut not larger data types like structures or arrays.

ptr must have pointer-to-simple-variable type, and the result of dereferencing ptr must be assignable to x without a cast.

Returns zero on success, or -EFAULT on error. On error, the variable x is set to zero.

意思是将一个简单的变量从用户空间传送到内核空间,毕竟驱动是运行在内核中。如果该函数(这是个宏)返回-EFAULT,表示失败,失败后,会将x付为0;函数成功返回0标识成功。

*/

//printk("write date ctrl=0x%0x\n", ctrl);

 

    i = (ctrl-0x30)&0x03;

    if(i==0) {

        __raw_writel(_BIT(5), GPIO_P3_OUTP_CLR(GPIO_IOBASE));//CLR GPIO_05

    } else {

        __raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05

    }

/*

由于LED的电路属于上拉型,所以只要AMR9 IO口输出低电平,那么LED就将被点亮。

*/

    up(&led_sem);

         return count;

}

 

static int tp_led_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)

{

          int level;

         if (_IOC_TYPE(cmd) != LED_IOC_MAGIC) {

                   return -ENOTTY;

         }

/*

上面的这段代码很有意思,说的通俗点,是为了防止命令冲突。今天先不讲这个,因为这个讲起来很费时间,但是很重要,下次我会详细说明!

*/

         if (_IOC_NR(cmd) >= LED_IOC_MAXNR) {

                   return -ENOTTY;

         }

/*

LED_IOC_MAXNR是定义在.h中的一个常量,它的意识是该模块的最大引用计数。

*/

       printk("arg=0x%x\n", arg);

         switch (cmd) {

         case SET_LED_ON:

        //printk("LED_ON\n");

        __raw_writel(_BIT(5), GPIO_P3_OUTP_CLR(GPIO_IOBASE));//CLR GPIO_05

        udelay(10);

                   break;

                  

         case SET_LED_OFF:

        //printk("LED_OFF\n");

        __raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05

        udelay(10);

                   break;

 

         default:

        __raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05

                   break;

         }

 

         return 0;

}

 

static struct file_operations tp_led_fops = {

         .owner   = THIS_MODULE,

         .write    = tp_led_write,

         .ioctl             = tp_led_ioctl,

         .open    = tp_led_open,

         .release  = tp_led_release,

};

/*

 file_operation就是把系统调用驱动程序关联起来的关键数据结构 

*/

 

static struct miscdevice tp_led_miscdev =

{

          .minor    = MISC_DYNAMIC_MINOR,

          .name    = DEV_NAME,

          .fops       = &tp_led_fops,

};

/*

先看下面的层次结构图
                     (
基类)
                     kobject --------------------
                    /               \                    \
                  /                  \                    \
                device          cdev                  driver
               /        \ (
设备驱动操作方法)           \
              /            \                                      \
    miscdevice         platform_device               platform_driver
(
设备驱动操作方法)    (设备的资源)                   (设备驱动

杂项设备(miscdevice
杂项设备也是在嵌入式系统中用得比较多的一种设备驱动。因为有的设备不是标准的,这个“标准的”可以这样理解,例如有的设备需要总线,而有压根就不需要。在 Linux 内核的include\linux目录下有Miscdevice.h文件,要把自己定义的misc device从设备定义在这里。其实是因为这些字符设备不符合预先确定的字符设备范畴,所有这些设备采用主编号10 ,一起归于miscdevice 也就是说,misc设备其实也就是特殊的字符设备。

    对于想LEDbeep这些不属于标准的设备,使用miscdevice 模型驱动比使用字符设备驱动要简单的多,复杂度大大降低,这就是为什么有这类设备的原因。

*/

 

static int tp_led_probe(struct device *dev)

{

         int ret;

         printk(KERN_INFO DEV_NAME " probing...\n");

         ret = misc_register(&tp_led_miscdev);

         if (ret)

                   printk(KERN_ERR "Failed to register miscdev.\n");

 

         return ret;

}

 

static int tp_led_remove(struct device *dev)

{

         misc_deregister(&tp_led_miscdev);

         printk(KERN_INFO DEV_NAME " removed!\n");

        

         return 0;

}

 

struct platform_device *tp_led_device;

static struct device_driver tp_led_driver = {

        .name    = DEV_NAME,

        .owner   = THIS_MODULE,

             .bus = &platform_bus_type,

        .probe   = tp_led_probe,

        .remove  = tp_led_remove,

};

/*通过上面的结构体 建立总线、驱动程序、设备三者之间的联系*/

 

static int __init tp_led_init(void)

{

         int rc = 0;

         int i;

 

         printk(KERN_INFO DEV_NAME " init...\n");

 

    __raw_writel(_BIT(5), GPIO_P3_MUX_CLR(GPIO_IOBASE));//pin as GPO_05

    __raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05

 

         tp_led_device = platform_device_register_simple(DEV_NAME, -1, NULL, 0);

         if(IS_ERR(tp_led_device)) {

                   goto out;

         }

                  

         rc = driver_register(&tp_led_driver);

        if (rc < 0) {

            platform_device_unregister(tp_led_device);

         }

 

         sema_init(&led_sem, 1);

out:

        return rc;

}

 

static void __exit tp_led_exit(void)

{

    __raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05

         printk(KERN_INFO DEV_NAME " init...\n");

    driver_unregister(&tp_led_driver);

    platform_device_unregister(tp_led_device);

         printk(KERN_INFO "tp_led exit!\n");

}

 

 

module_init(tp_led_init);

/*模块的初始化*/

module_exit(tp_led_exit);

/*模块的卸载*/

 

 

MODULE_AUTHOR("Abing ");

/*作者*/

MODULE_DESCRIPTION("ARM9-3250 led Driver");

/*描述*/

MODULE_LICENSE("GPL");

你可能感兴趣的:(LINUX下一个LED驱动程序的详细分析)