linux驱动(三):gpiolib

发布时间:2024年01月07日

? ? ? ? ?本文主要探讨使用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");

文章来源:https://blog.csdn.net/tyro_wzp/article/details/135441892
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。