原创 s3c6410的GPIO_LED的相关注释

2011-8-29 17:02 2229 8 8 分类: 软件与OS
/*   /arch/arm/mach-s3c6410/Mach-ldd6410.c   */
static struct gpio_led ldd6410_leds[] = {
    [0] = {
        .name = "LED1",
        .gpio = S3C64XX_GPM(0),
    },
    [1] = {
        .name = "LED2",
        .gpio = S3C64XX_GPM(1),
    },
    [2] = {
        .name = "LED3",
        .gpio = S3C64XX_GPM(2),
    },
    [3] = {
        .name = "LED4",
        .gpio = S3C64XX_GPM(3),
    },
};

static struct gpio_led_platform_data ldd6410_gpio_led_pdata = {
    .num_leds = ARRAY_SIZE(ldd6410_leds),
    .leds = ldd6410_leds,
};

static struct platform_device ldd6410_device_led = {
    .name       = "leds-gpio",
    .id     = -1,
    .dev        = {
        .platform_data = &ldd6410_gpio_led_pdata,
    },
};


/*-----------------------------------------------------------------
 ---------------------/include/linux/Leds.h------------------------
-----------------------------------------------------------------*/
 * Generic LED platform data for describing LED names and default triggers.
 */
struct led_info {
    const char    *name;
    const char    *default_trigger;
    int        flags;
};

struct led_platform_data {
    int        num_leds;
    struct led_info    *leds;
};

/* For the leds-gpio driver */
struct gpio_led {
    const char *name;  /*name会在/sysx显示相关的子目录*/
    const char *default_trigger;
    unsigned     gpio;
    u8         active_low;
};

struct gpio_led_platform_data {
    int         num_leds;   /*led灯的个数*/
    struct gpio_led *leds; /*leds指针*/
    int        (*gpio_blink_set)(unsigned gpio,
                    unsigned long *delay_on,
                    unsigned long *delay_off);
};


/*-----------------------------------------------------------------
 ------------------------ /include/linux/Leds.h ---------------------
-----------------------------------------------------------------*/



struct led_classdev {
    const char        *name;           /*设备名称*/
    int             brightness;        /*亮度*/
    int             flags;

#define LED_SUSPENDED        (1 << 0)

    /* Set LED brightness level 设置亮度级别*/
    /* Must not sleep, use a workqueue if needed */
    void        (*brightness_set)(struct led_classdev *led_cdev,
                      enum led_brightness brightness);
    /* Get LED brightness level 获取亮度级别 */
    enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);

    /* Activate hardware accelerated blink 激活硬件加速的闪烁*/
    int        (*blink_set)(struct led_classdev *led_cdev,
                     unsigned long *delay_on,
                     unsigned long *delay_off);

    struct device        *dev;       /**/
    struct list_head     node;            /* LED Device list */
    char        *default_trigger;    /* Trigger to use */

#ifdef CONFIG_LEDS_TRIGGERS
    /* Protects the trigger data below */
    struct rw_semaphore     trigger_lock;

    struct led_trigger    *trigger;
    struct list_head     trig_list;
    void            *trigger_data;
#endif
};

/*-----------------------------------------------------------------
 ------------------------/drivers/leds/Led-gpio.c-------------------
-----------------------------------------------------------------*/
/*
 * LEDs driver for GPIOs
 *
 * Copyright (C) 2007 8D Technologies inc.
 * Raphael Assenat <raph@8d.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>

#include <asm/gpio.h>

struct gpio_led_data {
    struct led_classdev cdev;
    unsigned gpio;
    struct work_struct work;    /*工作队列*/

    u8 new_level;
    u8 can_sleep;
    u8 active_low;  /*以上三个用于LED驱动睡眠*/

    int (*platform_gpio_blink_set)(unsigned gpio,
            unsigned long *delay_on, unsigned long *delay_off);
};

static void gpio_led_work(struct work_struct *work)
{
    struct gpio_led_data    *led_dat =
        container_of(work, struct gpio_led_data, work);

    gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level);
}

static void gpio_led_set(struct led_classdev *led_cdev,
    enum led_brightness value)
{
    struct gpio_led_data *led_dat =
        container_of(led_cdev, struct gpio_led_data, cdev);
    int level;

    if (value == LED_OFF)
        level = 0;
    else
        level = 1;

    if (led_dat->active_low)
        level = !level;

    /* Setting GPIOs with I2C/etc requires a task context, and we don't
     * seem to have a reliable way to know if we're already in one; so
     * let's just assume the worst.
     */
    if (led_dat->can_sleep) {
        led_dat->new_level = level;
        schedule_work(&led_dat->work);
    } else
        gpio_set_value(led_dat->gpio, level);       /*设置IO口电平*/
}

static int gpio_blink_set(struct led_classdev *led_cdev,
    unsigned long *delay_on, unsigned long *delay_off)
{
    struct gpio_led_data *led_dat =
        container_of(led_cdev, struct gpio_led_data, cdev);

    return led_dat->platform_gpio_blink_set(led_dat->gpio, delay_on, delay_off);
}

static int gpio_led_probe(struct platform_device *pdev)
{
    struct gpio_led_platform_data *pdata = pdev->dev.platform_data;  
            /*得到platform_device_pdev结构体里面dev的私有平台指针platform_data*/
    struct gpio_led *cur_led;   /*指向当前gpio_led_platform_data里面的gpio_led*/
    struct gpio_led_data *leds_data, *led_dat;
    /*leds_data用于申请内存空间的指针
    led_dat用于指向当前gpio_led_data内存地址*/
    int i, ret = 0;

    if (!pdata)
        return -EBUSY;

    leds_data = kzalloc(sizeof(struct gpio_led_data) * pdata->num_leds,
                GFP_KERNEL);
              /*申请内存*/
    if (!leds_data)
        return -ENOMEM;

    for (i = 0; i < pdata->num_leds; i++) {
        cur_led = &pdata->leds; /*用于指向当前gpio_led_platform_data里面的gpio_led*/
        led_dat = &leds_data;  /*led_dat用于指向当前gpio_led_data内存地址*/

        /*申请GPIO口,若失败返回错误,实际上这里总会成功*/
        ret = gpio_request(cur_led->gpio, cur_led->name);
        if (ret < 0)
            goto err;

        led_dat->cdev.name = cur_led->name;
        led_dat->cdev.default_trigger = cur_led->default_trigger;
        led_dat->gpio = cur_led->gpio;
        led_dat->can_sleep = gpio_cansleep(cur_led->gpio);
        led_dat->active_low = cur_led->active_low;
        /*把cur_led里面相应的数据赋给led_dat*/

        if (pdata->gpio_blink_set) {
            led_dat->platform_gpio_blink_set = pdata->gpio_blink_set;
            led_dat->cdev.blink_set = gpio_blink_set;
        }    /*如果设置gpio_blink_set,则把相应的指针指向它*/
        led_dat->cdev.brightness_set = gpio_led_set;
        led_dat->cdev.brightness = LED_OFF;
        /*把gpio_led_set指向类结构体led_classdev里的brightness_set*/
        gpio_direction_output(led_dat->gpio, led_dat->active_low);
        /*设置相应的IO口为输出,并指定值*/
        INIT_WORK(&led_dat->work, gpio_led_work); /*准备好gpio_led的原子操作,可以查看workqueue.h*/

        ret = led_classdev_register(&pdev->dev, &led_dat->cdev); /*设备注册*/
        if (ret < 0) {
            gpio_free(led_dat->gpio);
            goto err;
        }
    }

    platform_set_drvdata(pdev, leds_data); /*设为平台的私有数据*/

    return 0;

err:
    if (i > 0) {
        for (i = i - 1; i >= 0; i--) {
            led_classdev_unregister(&leds_data.cdev);
            cancel_work_sync(&leds_data.work);
            gpio_free(leds_data.gpio);
        }
    }

    kfree(leds_data);

    return ret;
}


/**
 * led_classdev_register - register a new object of led_classdev class.
 * @dev: The device to register.
 * @led_cdev: the led_classdev structure for this device.
 */
/* /drivers/leds/Led_class.c  */
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
    int rc;

    led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
                      "%s", led_cdev->name);      /*注册设备,并分配了设备号等*/
    if (IS_ERR(led_cdev->dev))
        return PTR_ERR(led_cdev->dev);

    /* register the attributes */
    rc = device_create_file(led_cdev->dev, &dev_attr_brightness); /*增加属性*/
    if (rc)
        goto err_out;

#ifdef CONFIG_LEDS_TRIGGERS
    init_rwsem(&led_cdev->trigger_lock);
#endif
    /* add to the list of leds */
    down_write(&leds_list_lock);
    list_add_tail(&led_cdev->node, &leds_list);
    up_write(&leds_list_lock);    /*放到LED队列中*/

    led_update_brightness(led_cdev); /*更新led_cdv的属性*/

#ifdef CONFIG_LEDS_TRIGGERS   /*主要增加led_cdev的dev_attr_trigger属性*/
    rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
    if (rc)
        goto err_out_led_list;

    led_trigger_set_default(led_cdev);
#endif

    printk(KERN_INFO "Registered led device: %s\n",
            led_cdev->name);

    return 0;

#ifdef CONFIG_LEDS_TRIGGERS
err_out_led_list:
    device_remove_file(led_cdev->dev, &dev_attr_brightness);
    list_del(&led_cdev->node);
#endif
err_out:
    device_unregister(led_cdev->dev);
    return rc;
}
相关参考:http://blog.csdn.net/yangjun_0621/article/details/6243089
PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
8
关闭 站长推荐上一条 /3 下一条