(安卓)linux常用驱动

本文介绍了几种不同的GPIO控制方法,包括直接通过内核文件操作控制输出状态、获取GPIO状态并返回不同字段,以及基于设备树的GPIO驱动实现。每种方法都提供了详细的代码示例。

Kernel: 3.10.49

平台:RK3399

要求:提供一个节点,可以控制GPIO的输出状态。


RK平台GPIO号的计算如:

gpio1_A7 :1x32+0x8+7=39
gpio0_B5 : 0x32+1x8+5=13
gpio3_C3 : 3x32+2x8+3=115
gpio_direction_output(115,1);

MTKGPIO号的计算

gpio31::31+902 = 933
gpio_direction_output(933,1);

方法一:(在sys/class/目录下生成节点,在对节点写入对应的字符,驱动力再进行相关的操作,注意函数:gpio1a7_store


下面代码实现了创建节点sys/class/dev_class/gpio1a7

当执行echo 0 >  sys/class/dev_class/gpio1a7GPIO1_A7  拉低

当执行echo 1 >   sys/class/dev_class/gpio1a7GPIO1_A7  拉高

(代码适用所有内核)      

#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/device.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <asm/uaccess.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/hrtimer.h>
#include <linux/platform_device.h>
#include <linux/kobject.h>
#include<linux/timer.h>
#include <linux/hrtimer.h>

static unsigned int dev_major=100;
static dev_t devno;
static struct class  *dev_class=NULL;
static struct cdev   *dev_cdev = NULL;

static long ben_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	long ret = 0;        
	return ret;
}
static int dev_open(struct inode *inode, struct file *file)
{
	printk(KERN_INFO"%s()-%d\n", __func__, __LINE__);
	return 0;
}
static ssize_t dev_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
	printk(KERN_INFO"%s()-%d\n", __func__, __LINE__);
	return count;
}

static	ssize_t dev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
	printk(KERN_INFO"%s()-%d\n", __func__, __LINE__);
	return count;
}

static ssize_t gpio1a7_store(struct class *cls,struct class_attribute *attr, const char __user *buf,size_t count)
{	
	int ret = -1;
	if(!strncmp(buf,"1",strlen("1")))//
	{ 
		gpio_free(39);
		ret = gpio_request(39, NULL);
		gpio_direction_output(39,1);
		printk("-------nbpt----the gpio1_a7 id high---- ret = %d--------------\n",gpio_get_value(39));
	
		return count;

	}
	else if(!strncmp(buf,"0",strlen("0")))//
	{

		gpio_free(39);
		ret = gpio_request(39, NULL);
		gpio_direction_output(39,0);
		printk("-------nbpt----the gpio1_a7 id high---- ret = %d--------------\n",gpio_get_value(39));
		return count;

	}

	return 0;
}

static CLASS_ATTR(gpio1a7, 0655, NULL, gpio1a7_store);

