字符设备驱动-通过GPIO子系统提供的API实现LED驱动

前言

写文章的目的是想通过记录自己的学习过程,以便以后使用到相关的知识点可以回顾和参考。

一、GPIO子系统提供的API

gpio 子系统提供了 API 函数来操作指定的 GPIO,gpio 子系统向驱动开发人员屏蔽了具体的读写寄存器过程。这就是驱动分层与分离的好处,大家各司其职,做好自己的本职工作即可。gpio 子系统提供的常用的 API 函数有下面几个:

1、gpio_request 函数
gpio_request 函数用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用 gpio_request 进行申请,函数原型如下:
int gpio_request(unsigned gpio, const char label)
函数参数和返回值含义如下:
gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信息,此函数会返回这个 GPIO 的标号。
label:给 gpio 设置个名字。 返回值:0,申请成功;其他值,申请失败。
——————————————–——————————————————————
2、gpio_free 函数
如果不使用某个 GPIO 了,那么就可以调用 gpio_free 函数进行释放。函数原型如下:
void gpio_free(unsigned gpio)
函数参数和返回值含义如下:
gpio:要释放的 gpio 标号。
返回值:无。
——————————————–——————————————————————
3、gpio_direction_input 函数
此函数用于设置某个 GPIO 为输入,函数原型如下所示:
int gpio_direction_input(unsigned gpio)
函数参数和返回值含义如下:
gpio:要设置为输入的 GPIO 标号。
返回值:0,设置成功;负值,设置失败。
——————————————–——————————————————————
4、gpio_direction_output 函数
此函数用于设置某个 GPIO 为输出,并且设置默认输出值,函数原型如下:
int gpio_direction_output(unsigned gpio, int value)
函数参数和返回值含义如下:
gpio:要设置为输出的 GPIO 标号。
value:GPIO 默认输出值。
返回值:0,设置成功;负值,设置失败。
——————————————–——————————————————————
5、gpio_get_value 函数
此函数用于获取某个 GPIO 的值(0 或 1),此函数是个宏,定义所示:
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)

函数参数和返回值含义如下:
gpio:要获取的 GPIO 标号。
返回值:非负值,得到的 GPIO 值;负值,获取失败。
——————————————–——————————————————————
6、gpio_set_value 函数
此函数用于设置某个 GPIO 的值,此函数是个宏,定义如下
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)

函数参数和返回值含义如下:
gpio:要设置的 GPIO 标号。
value:要设置的值。
返回值:无
关于 gpio 子系统常用的 API 函数就讲这些,这些是我们用的最多的。

二、驱动程序

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h> 
#include <linux/gpio.h> 
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <cfg_type.h>

#define CHRLED_CNT     1        /* 设备号个数 */
#define CHRLED_NAME    "led"    /* 名字 */
#define LEDOFF      0   /* 关灯 */
#define LEDON       1   /* 开灯 */

/* 定义一个设备信息的结构体 */
struct dev_str{
    dev_t devid;                    /* 设备号 */
    struct cdev led_cdev;           /* cdev */
    struct class *led_class;        /* 类 */
    struct device *led_device;      /* 设备 */
    int major;                      /* 主设备号 */
    int minor;                      /* 次设备号 */
};

struct dev_str led_dev;


/* 定义一个结构体存放gpio信息 */
struct led_gpio_info{
    unsigned int gpio;          /* GPIO标号 */
    const char *name;           /* 名字 */
};

static const struct led_gpio_info led_gpio_info_tbl[4]={
    {
        .gpio = PAD_GPIO_E+13,  /* 标号的形式 */
        .name = "gpioe13",
    },
    {
        .gpio = PAD_GPIO_C+17,
        .name = "gpioc17",
    },
    {
        .gpio = PAD_GPIO_C+8,
        .name = "gpioc8",
    },
    {
        .gpio = PAD_GPIO_C+7,
        .name = "gpioc7",
    },
};


static int led_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &led_dev;  /* 设置私有数据 */
    //printk(KERN_EMERG "kernel open file ok!\r\n");
    return 0;
}

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int retvalue;
    unsigned char databuf[2];
    unsigned char ledstat;
    unsigned char lednum;

    retvalue = copy_from_user(databuf, buf, cnt);
    if(retvalue < 0)
    {
        printk(KERN_EMERG "kernel write failed!\r\n");
        return -EFAULT;
    }

    lednum = databuf[0];
    ledstat = databuf[1];
    if(lednum >= 4){
        printk(KERN_EMERG "kernel: lednum don't specification!\r\n");
        return -EFAULT;
    }

    if(ledstat == LEDON){
        gpio_set_value(led_gpio_info_tbl[lednum].gpio, 0);
    }else if(ledstat == LEDOFF){
        gpio_set_value(led_gpio_info_tbl[lednum].gpio, 1);
    }

    return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{
    printk(KERN_EMERG "kernel close file ok!\r\n");
    return 0;
}

static struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .read = led_read,
    .write = led_write,
    .release = led_release,
};

/* 驱动入口函数 */
static int __init led_init(void)
{
    int retvalue;
    u32 i = 0;

    /* 初始化 LED */
    /* 申请GPIO */
    retvalue = gpio_request(led_gpio_info_tbl[i].gpio, led_gpio_info_tbl[i].name);
    if(retvalue < 0){
        printk(KERN_EMERG "kernel gpio_request failed!\r\n");
        return -EIO;
    }

    /* 设置4个GPIO为输出模式, 默认关闭LED*/
    for(i=0;i<4;i++){
        gpio_direction_output(led_gpio_info_tbl[i].gpio, 1);
    }

    /* 申请设备号 */
    if(led_dev.major){
        /* 如果自定义了设备号 */
        led_dev.devid = MKDEV(led_dev.major, 0); 
        retvalue = register_chrdev_region(led_dev.devid, CHRLED_CNT, CHRLED_NAME);
    }else{
        /* 否则,动态申请设备号 */
        retvalue = alloc_chrdev_region(&led_dev.devid, 0, CHRLED_CNT, CHRLED_NAME);
        led_dev.major = MAJOR(led_dev.devid);
        led_dev.minor = MINOR(led_dev.devid);
    }

    printk(KERN_EMERG "led_major = %d\r\n",led_dev.major);
	printk(KERN_EMERG "led_minor = %d\r\n",led_dev.minor);
    
    if(retvalue < 0){
        printk(KERN_EMERG "kernel register chrdev failed!\r\n");
        return -EIO;
    }

    /* 字符设备初始化 */
    led_dev.led_cdev.owner = THIS_MODULE;
    cdev_init(&led_dev.led_cdev, &led_fops);

    /* 把字符设备添加到内核 */
    cdev_add(&led_dev.led_cdev, led_dev.devid, CHRLED_CNT);


    /* 自动创建设备节点文件 */
    /* 创建类 */
    led_dev.led_class = class_create(THIS_MODULE, CHRLED_NAME);

    /* 创建设备 */
    led_dev.led_device = device_create(led_dev.led_class, NULL, led_dev.devid, NULL, CHRLED_NAME);

    return 0;
}

/* 驱动出口函数 */
static void __exit led_exit(void)
{
    
    int i;
	/* 释放GPIO */
    for(i=0; i<4; i++){
		gpio_free(led_gpio_info_tbl[i].gpio);
	}

    /* 删除设备 */
    device_destroy(led_dev.led_class, led_dev.devid);

    /* 删除类 */
    class_destroy(led_dev.led_class);

    /* 把字符设备从内核中删除 */
    cdev_del(&led_dev.led_cdev);

    /* 注销字符设备驱动 */
    unregister_chrdev_region(led_dev.devid, CHRLED_CNT);
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xzj");

三、应用程序

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

#define LEDOFF 0
#define LEDON 1

int main(int argc , char *argv[])
{
    int fd, retvalue;
    char *filename;
    unsigned char databuf[2];

    if(argc != 4){
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    /* 打开驱动文件 */
    fd = open(filename, O_RDWR);
    if(fd < 0){
        printf("Can't open file %s\r\n", filename);
        return -1;
    }

    databuf[0] = atoi(argv[2]);
    databuf[1] = atoi(argv[3]);
    /* 向设备驱动写数据 */
    retvalue = write(fd, databuf, sizeof(databuf));
    if(retvalue < 0){
        printf("LED Control failed!\r\n");
        close(fd);
        return -1;
    }

    /* 关闭设备 */
    retvalue = close(fd);
    if(retvalue < 0){
        printf("Can't close file %s\r\n", filename);
        return -1;
    }

    return 0;
}

四、测试

在开发板终端输入如下命令:

cd /lib/modules/3.4.39
insmod led.ko
./ledApp /dev/led 1 1
执行应用程序时需要输入4个参数,第3个参数为选择哪个led,第4个参数为点亮/关闭。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值