All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
To: tapio.vihuri@nokia.com
Cc: randy.dunlap@oracle.com, alsa-devel@alsa-project.org,
	ilkka.koskinen@nokia.com, linux-kernel@vger.kernel.org,
	samu.p.onkalo@nokia.com
Subject: Re: [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver
Date: Sun, 2 Jan 2011 00:19:22 -0800	[thread overview]
Message-ID: <20110102081922.GD5429@core.coreip.homeip.net> (raw)
In-Reply-To: <1293716219-26089-2-git-send-email-tapio.vihuri@nokia.com>

Hi Tapio,

On Thu, Dec 30, 2010 at 03:36:57PM +0200, tapio.vihuri@nokia.com wrote:
> From: Tapio Vihuri <tapio.vihuri@nokia.com>
> 
> ECI stands for (Enhancement Control Interface).
> 
> ECI is better known as Multimedia Headset for Nokia phones.
> If headset has many buttons, like play, vol+, vol- etc. then it is propably
> ECI accessory.
> Among several buttons ECI accessory contains memory for storing several
> parameters.
> 
> ECI input driver provides the following features:
> - reading ECI configuration memory
> - ECI buttons as input events
> 
> Signed-off-by: Tapio Vihuri <tapio.vihuri@nokia.com>
> ---
>  drivers/input/misc/Kconfig  |   18 +
>  drivers/input/misc/Makefile |    2 +-
>  drivers/input/misc/eci.c    | 1002 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/input/eci.h   |  157 +++++++
>  4 files changed, 1178 insertions(+), 1 deletions(-)
>  create mode 100644 drivers/input/misc/eci.c
>  create mode 100644 include/linux/input/eci.h
> 
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index b99b8cb..7a15bc6 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -448,4 +448,22 @@ config INPUT_ADXL34X_SPI
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called adxl34x-spi.
>  
> +config INPUT_ECI
> +	tristate "AV ECI (Enhancement Control Interface) input driver"
> +	help
> +	The Enhancement Control Interface functionality
> +	  ECI is better known as Multimedia Headset for Nokia phones.
> +	  If headset has many buttons, like play, vol+, vol- etc. then
> +	  it is propably ECI accessory.
> +	  Among several buttons ECI accessory contains memory for storing
> +	  several parameters.
> +
> +	  ECI input driver provides the following features:
> +	  - reading ECI configuration memory
> +	  - ECI buttons as input events
> +
> +	  Say 'y' here to statically link this module into the kernel or 'm'
> +	  to build it as a dynamically loadable module. The module will be
> +	  called eci.ko
> +
>  endif
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index 1fe1f6c..99d2289 100644
> --- a/drivers/input/misc/Makefile
> +++ b/drivers/input/misc/Makefile
> @@ -42,4 +42,4 @@ obj-$(CONFIG_INPUT_WINBOND_CIR)		+= winbond-cir.o
>  obj-$(CONFIG_INPUT_WISTRON_BTNS)	+= wistron_btns.o
>  obj-$(CONFIG_INPUT_WM831X_ON)		+= wm831x-on.o
>  obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
> -
> +obj-$(CONFIG_INPUT_ECI)			+= eci.o

Please keep Makefile and Kconfig sorted alphabetically.

> diff --git a/drivers/input/misc/eci.c b/drivers/input/misc/eci.c
> new file mode 100644
> index 0000000..72efccf
> --- /dev/null
> +++ b/drivers/input/misc/eci.c
> @@ -0,0 +1,1002 @@
> +/*
> + * This file is part of ECI (Enhancement Control Interface) accessory input
> + * driver
> + *
> + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
> + *
> + * Contact: Tapio Vihuri <tapio.vihuri@nokia.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * 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.
> + *
> + * 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 St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +/*
> + * ECI stands for (Enhancement Control Interface).
> + *
> + * ECI is better known as Multimedia Headset for Nokia phones.
> + * If headset has many buttons, like play, vol+, vol- etc. then it is propably
> + * ECI accessory.
> + * Among several buttons ECI accessory contains memory for storing several
> + * parameters.
> + *
> + * ECI input driver provides the following features:
> + *  - reading ECI configuration memory
> + *  - ECI buttons as input events
> + */
> +
> +#include <linux/init.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/debugfs.h>
> +
> +#include <linux/input.h>
> +#include <linux/input/eci.h>
> +#include <linux/miscdevice.h>
> +
> +#define ECI_DRIVERNAME	"ECI_accessory"
> +
> +#define ECI_WAIT_SEND_BUTTON		5	/* ms */
> +#define ECI_WAIT_BUS_SETTLE		40	/* ms */
> +#define ECI_TRY_GET_MEMORY		2000	/* ms */
> +#define ECI_TRY_INIT_IO			200	/* ms */
> +#define ECI_TRY_SET_MIC			200	/* ms */
> +#define ECI_KEY_REPEAT_INTERVAL		400	/* ms */
> +
> +#define ECI_EKEY_BLOCK_ID			0xb3
> +#define ECI_ENHANCEMENT_FEATURE_BLOCK_ID	0x02
> +
> +/* ECI Inputs */
> +#define ECI_NIL_FEATURE			0x00 /* No feature */
> +#define ECI_IGNS			0x01 /* Ignition Sense */
> +#define ECI_CK_HANDSET_HOOK		0x02 /* Car-Kit Handset Hook */
> +#define ECI_POWER_SUPPLY		0x03 /* Power Supply/Car Battery Det  */
> +#define ECI_EXT_AUD_IN			0x06 /* External audio In */
> +#define ECI_SEND_END_VR			0x07 /* Send, End, and Voice Recogn */
> +#define ECI_HD_PLUG			0x08 /* Headphone plug */
> +#define ECI_DEV_POWER_REQ		0x0a /* Device Power Request */
> +#define ECI_VOL_UP			0x0b /* Volume Up */
> +#define ECI_VOL_DOWN			0x0c /* Volume Down */
> +#define ECI_PLAY_PAUSE_CTRL		0x0d /* Play / Pause */
> +#define ECI_STOP			0x0e /* Stop */
> +#define ECI_NEXT_FF_AUTOSRC_UP		0x0f /* Next/Fast Fward/Autosearch up */
> +#define ECI_PREV_REW_AUTOSEARCH_DOWN	0x10 /* Prev/Rewind/Autosearch down */
> +#define ECI_POC				0x11 /* Push to Talk over Cellular */
> +#define ECI_SYNC_BTN			0x14 /* Synchronization Button */
> +#define ECI_MUSIC_RADIO_OFF_SELECTOR	0x15 /* Music/Radio/Off Selector */
> +#define ECI_REDIAL			0x16 /* Redial */
> +#define ECI_LEFT_SOFT_KEY		0x17 /* Left Soft Key */
> +#define ECI_RIGHT_SOFT_KEY		0x18 /* Right Soft key */
> +#define ECI_SEND_KEY			0x19 /* Send key */
> +#define ECI_END_KEY			0x1a /* End key */
> +#define ECI_MIDDLE_SOFT_KEY		0x1b /* Middle Soft key */
> +#define ECI_UP				0x1c /* UP key/joystick direction */
> +#define ECI_DOWN			0x1d /* DOWN key/joystick direction */
> +#define ECI_RIGHT			0x1e /* RIGHT key/joystick direction */
> +#define ECI_LEFT			0x1f /* LEFT key/joystick direction */
> +#define ECI_SYMBIAN_NAVY_KEY		0x20 /* Symbian Application key */
> +#define ECI_TERMINAL_APP_CTRL_IN	0x21 /* Terminal Applicat Ctrl Input */
> +#define ECI_USB_CLASS_SWITCHING		0x23 /* USB Class Switching */
> +#define ECI_MUTE			0x24 /* Mute */
> +/* ECI Outputs */
> +#define ECI_CRM				0x82 /* Car Radio Mute */
> +#define ECI_PWR				0x83 /* Power */
> +#define ECI_AUD_AMP			0x85 /* Audio Amplifier */
> +#define ECI_EXT_AUD_SWITCH		0x86 /* External Audio Switch */
> +#define ECI_HANDSET_AUDIO		0x87 /* Handset Audio */
> +#define ECI_RING_INDICATOR		0x88 /* Ringing Indicator */
> +#define ECI_CALL_ACTIVE			0x89 /* Call Active */
> +#define ECI_ENHANCEMENT_DETECTED	0x8b /* Enhancement Detected */
> +#define ECI_AUDIO_BLOCK_IN_USE		0x8e /* Audio Block In Use */
> +#define ECI_STEREO_AUDIO_ACTIVE		0x8f /* stereo audio used in terminal */
> +#define ECI_MONO_AUDIO_ACTIVE		0x90 /* mono audio used in terminal */
> +#define ECI_TERMINAL_APP_CTRL_OUT	0x91 /* Terminal Applicat Ctrl Output */
> +
> +/*
> + * Most of these are key events.
> + * Switch event codes are put on top of keys (KEY_MAX ->)
> + */
> +static int eci_codes[] = {
> +	KEY_UNKNOWN,		/* 0  ECI_NIL_FEATURE */
> +	KEY_UNKNOWN,		/* 1  ECI_IGNS */
> +	KEY_UNKNOWN,		/* 2  ECI_CK_HANDSET_HOOK */
> +	KEY_BATTERY,		/* 3  ECI_POWER_SUPPLY */
> +	KEY_RESERVED,		/* 4  ECI feature not defined */
> +	KEY_RESERVED,		/* 5  ECI feature not defined */
> +	KEY_AUDIO,		/* 6  ECI_EXT_AUD_IN */
> +	KEY_PHONE,		/* 7  ECI_SEND_END_VR */
> +	KEY_MAX + SW_HEADPHONE_INSERT,	/* 8  ECI_HD_PLUG, type switchs */
> +	KEY_RESERVED,		/* 9  ECI feature not defined */
> +	KEY_UNKNOWN,		/* 10 ECI_DEV_POWER_REQ */
> +	KEY_VOLUMEUP,		/* 11 ECI_VOL_UP */
> +	KEY_VOLUMEDOWN,		/* 12 ECI_VOL_DOWN */
> +	KEY_PLAYPAUSE,		/* 13 ECI_PLAY_PAUSE_CTRL */
> +	KEY_STOP,		/* 14 ECI_STOP */
> +	KEY_FORWARD,		/* 15 ECI_NEXT_FF_AUTOSRC_UP */
> +	KEY_REWIND,		/* 16 ECI_PREV_REW_AUTOSEARCH_DOWN */
> +	KEY_UNKNOWN,		/* 17 ECI_POC */
> +	KEY_RESERVED,		/* 18 ECI feature not defined */
> +	KEY_RESERVED,		/* 19 ECI feature not defined */
> +	KEY_UNKNOWN,		/* 20 ECI_SYNC_BTN */
> +	KEY_RADIO,		/* 21 ECI_MUSIC_RADIO_OFF_SELECTOR */
> +	KEY_UNKNOWN,		/* 22 ECI_REDIAL */
> +	KEY_UNKNOWN,		/* 23 ECI_LEFT_SOFT_KEY */
> +	KEY_UNKNOWN,		/* 24 ECI_RIGHT_SOFT_KEY */
> +	KEY_SEND,		/* 25 ECI_SEND_KEY */
> +	KEY_END,		/* 26 ECI_END_KEY */
> +	KEY_UNKNOWN,		/* 27 ECI_MIDDLE_SOFT_KEY */
> +	KEY_UP,			/* 28 ECI_UP */
> +	KEY_DOWN,		/* 29 ECI_DOWN */
> +	KEY_RIGHT,		/* 30 ECI_RIGHT */
> +	KEY_LEFT,		/* 31 ECI_LEFT */
> +	KEY_UNKNOWN,		/* 32 ECI_SYMBIAN_NAVY_KEY */
> +	KEY_UNKNOWN,		/* 33 ECI_TERMINAL_APP_CTRL_IN */
> +	KEY_RESERVED,		/* 34 ECI feature not defined */
> +	KEY_UNKNOWN,		/* 35 ECI_USB_CLASS_SWITCHING */
> +	KEY_MUTE,		/* 36 ECI_MUTE */
> +};

Consider switching to sparse keymap library. You won't be needing ugly
KEY_MAX + SW_XXX hacks and it will also support remapping keys from
userspace.

> +
> +/* ECI accessory register's bits */
> +#define ECI_MIC_AUTO	0x00
> +#define ECI_MIC_OFF	0x5a
> +#define ECI_MIC_ON	0xff
> +
> +#define ECI_INT_ENABLE		1
> +#define ECI_INT_DELAY_ENABLE	(1<<1)
> +#define ECI_INT_LEN_76MS	0
> +#define ECI_INT_LEN_82MS	(1<<5)
> +#define ECI_INT_LEN_37MS	(2<<5)
> +#define ECI_INT_LEN_19MS	(3<<5)
> +#define ECI_INT_LEN_10MS	(4<<5)
> +#define ECI_INT_LEN_5MS		(5<<5)
> +#define ECI_INT_LEN_2MS		(6<<5)
> +#define ECI_INT_LEN_120US	(7<<5)
> +
> +struct eci_mem_block {
> +	u8 id;
> +	u8 len;
> +	u16 size;
> +};
> +
> +static struct eci_cb eci_callback;
> +static struct audio_hsmic_event hsmic_event;
> +
> +static struct eci_data *the_eci;
> +
> +#ifdef CONFIG_DEBUG_FS
> +static void eci_accessory_event(int event, void *priv);
> +static void eci_hsmic_event(void *priv, bool on);
> +
> +static struct dentry *eci_debugfs_dir;
> +
> +static ssize_t mic_read(struct file *file, char __user *user_buf,
> +		size_t count, loff_t *ppos)
> +{
> +	/* Assosiated in default_open() */
> +	struct eci_data *eci = file->private_data;
> +	char buf[80], *state;
> +	int len = 0;
> +	int ret;
> +
> +	/* Do not run twice */
> +	if (*ppos == 0) {
> +		ret = eci->eci_hw_ops->acc_read_reg(ECICMD_MIC_CTRL, buf, 1);
> +		if (ret)
> +			return ret;
> +
> +		eci->mic_state = buf[0];
> +		switch (eci->mic_state) {
> +		case ECI_MIC_AUTO:
> +			state = "auto";
> +			break;
> +		case ECI_MIC_OFF:
> +			state = "off";
> +			break;
> +		case ECI_MIC_ON:
> +			state = "on";
> +			break;
> +		default:
> +			state = "unknown";
> +			break;
> +		}
> +
> +		len = snprintf(buf, sizeof(buf), "microphone %s\n", state);
> +	}
> +
> +	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
> +
> +	return ret;
> +}
> +
> +static ssize_t mic_write(struct file *file, const char __user *user_buf,
> +		size_t count, loff_t *ppos)
> +{
> +	/* Assosiated in default_open() */
> +	struct eci_data *eci = file->private_data;
> +	char buf[80];
> +	int buf_size;
> +
> +	buf_size = min(count, (sizeof(buf) - 1));
> +	if (copy_from_user(buf, user_buf, buf_size))
> +		return -EFAULT;
> +
> +	if (!memcmp(buf, "auto", 4))
> +		eci_hsmic_event(eci, true);
> +	else if (!memcmp(buf, "off", 3))
> +		eci_hsmic_event(eci, false);
> +	else if (!memcmp(buf, "on", 2)) {
> +		eci->mic_state = ECI_MIC_ON;
> +		if (eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL,
> +					eci->mic_state))
> +			dev_err(eci->dev, "Unable to control headset"
> +					"microphone\n");

Do not break strings on 80 column boundaries, wither align the arguments
differently or just go past 80 columns, like this:

			dev_err(eci->dev,
				"Unable to control headset microphone\n");

> +	}
> +
> +	return count;
> +}
> +
> +static int default_open(struct inode *inode, struct file *file)
> +{
> +	/* Assosiated in debugfs_create_file() */
> +	if (inode->i_private)
> +		file->private_data = inode->i_private;
> +
> +	return 0;
> +}
> +
> +static const struct file_operations mic_fops = {
> +	.open		= default_open,
> +	.read		= mic_read,
> +	.write		= mic_write,
> +};
> +
> +static void eci_uninitialize_debugfs(void)
> +{
> +	if (eci_debugfs_dir)
> +		debugfs_remove_recursive(eci_debugfs_dir);
> +}
> +
> +static long eci_initialize_debugfs(struct eci_data *eci)
> +{
> +	void *ok;
> +
> +	/* /sys/kernel/debug/ECI_accessory.# */
> +	eci_debugfs_dir = debugfs_create_dir(dev_name(eci->dev), NULL);
> +	if (!eci_debugfs_dir)
> +		return -ENOENT;
> +
> +	/* Struct eci assosiated to inode->i_private */
> +	ok = debugfs_create_file("mic", S_IRUGO | S_IWUSR,
> +			eci_debugfs_dir, eci, &mic_fops);
> +	if (!ok)
> +		goto fail;
> +
> +	return 0;
> +fail:
> +	eci_uninitialize_debugfs();
> +	return -ENOENT;
> +}
> +#else
> +#define eci_initialize_debugfs(eci)	1

Should be 0 I think, and not have a parameter, preferably

static inline int eci_initialize_debugfs(void)
{
	return 0;
}

> +#define eci_uninitialize_debugfs()
> +#endif
> +
> +/* Returns size of accessory memory or error */
> +static int eci_get_ekey(struct eci_data *eci, int *key)
> +{
> +	u8 buf[4];
> +	struct eci_mem_block *ekey = (void *)buf;
> +	int ret;
> +
> +	/* Read always four bytes */
> +	ret = eci->eci_hw_ops->acc_read_direct(0, buf);
> +
> +	if (ret)
> +		return ret;
> +
> +	if (ekey->id != ECI_EKEY_BLOCK_ID)
> +		return -ENODEV;
> +
> +	*key = cpu_to_be16(ekey->size);

cpu_to_be16()??? This looks really wierd.

> +
> +	return 0;
> +}
> +
> +static ssize_t show_eci_memory(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	if (!the_eci->mem_ok)
> +		return -ENXIO;
> +
> +	memcpy(buf, the_eci->memory, the_eci->mem_size);
> +
> +	return the_eci->mem_size;
> +}
> +
> +static ssize_t show_cable_plugged(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct eci_data *eci = dev_get_drvdata(dev);
> +
> +	return snprintf(buf, sizeof(buf), "Cable plugged %s\n",
> +				eci->plugged ? "in" : "out");

Should it be dsimple 0/1? How is this attribute supposed to be used?

> +}
> +
> +static ssize_t store_cable_plugged(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf, size_t len)
> +{
> +	struct eci_data *eci = dev_get_drvdata(dev);
> +
> +	if (!memcmp(buf, "in", 2)) {
> +		eci->plugged = true;
> +		eci_accessory_event(ECI_EVENT_PLUG_IN, eci);
> +	} else if (!memcmp(buf, "out", 3)) {
> +		eci->plugged = false;
> +		eci_accessory_event(ECI_EVENT_PLUG_OUT, eci);
> +	}

Same here. Should it be in debugfs probably?

> +
> +	return len;
> +}
> +
> +static DEVICE_ATTR(memory, S_IRUGO, show_eci_memory, NULL);
> +static DEVICE_ATTR(cable, S_IRUGO | S_IWUSR , show_cable_plugged,
> +		store_cable_plugged);
> +
> +static struct attribute *eci_attributes[] = {
> +	&dev_attr_memory.attr,
> +	&dev_attr_cable.attr,
> +	NULL
> +};
> +
> +static struct attribute_group eci_attr_group = {
> +	.attrs = eci_attributes
> +};
> +
> +/* Read ECI device memory into buffer */
> +static int eci_get_memory(struct eci_data *eci, int *restart)
> +{
> +	int i, ret;
> +
> +	for (i = *restart; i < eci->mem_size; i += 4) {
> +		ret = eci->eci_hw_ops->acc_read_direct(i, eci->memory + i);
> +		*restart = i;
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * This should be really init_features, but most oftens these are just buttons
> + */
> +static int eci_init_buttons(struct eci_data *eci)
> +{
> +	struct enchancement_features_fixed *eff = eci->e_features_fix;
> +	u8 n, mireg;
> +	int ret;
> +	u8 buf[4];
> +
> +	n = eff->number_of_features;
> +
> +	if (n > ECI_MAX_FEATURE_COUNT)
> +		return -EINVAL;
> +
> +	ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL, ECI_MIC_OFF);
> +	if (ret)
> +		return ret;
> +
> +	ret = eci->eci_hw_ops->acc_read_reg(ECICMD_MASTER_INT_REG, buf, 1);
> +	if (ret)
> +		return ret;
> +
> +	mireg = buf[0];
> +	mireg &= ~ECI_INT_ENABLE;
> +	mireg |= ECI_INT_LEN_120US | ECI_INT_DELAY_ENABLE;
> +
> +	ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MASTER_INT_REG, mireg);
> +	if (ret)
> +		return ret;
> +
> +	msleep(ECI_WAIT_BUS_SETTLE);
> +	mireg |= ECI_INT_ENABLE;
> +	ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MASTER_INT_REG, mireg);
> +	if (ret)
> +		return ret;
> +
> +	msleep(ECI_WAIT_BUS_SETTLE);
> +
> +	return ret;
> +}
> +
> +/* Find "enchangement features" block from buffer */
> +static int eci_get_enchancement_features(struct eci_data *eci)
> +{
> +	u8 *mem = (void *)eci->memory;
> +	struct eci_mem_block *b = (void *)mem;
> +	struct eci_mem_block *mem_end = (void *)(eci->memory + eci->mem_size);
> +
> +	if (b->id != ECI_EKEY_BLOCK_ID)
> +		return -ENODEV;
> +
> +	do {
> +		dev_dbg(eci->dev, "skip BLOCK 0x%02x, LEN 0x%02x\n",
> +				b->id, b->len);
> +		if (!b->len)
> +			return -EINVAL;
> +
> +		mem += b->len;
> +		b = (void *)mem;
> +		eci->e_features_fix = (void *)b;
> +		dev_dbg(eci->dev, "found BLOCK 0x%02x, LEN 0x%02x\n",
> +				b->id, b->len);
> +		if (b->id == ECI_ENHANCEMENT_FEATURE_BLOCK_ID)
> +			return 0;
> +	} while (b < mem_end);
> +
> +	return -ENFILE;
> +}
> +
> +/*
> + * Find out ECI features.
> + * All ECI memory block parsing are done here, be carefull as
> + * pointers to memory tend to go wrong easily.
> + * ECI "Enhancement Features block has variable size, so we try to
> + * catch pointers out of block due memory reading errors etc.
> + *
> + * I/O support field is not implemented.
> + * Data direction field is not implemented, nor writing to the ECI I/O
> + */
> +static int eci_parse_enchancement_features(struct eci_data *eci)
> +{
> +	struct enchancement_features_fixed *eff = eci->e_features_fix;
> +	struct enchancement_features_variable *efv = &eci->e_features_var;
> +	int i;
> +	u8 n, k;
> +	void *mem_end = (void *)((u8 *)eff + eff->length);
> +
> +	dev_dbg(eci->dev, "block id 0x%02x length 0x%02x connector "
> +			"configuration 0x%02x\n", eff->block_id, eff->length,
> +			eff->connector_conf);
> +	n = eff->number_of_features;
> +	dev_dbg(eci->dev, "number of features %d\n", n);
> +
> +	if (n > ECI_MAX_FEATURE_COUNT)
> +		return -EINVAL;
> +
> +	k = DIV_ROUND_UP(n, 8);
> +	dev_dbg(eci->dev, "I/O support bytes count %d\n", k);
> +
> +	efv->io_support = &eff->number_of_features + 1;
> +	/* efv->io_functionality[0] is not used! pins are in 1..31 range */
> +	efv->io_functionality = efv->io_support + k - 1;
> +	efv->active_state = efv->io_functionality + n + 1;
> +
> +	if ((void *)&efv->active_state[k] > mem_end)
> +		return -EINVAL;
> +
> +	/* Last part of block */
> +	for (i = 0; i < k; i++)
> +		dev_dbg(eci->dev, "active_state[%d] 0x%02x\n", i,
> +				efv->active_state[i]);
> +
> +	eci->buttons_data.buttons_up_mask =
> +		~(u32)(cpu_to_le32(*(u32 *)efv->active_state));
> +
> +	/*
> +	 * ECI accessory responces as many bytes needed for used I/O pins
> +	 * up to four bytes, when lines 24..31 are used
> +	 * all tested ECI accessories how ever return two data bytes
> +	 * event though there are less than eight I/O pins
> +	 *
> +	 * so we get alway reading error if there are less than eight I/Os
> +	 * meanwhile just use this kludge, FIXME
> +	 */
> +	k = DIV_ROUND_UP(n + 1, 8);
> +	if (k == 1)
> +		k = 2;
> +	eci->port_reg_count = k;
> +
> +	return 0;
> +}
> +
> +static int eci_init_accessory(struct eci_data *eci)
> +{
> +	int ret, key = 0, restart = 0;
> +	unsigned long future;
> +
> +	eci->mem_ok = false;
> +
> +	if (!eci->eci_hw_ops)
> +		return -ENXIO;
> +
> +	ret = eci->eci_hw_ops->acc_reset();
> +	if (ret)
> +		return ret;
> +
> +	msleep(ECI_WAIT_BUS_SETTLE);
> +
> +	ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL, ECI_MIC_OFF);
> +	if (ret)
> +		return ret;
> +
> +	/* Get ECI ekey block to determine memory size */
> +	future = jiffies + msecs_to_jiffies(ECI_TRY_GET_MEMORY);
> +	do {
> +		ret = eci_get_ekey(eci, &key);
> +		if (time_is_before_jiffies(future))
> +			break;
> +	} while (ret);
> +
> +	if (ret)
> +		return ret;
> +
> +	eci->mem_size = key;
> +	if (eci->mem_size > ECI_MAX_MEM_SIZE)
> +		return -EINVAL;
> +
> +	/* Get ECI memory */
> +	future = jiffies + msecs_to_jiffies(ECI_TRY_GET_MEMORY);
> +	do {
> +		ret = eci_get_memory(eci, &restart);
> +		if (time_is_before_jiffies(future))
> +			break;
> +	} while (ret);
> +
> +	if (ret)
> +		return ret;
> +
> +	if (eci_get_enchancement_features(eci))
> +		return -EIO;
> +
> +	if (eci_parse_enchancement_features(eci))
> +		return -EIO;
> +
> +	/*
> +	 * Configure ECI buttons now as we have after parsed
> +	 * enchancement features table
> +	 */

I do not understand this comment.

> +	msleep(ECI_WAIT_BUS_SETTLE);
> +	future = jiffies + msecs_to_jiffies(ECI_TRY_INIT_IO);
> +	do {
> +		ret = eci_init_buttons(eci);
> +		if (time_is_before_jiffies(future))
> +			break;
> +	} while (ret);
> +
> +	if (ret)
> +		return ret;
> +
> +	eci->mem_ok = true;
> +	msleep(ECI_WAIT_BUS_SETTLE);
> +
> +	if (eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL, eci->mic_state))
> +		dev_err(eci->dev, "Unable to control headset microphone\n");
> +
> +	return 0;
> +}
> +
> +static int init_accessory_input(struct eci_data *eci)
> +{
> +	int err, i, code;
> +
> +	eci->acc_input = input_allocate_device();
> +	if (!eci->acc_input) {
> +		dev_err(eci->dev, "Error allocating input device: %d\n",
> +				__LINE__);
> +		return -ENOMEM;
> +	}
> +
> +	eci->acc_input->name = "ECI Accessory";
> +
> +	/* Codes on top of KEY_MAX are switch events */
> +	for (i = 0; i < ARRAY_SIZE(eci_codes); i++) {
> +		code = eci_codes[i];
> +		if (code >= KEY_MAX) {
> +			code -= KEY_MAX;
> +			set_bit(code, eci->acc_input->swbit);
> +		} else {
> +			set_bit(code, eci->acc_input->keybit);
> +		}
> +	}
> +
> +	set_bit(EV_KEY, eci->acc_input->evbit);
> +	set_bit(EV_SW, eci->acc_input->evbit);
> +	set_bit(EV_REP, eci->acc_input->evbit);

__set_bit(), no neded to lock bus.

> +
> +	err = input_register_device(eci->acc_input);
> +	if (err) {
> +		dev_err(eci->dev, "Error registering input device: %d\n",
> +				__LINE__);
> +		goto err_free_dev;
> +	}
> +
> +	/* Must set after input_register_device() to take effect */
> +	eci->acc_input->rep[REP_PERIOD] = ECI_KEY_REPEAT_INTERVAL;
> +
> +	return 0;
> +
> +err_free_dev:
> +	input_free_device(eci->acc_input);
> +	return err;
> +}
> +
> +static void remove_accessory_input(struct eci_data *eci)
> +{
> +	input_unregister_device(eci->acc_input);
> +}
> +
> +/* Press/release ccessory button(s) */
> +static int eci_get_button(struct eci_data *eci)
> +{
> +	struct enchancement_features_fixed *eff = eci->e_features_fix;
> +	struct eci_buttons_data *b = &eci->buttons_data;
> +
> +	if (!eci->mem_ok)
> +		return -ENXIO;
> +
> +	if (((b->buttons & 0x0000ffff) == 0) && (eff->number_of_features > 2)) {
> +		dev_err(eci->dev, "ECI report all buttons down, rejected %d\n",
> +			       __LINE__);
> +		return -EINVAL;
> +	}
> +
> +	if (b->windex < ECI_BUTTON_BUF_SIZE) {
> +		if (b->buttons_buf[b->windex] == 0)
> +			b->buttons_buf[b->windex] = b->buttons;
> +		else
> +			dev_err(eci->dev, "ECI button queue owerflow %d\n",
> +			       __LINE__);
> +	}
> +	b->windex++;
> +	if (b->windex == ECI_BUTTON_BUF_SIZE)
> +		b->windex = 0;
> +
> +	return 0;
> +}
> +
> +/* Intended to use ONLY inside eci_parse_button() ! */
> +#define ACTIVE_STATE(x) (u32)(cpu_to_le32(*(u32 *)efv->active_state) & BIT(x-1))
> +#define BUTTON_STATE(x) ((buttons & BIT(x))>>1)
> +
> +static int eci_parse_button(struct eci_data *eci, u32 buttons)
> +{
> +	int pin, code, state;
> +	u8 n, io_fun;
> +	struct enchancement_features_variable *efv = &eci->e_features_var;
> +	struct enchancement_features_fixed *eff = eci->e_features_fix;
> +
> +	if (!eci->mem_ok)
> +		return -ENXIO;
> +
> +	n = eff->number_of_features;
> +
> +	for (pin = 1; pin <= n; pin++) {
> +		io_fun = efv->io_functionality[pin] & ~BIT(7);
> +		if (io_fun > ECI_MUTE)
> +			break;
> +		code = eci_codes[io_fun];
> +		state = (BUTTON_STATE(pin) == ACTIVE_STATE(pin));
> +		if (state)
> +			dev_dbg(eci->dev, "I/O functionality 0x%02x\n", io_fun);
> +		if (code >= KEY_MAX)
> +			input_report_switch(eci->acc_input, code - KEY_MAX,
> +					state);
> +		else
> +			input_report_key(eci->acc_input, code, state);
> +	}
> +	input_sync(eci->acc_input);
> +
> +	return 0;
> +}
> +
> +static int eci_send_button(struct eci_data *eci)
> +{
> +	int i;
> +	struct enchancement_features_fixed *eff = eci->e_features_fix;
> +	struct eci_buttons_data *b = &eci->buttons_data;
> +	u8 n;
> +
> +	if (!eci->mem_ok)
> +		return -ENXIO;
> +
> +	n = eff->number_of_features;
> +
> +	if (n > ECI_MAX_FEATURE_COUNT)
> +		return -EINVAL;
> +	/*
> +	 * Codes on top of KEY_MAX are switch events.
> +	 * Let input system take care multiple key events
> +	 */
> +	for (i = 0; i < ECI_BUTTON_BUF_SIZE; i++) {
> +		if (b->buttons_buf[b->rindex] == 0)
> +			break;
> +
> +		if (eci_parse_button(eci, b->buttons_buf[b->rindex]))
> +			return -ENXIO;
> +
> +		b->buttons_buf[b->rindex] = 0;
> +		b->rindex++;
> +		if (b->rindex == ECI_BUTTON_BUF_SIZE)
> +			b->rindex = 0;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Other driver(s) can call this after registering themselves using
> + * eci_register()
> + */
> +static void eci_accessory_event(int event, void *priv)
> +{
> +	struct eci_data *eci = priv;
> +	struct eci_buttons_data *b = &eci->buttons_data;
> +	int delay = 0;
> +	int ret = 0;
> +
> +	eci->event = event;
> +	switch (event) {
> +	case ECI_EVENT_IS_ECI:
> +		eci->is_eci = true;
> +		ret = eci->eci_hw_ops->acc_reset();
> +		if (ret)
> +			eci->is_eci = false;
> +		break;
> +	case ECI_EVENT_PLUG_IN:
> +		eci->first_event = true;
> +		ret = eci_init_accessory(eci);
> +		if (ret < 0)
> +			ret = eci_init_accessory(eci);
> +		if (ret) {
> +			dev_err(eci->dev, "Accessory init %s%s%s%sat: %d\n",
> +					ret & ACI_COMMERR ? "COMMERR " : "",
> +					ret & ACI_FRAERR ? "FRAERR " : "",
> +					ret & ACI_RESERR ? "RESERR " : "",
> +					ret & ACI_COLL ? "COLLERR " : "",
> +					__LINE__);
> +			break;
> +		}
> +		break;
> +	case ECI_EVENT_PLUG_OUT:
> +		eci->mem_ok = false;
> +		break;
> +	case ECI_EVENT_BUTTON:
> +		/*
> +		 * First event might not be valid due plug insertion
> +		 * so we filter it out if it seems garbage
> +		 */
> +		if (eci->first_event) {
> +			eci->first_event = false;
> +			if ((b->buttons & 0xff) == 0)
> +				break;
> +		}
> +		eci_get_button(eci);
> +		delay = msecs_to_jiffies(ECI_WAIT_SEND_BUTTON);
> +		schedule_delayed_work(&eci->eci_ws, delay);
> +		break;
> +	default:
> +		dev_err(eci->dev, "unknown event %d: %d\n", event, __LINE__);
> +		break;
> +	}
> +
> +	return;
> +}
> +
> +static void eci_hsmic_event(void *priv, bool on)
> +{
> +	struct eci_data *eci = priv;
> +	unsigned long future;
> +	int ret;
> +
> +	if (!eci)
> +		return;
> +
> +	if (on)
> +		eci->mic_state = ECI_MIC_AUTO;
> +	else
> +		eci->mic_state = ECI_MIC_OFF;
> +
> +	future = jiffies + msecs_to_jiffies(ECI_TRY_SET_MIC);
> +	do {
> +		ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL,
> +				eci->mic_state);
> +		if (time_is_before_jiffies(future))
> +			break;
> +	} while (ret);
> +
> +	if (ret)
> +		dev_err(eci->dev, "Unable to control headset microphone\n");
> +}
> +
> +/* General work func (eci_ws) for several tasks */
> +static void eci_work(struct work_struct *ws)
> +{
> +	struct eci_data *eci;
> +	int ret;
> +
> +	eci = container_of((struct delayed_work *)ws, struct eci_data,
> +			eci_ws);
> +
> +	ret = eci_send_button(eci);
> +	if (ret)
> +		dev_err(eci->dev, "Error sending event: %d\n", __LINE__);
> +}
> +
> +static struct miscdevice eci_device = {
> +	.minor = MISC_DYNAMIC_MINOR,
> +	.name = ECI_DRIVERNAME,
> +};

What does this device do?

> +
> +struct eci_cb *eci_register(struct eci_hw_ops *eci_ops)
> +{
> +	if (!the_eci)
> +		return ERR_PTR(-EBUSY);
> +
> +	if (!eci_ops || !eci_ops->acc_read_direct ||
> +			!eci_ops->acc_read_reg || !eci_ops->acc_write_reg ||
> +			!eci_ops->acc_reset)
> +		return ERR_PTR(-EINVAL);
> +
> +	the_eci->eci_hw_ops = eci_ops;
> +
> +	return &eci_callback;
> +}
> +EXPORT_SYMBOL(eci_register);
> +
> +static int __init eci_probe(struct platform_device *pdev)
> +{
> +	struct eci_data *eci;
> +	struct eci_platform_data *pdata = pdev->dev.platform_data;
> +	int ret;
> +
> +	eci = kzalloc(sizeof(*eci), GFP_KERNEL);
> +	if (!eci)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, eci);
> +	eci->dev = &pdev->dev;
> +
> +	ret = misc_register(&eci_device);
> +	if (ret) {
> +		dev_err(eci->dev, "could not register misc_device: %d\n",
> +				__LINE__);
> +		goto err_misc;
> +	}
> +
> +	the_eci = eci;
> +
> +	eci_callback.event              = eci_accessory_event;
> +	eci_callback.priv               = eci;
> +
> +	ret = sysfs_create_group(&pdev->dev.kobj, &eci_attr_group);
> +	if (ret) {
> +		dev_err(eci->dev, "could not create sysfs entries: %d\n",
> +				__LINE__);
> +		goto err_sysfs;
> +	}
> +
> +	ret = eci_initialize_debugfs(eci);
> +	if (ret)
> +		dev_err(eci->dev, "could not create debugfs entries: %d\n",
> +				__LINE__);
> +
> +	ret = init_accessory_input(eci);
> +	if (ret) {
> +		dev_err(eci->dev, "ERROR initializing accessory input: %d\n",
> +				__LINE__);
> +		goto err_input;
> +	}
> +
> +	/*
> +	 * If platform machine has audio driver providing
> +	 * register_hsmic_event_cb, we should give accessory microphone control,
> +	 * ie. eci_hsmic_event to it.
> +	 * This way audio driver get control to ECI accessory microphone and
> +	 * we can save power
> +	 */
> +	if (pdata) {
> +		if (pdata->register_hsmic_event_cb) {
> +			hsmic_event.private	= eci;
> +			hsmic_event.event	= eci_hsmic_event;
> +			pdata->register_hsmic_event_cb(&hsmic_event);
> +		}
> +	}
> +
> +	init_waitqueue_head(&eci->wait);
> +	INIT_DELAYED_WORK(&eci->eci_ws, eci_work);
> +
> +	eci->mem_ok = false;
> +	/*
> +	 * By default ECI driver leaves microphone off, to save power.
> +	 * Audio driver can set microphone on by using
> +	 * hsmic_event.event
> +	 */
> +	eci->mic_state = ECI_MIC_OFF;
> +
> +	/* Init buttons_data indexes and buffer */
> +	memset(&eci->buttons_data, 0, sizeof(struct eci_buttons_data));
> +	eci->buttons_data.buttons = 0xffffffff;
> +
> +	return 0;
> +
> +err_input:
> +	eci_uninitialize_debugfs();
> +	sysfs_remove_group(&pdev->dev.kobj, &eci_attr_group);
> +
> +err_sysfs:
> +	misc_deregister(&eci_device);
> +
> +err_misc:
> +	kfree(eci);
> +
> +	return ret;
> +}
> +
> +static int __exit eci_remove(struct platform_device *pdev)
> +{
> +	struct eci_data *eci = platform_get_drvdata(pdev);
> +	struct eci_platform_data *pdata = pdev->dev.platform_data;
> +
> +	pdata->register_hsmic_event_cb(NULL);
> +	cancel_delayed_work_sync(&eci->eci_ws);
> +	eci_uninitialize_debugfs();
> +	sysfs_remove_group(&pdev->dev.kobj, &eci_attr_group);
> +	remove_accessory_input(eci);
> +	kfree(eci);
> +	misc_deregister(&eci_device);
> +
> +	return 0;
> +}
> +
> +#ifdef	CONFIG_PM
> +
> +static int eci_suspend(struct platform_device *pdev, pm_message_t mesg)
> +{
> +	return -ENOSYS;
> +}
> +#else
> +#define	eci_suspend	NULL
> +#endif
> +
> +static struct platform_driver eci_driver = {
> +	.probe		= eci_probe,
> +	.remove		= __exit_p(eci_remove),
> +	.suspend	= eci_suspend,
> +	.driver		= {
> +		.name	= ECI_DRIVERNAME,
> +		.owner	= THIS_MODULE,
> +	},
> +};
> +
> +static int __init eci_init(void)
> +{
> +	return platform_driver_register(&eci_driver);
> +}
> +device_initcall(eci_init);
> +
> +static void __exit eci_exit(void)
> +{
> +	platform_driver_unregister(&eci_driver);
> +}
> +module_exit(eci_exit);
> +
> +MODULE_ALIAS("platform:" ECI_DRIVERNAME);
> +MODULE_AUTHOR("Nokia Corporation");
> +MODULE_DESCRIPTION("ECI accessory driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/input/eci.h b/include/linux/input/eci.h
> new file mode 100644
> index 0000000..b8be99c
> --- /dev/null
> +++ b/include/linux/input/eci.h
> @@ -0,0 +1,157 @@
> +/*
> + * This file is part of ECI (Enhancement Control Interface) driver
> + *
> + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
> + *
> + * Contact: Tapio Vihuri <tapio.vihuri@nokia.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * 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.
> + *
> + * 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 St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +#ifndef __ECI_H__
> +#define __ECI_H__
> +
> +#define ECI_MAX_MEM_SIZE	0x7c
> +#define ECI_BUTTON_BUF_SIZE	32
> +#define ECI_MAX_FEATURE_COUNT	31
> +
> +#define ACI_COMMERR	0x010
> +#define ACI_FRAERR	0x020
> +#define ACI_RESERR	0x040
> +#define ACI_COLL	0x080
> +
> +#define ECI_REAL_BUTTONS	0
> +#define ECI_FORCE_BUTTONS_UP	1
> +
> +/* fixed in ECI HW, do not change */
> +enum {
> +	ECICMD_HWID,
> +	ECICMD_SWID,
> +	ECICMD_ECI_BUS_SPEED,
> +	ECICMD_MIC_CTRL,
> +	ECICMD_MASTER_INT_REG,
> +	ECICMD_HW_CONF_MEM_ACCESS,
> +	ECICMD_EXTENDED_MEM_ACCESS,
> +	ECICMD_INDIRECT_MEM_ACCESS,
> +	ECICMD_PORT_DATA_0,
> +	ECICMD_PORT_DATA_1,
> +	ECICMD_PORT_DATA_2,
> +	ECICMD_PORT_DATA_3,
> +	ECICMD_LATCHED_PORT_DATA_0,
> +	ECICMD_LATCHED_PORT_DATA_1,
> +	ECICMD_LATCHED_PORT_DATA_2,
> +	ECICMD_LATCHED_PORT_DATA_3,
> +	ECICMD_DATA_DIR_0,
> +	ECICMD_DATA_DIR_1,
> +	ECICMD_DATA_DIR_2,
> +	ECICMD_DATA_DIR_3,
> +	ECICMD_INT_CONFIG_0_LOW,
> +	ECICMD_INT_CONFIG_0_HIGH,
> +	ECICMD_INT_CONFIG_1_LOW,
> +	ECICMD_INT_CONFIG_1_HIGH,
> +	ECICMD_INT_CONFIG_2_LOW,
> +	ECICMD_INT_CONFIG_2_HIGH,
> +	ECICMD_INT_CONFIG_3_LOW,
> +	ECICMD_INT_CONFIG_3_HIGH,
> +	/*
> +	 * 0x1c - 0x2f reserved for future
> +	 * 0x30 - 0x3d reserved
> +	 */
> +	ECICMD_EEPROM_LOCK = 0x3e,
> +	ECICMD_RESERVED,	/* 0x3f */
> +	ECIREG_STATUS,		/* 0x40 */
> +	ECIREG_READ_COUNT,	/* 0x41 */
> +	ECIREG_BUF_COUNT,	/* 0x42 */
> +	ECIREG_RST_LEARN,	/* 0x43 */
> +	/* 0x44 - 0xdf as data buffer */
> +	ECIREG_READ_DIRECT,	/* 0x44 */
> +	ECIREG_HW_ID = 0xe0,	/* 0xe0 */
> +	ECIREG_FW_ID,		/* 0xe1 */
> +	ECIREG_TEST_IN,		/* 0xe2 */
> +	ECIREG_TEST_OUT,	/* 0xe3 */
> +};
> +
> +enum {
> +	ECI_EVENT_IS_ECI,
> +	ECI_EVENT_PLUG_IN,
> +	ECI_EVENT_PLUG_OUT,
> +	ECI_EVENT_BUTTON,
> +	ECI_EVENT_NO,
> +};
> +
> +struct eci_hw_ops {
> +	int (*acc_reset)(void);
> +	int (*acc_read_direct)(u8 addr, char *buf);
> +	int (*acc_read_reg)(u8 reg, u8 *buf, int count);
> +	int (*acc_write_reg)(u8 reg, u8 param);
> +};
> +
> +struct eci_cb {
> +	void *priv;
> +	void (*event)(int event, void *priv);
> +};
> +
> +struct audio_hsmic_event {
> +	void *private;
> +	void (*event)(void *priv, bool on);
> +};
> +
> +struct eci_platform_data {
> +	void (*register_hsmic_event_cb)(struct audio_hsmic_event *);
> +};
> +
> +struct enchancement_features_fixed {
> +	u8	block_id;
> +	u8	length;
> +	u8	connector_conf;
> +	u8	number_of_features;
> +};
> +
> +struct enchancement_features_variable {
> +	u8	*io_support;
> +	u8	*io_functionality;
> +	u8	*active_state;
> +};
> +
> +struct eci_buttons_data {
> +	u32	buttons;
> +	int	windex;
> +	int	rindex;
> +	u32	buttons_up_mask;
> +	u32	buttons_buf[ECI_BUTTON_BUF_SIZE];
> +};
> +
> +struct eci_data {
> +	struct device				*dev;
> +	struct delayed_work			eci_ws;
> +	wait_queue_head_t			 wait;
> +	struct input_dev			*acc_input;
> +	int					event;
> +	bool					first_event;
> +	bool					mem_ok;
> +	u16					mem_size;
> +	u8					memory[ECI_MAX_MEM_SIZE];
> +	struct enchancement_features_fixed	*e_features_fix;
> +	struct enchancement_features_variable	e_features_var;
> +	u8					port_reg_count;
> +	struct eci_buttons_data			buttons_data;
> +	struct eci_hw_ops			*eci_hw_ops;
> +	u8					mic_state;
> +	bool					plugged;
> +	bool					is_eci;
> +};
> +
> +struct eci_cb *eci_register(struct eci_hw_ops *eci_ops);
> +#endif
> -- 
> 1.6.5
> 

Thank you.

-- 
Dmitry

WARNING: multiple messages have this Message-ID (diff)
From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
To: tapio.vihuri@nokia.com
Cc: randy.dunlap@oracle.com, linux-kernel@vger.kernel.org,
	alsa-devel@alsa-project.org, ilkka.koskinen@nokia.com,
	samu.p.onkalo@nokia.com
Subject: Re: [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver
Date: Sun, 2 Jan 2011 00:19:22 -0800	[thread overview]
Message-ID: <20110102081922.GD5429@core.coreip.homeip.net> (raw)
In-Reply-To: <1293716219-26089-2-git-send-email-tapio.vihuri@nokia.com>

Hi Tapio,

On Thu, Dec 30, 2010 at 03:36:57PM +0200, tapio.vihuri@nokia.com wrote:
> From: Tapio Vihuri <tapio.vihuri@nokia.com>
> 
> ECI stands for (Enhancement Control Interface).
> 
> ECI is better known as Multimedia Headset for Nokia phones.
> If headset has many buttons, like play, vol+, vol- etc. then it is propably
> ECI accessory.
> Among several buttons ECI accessory contains memory for storing several
> parameters.
> 
> ECI input driver provides the following features:
> - reading ECI configuration memory
> - ECI buttons as input events
> 
> Signed-off-by: Tapio Vihuri <tapio.vihuri@nokia.com>
> ---
>  drivers/input/misc/Kconfig  |   18 +
>  drivers/input/misc/Makefile |    2 +-
>  drivers/input/misc/eci.c    | 1002 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/input/eci.h   |  157 +++++++
>  4 files changed, 1178 insertions(+), 1 deletions(-)
>  create mode 100644 drivers/input/misc/eci.c
>  create mode 100644 include/linux/input/eci.h
> 
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index b99b8cb..7a15bc6 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -448,4 +448,22 @@ config INPUT_ADXL34X_SPI
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called adxl34x-spi.
>  
> +config INPUT_ECI
> +	tristate "AV ECI (Enhancement Control Interface) input driver"
> +	help
> +	The Enhancement Control Interface functionality
> +	  ECI is better known as Multimedia Headset for Nokia phones.
> +	  If headset has many buttons, like play, vol+, vol- etc. then
> +	  it is propably ECI accessory.
> +	  Among several buttons ECI accessory contains memory for storing
> +	  several parameters.
> +
> +	  ECI input driver provides the following features:
> +	  - reading ECI configuration memory
> +	  - ECI buttons as input events
> +
> +	  Say 'y' here to statically link this module into the kernel or 'm'
> +	  to build it as a dynamically loadable module. The module will be
> +	  called eci.ko
> +
>  endif
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index 1fe1f6c..99d2289 100644
> --- a/drivers/input/misc/Makefile
> +++ b/drivers/input/misc/Makefile
> @@ -42,4 +42,4 @@ obj-$(CONFIG_INPUT_WINBOND_CIR)		+= winbond-cir.o
>  obj-$(CONFIG_INPUT_WISTRON_BTNS)	+= wistron_btns.o
>  obj-$(CONFIG_INPUT_WM831X_ON)		+= wm831x-on.o
>  obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
> -
> +obj-$(CONFIG_INPUT_ECI)			+= eci.o

Please keep Makefile and Kconfig sorted alphabetically.

> diff --git a/drivers/input/misc/eci.c b/drivers/input/misc/eci.c
> new file mode 100644
> index 0000000..72efccf
> --- /dev/null
> +++ b/drivers/input/misc/eci.c
> @@ -0,0 +1,1002 @@
> +/*
> + * This file is part of ECI (Enhancement Control Interface) accessory input
> + * driver
> + *
> + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
> + *
> + * Contact: Tapio Vihuri <tapio.vihuri@nokia.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * 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.
> + *
> + * 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 St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +/*
> + * ECI stands for (Enhancement Control Interface).
> + *
> + * ECI is better known as Multimedia Headset for Nokia phones.
> + * If headset has many buttons, like play, vol+, vol- etc. then it is propably
> + * ECI accessory.
> + * Among several buttons ECI accessory contains memory for storing several
> + * parameters.
> + *
> + * ECI input driver provides the following features:
> + *  - reading ECI configuration memory
> + *  - ECI buttons as input events
> + */
> +
> +#include <linux/init.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/debugfs.h>
> +
> +#include <linux/input.h>
> +#include <linux/input/eci.h>
> +#include <linux/miscdevice.h>
> +
> +#define ECI_DRIVERNAME	"ECI_accessory"
> +
> +#define ECI_WAIT_SEND_BUTTON		5	/* ms */
> +#define ECI_WAIT_BUS_SETTLE		40	/* ms */
> +#define ECI_TRY_GET_MEMORY		2000	/* ms */
> +#define ECI_TRY_INIT_IO			200	/* ms */
> +#define ECI_TRY_SET_MIC			200	/* ms */
> +#define ECI_KEY_REPEAT_INTERVAL		400	/* ms */
> +
> +#define ECI_EKEY_BLOCK_ID			0xb3
> +#define ECI_ENHANCEMENT_FEATURE_BLOCK_ID	0x02
> +
> +/* ECI Inputs */
> +#define ECI_NIL_FEATURE			0x00 /* No feature */
> +#define ECI_IGNS			0x01 /* Ignition Sense */
> +#define ECI_CK_HANDSET_HOOK		0x02 /* Car-Kit Handset Hook */
> +#define ECI_POWER_SUPPLY		0x03 /* Power Supply/Car Battery Det  */
> +#define ECI_EXT_AUD_IN			0x06 /* External audio In */
> +#define ECI_SEND_END_VR			0x07 /* Send, End, and Voice Recogn */
> +#define ECI_HD_PLUG			0x08 /* Headphone plug */
> +#define ECI_DEV_POWER_REQ		0x0a /* Device Power Request */
> +#define ECI_VOL_UP			0x0b /* Volume Up */
> +#define ECI_VOL_DOWN			0x0c /* Volume Down */
> +#define ECI_PLAY_PAUSE_CTRL		0x0d /* Play / Pause */
> +#define ECI_STOP			0x0e /* Stop */
> +#define ECI_NEXT_FF_AUTOSRC_UP		0x0f /* Next/Fast Fward/Autosearch up */
> +#define ECI_PREV_REW_AUTOSEARCH_DOWN	0x10 /* Prev/Rewind/Autosearch down */
> +#define ECI_POC				0x11 /* Push to Talk over Cellular */
> +#define ECI_SYNC_BTN			0x14 /* Synchronization Button */
> +#define ECI_MUSIC_RADIO_OFF_SELECTOR	0x15 /* Music/Radio/Off Selector */
> +#define ECI_REDIAL			0x16 /* Redial */
> +#define ECI_LEFT_SOFT_KEY		0x17 /* Left Soft Key */
> +#define ECI_RIGHT_SOFT_KEY		0x18 /* Right Soft key */
> +#define ECI_SEND_KEY			0x19 /* Send key */
> +#define ECI_END_KEY			0x1a /* End key */
> +#define ECI_MIDDLE_SOFT_KEY		0x1b /* Middle Soft key */
> +#define ECI_UP				0x1c /* UP key/joystick direction */
> +#define ECI_DOWN			0x1d /* DOWN key/joystick direction */
> +#define ECI_RIGHT			0x1e /* RIGHT key/joystick direction */
> +#define ECI_LEFT			0x1f /* LEFT key/joystick direction */
> +#define ECI_SYMBIAN_NAVY_KEY		0x20 /* Symbian Application key */
> +#define ECI_TERMINAL_APP_CTRL_IN	0x21 /* Terminal Applicat Ctrl Input */
> +#define ECI_USB_CLASS_SWITCHING		0x23 /* USB Class Switching */
> +#define ECI_MUTE			0x24 /* Mute */
> +/* ECI Outputs */
> +#define ECI_CRM				0x82 /* Car Radio Mute */
> +#define ECI_PWR				0x83 /* Power */
> +#define ECI_AUD_AMP			0x85 /* Audio Amplifier */
> +#define ECI_EXT_AUD_SWITCH		0x86 /* External Audio Switch */
> +#define ECI_HANDSET_AUDIO		0x87 /* Handset Audio */
> +#define ECI_RING_INDICATOR		0x88 /* Ringing Indicator */
> +#define ECI_CALL_ACTIVE			0x89 /* Call Active */
> +#define ECI_ENHANCEMENT_DETECTED	0x8b /* Enhancement Detected */
> +#define ECI_AUDIO_BLOCK_IN_USE		0x8e /* Audio Block In Use */
> +#define ECI_STEREO_AUDIO_ACTIVE		0x8f /* stereo audio used in terminal */
> +#define ECI_MONO_AUDIO_ACTIVE		0x90 /* mono audio used in terminal */
> +#define ECI_TERMINAL_APP_CTRL_OUT	0x91 /* Terminal Applicat Ctrl Output */
> +
> +/*
> + * Most of these are key events.
> + * Switch event codes are put on top of keys (KEY_MAX ->)
> + */
> +static int eci_codes[] = {
> +	KEY_UNKNOWN,		/* 0  ECI_NIL_FEATURE */
> +	KEY_UNKNOWN,		/* 1  ECI_IGNS */
> +	KEY_UNKNOWN,		/* 2  ECI_CK_HANDSET_HOOK */
> +	KEY_BATTERY,		/* 3  ECI_POWER_SUPPLY */
> +	KEY_RESERVED,		/* 4  ECI feature not defined */
> +	KEY_RESERVED,		/* 5  ECI feature not defined */
> +	KEY_AUDIO,		/* 6  ECI_EXT_AUD_IN */
> +	KEY_PHONE,		/* 7  ECI_SEND_END_VR */
> +	KEY_MAX + SW_HEADPHONE_INSERT,	/* 8  ECI_HD_PLUG, type switchs */
> +	KEY_RESERVED,		/* 9  ECI feature not defined */
> +	KEY_UNKNOWN,		/* 10 ECI_DEV_POWER_REQ */
> +	KEY_VOLUMEUP,		/* 11 ECI_VOL_UP */
> +	KEY_VOLUMEDOWN,		/* 12 ECI_VOL_DOWN */
> +	KEY_PLAYPAUSE,		/* 13 ECI_PLAY_PAUSE_CTRL */
> +	KEY_STOP,		/* 14 ECI_STOP */
> +	KEY_FORWARD,		/* 15 ECI_NEXT_FF_AUTOSRC_UP */
> +	KEY_REWIND,		/* 16 ECI_PREV_REW_AUTOSEARCH_DOWN */
> +	KEY_UNKNOWN,		/* 17 ECI_POC */
> +	KEY_RESERVED,		/* 18 ECI feature not defined */
> +	KEY_RESERVED,		/* 19 ECI feature not defined */
> +	KEY_UNKNOWN,		/* 20 ECI_SYNC_BTN */
> +	KEY_RADIO,		/* 21 ECI_MUSIC_RADIO_OFF_SELECTOR */
> +	KEY_UNKNOWN,		/* 22 ECI_REDIAL */
> +	KEY_UNKNOWN,		/* 23 ECI_LEFT_SOFT_KEY */
> +	KEY_UNKNOWN,		/* 24 ECI_RIGHT_SOFT_KEY */
> +	KEY_SEND,		/* 25 ECI_SEND_KEY */
> +	KEY_END,		/* 26 ECI_END_KEY */
> +	KEY_UNKNOWN,		/* 27 ECI_MIDDLE_SOFT_KEY */
> +	KEY_UP,			/* 28 ECI_UP */
> +	KEY_DOWN,		/* 29 ECI_DOWN */
> +	KEY_RIGHT,		/* 30 ECI_RIGHT */
> +	KEY_LEFT,		/* 31 ECI_LEFT */
> +	KEY_UNKNOWN,		/* 32 ECI_SYMBIAN_NAVY_KEY */
> +	KEY_UNKNOWN,		/* 33 ECI_TERMINAL_APP_CTRL_IN */
> +	KEY_RESERVED,		/* 34 ECI feature not defined */
> +	KEY_UNKNOWN,		/* 35 ECI_USB_CLASS_SWITCHING */
> +	KEY_MUTE,		/* 36 ECI_MUTE */
> +};

Consider switching to sparse keymap library. You won't be needing ugly
KEY_MAX + SW_XXX hacks and it will also support remapping keys from
userspace.

> +
> +/* ECI accessory register's bits */
> +#define ECI_MIC_AUTO	0x00
> +#define ECI_MIC_OFF	0x5a
> +#define ECI_MIC_ON	0xff
> +
> +#define ECI_INT_ENABLE		1
> +#define ECI_INT_DELAY_ENABLE	(1<<1)
> +#define ECI_INT_LEN_76MS	0
> +#define ECI_INT_LEN_82MS	(1<<5)
> +#define ECI_INT_LEN_37MS	(2<<5)
> +#define ECI_INT_LEN_19MS	(3<<5)
> +#define ECI_INT_LEN_10MS	(4<<5)
> +#define ECI_INT_LEN_5MS		(5<<5)
> +#define ECI_INT_LEN_2MS		(6<<5)
> +#define ECI_INT_LEN_120US	(7<<5)
> +
> +struct eci_mem_block {
> +	u8 id;
> +	u8 len;
> +	u16 size;
> +};
> +
> +static struct eci_cb eci_callback;
> +static struct audio_hsmic_event hsmic_event;
> +
> +static struct eci_data *the_eci;
> +
> +#ifdef CONFIG_DEBUG_FS
> +static void eci_accessory_event(int event, void *priv);
> +static void eci_hsmic_event(void *priv, bool on);
> +
> +static struct dentry *eci_debugfs_dir;
> +
> +static ssize_t mic_read(struct file *file, char __user *user_buf,
> +		size_t count, loff_t *ppos)
> +{
> +	/* Assosiated in default_open() */
> +	struct eci_data *eci = file->private_data;
> +	char buf[80], *state;
> +	int len = 0;
> +	int ret;
> +
> +	/* Do not run twice */
> +	if (*ppos == 0) {
> +		ret = eci->eci_hw_ops->acc_read_reg(ECICMD_MIC_CTRL, buf, 1);
> +		if (ret)
> +			return ret;
> +
> +		eci->mic_state = buf[0];
> +		switch (eci->mic_state) {
> +		case ECI_MIC_AUTO:
> +			state = "auto";
> +			break;
> +		case ECI_MIC_OFF:
> +			state = "off";
> +			break;
> +		case ECI_MIC_ON:
> +			state = "on";
> +			break;
> +		default:
> +			state = "unknown";
> +			break;
> +		}
> +
> +		len = snprintf(buf, sizeof(buf), "microphone %s\n", state);
> +	}
> +
> +	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
> +
> +	return ret;
> +}
> +
> +static ssize_t mic_write(struct file *file, const char __user *user_buf,
> +		size_t count, loff_t *ppos)
> +{
> +	/* Assosiated in default_open() */
> +	struct eci_data *eci = file->private_data;
> +	char buf[80];
> +	int buf_size;
> +
> +	buf_size = min(count, (sizeof(buf) - 1));
> +	if (copy_from_user(buf, user_buf, buf_size))
> +		return -EFAULT;
> +
> +	if (!memcmp(buf, "auto", 4))
> +		eci_hsmic_event(eci, true);
> +	else if (!memcmp(buf, "off", 3))
> +		eci_hsmic_event(eci, false);
> +	else if (!memcmp(buf, "on", 2)) {
> +		eci->mic_state = ECI_MIC_ON;
> +		if (eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL,
> +					eci->mic_state))
> +			dev_err(eci->dev, "Unable to control headset"
> +					"microphone\n");

Do not break strings on 80 column boundaries, wither align the arguments
differently or just go past 80 columns, like this:

			dev_err(eci->dev,
				"Unable to control headset microphone\n");

> +	}
> +
> +	return count;
> +}
> +
> +static int default_open(struct inode *inode, struct file *file)
> +{
> +	/* Assosiated in debugfs_create_file() */
> +	if (inode->i_private)
> +		file->private_data = inode->i_private;
> +
> +	return 0;
> +}
> +
> +static const struct file_operations mic_fops = {
> +	.open		= default_open,
> +	.read		= mic_read,
> +	.write		= mic_write,
> +};
> +
> +static void eci_uninitialize_debugfs(void)
> +{
> +	if (eci_debugfs_dir)
> +		debugfs_remove_recursive(eci_debugfs_dir);
> +}
> +
> +static long eci_initialize_debugfs(struct eci_data *eci)
> +{
> +	void *ok;
> +
> +	/* /sys/kernel/debug/ECI_accessory.# */
> +	eci_debugfs_dir = debugfs_create_dir(dev_name(eci->dev), NULL);
> +	if (!eci_debugfs_dir)
> +		return -ENOENT;
> +
> +	/* Struct eci assosiated to inode->i_private */
> +	ok = debugfs_create_file("mic", S_IRUGO | S_IWUSR,
> +			eci_debugfs_dir, eci, &mic_fops);
> +	if (!ok)
> +		goto fail;
> +
> +	return 0;
> +fail:
> +	eci_uninitialize_debugfs();
> +	return -ENOENT;
> +}
> +#else
> +#define eci_initialize_debugfs(eci)	1

Should be 0 I think, and not have a parameter, preferably

static inline int eci_initialize_debugfs(void)
{
	return 0;
}

> +#define eci_uninitialize_debugfs()
> +#endif
> +
> +/* Returns size of accessory memory or error */
> +static int eci_get_ekey(struct eci_data *eci, int *key)
> +{
> +	u8 buf[4];
> +	struct eci_mem_block *ekey = (void *)buf;
> +	int ret;
> +
> +	/* Read always four bytes */
> +	ret = eci->eci_hw_ops->acc_read_direct(0, buf);
> +
> +	if (ret)
> +		return ret;
> +
> +	if (ekey->id != ECI_EKEY_BLOCK_ID)
> +		return -ENODEV;
> +
> +	*key = cpu_to_be16(ekey->size);

cpu_to_be16()??? This looks really wierd.

> +
> +	return 0;
> +}
> +
> +static ssize_t show_eci_memory(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	if (!the_eci->mem_ok)
> +		return -ENXIO;
> +
> +	memcpy(buf, the_eci->memory, the_eci->mem_size);
> +
> +	return the_eci->mem_size;
> +}
> +
> +static ssize_t show_cable_plugged(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct eci_data *eci = dev_get_drvdata(dev);
> +
> +	return snprintf(buf, sizeof(buf), "Cable plugged %s\n",
> +				eci->plugged ? "in" : "out");

Should it be dsimple 0/1? How is this attribute supposed to be used?

> +}
> +
> +static ssize_t store_cable_plugged(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf, size_t len)
> +{
> +	struct eci_data *eci = dev_get_drvdata(dev);
> +
> +	if (!memcmp(buf, "in", 2)) {
> +		eci->plugged = true;
> +		eci_accessory_event(ECI_EVENT_PLUG_IN, eci);
> +	} else if (!memcmp(buf, "out", 3)) {
> +		eci->plugged = false;
> +		eci_accessory_event(ECI_EVENT_PLUG_OUT, eci);
> +	}

Same here. Should it be in debugfs probably?

> +
> +	return len;
> +}
> +
> +static DEVICE_ATTR(memory, S_IRUGO, show_eci_memory, NULL);
> +static DEVICE_ATTR(cable, S_IRUGO | S_IWUSR , show_cable_plugged,
> +		store_cable_plugged);
> +
> +static struct attribute *eci_attributes[] = {
> +	&dev_attr_memory.attr,
> +	&dev_attr_cable.attr,
> +	NULL
> +};
> +
> +static struct attribute_group eci_attr_group = {
> +	.attrs = eci_attributes
> +};
> +
> +/* Read ECI device memory into buffer */
> +static int eci_get_memory(struct eci_data *eci, int *restart)
> +{
> +	int i, ret;
> +
> +	for (i = *restart; i < eci->mem_size; i += 4) {
> +		ret = eci->eci_hw_ops->acc_read_direct(i, eci->memory + i);
> +		*restart = i;
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * This should be really init_features, but most oftens these are just buttons
> + */
> +static int eci_init_buttons(struct eci_data *eci)
> +{
> +	struct enchancement_features_fixed *eff = eci->e_features_fix;
> +	u8 n, mireg;
> +	int ret;
> +	u8 buf[4];
> +
> +	n = eff->number_of_features;
> +
> +	if (n > ECI_MAX_FEATURE_COUNT)
> +		return -EINVAL;
> +
> +	ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL, ECI_MIC_OFF);
> +	if (ret)
> +		return ret;
> +
> +	ret = eci->eci_hw_ops->acc_read_reg(ECICMD_MASTER_INT_REG, buf, 1);
> +	if (ret)
> +		return ret;
> +
> +	mireg = buf[0];
> +	mireg &= ~ECI_INT_ENABLE;
> +	mireg |= ECI_INT_LEN_120US | ECI_INT_DELAY_ENABLE;
> +
> +	ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MASTER_INT_REG, mireg);
> +	if (ret)
> +		return ret;
> +
> +	msleep(ECI_WAIT_BUS_SETTLE);
> +	mireg |= ECI_INT_ENABLE;
> +	ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MASTER_INT_REG, mireg);
> +	if (ret)
> +		return ret;
> +
> +	msleep(ECI_WAIT_BUS_SETTLE);
> +
> +	return ret;
> +}
> +
> +/* Find "enchangement features" block from buffer */
> +static int eci_get_enchancement_features(struct eci_data *eci)
> +{
> +	u8 *mem = (void *)eci->memory;
> +	struct eci_mem_block *b = (void *)mem;
> +	struct eci_mem_block *mem_end = (void *)(eci->memory + eci->mem_size);
> +
> +	if (b->id != ECI_EKEY_BLOCK_ID)
> +		return -ENODEV;
> +
> +	do {
> +		dev_dbg(eci->dev, "skip BLOCK 0x%02x, LEN 0x%02x\n",
> +				b->id, b->len);
> +		if (!b->len)
> +			return -EINVAL;
> +
> +		mem += b->len;
> +		b = (void *)mem;
> +		eci->e_features_fix = (void *)b;
> +		dev_dbg(eci->dev, "found BLOCK 0x%02x, LEN 0x%02x\n",
> +				b->id, b->len);
> +		if (b->id == ECI_ENHANCEMENT_FEATURE_BLOCK_ID)
> +			return 0;
> +	} while (b < mem_end);
> +
> +	return -ENFILE;
> +}
> +
> +/*
> + * Find out ECI features.
> + * All ECI memory block parsing are done here, be carefull as
> + * pointers to memory tend to go wrong easily.
> + * ECI "Enhancement Features block has variable size, so we try to
> + * catch pointers out of block due memory reading errors etc.
> + *
> + * I/O support field is not implemented.
> + * Data direction field is not implemented, nor writing to the ECI I/O
> + */
> +static int eci_parse_enchancement_features(struct eci_data *eci)
> +{
> +	struct enchancement_features_fixed *eff = eci->e_features_fix;
> +	struct enchancement_features_variable *efv = &eci->e_features_var;
> +	int i;
> +	u8 n, k;
> +	void *mem_end = (void *)((u8 *)eff + eff->length);
> +
> +	dev_dbg(eci->dev, "block id 0x%02x length 0x%02x connector "
> +			"configuration 0x%02x\n", eff->block_id, eff->length,
> +			eff->connector_conf);
> +	n = eff->number_of_features;
> +	dev_dbg(eci->dev, "number of features %d\n", n);
> +
> +	if (n > ECI_MAX_FEATURE_COUNT)
> +		return -EINVAL;
> +
> +	k = DIV_ROUND_UP(n, 8);
> +	dev_dbg(eci->dev, "I/O support bytes count %d\n", k);
> +
> +	efv->io_support = &eff->number_of_features + 1;
> +	/* efv->io_functionality[0] is not used! pins are in 1..31 range */
> +	efv->io_functionality = efv->io_support + k - 1;
> +	efv->active_state = efv->io_functionality + n + 1;
> +
> +	if ((void *)&efv->active_state[k] > mem_end)
> +		return -EINVAL;
> +
> +	/* Last part of block */
> +	for (i = 0; i < k; i++)
> +		dev_dbg(eci->dev, "active_state[%d] 0x%02x\n", i,
> +				efv->active_state[i]);
> +
> +	eci->buttons_data.buttons_up_mask =
> +		~(u32)(cpu_to_le32(*(u32 *)efv->active_state));
> +
> +	/*
> +	 * ECI accessory responces as many bytes needed for used I/O pins
> +	 * up to four bytes, when lines 24..31 are used
> +	 * all tested ECI accessories how ever return two data bytes
> +	 * event though there are less than eight I/O pins
> +	 *
> +	 * so we get alway reading error if there are less than eight I/Os
> +	 * meanwhile just use this kludge, FIXME
> +	 */
> +	k = DIV_ROUND_UP(n + 1, 8);
> +	if (k == 1)
> +		k = 2;
> +	eci->port_reg_count = k;
> +
> +	return 0;
> +}
> +
> +static int eci_init_accessory(struct eci_data *eci)
> +{
> +	int ret, key = 0, restart = 0;
> +	unsigned long future;
> +
> +	eci->mem_ok = false;
> +
> +	if (!eci->eci_hw_ops)
> +		return -ENXIO;
> +
> +	ret = eci->eci_hw_ops->acc_reset();
> +	if (ret)
> +		return ret;
> +
> +	msleep(ECI_WAIT_BUS_SETTLE);
> +
> +	ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL, ECI_MIC_OFF);
> +	if (ret)
> +		return ret;
> +
> +	/* Get ECI ekey block to determine memory size */
> +	future = jiffies + msecs_to_jiffies(ECI_TRY_GET_MEMORY);
> +	do {
> +		ret = eci_get_ekey(eci, &key);
> +		if (time_is_before_jiffies(future))
> +			break;
> +	} while (ret);
> +
> +	if (ret)
> +		return ret;
> +
> +	eci->mem_size = key;
> +	if (eci->mem_size > ECI_MAX_MEM_SIZE)
> +		return -EINVAL;
> +
> +	/* Get ECI memory */
> +	future = jiffies + msecs_to_jiffies(ECI_TRY_GET_MEMORY);
> +	do {
> +		ret = eci_get_memory(eci, &restart);
> +		if (time_is_before_jiffies(future))
> +			break;
> +	} while (ret);
> +
> +	if (ret)
> +		return ret;
> +
> +	if (eci_get_enchancement_features(eci))
> +		return -EIO;
> +
> +	if (eci_parse_enchancement_features(eci))
> +		return -EIO;
> +
> +	/*
> +	 * Configure ECI buttons now as we have after parsed
> +	 * enchancement features table
> +	 */

I do not understand this comment.

> +	msleep(ECI_WAIT_BUS_SETTLE);
> +	future = jiffies + msecs_to_jiffies(ECI_TRY_INIT_IO);
> +	do {
> +		ret = eci_init_buttons(eci);
> +		if (time_is_before_jiffies(future))
> +			break;
> +	} while (ret);
> +
> +	if (ret)
> +		return ret;
> +
> +	eci->mem_ok = true;
> +	msleep(ECI_WAIT_BUS_SETTLE);
> +
> +	if (eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL, eci->mic_state))
> +		dev_err(eci->dev, "Unable to control headset microphone\n");
> +
> +	return 0;
> +}
> +
> +static int init_accessory_input(struct eci_data *eci)
> +{
> +	int err, i, code;
> +
> +	eci->acc_input = input_allocate_device();
> +	if (!eci->acc_input) {
> +		dev_err(eci->dev, "Error allocating input device: %d\n",
> +				__LINE__);
> +		return -ENOMEM;
> +	}
> +
> +	eci->acc_input->name = "ECI Accessory";
> +
> +	/* Codes on top of KEY_MAX are switch events */
> +	for (i = 0; i < ARRAY_SIZE(eci_codes); i++) {
> +		code = eci_codes[i];
> +		if (code >= KEY_MAX) {
> +			code -= KEY_MAX;
> +			set_bit(code, eci->acc_input->swbit);
> +		} else {
> +			set_bit(code, eci->acc_input->keybit);
> +		}
> +	}
> +
> +	set_bit(EV_KEY, eci->acc_input->evbit);
> +	set_bit(EV_SW, eci->acc_input->evbit);
> +	set_bit(EV_REP, eci->acc_input->evbit);

__set_bit(), no neded to lock bus.

> +
> +	err = input_register_device(eci->acc_input);
> +	if (err) {
> +		dev_err(eci->dev, "Error registering input device: %d\n",
> +				__LINE__);
> +		goto err_free_dev;
> +	}
> +
> +	/* Must set after input_register_device() to take effect */
> +	eci->acc_input->rep[REP_PERIOD] = ECI_KEY_REPEAT_INTERVAL;
> +
> +	return 0;
> +
> +err_free_dev:
> +	input_free_device(eci->acc_input);
> +	return err;
> +}
> +
> +static void remove_accessory_input(struct eci_data *eci)
> +{
> +	input_unregister_device(eci->acc_input);
> +}
> +
> +/* Press/release ccessory button(s) */
> +static int eci_get_button(struct eci_data *eci)
> +{
> +	struct enchancement_features_fixed *eff = eci->e_features_fix;
> +	struct eci_buttons_data *b = &eci->buttons_data;
> +
> +	if (!eci->mem_ok)
> +		return -ENXIO;
> +
> +	if (((b->buttons & 0x0000ffff) == 0) && (eff->number_of_features > 2)) {
> +		dev_err(eci->dev, "ECI report all buttons down, rejected %d\n",
> +			       __LINE__);
> +		return -EINVAL;
> +	}
> +
> +	if (b->windex < ECI_BUTTON_BUF_SIZE) {
> +		if (b->buttons_buf[b->windex] == 0)
> +			b->buttons_buf[b->windex] = b->buttons;
> +		else
> +			dev_err(eci->dev, "ECI button queue owerflow %d\n",
> +			       __LINE__);
> +	}
> +	b->windex++;
> +	if (b->windex == ECI_BUTTON_BUF_SIZE)
> +		b->windex = 0;
> +
> +	return 0;
> +}
> +
> +/* Intended to use ONLY inside eci_parse_button() ! */
> +#define ACTIVE_STATE(x) (u32)(cpu_to_le32(*(u32 *)efv->active_state) & BIT(x-1))
> +#define BUTTON_STATE(x) ((buttons & BIT(x))>>1)
> +
> +static int eci_parse_button(struct eci_data *eci, u32 buttons)
> +{
> +	int pin, code, state;
> +	u8 n, io_fun;
> +	struct enchancement_features_variable *efv = &eci->e_features_var;
> +	struct enchancement_features_fixed *eff = eci->e_features_fix;
> +
> +	if (!eci->mem_ok)
> +		return -ENXIO;
> +
> +	n = eff->number_of_features;
> +
> +	for (pin = 1; pin <= n; pin++) {
> +		io_fun = efv->io_functionality[pin] & ~BIT(7);
> +		if (io_fun > ECI_MUTE)
> +			break;
> +		code = eci_codes[io_fun];
> +		state = (BUTTON_STATE(pin) == ACTIVE_STATE(pin));
> +		if (state)
> +			dev_dbg(eci->dev, "I/O functionality 0x%02x\n", io_fun);
> +		if (code >= KEY_MAX)
> +			input_report_switch(eci->acc_input, code - KEY_MAX,
> +					state);
> +		else
> +			input_report_key(eci->acc_input, code, state);
> +	}
> +	input_sync(eci->acc_input);
> +
> +	return 0;
> +}
> +
> +static int eci_send_button(struct eci_data *eci)
> +{
> +	int i;
> +	struct enchancement_features_fixed *eff = eci->e_features_fix;
> +	struct eci_buttons_data *b = &eci->buttons_data;
> +	u8 n;
> +
> +	if (!eci->mem_ok)
> +		return -ENXIO;
> +
> +	n = eff->number_of_features;
> +
> +	if (n > ECI_MAX_FEATURE_COUNT)
> +		return -EINVAL;
> +	/*
> +	 * Codes on top of KEY_MAX are switch events.
> +	 * Let input system take care multiple key events
> +	 */
> +	for (i = 0; i < ECI_BUTTON_BUF_SIZE; i++) {
> +		if (b->buttons_buf[b->rindex] == 0)
> +			break;
> +
> +		if (eci_parse_button(eci, b->buttons_buf[b->rindex]))
> +			return -ENXIO;
> +
> +		b->buttons_buf[b->rindex] = 0;
> +		b->rindex++;
> +		if (b->rindex == ECI_BUTTON_BUF_SIZE)
> +			b->rindex = 0;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Other driver(s) can call this after registering themselves using
> + * eci_register()
> + */
> +static void eci_accessory_event(int event, void *priv)
> +{
> +	struct eci_data *eci = priv;
> +	struct eci_buttons_data *b = &eci->buttons_data;
> +	int delay = 0;
> +	int ret = 0;
> +
> +	eci->event = event;
> +	switch (event) {
> +	case ECI_EVENT_IS_ECI:
> +		eci->is_eci = true;
> +		ret = eci->eci_hw_ops->acc_reset();
> +		if (ret)
> +			eci->is_eci = false;
> +		break;
> +	case ECI_EVENT_PLUG_IN:
> +		eci->first_event = true;
> +		ret = eci_init_accessory(eci);
> +		if (ret < 0)
> +			ret = eci_init_accessory(eci);
> +		if (ret) {
> +			dev_err(eci->dev, "Accessory init %s%s%s%sat: %d\n",
> +					ret & ACI_COMMERR ? "COMMERR " : "",
> +					ret & ACI_FRAERR ? "FRAERR " : "",
> +					ret & ACI_RESERR ? "RESERR " : "",
> +					ret & ACI_COLL ? "COLLERR " : "",
> +					__LINE__);
> +			break;
> +		}
> +		break;
> +	case ECI_EVENT_PLUG_OUT:
> +		eci->mem_ok = false;
> +		break;
> +	case ECI_EVENT_BUTTON:
> +		/*
> +		 * First event might not be valid due plug insertion
> +		 * so we filter it out if it seems garbage
> +		 */
> +		if (eci->first_event) {
> +			eci->first_event = false;
> +			if ((b->buttons & 0xff) == 0)
> +				break;
> +		}
> +		eci_get_button(eci);
> +		delay = msecs_to_jiffies(ECI_WAIT_SEND_BUTTON);
> +		schedule_delayed_work(&eci->eci_ws, delay);
> +		break;
> +	default:
> +		dev_err(eci->dev, "unknown event %d: %d\n", event, __LINE__);
> +		break;
> +	}
> +
> +	return;
> +}
> +
> +static void eci_hsmic_event(void *priv, bool on)
> +{
> +	struct eci_data *eci = priv;
> +	unsigned long future;
> +	int ret;
> +
> +	if (!eci)
> +		return;
> +
> +	if (on)
> +		eci->mic_state = ECI_MIC_AUTO;
> +	else
> +		eci->mic_state = ECI_MIC_OFF;
> +
> +	future = jiffies + msecs_to_jiffies(ECI_TRY_SET_MIC);
> +	do {
> +		ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL,
> +				eci->mic_state);
> +		if (time_is_before_jiffies(future))
> +			break;
> +	} while (ret);
> +
> +	if (ret)
> +		dev_err(eci->dev, "Unable to control headset microphone\n");
> +}
> +
> +/* General work func (eci_ws) for several tasks */
> +static void eci_work(struct work_struct *ws)
> +{
> +	struct eci_data *eci;
> +	int ret;
> +
> +	eci = container_of((struct delayed_work *)ws, struct eci_data,
> +			eci_ws);
> +
> +	ret = eci_send_button(eci);
> +	if (ret)
> +		dev_err(eci->dev, "Error sending event: %d\n", __LINE__);
> +}
> +
> +static struct miscdevice eci_device = {
> +	.minor = MISC_DYNAMIC_MINOR,
> +	.name = ECI_DRIVERNAME,
> +};

What does this device do?

> +
> +struct eci_cb *eci_register(struct eci_hw_ops *eci_ops)
> +{
> +	if (!the_eci)
> +		return ERR_PTR(-EBUSY);
> +
> +	if (!eci_ops || !eci_ops->acc_read_direct ||
> +			!eci_ops->acc_read_reg || !eci_ops->acc_write_reg ||
> +			!eci_ops->acc_reset)
> +		return ERR_PTR(-EINVAL);
> +
> +	the_eci->eci_hw_ops = eci_ops;
> +
> +	return &eci_callback;
> +}
> +EXPORT_SYMBOL(eci_register);
> +
> +static int __init eci_probe(struct platform_device *pdev)
> +{
> +	struct eci_data *eci;
> +	struct eci_platform_data *pdata = pdev->dev.platform_data;
> +	int ret;
> +
> +	eci = kzalloc(sizeof(*eci), GFP_KERNEL);
> +	if (!eci)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, eci);
> +	eci->dev = &pdev->dev;
> +
> +	ret = misc_register(&eci_device);
> +	if (ret) {
> +		dev_err(eci->dev, "could not register misc_device: %d\n",
> +				__LINE__);
> +		goto err_misc;
> +	}
> +
> +	the_eci = eci;
> +
> +	eci_callback.event              = eci_accessory_event;
> +	eci_callback.priv               = eci;
> +
> +	ret = sysfs_create_group(&pdev->dev.kobj, &eci_attr_group);
> +	if (ret) {
> +		dev_err(eci->dev, "could not create sysfs entries: %d\n",
> +				__LINE__);
> +		goto err_sysfs;
> +	}
> +
> +	ret = eci_initialize_debugfs(eci);
> +	if (ret)
> +		dev_err(eci->dev, "could not create debugfs entries: %d\n",
> +				__LINE__);
> +
> +	ret = init_accessory_input(eci);
> +	if (ret) {
> +		dev_err(eci->dev, "ERROR initializing accessory input: %d\n",
> +				__LINE__);
> +		goto err_input;
> +	}
> +
> +	/*
> +	 * If platform machine has audio driver providing
> +	 * register_hsmic_event_cb, we should give accessory microphone control,
> +	 * ie. eci_hsmic_event to it.
> +	 * This way audio driver get control to ECI accessory microphone and
> +	 * we can save power
> +	 */
> +	if (pdata) {
> +		if (pdata->register_hsmic_event_cb) {
> +			hsmic_event.private	= eci;
> +			hsmic_event.event	= eci_hsmic_event;
> +			pdata->register_hsmic_event_cb(&hsmic_event);
> +		}
> +	}
> +
> +	init_waitqueue_head(&eci->wait);
> +	INIT_DELAYED_WORK(&eci->eci_ws, eci_work);
> +
> +	eci->mem_ok = false;
> +	/*
> +	 * By default ECI driver leaves microphone off, to save power.
> +	 * Audio driver can set microphone on by using
> +	 * hsmic_event.event
> +	 */
> +	eci->mic_state = ECI_MIC_OFF;
> +
> +	/* Init buttons_data indexes and buffer */
> +	memset(&eci->buttons_data, 0, sizeof(struct eci_buttons_data));
> +	eci->buttons_data.buttons = 0xffffffff;
> +
> +	return 0;
> +
> +err_input:
> +	eci_uninitialize_debugfs();
> +	sysfs_remove_group(&pdev->dev.kobj, &eci_attr_group);
> +
> +err_sysfs:
> +	misc_deregister(&eci_device);
> +
> +err_misc:
> +	kfree(eci);
> +
> +	return ret;
> +}
> +
> +static int __exit eci_remove(struct platform_device *pdev)
> +{
> +	struct eci_data *eci = platform_get_drvdata(pdev);
> +	struct eci_platform_data *pdata = pdev->dev.platform_data;
> +
> +	pdata->register_hsmic_event_cb(NULL);
> +	cancel_delayed_work_sync(&eci->eci_ws);
> +	eci_uninitialize_debugfs();
> +	sysfs_remove_group(&pdev->dev.kobj, &eci_attr_group);
> +	remove_accessory_input(eci);
> +	kfree(eci);
> +	misc_deregister(&eci_device);
> +
> +	return 0;
> +}
> +
> +#ifdef	CONFIG_PM
> +
> +static int eci_suspend(struct platform_device *pdev, pm_message_t mesg)
> +{
> +	return -ENOSYS;
> +}
> +#else
> +#define	eci_suspend	NULL
> +#endif
> +
> +static struct platform_driver eci_driver = {
> +	.probe		= eci_probe,
> +	.remove		= __exit_p(eci_remove),
> +	.suspend	= eci_suspend,
> +	.driver		= {
> +		.name	= ECI_DRIVERNAME,
> +		.owner	= THIS_MODULE,
> +	},
> +};
> +
> +static int __init eci_init(void)
> +{
> +	return platform_driver_register(&eci_driver);
> +}
> +device_initcall(eci_init);
> +
> +static void __exit eci_exit(void)
> +{
> +	platform_driver_unregister(&eci_driver);
> +}
> +module_exit(eci_exit);
> +
> +MODULE_ALIAS("platform:" ECI_DRIVERNAME);
> +MODULE_AUTHOR("Nokia Corporation");
> +MODULE_DESCRIPTION("ECI accessory driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/input/eci.h b/include/linux/input/eci.h
> new file mode 100644
> index 0000000..b8be99c
> --- /dev/null
> +++ b/include/linux/input/eci.h
> @@ -0,0 +1,157 @@
> +/*
> + * This file is part of ECI (Enhancement Control Interface) driver
> + *
> + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
> + *
> + * Contact: Tapio Vihuri <tapio.vihuri@nokia.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * 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.
> + *
> + * 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 St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +#ifndef __ECI_H__
> +#define __ECI_H__
> +
> +#define ECI_MAX_MEM_SIZE	0x7c
> +#define ECI_BUTTON_BUF_SIZE	32
> +#define ECI_MAX_FEATURE_COUNT	31
> +
> +#define ACI_COMMERR	0x010
> +#define ACI_FRAERR	0x020
> +#define ACI_RESERR	0x040
> +#define ACI_COLL	0x080
> +
> +#define ECI_REAL_BUTTONS	0
> +#define ECI_FORCE_BUTTONS_UP	1
> +
> +/* fixed in ECI HW, do not change */
> +enum {
> +	ECICMD_HWID,
> +	ECICMD_SWID,
> +	ECICMD_ECI_BUS_SPEED,
> +	ECICMD_MIC_CTRL,
> +	ECICMD_MASTER_INT_REG,
> +	ECICMD_HW_CONF_MEM_ACCESS,
> +	ECICMD_EXTENDED_MEM_ACCESS,
> +	ECICMD_INDIRECT_MEM_ACCESS,
> +	ECICMD_PORT_DATA_0,
> +	ECICMD_PORT_DATA_1,
> +	ECICMD_PORT_DATA_2,
> +	ECICMD_PORT_DATA_3,
> +	ECICMD_LATCHED_PORT_DATA_0,
> +	ECICMD_LATCHED_PORT_DATA_1,
> +	ECICMD_LATCHED_PORT_DATA_2,
> +	ECICMD_LATCHED_PORT_DATA_3,
> +	ECICMD_DATA_DIR_0,
> +	ECICMD_DATA_DIR_1,
> +	ECICMD_DATA_DIR_2,
> +	ECICMD_DATA_DIR_3,
> +	ECICMD_INT_CONFIG_0_LOW,
> +	ECICMD_INT_CONFIG_0_HIGH,
> +	ECICMD_INT_CONFIG_1_LOW,
> +	ECICMD_INT_CONFIG_1_HIGH,
> +	ECICMD_INT_CONFIG_2_LOW,
> +	ECICMD_INT_CONFIG_2_HIGH,
> +	ECICMD_INT_CONFIG_3_LOW,
> +	ECICMD_INT_CONFIG_3_HIGH,
> +	/*
> +	 * 0x1c - 0x2f reserved for future
> +	 * 0x30 - 0x3d reserved
> +	 */
> +	ECICMD_EEPROM_LOCK = 0x3e,
> +	ECICMD_RESERVED,	/* 0x3f */
> +	ECIREG_STATUS,		/* 0x40 */
> +	ECIREG_READ_COUNT,	/* 0x41 */
> +	ECIREG_BUF_COUNT,	/* 0x42 */
> +	ECIREG_RST_LEARN,	/* 0x43 */
> +	/* 0x44 - 0xdf as data buffer */
> +	ECIREG_READ_DIRECT,	/* 0x44 */
> +	ECIREG_HW_ID = 0xe0,	/* 0xe0 */
> +	ECIREG_FW_ID,		/* 0xe1 */
> +	ECIREG_TEST_IN,		/* 0xe2 */
> +	ECIREG_TEST_OUT,	/* 0xe3 */
> +};
> +
> +enum {
> +	ECI_EVENT_IS_ECI,
> +	ECI_EVENT_PLUG_IN,
> +	ECI_EVENT_PLUG_OUT,
> +	ECI_EVENT_BUTTON,
> +	ECI_EVENT_NO,
> +};
> +
> +struct eci_hw_ops {
> +	int (*acc_reset)(void);
> +	int (*acc_read_direct)(u8 addr, char *buf);
> +	int (*acc_read_reg)(u8 reg, u8 *buf, int count);
> +	int (*acc_write_reg)(u8 reg, u8 param);
> +};
> +
> +struct eci_cb {
> +	void *priv;
> +	void (*event)(int event, void *priv);
> +};
> +
> +struct audio_hsmic_event {
> +	void *private;
> +	void (*event)(void *priv, bool on);
> +};
> +
> +struct eci_platform_data {
> +	void (*register_hsmic_event_cb)(struct audio_hsmic_event *);
> +};
> +
> +struct enchancement_features_fixed {
> +	u8	block_id;
> +	u8	length;
> +	u8	connector_conf;
> +	u8	number_of_features;
> +};
> +
> +struct enchancement_features_variable {
> +	u8	*io_support;
> +	u8	*io_functionality;
> +	u8	*active_state;
> +};
> +
> +struct eci_buttons_data {
> +	u32	buttons;
> +	int	windex;
> +	int	rindex;
> +	u32	buttons_up_mask;
> +	u32	buttons_buf[ECI_BUTTON_BUF_SIZE];
> +};
> +
> +struct eci_data {
> +	struct device				*dev;
> +	struct delayed_work			eci_ws;
> +	wait_queue_head_t			 wait;
> +	struct input_dev			*acc_input;
> +	int					event;
> +	bool					first_event;
> +	bool					mem_ok;
> +	u16					mem_size;
> +	u8					memory[ECI_MAX_MEM_SIZE];
> +	struct enchancement_features_fixed	*e_features_fix;
> +	struct enchancement_features_variable	e_features_var;
> +	u8					port_reg_count;
> +	struct eci_buttons_data			buttons_data;
> +	struct eci_hw_ops			*eci_hw_ops;
> +	u8					mic_state;
> +	bool					plugged;
> +	bool					is_eci;
> +};
> +
> +struct eci_cb *eci_register(struct eci_hw_ops *eci_ops);
> +#endif
> -- 
> 1.6.5
> 

Thank you.

-- 
Dmitry

  parent reply	other threads:[~2011-01-02  8:46 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-12-30 13:36 [PATCH v2 0/3] input: Add support for ECI (multimedia) accessories tapio.vihuri
2010-12-30 13:36 ` tapio.vihuri
2010-12-30 13:36 ` [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver tapio.vihuri
2010-12-30 13:36   ` [PATCH v2 2/3] ECI: introducing ECI bus driver tapio.vihuri
2010-12-30 13:36     ` [PATCH v2 3/3] ECI: adding platform data for ECI driver tapio.vihuri
2011-01-03  8:42     ` [PATCH v2 2/3] ECI: introducing ECI bus driver Peter Ujfalusi
2011-01-03  8:42       ` [alsa-devel] " Peter Ujfalusi
2011-01-02  8:19   ` Dmitry Torokhov [this message]
2011-01-02  8:19     ` [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver Dmitry Torokhov
2011-01-04 14:01     ` Tapio Vihuri
2011-01-04 14:01       ` Tapio Vihuri

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20110102081922.GD5429@core.coreip.homeip.net \
    --to=dmitry.torokhov@gmail.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=ilkka.koskinen@nokia.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=randy.dunlap@oracle.com \
    --cc=samu.p.onkalo@nokia.com \
    --cc=tapio.vihuri@nokia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.