static struct file_operations dev_fops={
	.owner	= THIS_MODULE,
	.open	= dev_open,
	.read	= dev_read,
	.unlocked_ioctl   = ben_ioctl,      //
	.write	= dev_write,
};
static int __init dev_init(void)
{	
	int ret,ret1;
	devno = MKDEV(dev_major,0);
	printk("-----nbpt------dev_init! \n");
	ret = register_chrdev_region(devno,1,"xxx");
	if (ret < 0) {
		printk(KERN_ERR "unable to get major \n");
		return -EINVAL;
	}
		
	gpio_free(39);
	ret = gpio_request(39, NULL);
	gpio_direction_output(39,1);

	dev_cdev=cdev_alloc();

	cdev_init(dev_cdev,&dev_fops);

	cdev_add(dev_cdev,devno,1);

	dev_class=class_create(THIS_MODULE,"dev_class");
	if (IS_ERR(dev_class)) {
		printk(KERN_ERR "class_create() failed for dev_class\n");
		ret = -EFAULT;
		goto out_err_1;
	}	

	ret1 = class_create_file(dev_class,&class_attr_gpio1a7);
	if(ret1 <0){
		goto out_err_2;
	}

	return 0;

out_err_2:
	class_destroy(dev_class);
out_err_1:
	unregister_chrdev_region(MKDEV(dev_major,0),1);
	cdev_del(dev_cdev);
	return ret;


}
static void __exit dev_exit(void)
{


	unregister_chrdev_region(MKDEV(dev_major,0),1);
	device_destroy(dev_class,MKDEV(dev_major,0));

	class_destroy(dev_class);
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");

2.获取GPIO的状态,返回不同字段。

生成dev/cdevdemo节点。cat  dev/cdevdemo 会返回不同字段。来判断gpio的状态。

#include <linux/module.h>  
#include <linux/types.h>  
#include <linux/fs.h>  
#include <linux/errno.h>  
#include <linux/mm.h>  
#include <linux/sched.h>  
#include <linux/init.h>  
#include <linux/cdev.h>  
#include <asm/io.h>  
#include <asm/uaccess.h>  
#include <linux/timer.h>  
#include <asm/atomic.h>  
#include <linux/slab.h>  
#include <linux/device.h> 

#include <linux/of_gpio.h>
#include <linux/gpio.h>
#define CDEVDEMO_MAJOR 255  /*预设cdevdemo的主设备号*/  
  
static int cdevdemo_major = CDEVDEMO_MAJOR;
static int flag;
  
struct cdevdemo_dev   
{  
	    struct cdev cdev;  
};  
  
struct cdevdemo_dev *cdevdemo_devp; /*设备结构体指针*/  
  
int cdevdemo_open(struct inode *inode, struct file *filp)     
{  
	    flag = 1;
	    printk(KERN_NOTICE "======== cdevdemo_open ");  
	        return 0;  
} 

int cdevdemo_release(struct inode *inode, struct file *filp)      
{  
	    printk(KERN_NOTICE "======== cdevdemo_release ");     
	        return 0;  
}  

#define BUF_LEN 50
static char kbuf[BUF_LEN];

static ssize_t cdevdemo_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos)  
{  
	        int ret;
		
	    	if(flag == 0)
		return 0;
		
		gpio_free(902+52);
		gpio_request(902+52,NULL);
		gpio_direction_input(902+52);
		ret = gpio_get_value(902+52);
		
		if (ret == 1){ 
	    	        strcpy(kbuf, "hdmi state is 1");
		}else {
			strcpy(kbuf, "");
		}
			
			if (count < 0)
				return -EINVAL;
			if (count > strlen(kbuf))
			count = (strlen(kbuf));
			ret = copy_to_user(ubuf, kbuf, count); //拷贝给用户 //copy_from_user(to, from, size);
			if (ret) {
			printk(KERN_WARNING "copy_to_user failed! \n");
			return ret;
			}
			flag = 0;
	    return (ssize_t)count;
}  
  
static const struct file_operations cdevdemo_fops =  
{  
	    .owner = THIS_MODULE,  
	    .open = cdevdemo_open,  
		.release = cdevdemo_release,  
		.read = cdevdemo_read,  
};

static void cdevdemo_setup_cdev(struct cdevdemo_dev *dev, int index)  
{  
	        int err, devno = MKDEV(cdevdemo_major, index);
 
		    cdev_init(&dev->cdev, &cdevdemo_fops);  
			dev->cdev.owner = THIS_MODULE;  
		    dev->cdev.ops = &cdevdemo_fops;  
		    err = cdev_add(&dev->cdev, devno, 1);  
 
	        if(err) 
	     	{  
			printk(KERN_NOTICE "Error %d add cdevdemo %d", err, index);   
			}  
}

int cdevdemo_init(void)  
{  
	    int ret;  
		struct class *cdevdemo_class;  
		dev_t devno = MKDEV(cdevdemo_major, 0);  
		      
	    printk(KERN_NOTICE "======== cdevdemo_init ");    
			    /*申请设备号,如果申请失败采用动态申请方式*/  
		if(cdevdemo_major)  
		{  
		ret = register_chrdev_region(devno, 1, "cdevdemo");  
		}else{  
		ret = alloc_chrdev_region(&devno,0,1,"cdevdemo");  
		cdevdemo_major = MAJOR(devno);  
		}	

		if(ret < 0)  
	    {  
		return ret;  
		}  
    		/*动态申请设备结构体内存*/  
    	cdevdemo_devp = kmalloc(sizeof(struct cdevdemo_dev), GFP_KERNEL);  
        if(!cdevdemo_devp)  /*申请失败*/  
	    {	  
		ret = -ENOMEM;  
		printk(KERN_NOTICE "Error add cdevdemo");     
		goto fail_malloc;  
		}

		memset(cdevdemo_devp,0,sizeof(struct cdevdemo_dev));  
        cdevdemo_setup_cdev(cdevdemo_devp, 0);  
	  
	    	/*下面两行是创建了一个总线类型,会在/sys/class下生成cdevdemo目录 
	     	*       这里的还有一个主要作用是执行device_create后会在/dev/下自动生成 
	     	*             cdevdemo设备节点。而如果不调用此函数,如果想通过设备节点访问设备 
	     	*                   需要手动mknod来创建设备节点后再访问。*/  
	    cdevdemo_class = class_create(THIS_MODULE, "cdevdemo");  
	    device_create(cdevdemo_class, NULL, MKDEV(cdevdemo_major, 0), NULL, "cdevdemo");  
		return 0;  
			  
fail_malloc:  
	unregister_chrdev_region(devno,1);
      	return 0;	
}

