gpio驱动

  通过gpiolib标准库,实现gpio功能,如读写,中断。gpio产生中断后,kernel向进程发送一个SIGUSR1信号。可以供大家参考。

my_gpio.h

#define QL_RET_ERR_GPIO_UNREG		-11	/* pin not register or not register for GPIO */

#define QL_RET_ERR_KERNEL		-100      /* there are some error occured in kernel */
#define QL_RET_ERR_KERNEL_NOMEM		-101	/* no memory in kernel */
#define QL_RET_ERR_NOIOCTLCMD		-515	/* no ioctl command */

/*
* checek gpio reuslt Macro
*/

#define GPIO_INVAILID	0x01
#define GPIO_INUSE	0x02
#define	GPIO_UNUSE	0x03

/*
* gpio direction Macro
*/

#define GPIO_DIR_OUT	1
#define GPIO_DIR_IN	0


typedef struct {
	unsigned int gpio;	// gpio number
	pid_t	userpid;	// user process id
}quectel_gpio_reg_info;


typedef struct {
	unsigned int gpio;	// gpio number
	unsigned int dir;	// 1:out  0:in
	unsigned int value;	// pin level
}quectel_gpio_config;

struct list_irq_info_node
{
	pid_t userpid;
	unsigned int gpio;
	int irq;
	unsigned dir;
	struct list_head list;
};

struct key_value
{
	unsigned int key;
	unsigned int value;
};

struct list_irq_info_node infohead;  // for user register int and user pid


#endif


my_gpio.c

/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */
/*
 * My gpio driver
 *
 */
#include <linux/init.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/pid.h>
#include <linux/signal.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <asm/atomic.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/string.h>
//#include <linux/spinlock.h>
//#include <linux/workqueue.h>




#include "my_gpio.h"

#define DEVICE_NAME	"quectel_gpio"

dev_t gpio_dev_num;
struct cdev gpio_dev;
static struct class *gpio_class;

#if 0
static int quectel_gpio_open(struct inode *inode, struct file *file)
{
		
	printk("%s: gpio open\n",__func__);
	try_module_get(THIS_MODULE);
	return 0;
}

static int release()
{
	printk("%s: gpio_release!\n", __func__);
	module_put(THIS_MODULE);
	return 0;
}

#endif


static struct list_irq_info_node * find_gpio_in_list(unsigned gpio)
{
	struct list_irq_info_node *p;
	struct list_head * pos;
	list_for_each(pos, &infohead.list)
	{
		p = list_entry(pos, struct list_irq_info_node, list);
		if(p->gpio == gpio)
		{
			return p;
		}
	}
	return NULL;
}

static pid_t find_pid_by_irq(int irq)
{
	pid_t pid = -1;
	struct list_irq_info_node *p;
	struct list_head * pos;
	list_for_each(pos, &infohead.list)
	{
		p = list_entry(pos, struct list_irq_info_node, list);
		if(p->irq == irq)
		{
			return p->userpid;
		}
	}	
	
	return pid;
}
static int check_gpio(unsigned int gpio)
{
	//int ret = 0;
	if(!gpio_is_valid(gpio))
	{
		printk("%s:%d gpio_is_valid failed!\n", __func__, __LINE__);
		return GPIO_INVAILID;
	}
	if(find_gpio_in_list(gpio) == NULL)
		return GPIO_UNUSE;
	else
		return GPIO_INUSE;
}
static irqreturn_t irqHandler(int irq, void*dev_id)
{
	pid_t pid = -1;
	struct task_struct *p = NULL;
	//gpio = irq_to_gpio(irq);
	pid = find_pid_by_irq(irq);
	printk("%s:%d get a irq=%d  pid=%d\n", __func__, __LINE__, irq, pid);
	if(pid<0)
	{
		printk("%s:%d get pid failed!\n", __func__, __LINE__);
		return IRQ_NONE;
	}
	p = pid_task(find_vpid(pid), PIDTYPE_PID);
	if(NULL == p)
	{
		printk("%s:%d get task struct for pid:%d Failed!\n",__func__, __LINE__, pid);
		return IRQ_NONE;
	}
	if(send_sig(SIGUSR1, p, 0))
	{
		printk("%s:%d send_sig to pid:%d failed!\n", __func__, __LINE__, pid);
	}
	return IRQ_HANDLED;
}

static long register_gpio_irq_from_user(quectel_gpio_reg_info info)
{
	int err;
	int irq;
	struct list_irq_info_node *infonode;
	
	err = 	check_gpio(info.gpio);
	if(GPIO_INVAILID == err)
		return QL_RET_ERR_NOSUPPORTPIN;
	else if(GPIO_INUSE == err)
		return QL_RET_ERR_PINALREADYSUBCRIBE;
	
	err = gpio_request( info.gpio,"qgpio");
	if(err<0)
	{
		printk("%s:%d gpio_request %d error %d!\n",__func__, __LINE__, info.gpio, err);
		return QL_RET_ERR_KERNEL;
	}
	err = gpio_direction_input(info.gpio);
	if(err<0)
	{
		printk("%s:%d gpio_direction_input %d error %d!\n",__func__, __LINE__, info.gpio, err);
		gpio_free(info.gpio);
		return QL_RET_ERR_KERNEL;
	}
	err = gpio_to_irq(info.gpio);
	if(err<0)
	{
		printk("%s:%d gpio_to_irq %d error %d!\n",__func__, __LINE__, info.gpio, err);
		gpio_free(info.gpio);
		return QL_RET_ERR_KERNEL;
	}
	irq = err;
	printk("%s:%d gpio=%d irq=%d \n", __func__, __LINE__, info.gpio, irq);
	err = request_irq(irq, irqHandler, IRQF_TRIGGER_FALLING, DEVICE_NAME, NULL);
	if(err < 0)
	{
		printk("%s:%d request_irq %d error %d!\n",__func__, __LINE__, info.gpio, err);
		gpio_free(info.gpio);
		return QL_RET_ERR_KERNEL;
	}

	// add irq info node to list
	//printk("%s:%d add info to list gpio=%d userpid=%d!\n",__func__, __LINE__, info.gpio, info.userpid);
	infonode = (struct list_irq_info_node *)kmalloc(sizeof(struct list_irq_info_node), GFP_KERNEL);
	if(NULL == infonode)
	{
		printk("%s:%d kmalloc failed!\n",__func__, __LINE__);
		gpio_free(info.gpio);
		free_irq(irq, (void*)0);
		return QL_RET_ERR_KERNEL_NOMEM;
	}
	infonode->gpio = info.gpio;
	infonode->userpid = info.userpid;
	infonode->irq = irq;
	infonode->dir = GPIO_DIR_IN;
	list_add_tail(&infonode->list, &infohead.list);	
	
	
	return 0;	
	
}

static long release_gpio_irq_from_user(unsigned int gpio)
{
	struct list_head * pos, *n;
	struct list_irq_info_node * p = NULL;
	
	// move gpio irq info node to list
	list_for_each_safe(pos, n, &infohead.list)
	{
		
		p = list_entry(pos, struct list_irq_info_node, list);
		if(p->gpio == gpio && p->irq >0)
		{
			
			free_irq(p->irq,(void*)0);
			gpio_free(gpio);
			//disable_irq_wake(p->irq);
			list_del(pos);
			kfree(p);
			return 0;
		}
	}
	return QL_RET_ERR_EINT_UNREG;
}



static long request_gpio_func(unsigned int gpio)
{
	//long ret;
	int err;
	char lable[8] = {0};
	struct list_irq_info_node *infonode;	
	
	err = check_gpio(gpio);
	if(GPIO_INVAILID == err)
		return QL_RET_ERR_NOSUPPORTPIN;
	else if(GPIO_INUSE == gpio)
		return QL_RET_ERR_PINALREADYSUBCRIBE;

	sprintf(lable, "qgpio%u", gpio);
	err = gpio_request(gpio, lable);
	if(err<0)
	{
		printk("%s:%d gpio_request %d error %d!\n",__func__, __LINE__, gpio, err);
		return QL_RET_ERR_PINALREADYSUBCRIBE;
	}
	
	// add gpio info node to list
	printk("%s:%d add info to list gpio=%d!\n",__func__, __LINE__, gpio);
	infonode = (struct list_irq_info_node *)kmalloc(sizeof(struct list_irq_info_node), GFP_KERNEL);
	if(NULL == infonode)
	{
		printk("%s:%d kmalloc failed!\n",__func__, __LINE__);
		gpio_free(gpio);
		return QL_RET_ERR_KERNEL_NOMEM;
	}
	infonode->gpio = gpio;
	infonode->userpid = -1;
	infonode->irq = -1;
	infonode->dir = 0;
	list_add_tail(&infonode->list, &infohead.list);		
	
	return 0;
	
}

static long release_gpio_func(unsigned int gpio)
{
	//long ret;
	// int err;
	struct list_head * pos, *n;
	struct list_irq_info_node * p = NULL;
	
	// move gpio info node to list
	list_for_each_safe(pos, n, &infohead.list)
	{
		
		p = list_entry(pos, struct list_irq_info_node, list);
		if(p->gpio == gpio && p->irq<0)
		{
			gpio_free(gpio);
			list_del(pos);
			kfree(p);
			return 0;
		}
	}	
	
	return QL_RET_ERR_GPIO_UNREG;	
}


static long init_gpio_func(quectel_gpio_config gpioconf)
{
	int err;
	/* request gpio */
	char lable[8] = {0};
	struct list_irq_info_node *infonode;	
	
	err = check_gpio(gpioconf.gpio);
	if(GPIO_INVAILID == err)
		return QL_RET_ERR_NOSUPPORTPIN;
	else if(GPIO_INUSE == err)
		return QL_RET_ERR_PINALREADYSUBCRIBE;

	sprintf(lable, "qgpio%u", gpioconf.gpio);
	err = gpio_request(gpioconf.gpio, lable);
	if(err<0)
	{
		printk("%s:%d gpio_request %d error %d!\n",__func__, __LINE__, gpioconf.gpio, err);
		return QL_RET_ERR_KERNEL;
	}
	
	// add gpio info node to list
	printk("%s:%d add info to list gpio=%d!\n",__func__, __LINE__, gpioconf.gpio);
	infonode = (struct list_irq_info_node *)kmalloc(sizeof(struct list_irq_info_node), GFP_KERNEL);
	if(NULL == infonode)
	{
		printk("%s:%d kmalloc failed!\n",__func__, __LINE__);
		gpio_free(gpioconf.gpio);
		return QL_RET_ERR_KERNEL_NOMEM;
	}
	infonode->gpio = gpioconf.gpio;
	infonode->userpid = -1;
	infonode->irq = -1;
	infonode->dir = gpioconf.dir;
	list_add_tail(&infonode->list, &infohead.list);	
	
	/* set gpio direction */
	if( GPIO_DIR_IN == gpioconf.dir) /* gpio input */
	{
		if((err = gpio_direction_input(gpioconf.gpio))<0)
		{
			release_gpio_func(gpioconf.gpio);
			printk("%s:%d gpio_direction_input failed!\n",__func__, __LINE__);
			return QL_RET_ERR_KERNEL;
		}
	}
	else /* gpio output */
	{
		if((err = gpio_direction_output(gpioconf.gpio, gpioconf.value))<0)
		{
			release_gpio_func(gpioconf.gpio);
			printk("%s:%d gpio_direction_output failed!\n",__func__, __LINE__);
			return QL_RET_ERR_KERNEL;
		}
		
	}
	/* set level */
	gpio_set_value(gpioconf.gpio, gpioconf.value);
	
	return 0;
	
}
static long quectel_gpio_set_dir(unsigned int gpio, unsigned int dir)
{
	struct list_irq_info_node *p;
	long err;
	if((p=find_gpio_in_list(gpio)) == NULL)
	{
		return QL_RET_ERR_GPIO_UNREG;
	}
	p->dir = dir;
	if( GPIO_DIR_IN == dir) /* gpio input */
	{
		if((err = gpio_direction_input(gpio))<0)
		{
			// release_gpio_func(gpio);
			return -1;
		}
		
	}
	else /* gpio output */
	{
		if((err = gpio_direction_output(gpio, 1))<0)
		{
			// release_gpio_func(gpio);
			return -1;
		}
		
	}
	return 0;
}

static long quectel_gpio_get_conf(quectel_gpio_config * gpioconf)
{
	struct list_irq_info_node * p;
	if( (p=find_gpio_in_list(gpioconf->gpio)) == NULL)
	{
		return QL_RET_ERR_GPIO_UNREG;
	}
	gpioconf->dir = p->dir;
	gpioconf->value = gpio_get_value(gpioconf->gpio);
	return 0;
}
static long quectel_get_gpio_dir(struct key_value *kvalue)
{
	struct list_irq_info_node * p;
	if( (p=find_gpio_in_list(kvalue->key)) == NULL)
	{
		return QL_RET_ERR_GPIO_UNREG;
	}
	kvalue->value = p->dir;
	return 0;
}
static long quectel_gpio_get_value(struct key_value *kvalue)
{
	if( find_gpio_in_list(kvalue->key) == NULL)
	{
		return QL_RET_ERR_GPIO_UNREG;
	}
	kvalue->value = gpio_get_value(kvalue->key);
	return 0;
}

static long quectel_gpio_set_value(struct key_value kvalue)
{
	struct list_irq_info_node * p;
	if( (p=find_gpio_in_list(kvalue.key)) == NULL)
	{
		return QL_RET_ERR_GPIO_UNREG;
	}
	gpio_set_value(kvalue.key, kvalue.value);
	return 0;
}

