From e018e95b0785ba2ee0e219cd0b59ca58eeeeb9f3 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 8 Oct 2008 23:16:47 +0200 Subject: bcm5974 (0.5) unstable; urgency=low * Working palm detection * Changes for kernel.org * DKMS introduced. -- Henrik Rydberg Sun, 06 Jul 2008 20:59:39 +0200 --- INSTALL | 17 + README | 14 + bcm5974-dkms-mkdeb/Makefile | 10 + bcm5974-dkms-mkdeb/debian/README.Debian | 10 + bcm5974-dkms-mkdeb/debian/changelog | 6 + bcm5974-dkms-mkdeb/debian/compat | 1 + bcm5974-dkms-mkdeb/debian/control | 11 + bcm5974-dkms-mkdeb/debian/copyright | 4 + bcm5974-dkms-mkdeb/debian/dirs | 2 + bcm5974-dkms-mkdeb/debian/postinst | 71 ++ bcm5974-dkms-mkdeb/debian/postrm | 27 + bcm5974-dkms-mkdeb/debian/prerm | 28 + bcm5974-dkms-mkdeb/debian/rules | 53 ++ bcm5974.c | 1079 ++++++++++++++++--------------- dkms.conf | 8 + etc/modprobe.d/bcm5974 | 10 + scripts/bcm5974-diagnostics | 241 +++++++ scripts/bcm5974-post-install | 50 ++ scripts/bcm5974-post-remove | 29 + 19 files changed, 1132 insertions(+), 539 deletions(-) create mode 100644 INSTALL create mode 100644 README create mode 100644 bcm5974-dkms-mkdeb/Makefile create mode 100644 bcm5974-dkms-mkdeb/debian/README.Debian create mode 100644 bcm5974-dkms-mkdeb/debian/changelog create mode 100644 bcm5974-dkms-mkdeb/debian/compat create mode 100644 bcm5974-dkms-mkdeb/debian/control create mode 100644 bcm5974-dkms-mkdeb/debian/copyright create mode 100644 bcm5974-dkms-mkdeb/debian/dirs create mode 100644 bcm5974-dkms-mkdeb/debian/postinst create mode 100644 bcm5974-dkms-mkdeb/debian/postrm create mode 100644 bcm5974-dkms-mkdeb/debian/prerm create mode 100644 bcm5974-dkms-mkdeb/debian/rules create mode 100644 dkms.conf create mode 100644 etc/modprobe.d/bcm5974 create mode 100755 scripts/bcm5974-diagnostics create mode 100755 scripts/bcm5974-post-install create mode 100755 scripts/bcm5974-post-remove diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..7c7e975 --- /dev/null +++ b/INSTALL @@ -0,0 +1,17 @@ +Installation instructions bcm5974 +---------------------------------- + +Standing in the source directory, do: + +1. make -C /lib/modules/`uname -r`/build M=$PWD modules + +2. sudo cp bcm5974.ko /lib/modules/`uname -r`/kernel/drivers/input/mouse/ + +3. sudo ./scripts/bcm5974-post-install + +4. reboot (only if bcm5974 never was used before) + +For trouble-shooting, try running + + ./scripts/bcm5974-diagnostics + diff --git a/README b/README new file mode 100644 index 0000000..898826a --- /dev/null +++ b/README @@ -0,0 +1,14 @@ +Apple USB BCM5974 Multitouch trackpad support +--------------------------------------------- + +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. + +Authors +------- + +Written by Henrik Rydberg (rydberg@euromail.se); see bcm5974.c for the +complete list of credits. + diff --git a/bcm5974-dkms-mkdeb/Makefile b/bcm5974-dkms-mkdeb/Makefile new file mode 100644 index 0000000..b8d246c --- /dev/null +++ b/bcm5974-dkms-mkdeb/Makefile @@ -0,0 +1,10 @@ +SRC = $(DESTDIR)/usr/src + +all: + +clean: + +install: + install -d "$(SRC)" + install -m 644 $(NAME)-$(VERSION).dkms.tar.gz "$(SRC)" + diff --git a/bcm5974-dkms-mkdeb/debian/README.Debian b/bcm5974-dkms-mkdeb/debian/README.Debian new file mode 100644 index 0000000..7a1b108 --- /dev/null +++ b/bcm5974-dkms-mkdeb/debian/README.Debian @@ -0,0 +1,10 @@ +MODULE_NAME DKMS module for Debian + +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. + +This package was automatically generated by the DKMS system, +for distribution on Debian based operating systems. + diff --git a/bcm5974-dkms-mkdeb/debian/changelog b/bcm5974-dkms-mkdeb/debian/changelog new file mode 100644 index 0000000..029a7bc --- /dev/null +++ b/bcm5974-dkms-mkdeb/debian/changelog @@ -0,0 +1,6 @@ +MODULE_NAME-dkms (MODULE_VERSION) unstable; urgency=low + + * Automatically packaged by DKMS. + + -- Henrik Rydberg DATE_STAMP + diff --git a/bcm5974-dkms-mkdeb/debian/compat b/bcm5974-dkms-mkdeb/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/bcm5974-dkms-mkdeb/debian/compat @@ -0,0 +1 @@ +5 diff --git a/bcm5974-dkms-mkdeb/debian/control b/bcm5974-dkms-mkdeb/debian/control new file mode 100644 index 0000000..ad717e6 --- /dev/null +++ b/bcm5974-dkms-mkdeb/debian/control @@ -0,0 +1,11 @@ +Source: MODULE_NAME-dkms +Section: linux-input +Priority: optional +Maintainer: Henrik Rydberg +Build-Depends: debhelper (>= 5) +Standards-Version: 3.7.2 + +Package: MODULE_NAME-dkms +Architecture: all +Depends: dkms (>= 1.95), bash (>> 1.99) +Description: Apple USB BCM5974 Multitouch trackpad support (DKMS) diff --git a/bcm5974-dkms-mkdeb/debian/copyright b/bcm5974-dkms-mkdeb/debian/copyright new file mode 100644 index 0000000..d690e83 --- /dev/null +++ b/bcm5974-dkms-mkdeb/debian/copyright @@ -0,0 +1,4 @@ +GNU GENERAL PUBLIC LICENSE Version 2 + +See the LICENSE file of this distribution for details. + diff --git a/bcm5974-dkms-mkdeb/debian/dirs b/bcm5974-dkms-mkdeb/debian/dirs new file mode 100644 index 0000000..2b01d9a --- /dev/null +++ b/bcm5974-dkms-mkdeb/debian/dirs @@ -0,0 +1,2 @@ +usr/src +etc/modprobe.d diff --git a/bcm5974-dkms-mkdeb/debian/postinst b/bcm5974-dkms-mkdeb/debian/postinst new file mode 100644 index 0000000..65b16ae --- /dev/null +++ b/bcm5974-dkms-mkdeb/debian/postinst @@ -0,0 +1,71 @@ +#!/bin/sh + +set -e + +NAME=MODULE_NAME +VERSION=MODULE_VERSION + +case "$1" in + configure) + # Determine current arch / kernel + c_arch=`uname -m` + c_kern=`uname -r` + + if [ -e "/var/lib/dkms/$NAME/$VERSION" ]; then + echo "Removing old $NAME-$VERSION DKMS files..." + dkms remove -m $NAME -v $VERSION --all + fi + + echo "Loading new $NAME-$VERSION DKMS files..." + #dkms add -m $NAME -v $VERSION + dkms ldtarball --archive "/usr/src/$NAME-$VERSION.dkms.tar.gz" + rm -f "/usr/src/$NAME-$VERSION.dkms.tar.gz" + + echo "Installing prebuilt kernel module binaries (if any)" + set +e + IFS=' +' + for kern in `dkms status -m $NAME -v $VERSION -a $c_arch | grep ": built" | awk {'print $3'} | sed 's/,$//'`; do + echo "Trying kernel: $kern" + dkms install --force -m $NAME -v $VERSION -k $kern -a $c_arch + done + unset IFS + set -e + + # If none installed, install. + ##if [ `dkms status -m $NAME -v $VERSION -k $c_kern -a $c_arch | grep -c ": installed"` -eq 0 ]; then + dkms_status=`dkms status -m $NAME -v $VERSION -k $c_kern -a $c_arch` + if [ `echo $dkms_status | grep -c ": installed"` -eq 0 ]; then + if [ `echo $c_kern | grep -c "BOOT"` -eq 0 ] && [ -e "/lib/modules/$c_kern/build/include" ]; then + # Only build if we need to. + if [ `echo $dkms_status | grep -c ": built"` -eq 0 ]; then + echo "Building module..." + dkms build -m $NAME -v $VERSION + fi + echo "Installing module..." + dkms install -m $NAME -v $VERSION + elif [ `echo $c_kern | grep -c "BOOT"` -gt 0 ]; then + echo -e "" + echo -e "Module build for the currently running kernel was skipped since you" + echo -e "are running a BOOT variant of the kernel." + else + echo -e "" + echo -e "Module build for the currently running kernel was skipped since the" + echo -e "kernel source for this kernel does not seem to be installed." + fi + fi + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# + +exit 0 + diff --git a/bcm5974-dkms-mkdeb/debian/postrm b/bcm5974-dkms-mkdeb/debian/postrm new file mode 100644 index 0000000..78221c8 --- /dev/null +++ b/bcm5974-dkms-mkdeb/debian/postrm @@ -0,0 +1,27 @@ +#!/bin/sh + +NAME=MODULE_NAME +VERSION=MODULE_VERSION + +set -e + +case "$1" in + purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + moddir="/lib/modules/`uname -r`/kernel/" + depmod + if [ -d "/usr/src/$NAME-$VERSION" ]; then + echo "Removing old module source..." + rm -rf "/usr/src/$NAME-$VERSION" + fi + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# + +exit 0 + diff --git a/bcm5974-dkms-mkdeb/debian/prerm b/bcm5974-dkms-mkdeb/debian/prerm new file mode 100644 index 0000000..f42d293 --- /dev/null +++ b/bcm5974-dkms-mkdeb/debian/prerm @@ -0,0 +1,28 @@ +#!/bin/sh + +NAME=MODULE_NAME +VERSION=MODULE_VERSION + +set -e + +case "$1" in + remove|upgrade|deconfigure) + if [ "`dkms status -m $NAME`" ]; then + dkms remove -m $NAME -v $VERSION --all + fi + ;; + + failed-upgrade) + ;; + + *) + echo "prerm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# + +exit 0 + + diff --git a/bcm5974-dkms-mkdeb/debian/rules b/bcm5974-dkms-mkdeb/debian/rules new file mode 100644 index 0000000..385aa1c --- /dev/null +++ b/bcm5974-dkms-mkdeb/debian/rules @@ -0,0 +1,53 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +NAME=MODULE_NAME +VERSION=MODULE_VERSION + +configure: configure-stamp +configure-stamp: + dh_testdir + touch configure-stamp + + +build: build-stamp + +build-stamp: configure-stamp + dh_testdir + $(MAKE) + touch $@ + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + -$(MAKE) clean + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + $(MAKE) DESTDIR=$(CURDIR)/debian/$(NAME)-dkms NAME=$(NAME) VERSION=$(VERSION) install + +binary-arch: build install + +binary-indep: build install + dh_testdir + dh_testroot + dh_link + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/bcm5974.c b/bcm5974.c index 68f92c5..a7f070f 100644 --- a/bcm5974.c +++ b/bcm5974.c @@ -9,7 +9,7 @@ * * The BCM5974 driver is based on the appletouch driver: * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net) + * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net) * Copyright (C) 2005 Stelian Pop (stelian@popies.net) * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de) * Copyright (C) 2005 Peter Osterlund (petero2@telia.com) @@ -40,650 +40,651 @@ #include #include -#define APPLE_VENDOR_ID 0x05AC +#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 +/* MacbookAir, aka wellspring */ +#define ATP_WELLSPRING_ANSI 0x0223 +#define ATP_WELLSPRING_ISO 0x0224 +#define ATP_WELLSPRING_JIS 0x0225 +/* MacbookProPenryn, aka wellspring2 */ +#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 = USB_INTERFACE_CLASS_HID, \ - .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE \ - } + .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 = USB_INTERFACE_CLASS_HID, \ + .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE \ +} /* 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 */ - {} + /* 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 */ -}; +MODULE_AUTHOR("Henrik Rydberg"); +MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver"); +MODULE_LICENSE("GPL"); -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; /* finger pressure limits */ - struct atp_params_t w; /* finger width limits */ +#define dprintk(level, format, a...)\ + { if (debug >= level) printk(KERN_DEBUG format, ##a); } + +static int debug = 1; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activate debugging output"); + +/* button data structure */ +struct bt_data { + u8 unknown1; /* constant */ + u8 button; /* left button */ + u8 rel_x; /* relative x coordinate */ + u8 rel_y; /* relative y coordinate */ }; /* 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 */ +struct tp_header { + u8 unknown1[16]; /* constants, timers, etc */ + u8 fingers; /* 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 */ +struct tp_finger { + __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]; +/* trackpad data structure, empirically at least ten fingers */ +struct tp_data { + struct tp_header header; + struct tp_finger 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}, - {0, 2048, 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}, - {0, 2048, 0, 256, 16, 0} - }, - {} +/* device-specific parameters */ +struct atp_params { + int dim; /* logical dimension */ + int devmin; /* device minimum reading */ + int devmax; /* device maximum reading */ }; -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; -} +/* device-specific configuration */ +struct atp_config { + 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 p; /* finger pressure limits */ + struct atp_params w; /* finger width limits */ + struct atp_params x; /* horizontal limits */ + struct atp_params y; /* vertical limits */ +}; -/* 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 +/* logical trackpad state */ +struct tp_state { + int pressure; /* finger pressure value */ + int width; /* finger width value */ + int abs_x; /* absolute x coordinate */ + int abs_y; /* absolute y coordinate */ + int fingers; /* number of fingers on trackpad */ +}; -#define dprintk(format, a...) \ - { if (debug) printk(KERN_DEBUG format, ##a); } +/* logical device structure */ +struct atp { + char phys[64]; + struct usb_device *udev; /* usb device */ + struct input_dev *input; /* input dev */ + struct atp_config cfg; /* device configuration */ + int open; /* >0: open, else closed */ + int suspended; /* >0: suspended, else open */ + struct urb *bt_urb; /* button usb request block */ + struct bt_data *bt_data; /* button transferred data */ + unsigned bt_prev_state; /* logical button previous state */ + unsigned bt_state; /* logical button state */ + struct urb *tp_urb; /* trackpad usb request block */ + struct tp_data *tp_data; /* trackpad transferred data */ + struct tp_state tp_state; /* logical trackpad state */ + unsigned tp_valid; /* trackpad sensors valid */ +}; -MODULE_AUTHOR("Henrik Rydberg"); -MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver"); -MODULE_LICENSE("GPL"); +/* logical dimensions */ +#define DIM_PRESSURE 256 /* maximum finger pressure */ +#define DIM_WIDTH 16 /* maximum finger width */ +#define DIM_X 1280 /* maximum trackpad x value */ +#define DIM_Y 800 /* maximum trackpad y value */ +#define SNRATIO 50 /* signal to noise ratio */ -static int debug = 1; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Activate debugging output"); +/* device constants */ +static const struct atp_config atp_config_table[] = { + { + ATP_WELLSPRING_ANSI, + ATP_WELLSPRING_ISO, + ATP_WELLSPRING_JIS, + 0x84, sizeof(struct bt_data), + 0x81, sizeof(struct tp_data), + { DIM_PRESSURE, 0, 256 }, + { DIM_WIDTH, 0, 2048 }, + { DIM_X, -4824, 5342 }, + { DIM_Y, -172, 5820 } + }, + { + ATP_WELLSPRING2_ANSI, + ATP_WELLSPRING2_ISO, + ATP_WELLSPRING2_JIS, + 0x84, sizeof(struct bt_data), + 0x81, sizeof(struct tp_data), + { DIM_PRESSURE, 0, 256 }, + { DIM_WIDTH, 0, 2048 }, + { DIM_X, -4824, 5342 }, + { DIM_Y, -172, 5820 } + }, + {} +}; -static int atp_wellspring_init(struct usb_device *udev) +/* return the device-specific configuration by device */ +static const struct atp_config *atp_product_config(struct usb_device *udev) { - const struct atp_config_t *cfg = atp_product_config(udev); - char *data = kmalloc(8, GFP_KERNEL); - 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; - } - - printk(KERN_INFO "bcm5974: Wellspring mode initialized.\n"); - - kfree(data); - return 0; - - error: - kfree(data); - return -EIO; + u16 id = le16_to_cpu(udev->descriptor.idProduct); + const struct atp_config *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; } -/* 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 */ - int open; /* >0: open, else closed */ - int suspended; /* >0: suspended, else open */ - struct urb *bt_urb; /* button usb request block */ - signed char *bt_data; /* button transferred data */ - 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 ? */ -}; - +/* convert 16-bit little endian to signed integer */ static inline int raw2int(__le16 x) { - return (short)le16_to_cpu(x); + return (signed short)le16_to_cpu(x); } -static inline int int2scale(const struct atp_params_t *p, int x) +/* scale device data to logical dimensions (asserts devmin < devmax) */ +static inline int int2scale(const struct atp_params *p, int x) { - return ((p->max-p->min)*x)/(p->devmax-p->devmin); + return x * p->dim / (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) +/* all logical value ranges are [0,dim). */ +static inline int int2bound(const struct atp_params *p, int x) { - int s = p->min+int2scale(p, x); - return s < p->min?p->min:s >= p->max?p->max-1:s; + int s = int2scale(p, x); + return s < 0 ? 0 : s >= p->dim ? p->dim - 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) +/* convert button data to logical button state */ +static int compute_bt_state(struct atp *dev, int size) { - if (size < 26 || (size-26)%28 != 0) - return -1; + if (size != sizeof(struct bt_data)) + return -EIO; - return 1; + dev->bt_prev_state = dev->bt_state; + dev->bt_state = dev->bt_data->button; + + return 0; } -/** - * 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, int *width, - struct atp_config_t *cfg, - const unsigned char *data, int size) +/* convert trackpad data to logical trackpad state */ +static int compute_tp_state(struct atp *dev, 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)); - *width = int2bound(&cfg->w, raw2int(tp->finger->size_major)); - } else { - *abs_x = 0; - *abs_y = 0; - *rel_x = 0; - *rel_y = 0; - *pressure = 0; - *width = 0; - } - - /* report zero fingers for zero pressure */ - return *pressure > 0?nfinger:0; + const struct atp_config *c = &dev->cfg; + const struct tp_finger *f = dev->tp_data->finger; + const int fingers = (size - 26) / 28; + struct tp_state *s = &dev->tp_state; + + if (size < 26 || (size - 26) % 28 != 0) + return -EIO; + + if (!fingers) { + s->pressure = 0; + s->fingers = 0; + return 0; + } + + dprintk(9, "bcm5974: p: %+05d w: %+05d x: %+05d y: %+05d f: %d\n", + raw2int(f->force_major), raw2int(f->size_major), + raw2int(f->abs_x), raw2int(f->abs_y), fingers); + + s->pressure = int2bound(&c->p, raw2int(f->force_major)); + s->width = int2bound(&c->w, raw2int(f->size_major)); + s->abs_x = int2bound(&c->x, raw2int(f->abs_x) - c->x.devmin); + s->abs_y = int2bound(&c->y, c->y.devmax - raw2int(f->abs_y)); + s->fingers = s->pressure > 0 ? fingers : 0; + + return 0; } -static void atp_button(struct urb *urb) +/* 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 + +static int atp_wellspring_init(struct atp *dev) { - struct atp *dev = urb->context; - int button, retval; - - switch (urb->status) { - case 0: - break; - case -EOVERFLOW: - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - dbg("bcm5974: button urb shutting down: %d", urb->status); - return; - default: - dbg("bcm5974: button urb status: %d", urb->status); - goto exit; - } - - /* drop incomplete datasets */ - if (dev->bt_urb->actual_length != 4) { - dprintk("bcm5974: incomplete button package (first byte: %d, length: %d)\n", - (int)dev->bt_data[0], dev->bt_urb->actual_length); - goto exit; - } - - button = dev->bt_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("bcm5974: button urb failed: %d", retval); + char *data = kmalloc(8, GFP_KERNEL); + int error = 0, size; + + if (!data) { + err("bcm5974: out of memory"); + error = -ENOMEM; + goto error; + } + + /* reset button endpoint */ + if (usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, + 0, dev->cfg.bt_ep, NULL, 0, 5000)) { + err("bcm5974: could not reset button endpoint"); + error = -EIO; + goto error; + } + + /* reset trackpad endpoint */ + if (usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, + 0, dev->cfg.tp_ep, NULL, 0, 5000)) { + err("bcm5974: could not reset trackpad endpoint"); + error = -EIO; + goto error; + } + + /* read configuration */ + size = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->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("bcm5974: could not read from device"); + error = -EIO; + 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(dev->udev, usb_sndctrlpipe(dev->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("bcm5974: could not write to device"); + error = -EIO; + goto error; + } + + dev->tp_valid = 0; + + printk(KERN_INFO "bcm5974: Wellspring mode initialized.\n"); + + kfree(data); + return 0; + +error: + kfree(data); + return error; } -static void atp_trackpad(struct urb *urb) +static void irq_button(struct urb *urb) { - struct atp *dev = urb->context; - int abs_x, abs_y, rel_x, rel_y, pressure, width; - int quality, nfinger, retval; - - switch (urb->status) { - case 0: - break; - case -EOVERFLOW: - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - dbg("bcm5974: trackpad urb shutting down: %d", urb->status); - return; - default: - dbg("bcm5974: trackpad urb status: %d", 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, &width, - &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 w: %3d\n", - abs_x, abs_y, rel_x, rel_y, pressure, width); - } - - input_report_abs(dev->input, ABS_X, abs_x); - input_report_abs(dev->input, ABS_Y, abs_y); - input_report_abs(dev->input, ABS_PRESSURE, pressure); - input_report_abs(dev->input, ABS_TOOL_WIDTH, width); - 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("bcm5974: trackpad urb failed: %d", retval); + struct atp *dev = urb->context; + int error; + + switch (urb->status) { + case 0: + break; + case -EOVERFLOW: + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + dbg("bcm5974: button urb shutting down: %d", urb->status); + return; + default: + dbg("bcm5974: button urb status: %d", urb->status); + goto exit; + } + + if (compute_bt_state(dev, dev->bt_urb->actual_length)) { + dprintk(1, "bcm5974: bad button package, length: %d)\n", + dev->bt_urb->actual_length); + goto exit; + } + + /* only report button state changes */ + if (dev->bt_state != dev->bt_prev_state) { + input_report_key(dev->input, BTN_LEFT, dev->bt_data->button); + input_sync(dev->input); + } + +exit: + error = usb_submit_urb(dev->bt_urb, GFP_ATOMIC); + if (error) + err("bcm5974: button urb failed: %d", error); } -static int atp_open(struct input_dev *input) +static void irq_trackpad(struct urb *urb) { - struct atp *dev = input_get_drvdata(input); - - 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; - } - - dev->open = 1; - dev->suspended = 0; - dev->tp_valid = 0; - - return 0; + struct atp *dev = urb->context; + struct tp_state *state = &dev->tp_state; + int error; + + switch (urb->status) { + case 0: + break; + case -EOVERFLOW: + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + dbg("bcm5974: trackpad urb shutting down: %d", urb->status); + return; + default: + dbg("bcm5974: trackpad urb status: %d", urb->status); + goto exit; + } + + /* first sample data ignored */ + if (!dev->tp_valid) { + dev->tp_valid = 1; + goto exit; + } + + if (compute_tp_state(dev, dev->tp_urb->actual_length)) { + dprintk(1, "bcm5974: bad trackpad package, length: %d)\n", + dev->tp_urb->actual_length); + goto exit; + } + + input_report_abs(dev->input, ABS_PRESSURE, state->pressure); + input_report_abs(dev->input, ABS_TOOL_WIDTH, state->width); + input_report_abs(dev->input, ABS_X, state->abs_x); + input_report_abs(dev->input, ABS_Y, state->abs_y); + input_report_key(dev->input, BTN_TOOL_FINGER, state->fingers == 1); + input_report_key(dev->input, BTN_TOOL_DOUBLETAP, state->fingers == 2); + input_report_key(dev->input, BTN_TOOL_TRIPLETAP, state->fingers > 2); + input_sync(dev->input); + +exit: + error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC); + if (error) + err("bcm5974: trackpad urb failed: %d", error); +} - err_free_bt_urb: - usb_free_urb(dev->bt_urb); - error: - return -EIO; +static int atp_open(struct input_dev *input) +{ + struct atp *dev = input_get_drvdata(input); + + 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; + } + + dev->open = 1; + dev->suspended = 0; + 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); + struct atp *dev = input_get_drvdata(input); - if (dev->open && !dev->suspended) { - usb_kill_urb(dev->tp_urb); - usb_kill_urb(dev->bt_urb); - } + usb_kill_urb(dev->tp_urb); + usb_kill_urb(dev->bt_urb); - dev->open = 0; - dev->suspended = 0; + dev->open = 0; + dev->suspended = 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; - - printk(KERN_INFO "bcm5974: Probing device...\n"); - - /* 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; - - 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); - input_set_abs_params(input_dev, ABS_TOOL_WIDTH, - cfg->w.min, cfg->w.max, cfg->w.fuzz, cfg->w.flat); - - set_bit(EV_KEY, input_dev->evbit); - 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; + struct usb_device *udev = interface_to_usbdev(iface); + const struct atp_config *cfg; + struct atp *dev; + struct input_dev *input_dev; + int error = 0; + + /* 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("bcm5974: out of memory"); + error = -ENOMEM; + goto err_free_devs; + } + + dev->udev = udev; + dev->input = input_dev; + dev->cfg = *cfg; + + /* switch to raw sensor mode */ + if (atp_wellspring_init(dev)) { + error = -EIO; + goto err_free_devs; + } + + dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->bt_urb) { + error = -ENOMEM; + goto err_free_devs; + } + + dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->tp_urb) { + error = -ENOMEM; + 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) { + error = -ENOMEM; + 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) { + error = -ENOMEM; + 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, + irq_button, dev, 1); + + usb_fill_int_urb(dev->tp_urb, udev, + usb_rcvintpipe(udev, cfg->tp_ep), + dev->tp_data, dev->cfg.tp_datalen, + irq_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_PRESSURE, + 0, cfg->p.dim, cfg->p.dim / SNRATIO, 0); + input_set_abs_params(input_dev, ABS_TOOL_WIDTH, + 0, cfg->w.dim, cfg->w.dim / SNRATIO, 0); + input_set_abs_params(input_dev, ABS_X, + 0, cfg->x.dim, cfg->x.dim / SNRATIO, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, cfg->y.dim, cfg->y.dim / SNRATIO, 0); + + set_bit(EV_KEY, input_dev->evbit); + 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 "bcm5974: disconnected\n"); + 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 "bcm5974: disconnected\n"); } static int atp_suspend(struct usb_interface *iface, pm_message_t message) { - struct atp *dev = usb_get_intfdata(iface); + struct atp *dev = usb_get_intfdata(iface); - if (dev->open && !dev->suspended++) { - usb_kill_urb(dev->tp_urb); - usb_kill_urb(dev->bt_urb); - } + if (dev) { + if (!dev->suspended) + atp_close(dev->input); + dev->suspended++; + } - return 0; + return 0; } static int atp_resume(struct usb_interface *iface) { - struct atp *dev = usb_get_intfdata(iface); - - if (dev->open && !--dev->suspended) { - 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->tp_valid = 0; - return 0; - - err_free_bt_urb: - usb_free_urb(dev->bt_urb); - error: - return -EIO; + struct atp *dev = usb_get_intfdata(iface); + int error = 0; + + if (dev && dev->suspended) { + if (!--dev->suspended) + error = atp_open(dev->input); + } + + return error; } static int atp_reset_resume(struct usb_interface *iface) { - struct atp *dev = usb_get_intfdata(iface); + struct atp *dev = usb_get_intfdata(iface); - if (dev->suspended == 1) { - if (atp_wellspring_init(dev->udev)) - printk(KERN_INFO "bcm5974: warning: reset failed\n"); - } + if (dev && atp_wellspring_init(dev)) + printk(KERN_INFO "bcm5974: warning: reset failed\n"); - return atp_resume(iface); + return atp_resume(iface); } static int atp_pre_reset(struct usb_interface *iface) { - return atp_suspend(iface, PMSG_ON); + return atp_suspend(iface, PMSG_ON); } static int atp_post_reset(struct usb_interface *iface) { - return atp_reset_resume(iface); + return atp_reset_resume(iface); } static struct usb_driver atp_driver = { - .name = "bcm5974", - .probe = atp_probe, - .disconnect = atp_disconnect, - .suspend = atp_suspend, - .resume = atp_resume, - .reset_resume = atp_reset_resume, - .pre_reset = atp_pre_reset, - .post_reset = atp_post_reset, - .id_table = atp_table, + .name = "bcm5974", + .probe = atp_probe, + .disconnect = atp_disconnect, + .suspend = atp_suspend, + .resume = atp_resume, + .reset_resume = atp_reset_resume, + .pre_reset = atp_pre_reset, + .post_reset = atp_post_reset, + .id_table = atp_table, }; static int __init atp_init(void) { - return usb_register(&atp_driver); + return usb_register(&atp_driver); } static void __exit atp_exit(void) { - usb_deregister(&atp_driver); + usb_deregister(&atp_driver); } module_init(atp_init); module_exit(atp_exit); + diff --git a/dkms.conf b/dkms.conf new file mode 100644 index 0000000..89d112e --- /dev/null +++ b/dkms.conf @@ -0,0 +1,8 @@ +PACKAGE_NAME="bcm5974" +PACKAGE_VERSION="0.5" +MAKE[0]="make -C ${kernel_source_dir} + SUBDIRS=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build modules" +BUILT_MODULE_NAME[0]="bcm5974" +DEST_MODULE_LOCATION[0]="/kernel/drivers/input/mouse" +POST_INSTALL="scripts/bcm5974-post-install" +POST_REMOVE="scripts/bcm5974-post-remove" diff --git a/etc/modprobe.d/bcm5974 b/etc/modprobe.d/bcm5974 new file mode 100644 index 0000000..81cd5ce --- /dev/null +++ b/etc/modprobe.d/bcm5974 @@ -0,0 +1,10 @@ +### Options for the bcm5974 driver + +## quirks for the Macbook Air +#options usbhid quirks=0x05ac:0x0223:0x00020800,0x05ac:0x0224:0x00024800,0x05ac:0x0225:0x00020800 + +## quirks for the Macbook Pro Penryn +#options usbhid quirks=0x05ac:0x0230:0x00020800,0x05ac:0x0231:0x00024800,0x05ac:0x0232:0x00020800 + +## Debug level - uncomment this to get raw packets in /var/log/debug +#options bcm5974 debug=99 diff --git a/scripts/bcm5974-diagnostics b/scripts/bcm5974-diagnostics new file mode 100755 index 0000000..0322158 --- /dev/null +++ b/scripts/bcm5974-diagnostics @@ -0,0 +1,241 @@ +#!/bin/bash +# +# bcm5943-diagnostics - troubleshooting the BCM5974 setup +# +# Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) +# +# 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. +# + +DRIVER=bcm5974 + +echo ----------------------------------------------------------------------- + +# What kernel version am I running? +KERNEL=`uname -r` +echo \* Kernel version: $KERNEL + +# What synaptics version is installed (debian)? +if [ -x /usr/bin/dpkg-query ]; then + SYNAPTICS=`/usr/bin/dpkg-query -W -f='${Version}\n' xserver-xorg-input-synaptics` + echo \* Synaptics version: $SYNAPTICS +else + echo \* Synaptics version: UNKNOWN - dpkg not found +fi + +# What USB devices of interest can we find? +if [ -x /usr/sbin/lsusb ]; then + LSUSB=`lsusb|grep 05ac:02` + echo \* USB device: $LSUSB +else + echo \* USB device: UNKNOWN - lsusb not found +fi + +# Do we have the module intalled? +MODLIB=/lib/modules/$KERNEL/kernel/drivers/input/mouse/$DRIVER.ko +MODLIB2=/lib/modules/$KERNEL/updates/dkms/$DRIVER.ko +if [ -f $MODLIB ]; then + echo \* $MODLIB: exists +elif [ -f $MODLIB2 ]; then + echo \* $MODLIB2: exists +else + echo \* $DRIVER does NOT exist + echo dkms: install package + echo manual: copy the file $DRIVER.ko to $MODLIB + exit 1 +fi + +# Do we have the module configured? +MODULES=/etc/modules +if [ -f $MODULES ]; then + if (grep -q $DRIVER $MODULES); then + echo \* $MODULES: has $DRIVER config, please remove + exit 1 + else + echo \* $MODULES: $DRIVER no longer explicitly listed, good + fi +fi + +# Do we have old quirks? +MODPROBE=/etc/modprobe.d/options +if [ -f $MODPROBE ]; then + MODPROBEOPTS=`grep quirks= $MODPROBE|grep -v ^#|grep 0x05ac:0x02` + if [ "$MODPROBEOPTS" != "" ]; then + echo \* $MODPROBE: has obsolete quirks - please remove + exit 1 + else + echo \* $MODPROBE: no obsolete quirks, good + fi +else + echo \* $MODPROBE: file not found +fi + +# Do we have the right quirks? +DRIVMOD=/etc/modprobe.d/$DRIVER +if [ -f $DRIVMOD ]; then + DRIVMODOPTS=`grep quirks= $DRIVMOD|grep -v ^#|grep 0x05ac:0x02` + if [ "$DRIVMODOPTS" != "" ]; then + echo \* $DRIVMOD: has quirks, good + else + echo \* $DRIVMOD: does NOT have quirks + echo Uncomment the right quirk line in $DRIVMOD + exit 1 + fi +else + echo \* $DRIVMOD: file not found + echo Copy the etc/modprobe.d/bcm5974 file to /etc/modprobe.d/ + exit 1 +fi + +# Did the depmod bite? +USBMAP=/lib/modules/$KERNEL/modules.usbmap +if [ -f $USBMAP ]; then + typeset -i nusb=`grep $DRIVER $USBMAP|grep 0x05ac|grep 0x02|wc -l` + if [ $nusb -gt 0 ]; then + echo \* $USBMAP: maps to $DRIVER, good + else + echo \* $USBMAP: does NOT map to $DRIVER + echo Run depmod -a as root + exit 1 + fi +fi + +# Was the $DRIVER module loaded? +DMESG=`lsmod|grep $DRIVER` +if [ "$DMESG" != "" ]; then + echo \* $DRIVER: module is loaded +else + echo \* $DRIVER: module is NOT loaded + echo Add this line to /etc/modules: + echo "$DRIVER" + exit 1 +fi + +# Was the $DRIVER module registered? +DEVPATH=/proc/bus/input/devices +if [ -f $DEVPATH ]; then + DEVICES=`grep $DRIVER /proc/bus/input/devices` + if [ "$DEVICES" != "" ]; then + echo \* $DEVPATH: module is registered + else + echo \* $DEVPATH: module is NOT registered + echo No help on this one... + exit 1 + fi +else + echo \* $DEVPATH: does not exist, ignore +fi + +# Was the $DRIVER properly initialized? +WELLSPRING=`dmesg|grep $DRIVER|grep Wellspring` +if [ "$WELLSPRING" != "" ]; then + echo \* $DRIVER: Wellspring was initialized +else + echo \* $DRIVER: Wellspring was NOT initialized + exit 1 +fi + +# Do we have synaptics working properly? +typeset -i SYNLINES=`synclient -l|wc -l` +if [ $SYNLINES -gt 10 ]; then + echo \* synaptics: configuration readable +else + echo \* synaptics: configuration NOT readable + exit 1 +fi + +# Do we have a working synaptics configuration? +DIFF=/tmp/synclient-output.diff.$$ +TMP=/tmp/synclient-output.txt.$$ +synclient -l | grep -v Parameter | sort > $TMP +diff -u - $TMP > $DIFF < $ETCPATH + +exit 0 diff --git a/scripts/bcm5974-post-remove b/scripts/bcm5974-post-remove new file mode 100755 index 0000000..21977de --- /dev/null +++ b/scripts/bcm5974-post-remove @@ -0,0 +1,29 @@ +#!/bin/bash +# +# bcm5943-post-remove - post-removing the BCM5974 +# +# Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) +# +# 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. +# + +DRIVER=bcm5974 +ETCPATH=/etc/modprobe.d/$DRIVER + +echo ----------------------------------------------------------------------- + +rm -f $ETCPATH + +exit 0 -- cgit v1.2.3