void cdevdemo_exit(void)    /*模块卸载*/  
{  
	    printk(KERN_NOTICE "End cdevdemo");   
	    cdev_del(&cdevdemo_devp->cdev);  /*注销cdev*/  
	    kfree(cdevdemo_devp);       /*释放设备结构体内存*/  
	    unregister_chrdev_region(MKDEV(cdevdemo_major,0),1);    //释放设备号  
}  
  
MODULE_LICENSE("Dual BSD/GPL");  
module_param(cdevdemo_major, int, S_IRUGO);  
module_init(cdevdemo_init);  
module_exit(cdevdemo_exit); 

3.基于设备树的GPIO驱动(通过系统节点控制)

节点gpio_22可以进行cat和echo操作。






1、dts文件
    gpio_ldo_power {
        compatible = "xcz,gpio_ldo_power";
        qcom,gpio_ldo_pin = <&msm_gpio 22 0>;
    };

2、驱动
比较简单,直接看源码
#include <linux/types.h>
#include <linux/pm.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
#include <asm/setup.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/stat.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/spinlock.h>
#include <linux/err.h>
#include <linux/regulator/consumer.h>

int gpio_ldo_pin = -1;
int gpio_flag = -1;
static struct class *gpio_ldo_power_class = NULL;
static struct device *gpio_ldo_power_dev = NULL;

#define CTL_POWER_ON    "1"
#define CTL_POWER_OFF   "0"

static ssize_t gpio_22_show(struct device *dev,
        struct device_attribute *attr, char *buf)
{
    printk("%s\n", __func__);
    sprintf(buf, "gpio_22 is %d\n", gpio_flag);
    return strlen(buf);
}

static ssize_t gpio_22_store(struct device *dev,
        struct device_attribute *attr, const char *buf,
        size_t count)
{
    if(!strncmp(buf, CTL_POWER_ON, strlen(CTL_POWER_ON))) {
        printk("%s: to enable gpio_22\n", __func__);
        gpio_set_value(gpio_ldo_pin, 1);
        gpio_flag = 1;

    } else if(!strncmp(buf, CTL_POWER_OFF, strlen(CTL_POWER_OFF))) {
        printk("%s: to disable gpio_22\n", __func__);
        gpio_set_value(gpio_ldo_pin, 0);
        gpio_flag = 0;
    }

    return count;
}

static struct device_attribute gpio_22_dev_attr = {
    .attr = {
        .name = "gpio_22",
        .mode = S_IRWXU|S_IRWXG|S_IRWXO,
    },
    .show = gpio_22_show,
    .store = gpio_22_store,
};



static int gpio_ldo_power_probe(struct platform_device *pdev)
{
    int ret = 0;

    printk("xcz enter gpio_ldo_power_probe \n");

    gpio_ldo_pin = of_get_named_gpio(pdev->dev.of_node, "qcom,gpio_ldo_pin", 0);
    if (gpio_ldo_pin < 0)
        printk("xcz gpio_ldo_pin is not available \n");

    ret = gpio_request(gpio_ldo_pin, "gpio_ldo_pin");
    if(0 != ret) {
        printk("xcz gpio request %d failed.", gpio_ldo_pin);
        goto fail1;
    }
    
    gpio_direction_output(gpio_ldo_pin, 0);

    gpio_set_value(gpio_ldo_pin, 0);
    gpio_flag = 0;

    gpio_ldo_power_class = class_create(THIS_MODULE, "gpio_ldo_power");
    if(IS_ERR(gpio_ldo_power_class))
    {
        ret = PTR_ERR(gpio_ldo_power_class);
        printk("Failed to create class.\n");
        return ret;
    }

    gpio_ldo_power_dev = device_create(gpio_ldo_power_class, NULL, 0, NULL, "gpio_gpio_22");
    if (IS_ERR(gpio_ldo_power_dev))
    {
        ret = PTR_ERR(gpio_ldo_power_class);
        printk("Failed to create device(gpio_ldo_power_dev)!\n");
        return ret;
    }

    ret = device_create_file(gpio_ldo_power_dev, &gpio_22_dev_attr);
    if(ret)
    {
        pr_err("%s: gpio_22 creat sysfs failed\n",__func__);
        return ret;
    }

    printk("xcz enter gpio_ldo_power_probe, ok \n");

fail1:
    return ret;
}

static int gpio_ldo_power_remove(struct platform_device *pdev)
{
    device_destroy(gpio_ldo_power_class, 0);
    class_destroy(gpio_ldo_power_class);
    device_remove_file(gpio_ldo_power_dev, &gpio_22_dev_attr);

    return 0;
}

static int gpio_ldo_power_suspend(struct platform_device *pdev,pm_message_t state)
{
    return 0;
}

static int gpio_ldo_power_resume(struct platform_device *pdev)
{
    return 0;
}

static struct of_device_id gpio_ldo_power_dt_match[] = {
    { .compatible = "xcz,gpio_ldo_power",},
    { },
};
MODULE_DEVICE_TABLE(of, gpio_ldo_power_dt_match);

static struct platform_driver gpio_power_driver = {
    .driver = {
        .name = "gpio_ldo_power",
        .owner = THIS_MODULE,
        .of_match_table = of_match_ptr(gpio_ldo_power_dt_match),
    },
    .probe = gpio_ldo_power_probe,
    .remove = gpio_ldo_power_remove,
    .suspend = gpio_ldo_power_suspend,
    .resume = gpio_ldo_power_resume,
};

static __init int gpio_power_init(void)
{
    return platform_driver_register(&gpio_power_driver);
}

static void __exit gpio_power_exit(void)
{
    platform_driver_unregister(&gpio_power_driver);
}

module_init(gpio_power_init);
module_exit(gpio_power_exit);
MODULE_AUTHOR("GPIO_LDO_POWER, Inc.");
MODULE_DESCRIPTION("XCZ GPIO_LDO_POWER");
MODULE_LICENSE("GPL");


4. Sample kobject implementation:

    linux内核学习笔记之设备与模块 - 优快云博客:
    https://blog.youkuaiyun.com/brand_j/article/details/79844766



Linux常见驱动源码分析(kernel hacker修炼之道)--李万鹏 李万鹏 IBM Linux Technology Center kernel team 驱动资料清单内容如下: Linux设备模型(中)之上层容器.pdf Linux设备模型(上)之底层模型.pdf Linux驱动修炼之道-驱动中一些常见的宏.pdf Linux驱动修炼之道-内存映射.pdf Linux驱动修炼之道-看门狗框架源码分析.pdf Linux驱动修炼之道-触摸屏驱动之s3c2410_ts源码分析.pdf Linux驱动修炼之道-SPI驱动框架源码分析().pdf Linux驱动修炼之道-SPI驱动框架源码分析().pdf Linux驱动修炼之道-SPI驱动框架源码分析().pdf Linux驱动修炼之道-RTC子系统框架与源码分析.pdf Linux驱动修炼之道-platform.pdf Linux驱动修炼之道-LCD背光与gpio控制.pdf Linux驱动修炼之道-INPUT子系统().pdf Linux驱动修炼之道-INPUT子系统().pdf Linux驱动修炼之道-framebuffer(中).pdf Linux驱动修炼之道-framebuffer(下).pdf Linux驱动修炼之道-framebuffer(上).pdf Linux驱动修炼之道-DMA框架源码分析().pdf Linux驱动修炼之道-DMA框架源码分析().pdf Linux驱动修炼之道-DM9000A网卡驱动框架源码分析().pdf Linux驱动修炼之道-DM9000A网卡驱动框架源码分析().pdf Linux驱动修炼之道-DM9000A网卡驱动框架源码分析().pdf Linux驱动修炼之道-clock框架.pdf Linux驱动修炼之道-ADC驱动.pdf Linux内核访问外设I O资源的方式.pdf LINUX内核USB子系统学习笔记之初识USB.pdf kernel hacker修炼之道之驱动-流水灯.pdf kernel hacker修炼之道之驱动-混杂设备.pdf kernel hacker修炼之道之驱动-按键.pdf kernel hacker修炼之道之PCI subsystem().pdf kernel hacker修炼之道之PCI subsystem().pdf kernel hacker修炼之道之PCI subsystem().pdf kernel hacker修炼之道之PCI subsystem().pdf kernel hacker修炼之道之PCI subsystem().pdf
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kevin@1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值