[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-Id: <1214605070.19945.0.camel@alnilam>
Date: Sat, 28 Jun 2008 00:17:50 +0200
From: Henrik Rydberg <rydberg@...omail.se>
To: Oliver Neukum <oliver@...kum.org>
Cc: linux-input@...r.kernel.org, linux-kernel@...r.kernel.org,
dmitry.torokhov@...il.com, robfitz@...k.net, jikos@...os.cz,
vojtech@...e.cz, dmonakhov@...nvz.org
Subject: Re: [PATCH 001/001] linux-input: Support for BCM5974 multitouch
trackpad
On Fri, 2008-06-27 at 09:43 +0200, Oliver Neukum wrote:
> Am Freitag 27 Juni 2008 01:07:19 schrieb Henrik Rydberg:
> > From: Henrik Rydberg <rydberg@...omail.se>
> >
> > BCM5974: This driver adds support for the multitouch trackpad on the new
> > Apple Macbook Air and Macbook Pro Penryn laptops. It replaces the
> > appletouch driver on those computers, and integrates well with the
> > synaptics driver of the Xorg system.
>
> Some comments on the driver.
>
> Regards
> Oliver
>
Thank you very much for finding the bugs, and for your general comments.
I have modified the latest version accordingly, taking all of your
suggestions into account. The new patch follows below.
---
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 7bbea09..89ef7c3 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -130,6 +130,29 @@ config MOUSE_APPLETOUCH
To compile this driver as a module, choose M here: the
module will be called appletouch.
+config MOUSE_BCM5974
+ tristate "Apple USB BCM5974 Multitouch trackpad support"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you have an Apple USB BCM5974 Multitouch
+ trackpad.
+
+ The BCM5974 is the multitouch trackpad found in the Macbook
+ Air (JAN2008) and Macbook Pro Penryn (FEB2008) laptops.
+
+ It is also found in the IPhone (2007) and Ipod Touch (2008).
+
+ This driver provides multitouch functionality together with
+ the synaptics X11 driver.
+
+ The interface is currently identical to the appletouch interface,
+ for further information, see
+ <file:Documentation/input/appletouch.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bcm5974.
+
config MOUSE_INPORT
tristate "InPort/MS/ATIXL busmouse"
depends on ISA
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 9e6e363..d4d2025 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
+obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
obj-$(CONFIG_MOUSE_INPORT) += inport.o
diff -uprN -X upstream-2.6/Documentation/dontdiff baseline-2.6/drivers/input/mouse/bcm5974.c upstream-2.6/drivers/input/mouse/bcm5974.c
--- baseline-2.6/drivers/input/mouse/bcm5974.c 1970-01-01 01:00:00.000000000 +0100
+++ upstream-2.6/drivers/input/mouse/bcm5974.c 2008-06-27 23:59:42.000000000 +0200
@@ -0,0 +1,667 @@
+/*
+ * Apple USB BCM5974 (Macbook Air and Penryn Macbook Pro) multitouch driver
+ *
+ * Copyright (C) 2008 Henrik Rydberg (rydberg@...omail.se)
+ *
+ * The USB initialization and package decoding was made by
+ * Scott Shawcroft as part of the touchd user-space driver project:
+ * Copyright (C) 2008 Scott Shawcroft (tannewt of tannewt.org)
+ *
+ * The BCM5974 driver is based on the appletouch driver:
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@...ah.com)
+ * Copyright (C) 2005 Johannes Berg (johannes@...solutions.net)
+ * Copyright (C) 2005 Stelian Pop (stelian@...ies.net)
+ * Copyright (C) 2005 Frank Arnold (frank@...rocco-5v-turbo.de)
+ * Copyright (C) 2005 Peter Osterlund (petero2@...ia.com)
+ * Copyright (C) 2005 Michael Hanselmann (linux-kernel@...smi.ch)
+ * Copyright (C) 2006 Nicolas Boichat (nicolas@...chat.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+
+#define APPLE_VENDOR_ID 0x05AC
+
+/* MacbookAir BCM5974, aka wellspring */
+
+#define ATP_WELLSPRING_ANSI 0x0223
+#define ATP_WELLSPRING_ISO 0x0224
+#define ATP_WELLSPRING_JIS 0x0225
+#define ATP_WELLSPRING2_ANSI 0x0230
+#define ATP_WELLSPRING2_ISO 0x0231
+#define ATP_WELLSPRING2_JIS 0x0232
+
+#define ATP_DEVICE(prod) { \
+ .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_CLASS | \
+ USB_DEVICE_ID_MATCH_INT_PROTOCOL), \
+ .idVendor = APPLE_VENDOR_ID, \
+ .idProduct = (prod), \
+ .bInterfaceClass = 0x03, \
+ .bInterfaceProtocol = 0x02 \
+ }
+
+/* table of devices that work with this driver */
+static const struct usb_device_id atp_table [] = {
+ /* MacbookAir1.1 */
+ ATP_DEVICE(ATP_WELLSPRING_ANSI),
+ ATP_DEVICE(ATP_WELLSPRING_ISO),
+ ATP_DEVICE(ATP_WELLSPRING_JIS),
+
+ /* MacbookProPenryn */
+ ATP_DEVICE(ATP_WELLSPRING2_ANSI),
+ ATP_DEVICE(ATP_WELLSPRING2_ISO),
+ ATP_DEVICE(ATP_WELLSPRING2_JIS),
+
+ /* Terminating entry */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, atp_table);
+
+struct atp_params_t {
+ int devmin; /* device minimum reading */
+ int devmax; /* device maximum reading */
+ int min; /* logical minimum reading */
+ int max; /* logical maximum reading */
+ int fuzz; /* reading noise value */
+ int flat; /* zero */
+};
+
+struct atp_config_t {
+ int ansi, iso, jis; /* the product id of this device */
+ int bt_ep; /* the endpoint of the button interface */
+ int bt_datalen; /* data length of the button interface */
+ int tp_ep; /* the endpoint of the trackpad interface */
+ int tp_datalen; /* data length of the trackpad interface */
+ struct atp_params_t x; /* horizontal limits */
+ struct atp_params_t y; /* vertical limits */
+ struct atp_params_t p; /* pressure limits */
+};
+
+/* trackpad header structure */
+struct tp_header_t {
+ u8 unknown1[16]; /* constants, timers, etc */
+ u8 nfinger; /* number of fingers on trackpad */
+ u8 unknown2[9]; /* constants, timers, etc */
+};
+
+/* trackpad finger structure */
+struct tp_finger_t {
+ __le16 origin; /* left/right origin? */
+ __le16 abs_x; /* absolute x coodinate */
+ __le16 abs_y; /* absolute y coodinate */
+ __le16 rel_x; /* relative x coodinate */
+ __le16 rel_y; /* relative y coodinate */
+ __le16 size_major; /* finger size, major axis? */
+ __le16 size_minor; /* finger size, minor axis? */
+ __le16 orientation; /* 16384 when point, else 15 bit angle */
+ __le16 force_major; /* trackpad force, major axis? */
+ __le16 force_minor; /* trackpad force, minor axis? */
+ __le16 unused[3]; /* zeros */
+ __le16 multi; /* one finger: varies, more fingers: constant */
+};
+
+/* trackpad data structure */
+struct tp_data_t {
+ struct tp_header_t header;
+ struct tp_finger_t finger[16];
+};
+
+/* device constants */
+static const struct atp_config_t atp_config_table[] = {
+ {
+ ATP_WELLSPRING_ANSI,
+ ATP_WELLSPRING_ISO,
+ ATP_WELLSPRING_JIS,
+ 0x84, 4,
+ 0x81, sizeof(struct tp_data_t),
+ {-4824, 5342, 0, 1280, 16, 0},
+ {-172, 5820, 0, 800, 16, 0},
+ {0, 256, 0, 256, 16, 0}
+ },
+ {
+ ATP_WELLSPRING2_ANSI,
+ ATP_WELLSPRING2_ISO,
+ ATP_WELLSPRING2_JIS,
+ 0x84, 4,
+ 0x81, sizeof(struct tp_data_t),
+ {-4824, 5342, 0, 1280, 16, 0},
+ {-172, 5820, 0, 800, 16, 0},
+ {0, 256, 0, 256, 16, 0}
+ },
+ {}
+};
+
+static inline
+const struct atp_config_t *atp_product_config(struct usb_device *udev)
+{
+ u16 id = le16_to_cpu(udev->descriptor.idProduct);
+ const struct atp_config_t *config;
+ for (config = atp_config_table; config->ansi; ++config)
+ if (config->ansi == id || config->iso == id || config->jis == id)
+ return config;
+ return atp_config_table;
+}
+
+/* Wellspring initialization constants */
+#define ATP_WELLSPRING_MODE_READ_REQUEST_ID 1
+#define ATP_WELLSPRING_MODE_WRITE_REQUEST_ID 9
+#define ATP_WELLSPRING_MODE_REQUEST_VALUE 0x300
+#define ATP_WELLSPRING_MODE_REQUEST_INDEX 0
+#define ATP_WELLSPRING_MODE_VENDOR_VALUE_1 0x01
+#define ATP_WELLSPRING_MODE_VENDOR_VALUE_2 0x05
+
+#define dprintk(format, a...) \
+ { if (debug) printk(KERN_DEBUG format, ##a); }
+
+MODULE_AUTHOR("Henrik Rydberg");
+MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver");
+MODULE_LICENSE("GPL");
+
+static int debug = 1;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activate debugging output");
+
+static int atp_wellspring_init(struct usb_device *udev)
+{
+ const struct atp_config_t *cfg = atp_product_config(udev);
+ char *data = kmalloc(8, GFP_DMA);
+ int size;
+
+ /* reset button endpoint */
+ if (usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
+ 0, cfg->bt_ep, NULL, 0, 5000)) {
+ err("Could not reset button endpoint");
+ goto error;
+ }
+
+ /* reset trackpad endpoint */
+ if (usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
+ 0, cfg->tp_ep, NULL, 0, 5000)) {
+ err("Could not reset trackpad endpoint");
+ goto error;
+ }
+
+ /* read configuration */
+ size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ ATP_WELLSPRING_MODE_READ_REQUEST_ID,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ ATP_WELLSPRING_MODE_REQUEST_VALUE,
+ ATP_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
+
+ if (size != 8) {
+ err("Could not do mode read request from device (Wellspring Raw mode)");
+ goto error;
+ }
+
+ /* apply the mode switch */
+ data[0] = ATP_WELLSPRING_MODE_VENDOR_VALUE_1;
+ data[1] = ATP_WELLSPRING_MODE_VENDOR_VALUE_2;
+
+ /* write configuration */
+ size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ ATP_WELLSPRING_MODE_WRITE_REQUEST_ID,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ ATP_WELLSPRING_MODE_REQUEST_VALUE,
+ ATP_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
+
+ if (size != 8) {
+ err("Could not do mode write request to device (Wellspring Raw mode)");
+ goto error;
+ }
+
+ kfree(data);
+ return 0;
+
+ error:
+ kfree(data);
+ return -EIO;
+}
+
+/* Structure to hold all of our device specific stuff */
+struct atp {
+ char phys[64];
+ struct usb_device *udev; /* usb device */
+ struct input_dev *input; /* input dev */
+ struct atp_config_t cfg; /* device configuration */
+ unsigned open; /* non-zero if opened */
+ struct urb *bt_urb; /* button usb request block */
+ signed char *bt_data; /* button transferred data */
+ unsigned bt_valid; /* are the button sensors valid ? */
+ unsigned bt_state; /* current button state */
+ struct urb *tp_urb; /* trackpad usb request block */
+ signed char *tp_data; /* trackpad transferred data */
+ unsigned tp_valid; /* are the trackpad sensors valid ? */
+};
+
+static inline int raw2int(__le16 x)
+{
+ return (short)le16_to_cpu(x);
+}
+
+static inline int int2scale(const struct atp_params_t *p, int x)
+{
+ return ((p->max-p->min)*x)/(p->devmax-p->devmin);
+}
+
+/**
+ * all value ranges, both device and logical, are assumed to be [a,b).
+ */
+static inline int int2bound(const struct atp_params_t *p, int x)
+{
+ int s = p->min+int2scale(p, x);
+ return s < p->min?p->min:s >= p->max?p->max-1:s;
+}
+
+/**
+ * check quality of reading.
+ * -1: bad data
+ * 0: ignore this reading
+ * 1: good reading
+ */
+static int compute_quality(const unsigned char *data, int size)
+{
+ if (size < 26 || (size-26)%28 != 0)
+ return -1;
+
+ return 1;
+}
+
+/**
+ * convert raw data to synaptics sensor output.
+ * returns the number of fingers on the trackpad,
+ * or a negative number in vase of bad data.
+ */
+static int compute_movement(int *abs_x, int *abs_y,
+ int *rel_x, int *rel_y,
+ int *pressure,
+ struct atp_config_t *cfg,
+ const unsigned char *data, int size)
+{
+ const int nfinger = (size-26)/28;
+ const struct tp_data_t *tp = (struct tp_data_t *) data;
+
+ if (nfinger) {
+ *abs_x = int2bound(&cfg->x, raw2int(tp->finger->abs_x) - cfg->x.devmin);
+ *abs_y = int2bound(&cfg->y, cfg->y.devmax - raw2int(tp->finger->abs_y));
+ *rel_x = int2scale(&cfg->x, raw2int(tp->finger->rel_x));
+ *rel_y = int2scale(&cfg->y, -raw2int(tp->finger->rel_y));
+ *pressure = int2bound(&cfg->p, raw2int(tp->finger->force_major));
+ } else {
+ *abs_x = 0;
+ *abs_y = 0;
+ *rel_x = 0;
+ *rel_y = 0;
+ *pressure = 0;
+ }
+
+ /* report zero fingers for zero pressure */
+ return *pressure > 0?nfinger:0;
+}
+
+static void atp_button(struct urb *urb)
+{
+ struct atp *dev = urb->context;
+ const unsigned char *data = dev->bt_data;
+ const int size = dev->bt_urb->actual_length;
+ int button = 0, retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -EOVERFLOW:
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* This urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __func__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __func__, urb->status);
+ goto exit;
+ }
+
+ /* first sample data ignored */
+ if (!dev->bt_valid) {
+ dev->bt_valid = 1;
+ goto exit;
+ }
+
+ /* drop incomplete datasets */
+ if (size != 4) {
+ dprintk("bcm5974: incomplete button package (first byte: %d, length: %d)\n",
+ (int)data[0], size);
+ goto exit;
+ }
+
+ button = data[1];
+
+ /* only report button state changes */
+ if (button != dev->bt_state) {
+ input_report_key(dev->input, BTN_LEFT, button);
+ input_sync(dev->input);
+ }
+
+ dev->bt_state = button;
+
+ exit:
+ retval = usb_submit_urb(dev->bt_urb, GFP_ATOMIC);
+ if (retval) {
+ err("%s - button usb_submit_urb failed with result %d",
+ __func__, retval);
+ }
+}
+
+static void atp_trackpad(struct urb *urb)
+{
+ struct atp *dev = urb->context;
+ int abs_x, abs_y, rel_x, rel_y, pressure;
+ int quality, nfinger, retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -EOVERFLOW:
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* This urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __func__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __func__, urb->status);
+ goto exit;
+ }
+
+ /* first sample data ignored */
+ if (!dev->tp_valid) {
+ dev->tp_valid = 1;
+ goto exit;
+ }
+
+ /* determine quality of reading */
+ quality = compute_quality(dev->tp_data, dev->tp_urb->actual_length);
+
+ /* drop incomplete datasets */
+ if (quality < 0) {
+ dprintk("bcm5974: incomplete trackpad package "
+ "(first byte: %d, length: %d)\n",
+ (int)dev->tp_data[0], dev->tp_urb->actual_length);
+ goto exit;
+ }
+
+ /* drop poor quality readings */
+ if (quality == 0)
+ goto exit;
+
+ nfinger = compute_movement(&abs_x, &abs_y, &rel_x, &rel_y, &pressure,
+ &dev->cfg,
+ dev->tp_data, dev->tp_urb->actual_length);
+
+ if (debug > 1) {
+ printk(KERN_DEBUG "bcm5974: x: %04d y: %04d dx: %3d dy: %3d p: %3d\n",
+ abs_x, abs_y, rel_x, rel_y, pressure);
+ }
+
+ /* input_report_key(dev->input,BTN_TOUCH,pressure>dev->cfg.p.fuzz); */
+ input_report_abs(dev->input, ABS_PRESSURE, pressure);
+ input_report_abs(dev->input, ABS_X, abs_x);
+ input_report_abs(dev->input, ABS_Y, abs_y);
+ /* input_report_rel(dev->input, REL_X, rel_x); */
+ /* input_report_rel(dev->input, REL_Y, rel_y); */
+ input_report_key(dev->input, BTN_TOOL_FINGER, nfinger == 1);
+ input_report_key(dev->input, BTN_TOOL_DOUBLETAP, nfinger == 2);
+ input_report_key(dev->input, BTN_TOOL_TRIPLETAP, nfinger > 2);
+ input_sync(dev->input);
+
+ exit:
+ retval = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
+ if (retval) {
+ err("%s - trackpad usb_submit_urb failed with result %d",
+ __func__, retval);
+ }
+}
+
+static int atp_open(struct input_dev *input)
+{
+ struct atp *dev = input_get_drvdata(input);
+
+ if (usb_submit_urb(dev->bt_urb, GFP_ATOMIC))
+ goto error;
+
+ if (usb_submit_urb(dev->tp_urb, GFP_ATOMIC))
+ goto err_free_bt_urb;
+
+ dev->open = 1;
+ return 0;
+
+ err_free_bt_urb:
+ usb_free_urb(dev->bt_urb);
+ error:
+ return -EIO;
+}
+
+static void atp_close(struct input_dev *input)
+{
+ struct atp *dev = input_get_drvdata(input);
+
+ usb_kill_urb(dev->tp_urb);
+ usb_kill_urb(dev->bt_urb);
+ dev->open = 0;
+}
+
+static int atp_probe(struct usb_interface *iface,
+ const struct usb_device_id *id)
+{
+ int error = -ENOMEM;
+ struct usb_device *udev = interface_to_usbdev(iface);
+ const struct atp_config_t *cfg;
+ struct atp *dev;
+ struct input_dev *input_dev;
+
+ /* find the product index */
+ cfg = atp_product_config(udev);
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(struct atp), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!dev || !input_dev) {
+ err("Out of memory");
+ goto err_free_devs;
+ }
+
+ dev->udev = udev;
+ dev->input = input_dev;
+ dev->cfg = *cfg;
+
+ /* switch to raw sensor mode */
+ if (atp_wellspring_init(udev))
+ goto err_free_devs;
+
+ printk(KERN_INFO "bcm5974: Wellspring mode initialized.\n");
+
+ dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->bt_urb)
+ goto err_free_devs;
+
+ dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->tp_urb)
+ goto err_free_bt_urb;
+
+ dev->bt_data = usb_buffer_alloc(dev->udev,
+ dev->cfg.bt_datalen,
+ GFP_KERNEL,
+ &dev->bt_urb->transfer_dma);
+ if (!dev->bt_data)
+ goto err_free_urb;
+
+ dev->tp_data = usb_buffer_alloc(dev->udev,
+ dev->cfg.tp_datalen,
+ GFP_KERNEL,
+ &dev->tp_urb->transfer_dma);
+ if (!dev->tp_data)
+ goto err_free_bt_buffer;
+
+ usb_fill_int_urb(dev->bt_urb, udev,
+ usb_rcvintpipe(udev, cfg->bt_ep),
+ dev->bt_data, dev->cfg.bt_datalen,
+ atp_button, dev, 1);
+
+ usb_fill_int_urb(dev->tp_urb, udev,
+ usb_rcvintpipe(udev, cfg->tp_ep),
+ dev->tp_data, dev->cfg.tp_datalen,
+ atp_trackpad, dev, 1);
+
+ usb_make_path(udev, dev->phys, sizeof(dev->phys));
+ strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+ input_dev->name = "bcm5974";
+ input_dev->phys = dev->phys;
+ usb_to_input_id(dev->udev, &input_dev->id);
+ input_dev->dev.parent = &iface->dev;
+
+ input_set_drvdata(input_dev, dev);
+
+ input_dev->open = atp_open;
+ input_dev->close = atp_close;
+
+ set_bit(EV_ABS, input_dev->evbit);
+ input_set_abs_params(input_dev, ABS_X,
+ cfg->x.min, cfg->x.max, cfg->x.fuzz, cfg->x.flat);
+ input_set_abs_params(input_dev, ABS_Y,
+ cfg->y.min, cfg->y.max, cfg->y.fuzz, cfg->y.flat);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ cfg->p.min, cfg->p.max, cfg->p.fuzz, cfg->p.flat);
+ /* set_bit(EV_REL, input_dev->evbit); */
+
+ set_bit(EV_KEY, input_dev->evbit);
+ /* set_bit(BTN_TOUCH, input_dev->keybit); */
+ set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+ set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+ set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
+ set_bit(BTN_LEFT, input_dev->keybit);
+
+ error = input_register_device(dev->input);
+ if (error)
+ goto err_free_buffer;
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(iface, dev);
+
+ return 0;
+
+ err_free_buffer:
+ usb_buffer_free(dev->udev, dev->cfg.tp_datalen,
+ dev->tp_data, dev->tp_urb->transfer_dma);
+ err_free_bt_buffer:
+ usb_buffer_free(dev->udev, dev->cfg.bt_datalen,
+ dev->bt_data, dev->bt_urb->transfer_dma);
+ err_free_urb:
+ usb_free_urb(dev->tp_urb);
+ err_free_bt_urb:
+ usb_free_urb(dev->bt_urb);
+ err_free_devs:
+ usb_set_intfdata(iface, NULL);
+ kfree(dev);
+ input_free_device(input_dev);
+ return error;
+}
+
+static void atp_disconnect(struct usb_interface *iface)
+{
+ struct atp *dev = usb_get_intfdata(iface);
+
+ usb_set_intfdata(iface, NULL);
+ if (dev) {
+ input_unregister_device(dev->input);
+ usb_buffer_free(dev->udev, dev->cfg.tp_datalen,
+ dev->tp_data, dev->tp_urb->transfer_dma);
+ usb_buffer_free(dev->udev, dev->cfg.bt_datalen,
+ dev->bt_data, dev->bt_urb->transfer_dma);
+ usb_free_urb(dev->tp_urb);
+ usb_free_urb(dev->bt_urb);
+ kfree(dev);
+ }
+ printk(KERN_INFO "input: bcm5974 disconnected\n");
+}
+
+static int atp_suspend(struct usb_interface *iface, pm_message_t message)
+{
+ struct atp *dev = usb_get_intfdata(iface);
+
+ usb_kill_urb(dev->tp_urb);
+ dev->tp_valid = 0;
+
+ usb_kill_urb(dev->bt_urb);
+ dev->bt_valid = 0;
+
+ return 0;
+}
+
+static int atp_resume(struct usb_interface *iface)
+{
+ struct atp *dev = usb_get_intfdata(iface);
+
+ if (dev->open) {
+ if (usb_submit_urb(dev->bt_urb, GFP_ATOMIC))
+ goto error;
+ if (usb_submit_urb(dev->tp_urb, GFP_ATOMIC))
+ goto err_free_bt_urb;
+ }
+
+ return 0;
+
+ err_free_bt_urb:
+ usb_free_urb(dev->bt_urb);
+ error:
+ return -EIO;
+}
+
+static struct usb_driver atp_driver = {
+ .name = "bcm5974",
+ .probe = atp_probe,
+ .disconnect = atp_disconnect,
+ .suspend = atp_suspend,
+ .resume = atp_resume,
+ .id_table = atp_table,
+};
+
+static int __init atp_init(void)
+{
+ return usb_register(&atp_driver);
+}
+
+static void __exit atp_exit(void)
+{
+ usb_deregister(&atp_driver);
+}
+
+module_init(atp_init);
+module_exit(atp_exit);
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists