Title: [4131] trunk/drivers/input/joystick/ad7142.c:
Revision
4131
Author
cooloney
Date
2008-01-21 22:11:06 -0600 (Mon, 21 Jan 2008)

Log Message

[#2424] [PATCH try #4] Input/Joystick Driver: add support AD7142 joystick driver

[try #2] Changelog:
 - Coding style issues fixed, passed checkpatch.pl
 - Kill uselss "ad7142_used"
 - Move request_irq to probe
 - Move i2c_check_functionality to probe
 - Error handling added

[try #3] Changelog:
 - Use kzalloc
 - Use strlcpy
 - Use completion instead of wait queue
 - Move input_allocate_device and input_register_device to ad7142_probe()
 - Fix some issues pointed by LKML

[try #4] Changelog:
 - Use workqueue instead of completion
 - Shutdown AD7142 in ad7142_close()

Cc: Andrey Panin <[EMAIL PROTECTED]>
Cc: Roel Kluin <[EMAIL PROTECTED]>
Cc: Ahmed S. Darwish <[EMAIL PROTECTED]>
Cc: Dmitry Torokhov <[EMAIL PROTECTED]>
Signed-off-by: Bryan Wu <[EMAIL PROTECTED]>

Diffstat

 ad7142.c |  390 ++++++++++++++++++++++++++++++++-------------------------------
 1 files changed, 203 insertions(+), 187 deletions(-)

Modified Paths

Diff

Modified: trunk/drivers/input/joystick/ad7142.c (4130 => 4131)


--- trunk/drivers/input/joystick/ad7142.c	2008-01-22 04:08:50 UTC (rev 4130)
+++ trunk/drivers/input/joystick/ad7142.c	2008-01-22 04:11:06 UTC (rev 4131)
@@ -1,6 +1,5 @@
 /*
  * File:         drivers/input/joystick/ad7142.c
- * Based on:     drivers/input/joystick/amijoy.c
  * Original Author: Aubrey Li
  * Maintained by: Bryan Wu <[EMAIL PROTECTED]>
  *
@@ -38,25 +37,16 @@
 #include <linux/interrupt.h>
 #include <linux/i2c.h>
 #include <linux/delay.h>
-#include <linux/kthread.h>
 #include <linux/uaccess.h>
 #include <linux/irq.h>
+#include <linux/workqueue.h>
 
 #include <asm/blackfin.h>
 
 MODULE_AUTHOR("Aubrey Li, Bryan Wu <[EMAIL PROTECTED]>");
-MODULE_DESCRIPTION("Driver for AD7142 joysticks");
+MODULE_DESCRIPTION("Driver for AD7142 Joysticks");
 MODULE_LICENSE("GPL");
 
-/*
- * Feeding the output queue to the device is handled by way of a
- * workqueue.
- */
-static struct task_struct *ad7142_task;
-static DECLARE_WAIT_QUEUE_HEAD(ad7142_wait);
-
-static struct input_dev *ad7142_dev;
-
 #define AD7142_DRV_NAME		"ad7142_js"
 #define AD7142_I2C_ID		0xE622
 #define AD7142_I2C_ADDR		0x2C
@@ -134,80 +124,54 @@
 	{0xFFFF, 0x3FFF, 0x0000, 0x0606, 0x01F4, 0x01F4, 0x0320, 0x0320},
 };
 
-static struct i2c_driver ad7142_driver;
-static struct i2c_client *ad7142_client;
+struct ad7142_data {
+	struct input_dev *input_dev;
 
+	struct i2c_driver i2c_drv;
+	struct i2c_client client;
+
+	struct work_struct work;
+
+	unsigned short old_status_low;
+	unsigned short old_status_high;
+
+	int irq;
+	int is_open;
+};
+
 static unsigned short ignore[] = { I2C_CLIENT_END };
 static unsigned short normal_addr[] = { AD7142_I2C_ADDR, I2C_CLIENT_END };
 
-static int intr_flag;
-
 static struct i2c_client_address_data addr_data = {
 	.normal_i2c = normal_addr,
 	.probe = ignore,
 	.ignore = ignore,
 };
 
-static irqreturn_t ad7142_interrupt(int irq, void *dummy)
-{
-	disable_irq(CONFIG_BFIN_JOYSTICK_IRQ_PFX);
-	intr_flag = 1;
-	wake_up_interruptible(&ad7142_wait);
-	return IRQ_HANDLED;
-}
+static int ad7142_attach(struct i2c_adapter *adap);
+static int ad7142_detach(struct i2c_client *client);
+static int ad7142_i2c_read(struct i2c_client *client, unsigned short offset,
+		unsigned short *data, unsigned int len);
 
-static int
-ad7142_probe(struct i2c_adapter *adap, int addr, int kind)
+static struct i2c_driver ad7142_driver = {
+	.driver = {
+		.name = AD7142_DRV_NAME,
+	},
+	.id = AD7142_I2C_ID,
+	.attach_adapter = ad7142_attach,
+	.detach_client = ad7142_detach,
+};
+
+static irqreturn_t ad7142_interrupt(int irq, void *_data)
 {
-	struct i2c_client *client;
-	int rc;
+	struct ad7142_data *data = ""
 
-	client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
-	if (!client)
-		return -ENOMEM;
-	memset(client, 0, sizeof(struct i2c_client));
-	strncpy(client->name, AD7142_DRV_NAME, I2C_NAME_SIZE);
-	client->addr = addr;
-	client->adapter = adap;
-	client->driver = &ad7142_driver;
+	disable_irq(data->irq);
 
-	rc = i2c_attach_client(client);
-	if (rc) {
-		printk(KERN_ERR "i2c_attach_client fail: %d\n", rc);
-		goto fail_attach;
-	}
+	if (data->is_open)
+		schedule_work(&data->work);
 
-	/*
-	 * The ADV7142 has an autoincrement function,
-	 * use it if the adapter understands raw I2C
-	 */
-	rc = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
-	if (!rc) {
-		printk(KERN_ERR
-			"AD7142: i2c bus doesn't support raw I2C operation\n");
-		rc = -EINVAL;
-		goto fail_check;
-	}
-
-	rc = request_irq(CONFIG_BFIN_JOYSTICK_IRQ_PFX, ad7142_interrupt,
-			IRQF_TRIGGER_LOW, "ad7142_joy", ad7142_interrupt);
-	if (rc) {
-		printk(KERN_ERR "AD7142: Can't allocate irq %d\n",
-			CONFIG_BFIN_JOYSTICK_IRQ_PFX);
-		rc = -EBUSY;
-		goto fail_check;
-	}
-
-	ad7142_client = client;
-	printk(KERN_INFO "%s_attach: at 0x%02x\n",
-			client->name, client->addr << 1);
-	return 0;
-
-fail_check:
-	i2c_detach_client(client);
-fail_attach:
-	kfree(client);
-	return rc;
+	return IRQ_HANDLED;
 }
 
 static int ad7142_i2c_write(struct i2c_client *client, unsigned short offset,
@@ -247,7 +211,7 @@
 	int i;
 	u8 block_data[32];
 
-	if (len < 1 && len > 16) {
+	if (len < 1 || len > 16) {
 		printk(KERN_ERR "AD7142: read data length error\n");
 		return ret;
 	}
@@ -278,208 +242,260 @@
 	return ret;
 }
 
-static int
-ad7142_attach(struct i2c_adapter *adap)
+static void ad7142_work(struct work_struct *work)
 {
-	return i2c_probe(adap, &addr_data, &ad7142_probe);
-}
-
-static int
-ad7142_detach_client(struct i2c_client *client)
-{
-	int rc;
-
-	free_irq(CONFIG_BFIN_JOYSTICK_IRQ_PFX, ad7142_interrupt);
-
-	rc = i2c_detach_client(client);
-	if (!rc)
-		kfree(i2c_get_clientdata(client));
-	return rc;
-}
-
-static struct i2c_driver ad7142_driver = {
-	.driver = {
-		.name = AD7142_DRV_NAME,
-	},
-	.id = AD7142_I2C_ID,
-	.attach_adapter = ad7142_attach,
-	.detach_client = ad7142_detach_client,
-};
-
-unsigned short old_status_low, old_status_high;
-
-static void ad7142_decode(void)
-{
+	struct ad7142_data *data = "" struct ad7142_data, work);
+	struct i2c_client *client = &data->client;
+	struct input_dev *input_dev = data->input_dev;
 	unsigned short irqno_low, irqno_high;
 	unsigned short temp;
 
-	ad7142_i2c_read(ad7142_client, INTSTAT_REG0, &irqno_low, 1);
-	temp = irqno_low ^ old_status_low;
+	ad7142_i2c_read(client, INTSTAT_REG0, &irqno_low, 1);
+	temp = irqno_low ^ data->old_status_low;
 	switch (temp) {
 	case 0x0001:
-		input_report_key(ad7142_dev, BTN_BASE, (irqno_low & 0x0001));
+		input_report_key(input_dev, BTN_BASE, (irqno_low & 0x0001));
 		break;
 	case 0x0002:
-		input_report_key(ad7142_dev, BTN_BASE4,
+		input_report_key(input_dev, BTN_BASE4,
 					((irqno_low & 0x0002) >> 1));
 		break;
 	case 0x0004:
-		input_report_key(ad7142_dev, KEY_UP,
+		input_report_key(input_dev, KEY_UP,
 					((irqno_low & 0x0004) >> 2));
 		break;
 	case 0x0008:
-		input_report_key(ad7142_dev, KEY_RIGHT,
+		input_report_key(input_dev, KEY_RIGHT,
 					((irqno_low & 0x0008) >> 3));
 		break;
 	}
-	old_status_low = irqno_low;
+	data->old_status_low = irqno_low;
 
-	ad7142_i2c_read(ad7142_client, INTSTAT_REG1, &irqno_high, 1);
-	temp = irqno_high ^ old_status_high;
+	ad7142_i2c_read(client, INTSTAT_REG1, &irqno_high, 1);
+	temp = irqno_high ^ data->old_status_high;
 	switch (temp) {
 	case 0x0001:
-		input_report_key(ad7142_dev, BTN_BASE2, irqno_high & 0x0001);
+		input_report_key(input_dev, BTN_BASE2, irqno_high & 0x0001);
 		break;
 	case 0x0002:
-		input_report_key(ad7142_dev, BTN_BASE3,
+		input_report_key(input_dev, BTN_BASE3,
 					((irqno_high & 0x0002) >> 1));
 		break;
 	case 0x0004:
-		input_report_key(ad7142_dev, KEY_DOWN,
+		input_report_key(input_dev, KEY_DOWN,
 					((irqno_high & 0x0004) >> 2));
 		break;
 	case 0x0008:
-		input_report_key(ad7142_dev, KEY_LEFT,
+		input_report_key(input_dev, KEY_LEFT,
 					((irqno_high & 0x0008) >> 3));
 		break;
 	}
-	old_status_high = irqno_high;
+	data->old_status_high = irqno_high;
 
-	input_sync(ad7142_dev);
-}
+	input_sync(input_dev);
 
-static int ad7142_thread(void *nothing)
-{
-	do {
-		wait_event_interruptible(ad7142_wait,
-			kthread_should_stop() || (intr_flag != 0));
-		ad7142_decode();
-		intr_flag = 0;
-		enable_irq(CONFIG_BFIN_JOYSTICK_IRQ_PFX);
-	} while (!kthread_should_stop());
-
-	pr_debug("ad7142: kthread exiting\n");
-
-	return 0;
+	enable_irq(data->irq);
 }
 
 static int ad7142_open(struct input_dev *dev)
 {
+	struct ad7142_data *data = ""
+	struct i2c_client *client = &data->client;
 	unsigned short id, value;
 
-	ad7142_i2c_read(ad7142_client, DEVID, &id, 1);
+	ad7142_i2c_read(client, DEVID, &id, 1);
 	if (id != AD7142_I2C_ID) {
 		printk(KERN_ERR "Open AD7142 error\n");
 		return -ENODEV;
 	}
 
-	ad7142_i2c_write(ad7142_client, STAGE0_CONNECTION, stage[0], 8);
-	ad7142_i2c_write(ad7142_client, STAGE1_CONNECTION, stage[1], 8);
-	ad7142_i2c_write(ad7142_client, STAGE2_CONNECTION, stage[2], 8);
-	ad7142_i2c_write(ad7142_client, STAGE3_CONNECTION, stage[3], 8);
-	ad7142_i2c_write(ad7142_client, STAGE4_CONNECTION, stage[4], 8);
-	ad7142_i2c_write(ad7142_client, STAGE5_CONNECTION, stage[4], 8);
-	ad7142_i2c_write(ad7142_client, STAGE6_CONNECTION, stage[4], 8);
-	ad7142_i2c_write(ad7142_client, STAGE7_CONNECTION, stage[4], 8);
-	ad7142_i2c_write(ad7142_client, STAGE8_CONNECTION, stage[4], 8);
-	ad7142_i2c_write(ad7142_client, STAGE9_CONNECTION, stage[4], 8);
-	ad7142_i2c_write(ad7142_client, STAGE10_CONNECTION, stage[4], 8);
-	ad7142_i2c_write(ad7142_client, STAGE11_CONNECTION, stage[4], 8);
+	ad7142_i2c_write(client, STAGE0_CONNECTION, stage[0], 8);
+	ad7142_i2c_write(client, STAGE1_CONNECTION, stage[1], 8);
+	ad7142_i2c_write(client, STAGE2_CONNECTION, stage[2], 8);
+	ad7142_i2c_write(client, STAGE3_CONNECTION, stage[3], 8);
+	ad7142_i2c_write(client, STAGE4_CONNECTION, stage[4], 8);
+	ad7142_i2c_write(client, STAGE5_CONNECTION, stage[4], 8);
+	ad7142_i2c_write(client, STAGE6_CONNECTION, stage[4], 8);
+	ad7142_i2c_write(client, STAGE7_CONNECTION, stage[4], 8);
+	ad7142_i2c_write(client, STAGE8_CONNECTION, stage[4], 8);
+	ad7142_i2c_write(client, STAGE9_CONNECTION, stage[4], 8);
+	ad7142_i2c_write(client, STAGE10_CONNECTION, stage[4], 8);
+	ad7142_i2c_write(client, STAGE11_CONNECTION, stage[4], 8);
 
+	/* In full power mode */
 	value = 0x00B0;
-	ad7142_i2c_write(ad7142_client, PWRCONVCTL, &value, 1);
+	ad7142_i2c_write(client, PWRCONVCTL, &value, 1);
 
 	value = 0x0690;
-	ad7142_i2c_write(ad7142_client, AMBCOMPCTL_REG1, &value, 1);
+	ad7142_i2c_write(client, AMBCOMPCTL_REG1, &value, 1);
 
 	value = 0x0664;
-	ad7142_i2c_write(ad7142_client, AMBCOMPCTL_REG2, &value, 1);
+	ad7142_i2c_write(client, AMBCOMPCTL_REG2, &value, 1);
 
 	value = 0x290F;
-	ad7142_i2c_write(ad7142_client, AMBCOMPCTL_REG3, &value, 1);
+	ad7142_i2c_write(client, AMBCOMPCTL_REG3, &value, 1);
 
 	value = 0x000F;
-	ad7142_i2c_write(ad7142_client, INTEN_REG0, &value, 1);
-	ad7142_i2c_write(ad7142_client, INTEN_REG1, &value, 1);
+	ad7142_i2c_write(client, INTEN_REG0, &value, 1);
+	ad7142_i2c_write(client, INTEN_REG1, &value, 1);
 
 	value = 0x0000;
-	ad7142_i2c_write(ad7142_client, INTEN_REG2, &value, 1);
+	ad7142_i2c_write(client, INTEN_REG2, &value, 1);
 
-	ad7142_i2c_read(ad7142_client, AMBCOMPCTL_REG1, &value, 1);
+	ad7142_i2c_read(client, AMBCOMPCTL_REG1, &value, 1);
 
 	value = 0x000F;
-	ad7142_i2c_write(ad7142_client, AMBCOMPCTL_REG0, &value, 1);
+	ad7142_i2c_write(client, AMBCOMPCTL_REG0, &value, 1);
 
-	ad7142_task = kthread_run(ad7142_thread, NULL, "ad7142_task");
-	if (IS_ERR(ad7142_task)) {
-		printk(KERN_ERR "serio: Failed to start kseriod\n");
-		return PTR_ERR(ad7142_task);
-	}
+	data->is_open = 1;
+	enable_irq(data->irq);
 	return 0;
 }
 
 static void ad7142_close(struct input_dev *dev)
 {
-	kthread_stop(ad7142_task);
+	struct ad7142_data *data = ""
+	struct i2c_client *client = &data->client;
+	unsigned short value;
+
+	disable_irq(data->irq);
+	data->is_open = 0;
+
+	flush_scheduled_work();
+
+	/*
+	 * Turn AD7142 to full shutdown mode
+	 * No CDC conversions
+	 */
+	value = 0x0001;
+	ad7142_i2c_write(client, PWRCONVCTL, &value, 1);
 }
 
-static int __init ad7142_init(void)
+static int ad7142_probe(struct i2c_adapter *adap, int addr, int kind)
 {
-	int ret;
+	struct ad7142_data *data;
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	int rc;
 
-	ad7142_dev = input_allocate_device();
-	if (!ad7142_dev)
+	data = "" ad7142_data), GFP_KERNEL);
+	if (!data)
 		return -ENOMEM;
 
-	ad7142_dev->open = ad7142_open;
-	ad7142_dev->close = ad7142_close;
-	ad7142_dev->evbit[0] = BIT(EV_KEY);
-	ad7142_dev->keybit[LONG(BTN_BASE)] = BIT(BTN_BASE) | BIT(BTN_BASE2) |
+	client = &data->client;
+	i2c_set_clientdata(client, data);
+
+	strlcpy(client->name, AD7142_DRV_NAME, I2C_NAME_SIZE);
+	client->addr = addr;
+	client->adapter = adap;
+	client->driver = &ad7142_driver;
+
+	rc = i2c_attach_client(client);
+	if (rc) {
+		printk(KERN_ERR "i2c_attach_client fail: %d\n", rc);
+		goto fail_attach;
+	}
+
+	/*
+	 * The ADV7142 has an autoincrement function,
+	 * use it if the adapter understands raw I2C
+	 */
+	rc = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
+	if (!rc) {
+		printk(KERN_ERR
+			"AD7142: i2c bus doesn't support raw I2C operation\n");
+		rc = -ENOSYS;
+		goto fail_check;
+	}
+
+	/* Start workqueue for defer message transfer */
+	INIT_WORK(&data->work, ad7142_work);
+
+	data->irq = CONFIG_BFIN_JOYSTICK_IRQ_PFX;
+	rc = request_irq(data->irq, ad7142_interrupt,
+		IRQF_TRIGGER_LOW, "ad7142_joy", data);
+	if (rc) {
+		printk(KERN_ERR "AD7142: Can't allocate irq %d\n", data->irq);
+		goto fail_check;
+	}
+
+	printk(KERN_INFO "%s_attach: at 0x%02x\n",
+			client->name, client->addr << 1);
+
+	/* Allocate and register AD7142 input device */
+	data->input_dev = input_allocate_device();
+	if (!data->input_dev) {
+		printk(KERN_ERR "AD7142: Can't allocate input device\n");
+		rc = -ENOMEM;
+		goto fail_allocate;
+	}
+
+	input_dev = data->input_dev;
+	input_dev->open = ad7142_open;
+	input_dev->close = ad7142_close;
+	input_dev->evbit[0] = BIT(EV_KEY);
+	input_dev->keybit[LONG(BTN_BASE)] = BIT(BTN_BASE) | BIT(BTN_BASE2) |
 						BIT(BTN_BASE3) | BIT(BTN_BASE4);
-	ad7142_dev->keybit[LONG(KEY_UP)] |= BIT(KEY_UP) | BIT(KEY_DOWN) |
+	input_dev->keybit[LONG(KEY_UP)] |= BIT(KEY_UP) | BIT(KEY_DOWN) |
 						BIT(KEY_LEFT) | BIT(KEY_RIGHT);
 
-	ad7142_dev->name = "ad7142 joystick";
-	ad7142_dev->phys = "ad7142/input0";
-	ad7142_dev->id.bustype = BUS_I2C;
-	ad7142_dev->id.vendor = 0x0001;
-	ad7142_dev->id.product = 0x0001;
-	ad7142_dev->id.version = 0x0100;
+	input_dev->name = "ad7142 joystick";
+	input_dev->phys = "ad7142/input0";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0100;
 
-	ret = input_register_device(ad7142_dev);
-	if (ret) {
+	input_set_drvdata(input_dev, data);
+
+	rc = input_register_device(input_dev);
+	if (rc) {
 		printk(KERN_ERR "Failed to register AD7142 input device!\n");
 		goto fail_register;
 	}
 
-	ret = i2c_add_driver(&ad7142_driver);
-	if (ret) {
-		printk(KERN_ERR "Failed to add AD7142 I2C driver!\n");
-		goto fail_add;
-	}
 	return 0;
 
-fail_add:
-	input_unregister_device(ad7142_dev);
 fail_register:
-	input_free_device(ad7142_dev);
-	return ret;
+	input_free_device(input_dev);
+fail_allocate:
+	free_irq(data->irq, ad7142_interrupt);
+fail_check:
+	i2c_detach_client(client);
+fail_attach:
+	kfree(client);
+	return rc;
 }
 
+static int ad7142_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, &ad7142_probe);
+}
+
+static int ad7142_detach(struct i2c_client *client)
+{
+	struct ad7142_data *data = ""
+
+	free_irq(data->irq, ad7142_interrupt);
+
+	flush_scheduled_work();
+
+	input_unregister_device(data->input_dev);
+
+	kfree(data);
+
+	return i2c_detach_client(client);
+}
+
+
+static int __init ad7142_init(void)
+{
+	return i2c_add_driver(&ad7142_driver);
+}
+
 static void __exit ad7142_exit(void)
 {
 	i2c_del_driver(&ad7142_driver);
-	input_unregister_device(ad7142_dev);
 }
 
 module_init(ad7142_init);
_______________________________________________
Linux-kernel-commits mailing list
[email protected]
http://blackfin.uclinux.org/mailman/listinfo/linux-kernel-commits

Reply via email to