1f89bd95cSHenrik Rydberg /* 2f89bd95cSHenrik Rydberg * Apple USB BCM5974 (Macbook Air and Penryn Macbook Pro) multitouch driver 3f89bd95cSHenrik Rydberg * 4f89bd95cSHenrik Rydberg * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) 5*d5806926SJohn Horan * Copyright (C) 2015 John Horan (knasher@gmail.com) 6f89bd95cSHenrik Rydberg * 7f89bd95cSHenrik Rydberg * The USB initialization and package decoding was made by 8f89bd95cSHenrik Rydberg * Scott Shawcroft as part of the touchd user-space driver project: 9f89bd95cSHenrik Rydberg * Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com) 10f89bd95cSHenrik Rydberg * 11f89bd95cSHenrik Rydberg * The BCM5974 driver is based on the appletouch driver: 12f89bd95cSHenrik Rydberg * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) 13f89bd95cSHenrik Rydberg * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net) 14f89bd95cSHenrik Rydberg * Copyright (C) 2005 Stelian Pop (stelian@popies.net) 15f89bd95cSHenrik Rydberg * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de) 16f89bd95cSHenrik Rydberg * Copyright (C) 2005 Peter Osterlund (petero2@telia.com) 17f89bd95cSHenrik Rydberg * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch) 18f89bd95cSHenrik Rydberg * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch) 19f89bd95cSHenrik Rydberg * 20f89bd95cSHenrik Rydberg * This program is free software; you can redistribute it and/or modify 21f89bd95cSHenrik Rydberg * it under the terms of the GNU General Public License as published by 22f89bd95cSHenrik Rydberg * the Free Software Foundation; either version 2 of the License, or 23f89bd95cSHenrik Rydberg * (at your option) any later version. 24f89bd95cSHenrik Rydberg * 25f89bd95cSHenrik Rydberg * This program is distributed in the hope that it will be useful, 26f89bd95cSHenrik Rydberg * but WITHOUT ANY WARRANTY; without even the implied warranty of 27f89bd95cSHenrik Rydberg * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28f89bd95cSHenrik Rydberg * GNU General Public License for more details. 29f89bd95cSHenrik Rydberg * 30f89bd95cSHenrik Rydberg * You should have received a copy of the GNU General Public License 31f89bd95cSHenrik Rydberg * along with this program; if not, write to the Free Software 32f89bd95cSHenrik Rydberg * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 33f89bd95cSHenrik Rydberg * 34f89bd95cSHenrik Rydberg */ 35f89bd95cSHenrik Rydberg 36f89bd95cSHenrik Rydberg #include <linux/kernel.h> 37f89bd95cSHenrik Rydberg #include <linux/errno.h> 38f89bd95cSHenrik Rydberg #include <linux/slab.h> 39f89bd95cSHenrik Rydberg #include <linux/module.h> 40f89bd95cSHenrik Rydberg #include <linux/usb/input.h> 41f89bd95cSHenrik Rydberg #include <linux/hid.h> 42f89bd95cSHenrik Rydberg #include <linux/mutex.h> 4351c80b74SHenrik Rydberg #include <linux/input/mt.h> 44f89bd95cSHenrik Rydberg 45f89bd95cSHenrik Rydberg #define USB_VENDOR_ID_APPLE 0x05ac 46f89bd95cSHenrik Rydberg 47f89bd95cSHenrik Rydberg /* MacbookAir, aka wellspring */ 48f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI 0x0223 49f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING_ISO 0x0224 50f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING_JIS 0x0225 51f89bd95cSHenrik Rydberg /* MacbookProPenryn, aka wellspring2 */ 52f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230 53f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231 54f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232 55158e9287SHenrik Rydberg /* Macbook5,1 (unibody), aka wellspring3 */ 56158e9287SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI 0x0236 57158e9287SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO 0x0237 58158e9287SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238 596021afcfSEdgar (gimli) Hucek /* MacbookAir3,2 (unibody), aka wellspring5 */ 606021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI 0x023f 616021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO 0x0240 626021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS 0x0241 636021afcfSEdgar (gimli) Hucek /* MacbookAir3,1 (unibody), aka wellspring4 */ 646021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242 656021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243 666021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244 6747340bd9SAndy Botting /* Macbook8 (unibody, March 2011) */ 6847340bd9SAndy Botting #define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245 6947340bd9SAndy Botting #define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246 7047340bd9SAndy Botting #define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247 711c601beaSPieter-Augustijn Van Malleghem /* MacbookAir4,1 (unibody, July 2011) */ 721c601beaSPieter-Augustijn Van Malleghem #define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI 0x0249 731c601beaSPieter-Augustijn Van Malleghem #define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO 0x024a 741c601beaSPieter-Augustijn Van Malleghem #define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS 0x024b 75db0b34b0SJoshua V. Dillon /* MacbookAir4,2 (unibody, July 2011) */ 76db0b34b0SJoshua V. Dillon #define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI 0x024c 77db0b34b0SJoshua V. Dillon #define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO 0x024d 78db0b34b0SJoshua V. Dillon #define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS 0x024e 79c331eb58SAndrew Drake /* Macbook8,2 (unibody) */ 80c331eb58SAndrew Drake #define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI 0x0252 81c331eb58SAndrew Drake #define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO 0x0253 82c331eb58SAndrew Drake #define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS 0x0254 833dde22a9SHenrik Rydberg /* MacbookPro10,1 (unibody, June 2012) */ 843dde22a9SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI 0x0262 853dde22a9SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING7_ISO 0x0263 863dde22a9SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING7_JIS 0x0264 878d80da90SDirk Hohndel /* MacbookPro10,2 (unibody, October 2012) */ 888d80da90SDirk Hohndel #define USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI 0x0259 898d80da90SDirk Hohndel #define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO 0x025a 908d80da90SDirk Hohndel #define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS 0x025b 91148c1c8aSDmitry Torokhov /* MacbookAir6,2 (unibody, June 2013) */ 928c89cc17SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290 938c89cc17SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291 948c89cc17SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292 95*d5806926SJohn Horan /* MacbookPro12,1 (2015) */ 96*d5806926SJohn Horan #define USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI 0x0272 97*d5806926SJohn Horan #define USB_DEVICE_ID_APPLE_WELLSPRING9_ISO 0x0273 98*d5806926SJohn Horan #define USB_DEVICE_ID_APPLE_WELLSPRING9_JIS 0x0274 99f89bd95cSHenrik Rydberg 100f89bd95cSHenrik Rydberg #define BCM5974_DEVICE(prod) { \ 101f89bd95cSHenrik Rydberg .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \ 102f89bd95cSHenrik Rydberg USB_DEVICE_ID_MATCH_INT_CLASS | \ 103f89bd95cSHenrik Rydberg USB_DEVICE_ID_MATCH_INT_PROTOCOL), \ 104f89bd95cSHenrik Rydberg .idVendor = USB_VENDOR_ID_APPLE, \ 105f89bd95cSHenrik Rydberg .idProduct = (prod), \ 106f89bd95cSHenrik Rydberg .bInterfaceClass = USB_INTERFACE_CLASS_HID, \ 107f89bd95cSHenrik Rydberg .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE \ 108f89bd95cSHenrik Rydberg } 109f89bd95cSHenrik Rydberg 110f89bd95cSHenrik Rydberg /* table of devices that work with this driver */ 111f89bd95cSHenrik Rydberg static const struct usb_device_id bcm5974_table[] = { 112f89bd95cSHenrik Rydberg /* MacbookAir1.1 */ 113f89bd95cSHenrik Rydberg BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), 114f89bd95cSHenrik Rydberg BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ISO), 115f89bd95cSHenrik Rydberg BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_JIS), 116f89bd95cSHenrik Rydberg /* MacbookProPenryn */ 117f89bd95cSHenrik Rydberg BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI), 118f89bd95cSHenrik Rydberg BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ISO), 119f89bd95cSHenrik Rydberg BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_JIS), 120158e9287SHenrik Rydberg /* Macbook5,1 */ 121158e9287SHenrik Rydberg BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI), 122158e9287SHenrik Rydberg BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ISO), 123158e9287SHenrik Rydberg BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_JIS), 1246021afcfSEdgar (gimli) Hucek /* MacbookAir3,2 */ 1256021afcfSEdgar (gimli) Hucek BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI), 1266021afcfSEdgar (gimli) Hucek BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ISO), 1276021afcfSEdgar (gimli) Hucek BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_JIS), 1286021afcfSEdgar (gimli) Hucek /* MacbookAir3,1 */ 1296021afcfSEdgar (gimli) Hucek BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI), 1306021afcfSEdgar (gimli) Hucek BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO), 1316021afcfSEdgar (gimli) Hucek BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS), 13247340bd9SAndy Botting /* MacbookPro8 */ 13347340bd9SAndy Botting BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI), 13447340bd9SAndy Botting BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ISO), 13547340bd9SAndy Botting BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_JIS), 1361c601beaSPieter-Augustijn Van Malleghem /* MacbookAir4,1 */ 1371c601beaSPieter-Augustijn Van Malleghem BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI), 1381c601beaSPieter-Augustijn Van Malleghem BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO), 1391c601beaSPieter-Augustijn Van Malleghem BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS), 140db0b34b0SJoshua V. Dillon /* MacbookAir4,2 */ 141db0b34b0SJoshua V. Dillon BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI), 142db0b34b0SJoshua V. Dillon BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ISO), 143db0b34b0SJoshua V. Dillon BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_JIS), 144c331eb58SAndrew Drake /* MacbookPro8,2 */ 145c331eb58SAndrew Drake BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI), 146c331eb58SAndrew Drake BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO), 147c331eb58SAndrew Drake BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS), 1483dde22a9SHenrik Rydberg /* MacbookPro10,1 */ 1493dde22a9SHenrik Rydberg BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI), 1503dde22a9SHenrik Rydberg BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_ISO), 1513dde22a9SHenrik Rydberg BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_JIS), 1528d80da90SDirk Hohndel /* MacbookPro10,2 */ 1538d80da90SDirk Hohndel BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI), 1548d80da90SDirk Hohndel BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO), 1558d80da90SDirk Hohndel BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS), 156148c1c8aSDmitry Torokhov /* MacbookAir6,2 */ 157148c1c8aSDmitry Torokhov BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI), 158148c1c8aSDmitry Torokhov BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ISO), 159148c1c8aSDmitry Torokhov BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_JIS), 160*d5806926SJohn Horan /* MacbookPro12,1 */ 161*d5806926SJohn Horan BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI), 162*d5806926SJohn Horan BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_ISO), 163*d5806926SJohn Horan BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_JIS), 164f89bd95cSHenrik Rydberg /* Terminating entry */ 165f89bd95cSHenrik Rydberg {} 166f89bd95cSHenrik Rydberg }; 167f89bd95cSHenrik Rydberg MODULE_DEVICE_TABLE(usb, bcm5974_table); 168f89bd95cSHenrik Rydberg 169f89bd95cSHenrik Rydberg MODULE_AUTHOR("Henrik Rydberg"); 170f89bd95cSHenrik Rydberg MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver"); 171f89bd95cSHenrik Rydberg MODULE_LICENSE("GPL"); 172f89bd95cSHenrik Rydberg 173f89bd95cSHenrik Rydberg #define dprintk(level, format, a...)\ 174f89bd95cSHenrik Rydberg { if (debug >= level) printk(KERN_DEBUG format, ##a); } 175f89bd95cSHenrik Rydberg 176f89bd95cSHenrik Rydberg static int debug = 1; 177f89bd95cSHenrik Rydberg module_param(debug, int, 0644); 178f89bd95cSHenrik Rydberg MODULE_PARM_DESC(debug, "Activate debugging output"); 179f89bd95cSHenrik Rydberg 180f89bd95cSHenrik Rydberg /* button data structure */ 181f89bd95cSHenrik Rydberg struct bt_data { 182f89bd95cSHenrik Rydberg u8 unknown1; /* constant */ 183f89bd95cSHenrik Rydberg u8 button; /* left button */ 184f89bd95cSHenrik Rydberg u8 rel_x; /* relative x coordinate */ 185f89bd95cSHenrik Rydberg u8 rel_y; /* relative y coordinate */ 186f89bd95cSHenrik Rydberg }; 187f89bd95cSHenrik Rydberg 1889894cf0fSHenrik Rydberg /* trackpad header types */ 1899894cf0fSHenrik Rydberg enum tp_type { 190158e9287SHenrik Rydberg TYPE1, /* plain trackpad */ 191148c1c8aSDmitry Torokhov TYPE2, /* button integrated in trackpad */ 192*d5806926SJohn Horan TYPE3, /* additional header fields since June 2013 */ 193*d5806926SJohn Horan TYPE4 /* additional header field for pressure data */ 194f89bd95cSHenrik Rydberg }; 195f89bd95cSHenrik Rydberg 1969894cf0fSHenrik Rydberg /* trackpad finger data offsets, le16-aligned */ 197efbd3470SHenrik Rydberg #define HEADER_TYPE1 (13 * sizeof(__le16)) 198efbd3470SHenrik Rydberg #define HEADER_TYPE2 (15 * sizeof(__le16)) 199efbd3470SHenrik Rydberg #define HEADER_TYPE3 (19 * sizeof(__le16)) 200*d5806926SJohn Horan #define HEADER_TYPE4 (23 * sizeof(__le16)) 201158e9287SHenrik Rydberg 202158e9287SHenrik Rydberg /* trackpad button data offsets */ 203efbd3470SHenrik Rydberg #define BUTTON_TYPE1 0 204158e9287SHenrik Rydberg #define BUTTON_TYPE2 15 205148c1c8aSDmitry Torokhov #define BUTTON_TYPE3 23 206*d5806926SJohn Horan #define BUTTON_TYPE4 31 207158e9287SHenrik Rydberg 208158e9287SHenrik Rydberg /* list of device capability bits */ 209158e9287SHenrik Rydberg #define HAS_INTEGRATED_BUTTON 1 2109894cf0fSHenrik Rydberg 211efbd3470SHenrik Rydberg /* trackpad finger data block size */ 212efbd3470SHenrik Rydberg #define FSIZE_TYPE1 (14 * sizeof(__le16)) 213efbd3470SHenrik Rydberg #define FSIZE_TYPE2 (14 * sizeof(__le16)) 214efbd3470SHenrik Rydberg #define FSIZE_TYPE3 (14 * sizeof(__le16)) 215*d5806926SJohn Horan #define FSIZE_TYPE4 (15 * sizeof(__le16)) 216efbd3470SHenrik Rydberg 217efbd3470SHenrik Rydberg /* offset from header to finger struct */ 218efbd3470SHenrik Rydberg #define DELTA_TYPE1 (0 * sizeof(__le16)) 219efbd3470SHenrik Rydberg #define DELTA_TYPE2 (0 * sizeof(__le16)) 220efbd3470SHenrik Rydberg #define DELTA_TYPE3 (0 * sizeof(__le16)) 221*d5806926SJohn Horan #define DELTA_TYPE4 (1 * sizeof(__le16)) 222efbd3470SHenrik Rydberg 223efbd3470SHenrik Rydberg /* usb control message mode switch data */ 224efbd3470SHenrik Rydberg #define USBMSG_TYPE1 8, 0x300, 0, 0, 0x1, 0x8 225efbd3470SHenrik Rydberg #define USBMSG_TYPE2 8, 0x300, 0, 0, 0x1, 0x8 226efbd3470SHenrik Rydberg #define USBMSG_TYPE3 8, 0x300, 0, 0, 0x1, 0x8 227*d5806926SJohn Horan #define USBMSG_TYPE4 2, 0x302, 2, 1, 0x1, 0x0 228efbd3470SHenrik Rydberg 229efbd3470SHenrik Rydberg /* Wellspring initialization constants */ 230efbd3470SHenrik Rydberg #define BCM5974_WELLSPRING_MODE_READ_REQUEST_ID 1 231efbd3470SHenrik Rydberg #define BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID 9 232efbd3470SHenrik Rydberg 2339894cf0fSHenrik Rydberg /* trackpad finger structure, le16-aligned */ 234f89bd95cSHenrik Rydberg struct tp_finger { 23575e21e3fSHenrik Rydberg __le16 origin; /* zero when switching track finger */ 236f89bd95cSHenrik Rydberg __le16 abs_x; /* absolute x coodinate */ 237f89bd95cSHenrik Rydberg __le16 abs_y; /* absolute y coodinate */ 238f89bd95cSHenrik Rydberg __le16 rel_x; /* relative x coodinate */ 239f89bd95cSHenrik Rydberg __le16 rel_y; /* relative y coodinate */ 240f17953abSHenrik Rydberg __le16 tool_major; /* tool area, major axis */ 241f17953abSHenrik Rydberg __le16 tool_minor; /* tool area, minor axis */ 242f89bd95cSHenrik Rydberg __le16 orientation; /* 16384 when point, else 15 bit angle */ 243f17953abSHenrik Rydberg __le16 touch_major; /* touch area, major axis */ 244f17953abSHenrik Rydberg __le16 touch_minor; /* touch area, minor axis */ 245*d5806926SJohn Horan __le16 unused[2]; /* zeros */ 246*d5806926SJohn Horan __le16 pressure; /* pressure on forcetouch touchpad */ 247f89bd95cSHenrik Rydberg __le16 multi; /* one finger: varies, more fingers: constant */ 2489894cf0fSHenrik Rydberg } __attribute__((packed,aligned(2))); 249f89bd95cSHenrik Rydberg 2509894cf0fSHenrik Rydberg /* trackpad finger data size, empirically at least ten fingers */ 251f17953abSHenrik Rydberg #define MAX_FINGERS 16 2526f2701b7SHenrik Rydberg #define MAX_FINGER_ORIENTATION 16384 253f89bd95cSHenrik Rydberg 254f89bd95cSHenrik Rydberg /* device-specific parameters */ 255f89bd95cSHenrik Rydberg struct bcm5974_param { 2560e726966SHenrik Rydberg int snratio; /* signal-to-noise ratio */ 2570e726966SHenrik Rydberg int min; /* device minimum reading */ 2580e726966SHenrik Rydberg int max; /* device maximum reading */ 259f89bd95cSHenrik Rydberg }; 260f89bd95cSHenrik Rydberg 261f89bd95cSHenrik Rydberg /* device-specific configuration */ 262f89bd95cSHenrik Rydberg struct bcm5974_config { 263f89bd95cSHenrik Rydberg int ansi, iso, jis; /* the product id of this device */ 264158e9287SHenrik Rydberg int caps; /* device capability bitmask */ 265f89bd95cSHenrik Rydberg int bt_ep; /* the endpoint of the button interface */ 266f89bd95cSHenrik Rydberg int bt_datalen; /* data length of the button interface */ 267f89bd95cSHenrik Rydberg int tp_ep; /* the endpoint of the trackpad interface */ 2689894cf0fSHenrik Rydberg enum tp_type tp_type; /* type of trackpad interface */ 269efbd3470SHenrik Rydberg int tp_header; /* bytes in header block */ 270f89bd95cSHenrik Rydberg int tp_datalen; /* data length of the trackpad interface */ 271efbd3470SHenrik Rydberg int tp_button; /* offset to button data */ 272efbd3470SHenrik Rydberg int tp_fsize; /* bytes in single finger block */ 273efbd3470SHenrik Rydberg int tp_delta; /* offset from header to finger struct */ 274efbd3470SHenrik Rydberg int um_size; /* usb control message length */ 275efbd3470SHenrik Rydberg int um_req_val; /* usb control message value */ 276efbd3470SHenrik Rydberg int um_req_idx; /* usb control message index */ 277efbd3470SHenrik Rydberg int um_switch_idx; /* usb control message mode switch index */ 278efbd3470SHenrik Rydberg int um_switch_on; /* usb control message mode switch on */ 279efbd3470SHenrik Rydberg int um_switch_off; /* usb control message mode switch off */ 280f89bd95cSHenrik Rydberg struct bcm5974_param p; /* finger pressure limits */ 281f89bd95cSHenrik Rydberg struct bcm5974_param w; /* finger width limits */ 282f89bd95cSHenrik Rydberg struct bcm5974_param x; /* horizontal limits */ 283f89bd95cSHenrik Rydberg struct bcm5974_param y; /* vertical limits */ 2840e726966SHenrik Rydberg struct bcm5974_param o; /* orientation limits */ 285f89bd95cSHenrik Rydberg }; 286f89bd95cSHenrik Rydberg 287f89bd95cSHenrik Rydberg /* logical device structure */ 288f89bd95cSHenrik Rydberg struct bcm5974 { 289f89bd95cSHenrik Rydberg char phys[64]; 290f89bd95cSHenrik Rydberg struct usb_device *udev; /* usb device */ 29188da765fSDmitry Torokhov struct usb_interface *intf; /* our interface */ 292f89bd95cSHenrik Rydberg struct input_dev *input; /* input dev */ 293f89bd95cSHenrik Rydberg struct bcm5974_config cfg; /* device configuration */ 294f89bd95cSHenrik Rydberg struct mutex pm_mutex; /* serialize access to open/suspend */ 295f89bd95cSHenrik Rydberg int opened; /* 1: opened, 0: closed */ 296f89bd95cSHenrik Rydberg struct urb *bt_urb; /* button usb request block */ 297f89bd95cSHenrik Rydberg struct bt_data *bt_data; /* button transferred data */ 298f89bd95cSHenrik Rydberg struct urb *tp_urb; /* trackpad usb request block */ 2999894cf0fSHenrik Rydberg u8 *tp_data; /* trackpad transferred data */ 30051c80b74SHenrik Rydberg const struct tp_finger *index[MAX_FINGERS]; /* finger index data */ 30151c80b74SHenrik Rydberg struct input_mt_pos pos[MAX_FINGERS]; /* position array */ 30251c80b74SHenrik Rydberg int slots[MAX_FINGERS]; /* slot assignments */ 303f89bd95cSHenrik Rydberg }; 304f89bd95cSHenrik Rydberg 305efbd3470SHenrik Rydberg /* trackpad finger block data, le16-aligned */ 306efbd3470SHenrik Rydberg static const struct tp_finger *get_tp_finger(const struct bcm5974 *dev, int i) 307efbd3470SHenrik Rydberg { 308efbd3470SHenrik Rydberg const struct bcm5974_config *c = &dev->cfg; 309efbd3470SHenrik Rydberg u8 *f_base = dev->tp_data + c->tp_header + c->tp_delta; 310efbd3470SHenrik Rydberg 311efbd3470SHenrik Rydberg return (const struct tp_finger *)(f_base + i * c->tp_fsize); 312efbd3470SHenrik Rydberg } 313efbd3470SHenrik Rydberg 314efbd3470SHenrik Rydberg #define DATAFORMAT(type) \ 315efbd3470SHenrik Rydberg type, \ 316efbd3470SHenrik Rydberg HEADER_##type, \ 317efbd3470SHenrik Rydberg HEADER_##type + (MAX_FINGERS) * (FSIZE_##type), \ 318efbd3470SHenrik Rydberg BUTTON_##type, \ 319efbd3470SHenrik Rydberg FSIZE_##type, \ 320efbd3470SHenrik Rydberg DELTA_##type, \ 321efbd3470SHenrik Rydberg USBMSG_##type 322efbd3470SHenrik Rydberg 323f89bd95cSHenrik Rydberg /* logical signal quality */ 324f89bd95cSHenrik Rydberg #define SN_PRESSURE 45 /* pressure signal-to-noise ratio */ 32551c80b74SHenrik Rydberg #define SN_WIDTH 25 /* width signal-to-noise ratio */ 326f89bd95cSHenrik Rydberg #define SN_COORD 250 /* coordinate signal-to-noise ratio */ 3270e726966SHenrik Rydberg #define SN_ORIENT 10 /* orientation signal-to-noise ratio */ 32875e21e3fSHenrik Rydberg 329f89bd95cSHenrik Rydberg /* device constants */ 330f89bd95cSHenrik Rydberg static const struct bcm5974_config bcm5974_config_table[] = { 331f89bd95cSHenrik Rydberg { 332f89bd95cSHenrik Rydberg USB_DEVICE_ID_APPLE_WELLSPRING_ANSI, 333f89bd95cSHenrik Rydberg USB_DEVICE_ID_APPLE_WELLSPRING_ISO, 334f89bd95cSHenrik Rydberg USB_DEVICE_ID_APPLE_WELLSPRING_JIS, 335158e9287SHenrik Rydberg 0, 336f89bd95cSHenrik Rydberg 0x84, sizeof(struct bt_data), 337efbd3470SHenrik Rydberg 0x81, DATAFORMAT(TYPE1), 3380e726966SHenrik Rydberg { SN_PRESSURE, 0, 256 }, 3390e726966SHenrik Rydberg { SN_WIDTH, 0, 2048 }, 3400e726966SHenrik Rydberg { SN_COORD, -4824, 5342 }, 3410e726966SHenrik Rydberg { SN_COORD, -172, 5820 }, 3420e726966SHenrik Rydberg { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } 343f89bd95cSHenrik Rydberg }, 344f89bd95cSHenrik Rydberg { 345f89bd95cSHenrik Rydberg USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI, 346f89bd95cSHenrik Rydberg USB_DEVICE_ID_APPLE_WELLSPRING2_ISO, 347f89bd95cSHenrik Rydberg USB_DEVICE_ID_APPLE_WELLSPRING2_JIS, 348158e9287SHenrik Rydberg 0, 349f89bd95cSHenrik Rydberg 0x84, sizeof(struct bt_data), 350efbd3470SHenrik Rydberg 0x81, DATAFORMAT(TYPE1), 3510e726966SHenrik Rydberg { SN_PRESSURE, 0, 256 }, 3520e726966SHenrik Rydberg { SN_WIDTH, 0, 2048 }, 3530e726966SHenrik Rydberg { SN_COORD, -4824, 4824 }, 3540e726966SHenrik Rydberg { SN_COORD, -172, 4290 }, 3550e726966SHenrik Rydberg { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } 356f89bd95cSHenrik Rydberg }, 357158e9287SHenrik Rydberg { 358158e9287SHenrik Rydberg USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI, 359158e9287SHenrik Rydberg USB_DEVICE_ID_APPLE_WELLSPRING3_ISO, 360158e9287SHenrik Rydberg USB_DEVICE_ID_APPLE_WELLSPRING3_JIS, 361158e9287SHenrik Rydberg HAS_INTEGRATED_BUTTON, 362158e9287SHenrik Rydberg 0x84, sizeof(struct bt_data), 363efbd3470SHenrik Rydberg 0x81, DATAFORMAT(TYPE2), 3640e726966SHenrik Rydberg { SN_PRESSURE, 0, 300 }, 3650e726966SHenrik Rydberg { SN_WIDTH, 0, 2048 }, 3660e726966SHenrik Rydberg { SN_COORD, -4460, 5166 }, 3670e726966SHenrik Rydberg { SN_COORD, -75, 6700 }, 3680e726966SHenrik Rydberg { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } 369158e9287SHenrik Rydberg }, 3706021afcfSEdgar (gimli) Hucek { 3716021afcfSEdgar (gimli) Hucek USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI, 3726021afcfSEdgar (gimli) Hucek USB_DEVICE_ID_APPLE_WELLSPRING4_ISO, 3736021afcfSEdgar (gimli) Hucek USB_DEVICE_ID_APPLE_WELLSPRING4_JIS, 3746021afcfSEdgar (gimli) Hucek HAS_INTEGRATED_BUTTON, 3756021afcfSEdgar (gimli) Hucek 0x84, sizeof(struct bt_data), 376efbd3470SHenrik Rydberg 0x81, DATAFORMAT(TYPE2), 3770e726966SHenrik Rydberg { SN_PRESSURE, 0, 300 }, 3780e726966SHenrik Rydberg { SN_WIDTH, 0, 2048 }, 3790e726966SHenrik Rydberg { SN_COORD, -4620, 5140 }, 3800e726966SHenrik Rydberg { SN_COORD, -150, 6600 }, 3810e726966SHenrik Rydberg { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } 3826021afcfSEdgar (gimli) Hucek }, 3836021afcfSEdgar (gimli) Hucek { 3846021afcfSEdgar (gimli) Hucek USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI, 3856021afcfSEdgar (gimli) Hucek USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO, 3866021afcfSEdgar (gimli) Hucek USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS, 3876021afcfSEdgar (gimli) Hucek HAS_INTEGRATED_BUTTON, 3886021afcfSEdgar (gimli) Hucek 0x84, sizeof(struct bt_data), 389efbd3470SHenrik Rydberg 0x81, DATAFORMAT(TYPE2), 3900e726966SHenrik Rydberg { SN_PRESSURE, 0, 300 }, 3910e726966SHenrik Rydberg { SN_WIDTH, 0, 2048 }, 3920e726966SHenrik Rydberg { SN_COORD, -4616, 5112 }, 3930e726966SHenrik Rydberg { SN_COORD, -142, 5234 }, 3940e726966SHenrik Rydberg { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } 3956021afcfSEdgar (gimli) Hucek }, 39647340bd9SAndy Botting { 39747340bd9SAndy Botting USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI, 39847340bd9SAndy Botting USB_DEVICE_ID_APPLE_WELLSPRING5_ISO, 39947340bd9SAndy Botting USB_DEVICE_ID_APPLE_WELLSPRING5_JIS, 40047340bd9SAndy Botting HAS_INTEGRATED_BUTTON, 40147340bd9SAndy Botting 0x84, sizeof(struct bt_data), 402efbd3470SHenrik Rydberg 0x81, DATAFORMAT(TYPE2), 4030e726966SHenrik Rydberg { SN_PRESSURE, 0, 300 }, 4040e726966SHenrik Rydberg { SN_WIDTH, 0, 2048 }, 4050e726966SHenrik Rydberg { SN_COORD, -4415, 5050 }, 4060e726966SHenrik Rydberg { SN_COORD, -55, 6680 }, 4070e726966SHenrik Rydberg { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } 40847340bd9SAndy Botting }, 409db0b34b0SJoshua V. Dillon { 410db0b34b0SJoshua V. Dillon USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI, 411db0b34b0SJoshua V. Dillon USB_DEVICE_ID_APPLE_WELLSPRING6_ISO, 412db0b34b0SJoshua V. Dillon USB_DEVICE_ID_APPLE_WELLSPRING6_JIS, 413db0b34b0SJoshua V. Dillon HAS_INTEGRATED_BUTTON, 414db0b34b0SJoshua V. Dillon 0x84, sizeof(struct bt_data), 415efbd3470SHenrik Rydberg 0x81, DATAFORMAT(TYPE2), 4160e726966SHenrik Rydberg { SN_PRESSURE, 0, 300 }, 4170e726966SHenrik Rydberg { SN_WIDTH, 0, 2048 }, 4180e726966SHenrik Rydberg { SN_COORD, -4620, 5140 }, 4190e726966SHenrik Rydberg { SN_COORD, -150, 6600 }, 4200e726966SHenrik Rydberg { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } 421db0b34b0SJoshua V. Dillon }, 422c331eb58SAndrew Drake { 423c331eb58SAndrew Drake USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI, 424c331eb58SAndrew Drake USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO, 425c331eb58SAndrew Drake USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS, 426c331eb58SAndrew Drake HAS_INTEGRATED_BUTTON, 427c331eb58SAndrew Drake 0x84, sizeof(struct bt_data), 428efbd3470SHenrik Rydberg 0x81, DATAFORMAT(TYPE2), 4290e726966SHenrik Rydberg { SN_PRESSURE, 0, 300 }, 4300e726966SHenrik Rydberg { SN_WIDTH, 0, 2048 }, 4310e726966SHenrik Rydberg { SN_COORD, -4750, 5280 }, 4320e726966SHenrik Rydberg { SN_COORD, -150, 6730 }, 4330e726966SHenrik Rydberg { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } 434c331eb58SAndrew Drake }, 4351c601beaSPieter-Augustijn Van Malleghem { 4361c601beaSPieter-Augustijn Van Malleghem USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI, 4371c601beaSPieter-Augustijn Van Malleghem USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO, 4381c601beaSPieter-Augustijn Van Malleghem USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS, 4391c601beaSPieter-Augustijn Van Malleghem HAS_INTEGRATED_BUTTON, 4401c601beaSPieter-Augustijn Van Malleghem 0x84, sizeof(struct bt_data), 441efbd3470SHenrik Rydberg 0x81, DATAFORMAT(TYPE2), 4420e726966SHenrik Rydberg { SN_PRESSURE, 0, 300 }, 4430e726966SHenrik Rydberg { SN_WIDTH, 0, 2048 }, 4440e726966SHenrik Rydberg { SN_COORD, -4620, 5140 }, 4450e726966SHenrik Rydberg { SN_COORD, -150, 6600 }, 4460e726966SHenrik Rydberg { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } 4471c601beaSPieter-Augustijn Van Malleghem }, 4483dde22a9SHenrik Rydberg { 4493dde22a9SHenrik Rydberg USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI, 4503dde22a9SHenrik Rydberg USB_DEVICE_ID_APPLE_WELLSPRING7_ISO, 4513dde22a9SHenrik Rydberg USB_DEVICE_ID_APPLE_WELLSPRING7_JIS, 4523dde22a9SHenrik Rydberg HAS_INTEGRATED_BUTTON, 4533dde22a9SHenrik Rydberg 0x84, sizeof(struct bt_data), 454efbd3470SHenrik Rydberg 0x81, DATAFORMAT(TYPE2), 4550e726966SHenrik Rydberg { SN_PRESSURE, 0, 300 }, 4560e726966SHenrik Rydberg { SN_WIDTH, 0, 2048 }, 4570e726966SHenrik Rydberg { SN_COORD, -4750, 5280 }, 4580e726966SHenrik Rydberg { SN_COORD, -150, 6730 }, 4590e726966SHenrik Rydberg { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } 4603dde22a9SHenrik Rydberg }, 4618d80da90SDirk Hohndel { 4628d80da90SDirk Hohndel USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI, 4638d80da90SDirk Hohndel USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO, 4648d80da90SDirk Hohndel USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS, 4658d80da90SDirk Hohndel HAS_INTEGRATED_BUTTON, 4668d80da90SDirk Hohndel 0x84, sizeof(struct bt_data), 467efbd3470SHenrik Rydberg 0x81, DATAFORMAT(TYPE2), 4688d80da90SDirk Hohndel { SN_PRESSURE, 0, 300 }, 4698d80da90SDirk Hohndel { SN_WIDTH, 0, 2048 }, 4708d80da90SDirk Hohndel { SN_COORD, -4750, 5280 }, 4718d80da90SDirk Hohndel { SN_COORD, -150, 6730 }, 4728d80da90SDirk Hohndel { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } 4738d80da90SDirk Hohndel }, 474148c1c8aSDmitry Torokhov { 475148c1c8aSDmitry Torokhov USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI, 476148c1c8aSDmitry Torokhov USB_DEVICE_ID_APPLE_WELLSPRING8_ISO, 477148c1c8aSDmitry Torokhov USB_DEVICE_ID_APPLE_WELLSPRING8_JIS, 478148c1c8aSDmitry Torokhov HAS_INTEGRATED_BUTTON, 479148c1c8aSDmitry Torokhov 0, sizeof(struct bt_data), 480efbd3470SHenrik Rydberg 0x83, DATAFORMAT(TYPE3), 481148c1c8aSDmitry Torokhov { SN_PRESSURE, 0, 300 }, 482148c1c8aSDmitry Torokhov { SN_WIDTH, 0, 2048 }, 483148c1c8aSDmitry Torokhov { SN_COORD, -4620, 5140 }, 484148c1c8aSDmitry Torokhov { SN_COORD, -150, 6600 }, 485148c1c8aSDmitry Torokhov { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } 486148c1c8aSDmitry Torokhov }, 487*d5806926SJohn Horan { 488*d5806926SJohn Horan USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI, 489*d5806926SJohn Horan USB_DEVICE_ID_APPLE_WELLSPRING9_ISO, 490*d5806926SJohn Horan USB_DEVICE_ID_APPLE_WELLSPRING9_JIS, 491*d5806926SJohn Horan HAS_INTEGRATED_BUTTON, 492*d5806926SJohn Horan 0, sizeof(struct bt_data), 493*d5806926SJohn Horan 0x83, DATAFORMAT(TYPE4), 494*d5806926SJohn Horan { SN_PRESSURE, 0, 300 }, 495*d5806926SJohn Horan { SN_WIDTH, 0, 2048 }, 496*d5806926SJohn Horan { SN_COORD, -4828, 5345 }, 497*d5806926SJohn Horan { SN_COORD, -203, 6803 }, 498*d5806926SJohn Horan { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } 499*d5806926SJohn Horan }, 500f89bd95cSHenrik Rydberg {} 501f89bd95cSHenrik Rydberg }; 502f89bd95cSHenrik Rydberg 503f89bd95cSHenrik Rydberg /* return the device-specific configuration by device */ 504f89bd95cSHenrik Rydberg static const struct bcm5974_config *bcm5974_get_config(struct usb_device *udev) 505f89bd95cSHenrik Rydberg { 506f89bd95cSHenrik Rydberg u16 id = le16_to_cpu(udev->descriptor.idProduct); 507f89bd95cSHenrik Rydberg const struct bcm5974_config *cfg; 508f89bd95cSHenrik Rydberg 509f89bd95cSHenrik Rydberg for (cfg = bcm5974_config_table; cfg->ansi; ++cfg) 510f89bd95cSHenrik Rydberg if (cfg->ansi == id || cfg->iso == id || cfg->jis == id) 511f89bd95cSHenrik Rydberg return cfg; 512f89bd95cSHenrik Rydberg 513f89bd95cSHenrik Rydberg return bcm5974_config_table; 514f89bd95cSHenrik Rydberg } 515f89bd95cSHenrik Rydberg 516f89bd95cSHenrik Rydberg /* convert 16-bit little endian to signed integer */ 517f89bd95cSHenrik Rydberg static inline int raw2int(__le16 x) 518f89bd95cSHenrik Rydberg { 519f89bd95cSHenrik Rydberg return (signed short)le16_to_cpu(x); 520f89bd95cSHenrik Rydberg } 521f89bd95cSHenrik Rydberg 5220e726966SHenrik Rydberg static void set_abs(struct input_dev *input, unsigned int code, 5230e726966SHenrik Rydberg const struct bcm5974_param *p) 524f89bd95cSHenrik Rydberg { 5250e726966SHenrik Rydberg int fuzz = p->snratio ? (p->max - p->min) / p->snratio : 0; 5260e726966SHenrik Rydberg input_set_abs_params(input, code, p->min, p->max, fuzz, 0); 527f89bd95cSHenrik Rydberg } 528f89bd95cSHenrik Rydberg 529f89bd95cSHenrik Rydberg /* setup which logical events to report */ 530f89bd95cSHenrik Rydberg static void setup_events_to_report(struct input_dev *input_dev, 531f89bd95cSHenrik Rydberg const struct bcm5974_config *cfg) 532f89bd95cSHenrik Rydberg { 533f89bd95cSHenrik Rydberg __set_bit(EV_ABS, input_dev->evbit); 534f89bd95cSHenrik Rydberg 5350e726966SHenrik Rydberg /* for synaptics only */ 5360e726966SHenrik Rydberg input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 5, 0); 5370e726966SHenrik Rydberg input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 16, 0, 0); 5380e726966SHenrik Rydberg 5396f2701b7SHenrik Rydberg /* finger touch area */ 5400e726966SHenrik Rydberg set_abs(input_dev, ABS_MT_TOUCH_MAJOR, &cfg->w); 5410e726966SHenrik Rydberg set_abs(input_dev, ABS_MT_TOUCH_MINOR, &cfg->w); 5426f2701b7SHenrik Rydberg /* finger approach area */ 5430e726966SHenrik Rydberg set_abs(input_dev, ABS_MT_WIDTH_MAJOR, &cfg->w); 5440e726966SHenrik Rydberg set_abs(input_dev, ABS_MT_WIDTH_MINOR, &cfg->w); 5456f2701b7SHenrik Rydberg /* finger orientation */ 5460e726966SHenrik Rydberg set_abs(input_dev, ABS_MT_ORIENTATION, &cfg->o); 5476f2701b7SHenrik Rydberg /* finger position */ 5480e726966SHenrik Rydberg set_abs(input_dev, ABS_MT_POSITION_X, &cfg->x); 5490e726966SHenrik Rydberg set_abs(input_dev, ABS_MT_POSITION_Y, &cfg->y); 5506f2701b7SHenrik Rydberg 551f89bd95cSHenrik Rydberg __set_bit(EV_KEY, input_dev->evbit); 552f89bd95cSHenrik Rydberg __set_bit(BTN_LEFT, input_dev->keybit); 553c13aea03SHenrik Rydberg 55452965cc0SJussi Pakkanen if (cfg->caps & HAS_INTEGRATED_BUTTON) 55552965cc0SJussi Pakkanen __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); 55652965cc0SJussi Pakkanen 55751c80b74SHenrik Rydberg input_mt_init_slots(input_dev, MAX_FINGERS, 55851c80b74SHenrik Rydberg INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK); 559f89bd95cSHenrik Rydberg } 560f89bd95cSHenrik Rydberg 561f89bd95cSHenrik Rydberg /* report button data as logical button state */ 562f89bd95cSHenrik Rydberg static int report_bt_state(struct bcm5974 *dev, int size) 563f89bd95cSHenrik Rydberg { 564f89bd95cSHenrik Rydberg if (size != sizeof(struct bt_data)) 565f89bd95cSHenrik Rydberg return -EIO; 566f89bd95cSHenrik Rydberg 56753402193SHenrik Rydberg dprintk(7, 56853402193SHenrik Rydberg "bcm5974: button data: %x %x %x %x\n", 56953402193SHenrik Rydberg dev->bt_data->unknown1, dev->bt_data->button, 57053402193SHenrik Rydberg dev->bt_data->rel_x, dev->bt_data->rel_y); 57153402193SHenrik Rydberg 572f89bd95cSHenrik Rydberg input_report_key(dev->input, BTN_LEFT, dev->bt_data->button); 573f89bd95cSHenrik Rydberg input_sync(dev->input); 574f89bd95cSHenrik Rydberg 575f89bd95cSHenrik Rydberg return 0; 576f89bd95cSHenrik Rydberg } 577f89bd95cSHenrik Rydberg 57851c80b74SHenrik Rydberg static void report_finger_data(struct input_dev *input, int slot, 57951c80b74SHenrik Rydberg const struct input_mt_pos *pos, 5806f2701b7SHenrik Rydberg const struct tp_finger *f) 5816f2701b7SHenrik Rydberg { 58251c80b74SHenrik Rydberg input_mt_slot(input, slot); 58351c80b74SHenrik Rydberg input_mt_report_slot_state(input, MT_TOOL_FINGER, true); 58451c80b74SHenrik Rydberg 58557157becSHenrik Rydberg input_report_abs(input, ABS_MT_TOUCH_MAJOR, 586f17953abSHenrik Rydberg raw2int(f->touch_major) << 1); 58757157becSHenrik Rydberg input_report_abs(input, ABS_MT_TOUCH_MINOR, 588f17953abSHenrik Rydberg raw2int(f->touch_minor) << 1); 58957157becSHenrik Rydberg input_report_abs(input, ABS_MT_WIDTH_MAJOR, 590f17953abSHenrik Rydberg raw2int(f->tool_major) << 1); 59157157becSHenrik Rydberg input_report_abs(input, ABS_MT_WIDTH_MINOR, 592f17953abSHenrik Rydberg raw2int(f->tool_minor) << 1); 5936f2701b7SHenrik Rydberg input_report_abs(input, ABS_MT_ORIENTATION, 5946f2701b7SHenrik Rydberg MAX_FINGER_ORIENTATION - raw2int(f->orientation)); 59551c80b74SHenrik Rydberg input_report_abs(input, ABS_MT_POSITION_X, pos->x); 59651c80b74SHenrik Rydberg input_report_abs(input, ABS_MT_POSITION_Y, pos->y); 5976f2701b7SHenrik Rydberg } 5986f2701b7SHenrik Rydberg 5990e726966SHenrik Rydberg static void report_synaptics_data(struct input_dev *input, 6000e726966SHenrik Rydberg const struct bcm5974_config *cfg, 6010e726966SHenrik Rydberg const struct tp_finger *f, int raw_n) 6020e726966SHenrik Rydberg { 6030e726966SHenrik Rydberg int abs_p = 0, abs_w = 0; 6040e726966SHenrik Rydberg 6050e726966SHenrik Rydberg if (raw_n) { 6060e726966SHenrik Rydberg int p = raw2int(f->touch_major); 6070e726966SHenrik Rydberg int w = raw2int(f->tool_major); 6080e726966SHenrik Rydberg if (p > 0 && raw2int(f->origin)) { 6090e726966SHenrik Rydberg abs_p = clamp_val(256 * p / cfg->p.max, 0, 255); 6100e726966SHenrik Rydberg abs_w = clamp_val(16 * w / cfg->w.max, 0, 15); 6110e726966SHenrik Rydberg } 6120e726966SHenrik Rydberg } 6130e726966SHenrik Rydberg 6140e726966SHenrik Rydberg input_report_abs(input, ABS_PRESSURE, abs_p); 6150e726966SHenrik Rydberg input_report_abs(input, ABS_TOOL_WIDTH, abs_w); 6160e726966SHenrik Rydberg } 6170e726966SHenrik Rydberg 618f89bd95cSHenrik Rydberg /* report trackpad data as logical trackpad state */ 619f89bd95cSHenrik Rydberg static int report_tp_state(struct bcm5974 *dev, int size) 620f89bd95cSHenrik Rydberg { 621f89bd95cSHenrik Rydberg const struct bcm5974_config *c = &dev->cfg; 6229894cf0fSHenrik Rydberg const struct tp_finger *f; 623f89bd95cSHenrik Rydberg struct input_dev *input = dev->input; 62451c80b74SHenrik Rydberg int raw_n, i, n = 0; 625f89bd95cSHenrik Rydberg 626efbd3470SHenrik Rydberg if (size < c->tp_header || (size - c->tp_header) % c->tp_fsize != 0) 627f89bd95cSHenrik Rydberg return -EIO; 628f89bd95cSHenrik Rydberg 629efbd3470SHenrik Rydberg raw_n = (size - c->tp_header) / c->tp_fsize; 6309894cf0fSHenrik Rydberg 63151c80b74SHenrik Rydberg for (i = 0; i < raw_n; i++) { 632efbd3470SHenrik Rydberg f = get_tp_finger(dev, i); 633efbd3470SHenrik Rydberg if (raw2int(f->touch_major) == 0) 63451c80b74SHenrik Rydberg continue; 635efbd3470SHenrik Rydberg dev->pos[n].x = raw2int(f->abs_x); 636efbd3470SHenrik Rydberg dev->pos[n].y = c->y.min + c->y.max - raw2int(f->abs_y); 637efbd3470SHenrik Rydberg dev->index[n++] = f; 63875e21e3fSHenrik Rydberg } 639f89bd95cSHenrik Rydberg 640448c7f38SHenrik Rydberg input_mt_assign_slots(input, dev->slots, dev->pos, n, 0); 64151c80b74SHenrik Rydberg 64251c80b74SHenrik Rydberg for (i = 0; i < n; i++) 64351c80b74SHenrik Rydberg report_finger_data(input, dev->slots[i], 64451c80b74SHenrik Rydberg &dev->pos[i], dev->index[i]); 64551c80b74SHenrik Rydberg 64651c80b74SHenrik Rydberg input_mt_sync_frame(input); 64750635115SHenrik Rydberg 648efbd3470SHenrik Rydberg report_synaptics_data(input, c, get_tp_finger(dev, 0), raw_n); 64975e21e3fSHenrik Rydberg 650efbd3470SHenrik Rydberg /* later types report button events via integrated button only */ 651efbd3470SHenrik Rydberg if (c->caps & HAS_INTEGRATED_BUTTON) { 652efbd3470SHenrik Rydberg int ibt = raw2int(dev->tp_data[c->tp_button]); 653158e9287SHenrik Rydberg input_report_key(input, BTN_LEFT, ibt); 6540e726966SHenrik Rydberg } 655158e9287SHenrik Rydberg 656f89bd95cSHenrik Rydberg input_sync(input); 657f89bd95cSHenrik Rydberg 658f89bd95cSHenrik Rydberg return 0; 659f89bd95cSHenrik Rydberg } 660f89bd95cSHenrik Rydberg 661cd72ad3fSHenrik Rydberg static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on) 662f89bd95cSHenrik Rydberg { 663efbd3470SHenrik Rydberg const struct bcm5974_config *c = &dev->cfg; 664f89bd95cSHenrik Rydberg int retval = 0, size; 665148c1c8aSDmitry Torokhov char *data; 666f89bd95cSHenrik Rydberg 667148c1c8aSDmitry Torokhov /* Type 3 does not require a mode switch */ 668148c1c8aSDmitry Torokhov if (dev->cfg.tp_type == TYPE3) 669148c1c8aSDmitry Torokhov return 0; 670148c1c8aSDmitry Torokhov 671efbd3470SHenrik Rydberg data = kmalloc(c->um_size, GFP_KERNEL); 672f89bd95cSHenrik Rydberg if (!data) { 673ab943ca8SGreg Kroah-Hartman dev_err(&dev->intf->dev, "out of memory\n"); 674f89bd95cSHenrik Rydberg retval = -ENOMEM; 675f89bd95cSHenrik Rydberg goto out; 676f89bd95cSHenrik Rydberg } 677f89bd95cSHenrik Rydberg 678f89bd95cSHenrik Rydberg /* read configuration */ 679f89bd95cSHenrik Rydberg size = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), 680f89bd95cSHenrik Rydberg BCM5974_WELLSPRING_MODE_READ_REQUEST_ID, 681f89bd95cSHenrik Rydberg USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 682efbd3470SHenrik Rydberg c->um_req_val, c->um_req_idx, data, c->um_size, 5000); 683f89bd95cSHenrik Rydberg 684efbd3470SHenrik Rydberg if (size != c->um_size) { 685ab943ca8SGreg Kroah-Hartman dev_err(&dev->intf->dev, "could not read from device\n"); 686f89bd95cSHenrik Rydberg retval = -EIO; 687f89bd95cSHenrik Rydberg goto out; 688f89bd95cSHenrik Rydberg } 689f89bd95cSHenrik Rydberg 690f89bd95cSHenrik Rydberg /* apply the mode switch */ 691efbd3470SHenrik Rydberg data[c->um_switch_idx] = on ? c->um_switch_on : c->um_switch_off; 692f89bd95cSHenrik Rydberg 693f89bd95cSHenrik Rydberg /* write configuration */ 694f89bd95cSHenrik Rydberg size = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), 695f89bd95cSHenrik Rydberg BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID, 696f89bd95cSHenrik Rydberg USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 697efbd3470SHenrik Rydberg c->um_req_val, c->um_req_idx, data, c->um_size, 5000); 698f89bd95cSHenrik Rydberg 699efbd3470SHenrik Rydberg if (size != c->um_size) { 700ab943ca8SGreg Kroah-Hartman dev_err(&dev->intf->dev, "could not write to device\n"); 701f89bd95cSHenrik Rydberg retval = -EIO; 702f89bd95cSHenrik Rydberg goto out; 703f89bd95cSHenrik Rydberg } 704f89bd95cSHenrik Rydberg 705cd72ad3fSHenrik Rydberg dprintk(2, "bcm5974: switched to %s mode.\n", 706cd72ad3fSHenrik Rydberg on ? "wellspring" : "normal"); 707f89bd95cSHenrik Rydberg 708f89bd95cSHenrik Rydberg out: 709f89bd95cSHenrik Rydberg kfree(data); 710f89bd95cSHenrik Rydberg return retval; 711f89bd95cSHenrik Rydberg } 712f89bd95cSHenrik Rydberg 713f89bd95cSHenrik Rydberg static void bcm5974_irq_button(struct urb *urb) 714f89bd95cSHenrik Rydberg { 715f89bd95cSHenrik Rydberg struct bcm5974 *dev = urb->context; 716ab943ca8SGreg Kroah-Hartman struct usb_interface *intf = dev->intf; 717f89bd95cSHenrik Rydberg int error; 718f89bd95cSHenrik Rydberg 719f89bd95cSHenrik Rydberg switch (urb->status) { 720f89bd95cSHenrik Rydberg case 0: 721f89bd95cSHenrik Rydberg break; 722f89bd95cSHenrik Rydberg case -EOVERFLOW: 723f89bd95cSHenrik Rydberg case -ECONNRESET: 724f89bd95cSHenrik Rydberg case -ENOENT: 725f89bd95cSHenrik Rydberg case -ESHUTDOWN: 726ab943ca8SGreg Kroah-Hartman dev_dbg(&intf->dev, "button urb shutting down: %d\n", 727bd028769SGreg Kroah-Hartman urb->status); 728f89bd95cSHenrik Rydberg return; 729f89bd95cSHenrik Rydberg default: 730ab943ca8SGreg Kroah-Hartman dev_dbg(&intf->dev, "button urb status: %d\n", urb->status); 731f89bd95cSHenrik Rydberg goto exit; 732f89bd95cSHenrik Rydberg } 733f89bd95cSHenrik Rydberg 734f89bd95cSHenrik Rydberg if (report_bt_state(dev, dev->bt_urb->actual_length)) 735f89bd95cSHenrik Rydberg dprintk(1, "bcm5974: bad button package, length: %d\n", 736f89bd95cSHenrik Rydberg dev->bt_urb->actual_length); 737f89bd95cSHenrik Rydberg 738f89bd95cSHenrik Rydberg exit: 739f89bd95cSHenrik Rydberg error = usb_submit_urb(dev->bt_urb, GFP_ATOMIC); 740f89bd95cSHenrik Rydberg if (error) 741ab943ca8SGreg Kroah-Hartman dev_err(&intf->dev, "button urb failed: %d\n", error); 742f89bd95cSHenrik Rydberg } 743f89bd95cSHenrik Rydberg 744f89bd95cSHenrik Rydberg static void bcm5974_irq_trackpad(struct urb *urb) 745f89bd95cSHenrik Rydberg { 746f89bd95cSHenrik Rydberg struct bcm5974 *dev = urb->context; 747ab943ca8SGreg Kroah-Hartman struct usb_interface *intf = dev->intf; 748f89bd95cSHenrik Rydberg int error; 749f89bd95cSHenrik Rydberg 750f89bd95cSHenrik Rydberg switch (urb->status) { 751f89bd95cSHenrik Rydberg case 0: 752f89bd95cSHenrik Rydberg break; 753f89bd95cSHenrik Rydberg case -EOVERFLOW: 754f89bd95cSHenrik Rydberg case -ECONNRESET: 755f89bd95cSHenrik Rydberg case -ENOENT: 756f89bd95cSHenrik Rydberg case -ESHUTDOWN: 757ab943ca8SGreg Kroah-Hartman dev_dbg(&intf->dev, "trackpad urb shutting down: %d\n", 758bd028769SGreg Kroah-Hartman urb->status); 759f89bd95cSHenrik Rydberg return; 760f89bd95cSHenrik Rydberg default: 761ab943ca8SGreg Kroah-Hartman dev_dbg(&intf->dev, "trackpad urb status: %d\n", urb->status); 762f89bd95cSHenrik Rydberg goto exit; 763f89bd95cSHenrik Rydberg } 764f89bd95cSHenrik Rydberg 765f89bd95cSHenrik Rydberg /* control response ignored */ 766f89bd95cSHenrik Rydberg if (dev->tp_urb->actual_length == 2) 767f89bd95cSHenrik Rydberg goto exit; 768f89bd95cSHenrik Rydberg 769f89bd95cSHenrik Rydberg if (report_tp_state(dev, dev->tp_urb->actual_length)) 770f89bd95cSHenrik Rydberg dprintk(1, "bcm5974: bad trackpad package, length: %d\n", 771f89bd95cSHenrik Rydberg dev->tp_urb->actual_length); 772f89bd95cSHenrik Rydberg 773f89bd95cSHenrik Rydberg exit: 774f89bd95cSHenrik Rydberg error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC); 775f89bd95cSHenrik Rydberg if (error) 776ab943ca8SGreg Kroah-Hartman dev_err(&intf->dev, "trackpad urb failed: %d\n", error); 777f89bd95cSHenrik Rydberg } 778f89bd95cSHenrik Rydberg 779f89bd95cSHenrik Rydberg /* 780f89bd95cSHenrik Rydberg * The Wellspring trackpad, like many recent Apple trackpads, share 781f89bd95cSHenrik Rydberg * the usb device with the keyboard. Since keyboards are usually 782f89bd95cSHenrik Rydberg * handled by the HID system, the device ends up being handled by two 783f89bd95cSHenrik Rydberg * modules. Setting up the device therefore becomes slightly 784f89bd95cSHenrik Rydberg * complicated. To enable multitouch features, a mode switch is 785f89bd95cSHenrik Rydberg * required, which is usually applied via the control interface of the 786f89bd95cSHenrik Rydberg * device. It can be argued where this switch should take place. In 787f89bd95cSHenrik Rydberg * some drivers, like appletouch, the switch is made during 788f89bd95cSHenrik Rydberg * probe. However, the hid module may also alter the state of the 789f89bd95cSHenrik Rydberg * device, resulting in trackpad malfunction under certain 790f89bd95cSHenrik Rydberg * circumstances. To get around this problem, there is at least one 791f89bd95cSHenrik Rydberg * example that utilizes the USB_QUIRK_RESET_RESUME quirk in order to 79225985edcSLucas De Marchi * receive a reset_resume request rather than the normal resume. 793f89bd95cSHenrik Rydberg * Since the implementation of reset_resume is equal to mode switch 794f89bd95cSHenrik Rydberg * plus start_traffic, it seems easier to always do the switch when 795f89bd95cSHenrik Rydberg * starting traffic on the device. 796f89bd95cSHenrik Rydberg */ 797f89bd95cSHenrik Rydberg static int bcm5974_start_traffic(struct bcm5974 *dev) 798f89bd95cSHenrik Rydberg { 7991719ec41SLuo Jinghua int error; 8001719ec41SLuo Jinghua 8011719ec41SLuo Jinghua error = bcm5974_wellspring_mode(dev, true); 8021719ec41SLuo Jinghua if (error) { 803f89bd95cSHenrik Rydberg dprintk(1, "bcm5974: mode switch failed\n"); 8041719ec41SLuo Jinghua goto err_out; 805f89bd95cSHenrik Rydberg } 806f89bd95cSHenrik Rydberg 80743f482b4SHenrik Rydberg if (dev->bt_urb) { 8081719ec41SLuo Jinghua error = usb_submit_urb(dev->bt_urb, GFP_KERNEL); 8091719ec41SLuo Jinghua if (error) 8101719ec41SLuo Jinghua goto err_reset_mode; 81143f482b4SHenrik Rydberg } 812f89bd95cSHenrik Rydberg 8131719ec41SLuo Jinghua error = usb_submit_urb(dev->tp_urb, GFP_KERNEL); 8141719ec41SLuo Jinghua if (error) 815f89bd95cSHenrik Rydberg goto err_kill_bt; 816f89bd95cSHenrik Rydberg 817f89bd95cSHenrik Rydberg return 0; 818f89bd95cSHenrik Rydberg 819f89bd95cSHenrik Rydberg err_kill_bt: 820f89bd95cSHenrik Rydberg usb_kill_urb(dev->bt_urb); 8211719ec41SLuo Jinghua err_reset_mode: 8221719ec41SLuo Jinghua bcm5974_wellspring_mode(dev, false); 8231719ec41SLuo Jinghua err_out: 8241719ec41SLuo Jinghua return error; 825f89bd95cSHenrik Rydberg } 826f89bd95cSHenrik Rydberg 827f89bd95cSHenrik Rydberg static void bcm5974_pause_traffic(struct bcm5974 *dev) 828f89bd95cSHenrik Rydberg { 829f89bd95cSHenrik Rydberg usb_kill_urb(dev->tp_urb); 830f89bd95cSHenrik Rydberg usb_kill_urb(dev->bt_urb); 831cd72ad3fSHenrik Rydberg bcm5974_wellspring_mode(dev, false); 832f89bd95cSHenrik Rydberg } 833f89bd95cSHenrik Rydberg 834f89bd95cSHenrik Rydberg /* 835f89bd95cSHenrik Rydberg * The code below implements open/close and manual suspend/resume. 836f89bd95cSHenrik Rydberg * All functions may be called in random order. 837f89bd95cSHenrik Rydberg * 838f89bd95cSHenrik Rydberg * Opening a suspended device fails with EACCES - permission denied. 839f89bd95cSHenrik Rydberg * 840f89bd95cSHenrik Rydberg * Failing a resume leaves the device resumed but closed. 841f89bd95cSHenrik Rydberg */ 842f89bd95cSHenrik Rydberg static int bcm5974_open(struct input_dev *input) 843f89bd95cSHenrik Rydberg { 844f89bd95cSHenrik Rydberg struct bcm5974 *dev = input_get_drvdata(input); 845f89bd95cSHenrik Rydberg int error; 846f89bd95cSHenrik Rydberg 84788da765fSDmitry Torokhov error = usb_autopm_get_interface(dev->intf); 84888da765fSDmitry Torokhov if (error) 84988da765fSDmitry Torokhov return error; 85088da765fSDmitry Torokhov 851f89bd95cSHenrik Rydberg mutex_lock(&dev->pm_mutex); 852f89bd95cSHenrik Rydberg 853f89bd95cSHenrik Rydberg error = bcm5974_start_traffic(dev); 854f89bd95cSHenrik Rydberg if (!error) 855f89bd95cSHenrik Rydberg dev->opened = 1; 856f89bd95cSHenrik Rydberg 857f89bd95cSHenrik Rydberg mutex_unlock(&dev->pm_mutex); 858f89bd95cSHenrik Rydberg 85988da765fSDmitry Torokhov if (error) 86088da765fSDmitry Torokhov usb_autopm_put_interface(dev->intf); 86188da765fSDmitry Torokhov 862f89bd95cSHenrik Rydberg return error; 863f89bd95cSHenrik Rydberg } 864f89bd95cSHenrik Rydberg 865f89bd95cSHenrik Rydberg static void bcm5974_close(struct input_dev *input) 866f89bd95cSHenrik Rydberg { 867f89bd95cSHenrik Rydberg struct bcm5974 *dev = input_get_drvdata(input); 868f89bd95cSHenrik Rydberg 869f89bd95cSHenrik Rydberg mutex_lock(&dev->pm_mutex); 870f89bd95cSHenrik Rydberg 871f89bd95cSHenrik Rydberg bcm5974_pause_traffic(dev); 872f89bd95cSHenrik Rydberg dev->opened = 0; 873f89bd95cSHenrik Rydberg 874f89bd95cSHenrik Rydberg mutex_unlock(&dev->pm_mutex); 87588da765fSDmitry Torokhov 87688da765fSDmitry Torokhov usb_autopm_put_interface(dev->intf); 877f89bd95cSHenrik Rydberg } 878f89bd95cSHenrik Rydberg 879f89bd95cSHenrik Rydberg static int bcm5974_suspend(struct usb_interface *iface, pm_message_t message) 880f89bd95cSHenrik Rydberg { 881f89bd95cSHenrik Rydberg struct bcm5974 *dev = usb_get_intfdata(iface); 882f89bd95cSHenrik Rydberg 883f89bd95cSHenrik Rydberg mutex_lock(&dev->pm_mutex); 884f89bd95cSHenrik Rydberg 885f89bd95cSHenrik Rydberg if (dev->opened) 886f89bd95cSHenrik Rydberg bcm5974_pause_traffic(dev); 887f89bd95cSHenrik Rydberg 888f89bd95cSHenrik Rydberg mutex_unlock(&dev->pm_mutex); 889f89bd95cSHenrik Rydberg 890f89bd95cSHenrik Rydberg return 0; 891f89bd95cSHenrik Rydberg } 892f89bd95cSHenrik Rydberg 893f89bd95cSHenrik Rydberg static int bcm5974_resume(struct usb_interface *iface) 894f89bd95cSHenrik Rydberg { 895f89bd95cSHenrik Rydberg struct bcm5974 *dev = usb_get_intfdata(iface); 896f89bd95cSHenrik Rydberg int error = 0; 897f89bd95cSHenrik Rydberg 898f89bd95cSHenrik Rydberg mutex_lock(&dev->pm_mutex); 899f89bd95cSHenrik Rydberg 900f89bd95cSHenrik Rydberg if (dev->opened) 901f89bd95cSHenrik Rydberg error = bcm5974_start_traffic(dev); 902f89bd95cSHenrik Rydberg 903f89bd95cSHenrik Rydberg mutex_unlock(&dev->pm_mutex); 904f89bd95cSHenrik Rydberg 905f89bd95cSHenrik Rydberg return error; 906f89bd95cSHenrik Rydberg } 907f89bd95cSHenrik Rydberg 908f89bd95cSHenrik Rydberg static int bcm5974_probe(struct usb_interface *iface, 909f89bd95cSHenrik Rydberg const struct usb_device_id *id) 910f89bd95cSHenrik Rydberg { 911f89bd95cSHenrik Rydberg struct usb_device *udev = interface_to_usbdev(iface); 912f89bd95cSHenrik Rydberg const struct bcm5974_config *cfg; 913f89bd95cSHenrik Rydberg struct bcm5974 *dev; 914f89bd95cSHenrik Rydberg struct input_dev *input_dev; 915f89bd95cSHenrik Rydberg int error = -ENOMEM; 916f89bd95cSHenrik Rydberg 917f89bd95cSHenrik Rydberg /* find the product index */ 918f89bd95cSHenrik Rydberg cfg = bcm5974_get_config(udev); 919f89bd95cSHenrik Rydberg 920f89bd95cSHenrik Rydberg /* allocate memory for our device state and initialize it */ 921f89bd95cSHenrik Rydberg dev = kzalloc(sizeof(struct bcm5974), GFP_KERNEL); 922f89bd95cSHenrik Rydberg input_dev = input_allocate_device(); 923f89bd95cSHenrik Rydberg if (!dev || !input_dev) { 9246c1d1b24SGreg Kroah-Hartman dev_err(&iface->dev, "out of memory\n"); 925f89bd95cSHenrik Rydberg goto err_free_devs; 926f89bd95cSHenrik Rydberg } 927f89bd95cSHenrik Rydberg 928f89bd95cSHenrik Rydberg dev->udev = udev; 92988da765fSDmitry Torokhov dev->intf = iface; 930f89bd95cSHenrik Rydberg dev->input = input_dev; 931f89bd95cSHenrik Rydberg dev->cfg = *cfg; 932f89bd95cSHenrik Rydberg mutex_init(&dev->pm_mutex); 933f89bd95cSHenrik Rydberg 934f89bd95cSHenrik Rydberg /* setup urbs */ 93543f482b4SHenrik Rydberg if (cfg->tp_type == TYPE1) { 936f89bd95cSHenrik Rydberg dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL); 937f89bd95cSHenrik Rydberg if (!dev->bt_urb) 938f89bd95cSHenrik Rydberg goto err_free_devs; 93943f482b4SHenrik Rydberg } 940f89bd95cSHenrik Rydberg 941f89bd95cSHenrik Rydberg dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL); 942f89bd95cSHenrik Rydberg if (!dev->tp_urb) 943f89bd95cSHenrik Rydberg goto err_free_bt_urb; 944f89bd95cSHenrik Rydberg 94543f482b4SHenrik Rydberg if (dev->bt_urb) { 946997ea58eSDaniel Mack dev->bt_data = usb_alloc_coherent(dev->udev, 947f89bd95cSHenrik Rydberg dev->cfg.bt_datalen, GFP_KERNEL, 948f89bd95cSHenrik Rydberg &dev->bt_urb->transfer_dma); 949f89bd95cSHenrik Rydberg if (!dev->bt_data) 950f89bd95cSHenrik Rydberg goto err_free_urb; 95143f482b4SHenrik Rydberg } 952f89bd95cSHenrik Rydberg 953997ea58eSDaniel Mack dev->tp_data = usb_alloc_coherent(dev->udev, 954f89bd95cSHenrik Rydberg dev->cfg.tp_datalen, GFP_KERNEL, 955f89bd95cSHenrik Rydberg &dev->tp_urb->transfer_dma); 956f89bd95cSHenrik Rydberg if (!dev->tp_data) 957f89bd95cSHenrik Rydberg goto err_free_bt_buffer; 958f89bd95cSHenrik Rydberg 95943f482b4SHenrik Rydberg if (dev->bt_urb) 960f89bd95cSHenrik Rydberg usb_fill_int_urb(dev->bt_urb, udev, 961f89bd95cSHenrik Rydberg usb_rcvintpipe(udev, cfg->bt_ep), 962f89bd95cSHenrik Rydberg dev->bt_data, dev->cfg.bt_datalen, 963f89bd95cSHenrik Rydberg bcm5974_irq_button, dev, 1); 964f89bd95cSHenrik Rydberg 965f89bd95cSHenrik Rydberg usb_fill_int_urb(dev->tp_urb, udev, 966f89bd95cSHenrik Rydberg usb_rcvintpipe(udev, cfg->tp_ep), 967f89bd95cSHenrik Rydberg dev->tp_data, dev->cfg.tp_datalen, 968f89bd95cSHenrik Rydberg bcm5974_irq_trackpad, dev, 1); 969f89bd95cSHenrik Rydberg 970f89bd95cSHenrik Rydberg /* create bcm5974 device */ 971f89bd95cSHenrik Rydberg usb_make_path(udev, dev->phys, sizeof(dev->phys)); 972f89bd95cSHenrik Rydberg strlcat(dev->phys, "/input0", sizeof(dev->phys)); 973f89bd95cSHenrik Rydberg 974f89bd95cSHenrik Rydberg input_dev->name = "bcm5974"; 975f89bd95cSHenrik Rydberg input_dev->phys = dev->phys; 976f89bd95cSHenrik Rydberg usb_to_input_id(dev->udev, &input_dev->id); 977158e9287SHenrik Rydberg /* report driver capabilities via the version field */ 978158e9287SHenrik Rydberg input_dev->id.version = cfg->caps; 979f89bd95cSHenrik Rydberg input_dev->dev.parent = &iface->dev; 980f89bd95cSHenrik Rydberg 981f89bd95cSHenrik Rydberg input_set_drvdata(input_dev, dev); 982f89bd95cSHenrik Rydberg 983f89bd95cSHenrik Rydberg input_dev->open = bcm5974_open; 984f89bd95cSHenrik Rydberg input_dev->close = bcm5974_close; 985f89bd95cSHenrik Rydberg 986f89bd95cSHenrik Rydberg setup_events_to_report(input_dev, cfg); 987f89bd95cSHenrik Rydberg 988f89bd95cSHenrik Rydberg error = input_register_device(dev->input); 989f89bd95cSHenrik Rydberg if (error) 990f89bd95cSHenrik Rydberg goto err_free_buffer; 991f89bd95cSHenrik Rydberg 992f89bd95cSHenrik Rydberg /* save our data pointer in this interface device */ 993f89bd95cSHenrik Rydberg usb_set_intfdata(iface, dev); 994f89bd95cSHenrik Rydberg 995f89bd95cSHenrik Rydberg return 0; 996f89bd95cSHenrik Rydberg 997f89bd95cSHenrik Rydberg err_free_buffer: 998997ea58eSDaniel Mack usb_free_coherent(dev->udev, dev->cfg.tp_datalen, 999f89bd95cSHenrik Rydberg dev->tp_data, dev->tp_urb->transfer_dma); 1000f89bd95cSHenrik Rydberg err_free_bt_buffer: 100143f482b4SHenrik Rydberg if (dev->bt_urb) 1002997ea58eSDaniel Mack usb_free_coherent(dev->udev, dev->cfg.bt_datalen, 1003f89bd95cSHenrik Rydberg dev->bt_data, dev->bt_urb->transfer_dma); 1004f89bd95cSHenrik Rydberg err_free_urb: 1005f89bd95cSHenrik Rydberg usb_free_urb(dev->tp_urb); 1006f89bd95cSHenrik Rydberg err_free_bt_urb: 1007f89bd95cSHenrik Rydberg usb_free_urb(dev->bt_urb); 1008f89bd95cSHenrik Rydberg err_free_devs: 1009f89bd95cSHenrik Rydberg usb_set_intfdata(iface, NULL); 1010f89bd95cSHenrik Rydberg input_free_device(input_dev); 1011f89bd95cSHenrik Rydberg kfree(dev); 1012f89bd95cSHenrik Rydberg return error; 1013f89bd95cSHenrik Rydberg } 1014f89bd95cSHenrik Rydberg 1015f89bd95cSHenrik Rydberg static void bcm5974_disconnect(struct usb_interface *iface) 1016f89bd95cSHenrik Rydberg { 1017f89bd95cSHenrik Rydberg struct bcm5974 *dev = usb_get_intfdata(iface); 1018f89bd95cSHenrik Rydberg 1019f89bd95cSHenrik Rydberg usb_set_intfdata(iface, NULL); 1020f89bd95cSHenrik Rydberg 1021f89bd95cSHenrik Rydberg input_unregister_device(dev->input); 1022997ea58eSDaniel Mack usb_free_coherent(dev->udev, dev->cfg.tp_datalen, 1023f89bd95cSHenrik Rydberg dev->tp_data, dev->tp_urb->transfer_dma); 102443f482b4SHenrik Rydberg if (dev->bt_urb) 1025997ea58eSDaniel Mack usb_free_coherent(dev->udev, dev->cfg.bt_datalen, 1026f89bd95cSHenrik Rydberg dev->bt_data, dev->bt_urb->transfer_dma); 1027f89bd95cSHenrik Rydberg usb_free_urb(dev->tp_urb); 1028f89bd95cSHenrik Rydberg usb_free_urb(dev->bt_urb); 1029f89bd95cSHenrik Rydberg kfree(dev); 1030f89bd95cSHenrik Rydberg } 1031f89bd95cSHenrik Rydberg 1032f89bd95cSHenrik Rydberg static struct usb_driver bcm5974_driver = { 1033f89bd95cSHenrik Rydberg .name = "bcm5974", 1034f89bd95cSHenrik Rydberg .probe = bcm5974_probe, 1035f89bd95cSHenrik Rydberg .disconnect = bcm5974_disconnect, 1036f89bd95cSHenrik Rydberg .suspend = bcm5974_suspend, 1037f89bd95cSHenrik Rydberg .resume = bcm5974_resume, 1038f89bd95cSHenrik Rydberg .id_table = bcm5974_table, 103988da765fSDmitry Torokhov .supports_autosuspend = 1, 1040f89bd95cSHenrik Rydberg }; 1041f89bd95cSHenrik Rydberg 104208642e7cSGreg Kroah-Hartman module_usb_driver(bcm5974_driver); 1043