通过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");