Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH 6/7] Input: pmic8xxx-keypad - Migrate to devm_* APIs
From: Dmitry Torokhov @ 2013-12-16 15:37 UTC (permalink / raw)
  To: Stephen Boyd; +Cc: linux-kernel, linux-arm-msm, linux-arm-kernel, linux-input
In-Reply-To: <1386718996-3733-7-git-send-email-sboyd@codeaurora.org>

On Tue, Dec 10, 2013 at 03:43:15PM -0800, Stephen Boyd wrote:
> Simplify the error paths and reduce the lines of code in this
> driver by using the devm_* APIs.
> 
> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> ---
>  drivers/input/keyboard/pmic8xxx-keypad.c | 62 +++++++++-----------------------
>  1 file changed, 17 insertions(+), 45 deletions(-)
> 
> diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c
> index 2c9f19a..4e6bfbf 100644
> --- a/drivers/input/keyboard/pmic8xxx-keypad.c
> +++ b/drivers/input/keyboard/pmic8xxx-keypad.c
> @@ -586,7 +586,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
>  		return -EINVAL;
>  	}
>  
> -	kp = kzalloc(sizeof(*kp), GFP_KERNEL);
> +	kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
>  	if (!kp)
>  		return -ENOMEM;
>  
> @@ -595,32 +595,27 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
>  	kp->pdata	= pdata;
>  	kp->dev		= &pdev->dev;
>  
> -	kp->input = input_allocate_device();
> +	kp->input = devm_input_allocate_device(&pdev->dev);
>  	if (!kp->input) {
>  		dev_err(&pdev->dev, "unable to allocate input device\n");
> -		rc = -ENOMEM;
> -		goto err_alloc_device;
> +		return -ENOMEM;
>  	}
>  
>  	kp->key_sense_irq = platform_get_irq(pdev, 0);
>  	if (kp->key_sense_irq < 0) {
>  		dev_err(&pdev->dev, "unable to get keypad sense irq\n");
> -		rc = -ENXIO;
> -		goto err_get_irq;
> +		return kp->key_sense_irq;
>  	}
>  
>  	kp->key_stuck_irq = platform_get_irq(pdev, 1);
>  	if (kp->key_stuck_irq < 0) {
>  		dev_err(&pdev->dev, "unable to get keypad stuck irq\n");
> -		rc = -ENXIO;
> -		goto err_get_irq;
> +		return kp->key_stuck_irq;
>  	}
>  
>  	kp->input->name = pdata->input_name ? : "PMIC8XXX keypad";
>  	kp->input->phys = pdata->input_phys_device ? : "pmic8xxx_keypad/input0";
>  
> -	kp->input->dev.parent	= &pdev->dev;
> -
>  	kp->input->id.bustype	= BUS_I2C;
>  	kp->input->id.version	= 0x0001;
>  	kp->input->id.product	= 0x0001;
> @@ -634,7 +629,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
>  					kp->keycodes, kp->input);
>  	if (rc) {
>  		dev_err(&pdev->dev, "failed to build keymap\n");
> -		goto err_get_irq;
> +		return rc;
>  	}
>  
>  	if (pdata->rep)
> @@ -650,7 +645,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
>  	rc = pmic8xxx_kpd_init(kp);
>  	if (rc < 0) {
>  		dev_err(&pdev->dev, "unable to initialize keypad controller\n");
> -		goto err_get_irq;
> +		return rc;
>  	}
>  
>  	rc = pmic8xxx_kp_config_gpio(pdata->cols_gpio_start,
> @@ -667,24 +662,26 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
>  		goto err_gpio_config;
>  	}
>  
> -	rc = request_any_context_irq(kp->key_sense_irq, pmic8xxx_kp_irq,
> -				 IRQF_TRIGGER_RISING, "pmic-keypad", kp);
> +	rc = devm_request_any_context_irq(&pdev->dev, kp->key_sense_irq,
> +			pmic8xxx_kp_irq, IRQF_TRIGGER_RISING, "pmic-keypad",
> +			kp);
>  	if (rc < 0) {
>  		dev_err(&pdev->dev, "failed to request keypad sense irq\n");
> -		goto err_get_irq;
> +		return rc;
>  	}
>  
> -	rc = request_any_context_irq(kp->key_stuck_irq, pmic8xxx_kp_stuck_irq,
> -				 IRQF_TRIGGER_RISING, "pmic-keypad-stuck", kp);
> +	rc = devm_request_any_context_irq(&pdev->dev, kp->key_stuck_irq,
> +			pmic8xxx_kp_stuck_irq, IRQF_TRIGGER_RISING,
> +			"pmic-keypad-stuck", kp);
>  	if (rc < 0) {
>  		dev_err(&pdev->dev, "failed to request keypad stuck irq\n");
> -		goto err_req_stuck_irq;
> +		return rc;
>  	}
>  
>  	rc = pmic8xxx_kp_read_u8(kp, &ctrl_val, KEYP_CTRL);
>  	if (rc < 0) {
>  		dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n");
> -		goto err_pmic_reg_read;
> +		return rc;
>  	}
>  
>  	kp->ctrl_reg = ctrl_val;
> @@ -692,36 +689,12 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
>  	rc = input_register_device(kp->input);
>  	if (rc < 0) {
>  		dev_err(&pdev->dev, "unable to register keypad input device\n");
> -		goto err_pmic_reg_read;
> +		return rc;
>  	}
>  
>  	device_init_wakeup(&pdev->dev, pdata->wakeup);
>  
>  	return 0;
> -
> -err_pmic_reg_read:
> -	free_irq(kp->key_stuck_irq, kp);
> -err_req_stuck_irq:
> -	free_irq(kp->key_sense_irq, kp);
> -err_gpio_config:
> -err_get_irq:
> -	input_free_device(kp->input);
> -err_alloc_device:
> -	kfree(kp);
> -	return rc;
> -}
> -
> -static int pmic8xxx_kp_remove(struct platform_device *pdev)
> -{
> -	struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
> -
> -	device_init_wakeup(&pdev->dev, 0);

Why are we removing restoring wakeup capable state?

> -	free_irq(kp->key_stuck_irq, kp);
> -	free_irq(kp->key_sense_irq, kp);
> -	input_unregister_device(kp->input);
> -	kfree(kp);
> -
> -	return 0;
>  }
>  
>  #ifdef CONFIG_PM_SLEEP
> @@ -771,7 +744,6 @@ static SIMPLE_DEV_PM_OPS(pm8xxx_kp_pm_ops,
>  
>  static struct platform_driver pmic8xxx_kp_driver = {
>  	.probe		= pmic8xxx_kp_probe,
> -	.remove		= pmic8xxx_kp_remove,
>  	.driver		= {
>  		.name = PM8XXX_KEYPAD_DEV_NAME,
>  		.owner = THIS_MODULE,
> -- 
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> hosted by The Linux Foundation
> 

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 1/7] Input: pmic8xxx-pwrkey - Pass input device directly to interrupt
From: Stephen Boyd @ 2013-12-16 19:59 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, linux-arm-msm, linux-arm-kernel, linux-input
In-Reply-To: <20131215112103.GA14818@core.coreip.homeip.net>

On 12/15, Dmitry Torokhov wrote:
> Hi Stephen,
> 
> On Tue, Dec 10, 2013 at 03:43:10PM -0800, Stephen Boyd wrote:
> > Instead of passing the pointer to the container structure just
> > pass the input device here. This saves a dereference in the fast
> > path.
> > 
> > Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> > ---
> >  drivers/input/misc/pmic8xxx-pwrkey.c | 22 ++++++++--------------
> >  1 file changed, 8 insertions(+), 14 deletions(-)
> > 
> > diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
> > index ce3c426..233b216 100644
> > --- a/drivers/input/misc/pmic8xxx-pwrkey.c
> > +++ b/drivers/input/misc/pmic8xxx-pwrkey.c
> > @@ -32,26 +32,21 @@
> >   * @key_press_irq: key press irq number
> >   */
> >  struct pmic8xxx_pwrkey {
> > -	struct input_dev *pwr;
> 
> This causes compile errors as you need pwrkey->pwr in remove() to
> unregister input device. I'll fix it up.
> 

Ah sorry. I forgot to send the patch before this that converts it
to devm.

> >  	err = devm_request_irq(&pdev->dev, key_press_irq, pwrkey_press_irq,
> > -		IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwrkey);
> > +		IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwr);

As you can see here because this driver isn't currently using
devm_request_irq().

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply

* Re: [PATCH] Input: pmic8xxx-pwrkey - switch to using managed resources
From: Stephen Boyd @ 2013-12-16 20:36 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, Lars-Peter Clausen, Sachin Kamat, linux-kernel
In-Reply-To: <20131215120723.GA375@core.coreip.homeip.net>

On 12/15, Dmitry Torokhov wrote:
> This simplifies error handling and device removal paths.
> 
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> ---

Thanks. Like I said on the other mail, I forgot to send this
patch along. This looks almost identical to what I had so I'm
happy.

Acked-by: Stephen Boyd <sboyd@codeaurora.org>

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply

* Re: [PATCH] input: egalax: Send touch end event when is about to suspend
From: Felipe Ferreri Tonello @ 2013-12-16 23:45 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, linux-input, Andy Shevchenko, Sachin Kamat,
	Heiko Abraham
In-Reply-To: <20131215104053.GC20197@core.coreip.homeip.net>

Hi Dmitry,

On 12/15/2013 02:40 AM, Dmitry Torokhov wrote:
> Hi Felipe,
>
> On Tue, Dec 10, 2013 at 04:03:16PM -0800, Felipe F. Tonello wrote:
>> From: "Felipe F. Tonello" <eu@felipetonello.com>
>>
>> This is useful to report for users of this device that don't know anything
>> about the suspension of the device. So users will receive a touch end event
>> when the device is about to suspend, making it more user friendly.
>>
>> One example of users is the X Server with the evdev input driver. This patch
>> make sure that the X server will propagate a touch end event to its windows.
>>
>
> Hmm, I would argue we need to do this in input core, similarly to what
> we do for the keys, so that all drivers would benefit from the change.
>
> Thanks.

I agree with you. I didn't do in this case because previous patches that 
I had to change core functionality were declined due the fact it's hard 
to maintain backwards compatibility.

Do you recommend me something in this case?

Thanks for your answer.

Felipe

>
>> Signed-off-by: Felipe F. Tonello <eu@felipetonello.com>
>> ---
>>   drivers/input/touchscreen/egalax_ts.c | 18 +++++++++++++++++-
>>   1 file changed, 17 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
>> index 054d225..1f21f0d 100644
>> --- a/drivers/input/touchscreen/egalax_ts.c
>> +++ b/drivers/input/touchscreen/egalax_ts.c
>> @@ -63,6 +63,7 @@
>>   struct egalax_ts {
>>   	struct i2c_client		*client;
>>   	struct input_dev		*input_dev;
>> +	bool					ids_in_use[MAX_SUPPORT_POINTS];
>>   };
>>
>>   static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
>> @@ -117,6 +118,8 @@ static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
>>   	input_mt_report_pointer_emulation(input_dev, true);
>>   	input_sync(input_dev);
>>
>> +	ts->ids_in_use[id] = down;
>> +
>>   	return IRQ_HANDLED;
>>   }
>>
>> @@ -247,9 +250,22 @@ static int egalax_ts_suspend(struct device *dev)
>>   		0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0
>>   	};
>>   	struct i2c_client *client = to_i2c_client(dev);
>> -	int ret;
>> +	struct egalax_ts *ts = i2c_get_clientdata(client);
>> +	int ret, id;
>>
>>   	ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN);
>> +
>> +	/* We send a touch end event (for the ids in use) if the egalax module is
>> +	   about to be turned off. */
>> +	for (id = 0; id < MAX_SUPPORT_POINTS; ++id) {
>> +		if (ts->ids_in_use[id]) {
>> +			input_mt_slot(ts->input_dev, id);
>> +			input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
>> +			input_mt_report_pointer_emulation(ts->input_dev, true);
>> +			input_sync(ts->input_dev);
>> +		}
>> +	}
>> +
>>   	return ret > 0 ? 0 : ret;
>>   }
>>
>> --
>> 1.8.3.1
>>
>

^ permalink raw reply

* [PATCH] input: Port rotary_encoder to of_property_read_bool
From: Ben Gamari @ 2013-12-17  0:10 UTC (permalink / raw)
  To: linux-input; +Cc: Ben Gamari

---
 drivers/input/misc/rotary_encoder.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
index f920ba7..52049db 100644
--- a/drivers/input/misc/rotary_encoder.c
+++ b/drivers/input/misc/rotary_encoder.c
@@ -175,12 +175,9 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic
 	pdata->gpio_b = of_get_gpio_flags(np, 1, &flags);
 	pdata->inverted_b = flags & OF_GPIO_ACTIVE_LOW;
 
-	pdata->relative_axis = !!of_get_property(np,
-					"rotary-encoder,relative-axis", NULL);
-	pdata->rollover = !!of_get_property(np,
-					"rotary-encoder,rollover", NULL);
-	pdata->half_period = !!of_get_property(np,
-					"rotary-encoder,half-period", NULL);
+	pdata->relative_axis = of_property_read_bool(np, "rotary-encoder,relative-axis");
+	pdata->rollover = of_property_read_bool(np, "rotary-encoder,rollover");
+	pdata->half_period = of_property_read_bool(np, "rotary-encoder,half-period");
 
 	return pdata;
 }
-- 
1.8.3.2


^ permalink raw reply related

* Re: [PATCH v3] if xen_platform_pci=0 is set don't blow up.
From: Bjorn Helgaas @ 2013-12-17  1:00 UTC (permalink / raw)
  To: Konrad Rzeszutek Wilk
  Cc: Jens Axboe, Stefano Stabellini, ian.campbell, xen-devel,
	linux-kernel@vger.kernel.org, boris.ostrovsky, David Vrabel,
	leosilva, ashley, Peter Hüwe, mail, tpmdd, Dmitry Torokhov,
	plagnioj, tomi.valkeinen, tpmdd-devel,
	linux-input@vger.kernel.org, netdev, linux-pci@vger.kernel.org,
	linux-fbdev
In-Reply-To: <1387206250-13963-1-git-send-email-konrad.wilk@oracle.com>

On Mon, Dec 16, 2013 at 8:04 AM, Konrad Rzeszutek Wilk
<konrad.wilk@oracle.com> wrote:
> The first patch:
>  [PATCH v3 1/2] xen/pvhvm: If xen_platform_pci=0 is set don't blow up
>
> I would like to commit to stable as it is fixing an eggregious bug -
> where we blow up if the guest config has: "xen_platform_pci=0" setup.
> This bug has been in existence for years and it is time to stamp it out.
>
> The second patch is a cleanup - not a stable candidate.
>
> It touches all of the Xen frontend drivers and adds the logic of:
> "if user disabled us, don't init" - with variations. As you can
> specify exactly which ones you want to init and which ones
> not (Linux runtime parameter 'xen_emul_unplug'). But for the majority
> of drivers - it is just an on/off switch.
>
> Since it touches a lot of maintainers I figured I would send it
> to Linus on Wednesday or Thursday.
>
> Thank you!
>
>  arch/x86/xen/platform-pci-unplug.c         | 79 ++++++++++++++++++++++++++++--
>  drivers/block/xen-blkfront.c               |  4 +-
>  drivers/char/tpm/xen-tpmfront.c            |  4 ++
>  drivers/input/misc/xen-kbdfront.c          |  4 ++
>  drivers/net/xen-netfront.c                 |  2 +-
>  drivers/pci/xen-pcifront.c                 |  4 ++
>  drivers/video/xen-fbfront.c                |  4 ++
>  drivers/xen/xenbus/xenbus_probe_frontend.c |  2 +-
>  include/xen/platform_pci.h                 | 25 +++++++++-
>  9 files changed, 119 insertions(+), 9 deletions(-)

If you want it:

Acked-by: Bjorn Helgaas <bhelgaas@google.com>

for the PCI parts.

>
> Konrad Rzeszutek Wilk (2):
>       xen/pvhvm: If xen_platform_pci=0 is set don't blow up (v3).
>       xen/pvhvm: Remove the xen_platform_pci int.
>

^ permalink raw reply

* [PATCH -next] Input: pmic8xxx-pwrkey - fix to pass correct device identity to free_irq()
From: Wei Yongjun @ 2013-12-17  1:16 UTC (permalink / raw)
  To: dmitry.torokhov, sboyd, lars, sachin.kamat; +Cc: yongjun_wei, linux-input

From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>

free_irq() in the error handling case is missing when change
pass input device directly to interrupt.

Fixes: b27f8fee4965('Input: pmic8xxx-pwrkey - pass input device directly to interrupt')
Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
---
 drivers/input/misc/pmic8xxx-pwrkey.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
index ef93840..3914e5b 100644
--- a/drivers/input/misc/pmic8xxx-pwrkey.c
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -182,7 +182,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
 	return 0;
 
 free_press_irq:
-	free_irq(key_press_irq, pwrkey);
+	free_irq(key_press_irq, pwr);
 unreg_input_dev:
 	input_unregister_device(pwr);
 	pwr = NULL;


^ permalink raw reply related

* Re: [PATCH -next] Input: pmic8xxx-pwrkey - fix to pass correct device identity to free_irq()
From: Stephen Boyd @ 2013-12-17  1:23 UTC (permalink / raw)
  To: Wei Yongjun; +Cc: dmitry.torokhov, lars, sachin.kamat, yongjun_wei, linux-input
In-Reply-To: <CAPgLHd87FxZ6ur_WLC9Q9bjOfNbOTu6yzNWNcN-ar0=o1y48Kg@mail.gmail.com>

On 12/17, Wei Yongjun wrote:
> From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
> 
> free_irq() in the error handling case is missing when change
> pass input device directly to interrupt.
> 
> Fixes: b27f8fee4965('Input: pmic8xxx-pwrkey - pass input device directly to interrupt')
> Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>

This driver was supposed to be converted to devm before
that patch. I forgot to send it and then Dmitry seems to have
fixed up the patch to make it apply. We should just use the devm
conversion patch to avoid needing to apply this one.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply

* Re: [PATCH v2] elantech: Properly differentiate between clickpads and normal touchpads
From: Dmitry Torokhov @ 2013-12-16 15:12 UTC (permalink / raw)
  To: Hans de Goede; +Cc: linux-input, Peter Hutterer
In-Reply-To: <1386667874-6669-1-git-send-email-hdegoede@redhat.com>

On Tue, Dec 10, 2013 at 10:31:14AM +0100, Hans de Goede wrote:
> The current assumption in the elantech driver that hw version 3 touchpads are
> never clickpads and hw version 4 touchpads are always clickpads is wrong.
> 
> There are several bug reports for this, ie:
> https://bugzilla.redhat.com/show_bug.cgi?id=1030802
> http://superuser.com/questions/619582/right-elantech-touchpad-button-not-working-in-linux
> 
> I've spend a couple of hours wading through various bugzillas,
> launchpads and forum posts to create a list of fw-versions and capabilities
> for different laptop models to find a good method to differentiate between
> clickpads and versions with separate hardware buttons.
> 
> Which shows that a device being a clickpad is reliable indicated by bit 12
> being set in the fw_version. I've included the gathered list inside the driver,
> so that we've this info at hand if we need to revisit this later.
> 
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>

Applied, thank you.

> ---
>  drivers/input/mouse/elantech.c | 45 +++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 42 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
> index 597e9b8..ef1cf52 100644
> --- a/drivers/input/mouse/elantech.c
> +++ b/drivers/input/mouse/elantech.c
> @@ -486,6 +486,7 @@ static void elantech_input_sync_v4(struct psmouse *psmouse)
>  	unsigned char *packet = psmouse->packet;
>  
>  	input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
> +	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
>  	input_mt_report_pointer_emulation(dev, true);
>  	input_sync(dev);
>  }
> @@ -984,6 +985,44 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
>  }
>  
>  /*
> + * Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in
> + * fw_version for this is based on the following fw_version & caps table:
> + *
> + * Laptop-model:           fw_version:     caps:           buttons:
> + * Acer S3                 0x461f00        10, 13, 0e      clickpad
> + * Acer S7-392             0x581f01        50, 17, 0d      clickpad
> + * Acer V5-131             0x461f02        01, 16, 0c      clickpad
> + * Acer V5-551             0x461f00        ?               clickpad
> + * Asus K53SV              0x450f01        78, 15, 0c      2 hw buttons
> + * Asus G46VW              0x460f02        00, 18, 0c      2 hw buttons
> + * Asus G750JX             0x360f00        00, 16, 0c      2 hw buttons
> + * Asus UX31               0x361f00        20, 15, 0e      clickpad
> + * Asus UX32VD             0x361f02        00, 15, 0e      clickpad
> + * Avatar AVIU-145A2       0x361f00        ?               clickpad
> + * Gigabyte U2442          0x450f01        58, 17, 0c      2 hw buttons
> + * Lenovo L430             0x350f02        b9, 15, 0c      2 hw buttons (*)
> + * Samsung NF210           0x150b00        78, 14, 0a      2 hw buttons
> + * Samsung NP770Z5E        0x575f01        10, 15, 0f      clickpad
> + * Samsung NP700Z5B        0x361f06        21, 15, 0f      clickpad
> + * Samsung NP900X3E-A02    0x575f03        ?               clickpad
> + * Samsung NP-QX410        0x851b00        19, 14, 0c      clickpad
> + * Samsung RC512           0x450f00        08, 15, 0c      2 hw buttons
> + * Samsung RF710           0x450f00        ?               2 hw buttons
> + * System76 Pangolin       0x250f01        ?               2 hw buttons
> + * (*) + 3 trackpoint buttons
> + */
> +static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
> +{
> +	struct input_dev *dev = psmouse->dev;
> +	struct elantech_data *etd = psmouse->private;
> +
> +	if (etd->fw_version & 0x001000) {
> +		__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
> +		__clear_bit(BTN_RIGHT, dev->keybit);
> +	}
> +}
> +
> +/*
>   * Set the appropriate event bits for the input subsystem
>   */
>  static int elantech_set_input_params(struct psmouse *psmouse)
> @@ -1026,6 +1065,8 @@ static int elantech_set_input_params(struct psmouse *psmouse)
>  		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
>  		/* fall through */
>  	case 3:
> +		if (etd->hw_version == 3)
> +			elantech_set_buttonpad_prop(psmouse);
>  		input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
>  		input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
>  		if (etd->reports_pressure) {
> @@ -1047,9 +1088,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
>  			 */
>  			psmouse_warn(psmouse, "couldn't query resolution data.\n");
>  		}
> -		/* v4 is clickpad, with only one button. */
> -		__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
> -		__clear_bit(BTN_RIGHT, dev->keybit);
> +		elantech_set_buttonpad_prop(psmouse);
>  		__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
>  		/* For X to recognize me as touchpad. */
>  		input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
> -- 
> 1.8.4.2
> 

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] Input: add i2c/smbus driver for elan touchpad
From: Dmitry Torokhov @ 2013-12-16 21:00 UTC (permalink / raw)
  To: Duson Lin; +Cc: linux-input, linux-kernel, agnescheng, phoenix
In-Reply-To: <1385099816-2873-1-git-send-email-dusonlin@emc.com.tw>

Hi Duson,

On Fri, Nov 22, 2013 at 01:56:55PM +0800, Duson Lin wrote:
> This driver adds support for elan i2c/smbus touchpad found on some laptops PC
> ---
>  drivers/input/mouse/Kconfig    |   10 +
>  drivers/input/mouse/Makefile   |    1 +
>  drivers/input/mouse/elan_i2c.c | 1846 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1857 insertions(+)
>  create mode 100644 drivers/input/mouse/elan_i2c.c
> 
> diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
> index effa9c5..8ad4b38 100644
> --- a/drivers/input/mouse/Kconfig
> +++ b/drivers/input/mouse/Kconfig
> @@ -215,6 +215,16 @@ config MOUSE_CYAPA
>  	  To compile this driver as a module, choose M here: the module will be
>  	  called cyapa.
>  
> +config MOUSE_ELAN_I2C
> +	tristate "ELAN I2C Touchpad support"
> +	depends on I2C
> +	help
> +	  This driver adds support for Elan I2C Trackpads.
> +	  Say y here if you have a ELAN I2C Touchpad.
> +
> +	  To compile this driver as a module, choose M here: the module will be
> +	  called elan_i2c.
> +
>  config MOUSE_INPORT
>  	tristate "InPort/MS/ATIXL busmouse"
>  	depends on ISA
> diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
> index c25efdb..24a12a6 100644
> --- a/drivers/input/mouse/Makefile
> +++ b/drivers/input/mouse/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_MOUSE_APPLETOUCH)		+= appletouch.o
>  obj-$(CONFIG_MOUSE_ATARI)		+= atarimouse.o
>  obj-$(CONFIG_MOUSE_BCM5974)		+= bcm5974.o
>  obj-$(CONFIG_MOUSE_CYAPA)		+= cyapa.o
> +obj-$(CONFIG_MOUSE_ELAN_I2C)		+= elan_i2c.o
>  obj-$(CONFIG_MOUSE_GPIO)		+= gpio_mouse.o
>  obj-$(CONFIG_MOUSE_INPORT)		+= inport.o
>  obj-$(CONFIG_MOUSE_LOGIBM)		+= logibm.o
> diff --git a/drivers/input/mouse/elan_i2c.c b/drivers/input/mouse/elan_i2c.c
> new file mode 100644
> index 0000000..9892ee1
> --- /dev/null
> +++ b/drivers/input/mouse/elan_i2c.c
> @@ -0,0 +1,1846 @@
> +/*
> + * Elan I2C/SMBus Touchpad driver
> + *
> + * Copyright (c) 2013 ELAN Microelectronics Corp.
> + *
> + * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
> + * Version: 1.4.6
> + *
> + * 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.
> + *
> + * Trademarks are the property of their respective owners.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/firmware.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/input/mt.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/debugfs.h>
> +#include <linux/cdev.h>
> +#include <linux/kernel.h>
> +#include <linux/major.h>
> +#include <linux/sched.h>
> +#include <linux/input.h>
> +#include <linux/uaccess.h>
> +#include <linux/jiffies.h>
> +
> +#define DRIVER_NAME		"elan_i2c"
> +#define ELAN_DRIVER_VERSION	"1.4.6"
> +#define ETP_PRESSURE_OFFSET	25
> +#define ETP_MAX_PRESSURE	255
> +#define ETP_FWIDTH_REDUCE	90
> +#define ETP_FINGER_WIDTH	15
> +
> +#define ELAN_ADAPTER_FUNC_NONE   0
> +#define ELAN_ADAPTER_FUNC_I2C    1
> +#define ELAN_ADAPTER_FUNC_SMBUS  2
> +#define ELAN_ADAPTER_FUNC_BOTH   3
> +
> +/* Length of Elan touchpad information */
> +#define ETP_INF_LENGTH		2
> +#define ETP_MAX_FINGERS		5
> +#define ETP_FINGER_DATA_LEN	5
> +#define ETP_REPORT_ID		0x5D
> +#define ETP_MAX_REPORT_LEN	34
> +#define ETP_ENABLE_ABS		0x0001
> +#define ETP_ENABLE_CALIBRATE	0x0002
> +#define ETP_DISABLE_CALIBRATE	0x0000
> +
> +/* Elan smbus command */
> +#define ETP_SMBUS_IAP_CMD		0x00
> +#define ETP_SMBUS_ENABLE_TP		0x20
> +#define ETP_SMBUS_DISABLE_TP		0x21
> +#define ETP_SMBUS_IAP_PASSWORD_WRITE	0x29
> +#define ETP_SMBUS_IAP_PASSWORD_READ	0x80
> +#define ETP_SMBUS_WRITE_FW_BLOCK	0x2A
> +#define ETP_SMBUS_IAP_RESET_CMD		0x2B
> +#define ETP_SMBUS_RANGE_CMD		0xA0
> +#define ETP_SMBUS_FW_VERSION_CMD	0xA1
> +#define ETP_SMBUS_XY_TRACENUM_CMD	0xA2
> +#define ETP_SMBUS_SM_VERSION_CMD	0xA3
> +#define ETP_SMBUS_UNIQUEID_CMD		0xA3
> +#define ETP_SMBUS_RESOLUTION_CMD	0xA4
> +#define ETP_SMBUS_HELLOPACKET_CMD	0xA7
> +#define ETP_SMBUS_PACKET_QUERY		0xA8
> +#define ETP_SMBUS_IAP_VERSION_CMD	0xAC
> +#define ETP_SMBUS_IAP_CTRL_CMD		0xAD
> +#define ETP_SMBUS_IAP_CHECKSUM_CMD	0xAE
> +#define ETP_SMBUS_FW_CHECKSUM_CMD	0xAF
> +#define ETP_SMBUS_MAX_BASELINE_CMD	0xC3
> +#define ETP_SMBUS_MIN_BASELINE_CMD	0xC4
> +#define ETP_SMBUS_CALIBRATE_QUERY	0xC5
> +#define ETP_SMBUS_REPORT_LEN		32
> +#define ETP_SMBUS_FINGER_DATA_OFFSET	2
> +#define ETP_SMBUS_HELLOPACKET_LEN	5
> +#define ETP_SMBUS_IAP_PASSWORD		0x1234
> +#define ETP_SMBUS_IAP_MODE_ON		(1<<6)
> +
> +/* Elan i2c command */
> +#define ETP_I2C_RESET			0x0100
> +#define ETP_I2C_WAKE_UP			0x0800
> +#define ETP_I2C_SLEEP			0x0801
> +#define ETP_I2C_DESC_CMD		0x0001
> +#define ETP_I2C_REPORT_DESC_CMD		0x0002
> +#define ETP_I2C_STAND_CMD		0x0005
> +#define ETP_I2C_UNIQUEID_CMD		0x0101
> +#define ETP_I2C_FW_VERSION_CMD		0x0102
> +#define ETP_I2C_SM_VERSION_CMD		0x0103
> +#define ETP_I2C_XY_TRACENUM_CMD		0x0105
> +#define ETP_I2C_MAX_X_AXIS_CMD		0x0106
> +#define ETP_I2C_MAX_Y_AXIS_CMD		0x0107
> +#define ETP_I2C_RESOLUTION_CMD		0x0108
> +#define ETP_I2C_IAP_VERSION_CMD		0x0110
> +#define ETP_I2C_SET_CMD			0x0300
> +#define ETP_I2C_MAX_BASELINE_CMD	0x0306
> +#define ETP_I2C_MIN_BASELINE_CMD	0x0307
> +#define ETP_I2C_FW_CHECKSUM_CMD		0x030F
> +#define ETP_I2C_IAP_CTRL_CMD		0x0310
> +#define ETP_I2C_IAP_CMD			0x0311
> +#define ETP_I2C_IAP_RESET_CMD		0x0314
> +#define ETP_I2C_IAP_CHECKSUM_CMD	0x0315
> +#define ETP_I2C_CALIBRATE_CMD		0x0316
> +#define ETP_I2C_REPORT_LEN		34
> +#define ETP_I2C_FINGER_DATA_OFFSET	4
> +#define ETP_I2C_REPORT_ID_OFFSET	2
> +#define ETP_I2C_DESC_LENGTH		30
> +#define ETP_I2C_REPORT_DESC_LENGTH	158
> +#define ETP_I2C_IAP_PASSWORD		0x1EA5
> +#define ETP_I2C_IAP_RESET		0xF0F0
> +#define ETP_I2C_MAIN_MODE_ON		(1<<9)
> +#define ETP_I2C_IAP_REG_L		0x01
> +#define ETP_I2C_IAP_REG_H		0x06
> +
> +/* IAP F/W updater */
> +#define ETP_FW_NAME		"elan_i2c.bin"
> +#define ETP_IAP_VERSION_ADDR	0x0082
> +#define ETP_IAP_START_ADDR	0x0083
> +#define ETP_FW_IAP_PAGE_ERR	(1<<5)
> +#define ETP_FW_IAP_INTERFACE_ERR (1<<4)
> +#define ETP_FW_PAGE_SIZE	64
> +#define ETP_FW_PAGE_COUNT	768
> +#define ETP_FW_SIZE		(ETP_FW_PAGE_SIZE * ETP_FW_PAGE_COUNT)
> +enum {UNKNOWN_MODE, IAP_MODE, MAIN_MODE};
> +
> +struct dbfs_data {
> +	bool	bfetch;
> +	u8	buffer[ETP_MAX_REPORT_LEN];
> +};
> +
> +/* The main device structure */
> +struct elan_tp_data {
> +	struct i2c_client	*client;
> +	struct input_dev	*input;
> +	unsigned int		max_x;
> +	unsigned int		max_y;
> +	unsigned int		width_x;
> +	unsigned int		width_y;
> +	unsigned int		irq;
> +
> +	/* fields required for IAP firmware updater */
> +	u16			unique_id;
> +	u16			fw_version;
> +	u16			sm_version;
> +	u16			iap_version;
> +	bool			updated_fw;
> +	u16			iap_start_addr;
> +
> +	/* irq wake is enabled */
> +	bool			irq_wake;
> +	bool			smbus;
> +	bool			enable_detail_info;
> +
> +	/* fields required for debug fs */
> +	struct mutex		dbfs_mutex;
> +	struct dentry		*dbfs_root;
> +	struct dbfs_data	dbfs_buffer;
> +};
> +
> +u8 val[256];
> +static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 *val);
> +static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd);
> +static int elan_initialize(struct elan_tp_data *data);
> +
> +/*
> + **************************************************************
> + * debugfs interface
> + **************************************************************
> +*/
> +static int elan_dbfs_open(struct inode *inode, struct file *file)
> +{
> +	int retval;
> +	struct elan_tp_data *data = inode->i_private;
> +
> +	if (!data)
> +		return -ENODEV;
> +
> +	retval = mutex_lock_interruptible(&data->dbfs_mutex);
> +	if (retval)
> +		return retval;
> +
> +	if (!kobject_get(&data->client->dev.kobj)) {

Why are you pinning I2C device kobject? It will not prevent unbinding
the driver if that's what you are aiming for.

> +		retval = -ENODEV;
> +		goto dbfs_out;
> +	}
> +
> +	file->private_data = data;
> +dbfs_out:
> +	mutex_unlock(&data->dbfs_mutex);
> +	return 0;
> +}
> +
> +static int elan_dbfs_release(struct inode *inode, struct file *file)
> +{
> +	struct elan_tp_data *data = file->private_data;
> +	int retval;
> +	if (!data)
> +		return -ENODEV;
> +
> +	retval = mutex_lock_interruptible(&data->dbfs_mutex);
> +	if (retval)
> +		return retval;
> +	file->private_data = NULL;
> +	kobject_put(&data->client->dev.kobj);
> +	mutex_unlock(&data->dbfs_mutex);
> +	return 0;
> +}
> +
> +
> +static ssize_t elan_dbfs_read(struct file *file,
> +		char __user *buffer, size_t count, loff_t *ppos)
> +{
> +	struct elan_tp_data *data = file->private_data;
> +	int retval;
> +	if (!data)
> +		return -ENODEV;
> +
> +	retval = mutex_lock_interruptible(&data->dbfs_mutex);
> +	if (retval)
> +		return -EFAULT;
> +	if (data->dbfs_buffer.bfetch == false) {
> +		if (!copy_to_user(buffer, data->dbfs_buffer.buffer, count)) {
> +			data->dbfs_buffer.bfetch = true;
> +			retval = count;
> +		} else {
> +			retval = -2;

-ENOENT? Why?

> +		}
> +	} else {
> +		retval = -4;

-EINTR???

> +	}
> +	mutex_unlock(&data->dbfs_mutex);
> +	return retval;
> +}
> +
> +static ssize_t elan_dbfs_write(struct file *file,
> +		const char __user *buffer, size_t count, loff_t *ppos)
> +{
> +	struct elan_tp_data *data = file->private_data;
> +	int retval;
> +	if (!data)
> +		return -ENODEV;
> +
> +	retval = mutex_lock_interruptible(&data->dbfs_mutex);
> +	if (retval)
> +		return -EFAULT;
> +	retval = count;
> +	mutex_unlock(&data->dbfs_mutex);
> +	return retval;
> +}

This looks like an unneeded stub.

> +
> +static long elan_dbfs_ioctrl(struct file *file,
> +		unsigned int cmd, unsigned long arg)
> +{
> +	int retval = 0;
> +	struct elan_tp_data *data = file->private_data;
> +
> +	retval = mutex_lock_interruptible(&data->dbfs_mutex);
> +	if (retval)
> +		return retval;
> +	mutex_unlock(&data->dbfs_mutex);
> +	return retval;
> +}

Same here.

> +
> +static const struct file_operations elan_debug_fops = {
> +	.open = elan_dbfs_open,
> +	.release = elan_dbfs_release,
> +	.read = elan_dbfs_read,
> +	.write = elan_dbfs_write,
> +	.unlocked_ioctl = elan_dbfs_ioctrl
> +};
> +
> +static int elan_dbfs_init(struct elan_tp_data *data)
> +{
> +	/* Create a global debugfs root for all elan devices */
> +	/* sys/kernel/debug/elan */
> +	data->dbfs_root = debugfs_create_dir("elan", NULL);
> +	if (!data->dbfs_root) {
> +		dev_err(&data->client->dev, "cannot create dbfs_root.\n");
> +		return -ENODEV;
> +	}

That should not be done in per-device code. That said, do you really
need to dump your packets - looks like you are done with the driver
development?

> +	mutex_init(&data->dbfs_mutex);
> +
> +	debugfs_create_file(DRIVER_NAME, 0777,
> +				data->dbfs_root, data, &elan_debug_fops);
> +	data->dbfs_buffer.bfetch = false;
> +	return 0;
> +}
> +
> +/**********************************************************
> + * IAP firmware updater related routines                  *
> + **********************************************************
> +*/
> +
> +static int elan_iap_getmode(struct elan_tp_data *data)
> +{
> +	u16 constant;
> +	int retval;
> +	struct i2c_client *client = data->client;
> +
> +	if (data->smbus) {
> +		retval = i2c_smbus_read_block_data(client,
> +					ETP_SMBUS_IAP_CTRL_CMD, val);
> +		if (retval < 0) {
> +			dev_err(&client->dev, "read iap ctrol fail.\n");
> +			return UNKNOWN_MODE;
> +		}
> +		constant = be16_to_cpup((__be16 *)val);
> +		dev_dbg(&client->dev, "smbus iap control reg: 0x%04x.\n",
> +								constant);
> +		if ((constant & ETP_SMBUS_IAP_MODE_ON) == 0x00)
> +			return MAIN_MODE;
> +	} else {
> +		retval = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val);
> +		if (retval < 0) {
> +			dev_err(&client->dev, "read iap ctrol fail.\n");
> +			return UNKNOWN_MODE;
> +		}
> +		constant = le16_to_cpup((__le16 *)val);
> +		dev_dbg(&client->dev, "i2c iap control reg: 0x%04x.\n",
> +								constant);
> +		if (constant & ETP_I2C_MAIN_MODE_ON)
> +			return MAIN_MODE;
> +	}

Instead of doing

	if (smbus)
		do_this();
	else
		do_that();

everywhere maybe you could define transport functions and invoke them
through a pointer to transport ops?


> +
> +	return IAP_MODE;
> +}
> +
> +static int elan_iap_checksum(struct elan_tp_data *data)
> +{
> +	int retval = 0;
> +	u16 checksum = -1;
> +	struct i2c_client *client = data->client;
> +
> +	if (data->smbus) {
> +		retval = i2c_smbus_read_block_data(client,
> +					ETP_SMBUS_IAP_CHECKSUM_CMD, val);
> +		if (retval < 0) {
> +			dev_err(&client->dev, "Read checksum fail, %d\n",
> +								retval);
> +			return -1;
> +		}
> +		checksum = be16_to_cpup((__be16 *)val);
> +	} else {
> +		retval = elan_i2c_read_cmd(client,
> +					ETP_I2C_IAP_CHECKSUM_CMD, val);
> +		if (retval < 0) {
> +			dev_err(&client->dev, "Read checksum fail, %d\n",
> +								retval);
> +			return -1;
> +		}
> +		checksum = le16_to_cpup((__le16 *)val);
> +	}
> +	return checksum;
> +}
> +
> +static bool elan_iap_reset(struct elan_tp_data *data)
> +{
> +	int retval = 0;
> +	struct i2c_client *client = data->client;
> +
> +	if (data->smbus)
> +		retval = i2c_smbus_write_byte(client,
> +						ETP_SMBUS_IAP_RESET_CMD);
> +	else
> +		retval = elan_i2c_write_cmd(client, ETP_I2C_IAP_RESET_CMD,
> +						ETP_I2C_IAP_RESET);
> +	if (retval < 0) {
> +		dev_err(&client->dev, "cannot reset IC, %d\n", retval);
> +		return false;
> +	}
> +	return true;
> +}
> +
> +static bool elan_iap_setflashkey(struct elan_tp_data *data)
> +{
> +	int retval = 0;
> +	struct i2c_client *client = data->client;
> +	u8 smbus_cmd[4] = {0x00, 0x0B, 0x00, 0x5A};
> +
> +	if (data->smbus)
> +		retval = i2c_smbus_write_block_data(client,
> +				ETP_SMBUS_IAP_CMD, 4, smbus_cmd);
> +	else
> +		retval = elan_i2c_write_cmd(client, ETP_I2C_IAP_CMD,
> +					ETP_I2C_IAP_PASSWORD);
> +	if (retval < 0) {
> +		dev_err(&client->dev, "cannot set flash key, %d\n", retval);
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +static int elan_check_fw(struct elan_tp_data *data,
> +				const struct firmware *fw)
> +{
> +	struct device *dev = &data->client->dev;
> +
> +	/* Firmware must match exact PAGE_NUM * PAGE_SIZE bytes */
> +	if (fw->size != ETP_FW_SIZE) {
> +		dev_err(dev, "invalid firmware size = %zu, expected %d.\n",
> +					fw->size, ETP_FW_SIZE);
> +		return -EBADF;
> +	}
> +
> +	/* Get IAP Start Address*/
> +	memcpy(val, &fw->data[ETP_IAP_START_ADDR * 2], 2);
> +	data->iap_start_addr = le16_to_cpup((__le16 *)val);
> +	return 0;
> +}
> +
> +
> +static int elan_smbus_prepare_fw_update(struct elan_tp_data *data)
> +{
> +	struct i2c_client *client = data->client;
> +	struct device *dev = &data->client->dev;
> +	u16 password;
> +	u8 cmd[4] = {0x0F, 0x78, 0x00, 0x06};
> +
> +	/* Get FW in which mode	(IAP_MODE/MAIN_MODE)  */
> +	int mode = elan_iap_getmode(data);
> +	if (mode == UNKNOWN_MODE)
> +		return -1;
> +
> +	if (mode == MAIN_MODE) {
> +
> +		/* set flash key*/
> +		if (elan_iap_setflashkey(data) == false) {
> +			dev_err(dev, "cannot set flash key\n");
> +			return -1;
> +		}
> +
> +		/* write iap password */
> +		if (i2c_smbus_write_byte(client,
> +				ETP_SMBUS_IAP_PASSWORD_WRITE) < 0) {
> +			dev_err(dev, "cannot write iap password\n");
> +			return -1;

Would be nice using appropriate error codes. EIO?

> +		}
> +
> +		if (i2c_smbus_write_block_data(client,
> +				ETP_SMBUS_IAP_CMD, 4, cmd) < 0) {
> +			dev_err(dev, "cannot write cmd\n");
> +			return -1;
> +		}
> +
> +		/* read password to check we enabled successfully. */
> +		if (i2c_smbus_read_block_data(client,
> +				ETP_SMBUS_IAP_PASSWORD_READ, val) < 0) {
> +			dev_err(dev, "cannot get iap password\n");
> +			return -1;
> +		}
> +		password = be16_to_cpup((__be16 *)val);
> +
> +		if (password != ETP_SMBUS_IAP_PASSWORD) {
> +			dev_err(dev, "wrong iap password = 0x%X\n", password);
> +			return -1;
> +		}
> +		/* wait 30ms, from MAIN_MODE change to IAP_MODE*/
> +		msleep(30);
> +	}
> +
> +	/* set flash key*/
> +	if (elan_iap_setflashkey(data) == false) {
> +		dev_err(dev, "cannot set flash key\n");
> +		return -1;
> +	}
> +
> +	/* Reset IC */
> +	if (elan_iap_reset(data) == false) {
> +		dev_err(dev, "iap reset fail.\n");
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +static int elan_i2c_prepare_fw_update(struct elan_tp_data *data)
> +{
> +	struct i2c_client *client = data->client;
> +	struct device *dev = &data->client->dev;
> +	u16 password;
> +
> +	/* Get FW in which mode	(IAP_MODE/MAIN_MODE)  */
> +	int mode = elan_iap_getmode(data);
> +	if (mode == UNKNOWN_MODE)
> +		return -1;
> +
> +	if (mode == IAP_MODE) {
> +		/* Reset IC */
> +		if (elan_iap_reset(data) == false)
> +			return -1;
> +		msleep(30);
> +	}
> +
> +	/* set flash key*/
> +	if (elan_iap_setflashkey(data) == false) {
> +		dev_err(dev, "cannot set flash key\n");
> +		return -1;
> +	}
> +
> +	/* Wait for F/W IAP initialization */
> +	if (mode == MAIN_MODE)
> +		msleep(100);
> +	else
> +		msleep(30);
> +
> +	/* check is in iap mode or not*/
> +	if (elan_iap_getmode(data) == MAIN_MODE) {
> +		dev_err(dev, "status wrong.\n");
> +		return -1;
> +	}
> +
> +	/* set flash key again */
> +	if (elan_iap_setflashkey(data) == false) {
> +		dev_err(dev, "cannot set flash key\n");
> +		return -1;
> +	}
> +
> +	/* Wait for F/W IAP initialization */
> +	if (mode == MAIN_MODE)
> +		msleep(100);
> +	else
> +		msleep(30);
> +
> +	/* read back to check we actually enabled successfully. */
> +	if (elan_i2c_read_cmd(client, ETP_I2C_IAP_CMD, val) < 0) {
> +		dev_err(dev, "cannot get iap register\n");
> +		return -1;
> +	}
> +	password = le16_to_cpup((__le16 *)val);
> +
> +	if (password != ETP_I2C_IAP_PASSWORD) {
> +		dev_err(dev, "wrong iap password = 0x%X\n", password);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static bool elan_iap_page_write_ok(struct elan_tp_data *data)
> +{
> +	u16 constant;
> +	int retval = 0;
> +	struct i2c_client *client = data->client;
> +
> +
> +	if (data->smbus) {
> +		retval = i2c_smbus_read_block_data(client,
> +					ETP_SMBUS_IAP_CTRL_CMD, val);
> +		if (retval < 0)
> +			return false;
> +		constant = be16_to_cpup((__be16 *)val);
> +	} else {
> +		retval = elan_i2c_read_cmd(client,
> +					ETP_I2C_IAP_CTRL_CMD, val);
> +		if (retval < 0)
> +			return false;
> +		constant = le16_to_cpup((__le16 *)val);
> +	}
> +
> +	if (constant & ETP_FW_IAP_PAGE_ERR)
> +		return false;
> +
> +	if (constant & ETP_FW_IAP_INTERFACE_ERR)
> +		return false;
> +	return true;
> +}
> +
> +static int elan_smbus_write_fw_block(struct elan_tp_data *data,
> +				const u8 *page, u16 checksum, int idx)
> +{
> +	struct device *dev = &data->client->dev;
> +	int half_page_size = ETP_FW_PAGE_SIZE / 2;
> +	int repeat = 3;
> +
> +	do {
> +		/* due to smbus can write 32 bytes one time,
> +		so, we must write data 2 times.
> +		*/
> +		i2c_smbus_write_block_data(data->client,
> +					ETP_SMBUS_WRITE_FW_BLOCK,
> +					half_page_size,
> +					page);
> +		i2c_smbus_write_block_data(data->client,
> +					ETP_SMBUS_WRITE_FW_BLOCK,
> +					half_page_size,
> +					(page + half_page_size));
> +		/* Wait for F/W to update one page ROM data. */
> +		usleep_range(8000, 10000);
> +		if (elan_iap_page_write_ok(data))
> +			break;
> +		dev_info(dev, "IAP retry this page! [%d]\n", idx);
> +		repeat--;
> +	} while (repeat == 0);
> +
> +	if (repeat > 0)
> +		return 0;
> +	return -1;
> +
> +}
> +
> +static int elan_i2c_write_fw_block(struct elan_tp_data *data,
> +				const u8 *page, u16 checksum, int idx)
> +{
> +	struct device *dev = &data->client->dev;
> +	int ret;
> +	int repeat = 3;
> +	u8 page_store[ETP_FW_PAGE_SIZE + 4];
> +
> +	page_store[0] = ETP_I2C_IAP_REG_L;
> +	page_store[1] = ETP_I2C_IAP_REG_H;
> +	memcpy(&page_store[2], page, ETP_FW_PAGE_SIZE);
> +
> +	/* recode checksum at last two bytes */
> +	page_store[ETP_FW_PAGE_SIZE+2] = (u8)(checksum & 0xFF);
> +	page_store[ETP_FW_PAGE_SIZE+3] = (u8)((checksum >> 8)&0xFF);
> +
> +	do {
> +		ret = i2c_master_send(data->client, page_store,
> +						ETP_FW_PAGE_SIZE + 4);
> +
> +		/* Wait for F/W to update one page ROM data. */
> +		msleep(20);
> +
> +		if (ret == (ETP_FW_PAGE_SIZE + 4)) {
> +			if (elan_iap_page_write_ok(data))
> +				break;
> +		}
> +		dev_dbg(dev, "IAP retry this page! [%d]\n", idx);
> +		repeat--;
> +	} while (repeat == 0);
> +
> +	if (repeat > 0)
> +		return 0;
> +	return -1;
> +}
> +
> +static int elan_write_fw_block(struct elan_tp_data *data,
> +				const u8 *page, u16 checksum, int idx)
> +{
> +	int ret;
> +	if (data->smbus)
> +		ret = elan_smbus_write_fw_block(data, page, checksum, idx);
> +	else
> +		ret = elan_i2c_write_fw_block(data, page, checksum, idx);
> +	return ret;
> +}
> +
> +static int elan_prepare_fw_update(struct elan_tp_data *data)
> +{
> +	int ret = 0;
> +	if (data->smbus)
> +		ret = elan_smbus_prepare_fw_update(data);
> +	else
> +		ret = elan_i2c_prepare_fw_update(data);
> +	return ret;
> +}
> +
> +static int elan_firmware(struct elan_tp_data *data)
> +{
> +	struct device *dev = &data->client->dev;
> +	const struct firmware *fw;
> +	const char *fw_name = ETP_FW_NAME;
> +	int i, j, ret;
> +	u16 boot_page_count;
> +	u16 sw_checksum, fw_checksum;
> +	data->updated_fw = true;
> +
> +	dev_info(dev, "Start firmware update....\n");
> +
> +	ret = request_firmware(&fw, ETP_FW_NAME, dev);
> +	if (ret) {
> +		dev_err(dev, "cannot load firmware from %s, %d\n",
> +							fw_name, ret);
> +		goto done;
> +	}
> +	/* check fw data match current iap version */
> +	ret = elan_check_fw(data, fw);
> +	if (ret) {
> +		dev_err(dev, "Invalid Elan firmware from %s, %d\n",
> +							fw_name, ret);
> +		goto done;
> +	}
> +	/* setup IAP status */
> +	ret = elan_prepare_fw_update(data);
> +	if (ret)
> +		goto done;
> +	sw_checksum = 0;
> +	fw_checksum = 0;
> +	boot_page_count = (data->iap_start_addr * 2) / ETP_FW_PAGE_SIZE;
> +	for (i = boot_page_count; i < ETP_FW_PAGE_COUNT; i++) {
> +		u16 checksum = 0;
> +		const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE];
> +
> +		for (j = 0; j < ETP_FW_PAGE_SIZE; j += 2)
> +			checksum += ((page[j + 1] << 8) | page[j]);
> +
> +		ret = elan_write_fw_block(data, page, checksum, i);
> +		if (ret) {
> +			dev_err(dev, "write page %d fail\n", i);
> +			goto done;
> +		}
> +		sw_checksum += checksum;
> +	}
> +
> +	/* Wait WDT reset and power on reset */
> +	msleep(600);
> +
> +	/* check checksum */
> +	fw_checksum = elan_iap_checksum(data);
> +	if (sw_checksum != fw_checksum) {
> +		dev_err(dev, "checksum diff sw=[%04X], fw=[%04X]\n",
> +					sw_checksum, fw_checksum);
> +		ret = -1;
> +		goto done;
> +	}
> +	ret = 0;
> +done:
> +	if (ret != 0) {
> +		elan_iap_reset(data);
> +		data->updated_fw = false;
> +	} else {
> +		if (data->smbus) {
> +			data->updated_fw = false;
> +			elan_initialize(data);
> +		}
> +	}
> +	release_firmware(fw);
> +	return ret;
> +}
> +
> +/******************************************************************
> +* Elan smbus interface
> +*******************************************************************
> +*/
> +static int elan_smbus_initialize(struct i2c_client *client)
> +{
> +	u8 check[ETP_SMBUS_HELLOPACKET_LEN] = {0x55, 0x55, 0x55, 0x55, 0x55};
> +	u8 values[ETP_SMBUS_HELLOPACKET_LEN] = {0, 0, 0, 0, 0};
> +	int ret;
> +
> +	/* Get hello packet */
> +	ret = i2c_smbus_read_block_data(client,
> +				ETP_SMBUS_HELLOPACKET_CMD, values);
> +	if (ret != ETP_SMBUS_HELLOPACKET_LEN) {
> +		dev_err(&client->dev, "hello packet length fail\n");
> +		return -1;
> +	}
> +
> +	/* compare hello packet */
> +	if (memcmp(values, check, ETP_SMBUS_HELLOPACKET_LEN)) {
> +		dev_err(&client->dev, "hello packet fail [%x %x %x %x %x]\n",
> +		values[0], values[1], values[2], values[3], values[4]);
> +		return -1;
> +	}
> +
> +	/* enable tp */
> +	ret = i2c_smbus_write_byte(client, ETP_SMBUS_ENABLE_TP);
> +	return ret;
> +}
> +
> +static int elan_smbus_enable_calibrate(struct i2c_client *client)
> +{
> +	u8 cmd[4] = {0x00, 0x07, 0x00, ETP_ENABLE_ABS|ETP_ENABLE_CALIBRATE};
> +
> +	return i2c_smbus_write_block_data(client,
> +				ETP_SMBUS_IAP_CMD, 4, cmd);
> +}
> +
> +static int elan_smbus_disable_calibrate(struct i2c_client *client)
> +{
> +	u8 cmd[4] = {0x00, 0x07, 0x00, ETP_ENABLE_ABS|ETP_DISABLE_CALIBRATE};
> +
> +	return i2c_smbus_write_block_data(client,
> +				ETP_SMBUS_IAP_CMD, 4, cmd);
> +}
> +
> +static int elan_smbus_enable_absolute_mode(struct i2c_client *client)
> +{
> +	u8 cmd[4] = {0x00, 0x07, 0x00, ETP_ENABLE_ABS};
> +
> +	return i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD, 4, cmd);
> +}
> +
> +/*****************************************************************
> +* Elan i2c interface
> +******************************************************************
> +*/
> +static int elan_i2c_read_block(struct i2c_client *client,
> +				 u16 reg, u8 *val, u16 len)
> +{
> +	struct i2c_msg msgs[2];
> +	u8 buf[2];
> +	int ret;
> +
> +	buf[0] = reg & 0xff;
> +	buf[1] = (reg >> 8) & 0xff;
> +
> +	msgs[0].addr = client->addr;
> +	msgs[0].flags = client->flags & I2C_M_TEN;
> +	msgs[0].len = 2;
> +	msgs[0].buf = buf;
> +
> +	msgs[1].addr = client->addr;
> +	msgs[1].flags = client->flags & I2C_M_TEN;
> +	msgs[1].flags |= I2C_M_RD;
> +	msgs[1].len = len;
> +	msgs[1].buf = val;
> +
> +	ret = i2c_transfer(client->adapter, msgs, 2);
> +	return ret != 2 ? -EIO : 0;
> +}
> +
> +static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 *val)
> +{
> +	int retval;
> +
> +	retval = elan_i2c_read_block(client, reg, val, ETP_INF_LENGTH);
> +	if (retval < 0) {
> +		dev_err(&client->dev, "reading cmd (0x%04x) fail.\n", reg);
> +		return retval;
> +	}
> +	return 0;
> +}
> +
> +static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd)
> +{
> +	struct i2c_msg msg;
> +	u8 buf[4];
> +	int ret;
> +
> +	buf[0] = reg & 0xff;
> +	buf[1] = (reg >> 8) & 0xff;
> +	buf[2] = cmd & 0xff;
> +	buf[3] = (cmd >> 8) & 0xff;
> +
> +	msg.addr = client->addr;
> +	msg.flags = client->flags & I2C_M_TEN;
> +	msg.len = 4;
> +	msg.buf = buf;
> +
> +	ret = i2c_transfer(client->adapter, &msg, 1);
> +	return ret != 1 ? -EIO : 0;
> +}
> +
> +static int elan_i2c_reset(struct i2c_client *client)
> +{
> +	return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
> +						ETP_I2C_RESET);
> +}
> +
> +static int elan_i2c_wake_up(struct i2c_client *client)
> +{
> +	return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
> +						ETP_I2C_WAKE_UP);
> +}
> +
> +static int elan_i2c_sleep(struct i2c_client *client)
> +{
> +	return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
> +						ETP_I2C_SLEEP);
> +}
> +
> +static int elan_i2c_enable_absolute_mode(struct i2c_client *client)
> +{
> +	return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
> +						ETP_ENABLE_ABS);
> +}
> +
> +static int elan_i2c_enable_calibrate(struct i2c_client *client)
> +{
> +	return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
> +			ETP_ENABLE_ABS|ETP_ENABLE_CALIBRATE);
> +}
> +
> +static int elan_i2c_disable_calibrate(struct i2c_client *client)
> +{
> +	return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
> +			ETP_ENABLE_ABS|ETP_DISABLE_CALIBRATE);
> +}
> +
> +static int elan_i2c_get_desc(struct i2c_client *client, u8 *val)
> +{
> +	return elan_i2c_read_block(client, ETP_I2C_DESC_CMD, val,
> +					ETP_I2C_DESC_LENGTH);
> +}
> +
> +static int elan_i2c_get_report_desc(struct i2c_client *client, u8 *val)
> +{
> +	return elan_i2c_read_block(client, ETP_I2C_REPORT_DESC_CMD,
> +				 val, ETP_I2C_REPORT_DESC_LENGTH);
> +}
> +
> +static int elan_i2c_initialize(struct i2c_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	int rc;
> +
> +	rc = elan_i2c_reset(client);
> +	if (rc < 0) {
> +		dev_err(dev, "device reset failed.\n");
> +		return -1;
> +	}
> +
> +	/* wait for get reset return flag */
> +	msleep(100);
> +	/* get reset return flag 0000 */
> +	rc = i2c_master_recv(client, val, ETP_INF_LENGTH);
> +	if (rc < 0) {
> +		dev_err(dev, "get device reset return value failed.\n");
> +		return -1;
> +	}
> +
> +	rc = elan_i2c_get_desc(client, val);
> +	if (rc < 0) {
> +		dev_err(dev, "cannot get device descriptor.\n");
> +		return -1;
> +	}
> +
> +	rc = elan_i2c_get_report_desc(client, val);
> +	if (rc < 0) {
> +		dev_err(dev, "fetching report descriptor failed.\n");
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +/**************************************************************************
> +* Genernal functions
> +***************************************************************************
> +*/
> +
> +/*
> + * (value from firmware) * 10 + 790 = dpi
> + * we also have to convert dpi to dots/mm (*10/254 to avoid floating point)
> + */
> +static unsigned int elan_convert_res(char val)
> +{
> +	int res;
> +	if (val & 0x80) {
> +		val = ~val + 1;
> +		res = (790 - val * 10) * 10 / 254;
> +	} else
> +		res = (val * 10 + 790) * 10 / 254;
> +	return res;
> +}
> +
> +static int elan_get_iap_version(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus) {
> +		i2c_smbus_read_block_data(data->client,
> +					ETP_SMBUS_IAP_VERSION_CMD, val);
> +		ret = val[2];
> +	} else {
> +		elan_i2c_read_cmd(data->client,
> +				ETP_I2C_IAP_VERSION_CMD, val);
> +		ret = val[0];
> +	}
> +	return ret;
> +}
> +
> +static int elan_get_x_max(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus) {
> +		i2c_smbus_read_block_data(data->client,
> +					ETP_SMBUS_RANGE_CMD, val);
> +		ret = (0x0f & val[0]) << 8 | val[1];
> +	} else {
> +		elan_i2c_read_cmd(data->client,
> +					ETP_I2C_MAX_X_AXIS_CMD, val);
> +		ret = (0x0f & val[1]) << 8 | val[0];
> +	}
> +	return ret;
> +}
> +
> +static int elan_get_y_max(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus) {
> +		i2c_smbus_read_block_data(data->client,
> +					ETP_SMBUS_RANGE_CMD, val);
> +		ret = (0xf0 & val[0]) << 4 | val[2];
> +	} else {
> +		elan_i2c_read_cmd(data->client,
> +					ETP_I2C_MAX_Y_AXIS_CMD, val);
> +		ret = (0x0f & val[1]) << 8 | val[0];
> +	}
> +	return ret;
> +}
> +
> +static int elan_get_x_tracenum(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus) {
> +		i2c_smbus_read_block_data(data->client,
> +				ETP_SMBUS_XY_TRACENUM_CMD, val);
> +		ret = (val[1] - 1);
> +	} else {
> +		elan_i2c_read_cmd(data->client,
> +				ETP_I2C_XY_TRACENUM_CMD, val);
> +		ret = (val[0] - 1);
> +	}
> +	return ret;
> +}
> +
> +static int elan_get_y_tracenum(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus) {
> +		i2c_smbus_read_block_data(data->client,
> +				ETP_SMBUS_XY_TRACENUM_CMD, val);
> +		ret = (val[2] - 1);
> +	} else {
> +		ret = elan_i2c_read_cmd(data->client,
> +				ETP_I2C_XY_TRACENUM_CMD, val);
> +		ret = (val[1] - 1);
> +	}
> +	return ret;
> +}
> +
> +static int elan_get_fw_version(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus) {
> +		i2c_smbus_read_block_data(data->client,
> +				ETP_SMBUS_FW_VERSION_CMD, val);
> +		ret = val[2];
> +	} else {
> +		elan_i2c_read_cmd(data->client,
> +				ETP_I2C_FW_VERSION_CMD, val);
> +		ret = val[0];
> +	}
> +	return ret;
> +}
> +
> +static int elan_get_sm_version(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus)
> +		i2c_smbus_read_block_data(data->client,
> +				ETP_SMBUS_SM_VERSION_CMD, val);
> +	else
> +		elan_i2c_read_block(data->client,
> +				ETP_I2C_SM_VERSION_CMD, val, 1);
> +	ret = val[0];
> +	return ret;
> +}
> +
> +static int elan_get_unique_id(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus) {
> +		i2c_smbus_read_block_data(data->client,
> +				ETP_SMBUS_UNIQUEID_CMD, val);
> +		ret = val[1];
> +	} else {
> +		elan_i2c_read_cmd(data->client,
> +				ETP_I2C_UNIQUEID_CMD, val);
> +		ret = val[0];
> +	}
> +	return ret;
> +}
> +
> +static int elan_get_x_resolution(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus) {
> +		i2c_smbus_read_block_data(data->client,
> +				ETP_SMBUS_RESOLUTION_CMD, val);
> +		ret = elan_convert_res(val[1] & 0x0F);
> +	} else {
> +		elan_i2c_read_cmd(data->client,
> +				ETP_I2C_RESOLUTION_CMD, val);
> +		ret = elan_convert_res(val[0]);
> +	}
> +	return ret;
> +}
> +
> +static int elan_get_y_resolution(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus) {
> +		i2c_smbus_read_block_data(data->client,
> +				ETP_SMBUS_RESOLUTION_CMD, val);
> +		ret = elan_convert_res((val[1] & 0xF0) >> 4);
> +	} else {
> +		elan_i2c_read_cmd(data->client,
> +				ETP_I2C_RESOLUTION_CMD, val);
> +		ret = elan_convert_res(val[1]);
> +	}
> +	return ret;
> +}
> +
> +static int elan_get_fw_checksum(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus) {
> +		i2c_smbus_read_block_data(data->client,
> +				ETP_SMBUS_FW_CHECKSUM_CMD, val);
> +		ret = be16_to_cpup((__be16 *)val);
> +	} else {
> +		elan_i2c_read_cmd(data->client,
> +				ETP_I2C_FW_CHECKSUM_CMD, val);
> +		ret = le16_to_cpup((__le16 *)val);
> +	}
> +	return ret;
> +}
> +
> +static int elan_get_max_baseline(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus) {
> +		i2c_smbus_read_block_data(data->client,
> +				ETP_SMBUS_MAX_BASELINE_CMD, val);
> +		ret = be16_to_cpup((__be16 *)val);
> +	} else {
> +		elan_i2c_read_cmd(data->client,
> +				ETP_I2C_MAX_BASELINE_CMD, val);
> +		ret = le16_to_cpup((__le16 *)val);
> +	}
> +	return ret;
> +}
> +
> +static int elan_get_min_baseline(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus) {
> +		i2c_smbus_read_block_data(data->client,
> +				ETP_SMBUS_MIN_BASELINE_CMD, val);
> +		ret = be16_to_cpup((__be16 *)val);
> +	} else {
> +		elan_i2c_read_cmd(data->client,
> +			ETP_I2C_MIN_BASELINE_CMD, val);
> +		ret = le16_to_cpup((__le16 *)val);
> +	}
> +	return ret;
> +}
> +
> +static int elan_enable_calibrate(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus)
> +		ret = elan_smbus_enable_calibrate(data->client);
> +	else
> +		ret = elan_i2c_enable_calibrate(data->client);
> +	return ret;
> +}
> +
> +static int elan_disable_calibrate(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus)
> +		ret = elan_smbus_disable_calibrate(data->client);
> +	else
> +		ret = elan_i2c_disable_calibrate(data->client);
> +	return ret;
> +}
> +
> +static int elan_initialize(struct elan_tp_data *data)
> +{
> +	int ret;
> +	if (data->smbus) {
> +		ret = elan_smbus_initialize(data->client);
> +		if (ret < 0) {
> +			dev_err(&data->client->dev,
> +				"device initialize failed.\n");
> +			goto err_initialize;
> +		}
> +
> +		ret = elan_smbus_enable_absolute_mode(data->client);
> +		if (ret < 0)
> +			dev_err(&data->client->dev,
> +				"cannot switch to absolute mode.\n");
> +	} else {
> +		ret = elan_i2c_initialize(data->client);
> +		if (ret < 0) {
> +			dev_err(&data->client->dev,
> +				"device initialize failed.\n");
> +			goto err_initialize;
> +		}
> +
> +		ret = elan_i2c_enable_absolute_mode(data->client);
> +		if (ret < 0) {
> +			dev_err(&data->client->dev,
> +				"cannot switch to absolute mode.\n");
> +			goto err_initialize;
> +		}
> +
> +		ret = elan_i2c_wake_up(data->client);
> +		if (ret < 0)
> +			dev_err(&data->client->dev,
> +					"device wake up failed.\n");
> +	}
> +err_initialize:
> +	return ret;
> +}
> +
> +/********************************************************************
> + * below routines export interfaces to sysfs file system.
> + * so user can get firmware/driver/hardware information using cat command.
> + * e.g.: use below command to get firmware version
> + *      cat /sys/bus/i2c/drivers/elan_i2c/1-0015/firmware_version
> + *******************************************************************
> + */
> +static ssize_t elan_sysfs_enable_detailinfo(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	struct elan_tp_data *data = dev_get_drvdata(dev);
> +	data->enable_detail_info = true;

What is this for?

> +	return sprintf(buf, "enable\n");
> +}
> +
> +static ssize_t elan_sysfs_read_fw_checksum(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	unsigned int checksum = 0;
> +	struct elan_tp_data *data = dev_get_drvdata(dev);
> +	if (data->enable_detail_info == true) {
> +		checksum = elan_get_fw_checksum(data);
> +		data->enable_detail_info = false;
> +	}
> +	return sprintf(buf, "0x%04x\n", checksum);
> +}
> +
> +static ssize_t elan_sysfs_read_unique_id(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	struct elan_tp_data *data = dev_get_drvdata(dev);
> +	data->unique_id = elan_get_unique_id(data);
> +	return sprintf(buf, "0x%04x\n", data->unique_id);
> +}
> +
> +static ssize_t elan_sysfs_read_driver_ver(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	return sprintf(buf, "%s\n", ELAN_DRIVER_VERSION);

This should be reported in MODULE_VERSION if you need it.

> +}
> +
> +static ssize_t elan_sysfs_read_fw_ver(struct device *dev,
> +				    struct device_attribute *attr,
> +				    char *buf)
> +{
> +	struct elan_tp_data *data = dev_get_drvdata(dev);
> +	data->fw_version = elan_get_fw_version(data);
> +	return sprintf(buf, "0x%04x\n", data->fw_version);
> +}
> +
> +static ssize_t elan_sysfs_read_sm_ver(struct device *dev,
> +				    struct device_attribute *attr,
> +				    char *buf)
> +{
> +	struct elan_tp_data *data = dev_get_drvdata(dev);
> +	data->sm_version = elan_get_sm_version(data);
> +	return sprintf(buf, "0x%04x\n", data->sm_version);
> +}
> +
> +static ssize_t elan_sysfs_read_iap_ver(struct device *dev,
> +				     struct device_attribute *attr,
> +				     char *buf)
> +{
> +	struct elan_tp_data *data = dev_get_drvdata(dev);
> +	data->iap_version = elan_get_iap_version(data);
> +	return sprintf(buf, "0x%04x\n", data->iap_version);
> +}
> +
> +
> +static ssize_t elan_sysfs_update_fw(struct device *dev,
> +				  struct device_attribute *attr,
> +				  const char *buf, size_t count)
> +{
> +	struct elan_tp_data *data = dev_get_drvdata(dev);
> +	int ret;
> +	ret = elan_firmware(data);
> +	if (ret)
> +		dev_err(dev, "firmware update failed.\n");
> +	else
> +		dev_info(dev, "firmware update succeeded.\n");
> +	return ret ? ret : count;
> +}
> +
> +static ssize_t elan_sysfs_calibrate(struct device *dev,
> +				struct device_attribute *attr,
> +				char *buf)
> +{
> +	struct elan_tp_data *data = dev_get_drvdata(dev);
> +	/* start calibarate cmd */
> +	u8 smbus_cmd[4] = {0x00, 0x08, 0x00, 0x01};
> +	int tries = 20;
> +	int ret = 0;
> +	val[0] = 0;
> +
> +	disable_irq(data->irq);
> +	elan_enable_calibrate(data);
> +	if (data->smbus)
> +		i2c_smbus_write_block_data(data->client,
> +				ETP_SMBUS_IAP_CMD, 4, smbus_cmd);
> +	else
> +		elan_i2c_write_cmd(data->client,
> +					ETP_I2C_CALIBRATE_CMD, 1);
> +
> +	do {
> +		/* wait 250ms and check finish or not */
> +		msleep(250);
> +
> +		if (data->smbus)
> +			i2c_smbus_read_block_data(data->client,
> +					ETP_SMBUS_CALIBRATE_QUERY, val);
> +		else
> +			elan_i2c_read_block(data->client,
> +					ETP_I2C_CALIBRATE_CMD, val, 1);
> +
> +		/* calibrate finish */
> +		if (val[0] == 0)
> +			break;
> +	} while (--tries);
> +
> +	elan_disable_calibrate(data);
> +	enable_irq(data->irq);
> +
> +	if (tries == 0) {
> +		dev_err(dev, "Failed to calibrate. Timeout.\n");
> +		ret = -ETIMEDOUT;
> +	}
> +	return sprintf(buf, "calibration finish\n");
> +}
> +
> +
> +static ssize_t elan_sysfs_read_baseline(struct device *dev,
> +				   struct device_attribute *attr,
> +				   char *buf)
> +{
> +	struct elan_tp_data *data = dev_get_drvdata(dev);
> +	int max_baseline, min_baseline;
> +
> +	disable_irq(data->irq);
> +	elan_enable_calibrate(data);
> +	msleep(250);
> +	max_baseline = elan_get_max_baseline(data);
> +	min_baseline = elan_get_min_baseline(data);
> +	elan_disable_calibrate(data);
> +	enable_irq(data->irq);
> +	return sprintf(buf, "max:%d min:%d\n", max_baseline, min_baseline);
> +}
> +
> +static ssize_t elan_sysfs_reinitialize(struct device *dev,
> +				struct device_attribute *attr,
> +				char *buf)
> +{
> +	struct elan_tp_data *data = dev_get_drvdata(dev);
> +	int ret;
> +
> +	disable_irq(data->irq);
> +	ret = elan_initialize(data);
> +	enable_irq(data->irq);
> +
> +	if (ret < 0)
> +		return sprintf(buf, "reinitialize fail\n");
> +
> +	return sprintf(buf, "reinitialize success\n");

This actually should be a write method and instead of returning strings
you should use return code to indicate success/failure.

> +}
> +
> +static DEVICE_ATTR(unique_id, S_IRUGO, elan_sysfs_read_unique_id, NULL);
> +static DEVICE_ATTR(firmware_version, S_IRUGO, elan_sysfs_read_fw_ver, NULL);
> +static DEVICE_ATTR(sample_version, S_IRUGO, elan_sysfs_read_sm_ver, NULL);
> +static DEVICE_ATTR(driver_version, S_IRUGO, elan_sysfs_read_driver_ver, NULL);
> +static DEVICE_ATTR(iap_version, S_IRUGO, elan_sysfs_read_iap_ver, NULL);
> +static DEVICE_ATTR(fw_checksum, S_IRUGO, elan_sysfs_read_fw_checksum, NULL);
> +static DEVICE_ATTR(open_info, S_IRUGO, elan_sysfs_enable_detailinfo, NULL);
> +static DEVICE_ATTR(baseline, S_IRUGO, elan_sysfs_read_baseline, NULL);
> +static DEVICE_ATTR(reinitialize, S_IRUGO, elan_sysfs_reinitialize, NULL);
> +static DEVICE_ATTR(calibrate, S_IRUGO, elan_sysfs_calibrate, NULL);
> +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, elan_sysfs_update_fw);
> +
> +static struct attribute *elan_sysfs_entries[] = {
> +	&dev_attr_unique_id.attr,
> +	&dev_attr_firmware_version.attr,
> +	&dev_attr_sample_version.attr,
> +	&dev_attr_driver_version.attr,
> +	&dev_attr_iap_version.attr,
> +	&dev_attr_fw_checksum.attr,
> +	&dev_attr_open_info.attr,
> +	&dev_attr_baseline.attr,
> +	&dev_attr_reinitialize.attr,
> +	&dev_attr_calibrate.attr,
> +	&dev_attr_update_fw.attr,
> +	NULL,

Please add documentation for sysfs entries to Documentation/... 

> +};
> +
> +static const struct attribute_group elan_sysfs_group = {
> +	.attrs = elan_sysfs_entries,
> +};
> +
> +/*****************************************************************
> +* Elan isr functions
> +******************************************************************
> +*/
> +
> +static int elan_check_packet(struct elan_tp_data *data, u8 *packet)
> +{
> +	u8 rid;
> +
> +	if (data->smbus)
> +		rid = packet[0];
> +	else
> +		rid = packet[ETP_I2C_REPORT_ID_OFFSET];
> +
> +	/* check report id */
> +	if (rid != ETP_REPORT_ID) {
> +		dev_err(&data->client->dev, "report id [%x] fail.\n", rid);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
> +{
> +	struct input_dev *input = data->input;
> +	u8 *finger_data;
> +	bool finger_on;
> +	int pos_x, pos_y;
> +	int pressure, mk_x, mk_y;
> +	int i, area_x, area_y, major, minor, new_pressure;
> +	int finger_count = 0;
> +	int btn_click;
> +	u8  tp_info;
> +
> +	if (data->smbus) {
> +		finger_data = &packet[ETP_SMBUS_FINGER_DATA_OFFSET];
> +		tp_info = packet[1];
> +	} else {
> +		finger_data = &packet[ETP_I2C_FINGER_DATA_OFFSET];
> +		tp_info = packet[3];
> +	}
> +
> +	btn_click = (tp_info & 0x01);
> +	for (i = 0; i < ETP_MAX_FINGERS; i++) {
> +		finger_on = (tp_info >> (3 + i)) & 0x01;
> +
> +		/* analyze touched finger raw data*/
> +		if (finger_on) {
> +			pos_x = ((finger_data[0] & 0xf0) << 4) |
> +				finger_data[1];
> +			pos_y = ((finger_data[0] & 0x0f) << 8) |
> +				finger_data[2];
> +			pos_y =  data->max_y - pos_y;
> +			mk_x = (finger_data[3] & 0x0f);
> +			mk_y = (finger_data[3] >> 4);
> +			pressure = finger_data[4];
> +
> +			/*
> +			to avoid fat finger be as palm, so reduce the
> +			width x and y per trace
> +			*/
> +			area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE);
> +			area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE);
> +
> +			major = max(area_x, area_y);
> +			minor = min(area_x, area_y);
> +
> +			new_pressure = pressure + ETP_PRESSURE_OFFSET;
> +			if (new_pressure > ETP_MAX_PRESSURE)
> +				new_pressure = ETP_MAX_PRESSURE;
> +
> +			input_mt_slot(input, i);
> +			input_mt_report_slot_state(input, MT_TOOL_FINGER,
> +									true);
> +			input_report_abs(input, ABS_MT_POSITION_X, pos_x);
> +			input_report_abs(input, ABS_MT_POSITION_Y, pos_y);
> +			input_report_abs(input, ABS_MT_PRESSURE, new_pressure);
> +			input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
> +			input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
> +			input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
> +			finger_data += ETP_FINGER_DATA_LEN;
> +			finger_count++;
> +		} else {
> +			input_mt_slot(input, i);
> +			input_mt_report_slot_state(input,
> +						MT_TOOL_FINGER, false);
> +		}
> +	}
> +
> +	input_report_key(input, BTN_LEFT, (btn_click == 1));
> +	input_mt_report_pointer_emulation(input, true);
> +	input_sync(input);
> +}
> +
> +static irqreturn_t elan_isr(int irq, void *dev_id)
> +{
> +	struct elan_tp_data *data = dev_id;
> +	u8 raw[ETP_MAX_REPORT_LEN];
> +	int retval;
> +	int report_len;
> +
> +	retval = mutex_lock_interruptible(&data->dbfs_mutex);
> +	if (retval)
> +		return IRQ_HANDLED;
> +
> +	/*
> +	Only in I2C protocol, when IAP all page wrote finish, driver will
> +	get one INT signal from high to low, and driver must get 0000
> +	to confirm IAP is finished.
> +	*/
> +	if (data->updated_fw) {
> +		retval = i2c_master_recv(data->client, raw,
> +						ETP_INF_LENGTH);
> +		if (retval == 2 && !le16_to_cpup((__le16 *)raw)) {
> +			dev_info(&data->client->dev,
> +				"reinitializing after F/W update...");
> +			elan_initialize(data);
> +		}
> +		data->updated_fw = false;
> +		goto elan_isr_end;
> +	}
> +
> +	if (data->smbus) {
> +		report_len = ETP_SMBUS_REPORT_LEN;
> +		retval = i2c_smbus_read_block_data(data->client,
> +					ETP_SMBUS_PACKET_QUERY, raw);
> +	} else {
> +		report_len = ETP_I2C_REPORT_LEN;
> +		retval = i2c_master_recv(data->client, raw, report_len);
> +	}
> +
> +	if (retval != report_len) {
> +		dev_err(&data->client->dev, "wrong packet len(%d)", retval);
> +		goto elan_isr_end;
> +	}
> +
> +	if (elan_check_packet(data, raw) < 0) {
> +		dev_err(&data->client->dev, "wrong packet format.");
> +		goto elan_isr_end;
> +	}
> +	elan_report_absolute(data, raw);
> +	data->dbfs_buffer.bfetch = false;
> +	memcpy(data->dbfs_buffer.buffer, raw, report_len);
> +
> +elan_isr_end:
> +	mutex_unlock(&data->dbfs_mutex);
> +	return IRQ_HANDLED;
> +}
> +
> +
> +static int elan_input_dev_create(struct elan_tp_data *data)
> +{
> +	struct i2c_client *client = data->client;
> +	struct input_dev *input;
> +	unsigned int x_res, y_res;
> +	int ret;
> +
> +	data->input = input = input_allocate_device();
> +	if (!input)
> +		return -ENOMEM;
> +	input->name = "Elan Touchpad";
> +	input->id.bustype = BUS_I2C;
> +	input->dev.parent = &data->client->dev;
> +
> +	__set_bit(INPUT_PROP_POINTER, input->propbit);
> +	__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
> +	__set_bit(EV_KEY, input->evbit);
> +	__set_bit(EV_ABS, input->evbit);
> +
> +	__set_bit(BTN_LEFT, input->keybit);
> +	__set_bit(BTN_TOUCH, input->keybit);
> +	__set_bit(BTN_TOOL_FINGER, input->keybit);
> +	__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
> +	__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
> +	__set_bit(BTN_TOOL_QUADTAP, input->keybit);
> +	__set_bit(BTN_TOOL_QUINTTAP, input->keybit);
> +
> +	__set_bit(ABS_MT_TOUCH_MAJOR, input->absbit);
> +	__set_bit(ABS_MT_TOUCH_MINOR, input->absbit);
> +	__set_bit(ABS_MT_POSITION_X, input->absbit);
> +	__set_bit(ABS_MT_POSITION_Y, input->absbit);
> +
> +	data->unique_id = elan_get_unique_id(data);
> +	data->fw_version = elan_get_fw_version(data);
> +	data->sm_version = elan_get_sm_version(data);
> +	data->iap_version = elan_get_iap_version(data);
> +	data->max_x = elan_get_x_max(data);
> +	data->max_y = elan_get_y_max(data);
> +	data->width_x = data->max_x / elan_get_x_tracenum(data);
> +	data->width_y = data->max_y / elan_get_y_tracenum(data);
> +	x_res = elan_get_x_resolution(data);
> +	y_res = elan_get_y_resolution(data);
> +
> +	dev_info(&client->dev,
> +		"Elan Touchpad Information:\n"
> +		"    Module unique ID:  0x%04x\n"
> +		"    Firmware Version:  0x%04x\n"
> +		"    Sample Version:  0x%04x\n"
> +		"    IAP Version:  0x%04x\n"
> +		"    Max ABS X,Y:   %d,%d\n"
> +		"    Width X,Y:   %d,%d\n"
> +		"    Resolution X,Y:   %d,%d (dots/mm)\n",
> +		data->unique_id,
> +		data->fw_version,
> +		data->sm_version,
> +		data->iap_version,
> +		data->max_x, data->max_y,
> +		data->width_x, data->width_y,
> +		(char)x_res, (char)y_res);

Why cast to char?

> +
> +	input_set_abs_params(input, ABS_X, 0, data->max_x, 0, 0);
> +	input_set_abs_params(input, ABS_Y, 0, data->max_y, 0, 0);
> +	input_abs_set_res(input, ABS_X, x_res);
> +	input_abs_set_res(input, ABS_Y, y_res);
> +	input_set_abs_params(input, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0);
> +	input_set_abs_params(input, ABS_TOOL_WIDTH, 0, ETP_FINGER_WIDTH, 0, 0);
> +
> +	/* handle pointer emulation and unused slots in core */
> +	ret = input_mt_init_slots(input, ETP_MAX_FINGERS,
> +				  INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
> +	if (ret) {
> +		dev_info(&client->dev, "allocate MT slots failed, %d\n", ret);
> +		goto err_free_device;
> +	}
> +	input_set_abs_params(input, ABS_MT_POSITION_X, 0, data->max_x, 0, 0);
> +	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, data->max_y, 0, 0);
> +	input_abs_set_res(input, ABS_MT_POSITION_X, x_res);
> +	input_abs_set_res(input, ABS_MT_POSITION_Y, y_res);
> +	input_set_abs_params(input, ABS_MT_PRESSURE, 0,
> +				ETP_MAX_PRESSURE, 0, 0);
> +	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
> +		ETP_FINGER_WIDTH * max(data->width_x, data->width_y), 0, 0);
> +	input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0,
> +		ETP_FINGER_WIDTH * min(data->width_x, data->width_y), 0, 0);
> +
> +	/* Register the device in input subsystem */
> +	ret = input_register_device(input);
> +	if (ret) {
> +		dev_err(&client->dev, "input device register failed, %d\n",
> +			ret);
> +		goto err_free_device;
> +	}
> +
> +	return 0;
> +
> +err_free_device:
> +	input_free_device(input);
> +	return ret;
> +}
> +
> +static u8 elan_check_adapter_functionality(struct i2c_client *client)
> +{
> +	u8 ret = ELAN_ADAPTER_FUNC_NONE;
> +
> +	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> +		ret |= ELAN_ADAPTER_FUNC_I2C;
> +	if (i2c_check_functionality(client->adapter,
> +					I2C_FUNC_SMBUS_BYTE_DATA |
> +					I2C_FUNC_SMBUS_BLOCK_DATA |
> +					I2C_FUNC_SMBUS_I2C_BLOCK))
> +		ret |= ELAN_ADAPTER_FUNC_SMBUS;
> +	return ret;
> +}
> +
> +static int elan_probe(struct i2c_client *client,
> +				    const struct i2c_device_id *dev_id)
> +{
> +	struct elan_tp_data *data;
> +	int ret;
> +	u8 adapter_func;
> +	union i2c_smbus_data dummy;
> +	struct device *dev = &client->dev;
> +
> +	adapter_func = elan_check_adapter_functionality(client);
> +	if (adapter_func == ELAN_ADAPTER_FUNC_NONE) {
> +		dev_err(dev, "not a supported I2C/SMBus adapter\n");
> +		return -EIO;
> +	}
> +
> +	/* Make sure there is something at this address */
> +	if (dev->of_node && i2c_smbus_xfer(client->adapter, client->addr, 0,
> +			 I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0)
> +		return -ENODEV;
> +
> +	data = kzalloc(sizeof(struct elan_tp_data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	/* check protocol type */
> +	if (adapter_func == ELAN_ADAPTER_FUNC_SMBUS)
> +		data->smbus = true;
> +	else
> +		data->smbus = false;
> +	data->client = client;
> +	data->updated_fw = false;
> +	data->enable_detail_info = false;
> +	data->irq = client->irq;
> +
> +	ret = elan_dbfs_init(data);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "error create elan debugfs.\n");
> +		goto err_dbfs_init;
> +	}
> +	ret = request_threaded_irq(client->irq, NULL, elan_isr,
> +				  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +				  client->name, data);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "cannot register irq=%d\n",
> +							 client->irq);
> +		goto err_irq;
> +	}
> +
> +	/* initial elan touch pad */
> +	ret = elan_initialize(data);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	/* create input device */
> +	ret = elan_input_dev_create(data);
> +	if (ret < 0)
> +		goto err_input_dev;
> +
> +	device_init_wakeup(&client->dev, 1);
> +	ret = sysfs_create_group(&client->dev.kobj, &elan_sysfs_group);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "cannot register dev attribute %d", ret);
> +		goto err_create_group;
> +	}
> +	i2c_set_clientdata(client, data);
> +	return 0;
> +
> +err_create_group:
> +	input_free_device(data->input);

The device is registered here so you need to call unregister, not free.

> +err_input_dev:
> +err_init:
> +	free_irq(data->irq, data);
> +err_irq:
> +	debugfs_remove_recursive(data->dbfs_root);
> +	mutex_destroy(&data->dbfs_mutex);
> +err_dbfs_init:
> +	kfree(data);
> +	dev_err(&client->dev, "Elan Trackpad probe fail!\n");
> +	return ret;
> +}
> +
> +static int elan_remove(struct i2c_client *client)
> +{
> +	struct elan_tp_data *data = i2c_get_clientdata(client);
> +
> +	free_irq(data->irq, data);
> +	debugfs_remove_recursive(data->dbfs_root);
> +	mutex_destroy(&data->dbfs_mutex);
> +
> +	input_free_device(data->input);

Not needed (and harmful here).

> +	input_unregister_device(data->input);
> +	kfree(data);
> +	sysfs_remove_group(&client->dev.kobj, &elan_sysfs_group);

I'd start with removing sysfs attributes as first thing in remove().

> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int elan_suspend(struct device *dev)
> +{
> +	int ret;
> +	struct elan_tp_data *data = dev_get_drvdata(dev);
> +
> +	disable_irq(data->irq);
> +	if (data->smbus)
> +		ret = i2c_smbus_write_byte(data->client,
> +					ETP_SMBUS_DISABLE_TP);
> +	else
> +		ret = elan_i2c_sleep(data->client);
> +
> +	if (ret < 0) {
> +		dev_err(dev, "suspend mode failed, %d\n", ret);
> +	} else {
> +		if (device_may_wakeup(dev))
> +			data->irq_wake = (enable_irq_wake(data->irq) == 0);
> +	}
> +	return 0;
> +}
> +
> +static int elan_resume(struct device *dev)
> +{
> +	int ret = 0;
> +	struct elan_tp_data *data = dev_get_drvdata(dev);
> +
> +	if (device_may_wakeup(dev) && data->irq_wake)
> +		disable_irq_wake(data->irq);
> +
> +	ret = elan_initialize(data);
> +	if (ret < 0)
> +		dev_err(dev, "resume active power failed, %d\n", ret);
> +
> +	enable_irq(data->irq);
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);
> +
> +static const struct i2c_device_id elan_id[] = {
> +	{ DRIVER_NAME, 0 },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(i2c, elan_id);
> +
> +static struct i2c_driver elan_driver = {
> +	.driver = {
> +		.name	= DRIVER_NAME,
> +		.owner	= THIS_MODULE,
> +		.pm	= &elan_pm_ops,
> +	},
> +	.probe		= elan_probe,
> +	.remove		= elan_remove,
> +	.id_table	= elan_id,
> +};
> +
> +
> +static int __init elan_init(void)
> +{
> +	int ret;
> +	ret = i2c_add_driver(&elan_driver);
> +	if (ret) {
> +		pr_err("elan driver register FAILED.\n");
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static void __exit elan_exit(void)
> +{
> +	i2c_del_driver(&elan_driver);
> +}
> +
> +module_init(elan_init);
> +module_exit(elan_exit);

module_i2c_driver().

> +
> +MODULE_AUTHOR("Duson Lin <dusonlin@emc.com.tw>");
> +MODULE_DESCRIPTION("Elan I2C/SMBus Touchpad driver");
> +MODULE_LICENSE("GPL");
> -- 
> 1.7.10.4
> 

Thanks.

-- 
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 6/7] Input: pmic8xxx-keypad - Migrate to devm_* APIs
From: spamassassin system account @ 2013-12-17  2:01 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, linux-arm-msm, linux-arm-kernel, linux-input
In-Reply-To: <20131216153726.GB8884@core.coreip.homeip.net>

On 12/16, Dmitry Torokhov wrote:
> On Tue, Dec 10, 2013 at 03:43:15PM -0800, Stephen Boyd wrote:
> > -
> > -static int pmic8xxx_kp_remove(struct platform_device *pdev)
> > -{
> > -	struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
> > -
> > -	device_init_wakeup(&pdev->dev, 0);
> 
> Why are we removing restoring wakeup capable state?
> 

It's the only thing blocking removal of the remove callback and
as the driver is being unbound it didn't seem like we cared about
the state of the wakeup of the device. I greped the kernel tree
and I couldn't see a consistent pattern where driver probe was
setting the flag and driver remove was clearing it. Do we need to
keep it?

> > -	free_irq(kp->key_stuck_irq, kp);
> > -	free_irq(kp->key_sense_irq, kp);
> > -	input_unregister_device(kp->input);
> > -	kfree(kp);
> > -
> > -	return 0;
> >  }
> >  
> >  #ifdef CONFIG_PM_SLEEP

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply

* [PATCH] input synaptics-rmi4 trivial: Eliminate unused local variable.
From: Christopher Heiny @ 2013-12-17  2:25 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
	Vivian Ly, Daniel Rosenberg, Benjamin Tissoires

One-line change to eliminate an unused local variable warning in rmi_f11.c.

Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

This patch implements changes to the synaptics-rmi4 branch of
Dmitry's input tree.  The base for the patch is commit
e0c5aec5e6144ae8391d164e2dc659f8ef2b2ba7.

drivers/input/rmi4/rmi_f11.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
index d63ef31..c2be9de 100644
--- a/drivers/input/rmi4/rmi_f11.c
+++ b/drivers/input/rmi4/rmi_f11.c
@@ -1289,7 +1289,6 @@ static int rmi_f11_register_devices(struct rmi_function *fn)
 	struct f11_data *f11 = fn->data;
 	struct input_dev *input_dev;
 	struct input_dev *input_dev_mouse;
-	struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
 	struct rmi_driver *driver = rmi_dev->driver;
 	struct f11_2d_sensor *sensor = &f11->sensor;
 	int rc;

^ permalink raw reply related

* [PATCH -next] Input: zforce - fix error return code in zforce_start()
From: Wei Yongjun @ 2013-12-17  3:27 UTC (permalink / raw)
  To: dmitry.torokhov, rydberg, heiko; +Cc: yongjun_wei, linux-input

From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>

The error code was not set if unable to set config, so the error
condition wasn't reflected in the return value. Fix to return a
negative error code from the error handling case instead of 0.

Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
---
 drivers/input/touchscreen/zforce_ts.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c
index aa127ba..2175f34 100644
--- a/drivers/input/touchscreen/zforce_ts.c
+++ b/drivers/input/touchscreen/zforce_ts.c
@@ -279,7 +279,8 @@ static int zforce_start(struct zforce_ts *ts)
 		goto error;
 	}
 
-	if (zforce_setconfig(ts, SETCONFIG_DUALTOUCH)) {
+	ret = zforce_setconfig(ts, SETCONFIG_DUALTOUCH);
+	if (ret) {
 		dev_err(&client->dev, "Unable to set config\n");
 		goto error;
 	}


^ permalink raw reply related

* [PATCH] input synaptics-rmi4: Bug fixes to ATTN GPIO handling.
From: Christopher Heiny @ 2013-12-17  3:45 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
	Vivian Ly, Daniel Rosenberg, Jean Delvare, Joerie de Gram,
	Linus Walleij, Benjamin Tissoires

This patch fixes two bugs in handling of the RMI4 attention line GPIO.

1) in enable_sensor(), make sure the attn_gpio is defined before attempting to
get its value.

2) in rmi_driver_probe(), declare the name of the attn_gpio, then
request the attn_gpio before attempting to export it. As an added bonus,
the code relating to the export is tidied up.

Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

This patch implements changes to the synaptics-rmi4 branch of
Dmitry's input tree.  The base for the patch is commit
e0c5aec5e6144ae8391d164e2dc659f8ef2b2ba7.

 drivers/input/rmi4/rmi_driver.c | 37 ++++++++++++++++++++++---------------
 1 file changed, 22 insertions(+), 15 deletions(-)

diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index a30c7d3..030e8d5 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -169,7 +169,7 @@ static int enable_sensor(struct rmi_device *rmi_dev)
 
 	data->enabled = true;
 
-	if (!pdata->level_triggered &&
+	if (pdata->attn_gpio && !pdata->level_triggered &&
 		    gpio_get_value(pdata->attn_gpio) == pdata->attn_polarity)
 		retval = process_interrupt_requests(rmi_dev);
 
@@ -807,6 +807,9 @@ static int rmi_driver_remove(struct device *dev)
 	return 0;
 }
 
+
+static const char *GPIO_LABEL = "attn";
+
 static int rmi_driver_probe(struct device *dev)
 {
 	struct rmi_driver *rmi_driver;
@@ -959,20 +962,24 @@ static int rmi_driver_probe(struct device *dev)
 	}
 
 	if (IS_ENABLED(CONFIG_RMI4_DEV) && pdata->attn_gpio) {
-		retval = gpio_export(pdata->attn_gpio, false);
-		if (retval) {
-			dev_warn(dev, "WARNING: Failed to export ATTN gpio!\n");
-			retval = 0;
-		} else {
-			retval = gpio_export_link(dev,
-						  "attn", pdata->attn_gpio);
-			if (retval) {
-				dev_warn(dev,
-					"WARNING: Failed to symlink ATTN gpio!\n");
-				retval = 0;
-			} else {
-				dev_info(dev, "Exported ATTN GPIO %d.",
-					pdata->attn_gpio);
+		retval = gpio_request(pdata->attn_gpio, GPIO_LABEL);
+		if (retval)
+			dev_warn(dev, "WARNING: Failed to request ATTN gpio %d, code: %d.\n",
+				pdata->attn_gpio, retval);
+		else {
+			retval = gpio_export(pdata->attn_gpio, false);
+			if (retval)
+				dev_warn(dev, "WARNING: Failed to export ATTN  %d, code: %d.\n",
+					pdata->attn_gpio, retval);
+			else {
+				retval = gpio_export_link(dev, "attn",
+							  pdata->attn_gpio);
+				if (retval)
+					dev_warn(dev,
+						"WARNING: Failed to symlink ATTN gpio!\n");
+				else
+					dev_info(dev, "Exported ATTN GPIO %d.",
+						pdata->attn_gpio);
 			}
 		}
 	}

^ permalink raw reply related

* RE: [PATCH] Input: add i2c/smbus driver for elan touchpad
From: duson @ 2013-12-17  8:26 UTC (permalink / raw)
  To: 'Dmitry Torokhov'
  Cc: linux-input, linux-kernel, phoenix, bleung, agnescheng
In-Reply-To: <20131216210011.GC8884@core.coreip.homeip.net>

Add BLeung in mail loop.

Hi Dmitry:

Thanks for your review and suggestions.
First at all, I should say sorry to you because I had shrink and only reserved necessary basic functions from original source code and re-uploaded it before got your mail yesterday. To avoid your persecution, I pasted the newest patch and added your recommends again in this mail. Please see my responses as below and give me more recommends.

Thank you, 
Duson

This driver adds support for elan i2c/smbus touchpad found on some laptops PC

Signed-off-by: Duson Lin <dusonlin@emc.com.tw>
---
 drivers/input/mouse/Kconfig    |   10 +
 drivers/input/mouse/Makefile   |    1 +
 drivers/input/mouse/elan_i2c.c |  908 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 919 insertions(+)
 create mode 100644 drivers/input/mouse/elan_i2c.c

diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index effa9c5..095eccc 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -215,6 +215,16 @@ config MOUSE_CYAPA
 	  To compile this driver as a module, choose M here: the module will be
 	  called cyapa.
 
+config MOUSE_ELAN_I2C
+	tristate "ELAN I2C Touchpad support"
+	depends on I2C
+	help
+	  This driver adds support for Elan I2C Trackpads.
+	  Say Y here if you have a ELAN I2C Touchpad.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called elan_i2c.
+
 config MOUSE_INPORT
 	tristate "InPort/MS/ATIXL busmouse"
 	depends on ISA
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index c25efdb..24a12a6 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_MOUSE_APPLETOUCH)		+= appletouch.o
 obj-$(CONFIG_MOUSE_ATARI)		+= atarimouse.o
 obj-$(CONFIG_MOUSE_BCM5974)		+= bcm5974.o
 obj-$(CONFIG_MOUSE_CYAPA)		+= cyapa.o
+obj-$(CONFIG_MOUSE_ELAN_I2C)		+= elan_i2c.o
 obj-$(CONFIG_MOUSE_GPIO)		+= gpio_mouse.o
 obj-$(CONFIG_MOUSE_INPORT)		+= inport.o
 obj-$(CONFIG_MOUSE_LOGIBM)		+= logibm.o
diff --git a/drivers/input/mouse/elan_i2c.c b/drivers/input/mouse/elan_i2c.c new file mode 100644 index 0000000..a5a11bb
--- /dev/null
+++ b/drivers/input/mouse/elan_i2c.c
@@ -0,0 +1,908 @@
+?/*
+ * Elan I2C/SMBus Touchpad driver
+ *
+ * Copyright (c) 2013 ELAN Microelectronics Corp.
+ *
+ * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
+ * Version: 1.4.6
+ *
+ * Based on cyapa driver:
+ * copyright (c) 2011-2012 Cypress Semiconductor, Inc.
+ * copyright (c) 2011-2012 Google, Inc.
+ *
+ * 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.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_NAME		"elan_i2c"
+#define ELAN_DRIVER_VERSION	"1.4.6"
+#define ETP_PRESSURE_OFFSET	25
+#define ETP_MAX_PRESSURE	255
+#define ETP_FWIDTH_REDUCE	90
+#define ETP_FINGER_WIDTH	15
+
+#define ELAN_ADAPTER_FUNC_NONE   0
+#define ELAN_ADAPTER_FUNC_I2C    1
+#define ELAN_ADAPTER_FUNC_SMBUS  2
+#define ELAN_ADAPTER_FUNC_BOTH   3
+
+/* Length of Elan touchpad information */
+#define ETP_INF_LENGTH		2
+#define ETP_MAX_FINGERS		5
+#define ETP_FINGER_DATA_LEN	5
+#define ETP_REPORT_ID		0x5D
+#define ETP_MAX_REPORT_LEN	34
+#define ETP_ENABLE_ABS		0x0001
+#define ETP_ENABLE_CALIBRATE	0x0002
+#define ETP_DISABLE_CALIBRATE	0x0000
+
+/* Elan smbus command */
+#define ETP_SMBUS_IAP_CMD		0x00
+#define ETP_SMBUS_ENABLE_TP		0x20
+#define ETP_SMBUS_SLEEP_CMD		0x21
+#define ETP_SMBUS_IAP_PASSWORD_WRITE	0x29
+#define ETP_SMBUS_IAP_PASSWORD_READ	0x80
+#define ETP_SMBUS_WRITE_FW_BLOCK	0x2A
+#define ETP_SMBUS_IAP_RESET_CMD		0x2B
+#define ETP_SMBUS_RANGE_CMD		0xA0
+#define ETP_SMBUS_FW_VERSION_CMD	0xA1
+#define ETP_SMBUS_XY_TRACENUM_CMD	0xA2
+#define ETP_SMBUS_SM_VERSION_CMD	0xA3
+#define ETP_SMBUS_UNIQUEID_CMD		0xA3
+#define ETP_SMBUS_RESOLUTION_CMD	0xA4
+#define ETP_SMBUS_HELLOPACKET_CMD	0xA7
+#define ETP_SMBUS_PACKET_QUERY		0xA8
+#define ETP_SMBUS_IAP_VERSION_CMD	0xAC
+#define ETP_SMBUS_IAP_CTRL_CMD		0xAD
+#define ETP_SMBUS_IAP_CHECKSUM_CMD	0xAE
+#define ETP_SMBUS_FW_CHECKSUM_CMD	0xAF
+#define ETP_SMBUS_MAX_BASELINE_CMD	0xC3
+#define ETP_SMBUS_MIN_BASELINE_CMD	0xC4
+#define ETP_SMBUS_CALIBRATE_QUERY	0xC5
+#define ETP_SMBUS_REPORT_LEN		32
+#define ETP_SMBUS_FINGER_DATA_OFFSET	2
+#define ETP_SMBUS_HELLOPACKET_LEN	5
+#define ETP_SMBUS_IAP_PASSWORD		0x1234
+#define ETP_SMBUS_IAP_MODE_ON		(1<<6)
+
+/* Elan i2c command */
+#define ETP_I2C_RESET			0x0100
+#define ETP_I2C_WAKE_UP			0x0800
+#define ETP_I2C_SLEEP			0x0801
+#define ETP_I2C_DESC_CMD		0x0001
+#define ETP_I2C_REPORT_DESC_CMD		0x0002
+#define ETP_I2C_STAND_CMD		0x0005
+#define ETP_I2C_UNIQUEID_CMD		0x0101
+#define ETP_I2C_FW_VERSION_CMD		0x0102
+#define ETP_I2C_SM_VERSION_CMD		0x0103
+#define ETP_I2C_XY_TRACENUM_CMD		0x0105
+#define ETP_I2C_MAX_X_AXIS_CMD		0x0106
+#define ETP_I2C_MAX_Y_AXIS_CMD		0x0107
+#define ETP_I2C_RESOLUTION_CMD		0x0108
+#define ETP_I2C_IAP_VERSION_CMD		0x0110
+#define ETP_I2C_SET_CMD			0x0300
+#define ETP_I2C_MAX_BASELINE_CMD	0x0306
+#define ETP_I2C_MIN_BASELINE_CMD	0x0307
+#define ETP_I2C_FW_CHECKSUM_CMD		0x030F
+#define ETP_I2C_IAP_CTRL_CMD		0x0310
+#define ETP_I2C_IAP_CMD			0x0311
+#define ETP_I2C_IAP_RESET_CMD		0x0314
+#define ETP_I2C_IAP_CHECKSUM_CMD	0x0315
+#define ETP_I2C_CALIBRATE_CMD		0x0316
+#define ETP_I2C_REPORT_LEN		34
+#define ETP_I2C_FINGER_DATA_OFFSET	4
+#define ETP_I2C_REPORT_ID_OFFSET	2
+#define ETP_I2C_DESC_LENGTH		30
+#define ETP_I2C_REPORT_DESC_LENGTH	158
+#define ETP_I2C_IAP_PASSWORD		0x1EA5
+#define ETP_I2C_IAP_RESET		0xF0F0
+#define ETP_I2C_MAIN_MODE_ON		(1<<9)
+#define ETP_I2C_IAP_REG_L		0x01
+#define ETP_I2C_IAP_REG_H		0x06
+
+/* The main device structure */
+struct elan_tp_data {
+	struct i2c_client	*client;
+	struct input_dev	*input;
+	unsigned int		max_x;
+	unsigned int		max_y;
+	unsigned int		width_x;
+	unsigned int		width_y;
+	unsigned int		irq;
+	u16			unique_id;
+	u16			fw_version;
+	u16			sm_version;
+	u16			iap_version;
+	bool			smbus;
+};
+
+/*
+ *******************************************************************
+ * Elan smbus interface
+ *******************************************************************
+ */
+static int elan_smbus_initialize(struct i2c_client *client) {
+	u8 check[ETP_SMBUS_HELLOPACKET_LEN] = {0x55, 0x55, 0x55, 0x55, 0x55};
+	u8 values[ETP_SMBUS_HELLOPACKET_LEN] = {0, 0, 0, 0, 0};
+	int ret;
+
+	/* Get hello packet */
+	ret = i2c_smbus_read_block_data(client,
+					ETP_SMBUS_HELLOPACKET_CMD, values);
+	if (ret != ETP_SMBUS_HELLOPACKET_LEN) {
+		dev_err(&client->dev, "hello packet length fail\n");
+		return -1;
+	}
+
+	/* compare hello packet */
+	if (memcmp(values, check, ETP_SMBUS_HELLOPACKET_LEN)) {
+		dev_err(&client->dev, "hello packet fail [%x %x %x %x %x]\n",
+			values[0], values[1], values[2], values[3], values[4]);
+		return -1;
+	}
+
+	/* enable tp */
+	ret = i2c_smbus_write_byte(client, ETP_SMBUS_ENABLE_TP);
+	return ret;
+}
+
+static int elan_smbus_enable_absolute_mode(struct i2c_client *client) {
+	u8 cmd[4] = {0x00, 0x07, 0x00, ETP_ENABLE_ABS};
+
+	return i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD, 4, cmd); 
+}
+
+/*
+ ******************************************************************
+ * Elan i2c interface
+ ******************************************************************
+ */
+static int elan_i2c_read_block(struct i2c_client *client,
+			       u16 reg, u8 *val, u16 len)
+{
+	struct i2c_msg msgs[2];
+	u8 buf[2];
+	int ret;
+
+	buf[0] = reg & 0xff;
+	buf[1] = (reg >> 8) & 0xff;
+
+	msgs[0].addr = client->addr;
+	msgs[0].flags = client->flags & I2C_M_TEN;
+	msgs[0].len = 2;
+	msgs[0].buf = buf;
+
+	msgs[1].addr = client->addr;
+	msgs[1].flags = client->flags & I2C_M_TEN;
+	msgs[1].flags |= I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = val;
+
+	ret = i2c_transfer(client->adapter, msgs, 2);
+	return ret != 2 ? -EIO : 0;
+}
+
+static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 
+*val) {
+	int retval;
+
+	retval = elan_i2c_read_block(client, reg, val, ETP_INF_LENGTH);
+	if (retval < 0) {
+		dev_err(&client->dev, "reading cmd (0x%04x) fail.\n", reg);
+		return retval;
+	}
+	return 0;
+}
+
+static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 
+cmd) {
+	struct i2c_msg msg;
+	u8 buf[4];
+	int ret;
+
+	buf[0] = reg & 0xff;
+	buf[1] = (reg >> 8) & 0xff;
+	buf[2] = cmd & 0xff;
+	buf[3] = (cmd >> 8) & 0xff;
+
+	msg.addr = client->addr;
+	msg.flags = client->flags & I2C_M_TEN;
+	msg.len = 4;
+	msg.buf = buf;
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	return ret != 1 ? -EIO : 0;
+}
+
+static int elan_i2c_reset(struct i2c_client *client) {
+	return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
+				  ETP_I2C_RESET);
+}
+
+static int elan_i2c_wake_up(struct i2c_client *client) {
+	return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
+				  ETP_I2C_WAKE_UP);
+}
+
+static int elan_i2c_sleep(struct i2c_client *client) {
+	return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
+				  ETP_I2C_SLEEP);
+}
+
+static int elan_i2c_enable_absolute_mode(struct i2c_client *client) {
+	return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
+				  ETP_ENABLE_ABS);
+}
+
+static int elan_i2c_get_desc(struct i2c_client *client, u8 *val) {
+	return elan_i2c_read_block(client, ETP_I2C_DESC_CMD, val,
+				   ETP_I2C_DESC_LENGTH);
+}
+
+static int elan_i2c_get_report_desc(struct i2c_client *client, u8 *val) 
+{
+	return elan_i2c_read_block(client, ETP_I2C_REPORT_DESC_CMD,
+				   val, ETP_I2C_REPORT_DESC_LENGTH); }
+
+static int elan_i2c_initialize(struct i2c_client *client) {
+	struct device *dev = &client->dev;
+	int rc;
+	u8 val[256];
+
+	rc = elan_i2c_reset(client);
+	if (rc < 0) {
+		dev_err(dev, "device reset failed.\n");
+		return -1;
+	}
+
+	/* wait for get reset return flag */
+	msleep(100);
+	/* get reset return flag 0000 */
+	rc = i2c_master_recv(client, val, ETP_INF_LENGTH);
+	if (rc < 0) {
+		dev_err(dev, "get device reset return value failed.\n");
+		return -1;
+	}
+
+	rc = elan_i2c_get_desc(client, val);
+	if (rc < 0) {
+		dev_err(dev, "cannot get device descriptor.\n");
+		return -1;
+	}
+
+	rc = elan_i2c_get_report_desc(client, val);
+	if (rc < 0) {
+		dev_err(dev, "fetching report descriptor failed.\n");
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ ******************************************************************
+ * General functions
+ ******************************************************************
+ */
+/*
+ * (value from firmware) * 10 + 790 = dpi
+ * we also have to convert dpi to dots/mm (*10/254 to avoid floating 
+point)  */ static unsigned int elan_convert_res(char val) {
+	int res;
+	if (val & 0x80) {
+		val = ~val + 1;
+		res = (790 - val * 10) * 10 / 254;
+	} else
+		res = (val * 10 + 790) * 10 / 254;
+	return res;
+}
+
+static int elan_get_iap_version(struct elan_tp_data *data) {
+	int ret;
+	u8 val[3];
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+					  ETP_SMBUS_IAP_VERSION_CMD, val);
+		ret = val[2];
+	} else {
+		elan_i2c_read_cmd(data->client,
+				  ETP_I2C_IAP_VERSION_CMD, val);
+		ret = val[0];
+	}
+	return ret;
+}
+
+static int elan_get_x_max(struct elan_tp_data *data) {
+	int ret;
+	u8 val[3];
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+					  ETP_SMBUS_RANGE_CMD, val);
+		ret = (0x0f & val[0]) << 8 | val[1];
+	} else {
+		elan_i2c_read_cmd(data->client,
+				  ETP_I2C_MAX_X_AXIS_CMD, val);
+		ret = (0x0f & val[1]) << 8 | val[0];
+	}
+	return ret;
+}
+
+static int elan_get_y_max(struct elan_tp_data *data) {
+	int ret;
+	u8 val[3];
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+					  ETP_SMBUS_RANGE_CMD, val);
+		ret = (0xf0 & val[0]) << 4 | val[2];
+	} else {
+		elan_i2c_read_cmd(data->client,
+				  ETP_I2C_MAX_Y_AXIS_CMD, val);
+		ret = (0x0f & val[1]) << 8 | val[0];
+	}
+	return ret;
+}
+
+static int elan_get_x_tracenum(struct elan_tp_data *data) {
+	int ret;
+	u8 val[3];
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+					  ETP_SMBUS_XY_TRACENUM_CMD, val);
+		ret = (val[1] - 1);
+	} else {
+		elan_i2c_read_cmd(data->client,
+				  ETP_I2C_XY_TRACENUM_CMD, val);
+		ret = (val[0] - 1);
+	}
+	return ret;
+}
+
+static int elan_get_y_tracenum(struct elan_tp_data *data) {
+	int ret;
+	u8 val[3];
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+					  ETP_SMBUS_XY_TRACENUM_CMD, val);
+		ret = (val[2] - 1);
+	} else {
+		ret = elan_i2c_read_cmd(data->client,
+					ETP_I2C_XY_TRACENUM_CMD, val);
+		ret = (val[1] - 1);
+	}
+	return ret;
+}
+
+static int elan_get_fw_version(struct elan_tp_data *data) {
+	int ret;
+	u8 val[3];
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+					  ETP_SMBUS_FW_VERSION_CMD, val);
+		ret = val[2];
+	} else {
+		elan_i2c_read_cmd(data->client,
+				  ETP_I2C_FW_VERSION_CMD, val);
+		ret = val[0];
+	}
+	return ret;
+}
+
+static int elan_get_sm_version(struct elan_tp_data *data) {
+	int ret;
+	u8 val[3];
+	if (data->smbus)
+		i2c_smbus_read_block_data(data->client,
+					  ETP_SMBUS_SM_VERSION_CMD, val);
+	else
+		elan_i2c_read_block(data->client,
+				    ETP_I2C_SM_VERSION_CMD, val, 1);
+	ret = val[0];
+	return ret;
+}
+
+static int elan_get_unique_id(struct elan_tp_data *data) {
+	int ret;
+	u8 val[3];
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+					  ETP_SMBUS_UNIQUEID_CMD, val);
+		ret = val[1];
+	} else {
+		elan_i2c_read_cmd(data->client,
+				  ETP_I2C_UNIQUEID_CMD, val);
+		ret = val[0];
+	}
+	return ret;
+}
+
+static int elan_get_x_resolution(struct elan_tp_data *data) {
+	int ret;
+	u8 val[3];
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+					  ETP_SMBUS_RESOLUTION_CMD, val);
+		ret = elan_convert_res(val[1] & 0x0F);
+	} else {
+		elan_i2c_read_cmd(data->client,
+				  ETP_I2C_RESOLUTION_CMD, val);
+		ret = elan_convert_res(val[0]);
+	}
+	return ret;
+}
+
+static int elan_get_y_resolution(struct elan_tp_data *data) {
+	int ret;
+	u8 val[3];
+	if (data->smbus) {
+		i2c_smbus_read_block_data(data->client,
+					  ETP_SMBUS_RESOLUTION_CMD, val);
+		ret = elan_convert_res((val[1] & 0xF0) >> 4);
+	} else {
+		elan_i2c_read_cmd(data->client,
+				  ETP_I2C_RESOLUTION_CMD, val);
+		ret = elan_convert_res(val[1]);
+	}
+	return ret;
+}
+
+static int elan_initialize(struct elan_tp_data *data) {
+	int ret;
+	if (data->smbus) {
+		ret = elan_smbus_initialize(data->client);
+		if (ret < 0) {
+			dev_err(&data->client->dev,
+				"device initialize failed.\n");
+			goto err_initialize;
+		}
+
+		ret = elan_smbus_enable_absolute_mode(data->client);
+		if (ret < 0)
+			dev_err(&data->client->dev,
+				"cannot switch to absolute mode.\n");
+	} else {
+		ret = elan_i2c_initialize(data->client);
+		if (ret < 0) {
+			dev_err(&data->client->dev,
+				"device initialize failed.\n");
+			goto err_initialize;
+		}
+
+		ret = elan_i2c_enable_absolute_mode(data->client);
+		if (ret < 0) {
+			dev_err(&data->client->dev,
+				"cannot switch to absolute mode.\n");
+			goto err_initialize;
+		}
+
+		ret = elan_i2c_wake_up(data->client);
+		if (ret < 0)
+			dev_err(&data->client->dev,
+				"device wake up failed.\n");
+	}
+err_initialize:
+	return ret;
+}
+
+
+/*
+ ******************************************************************
+ * Elan isr functions
+ ******************************************************************
+ */
+static int elan_check_packet(struct elan_tp_data *data, u8 *packet) {
+	u8 rid;
+
+	if (data->smbus)
+		rid = packet[0];
+	else
+		rid = packet[ETP_I2C_REPORT_ID_OFFSET];
+
+	/* check report id */
+	if (rid != ETP_REPORT_ID) {
+		dev_err(&data->client->dev, "report id [%x] fail.\n", rid);
+		return -1;
+	}
+	return 0;
+}
+
+static void elan_report_absolute(struct elan_tp_data *data, u8 *packet) 
+{
+	struct input_dev *input = data->input;
+	u8 *finger_data;
+	bool finger_on;
+	int pos_x, pos_y;
+	int pressure, mk_x, mk_y;
+	int i, area_x, area_y, major, minor, new_pressure;
+	int finger_count = 0;
+	int btn_click;
+	u8  tp_info;
+
+	if (data->smbus) {
+		finger_data = &packet[ETP_SMBUS_FINGER_DATA_OFFSET];
+		tp_info = packet[1];
+	} else {
+		finger_data = &packet[ETP_I2C_FINGER_DATA_OFFSET];
+		tp_info = packet[3];
+	}
+
+	btn_click = (tp_info & 0x01);
+	for (i = 0; i < ETP_MAX_FINGERS; i++) {
+		finger_on = (tp_info >> (3 + i)) & 0x01;
+
+		/* analyze touched finger raw data*/
+		if (finger_on) {
+			pos_x = ((finger_data[0] & 0xf0) << 4) |
+							finger_data[1];
+			pos_y = ((finger_data[0] & 0x0f) << 8) |
+							finger_data[2];
+			pos_y =  data->max_y - pos_y;
+			mk_x = (finger_data[3] & 0x0f);
+			mk_y = (finger_data[3] >> 4);
+			pressure = finger_data[4];
+
+			/*
+			 * to avoid fat finger be as palm, so reduce the
+			 * width x and y per trace
+			 */
+			area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE);
+			area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE);
+
+			major = max(area_x, area_y);
+			minor = min(area_x, area_y);
+
+			new_pressure = pressure + ETP_PRESSURE_OFFSET;
+			if (new_pressure > ETP_MAX_PRESSURE)
+				new_pressure = ETP_MAX_PRESSURE;
+
+			input_mt_slot(input, i);
+			input_mt_report_slot_state(input, MT_TOOL_FINGER,
+						   true);
+			input_report_abs(input, ABS_MT_POSITION_X, pos_x);
+			input_report_abs(input, ABS_MT_POSITION_Y, pos_y);
+			input_report_abs(input, ABS_MT_PRESSURE, new_pressure);
+			input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
+			input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
+			input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
+			finger_data += ETP_FINGER_DATA_LEN;
+			finger_count++;
+		} else {
+			input_mt_slot(input, i);
+			input_mt_report_slot_state(input,
+						   MT_TOOL_FINGER, false);
+		}
+	}
+
+	input_report_key(input, BTN_LEFT, (btn_click == 1));
+	input_mt_report_pointer_emulation(input, true);
+	input_sync(input);
+}
+
+static irqreturn_t elan_isr(int irq, void *dev_id) {
+	struct elan_tp_data *data = dev_id;
+	u8 raw[ETP_MAX_REPORT_LEN];
+	int retval;
+	int report_len;
+
+	if (data->smbus) {
+		report_len = ETP_SMBUS_REPORT_LEN;
+		retval = i2c_smbus_read_block_data(data->client,
+						   ETP_SMBUS_PACKET_QUERY,
+						   raw);
+	} else {
+		report_len = ETP_I2C_REPORT_LEN;
+		retval = i2c_master_recv(data->client, raw, report_len);
+	}
+
+	if (retval != report_len) {
+		dev_err(&data->client->dev, "wrong packet len(%d)", retval);
+		goto elan_isr_end;
+	}
+
+	if (elan_check_packet(data, raw) < 0) {
+		dev_err(&data->client->dev, "wrong packet format.");
+		goto elan_isr_end;
+	}
+	elan_report_absolute(data, raw);
+
+elan_isr_end:
+	return IRQ_HANDLED;
+}
+
+/*
+ ******************************************************************
+ * Elan initial functions
+ ******************************************************************
+ */
+static int elan_input_dev_create(struct elan_tp_data *data) {
+	struct i2c_client *client = data->client;
+	struct input_dev *input;
+	unsigned int x_res, y_res;
+	int ret, max_width, min_width;
+
+	data->input = input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+	input->name = "Elan Touchpad";
+	input->id.bustype = BUS_I2C;
+	input->dev.parent = &data->client->dev;
+
+	__set_bit(EV_ABS, input->evbit);
+	__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+	__set_bit(BTN_LEFT, input->keybit);
+
+	data->unique_id = elan_get_unique_id(data);
+	data->fw_version = elan_get_fw_version(data);
+	data->sm_version = elan_get_sm_version(data);
+	data->iap_version = elan_get_iap_version(data);
+	data->max_x = elan_get_x_max(data);
+	data->max_y = elan_get_y_max(data);
+	data->width_x = data->max_x / elan_get_x_tracenum(data);
+	data->width_y = data->max_y / elan_get_y_tracenum(data);
+	x_res = elan_get_x_resolution(data);
+	y_res = elan_get_y_resolution(data);
+	max_width = max(data->width_x, data->width_y);
+	min_width = min(data->width_x, data->width_y);
+
+	dev_dbg(&client->dev,
+		"Elan Touchpad Information:\n"
+		"    Module unique ID:  0x%04x\n"
+		"    Firmware Version:  0x%04x\n"
+		"    Sample Version:  0x%04x\n"
+		"    IAP Version:  0x%04x\n"
+		"    Max ABS X,Y:   %d,%d\n"
+		"    Width X,Y:   %d,%d\n"
+		"    Resolution X,Y:   %d,%d (dots/mm)\n",
+		data->unique_id,
+		data->fw_version,
+		data->sm_version,
+		data->iap_version,
+		data->max_x, data->max_y,
+		data->width_x, data->width_y,
+		(char)x_res, (char)y_res);
+


Why cast to char?
Ans: OK. I will remove char type.


+	input_set_abs_params(input, ABS_X, 0, data->max_x, 0, 0);
+	input_set_abs_params(input, ABS_Y, 0, data->max_y, 0, 0);
+	input_abs_set_res(input, ABS_X, x_res);
+	input_abs_set_res(input, ABS_Y, y_res);
+	input_set_abs_params(input, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0);
+	input_set_abs_params(input, ABS_TOOL_WIDTH, 0, ETP_FINGER_WIDTH, 0, 
+0);
+
+	/* handle pointer emulation and unused slots in core */
+	ret = input_mt_init_slots(input, ETP_MAX_FINGERS,
+				  INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
+	if (ret) {
+		dev_err(&client->dev, "allocate MT slots failed, %d\n", ret);
+		goto err_free_device;
+	}
+	input_set_abs_params(input, ABS_MT_POSITION_X, 0, data->max_x, 0, 0);
+	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, data->max_y, 0, 0);
+	input_abs_set_res(input, ABS_MT_POSITION_X, x_res);
+	input_abs_set_res(input, ABS_MT_POSITION_Y, y_res);
+	input_set_abs_params(input, ABS_MT_PRESSURE, 0,
+			     ETP_MAX_PRESSURE, 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
+			     ETP_FINGER_WIDTH * max_width, 0, 0);
+	input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0,
+			     ETP_FINGER_WIDTH * min_width, 0, 0);
+
+	/* Register the device in input subsystem */
+	ret = input_register_device(input);
+	if (ret) {
+		dev_err(&client->dev, "input_dev register failed, %d\n", ret);
+		goto err_free_device;
+	}
+
+	return 0;
+
+err_free_device:
+	input_free_device(input);
+	return ret;
+}
+
+static u8 elan_check_adapter_functionality(struct i2c_client *client) {
+	u8 ret = ELAN_ADAPTER_FUNC_NONE;
+
+	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		ret |= ELAN_ADAPTER_FUNC_I2C;
+	if (i2c_check_functionality(client->adapter,
+				    I2C_FUNC_SMBUS_BYTE_DATA |
+				    I2C_FUNC_SMBUS_BLOCK_DATA |
+				    I2C_FUNC_SMBUS_I2C_BLOCK))
+		ret |= ELAN_ADAPTER_FUNC_SMBUS;
+	return ret;
+}
+
+static int elan_probe(struct i2c_client *client,
+		      const struct i2c_device_id *dev_id) {
+	struct elan_tp_data *data;
+	int ret;
+	u8 adapter_func;
+	union i2c_smbus_data dummy;
+	struct device *dev = &client->dev;
+
+	adapter_func = elan_check_adapter_functionality(client);
+	if (adapter_func == ELAN_ADAPTER_FUNC_NONE) {
+		dev_err(dev, "not a supported I2C/SMBus adapter\n");
+		return -EIO;
+	}
+
+	/* Make sure there is something at this address */
+	if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+			   I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0)
+		return -ENODEV;
+
+	data = kzalloc(sizeof(struct elan_tp_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	/* check protocol type */
+	if (adapter_func == ELAN_ADAPTER_FUNC_SMBUS)
+		data->smbus = true;
+	else
+		data->smbus = false;
+	data->client = client;
+	data->irq = client->irq;
+
+	ret = request_threaded_irq(client->irq, NULL, elan_isr,
+				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				   client->name, data);
+	if (ret < 0) {
+		dev_err(&client->dev, "cannot register irq=%d\n",
+				client->irq);
+		goto err_irq;
+	}
+
+	/* initial elan touch pad */
+	ret = elan_initialize(data);
+	if (ret < 0)
+		goto err_init;
+
+	/* create input device */
+	ret = elan_input_dev_create(data);
+	if (ret < 0)
+		goto err_input_dev;
+
+	device_init_wakeup(&client->dev, 1);
+	i2c_set_clientdata(client, data);
+	return 0;
+
+err_input_dev:
+err_init:
+	free_irq(data->irq, data);
+err_irq:
+	kfree(data);
+	dev_err(&client->dev, "Elan Trackpad probe fail!\n");
+	return ret;
+}
+
+static int elan_remove(struct i2c_client *client) {
+	struct elan_tp_data *data = i2c_get_clientdata(client);
+	free_irq(data->irq, data);
+	input_free_device(data->input);


Not needed (and harmful here).

Ans: OK. I will remove this.

+	input_unregister_device(data->input);
+	kfree(data);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int elan_suspend(struct device *dev) {
+	int ret = 0;
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+
+	disable_irq(data->irq);
+	if (data->smbus)
+		ret = i2c_smbus_write_byte(data->client,
+					   ETP_SMBUS_SLEEP_CMD);
+	else
+		ret = elan_i2c_sleep(data->client);
+
+	if (ret < 0)
+		dev_err(dev, "suspend mode failed, %d\n", ret);
+
+	return ret;
+}
+
+static int elan_resume(struct device *dev) {
+	int ret = 0;
+	struct elan_tp_data *data = dev_get_drvdata(dev);
+
+	ret = elan_initialize(data);
+	if (ret < 0)
+		dev_err(dev, "resume active power failed, %d\n", ret);
+
+	enable_irq(data->irq);
+	return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);
+
+static const struct i2c_device_id elan_id[] = {
+	{ DRIVER_NAME, 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, elan_id);
+
+static struct i2c_driver elan_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+		.pm	= &elan_pm_ops,
+	},
+	.probe		= elan_probe,
+	.remove		= elan_remove,
+	.id_table	= elan_id,
+};
+
+
+static int __init elan_init(void)
+{
+	int ret;
+	ret = i2c_add_driver(&elan_driver);
+	if (ret)
+		pr_err("elan driver register FAILED.\n");
+
+	return ret;
+}
+
+static void __exit elan_exit(void)
+{
+	i2c_del_driver(&elan_driver);
+}
+
+module_init(elan_init);
+module_exit(elan_exit);
+

module_i2c_driver().
Ans: OK. I will modify this.


+MODULE_AUTHOR("Duson Lin <dusonlin@emc.com.tw>"); 
+MODULE_DESCRIPTION("Elan I2C/SMBus Touchpad driver"); 
+MODULE_LICENSE("GPL");
--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at  http://vger.kernel.org/majordomo-info.html

















> -----Original Message-----
> From: Dmitry Torokhov [mailto:dmitry.torokhov@gmail.com]
> Sent: Tuesday, December 17, 2013 5:00 AM
> To: Duson Lin
> Cc: linux-input@vger.kernel.org; linux-kernel@vger.kernel.org;
> agnescheng@google.com; phoenix@emc.com.tw
> Subject: Re: [PATCH] Input: add i2c/smbus driver for elan touchpad
> 
> Hi Duson,
> 
> On Fri, Nov 22, 2013 at 01:56:55PM +0800, Duson Lin wrote:
> > This driver adds support for elan i2c/smbus touchpad found on some laptops PC
> > ---
> >  drivers/input/mouse/Kconfig    |   10 +
> >  drivers/input/mouse/Makefile   |    1 +
> >  drivers/input/mouse/elan_i2c.c | 1846
> ++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 1857 insertions(+)
> >  create mode 100644 drivers/input/mouse/elan_i2c.c
> >
> > diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
> > index effa9c5..8ad4b38 100644
> > --- a/drivers/input/mouse/Kconfig
> > +++ b/drivers/input/mouse/Kconfig
> > @@ -215,6 +215,16 @@ config MOUSE_CYAPA
> >  	  To compile this driver as a module, choose M here: the module will be
> >  	  called cyapa.
> >
> > +config MOUSE_ELAN_I2C
> > +	tristate "ELAN I2C Touchpad support"
> > +	depends on I2C
> > +	help
> > +	  This driver adds support for Elan I2C Trackpads.
> > +	  Say y here if you have a ELAN I2C Touchpad.
> > +
> > +	  To compile this driver as a module, choose M here: the module will be
> > +	  called elan_i2c.
> > +
> >  config MOUSE_INPORT
> >  	tristate "InPort/MS/ATIXL busmouse"
> >  	depends on ISA
> > diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
> > index c25efdb..24a12a6 100644
> > --- a/drivers/input/mouse/Makefile
> > +++ b/drivers/input/mouse/Makefile
> > @@ -9,6 +9,7 @@ obj-$(CONFIG_MOUSE_APPLETOUCH)		+=
> appletouch.o
> >  obj-$(CONFIG_MOUSE_ATARI)		+= atarimouse.o
> >  obj-$(CONFIG_MOUSE_BCM5974)		+= bcm5974.o
> >  obj-$(CONFIG_MOUSE_CYAPA)		+= cyapa.o
> > +obj-$(CONFIG_MOUSE_ELAN_I2C)		+= elan_i2c.o
> >  obj-$(CONFIG_MOUSE_GPIO)		+= gpio_mouse.o
> >  obj-$(CONFIG_MOUSE_INPORT)		+= inport.o
> >  obj-$(CONFIG_MOUSE_LOGIBM)		+= logibm.o
> > diff --git a/drivers/input/mouse/elan_i2c.c b/drivers/input/mouse/elan_i2c.c
> > new file mode 100644
> > index 0000000..9892ee1
> > --- /dev/null
> > +++ b/drivers/input/mouse/elan_i2c.c
> > @@ -0,0 +1,1846 @@
> > +/*
> > + * Elan I2C/SMBus Touchpad driver
> > + *
> > + * Copyright (c) 2013 ELAN Microelectronics Corp.
> > + *
> > + * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
> > + * Version: 1.4.6
> > + *
> > + * 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.
> > + *
> > + * Trademarks are the property of their respective owners.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/firmware.h>
> > +#include <linux/i2c.h>
> > +#include <linux/init.h>
> > +#include <linux/input/mt.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/module.h>
> > +#include <linux/slab.h>
> > +#include <linux/debugfs.h>
> > +#include <linux/cdev.h>
> > +#include <linux/kernel.h>
> > +#include <linux/major.h>
> > +#include <linux/sched.h>
> > +#include <linux/input.h>
> > +#include <linux/uaccess.h>
> > +#include <linux/jiffies.h>
> > +
> > +#define DRIVER_NAME		"elan_i2c"
> > +#define ELAN_DRIVER_VERSION	"1.4.6"
> > +#define ETP_PRESSURE_OFFSET	25
> > +#define ETP_MAX_PRESSURE	255
> > +#define ETP_FWIDTH_REDUCE	90
> > +#define ETP_FINGER_WIDTH	15
> > +
> > +#define ELAN_ADAPTER_FUNC_NONE   0
> > +#define ELAN_ADAPTER_FUNC_I2C    1
> > +#define ELAN_ADAPTER_FUNC_SMBUS  2
> > +#define ELAN_ADAPTER_FUNC_BOTH   3
> > +
> > +/* Length of Elan touchpad information */
> > +#define ETP_INF_LENGTH		2
> > +#define ETP_MAX_FINGERS		5
> > +#define ETP_FINGER_DATA_LEN	5
> > +#define ETP_REPORT_ID		0x5D
> > +#define ETP_MAX_REPORT_LEN	34
> > +#define ETP_ENABLE_ABS		0x0001
> > +#define ETP_ENABLE_CALIBRATE	0x0002
> > +#define ETP_DISABLE_CALIBRATE	0x0000
> > +
> > +/* Elan smbus command */
> > +#define ETP_SMBUS_IAP_CMD		0x00
> > +#define ETP_SMBUS_ENABLE_TP		0x20
> > +#define ETP_SMBUS_DISABLE_TP		0x21
> > +#define ETP_SMBUS_IAP_PASSWORD_WRITE	0x29
> > +#define ETP_SMBUS_IAP_PASSWORD_READ	0x80
> > +#define ETP_SMBUS_WRITE_FW_BLOCK	0x2A
> > +#define ETP_SMBUS_IAP_RESET_CMD		0x2B
> > +#define ETP_SMBUS_RANGE_CMD		0xA0
> > +#define ETP_SMBUS_FW_VERSION_CMD	0xA1
> > +#define ETP_SMBUS_XY_TRACENUM_CMD	0xA2
> > +#define ETP_SMBUS_SM_VERSION_CMD	0xA3
> > +#define ETP_SMBUS_UNIQUEID_CMD		0xA3
> > +#define ETP_SMBUS_RESOLUTION_CMD	0xA4
> > +#define ETP_SMBUS_HELLOPACKET_CMD	0xA7
> > +#define ETP_SMBUS_PACKET_QUERY		0xA8
> > +#define ETP_SMBUS_IAP_VERSION_CMD	0xAC
> > +#define ETP_SMBUS_IAP_CTRL_CMD		0xAD
> > +#define ETP_SMBUS_IAP_CHECKSUM_CMD	0xAE
> > +#define ETP_SMBUS_FW_CHECKSUM_CMD	0xAF
> > +#define ETP_SMBUS_MAX_BASELINE_CMD	0xC3
> > +#define ETP_SMBUS_MIN_BASELINE_CMD	0xC4
> > +#define ETP_SMBUS_CALIBRATE_QUERY	0xC5
> > +#define ETP_SMBUS_REPORT_LEN		32
> > +#define ETP_SMBUS_FINGER_DATA_OFFSET	2
> > +#define ETP_SMBUS_HELLOPACKET_LEN	5
> > +#define ETP_SMBUS_IAP_PASSWORD		0x1234
> > +#define ETP_SMBUS_IAP_MODE_ON		(1<<6)
> > +
> > +/* Elan i2c command */
> > +#define ETP_I2C_RESET			0x0100
> > +#define ETP_I2C_WAKE_UP			0x0800
> > +#define ETP_I2C_SLEEP			0x0801
> > +#define ETP_I2C_DESC_CMD		0x0001
> > +#define ETP_I2C_REPORT_DESC_CMD		0x0002
> > +#define ETP_I2C_STAND_CMD		0x0005
> > +#define ETP_I2C_UNIQUEID_CMD		0x0101
> > +#define ETP_I2C_FW_VERSION_CMD		0x0102
> > +#define ETP_I2C_SM_VERSION_CMD		0x0103
> > +#define ETP_I2C_XY_TRACENUM_CMD		0x0105
> > +#define ETP_I2C_MAX_X_AXIS_CMD		0x0106
> > +#define ETP_I2C_MAX_Y_AXIS_CMD		0x0107
> > +#define ETP_I2C_RESOLUTION_CMD		0x0108
> > +#define ETP_I2C_IAP_VERSION_CMD		0x0110
> > +#define ETP_I2C_SET_CMD			0x0300
> > +#define ETP_I2C_MAX_BASELINE_CMD	0x0306
> > +#define ETP_I2C_MIN_BASELINE_CMD	0x0307
> > +#define ETP_I2C_FW_CHECKSUM_CMD		0x030F
> > +#define ETP_I2C_IAP_CTRL_CMD		0x0310
> > +#define ETP_I2C_IAP_CMD			0x0311
> > +#define ETP_I2C_IAP_RESET_CMD		0x0314
> > +#define ETP_I2C_IAP_CHECKSUM_CMD	0x0315
> > +#define ETP_I2C_CALIBRATE_CMD		0x0316
> > +#define ETP_I2C_REPORT_LEN		34
> > +#define ETP_I2C_FINGER_DATA_OFFSET	4
> > +#define ETP_I2C_REPORT_ID_OFFSET	2
> > +#define ETP_I2C_DESC_LENGTH		30
> > +#define ETP_I2C_REPORT_DESC_LENGTH	158
> > +#define ETP_I2C_IAP_PASSWORD		0x1EA5
> > +#define ETP_I2C_IAP_RESET		0xF0F0
> > +#define ETP_I2C_MAIN_MODE_ON		(1<<9)
> > +#define ETP_I2C_IAP_REG_L		0x01
> > +#define ETP_I2C_IAP_REG_H		0x06
> > +
> > +/* IAP F/W updater */
> > +#define ETP_FW_NAME		"elan_i2c.bin"
> > +#define ETP_IAP_VERSION_ADDR	0x0082
> > +#define ETP_IAP_START_ADDR	0x0083
> > +#define ETP_FW_IAP_PAGE_ERR	(1<<5)
> > +#define ETP_FW_IAP_INTERFACE_ERR (1<<4)
> > +#define ETP_FW_PAGE_SIZE	64
> > +#define ETP_FW_PAGE_COUNT	768
> > +#define ETP_FW_SIZE		(ETP_FW_PAGE_SIZE *
> ETP_FW_PAGE_COUNT)
> > +enum {UNKNOWN_MODE, IAP_MODE, MAIN_MODE};
> > +
> > +struct dbfs_data {
> > +	bool	bfetch;
> > +	u8	buffer[ETP_MAX_REPORT_LEN];
> > +};
> > +
> > +/* The main device structure */
> > +struct elan_tp_data {
> > +	struct i2c_client	*client;
> > +	struct input_dev	*input;
> > +	unsigned int		max_x;
> > +	unsigned int		max_y;
> > +	unsigned int		width_x;
> > +	unsigned int		width_y;
> > +	unsigned int		irq;
> > +
> > +	/* fields required for IAP firmware updater */
> > +	u16			unique_id;
> > +	u16			fw_version;
> > +	u16			sm_version;
> > +	u16			iap_version;
> > +	bool			updated_fw;
> > +	u16			iap_start_addr;
> > +
> > +	/* irq wake is enabled */
> > +	bool			irq_wake;
> > +	bool			smbus;
> > +	bool			enable_detail_info;
> > +
> > +	/* fields required for debug fs */
> > +	struct mutex		dbfs_mutex;
> > +	struct dentry		*dbfs_root;
> > +	struct dbfs_data	dbfs_buffer;
> > +};
> > +
> > +u8 val[256];
> > +static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 *val);
> > +static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd);
> > +static int elan_initialize(struct elan_tp_data *data);
> > +
> > +/*
> > + **************************************************************
> > + * debugfs interface
> > + **************************************************************
> > +*/
> > +static int elan_dbfs_open(struct inode *inode, struct file *file)
> > +{
> > +	int retval;
> > +	struct elan_tp_data *data = inode->i_private;
> > +
> > +	if (!data)
> > +		return -ENODEV;
> > +
> > +	retval = mutex_lock_interruptible(&data->dbfs_mutex);
> > +	if (retval)
> > +		return retval;
> > +
> > +	if (!kobject_get(&data->client->dev.kobj)) {
> 
> Why are you pinning I2C device kobject? It will not prevent unbinding
> the driver if that's what you are aiming for.
> 
> > +		retval = -ENODEV;
> > +		goto dbfs_out;
> > +	}
> > +
> > +	file->private_data = data;
> > +dbfs_out:
> > +	mutex_unlock(&data->dbfs_mutex);
> > +	return 0;
> > +}
> > +
> > +static int elan_dbfs_release(struct inode *inode, struct file *file)
> > +{
> > +	struct elan_tp_data *data = file->private_data;
> > +	int retval;
> > +	if (!data)
> > +		return -ENODEV;
> > +
> > +	retval = mutex_lock_interruptible(&data->dbfs_mutex);
> > +	if (retval)
> > +		return retval;
> > +	file->private_data = NULL;
> > +	kobject_put(&data->client->dev.kobj);
> > +	mutex_unlock(&data->dbfs_mutex);
> > +	return 0;
> > +}
> > +
> > +
> > +static ssize_t elan_dbfs_read(struct file *file,
> > +		char __user *buffer, size_t count, loff_t *ppos)
> > +{
> > +	struct elan_tp_data *data = file->private_data;
> > +	int retval;
> > +	if (!data)
> > +		return -ENODEV;
> > +
> > +	retval = mutex_lock_interruptible(&data->dbfs_mutex);
> > +	if (retval)
> > +		return -EFAULT;
> > +	if (data->dbfs_buffer.bfetch == false) {
> > +		if (!copy_to_user(buffer, data->dbfs_buffer.buffer, count)) {
> > +			data->dbfs_buffer.bfetch = true;
> > +			retval = count;
> > +		} else {
> > +			retval = -2;
> 
> -ENOENT? Why?
> 
> > +		}
> > +	} else {
> > +		retval = -4;
> 
> -EINTR???
> 
> > +	}
> > +	mutex_unlock(&data->dbfs_mutex);
> > +	return retval;
> > +}
> > +
> > +static ssize_t elan_dbfs_write(struct file *file,
> > +		const char __user *buffer, size_t count, loff_t *ppos)
> > +{
> > +	struct elan_tp_data *data = file->private_data;
> > +	int retval;
> > +	if (!data)
> > +		return -ENODEV;
> > +
> > +	retval = mutex_lock_interruptible(&data->dbfs_mutex);
> > +	if (retval)
> > +		return -EFAULT;
> > +	retval = count;
> > +	mutex_unlock(&data->dbfs_mutex);
> > +	return retval;
> > +}
> 
> This looks like an unneeded stub.
> 
> > +
> > +static long elan_dbfs_ioctrl(struct file *file,
> > +		unsigned int cmd, unsigned long arg)
> > +{
> > +	int retval = 0;
> > +	struct elan_tp_data *data = file->private_data;
> > +
> > +	retval = mutex_lock_interruptible(&data->dbfs_mutex);
> > +	if (retval)
> > +		return retval;
> > +	mutex_unlock(&data->dbfs_mutex);
> > +	return retval;
> > +}
> 
> Same here.
> 
> > +
> > +static const struct file_operations elan_debug_fops = {
> > +	.open = elan_dbfs_open,
> > +	.release = elan_dbfs_release,
> > +	.read = elan_dbfs_read,
> > +	.write = elan_dbfs_write,
> > +	.unlocked_ioctl = elan_dbfs_ioctrl
> > +};
> > +
> > +static int elan_dbfs_init(struct elan_tp_data *data)
> > +{
> > +	/* Create a global debugfs root for all elan devices */
> > +	/* sys/kernel/debug/elan */
> > +	data->dbfs_root = debugfs_create_dir("elan", NULL);
> > +	if (!data->dbfs_root) {
> > +		dev_err(&data->client->dev, "cannot create dbfs_root.\n");
> > +		return -ENODEV;
> > +	}
> 
> That should not be done in per-device code. That said, do you really
> need to dump your packets - looks like you are done with the driver
> development?
> 
> > +	mutex_init(&data->dbfs_mutex);
> > +
> > +	debugfs_create_file(DRIVER_NAME, 0777,
> > +				data->dbfs_root, data, &elan_debug_fops);
> > +	data->dbfs_buffer.bfetch = false;
> > +	return 0;
> > +}
> > +
> > +/**********************************************************
> > + * IAP firmware updater related routines                  *
> > + **********************************************************
> > +*/
> > +
> > +static int elan_iap_getmode(struct elan_tp_data *data)
> > +{
> > +	u16 constant;
> > +	int retval;
> > +	struct i2c_client *client = data->client;
> > +
> > +	if (data->smbus) {
> > +		retval = i2c_smbus_read_block_data(client,
> > +					ETP_SMBUS_IAP_CTRL_CMD, val);
> > +		if (retval < 0) {
> > +			dev_err(&client->dev, "read iap ctrol fail.\n");
> > +			return UNKNOWN_MODE;
> > +		}
> > +		constant = be16_to_cpup((__be16 *)val);
> > +		dev_dbg(&client->dev, "smbus iap control reg: 0x%04x.\n",
> > +								constant);
> > +		if ((constant & ETP_SMBUS_IAP_MODE_ON) == 0x00)
> > +			return MAIN_MODE;
> > +	} else {
> > +		retval = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val);
> > +		if (retval < 0) {
> > +			dev_err(&client->dev, "read iap ctrol fail.\n");
> > +			return UNKNOWN_MODE;
> > +		}
> > +		constant = le16_to_cpup((__le16 *)val);
> > +		dev_dbg(&client->dev, "i2c iap control reg: 0x%04x.\n",
> > +								constant);
> > +		if (constant & ETP_I2C_MAIN_MODE_ON)
> > +			return MAIN_MODE;
> > +	}
> 
> Instead of doing
> 
> 	if (smbus)
> 		do_this();
> 	else
> 		do_that();
> 
> everywhere maybe you could define transport functions and invoke them
> through a pointer to transport ops?
> 
> 
> > +
> > +	return IAP_MODE;
> > +}
> > +
> > +static int elan_iap_checksum(struct elan_tp_data *data)
> > +{
> > +	int retval = 0;
> > +	u16 checksum = -1;
> > +	struct i2c_client *client = data->client;
> > +
> > +	if (data->smbus) {
> > +		retval = i2c_smbus_read_block_data(client,
> > +					ETP_SMBUS_IAP_CHECKSUM_CMD, val);
> > +		if (retval < 0) {
> > +			dev_err(&client->dev, "Read checksum fail, %d\n",
> > +								retval);
> > +			return -1;
> > +		}
> > +		checksum = be16_to_cpup((__be16 *)val);
> > +	} else {
> > +		retval = elan_i2c_read_cmd(client,
> > +					ETP_I2C_IAP_CHECKSUM_CMD, val);
> > +		if (retval < 0) {
> > +			dev_err(&client->dev, "Read checksum fail, %d\n",
> > +								retval);
> > +			return -1;
> > +		}
> > +		checksum = le16_to_cpup((__le16 *)val);
> > +	}
> > +	return checksum;
> > +}
> > +
> > +static bool elan_iap_reset(struct elan_tp_data *data)
> > +{
> > +	int retval = 0;
> > +	struct i2c_client *client = data->client;
> > +
> > +	if (data->smbus)
> > +		retval = i2c_smbus_write_byte(client,
> > +						ETP_SMBUS_IAP_RESET_CMD);
> > +	else
> > +		retval = elan_i2c_write_cmd(client, ETP_I2C_IAP_RESET_CMD,
> > +						ETP_I2C_IAP_RESET);
> > +	if (retval < 0) {
> > +		dev_err(&client->dev, "cannot reset IC, %d\n", retval);
> > +		return false;
> > +	}
> > +	return true;
> > +}
> > +
> > +static bool elan_iap_setflashkey(struct elan_tp_data *data)
> > +{
> > +	int retval = 0;
> > +	struct i2c_client *client = data->client;
> > +	u8 smbus_cmd[4] = {0x00, 0x0B, 0x00, 0x5A};
> > +
> > +	if (data->smbus)
> > +		retval = i2c_smbus_write_block_data(client,
> > +				ETP_SMBUS_IAP_CMD, 4, smbus_cmd);
> > +	else
> > +		retval = elan_i2c_write_cmd(client, ETP_I2C_IAP_CMD,
> > +					ETP_I2C_IAP_PASSWORD);
> > +	if (retval < 0) {
> > +		dev_err(&client->dev, "cannot set flash key, %d\n", retval);
> > +		return false;
> > +	}
> > +
> > +	return true;
> > +}
> > +
> > +static int elan_check_fw(struct elan_tp_data *data,
> > +				const struct firmware *fw)
> > +{
> > +	struct device *dev = &data->client->dev;
> > +
> > +	/* Firmware must match exact PAGE_NUM * PAGE_SIZE bytes */
> > +	if (fw->size != ETP_FW_SIZE) {
> > +		dev_err(dev, "invalid firmware size = %zu, expected %d.\n",
> > +					fw->size, ETP_FW_SIZE);
> > +		return -EBADF;
> > +	}
> > +
> > +	/* Get IAP Start Address*/
> > +	memcpy(val, &fw->data[ETP_IAP_START_ADDR * 2], 2);
> > +	data->iap_start_addr = le16_to_cpup((__le16 *)val);
> > +	return 0;
> > +}
> > +
> > +
> > +static int elan_smbus_prepare_fw_update(struct elan_tp_data *data)
> > +{
> > +	struct i2c_client *client = data->client;
> > +	struct device *dev = &data->client->dev;
> > +	u16 password;
> > +	u8 cmd[4] = {0x0F, 0x78, 0x00, 0x06};
> > +
> > +	/* Get FW in which mode	(IAP_MODE/MAIN_MODE)  */
> > +	int mode = elan_iap_getmode(data);
> > +	if (mode == UNKNOWN_MODE)
> > +		return -1;
> > +
> > +	if (mode == MAIN_MODE) {
> > +
> > +		/* set flash key*/
> > +		if (elan_iap_setflashkey(data) == false) {
> > +			dev_err(dev, "cannot set flash key\n");
> > +			return -1;
> > +		}
> > +
> > +		/* write iap password */
> > +		if (i2c_smbus_write_byte(client,
> > +				ETP_SMBUS_IAP_PASSWORD_WRITE) < 0) {
> > +			dev_err(dev, "cannot write iap password\n");
> > +			return -1;
> 
> Would be nice using appropriate error codes. EIO?
> 
> > +		}
> > +
> > +		if (i2c_smbus_write_block_data(client,
> > +				ETP_SMBUS_IAP_CMD, 4, cmd) < 0) {
> > +			dev_err(dev, "cannot write cmd\n");
> > +			return -1;
> > +		}
> > +
> > +		/* read password to check we enabled successfully. */
> > +		if (i2c_smbus_read_block_data(client,
> > +				ETP_SMBUS_IAP_PASSWORD_READ, val) < 0) {
> > +			dev_err(dev, "cannot get iap password\n");
> > +			return -1;
> > +		}
> > +		password = be16_to_cpup((__be16 *)val);
> > +
> > +		if (password != ETP_SMBUS_IAP_PASSWORD) {
> > +			dev_err(dev, "wrong iap password = 0x%X\n", password);
> > +			return -1;
> > +		}
> > +		/* wait 30ms, from MAIN_MODE change to IAP_MODE*/
> > +		msleep(30);
> > +	}
> > +
> > +	/* set flash key*/
> > +	if (elan_iap_setflashkey(data) == false) {
> > +		dev_err(dev, "cannot set flash key\n");
> > +		return -1;
> > +	}
> > +
> > +	/* Reset IC */
> > +	if (elan_iap_reset(data) == false) {
> > +		dev_err(dev, "iap reset fail.\n");
> > +		return -1;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int elan_i2c_prepare_fw_update(struct elan_tp_data *data)
> > +{
> > +	struct i2c_client *client = data->client;
> > +	struct device *dev = &data->client->dev;
> > +	u16 password;
> > +
> > +	/* Get FW in which mode	(IAP_MODE/MAIN_MODE)  */
> > +	int mode = elan_iap_getmode(data);
> > +	if (mode == UNKNOWN_MODE)
> > +		return -1;
> > +
> > +	if (mode == IAP_MODE) {
> > +		/* Reset IC */
> > +		if (elan_iap_reset(data) == false)
> > +			return -1;
> > +		msleep(30);
> > +	}
> > +
> > +	/* set flash key*/
> > +	if (elan_iap_setflashkey(data) == false) {
> > +		dev_err(dev, "cannot set flash key\n");
> > +		return -1;
> > +	}
> > +
> > +	/* Wait for F/W IAP initialization */
> > +	if (mode == MAIN_MODE)
> > +		msleep(100);
> > +	else
> > +		msleep(30);
> > +
> > +	/* check is in iap mode or not*/
> > +	if (elan_iap_getmode(data) == MAIN_MODE) {
> > +		dev_err(dev, "status wrong.\n");
> > +		return -1;
> > +	}
> > +
> > +	/* set flash key again */
> > +	if (elan_iap_setflashkey(data) == false) {
> > +		dev_err(dev, "cannot set flash key\n");
> > +		return -1;
> > +	}
> > +
> > +	/* Wait for F/W IAP initialization */
> > +	if (mode == MAIN_MODE)
> > +		msleep(100);
> > +	else
> > +		msleep(30);
> > +
> > +	/* read back to check we actually enabled successfully. */
> > +	if (elan_i2c_read_cmd(client, ETP_I2C_IAP_CMD, val) < 0) {
> > +		dev_err(dev, "cannot get iap register\n");
> > +		return -1;
> > +	}
> > +	password = le16_to_cpup((__le16 *)val);
> > +
> > +	if (password != ETP_I2C_IAP_PASSWORD) {
> > +		dev_err(dev, "wrong iap password = 0x%X\n", password);
> > +		return -1;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static bool elan_iap_page_write_ok(struct elan_tp_data *data)
> > +{
> > +	u16 constant;
> > +	int retval = 0;
> > +	struct i2c_client *client = data->client;
> > +
> > +
> > +	if (data->smbus) {
> > +		retval = i2c_smbus_read_block_data(client,
> > +					ETP_SMBUS_IAP_CTRL_CMD, val);
> > +		if (retval < 0)
> > +			return false;
> > +		constant = be16_to_cpup((__be16 *)val);
> > +	} else {
> > +		retval = elan_i2c_read_cmd(client,
> > +					ETP_I2C_IAP_CTRL_CMD, val);
> > +		if (retval < 0)
> > +			return false;
> > +		constant = le16_to_cpup((__le16 *)val);
> > +	}
> > +
> > +	if (constant & ETP_FW_IAP_PAGE_ERR)
> > +		return false;
> > +
> > +	if (constant & ETP_FW_IAP_INTERFACE_ERR)
> > +		return false;
> > +	return true;
> > +}
> > +
> > +static int elan_smbus_write_fw_block(struct elan_tp_data *data,
> > +				const u8 *page, u16 checksum, int idx)
> > +{
> > +	struct device *dev = &data->client->dev;
> > +	int half_page_size = ETP_FW_PAGE_SIZE / 2;
> > +	int repeat = 3;
> > +
> > +	do {
> > +		/* due to smbus can write 32 bytes one time,
> > +		so, we must write data 2 times.
> > +		*/
> > +		i2c_smbus_write_block_data(data->client,
> > +					ETP_SMBUS_WRITE_FW_BLOCK,
> > +					half_page_size,
> > +					page);
> > +		i2c_smbus_write_block_data(data->client,
> > +					ETP_SMBUS_WRITE_FW_BLOCK,
> > +					half_page_size,
> > +					(page + half_page_size));
> > +		/* Wait for F/W to update one page ROM data. */
> > +		usleep_range(8000, 10000);
> > +		if (elan_iap_page_write_ok(data))
> > +			break;
> > +		dev_info(dev, "IAP retry this page! [%d]\n", idx);
> > +		repeat--;
> > +	} while (repeat == 0);
> > +
> > +	if (repeat > 0)
> > +		return 0;
> > +	return -1;
> > +
> > +}
> > +
> > +static int elan_i2c_write_fw_block(struct elan_tp_data *data,
> > +				const u8 *page, u16 checksum, int idx)
> > +{
> > +	struct device *dev = &data->client->dev;
> > +	int ret;
> > +	int repeat = 3;
> > +	u8 page_store[ETP_FW_PAGE_SIZE + 4];
> > +
> > +	page_store[0] = ETP_I2C_IAP_REG_L;
> > +	page_store[1] = ETP_I2C_IAP_REG_H;
> > +	memcpy(&page_store[2], page, ETP_FW_PAGE_SIZE);
> > +
> > +	/* recode checksum at last two bytes */
> > +	page_store[ETP_FW_PAGE_SIZE+2] = (u8)(checksum & 0xFF);
> > +	page_store[ETP_FW_PAGE_SIZE+3] = (u8)((checksum >> 8)&0xFF);
> > +
> > +	do {
> > +		ret = i2c_master_send(data->client, page_store,
> > +						ETP_FW_PAGE_SIZE + 4);
> > +
> > +		/* Wait for F/W to update one page ROM data. */
> > +		msleep(20);
> > +
> > +		if (ret == (ETP_FW_PAGE_SIZE + 4)) {
> > +			if (elan_iap_page_write_ok(data))
> > +				break;
> > +		}
> > +		dev_dbg(dev, "IAP retry this page! [%d]\n", idx);
> > +		repeat--;
> > +	} while (repeat == 0);
> > +
> > +	if (repeat > 0)
> > +		return 0;
> > +	return -1;
> > +}
> > +
> > +static int elan_write_fw_block(struct elan_tp_data *data,
> > +				const u8 *page, u16 checksum, int idx)
> > +{
> > +	int ret;
> > +	if (data->smbus)
> > +		ret = elan_smbus_write_fw_block(data, page, checksum, idx);
> > +	else
> > +		ret = elan_i2c_write_fw_block(data, page, checksum, idx);
> > +	return ret;
> > +}
> > +
> > +static int elan_prepare_fw_update(struct elan_tp_data *data)
> > +{
> > +	int ret = 0;
> > +	if (data->smbus)
> > +		ret = elan_smbus_prepare_fw_update(data);
> > +	else
> > +		ret = elan_i2c_prepare_fw_update(data);
> > +	return ret;
> > +}
> > +
> > +static int elan_firmware(struct elan_tp_data *data)
> > +{
> > +	struct device *dev = &data->client->dev;
> > +	const struct firmware *fw;
> > +	const char *fw_name = ETP_FW_NAME;
> > +	int i, j, ret;
> > +	u16 boot_page_count;
> > +	u16 sw_checksum, fw_checksum;
> > +	data->updated_fw = true;
> > +
> > +	dev_info(dev, "Start firmware update....\n");
> > +
> > +	ret = request_firmware(&fw, ETP_FW_NAME, dev);
> > +	if (ret) {
> > +		dev_err(dev, "cannot load firmware from %s, %d\n",
> > +							fw_name, ret);
> > +		goto done;
> > +	}
> > +	/* check fw data match current iap version */
> > +	ret = elan_check_fw(data, fw);
> > +	if (ret) {
> > +		dev_err(dev, "Invalid Elan firmware from %s, %d\n",
> > +							fw_name, ret);
> > +		goto done;
> > +	}
> > +	/* setup IAP status */
> > +	ret = elan_prepare_fw_update(data);
> > +	if (ret)
> > +		goto done;
> > +	sw_checksum = 0;
> > +	fw_checksum = 0;
> > +	boot_page_count = (data->iap_start_addr * 2) / ETP_FW_PAGE_SIZE;
> > +	for (i = boot_page_count; i < ETP_FW_PAGE_COUNT; i++) {
> > +		u16 checksum = 0;
> > +		const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE];
> > +
> > +		for (j = 0; j < ETP_FW_PAGE_SIZE; j += 2)
> > +			checksum += ((page[j + 1] << 8) | page[j]);
> > +
> > +		ret = elan_write_fw_block(data, page, checksum, i);
> > +		if (ret) {
> > +			dev_err(dev, "write page %d fail\n", i);
> > +			goto done;
> > +		}
> > +		sw_checksum += checksum;
> > +	}
> > +
> > +	/* Wait WDT reset and power on reset */
> > +	msleep(600);
> > +
> > +	/* check checksum */
> > +	fw_checksum = elan_iap_checksum(data);
> > +	if (sw_checksum != fw_checksum) {
> > +		dev_err(dev, "checksum diff sw=[%04X], fw=[%04X]\n",
> > +					sw_checksum, fw_checksum);
> > +		ret = -1;
> > +		goto done;
> > +	}
> > +	ret = 0;
> > +done:
> > +	if (ret != 0) {
> > +		elan_iap_reset(data);
> > +		data->updated_fw = false;
> > +	} else {
> > +		if (data->smbus) {
> > +			data->updated_fw = false;
> > +			elan_initialize(data);
> > +		}
> > +	}
> > +	release_firmware(fw);
> > +	return ret;
> > +}
> > +
> > +/******************************************************************
> > +* Elan smbus interface
> > +*******************************************************************
> > +*/
> > +static int elan_smbus_initialize(struct i2c_client *client)
> > +{
> > +	u8 check[ETP_SMBUS_HELLOPACKET_LEN] = {0x55, 0x55, 0x55, 0x55,
> 0x55};
> > +	u8 values[ETP_SMBUS_HELLOPACKET_LEN] = {0, 0, 0, 0, 0};
> > +	int ret;
> > +
> > +	/* Get hello packet */
> > +	ret = i2c_smbus_read_block_data(client,
> > +				ETP_SMBUS_HELLOPACKET_CMD, values);
> > +	if (ret != ETP_SMBUS_HELLOPACKET_LEN) {
> > +		dev_err(&client->dev, "hello packet length fail\n");
> > +		return -1;
> > +	}
> > +
> > +	/* compare hello packet */
> > +	if (memcmp(values, check, ETP_SMBUS_HELLOPACKET_LEN)) {
> > +		dev_err(&client->dev, "hello packet fail [%x %x %x %x %x]\n",
> > +		values[0], values[1], values[2], values[3], values[4]);
> > +		return -1;
> > +	}
> > +
> > +	/* enable tp */
> > +	ret = i2c_smbus_write_byte(client, ETP_SMBUS_ENABLE_TP);
> > +	return ret;
> > +}
> > +
> > +static int elan_smbus_enable_calibrate(struct i2c_client *client)
> > +{
> > +	u8 cmd[4] = {0x00, 0x07, 0x00,
> ETP_ENABLE_ABS|ETP_ENABLE_CALIBRATE};
> > +
> > +	return i2c_smbus_write_block_data(client,
> > +				ETP_SMBUS_IAP_CMD, 4, cmd);
> > +}
> > +
> > +static int elan_smbus_disable_calibrate(struct i2c_client *client)
> > +{
> > +	u8 cmd[4] = {0x00, 0x07, 0x00,
> ETP_ENABLE_ABS|ETP_DISABLE_CALIBRATE};
> > +
> > +	return i2c_smbus_write_block_data(client,
> > +				ETP_SMBUS_IAP_CMD, 4, cmd);
> > +}
> > +
> > +static int elan_smbus_enable_absolute_mode(struct i2c_client *client)
> > +{
> > +	u8 cmd[4] = {0x00, 0x07, 0x00, ETP_ENABLE_ABS};
> > +
> > +	return i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD, 4, cmd);
> > +}
> > +
> > +/*****************************************************************
> > +* Elan i2c interface
> > +******************************************************************
> > +*/
> > +static int elan_i2c_read_block(struct i2c_client *client,
> > +				 u16 reg, u8 *val, u16 len)
> > +{
> > +	struct i2c_msg msgs[2];
> > +	u8 buf[2];
> > +	int ret;
> > +
> > +	buf[0] = reg & 0xff;
> > +	buf[1] = (reg >> 8) & 0xff;
> > +
> > +	msgs[0].addr = client->addr;
> > +	msgs[0].flags = client->flags & I2C_M_TEN;
> > +	msgs[0].len = 2;
> > +	msgs[0].buf = buf;
> > +
> > +	msgs[1].addr = client->addr;
> > +	msgs[1].flags = client->flags & I2C_M_TEN;
> > +	msgs[1].flags |= I2C_M_RD;
> > +	msgs[1].len = len;
> > +	msgs[1].buf = val;
> > +
> > +	ret = i2c_transfer(client->adapter, msgs, 2);
> > +	return ret != 2 ? -EIO : 0;
> > +}
> > +
> > +static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 *val)
> > +{
> > +	int retval;
> > +
> > +	retval = elan_i2c_read_block(client, reg, val, ETP_INF_LENGTH);
> > +	if (retval < 0) {
> > +		dev_err(&client->dev, "reading cmd (0x%04x) fail.\n", reg);
> > +		return retval;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd)
> > +{
> > +	struct i2c_msg msg;
> > +	u8 buf[4];
> > +	int ret;
> > +
> > +	buf[0] = reg & 0xff;
> > +	buf[1] = (reg >> 8) & 0xff;
> > +	buf[2] = cmd & 0xff;
> > +	buf[3] = (cmd >> 8) & 0xff;
> > +
> > +	msg.addr = client->addr;
> > +	msg.flags = client->flags & I2C_M_TEN;
> > +	msg.len = 4;
> > +	msg.buf = buf;
> > +
> > +	ret = i2c_transfer(client->adapter, &msg, 1);
> > +	return ret != 1 ? -EIO : 0;
> > +}
> > +
> > +static int elan_i2c_reset(struct i2c_client *client)
> > +{
> > +	return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
> > +						ETP_I2C_RESET);
> > +}
> > +
> > +static int elan_i2c_wake_up(struct i2c_client *client)
> > +{
> > +	return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
> > +						ETP_I2C_WAKE_UP);
> > +}
> > +
> > +static int elan_i2c_sleep(struct i2c_client *client)
> > +{
> > +	return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
> > +						ETP_I2C_SLEEP);
> > +}
> > +
> > +static int elan_i2c_enable_absolute_mode(struct i2c_client *client)
> > +{
> > +	return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
> > +						ETP_ENABLE_ABS);
> > +}
> > +
> > +static int elan_i2c_enable_calibrate(struct i2c_client *client)
> > +{
> > +	return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
> > +			ETP_ENABLE_ABS|ETP_ENABLE_CALIBRATE);
> > +}
> > +
> > +static int elan_i2c_disable_calibrate(struct i2c_client *client)
> > +{
> > +	return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
> > +			ETP_ENABLE_ABS|ETP_DISABLE_CALIBRATE);
> > +}
> > +
> > +static int elan_i2c_get_desc(struct i2c_client *client, u8 *val)
> > +{
> > +	return elan_i2c_read_block(client, ETP_I2C_DESC_CMD, val,
> > +					ETP_I2C_DESC_LENGTH);
> > +}
> > +
> > +static int elan_i2c_get_report_desc(struct i2c_client *client, u8 *val)
> > +{
> > +	return elan_i2c_read_block(client, ETP_I2C_REPORT_DESC_CMD,
> > +				 val, ETP_I2C_REPORT_DESC_LENGTH);
> > +}
> > +
> > +static int elan_i2c_initialize(struct i2c_client *client)
> > +{
> > +	struct device *dev = &client->dev;
> > +	int rc;
> > +
> > +	rc = elan_i2c_reset(client);
> > +	if (rc < 0) {
> > +		dev_err(dev, "device reset failed.\n");
> > +		return -1;
> > +	}
> > +
> > +	/* wait for get reset return flag */
> > +	msleep(100);
> > +	/* get reset return flag 0000 */
> > +	rc = i2c_master_recv(client, val, ETP_INF_LENGTH);
> > +	if (rc < 0) {
> > +		dev_err(dev, "get device reset return value failed.\n");
> > +		return -1;
> > +	}
> > +
> > +	rc = elan_i2c_get_desc(client, val);
> > +	if (rc < 0) {
> > +		dev_err(dev, "cannot get device descriptor.\n");
> > +		return -1;
> > +	}
> > +
> > +	rc = elan_i2c_get_report_desc(client, val);
> > +	if (rc < 0) {
> > +		dev_err(dev, "fetching report descriptor failed.\n");
> > +		return -1;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**************************************************************************
> > +* Genernal functions
> > +***************************************************************************
> > +*/
> > +
> > +/*
> > + * (value from firmware) * 10 + 790 = dpi
> > + * we also have to convert dpi to dots/mm (*10/254 to avoid floating point)
> > + */
> > +static unsigned int elan_convert_res(char val)
> > +{
> > +	int res;
> > +	if (val & 0x80) {
> > +		val = ~val + 1;
> > +		res = (790 - val * 10) * 10 / 254;
> > +	} else
> > +		res = (val * 10 + 790) * 10 / 254;
> > +	return res;
> > +}
> > +
> > +static int elan_get_iap_version(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus) {
> > +		i2c_smbus_read_block_data(data->client,
> > +					ETP_SMBUS_IAP_VERSION_CMD, val);
> > +		ret = val[2];
> > +	} else {
> > +		elan_i2c_read_cmd(data->client,
> > +				ETP_I2C_IAP_VERSION_CMD, val);
> > +		ret = val[0];
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int elan_get_x_max(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus) {
> > +		i2c_smbus_read_block_data(data->client,
> > +					ETP_SMBUS_RANGE_CMD, val);
> > +		ret = (0x0f & val[0]) << 8 | val[1];
> > +	} else {
> > +		elan_i2c_read_cmd(data->client,
> > +					ETP_I2C_MAX_X_AXIS_CMD, val);
> > +		ret = (0x0f & val[1]) << 8 | val[0];
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int elan_get_y_max(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus) {
> > +		i2c_smbus_read_block_data(data->client,
> > +					ETP_SMBUS_RANGE_CMD, val);
> > +		ret = (0xf0 & val[0]) << 4 | val[2];
> > +	} else {
> > +		elan_i2c_read_cmd(data->client,
> > +					ETP_I2C_MAX_Y_AXIS_CMD, val);
> > +		ret = (0x0f & val[1]) << 8 | val[0];
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int elan_get_x_tracenum(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus) {
> > +		i2c_smbus_read_block_data(data->client,
> > +				ETP_SMBUS_XY_TRACENUM_CMD, val);
> > +		ret = (val[1] - 1);
> > +	} else {
> > +		elan_i2c_read_cmd(data->client,
> > +				ETP_I2C_XY_TRACENUM_CMD, val);
> > +		ret = (val[0] - 1);
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int elan_get_y_tracenum(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus) {
> > +		i2c_smbus_read_block_data(data->client,
> > +				ETP_SMBUS_XY_TRACENUM_CMD, val);
> > +		ret = (val[2] - 1);
> > +	} else {
> > +		ret = elan_i2c_read_cmd(data->client,
> > +				ETP_I2C_XY_TRACENUM_CMD, val);
> > +		ret = (val[1] - 1);
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int elan_get_fw_version(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus) {
> > +		i2c_smbus_read_block_data(data->client,
> > +				ETP_SMBUS_FW_VERSION_CMD, val);
> > +		ret = val[2];
> > +	} else {
> > +		elan_i2c_read_cmd(data->client,
> > +				ETP_I2C_FW_VERSION_CMD, val);
> > +		ret = val[0];
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int elan_get_sm_version(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus)
> > +		i2c_smbus_read_block_data(data->client,
> > +				ETP_SMBUS_SM_VERSION_CMD, val);
> > +	else
> > +		elan_i2c_read_block(data->client,
> > +				ETP_I2C_SM_VERSION_CMD, val, 1);
> > +	ret = val[0];
> > +	return ret;
> > +}
> > +
> > +static int elan_get_unique_id(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus) {
> > +		i2c_smbus_read_block_data(data->client,
> > +				ETP_SMBUS_UNIQUEID_CMD, val);
> > +		ret = val[1];
> > +	} else {
> > +		elan_i2c_read_cmd(data->client,
> > +				ETP_I2C_UNIQUEID_CMD, val);
> > +		ret = val[0];
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int elan_get_x_resolution(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus) {
> > +		i2c_smbus_read_block_data(data->client,
> > +				ETP_SMBUS_RESOLUTION_CMD, val);
> > +		ret = elan_convert_res(val[1] & 0x0F);
> > +	} else {
> > +		elan_i2c_read_cmd(data->client,
> > +				ETP_I2C_RESOLUTION_CMD, val);
> > +		ret = elan_convert_res(val[0]);
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int elan_get_y_resolution(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus) {
> > +		i2c_smbus_read_block_data(data->client,
> > +				ETP_SMBUS_RESOLUTION_CMD, val);
> > +		ret = elan_convert_res((val[1] & 0xF0) >> 4);
> > +	} else {
> > +		elan_i2c_read_cmd(data->client,
> > +				ETP_I2C_RESOLUTION_CMD, val);
> > +		ret = elan_convert_res(val[1]);
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int elan_get_fw_checksum(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus) {
> > +		i2c_smbus_read_block_data(data->client,
> > +				ETP_SMBUS_FW_CHECKSUM_CMD, val);
> > +		ret = be16_to_cpup((__be16 *)val);
> > +	} else {
> > +		elan_i2c_read_cmd(data->client,
> > +				ETP_I2C_FW_CHECKSUM_CMD, val);
> > +		ret = le16_to_cpup((__le16 *)val);
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int elan_get_max_baseline(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus) {
> > +		i2c_smbus_read_block_data(data->client,
> > +				ETP_SMBUS_MAX_BASELINE_CMD, val);
> > +		ret = be16_to_cpup((__be16 *)val);
> > +	} else {
> > +		elan_i2c_read_cmd(data->client,
> > +				ETP_I2C_MAX_BASELINE_CMD, val);
> > +		ret = le16_to_cpup((__le16 *)val);
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int elan_get_min_baseline(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus) {
> > +		i2c_smbus_read_block_data(data->client,
> > +				ETP_SMBUS_MIN_BASELINE_CMD, val);
> > +		ret = be16_to_cpup((__be16 *)val);
> > +	} else {
> > +		elan_i2c_read_cmd(data->client,
> > +			ETP_I2C_MIN_BASELINE_CMD, val);
> > +		ret = le16_to_cpup((__le16 *)val);
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int elan_enable_calibrate(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus)
> > +		ret = elan_smbus_enable_calibrate(data->client);
> > +	else
> > +		ret = elan_i2c_enable_calibrate(data->client);
> > +	return ret;
> > +}
> > +
> > +static int elan_disable_calibrate(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus)
> > +		ret = elan_smbus_disable_calibrate(data->client);
> > +	else
> > +		ret = elan_i2c_disable_calibrate(data->client);
> > +	return ret;
> > +}
> > +
> > +static int elan_initialize(struct elan_tp_data *data)
> > +{
> > +	int ret;
> > +	if (data->smbus) {
> > +		ret = elan_smbus_initialize(data->client);
> > +		if (ret < 0) {
> > +			dev_err(&data->client->dev,
> > +				"device initialize failed.\n");
> > +			goto err_initialize;
> > +		}
> > +
> > +		ret = elan_smbus_enable_absolute_mode(data->client);
> > +		if (ret < 0)
> > +			dev_err(&data->client->dev,
> > +				"cannot switch to absolute mode.\n");
> > +	} else {
> > +		ret = elan_i2c_initialize(data->client);
> > +		if (ret < 0) {
> > +			dev_err(&data->client->dev,
> > +				"device initialize failed.\n");
> > +			goto err_initialize;
> > +		}
> > +
> > +		ret = elan_i2c_enable_absolute_mode(data->client);
> > +		if (ret < 0) {
> > +			dev_err(&data->client->dev,
> > +				"cannot switch to absolute mode.\n");
> > +			goto err_initialize;
> > +		}
> > +
> > +		ret = elan_i2c_wake_up(data->client);
> > +		if (ret < 0)
> > +			dev_err(&data->client->dev,
> > +					"device wake up failed.\n");
> > +	}
> > +err_initialize:
> > +	return ret;
> > +}
> > +
> > +/********************************************************************
> > + * below routines export interfaces to sysfs file system.
> > + * so user can get firmware/driver/hardware information using cat command.
> > + * e.g.: use below command to get firmware version
> > + *      cat /sys/bus/i2c/drivers/elan_i2c/1-0015/firmware_version
> > + *******************************************************************
> > + */
> > +static ssize_t elan_sysfs_enable_detailinfo(struct device *dev,
> > +					struct device_attribute *attr,
> > +					char *buf)
> > +{
> > +	struct elan_tp_data *data = dev_get_drvdata(dev);
> > +	data->enable_detail_info = true;
> 
> What is this for?
> 
> > +	return sprintf(buf, "enable\n");
> > +}
> > +
> > +static ssize_t elan_sysfs_read_fw_checksum(struct device *dev,
> > +					struct device_attribute *attr,
> > +					char *buf)
> > +{
> > +	unsigned int checksum = 0;
> > +	struct elan_tp_data *data = dev_get_drvdata(dev);
> > +	if (data->enable_detail_info == true) {
> > +		checksum = elan_get_fw_checksum(data);
> > +		data->enable_detail_info = false;
> > +	}
> > +	return sprintf(buf, "0x%04x\n", checksum);
> > +}
> > +
> > +static ssize_t elan_sysfs_read_unique_id(struct device *dev,
> > +					struct device_attribute *attr,
> > +					char *buf)
> > +{
> > +	struct elan_tp_data *data = dev_get_drvdata(dev);
> > +	data->unique_id = elan_get_unique_id(data);
> > +	return sprintf(buf, "0x%04x\n", data->unique_id);
> > +}
> > +
> > +static ssize_t elan_sysfs_read_driver_ver(struct device *dev,
> > +					struct device_attribute *attr,
> > +					char *buf)
> > +{
> > +	return sprintf(buf, "%s\n", ELAN_DRIVER_VERSION);
> 
> This should be reported in MODULE_VERSION if you need it.
> 
> > +}
> > +
> > +static ssize_t elan_sysfs_read_fw_ver(struct device *dev,
> > +				    struct device_attribute *attr,
> > +				    char *buf)
> > +{
> > +	struct elan_tp_data *data = dev_get_drvdata(dev);
> > +	data->fw_version = elan_get_fw_version(data);
> > +	return sprintf(buf, "0x%04x\n", data->fw_version);
> > +}
> > +
> > +static ssize_t elan_sysfs_read_sm_ver(struct device *dev,
> > +				    struct device_attribute *attr,
> > +				    char *buf)
> > +{
> > +	struct elan_tp_data *data = dev_get_drvdata(dev);
> > +	data->sm_version = elan_get_sm_version(data);
> > +	return sprintf(buf, "0x%04x\n", data->sm_version);
> > +}
> > +
> > +static ssize_t elan_sysfs_read_iap_ver(struct device *dev,
> > +				     struct device_attribute *attr,
> > +				     char *buf)
> > +{
> > +	struct elan_tp_data *data = dev_get_drvdata(dev);
> > +	data->iap_version = elan_get_iap_version(data);
> > +	return sprintf(buf, "0x%04x\n", data->iap_version);
> > +}
> > +
> > +
> > +static ssize_t elan_sysfs_update_fw(struct device *dev,
> > +				  struct device_attribute *attr,
> > +				  const char *buf, size_t count)
> > +{
> > +	struct elan_tp_data *data = dev_get_drvdata(dev);
> > +	int ret;
> > +	ret = elan_firmware(data);
> > +	if (ret)
> > +		dev_err(dev, "firmware update failed.\n");
> > +	else
> > +		dev_info(dev, "firmware update succeeded.\n");
> > +	return ret ? ret : count;
> > +}
> > +
> > +static ssize_t elan_sysfs_calibrate(struct device *dev,
> > +				struct device_attribute *attr,
> > +				char *buf)
> > +{
> > +	struct elan_tp_data *data = dev_get_drvdata(dev);
> > +	/* start calibarate cmd */
> > +	u8 smbus_cmd[4] = {0x00, 0x08, 0x00, 0x01};
> > +	int tries = 20;
> > +	int ret = 0;
> > +	val[0] = 0;
> > +
> > +	disable_irq(data->irq);
> > +	elan_enable_calibrate(data);
> > +	if (data->smbus)
> > +		i2c_smbus_write_block_data(data->client,
> > +				ETP_SMBUS_IAP_CMD, 4, smbus_cmd);
> > +	else
> > +		elan_i2c_write_cmd(data->client,
> > +					ETP_I2C_CALIBRATE_CMD, 1);
> > +
> > +	do {
> > +		/* wait 250ms and check finish or not */
> > +		msleep(250);
> > +
> > +		if (data->smbus)
> > +			i2c_smbus_read_block_data(data->client,
> > +					ETP_SMBUS_CALIBRATE_QUERY, val);
> > +		else
> > +			elan_i2c_read_block(data->client,
> > +					ETP_I2C_CALIBRATE_CMD, val, 1);
> > +
> > +		/* calibrate finish */
> > +		if (val[0] == 0)
> > +			break;
> > +	} while (--tries);
> > +
> > +	elan_disable_calibrate(data);
> > +	enable_irq(data->irq);
> > +
> > +	if (tries == 0) {
> > +		dev_err(dev, "Failed to calibrate. Timeout.\n");
> > +		ret = -ETIMEDOUT;
> > +	}
> > +	return sprintf(buf, "calibration finish\n");
> > +}
> > +
> > +
> > +static ssize_t elan_sysfs_read_baseline(struct device *dev,
> > +				   struct device_attribute *attr,
> > +				   char *buf)
> > +{
> > +	struct elan_tp_data *data = dev_get_drvdata(dev);
> > +	int max_baseline, min_baseline;
> > +
> > +	disable_irq(data->irq);
> > +	elan_enable_calibrate(data);
> > +	msleep(250);
> > +	max_baseline = elan_get_max_baseline(data);
> > +	min_baseline = elan_get_min_baseline(data);
> > +	elan_disable_calibrate(data);
> > +	enable_irq(data->irq);
> > +	return sprintf(buf, "max:%d min:%d\n", max_baseline, min_baseline);
> > +}
> > +
> > +static ssize_t elan_sysfs_reinitialize(struct device *dev,
> > +				struct device_attribute *attr,
> > +				char *buf)
> > +{
> > +	struct elan_tp_data *data = dev_get_drvdata(dev);
> > +	int ret;
> > +
> > +	disable_irq(data->irq);
> > +	ret = elan_initialize(data);
> > +	enable_irq(data->irq);
> > +
> > +	if (ret < 0)
> > +		return sprintf(buf, "reinitialize fail\n");
> > +
> > +	return sprintf(buf, "reinitialize success\n");
> 
> This actually should be a write method and instead of returning strings
> you should use return code to indicate success/failure.
> 
> > +}
> > +
> > +static DEVICE_ATTR(unique_id, S_IRUGO, elan_sysfs_read_unique_id, NULL);
> > +static DEVICE_ATTR(firmware_version, S_IRUGO, elan_sysfs_read_fw_ver,
> NULL);
> > +static DEVICE_ATTR(sample_version, S_IRUGO, elan_sysfs_read_sm_ver,
> NULL);
> > +static DEVICE_ATTR(driver_version, S_IRUGO, elan_sysfs_read_driver_ver,
> NULL);
> > +static DEVICE_ATTR(iap_version, S_IRUGO, elan_sysfs_read_iap_ver, NULL);
> > +static DEVICE_ATTR(fw_checksum, S_IRUGO, elan_sysfs_read_fw_checksum,
> NULL);
> > +static DEVICE_ATTR(open_info, S_IRUGO, elan_sysfs_enable_detailinfo,
> NULL);
> > +static DEVICE_ATTR(baseline, S_IRUGO, elan_sysfs_read_baseline, NULL);
> > +static DEVICE_ATTR(reinitialize, S_IRUGO, elan_sysfs_reinitialize, NULL);
> > +static DEVICE_ATTR(calibrate, S_IRUGO, elan_sysfs_calibrate, NULL);
> > +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, elan_sysfs_update_fw);
> > +
> > +static struct attribute *elan_sysfs_entries[] = {
> > +	&dev_attr_unique_id.attr,
> > +	&dev_attr_firmware_version.attr,
> > +	&dev_attr_sample_version.attr,
> > +	&dev_attr_driver_version.attr,
> > +	&dev_attr_iap_version.attr,
> > +	&dev_attr_fw_checksum.attr,
> > +	&dev_attr_open_info.attr,
> > +	&dev_attr_baseline.attr,
> > +	&dev_attr_reinitialize.attr,
> > +	&dev_attr_calibrate.attr,
> > +	&dev_attr_update_fw.attr,
> > +	NULL,
> 
> Please add documentation for sysfs entries to Documentation/...
> 
> > +};
> > +
> > +static const struct attribute_group elan_sysfs_group = {
> > +	.attrs = elan_sysfs_entries,
> > +};
> > +
> > +/*****************************************************************
> > +* Elan isr functions
> > +******************************************************************
> > +*/
> > +
> > +static int elan_check_packet(struct elan_tp_data *data, u8 *packet)
> > +{
> > +	u8 rid;
> > +
> > +	if (data->smbus)
> > +		rid = packet[0];
> > +	else
> > +		rid = packet[ETP_I2C_REPORT_ID_OFFSET];
> > +
> > +	/* check report id */
> > +	if (rid != ETP_REPORT_ID) {
> > +		dev_err(&data->client->dev, "report id [%x] fail.\n", rid);
> > +		return -1;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
> > +{
> > +	struct input_dev *input = data->input;
> > +	u8 *finger_data;
> > +	bool finger_on;
> > +	int pos_x, pos_y;
> > +	int pressure, mk_x, mk_y;
> > +	int i, area_x, area_y, major, minor, new_pressure;
> > +	int finger_count = 0;
> > +	int btn_click;
> > +	u8  tp_info;
> > +
> > +	if (data->smbus) {
> > +		finger_data = &packet[ETP_SMBUS_FINGER_DATA_OFFSET];
> > +		tp_info = packet[1];
> > +	} else {
> > +		finger_data = &packet[ETP_I2C_FINGER_DATA_OFFSET];
> > +		tp_info = packet[3];
> > +	}
> > +
> > +	btn_click = (tp_info & 0x01);
> > +	for (i = 0; i < ETP_MAX_FINGERS; i++) {
> > +		finger_on = (tp_info >> (3 + i)) & 0x01;
> > +
> > +		/* analyze touched finger raw data*/
> > +		if (finger_on) {
> > +			pos_x = ((finger_data[0] & 0xf0) << 4) |
> > +				finger_data[1];
> > +			pos_y = ((finger_data[0] & 0x0f) << 8) |
> > +				finger_data[2];
> > +			pos_y =  data->max_y - pos_y;
> > +			mk_x = (finger_data[3] & 0x0f);
> > +			mk_y = (finger_data[3] >> 4);
> > +			pressure = finger_data[4];
> > +
> > +			/*
> > +			to avoid fat finger be as palm, so reduce the
> > +			width x and y per trace
> > +			*/
> > +			area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE);
> > +			area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE);
> > +
> > +			major = max(area_x, area_y);
> > +			minor = min(area_x, area_y);
> > +
> > +			new_pressure = pressure + ETP_PRESSURE_OFFSET;
> > +			if (new_pressure > ETP_MAX_PRESSURE)
> > +				new_pressure = ETP_MAX_PRESSURE;
> > +
> > +			input_mt_slot(input, i);
> > +			input_mt_report_slot_state(input, MT_TOOL_FINGER,
> > +									true);
> > +			input_report_abs(input, ABS_MT_POSITION_X, pos_x);
> > +			input_report_abs(input, ABS_MT_POSITION_Y, pos_y);
> > +			input_report_abs(input, ABS_MT_PRESSURE, new_pressure);
> > +			input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
> > +			input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
> > +			input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
> > +			finger_data += ETP_FINGER_DATA_LEN;
> > +			finger_count++;
> > +		} else {
> > +			input_mt_slot(input, i);
> > +			input_mt_report_slot_state(input,
> > +						MT_TOOL_FINGER, false);
> > +		}
> > +	}
> > +
> > +	input_report_key(input, BTN_LEFT, (btn_click == 1));
> > +	input_mt_report_pointer_emulation(input, true);
> > +	input_sync(input);
> > +}
> > +
> > +static irqreturn_t elan_isr(int irq, void *dev_id)
> > +{
> > +	struct elan_tp_data *data = dev_id;
> > +	u8 raw[ETP_MAX_REPORT_LEN];
> > +	int retval;
> > +	int report_len;
> > +
> > +	retval = mutex_lock_interruptible(&data->dbfs_mutex);
> > +	if (retval)
> > +		return IRQ_HANDLED;
> > +
> > +	/*
> > +	Only in I2C protocol, when IAP all page wrote finish, driver will
> > +	get one INT signal from high to low, and driver must get 0000
> > +	to confirm IAP is finished.
> > +	*/
> > +	if (data->updated_fw) {
> > +		retval = i2c_master_recv(data->client, raw,
> > +						ETP_INF_LENGTH);
> > +		if (retval == 2 && !le16_to_cpup((__le16 *)raw)) {
> > +			dev_info(&data->client->dev,
> > +				"reinitializing after F/W update...");
> > +			elan_initialize(data);
> > +		}
> > +		data->updated_fw = false;
> > +		goto elan_isr_end;
> > +	}
> > +
> > +	if (data->smbus) {
> > +		report_len = ETP_SMBUS_REPORT_LEN;
> > +		retval = i2c_smbus_read_block_data(data->client,
> > +					ETP_SMBUS_PACKET_QUERY, raw);
> > +	} else {
> > +		report_len = ETP_I2C_REPORT_LEN;
> > +		retval = i2c_master_recv(data->client, raw, report_len);
> > +	}
> > +
> > +	if (retval != report_len) {
> > +		dev_err(&data->client->dev, "wrong packet len(%d)", retval);
> > +		goto elan_isr_end;
> > +	}
> > +
> > +	if (elan_check_packet(data, raw) < 0) {
> > +		dev_err(&data->client->dev, "wrong packet format.");
> > +		goto elan_isr_end;
> > +	}
> > +	elan_report_absolute(data, raw);
> > +	data->dbfs_buffer.bfetch = false;
> > +	memcpy(data->dbfs_buffer.buffer, raw, report_len);
> > +
> > +elan_isr_end:
> > +	mutex_unlock(&data->dbfs_mutex);
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +
> > +static int elan_input_dev_create(struct elan_tp_data *data)
> > +{
> > +	struct i2c_client *client = data->client;
> > +	struct input_dev *input;
> > +	unsigned int x_res, y_res;
> > +	int ret;
> > +
> > +	data->input = input = input_allocate_device();
> > +	if (!input)
> > +		return -ENOMEM;
> > +	input->name = "Elan Touchpad";
> > +	input->id.bustype = BUS_I2C;
> > +	input->dev.parent = &data->client->dev;
> > +
> > +	__set_bit(INPUT_PROP_POINTER, input->propbit);
> > +	__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
> > +	__set_bit(EV_KEY, input->evbit);
> > +	__set_bit(EV_ABS, input->evbit);
> > +
> > +	__set_bit(BTN_LEFT, input->keybit);
> > +	__set_bit(BTN_TOUCH, input->keybit);
> > +	__set_bit(BTN_TOOL_FINGER, input->keybit);
> > +	__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
> > +	__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
> > +	__set_bit(BTN_TOOL_QUADTAP, input->keybit);
> > +	__set_bit(BTN_TOOL_QUINTTAP, input->keybit);
> > +
> > +	__set_bit(ABS_MT_TOUCH_MAJOR, input->absbit);
> > +	__set_bit(ABS_MT_TOUCH_MINOR, input->absbit);
> > +	__set_bit(ABS_MT_POSITION_X, input->absbit);
> > +	__set_bit(ABS_MT_POSITION_Y, input->absbit);
> > +
> > +	data->unique_id = elan_get_unique_id(data);
> > +	data->fw_version = elan_get_fw_version(data);
> > +	data->sm_version = elan_get_sm_version(data);
> > +	data->iap_version = elan_get_iap_version(data);
> > +	data->max_x = elan_get_x_max(data);
> > +	data->max_y = elan_get_y_max(data);
> > +	data->width_x = data->max_x / elan_get_x_tracenum(data);
> > +	data->width_y = data->max_y / elan_get_y_tracenum(data);
> > +	x_res = elan_get_x_resolution(data);
> > +	y_res = elan_get_y_resolution(data);
> > +
> > +	dev_info(&client->dev,
> > +		"Elan Touchpad Information:\n"
> > +		"    Module unique ID:  0x%04x\n"
> > +		"    Firmware Version:  0x%04x\n"
> > +		"    Sample Version:  0x%04x\n"
> > +		"    IAP Version:  0x%04x\n"
> > +		"    Max ABS X,Y:   %d,%d\n"
> > +		"    Width X,Y:   %d,%d\n"
> > +		"    Resolution X,Y:   %d,%d (dots/mm)\n",
> > +		data->unique_id,
> > +		data->fw_version,
> > +		data->sm_version,
> > +		data->iap_version,
> > +		data->max_x, data->max_y,
> > +		data->width_x, data->width_y,
> > +		(char)x_res, (char)y_res);
> 
> Why cast to char?
> 
> > +
> > +	input_set_abs_params(input, ABS_X, 0, data->max_x, 0, 0);
> > +	input_set_abs_params(input, ABS_Y, 0, data->max_y, 0, 0);
> > +	input_abs_set_res(input, ABS_X, x_res);
> > +	input_abs_set_res(input, ABS_Y, y_res);
> > +	input_set_abs_params(input, ABS_PRESSURE, 0, ETP_MAX_PRESSURE,
> 0, 0);
> > +	input_set_abs_params(input, ABS_TOOL_WIDTH, 0, ETP_FINGER_WIDTH,
> 0, 0);
> > +
> > +	/* handle pointer emulation and unused slots in core */
> > +	ret = input_mt_init_slots(input, ETP_MAX_FINGERS,
> > +				  INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
> > +	if (ret) {
> > +		dev_info(&client->dev, "allocate MT slots failed, %d\n", ret);
> > +		goto err_free_device;
> > +	}
> > +	input_set_abs_params(input, ABS_MT_POSITION_X, 0, data->max_x, 0, 0);
> > +	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, data->max_y, 0, 0);
> > +	input_abs_set_res(input, ABS_MT_POSITION_X, x_res);
> > +	input_abs_set_res(input, ABS_MT_POSITION_Y, y_res);
> > +	input_set_abs_params(input, ABS_MT_PRESSURE, 0,
> > +				ETP_MAX_PRESSURE, 0, 0);
> > +	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
> > +		ETP_FINGER_WIDTH * max(data->width_x, data->width_y), 0, 0);
> > +	input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0,
> > +		ETP_FINGER_WIDTH * min(data->width_x, data->width_y), 0, 0);
> > +
> > +	/* Register the device in input subsystem */
> > +	ret = input_register_device(input);
> > +	if (ret) {
> > +		dev_err(&client->dev, "input device register failed, %d\n",
> > +			ret);
> > +		goto err_free_device;
> > +	}
> > +
> > +	return 0;
> > +
> > +err_free_device:
> > +	input_free_device(input);
> > +	return ret;
> > +}
> > +
> > +static u8 elan_check_adapter_functionality(struct i2c_client *client)
> > +{
> > +	u8 ret = ELAN_ADAPTER_FUNC_NONE;
> > +
> > +	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> > +		ret |= ELAN_ADAPTER_FUNC_I2C;
> > +	if (i2c_check_functionality(client->adapter,
> > +					I2C_FUNC_SMBUS_BYTE_DATA |
> > +					I2C_FUNC_SMBUS_BLOCK_DATA |
> > +					I2C_FUNC_SMBUS_I2C_BLOCK))
> > +		ret |= ELAN_ADAPTER_FUNC_SMBUS;
> > +	return ret;
> > +}
> > +
> > +static int elan_probe(struct i2c_client *client,
> > +				    const struct i2c_device_id *dev_id)
> > +{
> > +	struct elan_tp_data *data;
> > +	int ret;
> > +	u8 adapter_func;
> > +	union i2c_smbus_data dummy;
> > +	struct device *dev = &client->dev;
> > +
> > +	adapter_func = elan_check_adapter_functionality(client);
> > +	if (adapter_func == ELAN_ADAPTER_FUNC_NONE) {
> > +		dev_err(dev, "not a supported I2C/SMBus adapter\n");
> > +		return -EIO;
> > +	}
> > +
> > +	/* Make sure there is something at this address */
> > +	if (dev->of_node && i2c_smbus_xfer(client->adapter, client->addr, 0,
> > +			 I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0)
> > +		return -ENODEV;
> > +
> > +	data = kzalloc(sizeof(struct elan_tp_data), GFP_KERNEL);
> > +	if (!data)
> > +		return -ENOMEM;
> > +
> > +	/* check protocol type */
> > +	if (adapter_func == ELAN_ADAPTER_FUNC_SMBUS)
> > +		data->smbus = true;
> > +	else
> > +		data->smbus = false;
> > +	data->client = client;
> > +	data->updated_fw = false;
> > +	data->enable_detail_info = false;
> > +	data->irq = client->irq;
> > +
> > +	ret = elan_dbfs_init(data);
> > +	if (ret < 0) {
> > +		dev_err(&client->dev, "error create elan debugfs.\n");
> > +		goto err_dbfs_init;
> > +	}
> > +	ret = request_threaded_irq(client->irq, NULL, elan_isr,
> > +				  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> > +				  client->name, data);
> > +	if (ret < 0) {
> > +		dev_err(&client->dev, "cannot register irq=%d\n",
> > +							 client->irq);
> > +		goto err_irq;
> > +	}
> > +
> > +	/* initial elan touch pad */
> > +	ret = elan_initialize(data);
> > +	if (ret < 0)
> > +		goto err_init;
> > +
> > +	/* create input device */
> > +	ret = elan_input_dev_create(data);
> > +	if (ret < 0)
> > +		goto err_input_dev;
> > +
> > +	device_init_wakeup(&client->dev, 1);
> > +	ret = sysfs_create_group(&client->dev.kobj, &elan_sysfs_group);
> > +	if (ret < 0) {
> > +		dev_err(&client->dev, "cannot register dev attribute %d", ret);
> > +		goto err_create_group;
> > +	}
> > +	i2c_set_clientdata(client, data);
> > +	return 0;
> > +
> > +err_create_group:
> > +	input_free_device(data->input);
> 
> The device is registered here so you need to call unregister, not free.
> 
> > +err_input_dev:
> > +err_init:
> > +	free_irq(data->irq, data);
> > +err_irq:
> > +	debugfs_remove_recursive(data->dbfs_root);
> > +	mutex_destroy(&data->dbfs_mutex);
> > +err_dbfs_init:
> > +	kfree(data);
> > +	dev_err(&client->dev, "Elan Trackpad probe fail!\n");
> > +	return ret;
> > +}
> > +
> > +static int elan_remove(struct i2c_client *client)
> > +{
> > +	struct elan_tp_data *data = i2c_get_clientdata(client);
> > +
> > +	free_irq(data->irq, data);
> > +	debugfs_remove_recursive(data->dbfs_root);
> > +	mutex_destroy(&data->dbfs_mutex);
> > +
> > +	input_free_device(data->input);
> 
> Not needed (and harmful here).
> 
> > +	input_unregister_device(data->input);
> > +	kfree(data);
> > +	sysfs_remove_group(&client->dev.kobj, &elan_sysfs_group);
> 
> I'd start with removing sysfs attributes as first thing in remove().
> 
> > +
> > +	return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int elan_suspend(struct device *dev)
> > +{
> > +	int ret;
> > +	struct elan_tp_data *data = dev_get_drvdata(dev);
> > +
> > +	disable_irq(data->irq);
> > +	if (data->smbus)
> > +		ret = i2c_smbus_write_byte(data->client,
> > +					ETP_SMBUS_DISABLE_TP);
> > +	else
> > +		ret = elan_i2c_sleep(data->client);
> > +
> > +	if (ret < 0) {
> > +		dev_err(dev, "suspend mode failed, %d\n", ret);
> > +	} else {
> > +		if (device_may_wakeup(dev))
> > +			data->irq_wake = (enable_irq_wake(data->irq) == 0);
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int elan_resume(struct device *dev)
> > +{
> > +	int ret = 0;
> > +	struct elan_tp_data *data = dev_get_drvdata(dev);
> > +
> > +	if (device_may_wakeup(dev) && data->irq_wake)
> > +		disable_irq_wake(data->irq);
> > +
> > +	ret = elan_initialize(data);
> > +	if (ret < 0)
> > +		dev_err(dev, "resume active power failed, %d\n", ret);
> > +
> > +	enable_irq(data->irq);
> > +	return 0;
> > +}
> > +#endif
> > +
> > +static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);
> > +
> > +static const struct i2c_device_id elan_id[] = {
> > +	{ DRIVER_NAME, 0 },
> > +	{ },
> > +};
> > +MODULE_DEVICE_TABLE(i2c, elan_id);
> > +
> > +static struct i2c_driver elan_driver = {
> > +	.driver = {
> > +		.name	= DRIVER_NAME,
> > +		.owner	= THIS_MODULE,
> > +		.pm	= &elan_pm_ops,
> > +	},
> > +	.probe		= elan_probe,
> > +	.remove		= elan_remove,
> > +	.id_table	= elan_id,
> > +};
> > +
> > +
> > +static int __init elan_init(void)
> > +{
> > +	int ret;
> > +	ret = i2c_add_driver(&elan_driver);
> > +	if (ret) {
> > +		pr_err("elan driver register FAILED.\n");
> > +		return ret;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static void __exit elan_exit(void)
> > +{
> > +	i2c_del_driver(&elan_driver);
> > +}
> > +
> > +module_init(elan_init);
> > +module_exit(elan_exit);
> 
> module_i2c_driver().
> 
> > +
> > +MODULE_AUTHOR("Duson Lin <dusonlin@emc.com.tw>");
> > +MODULE_DESCRIPTION("Elan I2C/SMBus Touchpad driver");
> > +MODULE_LICENSE("GPL");
> > --
> > 1.7.10.4
> >
> 
> Thanks.
> 
> --
> Dmitry

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [Xen-devel] [PATCH v3 1/2] xen/pvhvm: If xen_platform_pci=0 is set don't blow up (v3).
From: Fabio Fantoni @ 2013-12-17  9:54 UTC (permalink / raw)
  To: Konrad Rzeszutek Wilk, axboe, stefano.stabellini, ian.campbell,
	xen-devel, linux-kernel, boris.ostrovsky, david.vrabel, leosilva,
	ashley, peterhuewe, mail, tpmdd, dmitry.torokhov, bhelgaas,
	plagnioj, tomi.valkeinen, tpmdd-devel, linux-input, netdev,
	linux-pci, linux-fbdev
In-Reply-To: <1387206250-13963-2-git-send-email-konrad.wilk@oracle.com>

Il 16/12/2013 16:04, Konrad Rzeszutek Wilk ha scritto:
> The user has the option of disabling the platform driver:
> 00:02.0 Unassigned class [ff80]: XenSource, Inc. Xen Platform Device (rev 01)
>
> which is used to unplug the emulated drivers (IDE, Realtek 8169, etc)
> and allow the PV drivers to take over. If the user wishes
> to disable that they can set:
>
>    xen_platform_pci=0
>    (in the guest config file)
>
> or
>    xen_emul_unplug=never
>    (on the Linux command line)
>
> except it does not work properly. The PV drivers still try to
> load and since the Xen platform driver is not run - and it
> has not initialized the grant tables, most of the PV drivers
> stumble upon:
>
> input: Xen Virtual Keyboard as /devices/virtual/input/input5
> input: Xen Virtual Pointer as /devices/virtual/input/input6M
> ------------[ cut here ]------------
> kernel BUG at /home/konrad/ssd/konrad/linux/drivers/xen/grant-table.c:1206!
> invalid opcode: 0000 [#1] SMP
> Modules linked in: xen_kbdfront(+) xenfs xen_privcmd
> CPU: 6 PID: 1389 Comm: modprobe Not tainted 3.13.0-rc1upstream-00021-ga6c892b-dirty #1
> Hardware name: Xen HVM domU, BIOS 4.4-unstable 11/26/2013
> RIP: 0010:[<ffffffff813ddc40>]  [<ffffffff813ddc40>] get_free_entries+0x2e0/0x300
> Call Trace:
>   [<ffffffff8150d9a3>] ? evdev_connect+0x1e3/0x240
>   [<ffffffff813ddd0e>] gnttab_grant_foreign_access+0x2e/0x70
>   [<ffffffffa0010081>] xenkbd_connect_backend+0x41/0x290 [xen_kbdfront]
>   [<ffffffffa0010a12>] xenkbd_probe+0x2f2/0x324 [xen_kbdfront]
>   [<ffffffff813e5757>] xenbus_dev_probe+0x77/0x130
>   [<ffffffff813e7217>] xenbus_frontend_dev_probe+0x47/0x50
>   [<ffffffff8145e9a9>] driver_probe_device+0x89/0x230
>   [<ffffffff8145ebeb>] __driver_attach+0x9b/0xa0
>   [<ffffffff8145eb50>] ? driver_probe_device+0x230/0x230
>   [<ffffffff8145eb50>] ? driver_probe_device+0x230/0x230
>   [<ffffffff8145cf1c>] bus_for_each_dev+0x8c/0xb0
>   [<ffffffff8145e7d9>] driver_attach+0x19/0x20
>   [<ffffffff8145e260>] bus_add_driver+0x1a0/0x220
>   [<ffffffff8145f1ff>] driver_register+0x5f/0xf0
>   [<ffffffff813e55c5>] xenbus_register_driver_common+0x15/0x20
>   [<ffffffff813e76b3>] xenbus_register_frontend+0x23/0x40
>   [<ffffffffa0015000>] ? 0xffffffffa0014fff
>   [<ffffffffa001502b>] xenkbd_init+0x2b/0x1000 [xen_kbdfront]
>   [<ffffffff81002049>] do_one_initcall+0x49/0x170
>
> .. snip..
>
> which is hardly nice. This patch fixes this by having each
> PV driver check for:
>   - if running in PV, then it is fine to execute (as that is their
>     native environment).
>   - if running in HVM, check if user wanted 'xen_emul_unplug=never',
>     in which case bail out and don't load any PV drivers.
>   - if running in HVM, and if PCI device 5853:0001 (xen_platform_pci)
>     does not exist, then bail out and not load PV drivers.
>   - (v2) if running in HVM, and if the user wanted 'xen_emul_unplug=disks',
>     then bail out for all PV devices _except_ the block one.
>     Ditto for the network one ('nics').
>   - (v2) if running in HVM, and if the user wanted 'xen_emul_unplug=unnecessary'
>     then load block PV driver, and also setup the legacy IDE paths.
>     In (v3) make it actually load PV drivers.
>
> Reported-by: Sander Eikelenboom <linux@eikelenboom.it
> Reported-by: Anthony PERARD <anthony.perard@citrix.com>
> Reported-by: Fabio Fantoni <fabio.fantoni@m2r.biz>
> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
> [v2: Add extra logic to handle the myrid ways 'xen_emul_unplug'
> can be used per Ian and Stefano suggestion]
> [v3: Make the unnecessary case work properly]
> Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>

I tested this patch with all possible cases that I know, no crash or 
calltrace found.

I don't understand the utility of 'xen_emul_unplug=unnecessary' but 
probably it is working correctly, it shows both pv and not pv for blocks 
and nics.

About 'xen_emul_unplug=disks' and 'xen_emul_unplug=nics' probably there 
is something wrong.
With 'xen_emul_unplug=nics' it shows pv nic (this should be correct) and 
about disks it shows the same disk twice, as pv and not pv (xvda and 
sda) with different device number.
With 'xen_emul_unplug=disks' it shows pv block (this should be correct) 
and it seems to show the pv nic too (this should be wrong).

Thanks for any reply.

> ---
>   arch/x86/xen/platform-pci-unplug.c         | 74 ++++++++++++++++++++++++++++++
>   drivers/block/xen-blkfront.c               |  4 +-
>   drivers/char/tpm/xen-tpmfront.c            |  4 ++
>   drivers/input/misc/xen-kbdfront.c          |  4 ++
>   drivers/net/xen-netfront.c                 |  2 +-
>   drivers/pci/xen-pcifront.c                 |  4 ++
>   drivers/video/xen-fbfront.c                |  4 ++
>   drivers/xen/xenbus/xenbus_probe_frontend.c |  2 +-
>   include/xen/platform_pci.h                 | 23 ++++++++++
>   9 files changed, 117 insertions(+), 4 deletions(-)
>
> diff --git a/arch/x86/xen/platform-pci-unplug.c b/arch/x86/xen/platform-pci-unplug.c
> index 0a78524..ab84ac1 100644
> --- a/arch/x86/xen/platform-pci-unplug.c
> +++ b/arch/x86/xen/platform-pci-unplug.c
> @@ -69,6 +69,80 @@ static int check_platform_magic(void)
>   	return 0;
>   }
>   
> +bool xen_has_pv_devices()
> +{
> +	if (!xen_domain())
> +		return false;
> +
> +	/* PV domains always have them. */
> +	if (xen_pv_domain())
> +		return true;
> +
> +	/* And user has xen_platform_pci=0 set in guest config as
> +	 * driver did not modify the value. */
> +	if (xen_platform_pci_unplug == 0)
> +		return false;
> +
> +	if (xen_platform_pci_unplug & XEN_UNPLUG_NEVER)
> +		return false;
> +
> +	if (xen_platform_pci_unplug & XEN_UNPLUG_ALL)
> +		return true;
> +
> +	/* This is an odd one - we are going to run legacy
> +	 * and PV drivers at the same time. */
> +	if (xen_platform_pci_unplug & XEN_UNPLUG_UNNECESSARY)
> +		return true;
> +
> +	/* And the caller has to follow with xen_pv_{disk,nic}_devices
> +	 * to be certain which driver can load. */
> +	return false;
> +}
> +EXPORT_SYMBOL_GPL(xen_has_pv_devices);
> +
> +static bool __xen_has_pv_device(int state)
> +{
> +	/* HVM domains might or might not */
> +	if (xen_hvm_domain() && (xen_platform_pci_unplug & state))
> +		return true;
> +
> +	return xen_has_pv_devices();
> +}
> +
> +bool xen_has_pv_nic_devices(void)
> +{
> +	return __xen_has_pv_device(XEN_UNPLUG_ALL_NICS | XEN_UNPLUG_ALL);
> +}
> +EXPORT_SYMBOL_GPL(xen_has_pv_nic_devices);
> +
> +bool xen_has_pv_disk_devices(void)
> +{
> +	return __xen_has_pv_device(XEN_UNPLUG_ALL_IDE_DISKS |
> +				   XEN_UNPLUG_AUX_IDE_DISKS | XEN_UNPLUG_ALL);
> +}
> +EXPORT_SYMBOL_GPL(xen_has_pv_disk_devices);
> +
> +/*
> + * This one is odd - it determines whether you want to run PV _and_
> + * legacy (IDE) drivers together. This combination is only possible
> + * under HVM.
> + */
> +bool xen_has_pv_and_legacy_disk_devices(void)
> +{
> +	if (!xen_domain())
> +		return false;
> +
> +	/* N.B. This is only ever used in HVM mode */
> +	if (xen_pv_domain())
> +		return false;
> +
> +	if (xen_platform_pci_unplug & XEN_UNPLUG_UNNECESSARY)
> +		return true;
> +
> +	return false;
> +}
> +EXPORT_SYMBOL_GPL(xen_has_pv_and_legacy_disk_devices);
> +
>   void xen_unplug_emulated_devices(void)
>   {
>   	int r;
> diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
> index a4660bb..ed88b3c 100644
> --- a/drivers/block/xen-blkfront.c
> +++ b/drivers/block/xen-blkfront.c
> @@ -1278,7 +1278,7 @@ static int blkfront_probe(struct xenbus_device *dev,
>   		char *type;
>   		int len;
>   		/* no unplug has been done: do not hook devices != xen vbds */
> -		if (xen_platform_pci_unplug & XEN_UNPLUG_UNNECESSARY) {
> +		if (xen_has_pv_and_legacy_disk_devices()) {
>   			int major;
>   
>   			if (!VDEV_IS_EXTENDED(vdevice))
> @@ -2022,7 +2022,7 @@ static int __init xlblk_init(void)
>   	if (!xen_domain())
>   		return -ENODEV;
>   
> -	if (xen_hvm_domain() && !xen_platform_pci_unplug)
> +	if (!xen_has_pv_disk_devices())
>   		return -ENODEV;
>   
>   	if (register_blkdev(XENVBD_MAJOR, DEV_NAME)) {
> diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c
> index 06189e5..9c2cbd1 100644
> --- a/drivers/char/tpm/xen-tpmfront.c
> +++ b/drivers/char/tpm/xen-tpmfront.c
> @@ -16,6 +16,7 @@
>   #include <xen/xenbus.h>
>   #include <xen/page.h>
>   #include "tpm.h"
> +#include <xen/platform_pci.h>
>   
>   struct tpm_private {
>   	struct tpm_chip *chip;
> @@ -422,6 +423,9 @@ static int __init xen_tpmfront_init(void)
>   	if (!xen_domain())
>   		return -ENODEV;
>   
> +	if (!xen_has_pv_devices())
> +		return -ENODEV;
> +
>   	return xenbus_register_frontend(&tpmfront_driver);
>   }
>   module_init(xen_tpmfront_init);
> diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
> index e21c181..fbfdc10 100644
> --- a/drivers/input/misc/xen-kbdfront.c
> +++ b/drivers/input/misc/xen-kbdfront.c
> @@ -29,6 +29,7 @@
>   #include <xen/interface/io/fbif.h>
>   #include <xen/interface/io/kbdif.h>
>   #include <xen/xenbus.h>
> +#include <xen/platform_pci.h>
>   
>   struct xenkbd_info {
>   	struct input_dev *kbd;
> @@ -380,6 +381,9 @@ static int __init xenkbd_init(void)
>   	if (xen_initial_domain())
>   		return -ENODEV;
>   
> +	if (!xen_has_pv_devices())
> +		return -ENODEV;
> +
>   	return xenbus_register_frontend(&xenkbd_driver);
>   }
>   
> diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
> index 36808bf..eea2392 100644
> --- a/drivers/net/xen-netfront.c
> +++ b/drivers/net/xen-netfront.c
> @@ -2106,7 +2106,7 @@ static int __init netif_init(void)
>   	if (!xen_domain())
>   		return -ENODEV;
>   
> -	if (xen_hvm_domain() && !xen_platform_pci_unplug)
> +	if (!xen_has_pv_nic_devices())
>   		return -ENODEV;
>   
>   	pr_info("Initialising Xen virtual ethernet driver\n");
> diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
> index f7197a7..eae7cd9 100644
> --- a/drivers/pci/xen-pcifront.c
> +++ b/drivers/pci/xen-pcifront.c
> @@ -20,6 +20,7 @@
>   #include <linux/workqueue.h>
>   #include <linux/bitops.h>
>   #include <linux/time.h>
> +#include <xen/platform_pci.h>
>   
>   #include <asm/xen/swiotlb-xen.h>
>   #define INVALID_GRANT_REF (0)
> @@ -1138,6 +1139,9 @@ static int __init pcifront_init(void)
>   	if (!xen_pv_domain() || xen_initial_domain())
>   		return -ENODEV;
>   
> +	if (!xen_has_pv_devices())
> +		return -ENODEV;
> +
>   	pci_frontend_registrar(1 /* enable */);
>   
>   	return xenbus_register_frontend(&xenpci_driver);
> diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
> index cd005c2..4b2d3ab 100644
> --- a/drivers/video/xen-fbfront.c
> +++ b/drivers/video/xen-fbfront.c
> @@ -35,6 +35,7 @@
>   #include <xen/interface/io/fbif.h>
>   #include <xen/interface/io/protocols.h>
>   #include <xen/xenbus.h>
> +#include <xen/platform_pci.h>
>   
>   struct xenfb_info {
>   	unsigned char		*fb;
> @@ -699,6 +700,9 @@ static int __init xenfb_init(void)
>   	if (xen_initial_domain())
>   		return -ENODEV;
>   
> +	if (!xen_has_pv_devices())
> +		return -ENODEV;
> +
>   	return xenbus_register_frontend(&xenfb_driver);
>   }
>   
> diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c
> index 34b20bf..6244f9c 100644
> --- a/drivers/xen/xenbus/xenbus_probe_frontend.c
> +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c
> @@ -496,7 +496,7 @@ subsys_initcall(xenbus_probe_frontend_init);
>   #ifndef MODULE
>   static int __init boot_wait_for_devices(void)
>   {
> -	if (xen_hvm_domain() && !xen_platform_pci_unplug)
> +	if (!xen_has_pv_devices())
>   		return -ENODEV;
>   
>   	ready_to_wait_for_devices = 1;
> diff --git a/include/xen/platform_pci.h b/include/xen/platform_pci.h
> index 438c256..b49eeab 100644
> --- a/include/xen/platform_pci.h
> +++ b/include/xen/platform_pci.h
> @@ -48,4 +48,27 @@ static inline int xen_must_unplug_disks(void) {
>   
>   extern int xen_platform_pci_unplug;
>   
> +#if defined(CONFIG_XEN_PVHVM)
> +extern bool xen_has_pv_devices(void);
> +extern bool xen_has_pv_disk_devices(void);
> +extern bool xen_has_pv_nic_devices(void);
> +extern bool xen_has_pv_and_legacy_disk_devices(void);
> +#else
> +static inline bool xen_has_pv_devices(void)
> +{
> +	return IS_ENABLED(CONFIG_XEN);
> +}
> +static inline bool xen_has_pv_disk_devices(void)
> +{
> +	return IS_ENABLED(CONFIG_XEN);
> +}
> +static inline bool xen_has_pv_nic_devices(void)
> +{
> +	return IS_ENABLED(CONFIG_XEN);
> +}
> +static inline bool xen_has_pv_and_legacy_disk_devices(void)
> +{
> +	return false;
> +}
> +#endif
>   #endif /* _XEN_PLATFORM_PCI_H */

^ permalink raw reply

* LG 23ET83V-W multitouch monitor
From: Randolf Rotta @ 2013-12-17 10:35 UTC (permalink / raw)
  To: linux-input

Hello,

I got a LG 23ET83V multitouch monitor and needed driver support for 
that. I got it working with

options usbhid quirks=0x0457:0x1030:0x20000000

in /etc/modprobe.d/usbhid.conf

and

echo 3 0457 1030 18 
/sys/module/hid_multitouch/drivers/hid\:hid-multitouch/new_id


Just saw, that there is a patch for this monitor now from Emanuel Krenz 
and Jiri Kosina. So maybe, my experiments do not provide new information.

Randolf

^ permalink raw reply

* [PATCHv2] Input: fix typos in Documentation/input/gamepad.txt
From: Antonio Ospite @ 2013-12-17 11:33 UTC (permalink / raw)
  To: linux-input
  Cc: Antonio Ospite, Randy Dunlap, David Herrmann, Dmitry Torokhov,
	Jiri Kosina
In-Reply-To: <52AF51EA.8090400@infradead.org>

Fix some typos and while at it also use "PS" as the name for the central
"HOME" button on Sony controllers, this is how Sony itself calls it.

Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Antonio Ospite <ospite@studenti.unina.it>
---

Changes since v1:
  - Fix one of the fixes: use "advise" (verb) in place of "advice" (noun)

Thanks Randy.

 Documentation/input/gamepad.txt | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Documentation/input/gamepad.txt b/Documentation/input/gamepad.txt
index 31bb6a4..3f6d8a5 100644
--- a/Documentation/input/gamepad.txt
+++ b/Documentation/input/gamepad.txt
@@ -68,7 +68,7 @@ features that you need, first. How each feature is mapped is described below.
 Legacy drivers often don't comply to these rules. As we cannot change them
 for backwards-compatibility reasons, you need to provide fixup mappings in
 user-space yourself. Some of them might also provide module-options that
-change the mappings so you can adivce users to set these.
+change the mappings so you can advise users to set these.
 
 All new gamepads are supposed to comply with this mapping. Please report any
 bugs, if they don't.
@@ -150,10 +150,10 @@ Menu-Pad:
                   BTN_START
   Many pads also have a third button which is branded or has a special symbol
   and meaning. Such buttons are mapped as BTN_MODE. Examples are the Nintendo
-  "HOME" button, the XBox "X"-button or Sony "P" button.
+  "HOME" button, the XBox "X"-button or Sony "PS" button.
 
 Rumble:
-  Rumble is adverticed as FF_RUMBLE.
+  Rumble is advertised as FF_RUMBLE.
 
 ----------------------------------------------------------------------------
   Written 2013 by David Herrmann <dh.herrmann@gmail.com>
-- 
1.8.5.1


^ permalink raw reply related

* [PATCH] HID: debug: add labels for some new buttons
From: Antonio Ospite @ 2013-12-17 11:52 UTC (permalink / raw)
  To: linux-input; +Cc: Antonio Ospite, Jiri Kosina

Signed-off-by: Antonio Ospite <ospite@studenti.unina.it>
---

I also noticed that BTN_GEAR_DOWN is named "WheelBtn", should this be fixed?

Thanks,
   Antonio

 drivers/hid/hid-debug.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 8453214..53b771d 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -768,6 +768,8 @@ static const char *keys[KEY_MAX + 1] = {
 	[KEY_ALTERASE] = "AlternateErase",	[KEY_CANCEL] = "Cancel",
 	[KEY_BRIGHTNESSDOWN] = "BrightnessDown", [KEY_BRIGHTNESSUP] = "BrightnessUp",
 	[KEY_MEDIA] = "Media",			[KEY_UNKNOWN] = "Unknown",
+	[BTN_DPAD_UP] = "BtnDPadUp",		[BTN_DPAD_DOWN] = "BtnDPadDown",
+	[BTN_DPAD_LEFT] = "BtnDPadLeft",	[BTN_DPAD_RIGHT] = "BtnDPadRight",
 	[BTN_0] = "Btn0",			[BTN_1] = "Btn1",
 	[BTN_2] = "Btn2",			[BTN_3] = "Btn3",
 	[BTN_4] = "Btn4",			[BTN_5] = "Btn5",
@@ -797,7 +799,8 @@ static const char *keys[KEY_MAX + 1] = {
 	[BTN_TOOL_MOUSE] = "ToolMouse",		[BTN_TOOL_LENS] = "ToolLens",
 	[BTN_TOUCH] = "Touch",			[BTN_STYLUS] = "Stylus",
 	[BTN_STYLUS2] = "Stylus2",		[BTN_TOOL_DOUBLETAP] = "ToolDoubleTap",
-	[BTN_TOOL_TRIPLETAP] = "ToolTripleTap", [BTN_GEAR_DOWN] = "WheelBtn",
+	[BTN_TOOL_TRIPLETAP] = "ToolTripleTap",	[BTN_TOOL_QUADTAP] = "ToolQuadrupleTap",
+	[BTN_GEAR_DOWN] = "WheelBtn",
 	[BTN_GEAR_UP] = "Gear up",		[KEY_OK] = "Ok",
 	[KEY_SELECT] = "Select",		[KEY_GOTO] = "Goto",
 	[KEY_CLEAR] = "Clear",			[KEY_POWER2] = "Power2",
-- 
1.8.5.1


^ permalink raw reply related

* Re: [PATCH] HID: debug: add labels for some new buttons
From: Jiri Kosina @ 2013-12-17 12:56 UTC (permalink / raw)
  To: Antonio Ospite; +Cc: linux-input
In-Reply-To: <1387281168-13856-1-git-send-email-ospite@studenti.unina.it>

On Tue, 17 Dec 2013, Antonio Ospite wrote:

> Signed-off-by: Antonio Ospite <ospite@studenti.unina.it>

I have added some changelog and applied, thanks.

> ---
> 
> I also noticed that BTN_GEAR_DOWN is named "WheelBtn", should this be fixed?

	#define BTN_WHEEL		0x150
	#define BTN_GEAR_DOWN		0x150
	#define BTN_GEAR_UP		0x151

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply

* Re: [Xen-devel] [PATCH v3 1/2] xen/pvhvm: If xen_platform_pci=0 is set don't blow up (v3).
From: Konrad Rzeszutek Wilk @ 2013-12-17 14:51 UTC (permalink / raw)
  To: Fabio Fantoni
  Cc: axboe, stefano.stabellini, ian.campbell, xen-devel, linux-kernel,
	boris.ostrovsky, david.vrabel, leosilva, ashley, peterhuewe, mail,
	tpmdd, dmitry.torokhov, bhelgaas, plagnioj, tomi.valkeinen,
	tpmdd-devel, linux-input, netdev, linux-pci, linux-fbdev
In-Reply-To: <52B01F67.1030108@m2r.biz>

On Tue, Dec 17, 2013 at 10:54:47AM +0100, Fabio Fantoni wrote:
> Il 16/12/2013 16:04, Konrad Rzeszutek Wilk ha scritto:
> >The user has the option of disabling the platform driver:
> >00:02.0 Unassigned class [ff80]: XenSource, Inc. Xen Platform Device (rev 01)
> >
> >which is used to unplug the emulated drivers (IDE, Realtek 8169, etc)
> >and allow the PV drivers to take over. If the user wishes
> >to disable that they can set:
> >
> >   xen_platform_pci=0
> >   (in the guest config file)
> >
> >or
> >   xen_emul_unplug=never
> >   (on the Linux command line)
> >
> >except it does not work properly. The PV drivers still try to
> >load and since the Xen platform driver is not run - and it
> >has not initialized the grant tables, most of the PV drivers
> >stumble upon:
> >
> >input: Xen Virtual Keyboard as /devices/virtual/input/input5
> >input: Xen Virtual Pointer as /devices/virtual/input/input6M
> >------------[ cut here ]------------
> >kernel BUG at /home/konrad/ssd/konrad/linux/drivers/xen/grant-table.c:1206!
> >invalid opcode: 0000 [#1] SMP
> >Modules linked in: xen_kbdfront(+) xenfs xen_privcmd
> >CPU: 6 PID: 1389 Comm: modprobe Not tainted 3.13.0-rc1upstream-00021-ga6c892b-dirty #1
> >Hardware name: Xen HVM domU, BIOS 4.4-unstable 11/26/2013
> >RIP: 0010:[<ffffffff813ddc40>]  [<ffffffff813ddc40>] get_free_entries+0x2e0/0x300
> >Call Trace:
> >  [<ffffffff8150d9a3>] ? evdev_connect+0x1e3/0x240
> >  [<ffffffff813ddd0e>] gnttab_grant_foreign_access+0x2e/0x70
> >  [<ffffffffa0010081>] xenkbd_connect_backend+0x41/0x290 [xen_kbdfront]
> >  [<ffffffffa0010a12>] xenkbd_probe+0x2f2/0x324 [xen_kbdfront]
> >  [<ffffffff813e5757>] xenbus_dev_probe+0x77/0x130
> >  [<ffffffff813e7217>] xenbus_frontend_dev_probe+0x47/0x50
> >  [<ffffffff8145e9a9>] driver_probe_device+0x89/0x230
> >  [<ffffffff8145ebeb>] __driver_attach+0x9b/0xa0
> >  [<ffffffff8145eb50>] ? driver_probe_device+0x230/0x230
> >  [<ffffffff8145eb50>] ? driver_probe_device+0x230/0x230
> >  [<ffffffff8145cf1c>] bus_for_each_dev+0x8c/0xb0
> >  [<ffffffff8145e7d9>] driver_attach+0x19/0x20
> >  [<ffffffff8145e260>] bus_add_driver+0x1a0/0x220
> >  [<ffffffff8145f1ff>] driver_register+0x5f/0xf0
> >  [<ffffffff813e55c5>] xenbus_register_driver_common+0x15/0x20
> >  [<ffffffff813e76b3>] xenbus_register_frontend+0x23/0x40
> >  [<ffffffffa0015000>] ? 0xffffffffa0014fff
> >  [<ffffffffa001502b>] xenkbd_init+0x2b/0x1000 [xen_kbdfront]
> >  [<ffffffff81002049>] do_one_initcall+0x49/0x170
> >
> >.. snip..
> >
> >which is hardly nice. This patch fixes this by having each
> >PV driver check for:
> >  - if running in PV, then it is fine to execute (as that is their
> >    native environment).
> >  - if running in HVM, check if user wanted 'xen_emul_unplug=never',
> >    in which case bail out and don't load any PV drivers.
> >  - if running in HVM, and if PCI device 5853:0001 (xen_platform_pci)
> >    does not exist, then bail out and not load PV drivers.
> >  - (v2) if running in HVM, and if the user wanted 'xen_emul_unplug=disks',
> >    then bail out for all PV devices _except_ the block one.
> >    Ditto for the network one ('nics').
> >  - (v2) if running in HVM, and if the user wanted 'xen_emul_unplug=unnecessary'
> >    then load block PV driver, and also setup the legacy IDE paths.
> >    In (v3) make it actually load PV drivers.
> >
> >Reported-by: Sander Eikelenboom <linux@eikelenboom.it
> >Reported-by: Anthony PERARD <anthony.perard@citrix.com>
> >Reported-by: Fabio Fantoni <fabio.fantoni@m2r.biz>
> >Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
> >[v2: Add extra logic to handle the myrid ways 'xen_emul_unplug'
> >can be used per Ian and Stefano suggestion]
> >[v3: Make the unnecessary case work properly]
> >Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
> 
> I tested this patch with all possible cases that I know, no crash or
> calltrace found.
> 
> I don't understand the utility of 'xen_emul_unplug=unnecessary' but
> probably it is working correctly, it shows both pv and not pv for
> blocks and nics.

Great!
> 
> About 'xen_emul_unplug=disks' and 'xen_emul_unplug=nics' probably
> there is something wrong.
> With 'xen_emul_unplug=nics' it shows pv nic (this should be correct)
> and about disks it shows the same disk twice, as pv and not pv (xvda
> and sda) with different device number.

Good.
> With 'xen_emul_unplug=disks' it shows pv block (this should be
> correct) and it seems to show the pv nic too (this should be wrong).

Correct. You should see only the PV disk and all other PV devices
should not function. Let me dig in this and see whether there is
another bug. Thank you!

> 
> Thanks for any reply.
> 
> >---
> >  arch/x86/xen/platform-pci-unplug.c         | 74 ++++++++++++++++++++++++++++++
> >  drivers/block/xen-blkfront.c               |  4 +-
> >  drivers/char/tpm/xen-tpmfront.c            |  4 ++
> >  drivers/input/misc/xen-kbdfront.c          |  4 ++
> >  drivers/net/xen-netfront.c                 |  2 +-
> >  drivers/pci/xen-pcifront.c                 |  4 ++
> >  drivers/video/xen-fbfront.c                |  4 ++
> >  drivers/xen/xenbus/xenbus_probe_frontend.c |  2 +-
> >  include/xen/platform_pci.h                 | 23 ++++++++++
> >  9 files changed, 117 insertions(+), 4 deletions(-)
> >
> >diff --git a/arch/x86/xen/platform-pci-unplug.c b/arch/x86/xen/platform-pci-unplug.c
> >index 0a78524..ab84ac1 100644
> >--- a/arch/x86/xen/platform-pci-unplug.c
> >+++ b/arch/x86/xen/platform-pci-unplug.c
> >@@ -69,6 +69,80 @@ static int check_platform_magic(void)
> >  	return 0;
> >  }
> >+bool xen_has_pv_devices()
> >+{
> >+	if (!xen_domain())
> >+		return false;
> >+
> >+	/* PV domains always have them. */
> >+	if (xen_pv_domain())
> >+		return true;
> >+
> >+	/* And user has xen_platform_pci=0 set in guest config as
> >+	 * driver did not modify the value. */
> >+	if (xen_platform_pci_unplug == 0)
> >+		return false;
> >+
> >+	if (xen_platform_pci_unplug & XEN_UNPLUG_NEVER)
> >+		return false;
> >+
> >+	if (xen_platform_pci_unplug & XEN_UNPLUG_ALL)
> >+		return true;
> >+
> >+	/* This is an odd one - we are going to run legacy
> >+	 * and PV drivers at the same time. */
> >+	if (xen_platform_pci_unplug & XEN_UNPLUG_UNNECESSARY)
> >+		return true;
> >+
> >+	/* And the caller has to follow with xen_pv_{disk,nic}_devices
> >+	 * to be certain which driver can load. */
> >+	return false;
> >+}
> >+EXPORT_SYMBOL_GPL(xen_has_pv_devices);
> >+
> >+static bool __xen_has_pv_device(int state)
> >+{
> >+	/* HVM domains might or might not */
> >+	if (xen_hvm_domain() && (xen_platform_pci_unplug & state))
> >+		return true;
> >+
> >+	return xen_has_pv_devices();
> >+}
> >+
> >+bool xen_has_pv_nic_devices(void)
> >+{
> >+	return __xen_has_pv_device(XEN_UNPLUG_ALL_NICS | XEN_UNPLUG_ALL);
> >+}
> >+EXPORT_SYMBOL_GPL(xen_has_pv_nic_devices);
> >+
> >+bool xen_has_pv_disk_devices(void)
> >+{
> >+	return __xen_has_pv_device(XEN_UNPLUG_ALL_IDE_DISKS |
> >+				   XEN_UNPLUG_AUX_IDE_DISKS | XEN_UNPLUG_ALL);
> >+}
> >+EXPORT_SYMBOL_GPL(xen_has_pv_disk_devices);
> >+
> >+/*
> >+ * This one is odd - it determines whether you want to run PV _and_
> >+ * legacy (IDE) drivers together. This combination is only possible
> >+ * under HVM.
> >+ */
> >+bool xen_has_pv_and_legacy_disk_devices(void)
> >+{
> >+	if (!xen_domain())
> >+		return false;
> >+
> >+	/* N.B. This is only ever used in HVM mode */
> >+	if (xen_pv_domain())
> >+		return false;
> >+
> >+	if (xen_platform_pci_unplug & XEN_UNPLUG_UNNECESSARY)
> >+		return true;
> >+
> >+	return false;
> >+}
> >+EXPORT_SYMBOL_GPL(xen_has_pv_and_legacy_disk_devices);
> >+
> >  void xen_unplug_emulated_devices(void)
> >  {
> >  	int r;
> >diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
> >index a4660bb..ed88b3c 100644
> >--- a/drivers/block/xen-blkfront.c
> >+++ b/drivers/block/xen-blkfront.c
> >@@ -1278,7 +1278,7 @@ static int blkfront_probe(struct xenbus_device *dev,
> >  		char *type;
> >  		int len;
> >  		/* no unplug has been done: do not hook devices != xen vbds */
> >-		if (xen_platform_pci_unplug & XEN_UNPLUG_UNNECESSARY) {
> >+		if (xen_has_pv_and_legacy_disk_devices()) {
> >  			int major;
> >  			if (!VDEV_IS_EXTENDED(vdevice))
> >@@ -2022,7 +2022,7 @@ static int __init xlblk_init(void)
> >  	if (!xen_domain())
> >  		return -ENODEV;
> >-	if (xen_hvm_domain() && !xen_platform_pci_unplug)
> >+	if (!xen_has_pv_disk_devices())
> >  		return -ENODEV;
> >  	if (register_blkdev(XENVBD_MAJOR, DEV_NAME)) {
> >diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c
> >index 06189e5..9c2cbd1 100644
> >--- a/drivers/char/tpm/xen-tpmfront.c
> >+++ b/drivers/char/tpm/xen-tpmfront.c
> >@@ -16,6 +16,7 @@
> >  #include <xen/xenbus.h>
> >  #include <xen/page.h>
> >  #include "tpm.h"
> >+#include <xen/platform_pci.h>
> >  struct tpm_private {
> >  	struct tpm_chip *chip;
> >@@ -422,6 +423,9 @@ static int __init xen_tpmfront_init(void)
> >  	if (!xen_domain())
> >  		return -ENODEV;
> >+	if (!xen_has_pv_devices())
> >+		return -ENODEV;
> >+
> >  	return xenbus_register_frontend(&tpmfront_driver);
> >  }
> >  module_init(xen_tpmfront_init);
> >diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
> >index e21c181..fbfdc10 100644
> >--- a/drivers/input/misc/xen-kbdfront.c
> >+++ b/drivers/input/misc/xen-kbdfront.c
> >@@ -29,6 +29,7 @@
> >  #include <xen/interface/io/fbif.h>
> >  #include <xen/interface/io/kbdif.h>
> >  #include <xen/xenbus.h>
> >+#include <xen/platform_pci.h>
> >  struct xenkbd_info {
> >  	struct input_dev *kbd;
> >@@ -380,6 +381,9 @@ static int __init xenkbd_init(void)
> >  	if (xen_initial_domain())
> >  		return -ENODEV;
> >+	if (!xen_has_pv_devices())
> >+		return -ENODEV;
> >+
> >  	return xenbus_register_frontend(&xenkbd_driver);
> >  }
> >diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
> >index 36808bf..eea2392 100644
> >--- a/drivers/net/xen-netfront.c
> >+++ b/drivers/net/xen-netfront.c
> >@@ -2106,7 +2106,7 @@ static int __init netif_init(void)
> >  	if (!xen_domain())
> >  		return -ENODEV;
> >-	if (xen_hvm_domain() && !xen_platform_pci_unplug)
> >+	if (!xen_has_pv_nic_devices())
> >  		return -ENODEV;
> >  	pr_info("Initialising Xen virtual ethernet driver\n");
> >diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
> >index f7197a7..eae7cd9 100644
> >--- a/drivers/pci/xen-pcifront.c
> >+++ b/drivers/pci/xen-pcifront.c
> >@@ -20,6 +20,7 @@
> >  #include <linux/workqueue.h>
> >  #include <linux/bitops.h>
> >  #include <linux/time.h>
> >+#include <xen/platform_pci.h>
> >  #include <asm/xen/swiotlb-xen.h>
> >  #define INVALID_GRANT_REF (0)
> >@@ -1138,6 +1139,9 @@ static int __init pcifront_init(void)
> >  	if (!xen_pv_domain() || xen_initial_domain())
> >  		return -ENODEV;
> >+	if (!xen_has_pv_devices())
> >+		return -ENODEV;
> >+
> >  	pci_frontend_registrar(1 /* enable */);
> >  	return xenbus_register_frontend(&xenpci_driver);
> >diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
> >index cd005c2..4b2d3ab 100644
> >--- a/drivers/video/xen-fbfront.c
> >+++ b/drivers/video/xen-fbfront.c
> >@@ -35,6 +35,7 @@
> >  #include <xen/interface/io/fbif.h>
> >  #include <xen/interface/io/protocols.h>
> >  #include <xen/xenbus.h>
> >+#include <xen/platform_pci.h>
> >  struct xenfb_info {
> >  	unsigned char		*fb;
> >@@ -699,6 +700,9 @@ static int __init xenfb_init(void)
> >  	if (xen_initial_domain())
> >  		return -ENODEV;
> >+	if (!xen_has_pv_devices())
> >+		return -ENODEV;
> >+
> >  	return xenbus_register_frontend(&xenfb_driver);
> >  }
> >diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c
> >index 34b20bf..6244f9c 100644
> >--- a/drivers/xen/xenbus/xenbus_probe_frontend.c
> >+++ b/drivers/xen/xenbus/xenbus_probe_frontend.c
> >@@ -496,7 +496,7 @@ subsys_initcall(xenbus_probe_frontend_init);
> >  #ifndef MODULE
> >  static int __init boot_wait_for_devices(void)
> >  {
> >-	if (xen_hvm_domain() && !xen_platform_pci_unplug)
> >+	if (!xen_has_pv_devices())
> >  		return -ENODEV;
> >  	ready_to_wait_for_devices = 1;
> >diff --git a/include/xen/platform_pci.h b/include/xen/platform_pci.h
> >index 438c256..b49eeab 100644
> >--- a/include/xen/platform_pci.h
> >+++ b/include/xen/platform_pci.h
> >@@ -48,4 +48,27 @@ static inline int xen_must_unplug_disks(void) {
> >  extern int xen_platform_pci_unplug;
> >+#if defined(CONFIG_XEN_PVHVM)
> >+extern bool xen_has_pv_devices(void);
> >+extern bool xen_has_pv_disk_devices(void);
> >+extern bool xen_has_pv_nic_devices(void);
> >+extern bool xen_has_pv_and_legacy_disk_devices(void);
> >+#else
> >+static inline bool xen_has_pv_devices(void)
> >+{
> >+	return IS_ENABLED(CONFIG_XEN);
> >+}
> >+static inline bool xen_has_pv_disk_devices(void)
> >+{
> >+	return IS_ENABLED(CONFIG_XEN);
> >+}
> >+static inline bool xen_has_pv_nic_devices(void)
> >+{
> >+	return IS_ENABLED(CONFIG_XEN);
> >+}
> >+static inline bool xen_has_pv_and_legacy_disk_devices(void)
> >+{
> >+	return false;
> >+}
> >+#endif
> >  #endif /* _XEN_PLATFORM_PCI_H */
> 

^ permalink raw reply

* Re: [PATCH -next] Input: zforce - fix error return code in zforce_start()
From: Heiko Stübner @ 2013-12-17 15:06 UTC (permalink / raw)
  To: Wei Yongjun; +Cc: dmitry.torokhov, rydberg, yongjun_wei, linux-input
In-Reply-To: <CAPgLHd8WBR38qd9VHfazXVTbuoJ=GPOafHzSz8d9XJ-F61a7nQ@mail.gmail.com>

Am Dienstag, 17. Dezember 2013, 04:27:16 schrieb Wei Yongjun:
> From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
> 
> The error code was not set if unable to set config, so the error
> condition wasn't reflected in the return value. Fix to return a
> negative error code from the error handling case instead of 0.
> 
> Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>

Thanks for the catch.

Acked-by: Heiko Stuebner <heiko@sntech.de>

^ permalink raw reply

* Re: kernel panic on gpio-keys
From: Paul Cercueil @ 2013-12-17 15:17 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input
In-Reply-To: <20131214093900.GA10799@core.coreip.homeip.net>

On 14/12/2013 10:39, Dmitry Torokhov wrote:
> On Wed, Dec 11, 2013 at 08:17:29PM +0100, Paul Cercueil wrote:
>> Hi there,
>>
>> I am trying to use the gpio-keys driver to inject joystick events.
>> There seems to be some basic support of it, looking at <linux/gpio_keys.h>.
>>
>> However, registering the following will trigger a kernel panic in
>> the kernel:
>>
>> static struct gpio_keys_button my_buttons[] {
>> 	{
>> 		.gpio = GPIO_FOO,
>> 		.type = EV_ABS,
>> 		.code = ABS_HAT0X,
>> 		.value = 1,
>> 	},
>> };
>>
>> (tested on kernel 3.12).
>>
>> I don't know well the input subsystem, so I have no idea of what is
>> going wrong. Could anybody try to at least reproduce the issue?
>
> It woudl be helpful if you poster the stack trace from panic so we'd
> have an idea where the fault happens.
>
> Thanks.
>

Here is the crash log I get: http://pastebin.com/FzTTGxsR
(I did put it on pastebin because it's huge, 200+ lines).

The first OOPS happen as soon as the GPIO button is pressed; the other 
ones seem to happen recursively. I included only a part of the log I 
get, as the OOPSes continue to flow until the watchdog kicks in.

^ permalink raw reply

* Disable i8042 check on Apple machines
From: Josh Boyer @ 2013-12-17 15:25 UTC (permalink / raw)
  To: Bastien Nocera, Dmitry Torokhov; +Cc: linux-input

Hi Bastien and Dmitry,

I was going through some of the patches we're carrying in Fedora and
came across this one that Bastien submitted a long time ago:

http://lkml.indiana.edu/hypermail/linux/kernel/1005.0/00938.html

It works and we're still carrying it, but Dmitry said it needed to be
done differently in i8042-x86ia64io.h.  Would that just mean adding a
DMI for Apple machines to the i8042_dmi_nopnp_table struct?  Or
exactly what should be done here?

I'd like to get this upstream.

josh

^ permalink raw reply

* [PATCH 1/4] Input: uinput: add full absinfo support
From: David Herrmann @ 2013-12-17 15:48 UTC (permalink / raw)
  To: linux-input
  Cc: Dmitry Torokhov, Jiri Kosina, Benjamin Tissoires, Peter Hutterer,
	Antonio Ospite, linux-kernel, input-tools, David Herrmann
In-Reply-To: <1387295334-1744-1-git-send-email-dh.herrmann@gmail.com>

We currently lack support for abs-resolution and abs-value parameters
during uinput ABS initialization. Furthermore, our parsers don't allow
growing ABS_CNT values. Therefore, introduce uinput_user_dev2.

User-space is free to write uinput_user_dev2 objects instead of
uinput_user_dev legacy objects now. If the kernel lacks support for it,
our comparison for "count != sizeof(struct uinput_user_dev)" will catch
this and return EINVAL. User-space shall retry with the legacy mode then.

Internally, we transform the legacy object into uinput_user_dev2 and then
handle both the same way.

The new uinput_user_dev2 object has multiple new features:
 - abs payload now has "value" and "resolution" parameters as part of the
   switch to "struct input_absinfo". We simply copy these over.
 - Our parser allows growing ABS_CNT. We automatically detect the payload
   size of the caller, thus, calculating the ABS_CNT the program was
   compiled with.
 - A "version" field to denote the uinput-version used. This is required
   to properly support changing "struct input_user_dev2" changes in the
   future. Due to the dynamic ABS_CNT support, we cannot simply add new
   fields, as we cannot deduce the structure size from the user-space
   given size. Thus, we need the "version" field to allow changing the
   object and properly detecting it in our write() handler.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/input/misc/uinput.c | 142 ++++++++++++++++++++++++++++++++------------
 include/uapi/linux/uinput.h |   9 +++
 2 files changed, 114 insertions(+), 37 deletions(-)

diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 7728359..927ad9a 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -358,14 +358,16 @@ static int uinput_allocate_device(struct uinput_device *udev)
 }
 
 static int uinput_setup_device(struct uinput_device *udev,
-			       const char __user *buffer, size_t count)
+			       struct uinput_user_dev2 *user_dev2,
+			       size_t abscnt)
 {
-	struct uinput_user_dev	*user_dev;
 	struct input_dev	*dev;
 	int			i;
 	int			retval;
+	struct input_absinfo	*abs;
 
-	if (count != sizeof(struct uinput_user_dev))
+	/* Ensure name is filled in */
+	if (!user_dev2->name[0])
 		return -EINVAL;
 
 	if (!udev->dev) {
@@ -375,37 +377,27 @@ static int uinput_setup_device(struct uinput_device *udev,
 	}
 
 	dev = udev->dev;
-
-	user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev));
-	if (IS_ERR(user_dev))
-		return PTR_ERR(user_dev);
-
-	udev->ff_effects_max = user_dev->ff_effects_max;
-
-	/* Ensure name is filled in */
-	if (!user_dev->name[0]) {
-		retval = -EINVAL;
-		goto exit;
-	}
+	udev->ff_effects_max = user_dev2->ff_effects_max;
 
 	kfree(dev->name);
-	dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE,
+	dev->name = kstrndup(user_dev2->name, UINPUT_MAX_NAME_SIZE,
 			     GFP_KERNEL);
-	if (!dev->name) {
-		retval = -ENOMEM;
-		goto exit;
-	}
-
-	dev->id.bustype	= user_dev->id.bustype;
-	dev->id.vendor	= user_dev->id.vendor;
-	dev->id.product	= user_dev->id.product;
-	dev->id.version	= user_dev->id.version;
+	if (!dev->name)
+		return -ENOMEM;
 
-	for (i = 0; i < ABS_CNT; i++) {
-		input_abs_set_max(dev, i, user_dev->absmax[i]);
-		input_abs_set_min(dev, i, user_dev->absmin[i]);
-		input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]);
-		input_abs_set_flat(dev, i, user_dev->absflat[i]);
+	dev->id.bustype = user_dev2->id.bustype;
+	dev->id.vendor = user_dev2->id.vendor;
+	dev->id.product = user_dev2->id.product;
+	dev->id.version = user_dev2->id.version;
+
+	for (i = 0; i < abscnt; i++) {
+		abs = &user_dev2->abs[i];
+		input_abs_set_val(dev, i, abs->value);
+		input_abs_set_max(dev, i, abs->maximum);
+		input_abs_set_min(dev, i, abs->minimum);
+		input_abs_set_fuzz(dev, i, abs->fuzz);
+		input_abs_set_flat(dev, i, abs->flat);
+		input_abs_set_res(dev, i, abs->resolution);
 	}
 
 	/* check if absmin/absmax/absfuzz/absflat are filled as
@@ -413,7 +405,7 @@ static int uinput_setup_device(struct uinput_device *udev,
 	if (test_bit(EV_ABS, dev->evbit)) {
 		retval = uinput_validate_absbits(dev);
 		if (retval < 0)
-			goto exit;
+			return retval;
 		if (test_bit(ABS_MT_SLOT, dev->absbit)) {
 			int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
 			input_mt_init_slots(dev, nslot, 0);
@@ -423,11 +415,84 @@ static int uinput_setup_device(struct uinput_device *udev,
 	}
 
 	udev->state = UIST_SETUP_COMPLETE;
-	retval = count;
+	return 0;
+}
+
+static int uinput_setup_device1(struct uinput_device *udev,
+				const char __user *buffer, size_t count)
+{
+	struct uinput_user_dev	*user_dev;
+	struct uinput_user_dev2	*user_dev2;
+	int			i;
+	int			retval;
+
+	if (count != sizeof(struct uinput_user_dev))
+		return -EINVAL;
+
+	user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev));
+	if (IS_ERR(user_dev))
+		return PTR_ERR(user_dev);
+
+	user_dev2 = kmalloc(sizeof(*user_dev2), GFP_KERNEL);
+	if (!user_dev2) {
+		kfree(user_dev);
+		return -ENOMEM;
+	}
+
+	user_dev2->version = UINPUT_VERSION;
+	memcpy(user_dev2->name, user_dev->name, UINPUT_MAX_NAME_SIZE);
+	memcpy(&user_dev2->id, &user_dev->id, sizeof(struct input_id));
+	user_dev2->ff_effects_max = user_dev->ff_effects_max;
+
+	for (i = 0; i < ABS_CNT; ++i) {
+		user_dev2->abs[i].value = 0;
+		user_dev2->abs[i].maximum = user_dev->absmax[i];
+		user_dev2->abs[i].minimum = user_dev->absmin[i];
+		user_dev2->abs[i].fuzz = user_dev->absfuzz[i];
+		user_dev2->abs[i].flat = user_dev->absflat[i];
+		user_dev2->abs[i].resolution = 0;
+	}
+
+	retval = uinput_setup_device(udev, user_dev2, ABS_CNT);
 
- exit:
 	kfree(user_dev);
-	return retval;
+	kfree(user_dev2);
+
+	return retval ? retval : count;
+}
+
+static int uinput_setup_device2(struct uinput_device *udev,
+			       const char __user *buffer, size_t count)
+{
+	struct uinput_user_dev2	*user_dev2;
+	int			retval;
+	size_t			off, abscnt, max;
+
+	/* The first revision of "uinput_user_dev2" is bigger than
+	 * "uinput_user_dev" and growing. Disallow any smaller payloads. */
+	if (count <= sizeof(struct uinput_user_dev))
+		return -EINVAL;
+
+	/* rough check to avoid huge kernel space allocations */
+	max = ABS_CNT * sizeof(*user_dev2->abs) + sizeof(*user_dev2);
+	if (count > max)
+		return -EINVAL;
+
+	user_dev2 = memdup_user(buffer, count);
+	if (IS_ERR(user_dev2))
+		return PTR_ERR(user_dev2);
+
+	if (user_dev2->version > UINPUT_VERSION) {
+		retval = -EINVAL;
+	} else {
+		off = offsetof(struct uinput_user_dev2, abs);
+		abscnt = (count - off) / sizeof(*user_dev2->abs);
+		retval = uinput_setup_device(udev, user_dev2, abscnt);
+	}
+
+	kfree(user_dev2);
+
+	return retval ? retval : count;
 }
 
 static ssize_t uinput_inject_events(struct uinput_device *udev,
@@ -469,9 +534,12 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer,
 	if (retval)
 		return retval;
 
-	retval = udev->state == UIST_CREATED ?
-			uinput_inject_events(udev, buffer, count) :
-			uinput_setup_device(udev, buffer, count);
+	if (udev->state == UIST_CREATED)
+		retval = uinput_inject_events(udev, buffer, count);
+	else if (count <= sizeof(struct uinput_user_dev))
+		retval = uinput_setup_device1(udev, buffer, count);
+	else
+		retval = uinput_setup_device2(udev, buffer, count);
 
 	mutex_unlock(&udev->mutex);
 
diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h
index fe46431..c2e8710 100644
--- a/include/uapi/linux/uinput.h
+++ b/include/uapi/linux/uinput.h
@@ -134,4 +134,13 @@ struct uinput_user_dev {
 	__s32 absfuzz[ABS_CNT];
 	__s32 absflat[ABS_CNT];
 };
+
+struct uinput_user_dev2 {
+	__u8 version;
+	char name[UINPUT_MAX_NAME_SIZE];
+	struct input_id id;
+	__u32 ff_effects_max;
+	struct input_absinfo abs[ABS_CNT];
+};
+
 #endif /* _UAPI__UINPUT_H_ */
-- 
1.8.5.1

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox