BigW Consortium Gitlab

Commit 90f2e9ac by Forest Godfrey

Add driver info structure to allow driver to operate against multiple devices and

eliminate the use of global variables to keep track of information.
parent 8e45ea3a
/*
* This file is part of the Arduino I2C keyboard.
* Copyright (C) 2018 Forest Godfrey <fgodfrey@bigw.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _AI2C_H
#define _AI2C_H
typedef struct arduino_i2c_driver_data_s {
struct list_head driver_list;
struct work_struct intr_upper_work;
struct i2c_client *client;
int irq_num;
int irq_pin;
int irq_count;
int numkeys;
int *keymap;
} arduino_i2c_driver_data_t;
#endif /* _AI2C_H */
...@@ -16,69 +16,90 @@ ...@@ -16,69 +16,90 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* * along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
/*
* Kernel Includes
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
/*
* Project Includes
*/
#include "ai2c.h"
#include "ai2c_platform_data.h" #include "ai2c_platform_data.h"
#define IRQ_GPIO_PIN 9 #define IRQ_GPIO_PIN 9
#define GPIO_IRQ_NAME "ai2c_keyboard_irq" #define GPIO_IRQ_NAME "ai2c_keyboard_irq"
static int irq_num = -1; const int default_keymap[] = {
static int count = 0;
static int up_count = 0;
struct delayed_work my_work;
static struct i2c_client *my_client = NULL; // XXX - FIXME
const int keymap[] = {
0, 0,
}; };
static LIST_HEAD(driver_list);
static DEFINE_MUTEX(driver_list_lock);
static void static void
ai2c_intr(struct work_struct *work) { ai2c_intr(struct work_struct *work) {
if (up_count++ < 10) { arduino_i2c_driver_data_t *drv_info = container_of(
printk("ai2c_intr: entered\n"); work, arduino_i2c_driver_data_t, intr_upper_work);
}
if (my_client) BUG_ON(!drv_info->client);
i2c_smbus_write_byte_data(my_client, 50, 48); dev_info(&drv_info->client->dev, "ai2c_intr: entered\n");
i2c_smbus_write_byte_data(drv_info->client, 50, 48);
return; return;
} }
static irqreturn_t static irqreturn_t
ai2c_intr_lower(int irq, void *data) { ai2c_intr_lower(int irq, void *data) {
if (count++ < 10) { arduino_i2c_driver_data_t *drv_info = (arduino_i2c_driver_data_t *)data;
BUG_ON(!drv_info);
if (drv_info->irq_count++ < 10) {
printk("ai2c_intr_lower: entered\n"); printk("ai2c_intr_lower: entered\n");
} }
schedule_delayed_work(&my_work, 25); schedule_work(&drv_info->intr_upper_work);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int static int
ai2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { ai2c_probe(struct i2c_client *client, const struct i2c_device_id *id) {
int ret; int ret = 0;
int flags = 0; int flags = 0;
void *drv_info = NULL;
const arduino_i2c_keyboard_platform_data_t *pdata = const arduino_i2c_keyboard_platform_data_t *pdata =
dev_get_platdata(&client->dev); dev_get_platdata(&client->dev);
arduino_i2c_driver_data_t *drv_info = NULL;
dev_info(&client->dev, "Arduino I2C Keyboard Probing Device\n"); dev_info(&client->dev, "Arduino I2C Keyboard Probing Device");
if (!pdata) { if (!pdata) {
dev_err(&client->dev, "No platform present - failing\n"); dev_err(&client->dev, "No platform present - failing");
return -EINVAL; ret = -EINVAL;
goto out;
}
drv_info = (arduino_i2c_driver_data_t *)kmalloc(sizeof(arduino_i2c_driver_data_t), GFP_KERNEL);
if (!drv_info) {
dev_err(&client->dev, "Unable to allocate driver memory");
ret = -ENOMEM;
goto out;
} }
memset(drv_info, 0, sizeof(arduino_i2c_driver_data_t));
ret = gpio_request(pdata->gpio_intr_pin, GPIO_IRQ_NAME); ret = gpio_request(pdata->gpio_intr_pin, GPIO_IRQ_NAME);
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, "ai2c: IRQ pin reservation of %d failed: error %d\n", pdata->gpio_intr_pin, ret); dev_err(&client->dev, "ai2c: IRQ pin reservation of %d failed: error %d", pdata->gpio_intr_pin, ret);
return ret; goto out_dealloc;
} }
gpio_direction_input(pdata->gpio_intr_pin); gpio_direction_input(pdata->gpio_intr_pin);
...@@ -89,29 +110,58 @@ ai2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { ...@@ -89,29 +110,58 @@ ai2c_probe(struct i2c_client *client, const struct i2c_device_id *id) {
gpio_export(pdata->gpio_intr_pin, "arduino_i2c_intr_pin"); gpio_export(pdata->gpio_intr_pin, "arduino_i2c_intr_pin");
#endif /* CONFIG_SIERRA */ #endif /* CONFIG_SIERRA */
irq_num = gpio_to_irq(pdata->gpio_intr_pin); drv_info->irq_pin = pdata->gpio_intr_pin;
drv_info->irq_num = gpio_to_irq(pdata->gpio_intr_pin);
dev_dbg(&client->dev, "ai2c: Reserved pin [%d]: irq = %d\n", pdata->gpio_intr_pin, irq_num); dev_dbg(&client->dev, "ai2c: Reserved pin [%d]: irq = %d\n", pdata->gpio_intr_pin, drv_info->irq_num);
flags |= IRQF_TRIGGER_FALLING; flags |= IRQF_TRIGGER_FALLING;
ret = request_irq(irq_num, ai2c_intr_lower, flags, "ai2c_keyboard", drv_info); ret = request_irq(drv_info->irq_num, ai2c_intr_lower, flags, "ai2c_keyboard", drv_info);
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, "ai2c: request_irq failed: error %d\n", ret); dev_err(&client->dev, "ai2c: request_irq failed: error %d\n", ret);
return ret; goto out_cleanup;
} }
irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_FALLING); irq_set_irq_type(drv_info->irq_num, IRQ_TYPE_EDGE_FALLING);
drv_info->client = client;
my_client = client; // XXX - FIXME - Put in IRQ Info INIT_WORK(&drv_info->intr_upper_work, ai2c_intr);
dev_set_drvdata(&client->dev, drv_info);
INIT_LIST_HEAD(&drv_info->driver_list);
mutex_lock(&driver_list_lock);
list_add(&drv_info->driver_list, &driver_list);
mutex_unlock(&driver_list_lock);
return 0; return 0;
out_cleanup:
gpio_free(drv_info->irq_num);
out_dealloc:
kfree(drv_info);
out:
return ret;
} }
static int static int
ai2c_remove(struct i2c_client *client) { ai2c_remove(struct i2c_client *client) {
if (irq_num > 0) { arduino_i2c_driver_data_t *drv_info = (arduino_i2c_driver_data_t *)dev_get_drvdata(&client->dev);
free_irq(irq_num, ai2c_intr_lower); BUG_ON(!drv_info);
}
free_irq(drv_info->irq_num, drv_info);
flush_work(&drv_info->intr_upper_work);
gpio_free(drv_info->irq_num);
dev_set_drvdata(&drv_info->client->dev, NULL);
mutex_lock(&driver_list_lock);
list_del(&drv_info->driver_list);
mutex_unlock(&driver_list_lock);
kfree(drv_info);
return 0; return 0;
} }
...@@ -141,13 +191,13 @@ ai2c_init(void) { ...@@ -141,13 +191,13 @@ ai2c_init(void) {
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "Unable to register AI2C keyboard driver\n"); printk(KERN_ERR "Unable to register AI2C keyboard driver\n");
} }
INIT_DELAYED_WORK(&my_work, ai2c_intr);
return ret; return ret;
} }
void void
ai2c_shutdown(void) { ai2c_shutdown(void) {
printk(KERN_INFO "Arduino I2C Keyboard Shutting Down\n"); printk(KERN_INFO "Arduino I2C Keyboard Shutting Down\n");
i2c_del_driver(&ai2c_driver); i2c_del_driver(&ai2c_driver);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment