summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--INSTALL17
-rw-r--r--README14
-rw-r--r--bcm5974-dkms-mkdeb/Makefile10
-rw-r--r--bcm5974-dkms-mkdeb/debian/README.Debian10
-rw-r--r--bcm5974-dkms-mkdeb/debian/changelog6
-rw-r--r--bcm5974-dkms-mkdeb/debian/compat1
-rw-r--r--bcm5974-dkms-mkdeb/debian/control11
-rw-r--r--bcm5974-dkms-mkdeb/debian/copyright4
-rw-r--r--bcm5974-dkms-mkdeb/debian/dirs2
-rw-r--r--bcm5974-dkms-mkdeb/debian/postinst71
-rw-r--r--bcm5974-dkms-mkdeb/debian/postrm27
-rw-r--r--bcm5974-dkms-mkdeb/debian/prerm28
-rw-r--r--bcm5974-dkms-mkdeb/debian/rules53
-rw-r--r--bcm5974.c1079
-rw-r--r--dkms.conf8
-rw-r--r--etc/modprobe.d/bcm597410
-rwxr-xr-xscripts/bcm5974-diagnostics241
-rwxr-xr-xscripts/bcm5974-post-install50
-rwxr-xr-xscripts/bcm5974-post-remove29
19 files changed, 1132 insertions, 539 deletions
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..7c7e975
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,17 @@
1Installation instructions bcm5974
2----------------------------------
3
4Standing in the source directory, do:
5
61. make -C /lib/modules/`uname -r`/build M=$PWD modules
7
82. sudo cp bcm5974.ko /lib/modules/`uname -r`/kernel/drivers/input/mouse/
9
103. sudo ./scripts/bcm5974-post-install
11
124. reboot (only if bcm5974 never was used before)
13
14For trouble-shooting, try running
15
16 ./scripts/bcm5974-diagnostics
17
diff --git a/README b/README
new file mode 100644
index 0000000..898826a
--- /dev/null
+++ b/README
@@ -0,0 +1,14 @@
1Apple USB BCM5974 Multitouch trackpad support
2---------------------------------------------
3
4This driver adds support for the multitouch trackpad on the new
5Apple Macbook Air and Macbook Pro Penryn laptops. It replaces the
6appletouch driver on those computers, and integrates well with the
7synaptics driver of the Xorg system.
8
9Authors
10-------
11
12Written by Henrik Rydberg (rydberg@euromail.se); see bcm5974.c for the
13complete list of credits.
14
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 @@
1SRC = $(DESTDIR)/usr/src
2
3all:
4
5clean:
6
7install:
8 install -d "$(SRC)"
9 install -m 644 $(NAME)-$(VERSION).dkms.tar.gz "$(SRC)"
10
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 @@
1MODULE_NAME DKMS module for Debian
2
3This driver adds support for the multitouch trackpad on the new
4Apple Macbook Air and Macbook Pro Penryn laptops. It replaces the
5appletouch driver on those computers, and integrates well with the
6synaptics driver of the Xorg system.
7
8This package was automatically generated by the DKMS system,
9for distribution on Debian based operating systems.
10
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 @@
1MODULE_NAME-dkms (MODULE_VERSION) unstable; urgency=low
2
3 * Automatically packaged by DKMS.
4
5 -- Henrik Rydberg <rydberg@euromail.se> DATE_STAMP
6
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 @@
1Source: MODULE_NAME-dkms
2Section: linux-input
3Priority: optional
4Maintainer: Henrik Rydberg <rydberg@euromail.se>
5Build-Depends: debhelper (>= 5)
6Standards-Version: 3.7.2
7
8Package: MODULE_NAME-dkms
9Architecture: all
10Depends: dkms (>= 1.95), bash (>> 1.99)
11Description: 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 @@
1GNU GENERAL PUBLIC LICENSE Version 2
2
3See the LICENSE file of this distribution for details.
4
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 @@
1usr/src
2etc/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 @@
1#!/bin/sh
2
3set -e
4
5NAME=MODULE_NAME
6VERSION=MODULE_VERSION
7
8case "$1" in
9 configure)
10 # Determine current arch / kernel
11 c_arch=`uname -m`
12 c_kern=`uname -r`
13
14 if [ -e "/var/lib/dkms/$NAME/$VERSION" ]; then
15 echo "Removing old $NAME-$VERSION DKMS files..."
16 dkms remove -m $NAME -v $VERSION --all
17 fi
18
19 echo "Loading new $NAME-$VERSION DKMS files..."
20 #dkms add -m $NAME -v $VERSION
21 dkms ldtarball --archive "/usr/src/$NAME-$VERSION.dkms.tar.gz"
22 rm -f "/usr/src/$NAME-$VERSION.dkms.tar.gz"
23
24 echo "Installing prebuilt kernel module binaries (if any)"
25 set +e
26 IFS='
27'
28 for kern in `dkms status -m $NAME -v $VERSION -a $c_arch | grep ": built" | awk {'print $3'} | sed 's/,$//'`; do
29 echo "Trying kernel: $kern"
30 dkms install --force -m $NAME -v $VERSION -k $kern -a $c_arch
31 done
32 unset IFS
33 set -e
34
35 # If none installed, install.
36 ##if [ `dkms status -m $NAME -v $VERSION -k $c_kern -a $c_arch | grep -c ": installed"` -eq 0 ]; then
37 dkms_status=`dkms status -m $NAME -v $VERSION -k $c_kern -a $c_arch`
38 if [ `echo $dkms_status | grep -c ": installed"` -eq 0 ]; then
39 if [ `echo $c_kern | grep -c "BOOT"` -eq 0 ] && [ -e "/lib/modules/$c_kern/build/include" ]; then
40 # Only build if we need to.
41 if [ `echo $dkms_status | grep -c ": built"` -eq 0 ]; then
42 echo "Building module..."
43 dkms build -m $NAME -v $VERSION
44 fi
45 echo "Installing module..."
46 dkms install -m $NAME -v $VERSION
47 elif [ `echo $c_kern | grep -c "BOOT"` -gt 0 ]; then
48 echo -e ""
49 echo -e "Module build for the currently running kernel was skipped since you"
50 echo -e "are running a BOOT variant of the kernel."
51 else
52 echo -e ""
53 echo -e "Module build for the currently running kernel was skipped since the"
54 echo -e "kernel source for this kernel does not seem to be installed."
55 fi
56 fi
57 ;;
58
59 abort-upgrade|abort-remove|abort-deconfigure)
60 ;;
61
62 *)
63 echo "postinst called with unknown argument \`$1'" >&2
64 exit 1
65 ;;
66esac
67
68#DEBHELPER#
69
70exit 0
71
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 @@
1#!/bin/sh
2
3NAME=MODULE_NAME
4VERSION=MODULE_VERSION
5
6set -e
7
8case "$1" in
9 purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
10 moddir="/lib/modules/`uname -r`/kernel/"
11 depmod
12 if [ -d "/usr/src/$NAME-$VERSION" ]; then
13 echo "Removing old module source..."
14 rm -rf "/usr/src/$NAME-$VERSION"
15 fi
16 ;;
17
18 *)
19 echo "postrm called with unknown argument \`$1'" >&2
20 exit 1
21 ;;
22esac
23
24#DEBHELPER#
25
26exit 0
27
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 @@
1#!/bin/sh
2
3NAME=MODULE_NAME
4VERSION=MODULE_VERSION
5
6set -e
7
8case "$1" in
9 remove|upgrade|deconfigure)
10 if [ "`dkms status -m $NAME`" ]; then
11 dkms remove -m $NAME -v $VERSION --all
12 fi
13 ;;
14
15 failed-upgrade)
16 ;;
17
18 *)
19 echo "prerm called with unknown argument \`$1'" >&2
20 exit 1
21 ;;
22esac
23
24#DEBHELPER#
25
26exit 0
27
28
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 @@
1#!/usr/bin/make -f
2# -*- makefile -*-
3
4# Uncomment this to turn on verbose mode.
5#export DH_VERBOSE=1
6
7NAME=MODULE_NAME
8VERSION=MODULE_VERSION
9
10configure: configure-stamp
11configure-stamp:
12 dh_testdir
13 touch configure-stamp
14
15
16build: build-stamp
17
18build-stamp: configure-stamp
19 dh_testdir
20 $(MAKE)
21 touch $@
22
23clean:
24 dh_testdir
25 dh_testroot
26 rm -f build-stamp configure-stamp
27 -$(MAKE) clean
28 dh_clean
29
30install: build
31 dh_testdir
32 dh_testroot
33 dh_clean -k
34 dh_installdirs
35 $(MAKE) DESTDIR=$(CURDIR)/debian/$(NAME)-dkms NAME=$(NAME) VERSION=$(VERSION) install
36
37binary-arch: build install
38
39binary-indep: build install
40 dh_testdir
41 dh_testroot
42 dh_link
43 dh_strip
44 dh_compress
45 dh_fixperms
46 dh_installdeb
47 dh_shlibdeps
48 dh_gencontrol
49 dh_md5sums
50 dh_builddeb
51
52binary: binary-indep binary-arch
53.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 @@
9 * 9 *
10 * The BCM5974 driver is based on the appletouch driver: 10 * The BCM5974 driver is based on the appletouch driver:
11 * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) 11 * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
12 * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net) 12 * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
13 * Copyright (C) 2005 Stelian Pop (stelian@popies.net) 13 * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
14 * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de) 14 * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
15 * Copyright (C) 2005 Peter Osterlund (petero2@telia.com) 15 * Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
@@ -40,650 +40,651 @@
40#include <linux/usb/input.h> 40#include <linux/usb/input.h>
41#include <linux/hid.h> 41#include <linux/hid.h>
42 42
43#define APPLE_VENDOR_ID 0x05AC 43#define APPLE_VENDOR_ID 0x05AC
44 44
45/* MacbookAir BCM5974, aka wellspring */ 45/* MacbookAir, aka wellspring */
46 46#define ATP_WELLSPRING_ANSI 0x0223
47#define ATP_WELLSPRING_ANSI 0x0223 47#define ATP_WELLSPRING_ISO 0x0224
48#define ATP_WELLSPRING_ISO 0x0224 48#define ATP_WELLSPRING_JIS 0x0225
49#define ATP_WELLSPRING_JIS 0x0225 49/* MacbookProPenryn, aka wellspring2 */
50#define ATP_WELLSPRING2_ANSI 0x0230 50#define ATP_WELLSPRING2_ANSI 0x0230
51#define ATP_WELLSPRING2_ISO 0x0231 51#define ATP_WELLSPRING2_ISO 0x0231
52#define ATP_WELLSPRING2_JIS 0x0232 52#define ATP_WELLSPRING2_JIS 0x0232
53 53
54#define ATP_DEVICE(prod) { \ 54#define ATP_DEVICE(prod) { \
55 .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \ 55 .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \
56 USB_DEVICE_ID_MATCH_INT_CLASS | \ 56 USB_DEVICE_ID_MATCH_INT_CLASS | \
57 USB_DEVICE_ID_MATCH_INT_PROTOCOL), \ 57 USB_DEVICE_ID_MATCH_INT_PROTOCOL), \
58 .idVendor = APPLE_VENDOR_ID, \ 58 .idVendor = APPLE_VENDOR_ID, \
59 .idProduct = (prod), \ 59 .idProduct = (prod), \
60 .bInterfaceClass = USB_INTERFACE_CLASS_HID, \ 60 .bInterfaceClass = USB_INTERFACE_CLASS_HID, \
61 .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE \ 61 .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE \
62 } 62}
63 63
64/* table of devices that work with this driver */ 64/* table of devices that work with this driver */
65static const struct usb_device_id atp_table [] = { 65static const struct usb_device_id atp_table [] = {
66 /* MacbookAir1.1 */ 66 /* MacbookAir1.1 */
67 ATP_DEVICE(ATP_WELLSPRING_ANSI), 67 ATP_DEVICE(ATP_WELLSPRING_ANSI),
68 ATP_DEVICE(ATP_WELLSPRING_ISO), 68 ATP_DEVICE(ATP_WELLSPRING_ISO),
69 ATP_DEVICE(ATP_WELLSPRING_JIS), 69 ATP_DEVICE(ATP_WELLSPRING_JIS),
70 70 /* MacbookProPenryn */
71 /* MacbookProPenryn */ 71 ATP_DEVICE(ATP_WELLSPRING2_ANSI),
72 ATP_DEVICE(ATP_WELLSPRING2_ANSI), 72 ATP_DEVICE(ATP_WELLSPRING2_ISO),
73 ATP_DEVICE(ATP_WELLSPRING2_ISO), 73 ATP_DEVICE(ATP_WELLSPRING2_JIS),
74 ATP_DEVICE(ATP_WELLSPRING2_JIS), 74 /* Terminating entry */
75 75 {}
76 /* Terminating entry */
77 {}
78}; 76};
79MODULE_DEVICE_TABLE(usb, atp_table); 77MODULE_DEVICE_TABLE(usb, atp_table);
80 78
81struct atp_params_t { 79MODULE_AUTHOR("Henrik Rydberg");
82 int devmin; /* device minimum reading */ 80MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver");
83 int devmax; /* device maximum reading */ 81MODULE_LICENSE("GPL");
84 int min; /* logical minimum reading */
85 int max; /* logical maximum reading */
86 int fuzz; /* reading noise value */
87 int flat; /* zero */
88};
89 82
90struct atp_config_t { 83#define dprintk(level, format, a...)\
91 int ansi, iso, jis; /* the product id of this device */ 84 { if (debug >= level) printk(KERN_DEBUG format, ##a); }
92 int bt_ep; /* the endpoint of the button interface */ 85
93 int bt_datalen; /* data length of the button interface */ 86static int debug = 1;
94 int tp_ep; /* the endpoint of the trackpad interface */ 87module_param(debug, int, 0644);
95 int tp_datalen; /* data length of the trackpad interface */ 88MODULE_PARM_DESC(debug, "Activate debugging output");
96 struct atp_params_t x; /* horizontal limits */ 89
97 struct atp_params_t y; /* vertical limits */ 90/* button data structure */
98 struct atp_params_t p; /* finger pressure limits */ 91struct bt_data {
99 struct atp_params_t w; /* finger width limits */ 92 u8 unknown1; /* constant */
93 u8 button; /* left button */
94 u8 rel_x; /* relative x coordinate */
95 u8 rel_y; /* relative y coordinate */
100}; 96};
101 97
102/* trackpad header structure */ 98/* trackpad header structure */
103struct tp_header_t { 99struct tp_header {
104 u8 unknown1[16]; /* constants, timers, etc */ 100 u8 unknown1[16]; /* constants, timers, etc */
105 u8 nfinger; /* number of fingers on trackpad */ 101 u8 fingers; /* number of fingers on trackpad */
106 u8 unknown2[9]; /* constants, timers, etc */ 102 u8 unknown2[9]; /* constants, timers, etc */
107}; 103};
108 104
109/* trackpad finger structure */ 105/* trackpad finger structure */
110struct tp_finger_t { 106struct tp_finger {
111 __le16 origin; /* left/right origin? */ 107 __le16 origin; /* left/right origin? */
112 __le16 abs_x; /* absolute x coodinate */ 108 __le16 abs_x; /* absolute x coodinate */
113 __le16 abs_y; /* absolute y coodinate */ 109 __le16 abs_y; /* absolute y coodinate */
114 __le16 rel_x; /* relative x coodinate */ 110 __le16 rel_x; /* relative x coodinate */
115 __le16 rel_y; /* relative y coodinate */ 111 __le16 rel_y; /* relative y coodinate */
116 __le16 size_major; /* finger size, major axis? */ 112 __le16 size_major; /* finger size, major axis? */
117 __le16 size_minor; /* finger size, minor axis? */ 113 __le16 size_minor; /* finger size, minor axis? */
118 __le16 orientation; /* 16384 when point, else 15 bit angle */ 114 __le16 orientation; /* 16384 when point, else 15 bit angle */
119 __le16 force_major; /* trackpad force, major axis? */ 115 __le16 force_major; /* trackpad force, major axis? */
120 __le16 force_minor; /* trackpad force, minor axis? */ 116 __le16 force_minor; /* trackpad force, minor axis? */
121 __le16 unused[3]; /* zeros */ 117 __le16 unused[3]; /* zeros */
122 __le16 multi; /* one finger: varies, more fingers: constant */ 118 __le16 multi; /* one finger: varies, more fingers: constant */
123}; 119};
124 120
125/* trackpad data structure */ 121/* trackpad data structure, empirically at least ten fingers */
126struct tp_data_t { 122struct tp_data {
127 struct tp_header_t header; 123 struct tp_header header;
128 struct tp_finger_t finger[16]; 124 struct tp_finger finger[16];
129}; 125};
130 126
131/* device constants */ 127/* device-specific parameters */
132static const struct atp_config_t atp_config_table[] = { 128struct atp_params {
133 { 129 int dim; /* logical dimension */
134 ATP_WELLSPRING_ANSI, 130 int devmin; /* device minimum reading */
135 ATP_WELLSPRING_ISO, 131 int devmax; /* device maximum reading */
136 ATP_WELLSPRING_JIS,
137 0x84, 4,
138 0x81, sizeof(struct tp_data_t),
139 {-4824, 5342, 0, 1280, 16, 0},
140 {-172, 5820, 0, 800, 16, 0},
141 {0, 256, 0, 256, 16, 0},
142 {0, 2048, 0, 256, 16, 0}
143 },
144 {
145 ATP_WELLSPRING2_ANSI,
146 ATP_WELLSPRING2_ISO,
147 ATP_WELLSPRING2_JIS,
148 0x84, 4,
149 0x81, sizeof(struct tp_data_t),
150 {-4824, 5342, 0, 1280, 16, 0},
151 {-172, 5820, 0, 800, 16, 0},
152 {0, 256, 0, 256, 16, 0},
153 {0, 2048, 0, 256, 16, 0}
154 },
155 {}
156}; 132};
157 133
158static inline 134/* device-specific configuration */
159const struct atp_config_t *atp_product_config(struct usb_device *udev) 135struct atp_config {
160{ 136 int ansi, iso, jis; /* the product id of this device */
161 u16 id = le16_to_cpu(udev->descriptor.idProduct); 137 int bt_ep; /* the endpoint of the button interface */
162 const struct atp_config_t *config; 138 int bt_datalen; /* data length of the button interface */
163 for (config = atp_config_table; config->ansi; ++config) 139 int tp_ep; /* the endpoint of the trackpad interface */
164 if (config->ansi == id || config->iso == id || config->jis == id) 140 int tp_datalen; /* data length of the trackpad interface */
165 return config; 141 struct atp_params p; /* finger pressure limits */
166 return atp_config_table; 142 struct atp_params w; /* finger width limits */
167} 143 struct atp_params x; /* horizontal limits */
144 struct atp_params y; /* vertical limits */
145};
168 146
169/* Wellspring initialization constants */ 147/* logical trackpad state */
170#define ATP_WELLSPRING_MODE_READ_REQUEST_ID 1 148struct tp_state {
171#define ATP_WELLSPRING_MODE_WRITE_REQUEST_ID 9 149 int pressure; /* finger pressure value */
172#define ATP_WELLSPRING_MODE_REQUEST_VALUE 0x300 150 int width; /* finger width value */
173#define ATP_WELLSPRING_MODE_REQUEST_INDEX 0 151 int abs_x; /* absolute x coordinate */
174#define ATP_WELLSPRING_MODE_VENDOR_VALUE_1 0x01 152 int abs_y; /* absolute y coordinate */
175#define ATP_WELLSPRING_MODE_VENDOR_VALUE_2 0x05 153 int fingers; /* number of fingers on trackpad */
154};
176 155
177#define dprintk(format, a...) \ 156/* logical device structure */
178 { if (debug) printk(KERN_DEBUG format, ##a); } 157struct atp {
158 char phys[64];
159 struct usb_device *udev; /* usb device */
160 struct input_dev *input; /* input dev */
161 struct atp_config cfg; /* device configuration */
162 int open; /* >0: open, else closed */
163 int suspended; /* >0: suspended, else open */
164 struct urb *bt_urb; /* button usb request block */
165 struct bt_data *bt_data; /* button transferred data */
166 unsigned bt_prev_state; /* logical button previous state */
167 unsigned bt_state; /* logical button state */
168 struct urb *tp_urb; /* trackpad usb request block */
169 struct tp_data *tp_data; /* trackpad transferred data */
170 struct tp_state tp_state; /* logical trackpad state */
171 unsigned tp_valid; /* trackpad sensors valid */
172};
179 173
180MODULE_AUTHOR("Henrik Rydberg"); 174/* logical dimensions */
181MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver"); 175#define DIM_PRESSURE 256 /* maximum finger pressure */
182MODULE_LICENSE("GPL"); 176#define DIM_WIDTH 16 /* maximum finger width */
177#define DIM_X 1280 /* maximum trackpad x value */
178#define DIM_Y 800 /* maximum trackpad y value */
179#define SNRATIO 50 /* signal to noise ratio */
183 180
184static int debug = 1; 181/* device constants */
185module_param(debug, int, 0644); 182static const struct atp_config atp_config_table[] = {
186MODULE_PARM_DESC(debug, "Activate debugging output"); 183 {
184 ATP_WELLSPRING_ANSI,
185 ATP_WELLSPRING_ISO,
186 ATP_WELLSPRING_JIS,
187 0x84, sizeof(struct bt_data),
188 0x81, sizeof(struct tp_data),
189 { DIM_PRESSURE, 0, 256 },
190 { DIM_WIDTH, 0, 2048 },
191 { DIM_X, -4824, 5342 },
192 { DIM_Y, -172, 5820 }
193 },
194 {
195 ATP_WELLSPRING2_ANSI,
196 ATP_WELLSPRING2_ISO,
197 ATP_WELLSPRING2_JIS,
198 0x84, sizeof(struct bt_data),
199 0x81, sizeof(struct tp_data),
200 { DIM_PRESSURE, 0, 256 },
201 { DIM_WIDTH, 0, 2048 },
202 { DIM_X, -4824, 5342 },
203 { DIM_Y, -172, 5820 }
204 },
205 {}
206};
187 207
188static int atp_wellspring_init(struct usb_device *udev) 208/* return the device-specific configuration by device */
209static const struct atp_config *atp_product_config(struct usb_device *udev)
189{ 210{
190 const struct atp_config_t *cfg = atp_product_config(udev); 211 u16 id = le16_to_cpu(udev->descriptor.idProduct);
191 char *data = kmalloc(8, GFP_KERNEL); 212 const struct atp_config *config;
192 int size; 213 for (config = atp_config_table; config->ansi; ++config)
193 214 if (config->ansi == id || config->iso == id ||
194 /* reset button endpoint */ 215 config->jis == id)
195 if (usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 216 return config;
196 USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 217 return atp_config_table;
197 0, cfg->bt_ep, NULL, 0, 5000)) {
198 err("Could not reset button endpoint");
199 goto error;
200 }
201
202 /* reset trackpad endpoint */
203 if (usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
204 USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
205 0, cfg->tp_ep, NULL, 0, 5000)) {
206 err("Could not reset trackpad endpoint");
207 goto error;
208 }
209
210 /* read configuration */
211 size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
212 ATP_WELLSPRING_MODE_READ_REQUEST_ID,
213 USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
214 ATP_WELLSPRING_MODE_REQUEST_VALUE,
215 ATP_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
216
217 if (size != 8) {
218 err("Could not do mode read request from device (Wellspring Raw mode)");
219 goto error;
220 }
221
222 /* apply the mode switch */
223 data[0] = ATP_WELLSPRING_MODE_VENDOR_VALUE_1;
224 data[1] = ATP_WELLSPRING_MODE_VENDOR_VALUE_2;
225
226 /* write configuration */
227 size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
228 ATP_WELLSPRING_MODE_WRITE_REQUEST_ID,
229 USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
230 ATP_WELLSPRING_MODE_REQUEST_VALUE,
231 ATP_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
232
233 if (size != 8) {
234 err("Could not do mode write request to device (Wellspring Raw mode)");
235 goto error;
236 }
237
238 printk(KERN_INFO "bcm5974: Wellspring mode initialized.\n");
239
240 kfree(data);
241 return 0;
242
243 error:
244 kfree(data);
245 return -EIO;
246} 218}
247 219
248/* Structure to hold all of our device specific stuff */ 220/* convert 16-bit little endian to signed integer */
249struct atp {
250 char phys[64];
251 struct usb_device *udev; /* usb device */
252 struct input_dev *input; /* input dev */
253 struct atp_config_t cfg; /* device configuration */
254 int open; /* >0: open, else closed */
255 int suspended; /* >0: suspended, else open */
256 struct urb *bt_urb; /* button usb request block */
257 signed char *bt_data; /* button transferred data */
258 unsigned bt_state; /* current button state */
259 struct urb *tp_urb; /* trackpad usb request block */
260 signed char *tp_data; /* trackpad transferred data */
261 unsigned tp_valid; /* are the trackpad sensors valid ? */
262};
263
264static inline int raw2int(__le16 x) 221static inline int raw2int(__le16 x)
265{ 222{
266 return (short)le16_to_cpu(x); 223 return (signed short)le16_to_cpu(x);
267} 224}
268 225
269static inline int int2scale(const struct atp_params_t *p, int x) 226/* scale device data to logical dimensions (asserts devmin < devmax) */
227static inline int int2scale(const struct atp_params *p, int x)
270{ 228{
271 return ((p->max-p->min)*x)/(p->devmax-p->devmin); 229 return x * p->dim / (p->devmax - p->devmin);
272} 230}
273 231
274/** 232/* all logical value ranges are [0,dim). */
275 * all value ranges, both device and logical, are assumed to be [a,b). 233static inline int int2bound(const struct atp_params *p, int x)
276 */
277static inline int int2bound(const struct atp_params_t *p, int x)
278{ 234{
279 int s = p->min+int2scale(p, x); 235 int s = int2scale(p, x);
280 return s < p->min?p->min:s >= p->max?p->max-1:s; 236 return s < 0 ? 0 : s >= p->dim ? p->dim - 1 : s;
281} 237}
282 238
283/** 239/* convert button data to logical button state */
284 * check quality of reading. 240static int compute_bt_state(struct atp *dev, int size)
285 * -1: bad data
286 * 0: ignore this reading
287 * 1: good reading
288 */
289static int compute_quality(const unsigned char *data, int size)
290{ 241{
291 if (size < 26 || (size-26)%28 != 0) 242 if (size != sizeof(struct bt_data))
292 return -1; 243 return -EIO;
293 244
294 return 1; 245 dev->bt_prev_state = dev->bt_state;
246 dev->bt_state = dev->bt_data->button;
247
248 return 0;
295} 249}
296 250
297/** 251/* convert trackpad data to logical trackpad state */
298 * convert raw data to synaptics sensor output. 252static int compute_tp_state(struct atp *dev, int size)
299 * returns the number of fingers on the trackpad,
300 * or a negative number in vase of bad data.
301 */
302static int compute_movement(int *abs_x, int *abs_y,
303 int *rel_x, int *rel_y,
304 int *pressure, int *width,
305 struct atp_config_t *cfg,
306 const unsigned char *data, int size)
307{ 253{
308 const int nfinger = (size-26)/28; 254 const struct atp_config *c = &dev->cfg;
309 const struct tp_data_t *tp = (struct tp_data_t *) data; 255 const struct tp_finger *f = dev->tp_data->finger;
310 256 const int fingers = (size - 26) / 28;
311 if (nfinger) { 257 struct tp_state *s = &dev->tp_state;
312 *abs_x = int2bound(&cfg->x, raw2int(tp->finger->abs_x) - cfg->x.devmin); 258
313 *abs_y = int2bound(&cfg->y, cfg->y.devmax - raw2int(tp->finger->abs_y)); 259 if (size < 26 || (size - 26) % 28 != 0)
314 *rel_x = int2scale(&cfg->x, raw2int(tp->finger->rel_x)); 260 return -EIO;
315 *rel_y = int2scale(&cfg->y, -raw2int(tp->finger->rel_y)); 261
316 *pressure = int2bound(&cfg->p, raw2int(tp->finger->force_major)); 262 if (!fingers) {
317 *width = int2bound(&cfg->w, raw2int(tp->finger->size_major)); 263 s->pressure = 0;
318 } else { 264 s->fingers = 0;
319 *abs_x = 0; 265 return 0;
320 *abs_y = 0; 266 }
321 *rel_x = 0; 267
322 *rel_y = 0; 268 dprintk(9, "bcm5974: p: %+05d w: %+05d x: %+05d y: %+05d f: %d\n",
323 *pressure = 0; 269 raw2int(f->force_major), raw2int(f->size_major),
324 *width = 0; 270 raw2int(f->abs_x), raw2int(f->abs_y), fingers);
325 } 271
326 272 s->pressure = int2bound(&c->p, raw2int(f->force_major));
327 /* report zero fingers for zero pressure */ 273 s->width = int2bound(&c->w, raw2int(f->size_major));
328 return *pressure > 0?nfinger:0; 274 s->abs_x = int2bound(&c->x, raw2int(f->abs_x) - c->x.devmin);
275 s->abs_y = int2bound(&c->y, c->y.devmax - raw2int(f->abs_y));
276 s->fingers = s->pressure > 0 ? fingers : 0;
277
278 return 0;
329} 279}
330 280
331static void atp_button(struct urb *urb) 281/* Wellspring initialization constants */
282#define ATP_WELLSPRING_MODE_READ_REQUEST_ID 1
283#define ATP_WELLSPRING_MODE_WRITE_REQUEST_ID 9
284#define ATP_WELLSPRING_MODE_REQUEST_VALUE 0x300
285#define ATP_WELLSPRING_MODE_REQUEST_INDEX 0
286#define ATP_WELLSPRING_MODE_VENDOR_VALUE_1 0x01
287#define ATP_WELLSPRING_MODE_VENDOR_VALUE_2 0x05
288
289static int atp_wellspring_init(struct atp *dev)
332{ 290{
333 struct atp *dev = urb->context; 291 char *data = kmalloc(8, GFP_KERNEL);
334 int button, retval; 292 int error = 0, size;
335 293
336 switch (urb->status) { 294 if (!data) {
337 case 0: 295 err("bcm5974: out of memory");
338 break; 296 error = -ENOMEM;
339 case -EOVERFLOW: 297 goto error;
340 case -ECONNRESET: 298 }
341 case -ENOENT: 299
342 case -ESHUTDOWN: 300 /* reset button endpoint */
343 dbg("bcm5974: button urb shutting down: %d", urb->status); 301 if (usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
344 return; 302 USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
345 default: 303 0, dev->cfg.bt_ep, NULL, 0, 5000)) {
346 dbg("bcm5974: button urb status: %d", urb->status); 304 err("bcm5974: could not reset button endpoint");
347 goto exit; 305 error = -EIO;
348 } 306 goto error;
349 307 }
350 /* drop incomplete datasets */ 308
351 if (dev->bt_urb->actual_length != 4) { 309 /* reset trackpad endpoint */
352 dprintk("bcm5974: incomplete button package (first byte: %d, length: %d)\n", 310 if (usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
353 (int)dev->bt_data[0], dev->bt_urb->actual_length); 311 USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
354 goto exit; 312 0, dev->cfg.tp_ep, NULL, 0, 5000)) {
355 } 313 err("bcm5974: could not reset trackpad endpoint");
356 314 error = -EIO;
357 button = dev->bt_data[1]; 315 goto error;
358 316 }
359 /* only report button state changes */ 317
360 if (button != dev->bt_state) { 318 /* read configuration */
361 input_report_key(dev->input, BTN_LEFT, button); 319 size = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
362 input_sync(dev->input); 320 ATP_WELLSPRING_MODE_READ_REQUEST_ID,
363 } 321 USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
364 322 ATP_WELLSPRING_MODE_REQUEST_VALUE,
365 dev->bt_state = button; 323 ATP_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
366 324
367 exit: 325 if (size != 8) {
368 retval = usb_submit_urb(dev->bt_urb, GFP_ATOMIC); 326 err("bcm5974: could not read from device");
369 if (retval) 327 error = -EIO;
370 err("bcm5974: button urb failed: %d", retval); 328 goto error;
329 }
330
331 /* apply the mode switch */
332 data[0] = ATP_WELLSPRING_MODE_VENDOR_VALUE_1;
333 data[1] = ATP_WELLSPRING_MODE_VENDOR_VALUE_2;
334
335 /* write configuration */
336 size = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
337 ATP_WELLSPRING_MODE_WRITE_REQUEST_ID,
338 USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
339 ATP_WELLSPRING_MODE_REQUEST_VALUE,
340 ATP_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
341
342 if (size != 8) {
343 err("bcm5974: could not write to device");
344 error = -EIO;
345 goto error;
346 }
347
348 dev->tp_valid = 0;
349
350 printk(KERN_INFO "bcm5974: Wellspring mode initialized.\n");
351
352 kfree(data);
353 return 0;
354
355error:
356 kfree(data);
357 return error;
371} 358}
372 359
373static void atp_trackpad(struct urb *urb) 360static void irq_button(struct urb *urb)
374{ 361{
375 struct atp *dev = urb->context; 362 struct atp *dev = urb->context;
376 int abs_x, abs_y, rel_x, rel_y, pressure, width; 363 int error;
377 int quality, nfinger, retval; 364
378 365 switch (urb->status) {
379 switch (urb->status) { 366 case 0:
380 case 0: 367 break;
381 break; 368 case -EOVERFLOW:
382 case -EOVERFLOW: 369 case -ECONNRESET:
383 case -ECONNRESET: 370 case -ENOENT:
384 case -ENOENT: 371 case -ESHUTDOWN:
385 case -ESHUTDOWN: 372 dbg("bcm5974: button urb shutting down: %d", urb->status);
386 dbg("bcm5974: trackpad urb shutting down: %d", urb->status); 373 return;
387 return; 374 default:
388 default: 375 dbg("bcm5974: button urb status: %d", urb->status);
389 dbg("bcm5974: trackpad urb status: %d", urb->status); 376 goto exit;
390 goto exit; 377 }
391 } 378
392 379 if (compute_bt_state(dev, dev->bt_urb->actual_length)) {
393 /* first sample data ignored */ 380 dprintk(1, "bcm5974: bad button package, length: %d)\n",
394 if (!dev->tp_valid) { 381 dev->bt_urb->actual_length);
395 dev->tp_valid = 1; 382 goto exit;
396 goto exit; 383 }
397 } 384
398 385 /* only report button state changes */
399 /* determine quality of reading */ 386 if (dev->bt_state != dev->bt_prev_state) {
400 quality = compute_quality(dev->tp_data, dev->tp_urb->actual_length); 387 input_report_key(dev->input, BTN_LEFT, dev->bt_data->button);
401 388 input_sync(dev->input);
402 /* drop incomplete datasets */ 389 }
403 if (quality < 0) { 390
404 dprintk("bcm5974: incomplete trackpad package " 391exit:
405 "(first byte: %d, length: %d)\n", 392 error = usb_submit_urb(dev->bt_urb, GFP_ATOMIC);
406 (int)dev->tp_data[0], dev->tp_urb->actual_length); 393 if (error)
407 goto exit; 394 err("bcm5974: button urb failed: %d", error);
408 }
409
410 /* drop poor quality readings */
411 if (quality == 0)
412 goto exit;
413
414 nfinger = compute_movement(&abs_x, &abs_y,
415 &rel_x, &rel_y,
416 &pressure, &width,
417 &dev->cfg,
418 dev->tp_data, dev->tp_urb->actual_length);
419
420 if (debug > 1) {
421 printk(KERN_DEBUG "bcm5974: x: %04d y: %04d "
422 "dx: %3d dy: %3d p: %3d w: %3d\n",
423 abs_x, abs_y, rel_x, rel_y, pressure, width);
424 }
425
426 input_report_abs(dev->input, ABS_X, abs_x);
427 input_report_abs(dev->input, ABS_Y, abs_y);
428 input_report_abs(dev->input, ABS_PRESSURE, pressure);
429 input_report_abs(dev->input, ABS_TOOL_WIDTH, width);
430 input_report_key(dev->input, BTN_TOOL_FINGER, nfinger == 1);
431 input_report_key(dev->input, BTN_TOOL_DOUBLETAP, nfinger == 2);
432 input_report_key(dev->input, BTN_TOOL_TRIPLETAP, nfinger > 2);
433 input_sync(dev->input);
434
435 exit:
436 retval = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
437 if (retval)
438 err("bcm5974: trackpad urb failed: %d", retval);
439} 395}
440 396
441static int atp_open(struct input_dev *input) 397static void irq_trackpad(struct urb *urb)
442{ 398{
443 struct atp *dev = input_get_drvdata(input); 399 struct atp *dev = urb->context;
444 400 struct tp_state *state = &dev->tp_state;
445 if (!dev->open) { 401 int error;
446 if (usb_submit_urb(dev->bt_urb, GFP_ATOMIC)) 402
447 goto error; 403 switch (urb->status) {
448 if (usb_submit_urb(dev->tp_urb, GFP_ATOMIC)) 404 case 0:
449 goto err_free_bt_urb; 405 break;
450 } 406 case -EOVERFLOW:
451 407 case -ECONNRESET:
452 dev->open = 1; 408 case -ENOENT:
453 dev->suspended = 0; 409 case -ESHUTDOWN:
454 dev->tp_valid = 0; 410 dbg("bcm5974: trackpad urb shutting down: %d", urb->status);
455 411 return;
456 return 0; 412 default:
413 dbg("bcm5974: trackpad urb status: %d", urb->status);
414 goto exit;
415 }
416
417 /* first sample data ignored */
418 if (!dev->tp_valid) {
419 dev->tp_valid = 1;
420 goto exit;
421 }
422
423 if (compute_tp_state(dev, dev->tp_urb->actual_length)) {
424 dprintk(1, "bcm5974: bad trackpad package, length: %d)\n",
425 dev->tp_urb->actual_length);
426 goto exit;
427 }
428
429 input_report_abs(dev->input, ABS_PRESSURE, state->pressure);
430 input_report_abs(dev->input, ABS_TOOL_WIDTH, state->width);
431 input_report_abs(dev->input, ABS_X, state->abs_x);
432 input_report_abs(dev->input, ABS_Y, state->abs_y);
433 input_report_key(dev->input, BTN_TOOL_FINGER, state->fingers == 1);
434 input_report_key(dev->input, BTN_TOOL_DOUBLETAP, state->fingers == 2);
435 input_report_key(dev->input, BTN_TOOL_TRIPLETAP, state->fingers > 2);
436 input_sync(dev->input);
437
438exit:
439 error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
440 if (error)
441 err("bcm5974: trackpad urb failed: %d", error);
442}
457 443
458 err_free_bt_urb: 444static int atp_open(struct input_dev *input)
459 usb_free_urb(dev->bt_urb); 445{
460 error: 446 struct atp *dev = input_get_drvdata(input);
461 return -EIO; 447
448 if (!dev->open) {
449 if (usb_submit_urb(dev->bt_urb, GFP_ATOMIC))
450 goto error;
451 if (usb_submit_urb(dev->tp_urb, GFP_ATOMIC))
452 goto err_free_bt_urb;
453 }
454
455 dev->open = 1;
456 dev->suspended = 0;
457 return 0;
458
459err_free_bt_urb:
460 usb_free_urb(dev->bt_urb);
461error:
462 return -EIO;
462} 463}
463 464
464static void atp_close(struct input_dev *input) 465static void atp_close(struct input_dev *input)
465{ 466{
466 struct atp *dev = input_get_drvdata(input); 467 struct atp *dev = input_get_drvdata(input);
467 468
468 if (dev->open && !dev->suspended) { 469 usb_kill_urb(dev->tp_urb);
469 usb_kill_urb(dev->tp_urb); 470 usb_kill_urb(dev->bt_urb);
470 usb_kill_urb(dev->bt_urb);
471 }
472 471
473 dev->open = 0; 472 dev->open = 0;
474 dev->suspended = 0; 473 dev->suspended = 0;
475} 474}
476 475
477static int atp_probe(struct usb_interface *iface, 476static int atp_probe(struct usb_interface *iface,
478 const struct usb_device_id *id) 477 const struct usb_device_id *id)
479{ 478{
480 int error = -ENOMEM; 479 struct usb_device *udev = interface_to_usbdev(iface);
481 struct usb_device *udev = interface_to_usbdev(iface); 480 const struct atp_config *cfg;
482 const struct atp_config_t *cfg; 481 struct atp *dev;
483 struct atp *dev; 482 struct input_dev *input_dev;
484 struct input_dev *input_dev; 483 int error = 0;
485 484
486 printk(KERN_INFO "bcm5974: Probing device...\n"); 485 /* find the product index */
487 486 cfg = atp_product_config(udev);
488 /* find the product index */ 487
489 cfg = atp_product_config(udev); 488 /* allocate memory for our device state and initialize it */
490 489 dev = kzalloc(sizeof(struct atp), GFP_KERNEL);
491 /* allocate memory for our device state and initialize it */ 490 input_dev = input_allocate_device();
492 dev = kzalloc(sizeof(struct atp), GFP_KERNEL); 491 if (!dev || !input_dev) {
493 input_dev = input_allocate_device(); 492 err("bcm5974: out of memory");
494 if (!dev || !input_dev) { 493 error = -ENOMEM;
495 err("Out of memory"); 494 goto err_free_devs;
496 goto err_free_devs; 495 }
497 } 496
498 497 dev->udev = udev;
499 dev->udev = udev; 498 dev->input = input_dev;
500 dev->input = input_dev; 499 dev->cfg = *cfg;
501 dev->cfg = *cfg; 500
502 501 /* switch to raw sensor mode */
503 /* switch to raw sensor mode */ 502 if (atp_wellspring_init(dev)) {
504 if (atp_wellspring_init(udev)) 503 error = -EIO;
505 goto err_free_devs; 504 goto err_free_devs;
506 505 }
507 dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL); 506
508 if (!dev->bt_urb) 507 dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
509 goto err_free_devs; 508 if (!dev->bt_urb) {
510 509 error = -ENOMEM;
511 dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL); 510 goto err_free_devs;
512 if (!dev->tp_urb) 511 }
513 goto err_free_bt_urb; 512
514 513 dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);
515 dev->bt_data = usb_buffer_alloc(dev->udev, 514 if (!dev->tp_urb) {
516 dev->cfg.bt_datalen, 515 error = -ENOMEM;
517 GFP_KERNEL, 516 goto err_free_bt_urb;
518 &dev->bt_urb->transfer_dma); 517 }
519 if (!dev->bt_data) 518
520 goto err_free_urb; 519 dev->bt_data = usb_buffer_alloc(dev->udev,
521 520 dev->cfg.bt_datalen, GFP_KERNEL,
522 dev->tp_data = usb_buffer_alloc(dev->udev, 521 &dev->bt_urb->transfer_dma);
523 dev->cfg.tp_datalen, 522 if (!dev->bt_data) {
524 GFP_KERNEL, 523 error = -ENOMEM;
525 &dev->tp_urb->transfer_dma); 524 goto err_free_urb;
526 if (!dev->tp_data) 525 }
527 goto err_free_bt_buffer; 526
528 527 dev->tp_data = usb_buffer_alloc(dev->udev,
529 usb_fill_int_urb(dev->bt_urb, udev, 528 dev->cfg.tp_datalen, GFP_KERNEL,
530 usb_rcvintpipe(udev, cfg->bt_ep), 529 &dev->tp_urb->transfer_dma);
531 dev->bt_data, dev->cfg.bt_datalen, 530 if (!dev->tp_data) {
532 atp_button, dev, 1); 531 error = -ENOMEM;
533 532 goto err_free_bt_buffer;
534 usb_fill_int_urb(dev->tp_urb, udev, 533 }
535 usb_rcvintpipe(udev, cfg->tp_ep), 534
536 dev->tp_data, dev->cfg.tp_datalen, 535 usb_fill_int_urb(dev->bt_urb, udev,
537 atp_trackpad, dev, 1); 536 usb_rcvintpipe(udev, cfg->bt_ep),
538 537 dev->bt_data, dev->cfg.bt_datalen,
539 usb_make_path(udev, dev->phys, sizeof(dev->phys)); 538 irq_button, dev, 1);
540 strlcat(dev->phys, "/input0", sizeof(dev->phys)); 539
541 540 usb_fill_int_urb(dev->tp_urb, udev,
542 input_dev->name = "bcm5974"; 541 usb_rcvintpipe(udev, cfg->tp_ep),
543 input_dev->phys = dev->phys; 542 dev->tp_data, dev->cfg.tp_datalen,
544 usb_to_input_id(dev->udev, &input_dev->id); 543 irq_trackpad, dev, 1);
545 input_dev->dev.parent = &iface->dev; 544
546 545 usb_make_path(udev, dev->phys, sizeof(dev->phys));
547 input_set_drvdata(input_dev, dev); 546 strlcat(dev->phys, "/input0", sizeof(dev->phys));
548 547
549 input_dev->open = atp_open; 548 input_dev->name = "bcm5974";
550 input_dev->close = atp_close; 549 input_dev->phys = dev->phys;
551 550 usb_to_input_id(dev->udev, &input_dev->id);
552 set_bit(EV_ABS, input_dev->evbit); 551 input_dev->dev.parent = &iface->dev;
553 input_set_abs_params(input_dev, ABS_X, 552
554 cfg->x.min, cfg->x.max, cfg->x.fuzz, cfg->x.flat); 553 input_set_drvdata(input_dev, dev);
555 input_set_abs_params(input_dev, ABS_Y, 554
556 cfg->y.min, cfg->y.max, cfg->y.fuzz, cfg->y.flat); 555 input_dev->open = atp_open;
557 input_set_abs_params(input_dev, ABS_PRESSURE, 556 input_dev->close = atp_close;
558 cfg->p.min, cfg->p.max, cfg->p.fuzz, cfg->p.flat); 557
559 input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 558 set_bit(EV_ABS, input_dev->evbit);
560 cfg->w.min, cfg->w.max, cfg->w.fuzz, cfg->w.flat); 559 input_set_abs_params(input_dev, ABS_PRESSURE,
561 560 0, cfg->p.dim, cfg->p.dim / SNRATIO, 0);
562 set_bit(EV_KEY, input_dev->evbit); 561 input_set_abs_params(input_dev, ABS_TOOL_WIDTH,
563 set_bit(BTN_TOOL_FINGER, input_dev->keybit); 562 0, cfg->w.dim, cfg->w.dim / SNRATIO, 0);
564 set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); 563 input_set_abs_params(input_dev, ABS_X,
565 set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); 564 0, cfg->x.dim, cfg->x.dim / SNRATIO, 0);
566 set_bit(BTN_LEFT, input_dev->keybit); 565 input_set_abs_params(input_dev, ABS_Y,
567 566 0, cfg->y.dim, cfg->y.dim / SNRATIO, 0);
568 error = input_register_device(dev->input); 567
569 if (error) 568 set_bit(EV_KEY, input_dev->evbit);
570 goto err_free_buffer; 569 set_bit(BTN_TOOL_FINGER, input_dev->keybit);
571 570 set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
572 /* save our data pointer in this interface device */ 571 set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
573 usb_set_intfdata(iface, dev); 572 set_bit(BTN_LEFT, input_dev->keybit);
574 573
575 return 0; 574 error = input_register_device(dev->input);
576 575 if (error)
577 err_free_buffer: 576 goto err_free_buffer;
578 usb_buffer_free(dev->udev, dev->cfg.tp_datalen, 577
579 dev->tp_data, dev->tp_urb->transfer_dma); 578 /* save our data pointer in this interface device */
580 err_free_bt_buffer: 579 usb_set_intfdata(iface, dev);
581 usb_buffer_free(dev->udev, dev->cfg.bt_datalen, 580
582 dev->bt_data, dev->bt_urb->transfer_dma); 581 return 0;
583 err_free_urb: 582
584 usb_free_urb(dev->tp_urb); 583err_free_buffer:
585 err_free_bt_urb: 584 usb_buffer_free(dev->udev, dev->cfg.tp_datalen,
586 usb_free_urb(dev->bt_urb); 585 dev->tp_data, dev->tp_urb->transfer_dma);
587 err_free_devs: 586err_free_bt_buffer:
588 usb_set_intfdata(iface, NULL); 587 usb_buffer_free(dev->udev, dev->cfg.bt_datalen,
589 kfree(dev); 588 dev->bt_data, dev->bt_urb->transfer_dma);
590 input_free_device(input_dev); 589err_free_urb:
591 return error; 590 usb_free_urb(dev->tp_urb);
591err_free_bt_urb:
592 usb_free_urb(dev->bt_urb);
593err_free_devs:
594 usb_set_intfdata(iface, NULL);
595 kfree(dev);
596 input_free_device(input_dev);
597 return error;
592} 598}
593 599
594static void atp_disconnect(struct usb_interface *iface) 600static void atp_disconnect(struct usb_interface *iface)
595{ 601{
596 struct atp *dev = usb_get_intfdata(iface); 602 struct atp *dev = usb_get_intfdata(iface);
597 603
598 usb_set_intfdata(iface, NULL); 604 usb_set_intfdata(iface, NULL);
599 if (dev) { 605
600 input_unregister_device(dev->input); 606 if (dev) {
601 usb_buffer_free(dev->udev, dev->cfg.tp_datalen, 607 input_unregister_device(dev->input);
602 dev->tp_data, dev->tp_urb->transfer_dma); 608 usb_buffer_free(dev->udev, dev->cfg.tp_datalen,
603 usb_buffer_free(dev->udev, dev->cfg.bt_datalen, 609 dev->tp_data, dev->tp_urb->transfer_dma);
604 dev->bt_data, dev->bt_urb->transfer_dma); 610 usb_buffer_free(dev->udev, dev->cfg.bt_datalen,
605 usb_free_urb(dev->tp_urb); 611 dev->bt_data, dev->bt_urb->transfer_dma);
606 usb_free_urb(dev->bt_urb); 612 usb_free_urb(dev->tp_urb);
607 kfree(dev); 613 usb_free_urb(dev->bt_urb);
608 } 614 kfree(dev);
609 printk(KERN_INFO "bcm5974: disconnected\n"); 615 }
616
617 printk(KERN_INFO "bcm5974: disconnected\n");
610} 618}
611 619
612static int atp_suspend(struct usb_interface *iface, pm_message_t message) 620static int atp_suspend(struct usb_interface *iface, pm_message_t message)
613{ 621{
614 struct atp *dev = usb_get_intfdata(iface); 622 struct atp *dev = usb_get_intfdata(iface);
615 623
616 if (dev->open && !dev->suspended++) { 624 if (dev) {
617 usb_kill_urb(dev->tp_urb); 625 if (!dev->suspended)
618 usb_kill_urb(dev->bt_urb); 626 atp_close(dev->input);
619 } 627 dev->suspended++;
628 }
620 629
621 return 0; 630 return 0;
622} 631}
623 632
624static int atp_resume(struct usb_interface *iface) 633static int atp_resume(struct usb_interface *iface)
625{ 634{
626 struct atp *dev = usb_get_intfdata(iface); 635 struct atp *dev = usb_get_intfdata(iface);
627 636 int error = 0;
628 if (dev->open && !--dev->suspended) { 637
629 if (usb_submit_urb(dev->bt_urb, GFP_ATOMIC)) 638 if (dev && dev->suspended) {
630 goto error; 639 if (!--dev->suspended)
631 if (usb_submit_urb(dev->tp_urb, GFP_ATOMIC)) 640 error = atp_open(dev->input);
632 goto err_free_bt_urb; 641 }
633 } 642
634 643 return error;
635 dev->tp_valid = 0;
636 return 0;
637
638 err_free_bt_urb:
639 usb_free_urb(dev->bt_urb);
640 error:
641 return -EIO;
642} 644}
643 645
644static int atp_reset_resume(struct usb_interface *iface) 646static int atp_reset_resume(struct usb_interface *iface)
645{ 647{
646 struct atp *dev = usb_get_intfdata(iface); 648 struct atp *dev = usb_get_intfdata(iface);
647 649
648 if (dev->suspended == 1) { 650 if (dev && atp_wellspring_init(dev))
649 if (atp_wellspring_init(dev->udev)) 651 printk(KERN_INFO "bcm5974: warning: reset failed\n");
650 printk(KERN_INFO "bcm5974: warning: reset failed\n");
651 }
652 652
653 return atp_resume(iface); 653 return atp_resume(iface);
654} 654}
655 655
656static int atp_pre_reset(struct usb_interface *iface) 656static int atp_pre_reset(struct usb_interface *iface)
657{ 657{
658 return atp_suspend(iface, PMSG_ON); 658 return atp_suspend(iface, PMSG_ON);
659} 659}
660 660
661static int atp_post_reset(struct usb_interface *iface) 661static int atp_post_reset(struct usb_interface *iface)
662{ 662{
663 return atp_reset_resume(iface); 663 return atp_reset_resume(iface);
664} 664}
665 665
666static struct usb_driver atp_driver = { 666static struct usb_driver atp_driver = {
667 .name = "bcm5974", 667 .name = "bcm5974",
668 .probe = atp_probe, 668 .probe = atp_probe,
669 .disconnect = atp_disconnect, 669 .disconnect = atp_disconnect,
670 .suspend = atp_suspend, 670 .suspend = atp_suspend,
671 .resume = atp_resume, 671 .resume = atp_resume,
672 .reset_resume = atp_reset_resume, 672 .reset_resume = atp_reset_resume,
673 .pre_reset = atp_pre_reset, 673 .pre_reset = atp_pre_reset,
674 .post_reset = atp_post_reset, 674 .post_reset = atp_post_reset,
675 .id_table = atp_table, 675 .id_table = atp_table,
676}; 676};
677 677
678static int __init atp_init(void) 678static int __init atp_init(void)
679{ 679{
680 return usb_register(&atp_driver); 680 return usb_register(&atp_driver);
681} 681}
682 682
683static void __exit atp_exit(void) 683static void __exit atp_exit(void)
684{ 684{
685 usb_deregister(&atp_driver); 685 usb_deregister(&atp_driver);
686} 686}
687 687
688module_init(atp_init); 688module_init(atp_init);
689module_exit(atp_exit); 689module_exit(atp_exit);
690
diff --git a/dkms.conf b/dkms.conf
new file mode 100644
index 0000000..89d112e
--- /dev/null
+++ b/dkms.conf
@@ -0,0 +1,8 @@
1PACKAGE_NAME="bcm5974"
2PACKAGE_VERSION="0.5"
3MAKE[0]="make -C ${kernel_source_dir}
4 SUBDIRS=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build modules"
5BUILT_MODULE_NAME[0]="bcm5974"
6DEST_MODULE_LOCATION[0]="/kernel/drivers/input/mouse"
7POST_INSTALL="scripts/bcm5974-post-install"
8POST_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 @@
1### Options for the bcm5974 driver
2
3## quirks for the Macbook Air
4#options usbhid quirks=0x05ac:0x0223:0x00020800,0x05ac:0x0224:0x00024800,0x05ac:0x0225:0x00020800
5
6## quirks for the Macbook Pro Penryn
7#options usbhid quirks=0x05ac:0x0230:0x00020800,0x05ac:0x0231:0x00024800,0x05ac:0x0232:0x00020800
8
9## Debug level - uncomment this to get raw packets in /var/log/debug
10#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 @@
1#!/bin/bash
2#
3# bcm5943-diagnostics - troubleshooting the BCM5974 setup
4#
5# Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se)
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20#
21
22DRIVER=bcm5974
23
24echo -----------------------------------------------------------------------
25
26# What kernel version am I running?
27KERNEL=`uname -r`
28echo \* Kernel version: $KERNEL
29
30# What synaptics version is installed (debian)?
31if [ -x /usr/bin/dpkg-query ]; then
32 SYNAPTICS=`/usr/bin/dpkg-query -W -f='${Version}\n' xserver-xorg-input-synaptics`
33 echo \* Synaptics version: $SYNAPTICS
34else
35 echo \* Synaptics version: UNKNOWN - dpkg not found
36fi
37
38# What USB devices of interest can we find?
39if [ -x /usr/sbin/lsusb ]; then
40 LSUSB=`lsusb|grep 05ac:02`
41 echo \* USB device: $LSUSB
42else
43 echo \* USB device: UNKNOWN - lsusb not found
44fi
45
46# Do we have the module intalled?
47MODLIB=/lib/modules/$KERNEL/kernel/drivers/input/mouse/$DRIVER.ko
48MODLIB2=/lib/modules/$KERNEL/updates/dkms/$DRIVER.ko
49if [ -f $MODLIB ]; then
50 echo \* $MODLIB: exists
51elif [ -f $MODLIB2 ]; then
52 echo \* $MODLIB2: exists
53else
54 echo \* $DRIVER does NOT exist
55 echo dkms: install package
56 echo manual: copy the file $DRIVER.ko to $MODLIB
57 exit 1
58fi
59
60# Do we have the module configured?
61MODULES=/etc/modules
62if [ -f $MODULES ]; then
63 if (grep -q $DRIVER $MODULES); then
64 echo \* $MODULES: has $DRIVER config, please remove
65 exit 1
66 else
67 echo \* $MODULES: $DRIVER no longer explicitly listed, good
68 fi
69fi
70
71# Do we have old quirks?
72MODPROBE=/etc/modprobe.d/options
73if [ -f $MODPROBE ]; then
74 MODPROBEOPTS=`grep quirks= $MODPROBE|grep -v ^#|grep 0x05ac:0x02`
75 if [ "$MODPROBEOPTS" != "" ]; then
76 echo \* $MODPROBE: has obsolete quirks - please remove
77 exit 1
78 else
79 echo \* $MODPROBE: no obsolete quirks, good
80 fi
81else
82 echo \* $MODPROBE: file not found
83fi
84
85# Do we have the right quirks?
86DRIVMOD=/etc/modprobe.d/$DRIVER
87if [ -f $DRIVMOD ]; then
88 DRIVMODOPTS=`grep quirks= $DRIVMOD|grep -v ^#|grep 0x05ac:0x02`
89 if [ "$DRIVMODOPTS" != "" ]; then
90 echo \* $DRIVMOD: has quirks, good
91 else
92 echo \* $DRIVMOD: does NOT have quirks
93 echo Uncomment the right quirk line in $DRIVMOD
94 exit 1
95 fi
96else
97 echo \* $DRIVMOD: file not found
98 echo Copy the etc/modprobe.d/bcm5974 file to /etc/modprobe.d/
99 exit 1
100fi
101
102# Did the depmod bite?
103USBMAP=/lib/modules/$KERNEL/modules.usbmap
104if [ -f $USBMAP ]; then
105 typeset -i nusb=`grep $DRIVER $USBMAP|grep 0x05ac|grep 0x02|wc -l`
106 if [ $nusb -gt 0 ]; then
107 echo \* $USBMAP: maps to $DRIVER, good
108 else
109 echo \* $USBMAP: does NOT map to $DRIVER
110 echo Run depmod -a as root
111 exit 1
112 fi
113fi
114
115# Was the $DRIVER module loaded?
116DMESG=`lsmod|grep $DRIVER`
117if [ "$DMESG" != "" ]; then
118 echo \* $DRIVER: module is loaded
119else
120 echo \* $DRIVER: module is NOT loaded
121 echo Add this line to /etc/modules:
122 echo "$DRIVER"
123 exit 1
124fi
125
126# Was the $DRIVER module registered?
127DEVPATH=/proc/bus/input/devices
128if [ -f $DEVPATH ]; then
129 DEVICES=`grep $DRIVER /proc/bus/input/devices`
130 if [ "$DEVICES" != "" ]; then
131 echo \* $DEVPATH: module is registered
132 else
133 echo \* $DEVPATH: module is NOT registered
134 echo No help on this one...
135 exit 1
136 fi
137else
138 echo \* $DEVPATH: does not exist, ignore
139fi
140
141# Was the $DRIVER properly initialized?
142WELLSPRING=`dmesg|grep $DRIVER|grep Wellspring`
143if [ "$WELLSPRING" != "" ]; then
144 echo \* $DRIVER: Wellspring was initialized
145else
146 echo \* $DRIVER: Wellspring was NOT initialized
147 exit 1
148fi
149
150# Do we have synaptics working properly?
151typeset -i SYNLINES=`synclient -l|wc -l`
152if [ $SYNLINES -gt 10 ]; then
153 echo \* synaptics: configuration readable
154else
155 echo \* synaptics: configuration NOT readable
156 exit 1
157fi
158
159# Do we have a working synaptics configuration?
160DIFF=/tmp/synclient-output.diff.$$
161TMP=/tmp/synclient-output.txt.$$
162synclient -l | grep -v Parameter | sort > $TMP
163diff -u - $TMP > $DIFF <<EOF
164 AccelFactor = 0.1
165 BottomEdge = 800
166 CircScrollDelta = 0.1
167 CircScrollTrigger = 0
168 CircularPad = 0
169 CircularScrolling = 0
170 ClickTime = 100
171 CoastingSpeed = 0
172 CornerCoasting = 0
173 EdgeMotionMaxSpeed = 80
174 EdgeMotionMaxZ = 160
175 EdgeMotionMinSpeed = 1
176 EdgeMotionMinZ = 30
177 EdgeMotionUseAlways = 0
178 EmulateMidButtonTime = 75
179 EmulateTwoFingerMinZ = 257
180 FastTaps = 0
181 FingerHigh = 80
182 FingerLow = 16
183 FingerPress = 256
184 GrabEventDevice = 1
185 GuestMouseOff = 0
186 HorizEdgeScroll = 0
187 HorizScrollDelta = 0
188 HorizTwoFingerScroll = 1
189 LBCornerButton = 0
190 LeftEdge = 0
191 LeftRightScrolling = 1
192 LeftRightScrollRepeat = 1
193 LockedDrags = 0
194 LockedDragTimeout = 5000
195 LTCornerButton = 0
196 MaxDoubleTapTime = 200
197 MaxSpeed = 1.2
198 MaxTapMove = 100
199 MaxTapTime = 223
200 MinSpeed = 0.8
201 MultiFingerButton = 2
202 PalmDetect = 0
203 PalmMinWidth = 10
204 PalmMinZ = 200
205 PressureMotionMaxFactor = 1
206 PressureMotionMaxZ = 160
207 PressureMotionMinFactor = 1
208 PressureMotionMinZ = 10
209 RBCornerButton = 0
210 RightEdge = 1280
211 RTCornerButton = 0
212 ScrollButtonRepeat = 100
213 SingleTapTimeout = 180
214 TapButton1 = 0
215 TapButton2 = 0
216 TapButton3 = 0
217 TopEdge = 0
218 TouchpadOff = 0
219 TrackstickSpeed = 40
220 UpDownScrolling = 1
221 UpDownScrollRepeat = 1
222 VertEdgeScroll = 0
223 VertScrollDelta = 40
224 VertTwoFingerScroll = 1
225EOF
226rm -f $TMP
227
228echo =======================================================================
229echo The bcm5974 seems properly configured
230
231typeset -i NDIFF=`cat $DIFF|wc -l`
232if [ $NDIFF -gt 0 ]; then
233 echo
234 echo Your synaptics settings differ from the bcm5974 default:
235 echo -----------------------------------------------------------------------
236 tail -n +4 $DIFF
237 echo -----------------------------------------------------------------------
238 echo If in doubt, please check your /etc/X11/xorg.conf settings
239fi
240
241rm -f $DIFF
diff --git a/scripts/bcm5974-post-install b/scripts/bcm5974-post-install
new file mode 100755
index 0000000..21e989e
--- /dev/null
+++ b/scripts/bcm5974-post-install
@@ -0,0 +1,50 @@
1#!/bin/bash
2#
3# bcm5943-post-install - post-installing the BCM5974
4#
5# Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se)
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20#
21
22DRIVER=bcm5974
23ETCPATH=/etc/modprobe.d/$DRIVER
24
25echo -----------------------------------------------------------------------
26
27DIR=`dirname $0`
28PAT_HEAD="options usbhid quirks="
29PAT_MBA="0x05ac:0x022"
30PAT_MBP="0x05ac:0x023"
31MATCH_MBA=$PAT_HEAD$PAT_MBA
32MATCH_MBB=$PAT_HEAD$PAT_MBB
33
34if [ -x /usr/sbin/lsusb ]; then
35 if [ "lsusb|grep $PAT_MBA" != "" ]; then
36 SED="s/#$MATCH_MBA/$MATCH_MBA/"
37 elif [ $has_mbb -gt 0 ]; then
38 SED="s/#$MATCH_MBP/$MATCH_MBP/"
39 else
40 echo No suitable device found
41 exit 1
42 fi
43else
44 echo \* USB device: UNKNOWN - lsusb not found
45 exit 1
46fi
47
48cat $DIR/..$ETCPATH | sed "$SED" > $ETCPATH
49
50exit 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 @@
1#!/bin/bash
2#
3# bcm5943-post-remove - post-removing the BCM5974
4#
5# Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se)
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20#
21
22DRIVER=bcm5974
23ETCPATH=/etc/modprobe.d/$DRIVER
24
25echo -----------------------------------------------------------------------
26
27rm -f $ETCPATH
28
29exit 0