linux驱动(三):gpiolib

         本文主要探讨使用210的gpiolib库编写led驱动。

gpio.h

#define S5PV210_GPIO_A0_NR (8)
......
#define S5PV210_GPIO_ETC4_NR    (6)

        定义端口的GPIO数目

#define S5PV210_GPIO_NEXT(__gpio) \
    ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)

        定义端口起始GPIO值(gpiochip0,gpiochip9...)

#define S5PV210_GPA0(_nr) (S5PV210_GPIO_A0_START + (_nr))
...
#define S5PV210_ETC4(_nr)    (S5PV210_GPIO_ETC4_START + (_nr))

        定义每个GPIO号码,宏可获取每个GPIO号码,    S5PV210_GPA0(4)为GPA0_4的号码


/sys/class/gpio

        export文件写入端口n号到export将生成gpion目录(用于端口测试)
        unexport文件写入端口n号到export将删除gpion目录
        gpiochipn:
        base为端口号(GPA0为0)
        label端口名称
        ngpio为gpio数目(gpio = base)
        device
        power电源相关
        subsystem GPIO所属的子系统
        uevent GPIO状态改变,通知用户

        gpion:
        active_low(1:GPIO高是逻辑低,0:GPIO高是逻辑高)
        device
        direction(输入:in, 输出:out)
        edge中断触发(none:无,rising:上升沿触发,falling:下降沿触发,both:上升沿和下降沿触发)
        power电源相关
        subsystem GPIO所属的子系统
        uevent GPIO状态改变,通知用户
        value(0、1)

LED内核驱动框架
        drivers/leds为LED硬件驱动目录,led-class.c和led-core.c为内核提供,其他leds-xxx.c为厂商提供,leds-xxx.c调用led-class.c和led-core.c的接口实现led驱动
        leds-s3c24xx.c调用led_classdev_register注册LED驱动,led_classdev_register在drivers/leds/led-class.c定义
        led_classdev_register函数创建leds类设备
        210的led驱动为drivers/char/led/x210-led.c

static int s3c24xx_led_probe(struct platform_device *dev)
{
    ...

    /* register our new led device */

    ret = led_classdev_register(&dev->dev, &led->cdev);
    if (ret < 0) {
        dev_err(&dev->dev, "led_classdev_register failed\n");
        kfree(led);
        return ret;
    }
}
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
    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);

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

    if (!led_cdev->max_brightness)
        led_cdev->max_brightness = LED_FULL;

    led_update_brightness(led_cdev);

#ifdef CONFIG_LEDS_TRIGGERS
    led_trigger_set_default(led_cdev);
#endif

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

    return 0;
}
struct led_classdev {
    const char        *name;
    int             brightness;
    int             max_brightness;
    int             flags;

    /* Lower 16 bits reflect status */
#define LED_SUSPENDED        (1 << 0)
    /* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME    (1 << 16)

    /* 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, delays are in
     * miliseconds and if none is provided then a sensible default
     * should be chosen. The call can adjust the timings if it can't
     * match the values specified exactly. */
    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 */
    const 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
};
//led-class.c
subsys_initcall(leds_init);

//include/linux/init.h
#define subsys_initcall(fn)        __define_initcall("4",fn,4)

//include/linux/init.h
#define __define_initcall(level,fn,id) \
    static initcall_t __initcall_##fn##id __used \
    __attribute__((__section__(".initcall" level ".init"))) = fn

//推导出自定义段
.initcall4.init

//arch/arm/kernel/vmlinux.lds
  __initcall_start = .; *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;

        subsys_initcall宏,定义在linux/init.h中,宏将其声明的函数放到.initcall4.init段
        module_init宏将声明函数放到.initcall6.init段
        内核在启动过程中按照.initcalln.init(n=1..8)的层级依次初始化

led_class_attrs

subsys_initcall(leds_init);


static int __init leds_init(void)
{
    leds_class = class_create(THIS_MODULE, "leds");
    if (IS_ERR(leds_class))
        return PTR_ERR(leds_class);
    leds_class->suspend = led_suspend;
    leds_class->resume = led_resume;
    leds_class->dev_attrs = led_class_attrs;
    return 0;
}

static struct device_attribute led_class_attrs[] = {
    __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
    __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
#ifdef CONFIG_LEDS_TRIGGERS
    __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
#endif
    __ATTR_NULL,
};

        leds_init中创建了leds类(/sys/class/leds),led_class_attrs数组内容为/sys/class/leds/目录里的文件,该文件为内核向应用层提供的操作接口(类似设备文件)
        led_brightness_show函数类似read,led_brightness_store雷日write,他们对应struct led_classdev结构体的读和写方法
        写方法:void (*brightness_set)(struct led_classdev *led_cdev,enum led_brightness brightness);
        读方法:法enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);

gpiolib        

//mach-mdkc110.c
#ifdef CONFIG_MACH_SMDKC110
MACHINE_START(SMDKC110, "SMDKC110")
#elif CONFIG_MACH_SMDKV210
MACHINE_START(SMDKV210, "SMDKV210")
#endif
    /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
    .phys_io    = S3C_PA_UART & 0xfff00000,
    .io_pg_offst    = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
    .boot_params    = S5P_PA_SDRAM + 0x100,
    //.fixup        = smdkv210_fixup,
    .init_irq    = s5pv210_init_irq,
    .map_io        = smdkc110_map_io,
    .init_machine    = smdkc110_machine_init,
    .timer        = &s5p_systimer,
MACHINE_END
static void __init smdkc110_map_io(void)
{
    s5p_init_io(NULL, 0, S5P_VA_CHIPID);
    s3c24xx_init_clocks(24000000);
    s5pv210_gpiolib_init();
    s3c24xx_init_uarts(smdkc110_uartcfgs, ARRAY_SIZE(smdkc110_uartcfgs));
    s5p_reserve_bootmem(smdkc110_media_devs, ARRAY_SIZE(smdkc110_media_devs));
#ifdef CONFIG_MTD_ONENAND
    s5pc110_device_onenand.name = "s5pc110-onenand";
#endif
#ifdef CONFIG_MTD_NAND
    s3c_device_nand.name = "s5pv210-nand";
#endif
    s5p_device_rtc.name = "smdkc110-rtc";
}

初始化gpiolib库

//gpiolib.c
__init int s5pv210_gpiolib_init(void)
{
    struct s3c_gpio_chip *chip = s5pv210_gpio_4bit;
    int nr_chips = ARRAY_SIZE(s5pv210_gpio_4bit);
    int i = 0;

    for (i = 0; i < nr_chips; i++, chip++) {
        if (chip->config == NULL)
            chip->config = &gpio_cfg;
        if (chip->base == NULL)
            chip->base = S5PV210_BANK_BASE(i);
    }

    samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips);

    return 0;
}
struct s3c_gpio_chip {
    struct gpio_chip    chip;        
    struct s3c_gpio_cfg    *config;
    struct s3c_gpio_pm    *pm;
    void __iomem        *base;
    int            eint_offset;
    spinlock_t         lock;
#ifdef CONFIG_PM
    u32            pm_save[7];
#endif
};
struct gpio_chip {
    const char        *label;
    struct device        *dev;
    struct module        *owner;

    int            (*request)(struct gpio_chip *chip,
                        unsigned offset);
    void            (*free)(struct gpio_chip *chip,
                        unsigned offset);

    int            (*direction_input)(struct gpio_chip *chip,
                        unsigned offset);
    int            (*get)(struct gpio_chip *chip,
                        unsigned offset);
    int            (*direction_output)(struct gpio_chip *chip,
                        unsigned offset, int value);
    int            (*set_debounce)(struct gpio_chip *chip,
                        unsigned offset, unsigned debounce);

    void            (*set)(struct gpio_chip *chip,
                        unsigned offset, int value);

    int            (*to_irq)(struct gpio_chip *chip,
                        unsigned offset);

    void            (*dbg_show)(struct seq_file *s,
                        struct gpio_chip *chip);
    int            base;
    u16            ngpio;
    const char        *const *names;
    unsigned        can_sleep:1;
    unsigned        exported:1;
};
static struct s3c_gpio_chip s5pv210_gpio_4bit[] = {
    {
        .chip    = {
            .base    = S5PV210_GPA0(0),
            .ngpio    = S5PV210_GPIO_A0_NR,
            .label    = "GPA0",
            .to_irq = s5p_gpiolib_gpioint_to_irq,
        },
    }, 
    {
        .chip    = {
            .base    = S5PV210_GPA1(0),
            .ngpio    = S5PV210_GPIO_A1_NR,
            .label    = "GPA1",
            .to_irq = s5p_gpiolib_gpioint_to_irq,
        },
    }
    ......
    }
 struct s3c_gpio_cfg {
    unsigned int    cfg_eint;

    s3c_gpio_pull_t    (*get_pull)(struct s3c_gpio_chip *chip, unsigned offs);
    int        (*set_pull)(struct s3c_gpio_chip *chip, unsigned offs,
                    s3c_gpio_pull_t pull);

    int        (*set_pin)(struct s3c_gpio_chip *chip, unsigned offs,
                    s3c_gpio_pull_t level);

    unsigned (*get_config)(struct s3c_gpio_chip *chip, unsigned offs);
    int     (*set_config)(struct s3c_gpio_chip *chip, unsigned offs,
                   unsigned config);
};
void __init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip,
                       int nr_chips)
{
    for (; nr_chips > 0; nr_chips--, chip++) {
        samsung_gpiolib_add_4bit(chip);
        s3c_gpiolib_add(chip);
    }
}

        struct gpio_chip    chip;单口各模式定义(输入,输出,设置,读取)
        int            base;端口的基址编号
        u16            ngpio;io口数量
        const char        *const *names;端口名
        int            (*to_irq)(struct gpio_chip *chip;基于io口计算中号方法
        s5pv210_gpio_4bit为是s3c_gpio_chip结构体数组,用于gpiolib定义注册端口(未定义注册模式及模式操作方法)
        struct s3c_gpio_cfg    *config;端口上下拉等操作定义
        void __iomem        *base;端口寄存器虚拟地址基地址(JPA0端口地址,端口包含多个io口)
        samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips);定义注册端口模式及模式操作方法和端口注册
        samsung_gpiolib_add_4bit(chip);定义注册端口模式及模式操作方法
        s3c_gpiolib_add(chip);端口注册(调用gpiochip_add函数将GPIO端口信息chip结构体链接gpiolib定义的gpio_desc数组)

int gpiochip_add(struct gpio_chip *chip)
{
    ......
    /* these GPIO numbers must not be managed by another gpio_chip */
    for (id = base; id < base + chip->ngpio; id++) {
        if (gpio_desc[id].chip != NULL) {
            status = -EBUSY;
            break;
        }
    }
    .....
}


gpiolib通用函数(drivers/gpio/gpiolib.c)

int gpiochip_add(struct gpio_chip *chip);
注册gpiolib

int __must_check gpiochip_remove(struct gpio_chip *chip);
清除注册

struct gpio {
    unsigned    gpio;
    unsigned long    flags;
    const char    *label;
};


flag(gpio.h):
#define GPIOF_DIR_OUT    (0 << 0)
#define GPIOF_DIR_IN    (1 << 0)
#define GPIOF_INIT_LOW    (0 << 1)
#define GPIOF_INIT_HIGH    (1 << 1)
#define GPIOF_IN        (GPIOF_DIR_IN)
#define GPIOF_OUT_INIT_LOW    (GPIOF_DIR_OUT | GPIOF_INIT_LOW)
#define GPIOF_OUT_INIT_HIGH    (GPIOF_DIR_OUT | GPIOF_INIT_HIGH)


GPIOF_DIR_IN                  输入
GPIOF_DIR_OUT                  输出
GPIOF_INIT_LOW               输出低电平
GPIOF_INIT_HIGH               输出高电平
GPIOF_IN                              输入
GPIOF_OUT_INIT_LOW         输出低电平
GPIOF_OUT_INIT_HIGH        输出高电平

int gpio_request(unsigned gpio, const char *label)
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
int gpio_request_array(struct gpio *array, size_t num);
申请gpio口

int gpio_is_valid(int number)
检查GPIO是否有效

int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
设置GPIO为输入或输出模式

int __gpio_get_value(unsigned gpio);
读取gpio

void __gpio_set_value(unsigned gpio, int value);
设置GPIO

void gpio_free(unsigned gpio);                
void gpio_free_array(struct gpio *array, size_t num);    
释放申请的gpio

demo: 

        使用gpiolib编写led驱动

Makefile

# LED Platform Drivers

obj-$(CONFIG_LEDS_CXB_210)             += led-cxb_210.o

Kconfig 