static long quectel_gpio_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  
{
	long ret;
	quectel_gpio_reg_info info;
	quectel_gpio_config gpioconf;
	unsigned int gpio;
	struct key_value kvalue;
	switch(cmd)
	{
		case QUECTEL_GPIO_REQ: /* get unsigned int gpio */
			if(copy_from_user(&gpio, (unsigned int *)arg, sizeof(gpio)))
			{
				printk("%s:%d copy_from_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}
			return request_gpio_func(gpio);
			break;
		case QUECTEL_GPIO_INIT: /* get quectel_gpio_config gpioconf */
			if(copy_from_user(&gpioconf, (quectel_gpio_config *)arg, sizeof(gpioconf)))
			{
				printk("%s:%d copy_from_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}
			return init_gpio_func(gpioconf);
			break;
		case QUECTEL_GPIO_RELEASE:
			if(copy_from_user(&gpio, (unsigned int *)arg, sizeof(gpio)))
			{
				printk("%s:%d copy_from_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}
			return release_gpio_func(gpio);
			break;

		case QUECTEL_GPIO_GET_DIR:
			if(copy_from_user(&kvalue, (struct key_value *)arg, sizeof(kvalue)))
			{
				printk("%s:%d copy_from_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}			
			ret = quectel_get_gpio_dir(&kvalue);
			if(ret)
			{
				return ret;
			}
			if(copy_to_user((struct key_value *)arg, &kvalue, sizeof(kvalue)))
			{
				printk("%s:%d copy_to_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}			
			return 0;
			break;
		case QUECTEL_GPIO_SET_DIR_IN:
			if(copy_from_user(&gpio, (unsigned int *)arg, sizeof(gpio)))
			{
				printk("%s:%d copy_from_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}
			return quectel_gpio_set_dir(gpio, GPIO_DIR_IN);
			break;
		case QUECTEL_GPIO_SET_DIR_OUT:
			if(copy_from_user(&gpio, (unsigned int *)arg, sizeof(gpio)))
			{
				printk("%s:%d copy_from_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}
			return quectel_gpio_set_dir(gpio, GPIO_DIR_OUT);
			break;
		case QUECTEL_GPIO_READ:
		case QUECTEL_GPIO_GET_CONF:
			if(copy_from_user(&gpioconf, (quectel_gpio_config *)arg, sizeof(gpioconf)))
			{
				printk("%s:%d copy_from_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}
			ret = quectel_gpio_get_conf(&gpioconf);
			if(ret)
			{
				return ret;
			}
			if(copy_to_user((quectel_gpio_config *)arg, &gpioconf, sizeof(gpioconf)))
			{
				printk("%s:%d copy_to_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}			
			return 0;
			break;
		case QUECTEL_GPIO_GET_VALUE:
			if(copy_from_user(&kvalue, (struct key_value *)arg, sizeof(kvalue)))
			{
				printk("%s:%d copy_from_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}			
			ret = quectel_gpio_get_value(&kvalue);
			if(ret)
			{
				return ret;
			}
			if(copy_to_user((struct key_value *)arg, &kvalue, sizeof(kvalue)))
			{
				printk("%s:%d copy_to_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}			
			return 0;
			break;
		case QUECTEL_GPIO_SET_VALUE:
			if(copy_from_user(&kvalue, (struct key_value *)arg, sizeof(kvalue)))
			{
				printk("%s:%d copy_from_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}
			return quectel_gpio_set_value(kvalue);
			break;
		/* for gpio interrupts */
		case QUECTEL_GPIO_REG_IRQ:
			if(copy_from_user(&info, (quectel_gpio_reg_info *)arg, sizeof(info)))
			{
				printk("%s:%d copy_from_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}
			return register_gpio_irq_from_user(info);
			break;
		case QUECTEL_GPIO_REL_IRQ:
			if(copy_from_user(&gpio, (unsigned int *)arg, sizeof(gpio)))
			{
				printk("%s:%d copy_from_user error!\n",__func__, __LINE__);
				return QL_RET_ERR_KERNEL;
			}
			return release_gpio_irq_from_user(gpio);
			break;		
		
		default:
			return QL_RET_ERR_NOIOCTLCMD;		
	}
	return QL_RET_OK;
}

struct file_operations quectel_gpio_fops =
{
	.owner = THIS_MODULE,
	.unlocked_ioctl  = quectel_gpio_unlocked_ioctl,
//	.open = quectel_gpio_open,
//	.release = quectel_gpio_release,
};

static int __init quectel_gpio_init(void)
{
	int ret;
	printk("%s:in gpio driver\n",__func__);
	ret = alloc_chrdev_region(&gpio_dev_num, 0, 1, DEVICE_NAME);
	
	if(ret < 0)
	{
		printk("%s:alloc_chrdev_region error!\n", __func__);
		goto err_alloc_chardev;
	}
	
	cdev_init(&gpio_dev, &quectel_gpio_fops);
	ret = cdev_add(&gpio_dev, gpio_dev_num, 1);
	if(ret)
	{
		printk("%s: cdev_add error!\n", __func__);
		goto err_register_chrdev;
	}
	
	gpio_class = class_create(THIS_MODULE, DEVICE_NAME);
	if (IS_ERR(gpio_class))
	{
		printk("%s:%d Err:failed in creating class.\n",__func__, __LINE__);
		goto err_register_chrdev;
	}
	device_create(gpio_class, NULL, gpio_dev_num, NULL, "%s", DEVICE_NAME);
	// init list.
	INIT_LIST_HEAD(&infohead.list);
	
	return 0;

err_register_chrdev:
	unregister_chrdev_region(gpio_dev_num, 1);

err_alloc_chardev:
	return ret;
}

static void __exit quectel_gpio_cleanup(void)
{
	struct list_head * pos, *n;
	struct list_irq_info_node * p = NULL;
	cdev_del(&gpio_dev);
	device_destroy(gpio_class, gpio_dev_num);
	class_destroy(gpio_class);
	unregister_chrdev_region(gpio_dev_num, 1);
	
	// release list
	list_for_each_safe(pos, n, &infohead.list)
	{
		list_del(pos);
		p = list_entry(pos, struct list_irq_info_node, list);
		if(p->gpio>=0)
		{
			gpio_free(p->gpio);
		}
		if(p->irq>0)
		{
			free_irq(p->irq, (void*)0);
			//disable_irq_wake(p->irq);
		}
		kfree(p);
	}
	return;
}



module_init(quectel_gpio_init);
module_exit(quectel_gpio_cleanup);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("QUECTEL GPIO driver");
MODULE_VERSION("1.0");
MODULE_ALIAS("sam.wu@quectel.com");





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值