if LEDS_CLASS

comment "LED drivers"

config LEDS_CXB_210
        tristate "LED Support for x210 led "
        help
          x210 led form cxb.

menuconfig 

Device Drivers  ---> 
    [*] LED Support  --->
        --- LED Support                                                                                                                      <*>     LED Class Support                                                                                  
        *** LED drivers ***                                                                            
<*>     LED Support for x210 led 

 led-cxb_210.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/leds.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <mach/gpio.h>


#define LED1 S5PV210_GPJ0(3)
#define LED2 S5PV210_GPJ0(4)
#define LED3 S5PV210_GPJ0(5)

#define LED1_GPIO "led1_gpio3"
#define LED2_GPIO "led2_gpio4"
#define LED3_GPIO "led3_gpio5"

#define LED1_NAME "led1"
#define LED2_NAME "led2"
#define LED3_NAME "led3"

static struct led_classdev s5pv210_led1;
static struct led_classdev s5pv210_led2;
static struct led_classdev s5pv210_led3;

#define LED_OFF 1
#define LED_ON  0


struct gpio led[] = {
        {S5PV210_GPJ0(3),GPIOF_OUT_INIT_HIGH,LED1_GPIO},
        {S5PV210_GPJ0(4),GPIOF_OUT_INIT_HIGH,LED2_GPIO},
        {S5PV210_GPJ0(5),GPIOF_OUT_INIT_HIGH,LED3_GPIO},
};


static void s5pv210_led1_set(struct led_classdev *led_cdev,enum led_brightness value)
{
        if(value == LED_OFF)
        {
                gpio_set_value(LED1,LED_OFF);
        }
        else
        {
                gpio_set_value(LED1,LED_ON);
        }
}


static void s5pv210_led2_set(struct led_classdev *led_cdev,enum led_brightness value)
{
        if(value == LED_OFF)
        {
                gpio_set_value(LED1,LED_OFF);
        }
        else
        {
                gpio_set_value(LED1,LED_ON);
        }
}

static void s5pv210_led3_set(struct led_classdev *led_cdev,enum led_brightness value)
{
        if(value == LED_OFF)
        {
                gpio_set_value(LED1,LED_OFF);
        }
        else
        {
                gpio_set_value(LED1,LED_ON);
        }
}


static int __init led_dev_init(void)
{
        int ret = -1;

        s5pv210_led1.name = LED1_NAME;
        s5pv210_led1.brightness = 0;
        s5pv210_led1.brightness_set = s5pv210_led1_set;
        ret = led_classdev_register(NULL, &s5pv210_led1);
        if(ret < 0)
        {
                printk(KERN_INFO "210_led1 register fail\n");
                goto flag_1;
        }

        s5pv210_led2.name = LED2_NAME;
        s5pv210_led2.brightness = 0;
        s5pv210_led2.brightness_set = s5pv210_led2_set;
        ret = led_classdev_register(NULL, &s5pv210_led2);
        if(ret < 0)
        {
                printk(KERN_INFO "210_led2 register fail\n");
                goto flag_2;
        }

        s5pv210_led3.name = LED3_NAME;
        s5pv210_led3.brightness = 0;
        s5pv210_led3.brightness_set = s5pv210_led3_set;
        ret = led_classdev_register(NULL, &s5pv210_led3);
        if(ret < 0)
        {
                printk(KERN_INFO "210_led3 register fail\n");
                goto flag_3;
        }
        ret = gpio_request_array(led, ARRAY_SIZE(led));
        if(ret < 0)
        {
                printk(KERN_INFO "request gpio fail\n");
                goto flag_4;
        }

        return ret;

flag_4:
        gpio_free_array(led,ARRAY_SIZE(led));
flag_3:
        led_classdev_unregister(&s5pv210_led3);
flag_2:
        led_classdev_unregister(&s5pv210_led2);
flag_1:
        led_classdev_unregister(&s5pv210_led1);
        return ret;

}

static void __exit led_dev_exit(void)
{
        led_classdev_unregister(&s5pv210_led1);
        led_classdev_unregister(&s5pv210_led2);
        led_classdev_unregister(&s5pv210_led2);

        gpio_free_array(led,ARRAY_SIZE(led));
}


module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cxb");
MODULE_DESCRIPTION("led gpio  module");
MODULE_ALIAS("led");

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值