2008-08-08 14:59:30 -04:00
/*
* Apple USB BCM5974 ( Macbook Air and Penryn Macbook Pro ) multitouch driver
*
* Copyright ( C ) 2008 Henrik Rydberg ( rydberg @ euromail . se )
2015-07-24 14:45:33 -07:00
* Copyright ( C ) 2015 John Horan ( knasher @ gmail . com )
2008-08-08 14:59:30 -04:00
*
* The USB initialization and package decoding was made by
* Scott Shawcroft as part of the touchd user - space driver project :
* Copyright ( C ) 2008 Scott Shawcroft ( scott . shawcroft @ gmail . com )
*
* The BCM5974 driver is based on the appletouch driver :
* Copyright ( C ) 2001 - 2004 Greg Kroah - Hartman ( greg @ kroah . com )
* Copyright ( C ) 2005 Johannes Berg ( johannes @ sipsolutions . net )
* Copyright ( C ) 2005 Stelian Pop ( stelian @ popies . net )
* Copyright ( C ) 2005 Frank Arnold ( frank @ scirocco - 5 v - turbo . de )
* Copyright ( C ) 2005 Peter Osterlund ( petero2 @ telia . com )
* Copyright ( C ) 2005 Michael Hanselmann ( linux - kernel @ hansmi . ch )
* Copyright ( C ) 2006 Nicolas Boichat ( nicolas @ boichat . ch )
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/usb/input.h>
# include <linux/hid.h>
# include <linux/mutex.h>
2012-09-15 16:40:04 +02:00
# include <linux/input/mt.h>
2008-08-08 14:59:30 -04:00
# define USB_VENDOR_ID_APPLE 0x05ac
/* MacbookAir, aka wellspring */
# define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI 0x0223
# define USB_DEVICE_ID_APPLE_WELLSPRING_ISO 0x0224
# define USB_DEVICE_ID_APPLE_WELLSPRING_JIS 0x0225
/* MacbookProPenryn, aka wellspring2 */
# define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230
# define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231
# define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232
2009-04-28 07:03:54 -07:00
/* Macbook5,1 (unibody), aka wellspring3 */
# define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI 0x0236
# define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO 0x0237
# define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238
2010-11-09 17:38:42 +01:00
/* MacbookAir3,2 (unibody), aka wellspring5 */
# define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI 0x023f
# define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO 0x0240
# define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS 0x0241
/* MacbookAir3,1 (unibody), aka wellspring4 */
# define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242
# define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243
# define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244
2011-03-12 20:27:22 -08:00
/* Macbook8 (unibody, March 2011) */
# define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245
# define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246
# define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247
2011-09-09 13:29:45 -07:00
/* MacbookAir4,1 (unibody, July 2011) */
# define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI 0x0249
# define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO 0x024a
# define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS 0x024b
2011-08-08 23:45:14 -07:00
/* MacbookAir4,2 (unibody, July 2011) */
# define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI 0x024c
# define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO 0x024d
# define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS 0x024e
2011-08-16 11:07:39 -07:00
/* Macbook8,2 (unibody) */
# define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI 0x0252
# define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO 0x0253
# define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS 0x0254
2012-07-10 09:43:57 -07:00
/* MacbookPro10,1 (unibody, June 2012) */
# define USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI 0x0262
# define USB_DEVICE_ID_APPLE_WELLSPRING7_ISO 0x0263
# define USB_DEVICE_ID_APPLE_WELLSPRING7_JIS 0x0264
2012-10-30 11:11:48 -07:00
/* MacbookPro10,2 (unibody, October 2012) */
# define USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI 0x0259
# define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO 0x025a
# define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS 0x025b
2013-07-01 11:47:51 -07:00
/* MacbookAir6,2 (unibody, June 2013) */
2013-09-01 15:31:44 +02:00
# define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290
# define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291
# define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292
2015-07-24 14:45:33 -07:00
/* MacbookPro12,1 (2015) */
# define USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI 0x0272
# define USB_DEVICE_ID_APPLE_WELLSPRING9_ISO 0x0273
# define USB_DEVICE_ID_APPLE_WELLSPRING9_JIS 0x0274
2008-08-08 14:59:30 -04:00
# define BCM5974_DEVICE(prod) { \
. match_flags = ( USB_DEVICE_ID_MATCH_DEVICE | \
USB_DEVICE_ID_MATCH_INT_CLASS | \
USB_DEVICE_ID_MATCH_INT_PROTOCOL ) , \
. idVendor = USB_VENDOR_ID_APPLE , \
. idProduct = ( prod ) , \
. bInterfaceClass = USB_INTERFACE_CLASS_HID , \
. bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE \
}
/* table of devices that work with this driver */
2008-09-04 22:20:10 -04:00
static const struct usb_device_id bcm5974_table [ ] = {
2008-08-08 14:59:30 -04:00
/* MacbookAir1.1 */
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING_ANSI ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING_ISO ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING_JIS ) ,
/* MacbookProPenryn */
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING2_ISO ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING2_JIS ) ,
2009-04-28 07:03:54 -07:00
/* Macbook5,1 */
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING3_ISO ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING3_JIS ) ,
2010-11-09 17:38:42 +01:00
/* MacbookAir3,2 */
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING4_ISO ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING4_JIS ) ,
/* MacbookAir3,1 */
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS ) ,
2011-03-12 20:27:22 -08:00
/* MacbookPro8 */
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING5_ISO ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING5_JIS ) ,
2011-09-09 13:29:45 -07:00
/* MacbookAir4,1 */
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS ) ,
2011-08-08 23:45:14 -07:00
/* MacbookAir4,2 */
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING6_ISO ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING6_JIS ) ,
2011-08-16 11:07:39 -07:00
/* MacbookPro8,2 */
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS ) ,
2012-07-10 09:43:57 -07:00
/* MacbookPro10,1 */
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING7_ISO ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING7_JIS ) ,
2012-10-30 11:11:48 -07:00
/* MacbookPro10,2 */
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS ) ,
2013-07-01 11:47:51 -07:00
/* MacbookAir6,2 */
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING8_ISO ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING8_JIS ) ,
2015-07-24 14:45:33 -07:00
/* MacbookPro12,1 */
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING9_ISO ) ,
BCM5974_DEVICE ( USB_DEVICE_ID_APPLE_WELLSPRING9_JIS ) ,
2008-08-08 14:59:30 -04:00
/* Terminating entry */
{ }
} ;
MODULE_DEVICE_TABLE ( usb , bcm5974_table ) ;
MODULE_AUTHOR ( " Henrik Rydberg " ) ;
MODULE_DESCRIPTION ( " Apple USB BCM5974 multitouch driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define dprintk(level, format, a...)\
{ if ( debug > = level ) printk ( KERN_DEBUG format , # # a ) ; }
static int debug = 1 ;
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " Activate debugging output " ) ;
/* button data structure */
struct bt_data {
u8 unknown1 ; /* constant */
u8 button ; /* left button */
u8 rel_x ; /* relative x coordinate */
u8 rel_y ; /* relative y coordinate */
} ;
2009-04-27 11:52:42 -07:00
/* trackpad header types */
enum tp_type {
2009-04-28 07:03:54 -07:00
TYPE1 , /* plain trackpad */
2013-07-01 11:47:51 -07:00
TYPE2 , /* button integrated in trackpad */
2015-07-24 14:45:33 -07:00
TYPE3 , /* additional header fields since June 2013 */
TYPE4 /* additional header field for pressure data */
2008-08-08 14:59:30 -04:00
} ;
2009-04-27 11:52:42 -07:00
/* trackpad finger data offsets, le16-aligned */
2015-07-24 14:44:37 -07:00
# define HEADER_TYPE1 (13 * sizeof(__le16))
# define HEADER_TYPE2 (15 * sizeof(__le16))
# define HEADER_TYPE3 (19 * sizeof(__le16))
2015-07-24 14:45:33 -07:00
# define HEADER_TYPE4 (23 * sizeof(__le16))
2009-04-28 07:03:54 -07:00
/* trackpad button data offsets */
2015-07-24 14:44:37 -07:00
# define BUTTON_TYPE1 0
2009-04-28 07:03:54 -07:00
# define BUTTON_TYPE2 15
2013-07-01 11:47:51 -07:00
# define BUTTON_TYPE3 23
2015-07-24 14:45:33 -07:00
# define BUTTON_TYPE4 31
2009-04-28 07:03:54 -07:00
/* list of device capability bits */
# define HAS_INTEGRATED_BUTTON 1
2009-04-27 11:52:42 -07:00
2015-07-24 14:44:37 -07:00
/* trackpad finger data block size */
# define FSIZE_TYPE1 (14 * sizeof(__le16))
# define FSIZE_TYPE2 (14 * sizeof(__le16))
# define FSIZE_TYPE3 (14 * sizeof(__le16))
2015-07-24 14:45:33 -07:00
# define FSIZE_TYPE4 (15 * sizeof(__le16))
2015-07-24 14:44:37 -07:00
/* offset from header to finger struct */
# define DELTA_TYPE1 (0 * sizeof(__le16))
# define DELTA_TYPE2 (0 * sizeof(__le16))
# define DELTA_TYPE3 (0 * sizeof(__le16))
2015-07-24 14:45:33 -07:00
# define DELTA_TYPE4 (1 * sizeof(__le16))
2015-07-24 14:44:37 -07:00
/* usb control message mode switch data */
# define USBMSG_TYPE1 8, 0x300, 0, 0, 0x1, 0x8
# define USBMSG_TYPE2 8, 0x300, 0, 0, 0x1, 0x8
# define USBMSG_TYPE3 8, 0x300, 0, 0, 0x1, 0x8
2015-07-24 14:45:33 -07:00
# define USBMSG_TYPE4 2, 0x302, 2, 1, 0x1, 0x0
2015-07-24 14:44:37 -07:00
/* Wellspring initialization constants */
# define BCM5974_WELLSPRING_MODE_READ_REQUEST_ID 1
# define BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID 9
2009-04-27 11:52:42 -07:00
/* trackpad finger structure, le16-aligned */
2008-08-08 14:59:30 -04:00
struct tp_finger {
2008-09-04 22:28:23 -04:00
__le16 origin ; /* zero when switching track finger */
2008-08-08 14:59:30 -04:00
__le16 abs_x ; /* absolute x coodinate */
__le16 abs_y ; /* absolute y coodinate */
__le16 rel_x ; /* relative x coodinate */
__le16 rel_y ; /* relative y coodinate */
2012-08-12 21:21:59 +02:00
__le16 tool_major ; /* tool area, major axis */
__le16 tool_minor ; /* tool area, minor axis */
2008-08-08 14:59:30 -04:00
__le16 orientation ; /* 16384 when point, else 15 bit angle */
2012-08-12 21:21:59 +02:00
__le16 touch_major ; /* touch area, major axis */
__le16 touch_minor ; /* touch area, minor axis */
2015-07-24 14:45:33 -07:00
__le16 unused [ 2 ] ; /* zeros */
__le16 pressure ; /* pressure on forcetouch touchpad */
2008-08-08 14:59:30 -04:00
__le16 multi ; /* one finger: varies, more fingers: constant */
2009-04-27 11:52:42 -07:00
} __attribute__ ( ( packed , aligned ( 2 ) ) ) ;
2008-08-08 14:59:30 -04:00
2009-04-27 11:52:42 -07:00
/* trackpad finger data size, empirically at least ten fingers */
2012-08-12 21:21:59 +02:00
# define MAX_FINGERS 16
2010-01-06 00:32:48 -08:00
# define MAX_FINGER_ORIENTATION 16384
2008-08-08 14:59:30 -04:00
/* device-specific parameters */
struct bcm5974_param {
2012-09-15 09:49:51 +02:00
int snratio ; /* signal-to-noise ratio */
int min ; /* device minimum reading */
int max ; /* device maximum reading */
2008-08-08 14:59:30 -04:00
} ;
/* device-specific configuration */
struct bcm5974_config {
int ansi , iso , jis ; /* the product id of this device */
2009-04-28 07:03:54 -07:00
int caps ; /* device capability bitmask */
2008-08-08 14:59:30 -04:00
int bt_ep ; /* the endpoint of the button interface */
int bt_datalen ; /* data length of the button interface */
int tp_ep ; /* the endpoint of the trackpad interface */
2009-04-27 11:52:42 -07:00
enum tp_type tp_type ; /* type of trackpad interface */
2015-07-24 14:44:37 -07:00
int tp_header ; /* bytes in header block */
2008-08-08 14:59:30 -04:00
int tp_datalen ; /* data length of the trackpad interface */
2015-07-24 14:44:37 -07:00
int tp_button ; /* offset to button data */
int tp_fsize ; /* bytes in single finger block */
int tp_delta ; /* offset from header to finger struct */
int um_size ; /* usb control message length */
int um_req_val ; /* usb control message value */
int um_req_idx ; /* usb control message index */
int um_switch_idx ; /* usb control message mode switch index */
int um_switch_on ; /* usb control message mode switch on */
int um_switch_off ; /* usb control message mode switch off */
2008-08-08 14:59:30 -04:00
struct bcm5974_param p ; /* finger pressure limits */
struct bcm5974_param w ; /* finger width limits */
struct bcm5974_param x ; /* horizontal limits */
struct bcm5974_param y ; /* vertical limits */
2012-09-15 09:49:51 +02:00
struct bcm5974_param o ; /* orientation limits */
2008-08-08 14:59:30 -04:00
} ;
/* logical device structure */
struct bcm5974 {
char phys [ 64 ] ;
struct usb_device * udev ; /* usb device */
2008-08-08 14:59:32 -04:00
struct usb_interface * intf ; /* our interface */
2008-08-08 14:59:30 -04:00
struct input_dev * input ; /* input dev */
struct bcm5974_config cfg ; /* device configuration */
struct mutex pm_mutex ; /* serialize access to open/suspend */
int opened ; /* 1: opened, 0: closed */
struct urb * bt_urb ; /* button usb request block */
struct bt_data * bt_data ; /* button transferred data */
struct urb * tp_urb ; /* trackpad usb request block */
2009-04-27 11:52:42 -07:00
u8 * tp_data ; /* trackpad transferred data */
2012-09-15 16:40:04 +02:00
const struct tp_finger * index [ MAX_FINGERS ] ; /* finger index data */
struct input_mt_pos pos [ MAX_FINGERS ] ; /* position array */
int slots [ MAX_FINGERS ] ; /* slot assignments */
2008-08-08 14:59:30 -04:00
} ;
2015-07-24 14:44:37 -07:00
/* trackpad finger block data, le16-aligned */
static const struct tp_finger * get_tp_finger ( const struct bcm5974 * dev , int i )
{
const struct bcm5974_config * c = & dev - > cfg ;
u8 * f_base = dev - > tp_data + c - > tp_header + c - > tp_delta ;
return ( const struct tp_finger * ) ( f_base + i * c - > tp_fsize ) ;
}
# define DATAFORMAT(type) \
type , \
HEADER_ # # type , \
HEADER_ # # type + ( MAX_FINGERS ) * ( FSIZE_ # # type ) , \
BUTTON_ # # type , \
FSIZE_ # # type , \
DELTA_ # # type , \
USBMSG_ # # type
2008-08-08 14:59:30 -04:00
/* logical signal quality */
# define SN_PRESSURE 45 /* pressure signal-to-noise ratio */
2012-09-15 16:40:04 +02:00
# define SN_WIDTH 25 /* width signal-to-noise ratio */
2008-08-08 14:59:30 -04:00
# define SN_COORD 250 /* coordinate signal-to-noise ratio */
2012-09-15 09:49:51 +02:00
# define SN_ORIENT 10 /* orientation signal-to-noise ratio */
2008-09-04 22:28:23 -04:00
2008-08-08 14:59:30 -04:00
/* device constants */
static const struct bcm5974_config bcm5974_config_table [ ] = {
{
USB_DEVICE_ID_APPLE_WELLSPRING_ANSI ,
USB_DEVICE_ID_APPLE_WELLSPRING_ISO ,
USB_DEVICE_ID_APPLE_WELLSPRING_JIS ,
2009-04-28 07:03:54 -07:00
0 ,
2008-08-08 14:59:30 -04:00
0x84 , sizeof ( struct bt_data ) ,
2015-07-24 14:44:37 -07:00
0x81 , DATAFORMAT ( TYPE1 ) ,
2012-09-15 09:49:51 +02:00
{ SN_PRESSURE , 0 , 256 } ,
{ SN_WIDTH , 0 , 2048 } ,
{ SN_COORD , - 4824 , 5342 } ,
{ SN_COORD , - 172 , 5820 } ,
{ SN_ORIENT , - MAX_FINGER_ORIENTATION , MAX_FINGER_ORIENTATION }
2008-08-08 14:59:30 -04:00
} ,
{
USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI ,
USB_DEVICE_ID_APPLE_WELLSPRING2_ISO ,
USB_DEVICE_ID_APPLE_WELLSPRING2_JIS ,
2009-04-28 07:03:54 -07:00
0 ,
2008-08-08 14:59:30 -04:00
0x84 , sizeof ( struct bt_data ) ,
2015-07-24 14:44:37 -07:00
0x81 , DATAFORMAT ( TYPE1 ) ,
2012-09-15 09:49:51 +02:00
{ SN_PRESSURE , 0 , 256 } ,
{ SN_WIDTH , 0 , 2048 } ,
{ SN_COORD , - 4824 , 4824 } ,
{ SN_COORD , - 172 , 4290 } ,
{ SN_ORIENT , - MAX_FINGER_ORIENTATION , MAX_FINGER_ORIENTATION }
2008-08-08 14:59:30 -04:00
} ,
2009-04-28 07:03:54 -07:00
{
USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI ,
USB_DEVICE_ID_APPLE_WELLSPRING3_ISO ,
USB_DEVICE_ID_APPLE_WELLSPRING3_JIS ,
HAS_INTEGRATED_BUTTON ,
0x84 , sizeof ( struct bt_data ) ,
2015-07-24 14:44:37 -07:00
0x81 , DATAFORMAT ( TYPE2 ) ,
2012-09-15 09:49:51 +02:00
{ SN_PRESSURE , 0 , 300 } ,
{ SN_WIDTH , 0 , 2048 } ,
{ SN_COORD , - 4460 , 5166 } ,
{ SN_COORD , - 75 , 6700 } ,
{ SN_ORIENT , - MAX_FINGER_ORIENTATION , MAX_FINGER_ORIENTATION }
2009-04-28 07:03:54 -07:00
} ,
2010-11-09 17:38:42 +01:00
{
USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI ,
USB_DEVICE_ID_APPLE_WELLSPRING4_ISO ,
USB_DEVICE_ID_APPLE_WELLSPRING4_JIS ,
HAS_INTEGRATED_BUTTON ,
0x84 , sizeof ( struct bt_data ) ,
2015-07-24 14:44:37 -07:00
0x81 , DATAFORMAT ( TYPE2 ) ,
2012-09-15 09:49:51 +02:00
{ SN_PRESSURE , 0 , 300 } ,
{ SN_WIDTH , 0 , 2048 } ,
{ SN_COORD , - 4620 , 5140 } ,
{ SN_COORD , - 150 , 6600 } ,
{ SN_ORIENT , - MAX_FINGER_ORIENTATION , MAX_FINGER_ORIENTATION }
2010-11-09 17:38:42 +01:00
} ,
{
USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI ,
USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO ,
USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS ,
HAS_INTEGRATED_BUTTON ,
0x84 , sizeof ( struct bt_data ) ,
2015-07-24 14:44:37 -07:00
0x81 , DATAFORMAT ( TYPE2 ) ,
2012-09-15 09:49:51 +02:00
{ SN_PRESSURE , 0 , 300 } ,
{ SN_WIDTH , 0 , 2048 } ,
{ SN_COORD , - 4616 , 5112 } ,
{ SN_COORD , - 142 , 5234 } ,
{ SN_ORIENT , - MAX_FINGER_ORIENTATION , MAX_FINGER_ORIENTATION }
2010-11-09 17:38:42 +01:00
} ,
2011-03-12 20:27:22 -08:00
{
USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI ,
USB_DEVICE_ID_APPLE_WELLSPRING5_ISO ,
USB_DEVICE_ID_APPLE_WELLSPRING5_JIS ,
HAS_INTEGRATED_BUTTON ,
0x84 , sizeof ( struct bt_data ) ,
2015-07-24 14:44:37 -07:00
0x81 , DATAFORMAT ( TYPE2 ) ,
2012-09-15 09:49:51 +02:00
{ SN_PRESSURE , 0 , 300 } ,
{ SN_WIDTH , 0 , 2048 } ,
{ SN_COORD , - 4415 , 5050 } ,
{ SN_COORD , - 55 , 6680 } ,
{ SN_ORIENT , - MAX_FINGER_ORIENTATION , MAX_FINGER_ORIENTATION }
2011-03-12 20:27:22 -08:00
} ,
2011-08-08 23:45:14 -07:00
{
USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI ,
USB_DEVICE_ID_APPLE_WELLSPRING6_ISO ,
USB_DEVICE_ID_APPLE_WELLSPRING6_JIS ,
HAS_INTEGRATED_BUTTON ,
0x84 , sizeof ( struct bt_data ) ,
2015-07-24 14:44:37 -07:00
0x81 , DATAFORMAT ( TYPE2 ) ,
2012-09-15 09:49:51 +02:00
{ SN_PRESSURE , 0 , 300 } ,
{ SN_WIDTH , 0 , 2048 } ,
{ SN_COORD , - 4620 , 5140 } ,
{ SN_COORD , - 150 , 6600 } ,
{ SN_ORIENT , - MAX_FINGER_ORIENTATION , MAX_FINGER_ORIENTATION }
2011-08-08 23:45:14 -07:00
} ,
2011-08-16 11:07:39 -07:00
{
USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI ,
USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO ,
USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS ,
HAS_INTEGRATED_BUTTON ,
0x84 , sizeof ( struct bt_data ) ,
2015-07-24 14:44:37 -07:00
0x81 , DATAFORMAT ( TYPE2 ) ,
2012-09-15 09:49:51 +02:00
{ SN_PRESSURE , 0 , 300 } ,
{ SN_WIDTH , 0 , 2048 } ,
{ SN_COORD , - 4750 , 5280 } ,
{ SN_COORD , - 150 , 6730 } ,
{ SN_ORIENT , - MAX_FINGER_ORIENTATION , MAX_FINGER_ORIENTATION }
2011-08-16 11:07:39 -07:00
} ,
2011-09-09 13:29:45 -07:00
{
USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI ,
USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO ,
USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS ,
HAS_INTEGRATED_BUTTON ,
0x84 , sizeof ( struct bt_data ) ,
2015-07-24 14:44:37 -07:00
0x81 , DATAFORMAT ( TYPE2 ) ,
2012-09-15 09:49:51 +02:00
{ SN_PRESSURE , 0 , 300 } ,
{ SN_WIDTH , 0 , 2048 } ,
{ SN_COORD , - 4620 , 5140 } ,
{ SN_COORD , - 150 , 6600 } ,
{ SN_ORIENT , - MAX_FINGER_ORIENTATION , MAX_FINGER_ORIENTATION }
2011-09-09 13:29:45 -07:00
} ,
2012-07-10 09:43:57 -07:00
{
USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI ,
USB_DEVICE_ID_APPLE_WELLSPRING7_ISO ,
USB_DEVICE_ID_APPLE_WELLSPRING7_JIS ,
HAS_INTEGRATED_BUTTON ,
0x84 , sizeof ( struct bt_data ) ,
2015-07-24 14:44:37 -07:00
0x81 , DATAFORMAT ( TYPE2 ) ,
2012-09-15 09:49:51 +02:00
{ SN_PRESSURE , 0 , 300 } ,
{ SN_WIDTH , 0 , 2048 } ,
{ SN_COORD , - 4750 , 5280 } ,
{ SN_COORD , - 150 , 6730 } ,
{ SN_ORIENT , - MAX_FINGER_ORIENTATION , MAX_FINGER_ORIENTATION }
2012-07-10 09:43:57 -07:00
} ,
2012-10-30 11:11:48 -07:00
{
USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI ,
USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO ,
USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS ,
HAS_INTEGRATED_BUTTON ,
0x84 , sizeof ( struct bt_data ) ,
2015-07-24 14:44:37 -07:00
0x81 , DATAFORMAT ( TYPE2 ) ,
2012-10-30 11:11:48 -07:00
{ SN_PRESSURE , 0 , 300 } ,
{ SN_WIDTH , 0 , 2048 } ,
{ SN_COORD , - 4750 , 5280 } ,
{ SN_COORD , - 150 , 6730 } ,
{ SN_ORIENT , - MAX_FINGER_ORIENTATION , MAX_FINGER_ORIENTATION }
} ,
2013-07-01 11:47:51 -07:00
{
USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI ,
USB_DEVICE_ID_APPLE_WELLSPRING8_ISO ,
USB_DEVICE_ID_APPLE_WELLSPRING8_JIS ,
HAS_INTEGRATED_BUTTON ,
0 , sizeof ( struct bt_data ) ,
2015-07-24 14:44:37 -07:00
0x83 , DATAFORMAT ( TYPE3 ) ,
2013-07-01 11:47:51 -07:00
{ SN_PRESSURE , 0 , 300 } ,
{ SN_WIDTH , 0 , 2048 } ,
{ SN_COORD , - 4620 , 5140 } ,
{ SN_COORD , - 150 , 6600 } ,
{ SN_ORIENT , - MAX_FINGER_ORIENTATION , MAX_FINGER_ORIENTATION }
} ,
2015-07-24 14:45:33 -07:00
{
USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI ,
USB_DEVICE_ID_APPLE_WELLSPRING9_ISO ,
USB_DEVICE_ID_APPLE_WELLSPRING9_JIS ,
HAS_INTEGRATED_BUTTON ,
0 , sizeof ( struct bt_data ) ,
0x83 , DATAFORMAT ( TYPE4 ) ,
{ SN_PRESSURE , 0 , 300 } ,
{ SN_WIDTH , 0 , 2048 } ,
{ SN_COORD , - 4828 , 5345 } ,
{ SN_COORD , - 203 , 6803 } ,
{ SN_ORIENT , - MAX_FINGER_ORIENTATION , MAX_FINGER_ORIENTATION }
} ,
2008-08-08 14:59:30 -04:00
{ }
} ;
/* return the device-specific configuration by device */
static const struct bcm5974_config * bcm5974_get_config ( struct usb_device * udev )
{
u16 id = le16_to_cpu ( udev - > descriptor . idProduct ) ;
const struct bcm5974_config * cfg ;
for ( cfg = bcm5974_config_table ; cfg - > ansi ; + + cfg )
if ( cfg - > ansi = = id | | cfg - > iso = = id | | cfg - > jis = = id )
return cfg ;
return bcm5974_config_table ;
}
/* convert 16-bit little endian to signed integer */
static inline int raw2int ( __le16 x )
{
return ( signed short ) le16_to_cpu ( x ) ;
}
2012-09-15 09:49:51 +02:00
static void set_abs ( struct input_dev * input , unsigned int code ,
const struct bcm5974_param * p )
2008-08-08 14:59:30 -04:00
{
2012-09-15 09:49:51 +02:00
int fuzz = p - > snratio ? ( p - > max - p - > min ) / p - > snratio : 0 ;
input_set_abs_params ( input , code , p - > min , p - > max , fuzz , 0 ) ;
2008-08-08 14:59:30 -04:00
}
/* setup which logical events to report */
static void setup_events_to_report ( struct input_dev * input_dev ,
const struct bcm5974_config * cfg )
{
__set_bit ( EV_ABS , input_dev - > evbit ) ;
2012-09-15 09:49:51 +02:00
/* for synaptics only */
input_set_abs_params ( input_dev , ABS_PRESSURE , 0 , 256 , 5 , 0 ) ;
input_set_abs_params ( input_dev , ABS_TOOL_WIDTH , 0 , 16 , 0 , 0 ) ;
2010-01-06 00:32:48 -08:00
/* finger touch area */
2012-09-15 09:49:51 +02:00
set_abs ( input_dev , ABS_MT_TOUCH_MAJOR , & cfg - > w ) ;
set_abs ( input_dev , ABS_MT_TOUCH_MINOR , & cfg - > w ) ;
2010-01-06 00:32:48 -08:00
/* finger approach area */
2012-09-15 09:49:51 +02:00
set_abs ( input_dev , ABS_MT_WIDTH_MAJOR , & cfg - > w ) ;
set_abs ( input_dev , ABS_MT_WIDTH_MINOR , & cfg - > w ) ;
2010-01-06 00:32:48 -08:00
/* finger orientation */
2012-09-15 09:49:51 +02:00
set_abs ( input_dev , ABS_MT_ORIENTATION , & cfg - > o ) ;
2010-01-06 00:32:48 -08:00
/* finger position */
2012-09-15 09:49:51 +02:00
set_abs ( input_dev , ABS_MT_POSITION_X , & cfg - > x ) ;
set_abs ( input_dev , ABS_MT_POSITION_Y , & cfg - > y ) ;
2010-01-06 00:32:48 -08:00
2008-08-08 14:59:30 -04:00
__set_bit ( EV_KEY , input_dev - > evbit ) ;
__set_bit ( BTN_LEFT , input_dev - > keybit ) ;
2010-06-23 09:30:22 -07:00
2012-01-10 23:04:16 -08:00
if ( cfg - > caps & HAS_INTEGRATED_BUTTON )
__set_bit ( INPUT_PROP_BUTTONPAD , input_dev - > propbit ) ;
2012-09-15 16:40:04 +02:00
input_mt_init_slots ( input_dev , MAX_FINGERS ,
INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK ) ;
2008-08-08 14:59:30 -04:00
}
/* report button data as logical button state */
static int report_bt_state ( struct bcm5974 * dev , int size )
{
if ( size ! = sizeof ( struct bt_data ) )
return - EIO ;
2009-04-28 07:04:42 -07:00
dprintk ( 7 ,
" bcm5974: button data: %x %x %x %x \n " ,
dev - > bt_data - > unknown1 , dev - > bt_data - > button ,
dev - > bt_data - > rel_x , dev - > bt_data - > rel_y ) ;
2008-08-08 14:59:30 -04:00
input_report_key ( dev - > input , BTN_LEFT , dev - > bt_data - > button ) ;
input_sync ( dev - > input ) ;
return 0 ;
}
2012-09-15 16:40:04 +02:00
static void report_finger_data ( struct input_dev * input , int slot ,
const struct input_mt_pos * pos ,
2010-01-06 00:32:48 -08:00
const struct tp_finger * f )
{
2012-09-15 16:40:04 +02:00
input_mt_slot ( input , slot ) ;
input_mt_report_slot_state ( input , MT_TOOL_FINGER , true ) ;
2010-08-31 17:27:02 -07:00
input_report_abs ( input , ABS_MT_TOUCH_MAJOR ,
2012-08-12 21:21:59 +02:00
raw2int ( f - > touch_major ) < < 1 ) ;
2010-08-31 17:27:02 -07:00
input_report_abs ( input , ABS_MT_TOUCH_MINOR ,
2012-08-12 21:21:59 +02:00
raw2int ( f - > touch_minor ) < < 1 ) ;
2010-08-31 17:27:02 -07:00
input_report_abs ( input , ABS_MT_WIDTH_MAJOR ,
2012-08-12 21:21:59 +02:00
raw2int ( f - > tool_major ) < < 1 ) ;
2010-08-31 17:27:02 -07:00
input_report_abs ( input , ABS_MT_WIDTH_MINOR ,
2012-08-12 21:21:59 +02:00
raw2int ( f - > tool_minor ) < < 1 ) ;
2010-01-06 00:32:48 -08:00
input_report_abs ( input , ABS_MT_ORIENTATION ,
MAX_FINGER_ORIENTATION - raw2int ( f - > orientation ) ) ;
2012-09-15 16:40:04 +02:00
input_report_abs ( input , ABS_MT_POSITION_X , pos - > x ) ;
input_report_abs ( input , ABS_MT_POSITION_Y , pos - > y ) ;
2010-01-06 00:32:48 -08:00
}
2012-09-15 09:49:51 +02:00
static void report_synaptics_data ( struct input_dev * input ,
const struct bcm5974_config * cfg ,
const struct tp_finger * f , int raw_n )
{
int abs_p = 0 , abs_w = 0 ;
if ( raw_n ) {
int p = raw2int ( f - > touch_major ) ;
int w = raw2int ( f - > tool_major ) ;
if ( p > 0 & & raw2int ( f - > origin ) ) {
abs_p = clamp_val ( 256 * p / cfg - > p . max , 0 , 255 ) ;
abs_w = clamp_val ( 16 * w / cfg - > w . max , 0 , 15 ) ;
}
}
input_report_abs ( input , ABS_PRESSURE , abs_p ) ;
input_report_abs ( input , ABS_TOOL_WIDTH , abs_w ) ;
}
2008-08-08 14:59:30 -04:00
/* report trackpad data as logical trackpad state */
static int report_tp_state ( struct bcm5974 * dev , int size )
{
const struct bcm5974_config * c = & dev - > cfg ;
2009-04-27 11:52:42 -07:00
const struct tp_finger * f ;
2008-08-08 14:59:30 -04:00
struct input_dev * input = dev - > input ;
2012-09-15 16:40:04 +02:00
int raw_n , i , n = 0 ;
2008-08-08 14:59:30 -04:00
2015-07-24 14:44:37 -07:00
if ( size < c - > tp_header | | ( size - c - > tp_header ) % c - > tp_fsize ! = 0 )
2008-08-08 14:59:30 -04:00
return - EIO ;
2015-07-24 14:44:37 -07:00
raw_n = ( size - c - > tp_header ) / c - > tp_fsize ;
2009-04-27 11:52:42 -07:00
2012-09-15 16:40:04 +02:00
for ( i = 0 ; i < raw_n ; i + + ) {
2015-07-24 14:44:37 -07:00
f = get_tp_finger ( dev , i ) ;
if ( raw2int ( f - > touch_major ) = = 0 )
2012-09-15 16:40:04 +02:00
continue ;
2015-07-24 14:44:37 -07:00
dev - > pos [ n ] . x = raw2int ( f - > abs_x ) ;
dev - > pos [ n ] . y = c - > y . min + c - > y . max - raw2int ( f - > abs_y ) ;
dev - > index [ n + + ] = f ;
2008-08-08 14:59:30 -04:00
}
2015-02-01 11:25:14 -08:00
input_mt_assign_slots ( input , dev - > slots , dev - > pos , n , 0 ) ;
2008-09-04 22:28:23 -04:00
2012-09-15 16:40:04 +02:00
for ( i = 0 ; i < n ; i + + )
report_finger_data ( input , dev - > slots [ i ] ,
& dev - > pos [ i ] , dev - > index [ i ] ) ;
2008-08-08 14:59:30 -04:00
2012-09-15 16:40:04 +02:00
input_mt_sync_frame ( input ) ;
2015-07-24 14:44:37 -07:00
report_synaptics_data ( input , c , get_tp_finger ( dev , 0 ) , raw_n ) ;
2008-08-08 14:59:30 -04:00
2015-07-24 14:44:37 -07:00
/* later types report button events via integrated button only */
if ( c - > caps & HAS_INTEGRATED_BUTTON ) {
int ibt = raw2int ( dev - > tp_data [ c - > tp_button ] ) ;
2009-04-28 07:03:54 -07:00
input_report_key ( input , BTN_LEFT , ibt ) ;
2012-09-15 09:49:51 +02:00
}
2009-04-28 07:03:54 -07:00
2008-08-08 14:59:30 -04:00
input_sync ( input ) ;
return 0 ;
}
2008-09-14 11:52:44 -04:00
static int bcm5974_wellspring_mode ( struct bcm5974 * dev , bool on )
2008-08-08 14:59:30 -04:00
{
2015-07-24 14:44:37 -07:00
const struct bcm5974_config * c = & dev - > cfg ;
2008-08-08 14:59:30 -04:00
int retval = 0 , size ;
2013-07-01 11:47:51 -07:00
char * data ;
/* Type 3 does not require a mode switch */
if ( dev - > cfg . tp_type = = TYPE3 )
return 0 ;
2008-08-08 14:59:30 -04:00
2015-07-24 14:44:37 -07:00
data = kmalloc ( c - > um_size , GFP_KERNEL ) ;
2008-08-08 14:59:30 -04:00
if ( ! data ) {
2012-05-04 15:33:02 -07:00
dev_err ( & dev - > intf - > dev , " out of memory \n " ) ;
2008-08-08 14:59:30 -04:00
retval = - ENOMEM ;
goto out ;
}
/* read configuration */
size = usb_control_msg ( dev - > udev , usb_rcvctrlpipe ( dev - > udev , 0 ) ,
BCM5974_WELLSPRING_MODE_READ_REQUEST_ID ,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE ,
2015-07-24 14:44:37 -07:00
c - > um_req_val , c - > um_req_idx , data , c - > um_size , 5000 ) ;
2008-08-08 14:59:30 -04:00
2015-07-24 14:44:37 -07:00
if ( size ! = c - > um_size ) {
2012-05-04 15:33:02 -07:00
dev_err ( & dev - > intf - > dev , " could not read from device \n " ) ;
2008-08-08 14:59:30 -04:00
retval = - EIO ;
goto out ;
}
/* apply the mode switch */
2015-07-24 14:44:37 -07:00
data [ c - > um_switch_idx ] = on ? c - > um_switch_on : c - > um_switch_off ;
2008-08-08 14:59:30 -04:00
/* write configuration */
size = usb_control_msg ( dev - > udev , usb_sndctrlpipe ( dev - > udev , 0 ) ,
BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID ,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE ,
2015-07-24 14:44:37 -07:00
c - > um_req_val , c - > um_req_idx , data , c - > um_size , 5000 ) ;
2008-08-08 14:59:30 -04:00
2015-07-24 14:44:37 -07:00
if ( size ! = c - > um_size ) {
2012-05-04 15:33:02 -07:00
dev_err ( & dev - > intf - > dev , " could not write to device \n " ) ;
2008-08-08 14:59:30 -04:00
retval = - EIO ;
goto out ;
}
2008-09-14 11:52:44 -04:00
dprintk ( 2 , " bcm5974: switched to %s mode. \n " ,
on ? " wellspring " : " normal " ) ;
2008-08-08 14:59:30 -04:00
out :
kfree ( data ) ;
return retval ;
}
static void bcm5974_irq_button ( struct urb * urb )
{
struct bcm5974 * dev = urb - > context ;
2012-05-04 15:33:02 -07:00
struct usb_interface * intf = dev - > intf ;
2008-08-08 14:59:30 -04:00
int error ;
switch ( urb - > status ) {
case 0 :
break ;
case - EOVERFLOW :
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
2012-05-04 15:33:02 -07:00
dev_dbg ( & intf - > dev , " button urb shutting down: %d \n " ,
2012-05-01 21:33:05 -07:00
urb - > status ) ;
2008-08-08 14:59:30 -04:00
return ;
default :
2012-05-04 15:33:02 -07:00
dev_dbg ( & intf - > dev , " button urb status: %d \n " , urb - > status ) ;
2008-08-08 14:59:30 -04:00
goto exit ;
}
if ( report_bt_state ( dev , dev - > bt_urb - > actual_length ) )
dprintk ( 1 , " bcm5974: bad button package, length: %d \n " ,
dev - > bt_urb - > actual_length ) ;
exit :
error = usb_submit_urb ( dev - > bt_urb , GFP_ATOMIC ) ;
if ( error )
2012-05-04 15:33:02 -07:00
dev_err ( & intf - > dev , " button urb failed: %d \n " , error ) ;
2008-08-08 14:59:30 -04:00
}
static void bcm5974_irq_trackpad ( struct urb * urb )
{
struct bcm5974 * dev = urb - > context ;
2012-05-04 15:33:02 -07:00
struct usb_interface * intf = dev - > intf ;
2008-08-08 14:59:30 -04:00
int error ;
switch ( urb - > status ) {
case 0 :
break ;
case - EOVERFLOW :
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
2012-05-04 15:33:02 -07:00
dev_dbg ( & intf - > dev , " trackpad urb shutting down: %d \n " ,
2012-05-01 21:33:05 -07:00
urb - > status ) ;
2008-08-08 14:59:30 -04:00
return ;
default :
2012-05-04 15:33:02 -07:00
dev_dbg ( & intf - > dev , " trackpad urb status: %d \n " , urb - > status ) ;
2008-08-08 14:59:30 -04:00
goto exit ;
}
/* control response ignored */
if ( dev - > tp_urb - > actual_length = = 2 )
goto exit ;
if ( report_tp_state ( dev , dev - > tp_urb - > actual_length ) )
dprintk ( 1 , " bcm5974: bad trackpad package, length: %d \n " ,
dev - > tp_urb - > actual_length ) ;
exit :
error = usb_submit_urb ( dev - > tp_urb , GFP_ATOMIC ) ;
if ( error )
2012-05-04 15:33:02 -07:00
dev_err ( & intf - > dev , " trackpad urb failed: %d \n " , error ) ;
2008-08-08 14:59:30 -04:00
}
/*
* The Wellspring trackpad , like many recent Apple trackpads , share
* the usb device with the keyboard . Since keyboards are usually
* handled by the HID system , the device ends up being handled by two
* modules . Setting up the device therefore becomes slightly
* complicated . To enable multitouch features , a mode switch is
* required , which is usually applied via the control interface of the
* device . It can be argued where this switch should take place . In
* some drivers , like appletouch , the switch is made during
* probe . However , the hid module may also alter the state of the
* device , resulting in trackpad malfunction under certain
* circumstances . To get around this problem , there is at least one
* example that utilizes the USB_QUIRK_RESET_RESUME quirk in order to
2011-03-30 22:57:33 -03:00
* receive a reset_resume request rather than the normal resume .
2008-08-08 14:59:30 -04:00
* Since the implementation of reset_resume is equal to mode switch
* plus start_traffic , it seems easier to always do the switch when
* starting traffic on the device .
*/
static int bcm5974_start_traffic ( struct bcm5974 * dev )
{
2010-06-08 01:01:48 -07:00
int error ;
error = bcm5974_wellspring_mode ( dev , true ) ;
if ( error ) {
2008-08-08 14:59:30 -04:00
dprintk ( 1 , " bcm5974: mode switch failed \n " ) ;
2010-06-08 01:01:48 -07:00
goto err_out ;
2008-08-08 14:59:30 -04:00
}
2012-08-13 00:18:44 +02:00
if ( dev - > bt_urb ) {
error = usb_submit_urb ( dev - > bt_urb , GFP_KERNEL ) ;
if ( error )
goto err_reset_mode ;
}
2008-08-08 14:59:30 -04:00
2010-06-08 01:01:48 -07:00
error = usb_submit_urb ( dev - > tp_urb , GFP_KERNEL ) ;
if ( error )
2008-08-08 14:59:30 -04:00
goto err_kill_bt ;
return 0 ;
err_kill_bt :
usb_kill_urb ( dev - > bt_urb ) ;
2010-06-08 01:01:48 -07:00
err_reset_mode :
bcm5974_wellspring_mode ( dev , false ) ;
err_out :
return error ;
2008-08-08 14:59:30 -04:00
}
static void bcm5974_pause_traffic ( struct bcm5974 * dev )
{
usb_kill_urb ( dev - > tp_urb ) ;
usb_kill_urb ( dev - > bt_urb ) ;
2008-09-14 11:52:44 -04:00
bcm5974_wellspring_mode ( dev , false ) ;
2008-08-08 14:59:30 -04:00
}
/*
* The code below implements open / close and manual suspend / resume .
* All functions may be called in random order .
*
* Opening a suspended device fails with EACCES - permission denied .
*
* Failing a resume leaves the device resumed but closed .
*/
static int bcm5974_open ( struct input_dev * input )
{
struct bcm5974 * dev = input_get_drvdata ( input ) ;
int error ;
2008-08-08 14:59:32 -04:00
error = usb_autopm_get_interface ( dev - > intf ) ;
if ( error )
return error ;
2008-08-08 14:59:30 -04:00
mutex_lock ( & dev - > pm_mutex ) ;
error = bcm5974_start_traffic ( dev ) ;
if ( ! error )
dev - > opened = 1 ;
mutex_unlock ( & dev - > pm_mutex ) ;
2008-08-08 14:59:32 -04:00
if ( error )
usb_autopm_put_interface ( dev - > intf ) ;
2008-08-08 14:59:30 -04:00
return error ;
}
static void bcm5974_close ( struct input_dev * input )
{
struct bcm5974 * dev = input_get_drvdata ( input ) ;
mutex_lock ( & dev - > pm_mutex ) ;
bcm5974_pause_traffic ( dev ) ;
dev - > opened = 0 ;
mutex_unlock ( & dev - > pm_mutex ) ;
2008-08-08 14:59:32 -04:00
usb_autopm_put_interface ( dev - > intf ) ;
2008-08-08 14:59:30 -04:00
}
static int bcm5974_suspend ( struct usb_interface * iface , pm_message_t message )
{
struct bcm5974 * dev = usb_get_intfdata ( iface ) ;
mutex_lock ( & dev - > pm_mutex ) ;
if ( dev - > opened )
bcm5974_pause_traffic ( dev ) ;
mutex_unlock ( & dev - > pm_mutex ) ;
return 0 ;
}
static int bcm5974_resume ( struct usb_interface * iface )
{
struct bcm5974 * dev = usb_get_intfdata ( iface ) ;
int error = 0 ;
mutex_lock ( & dev - > pm_mutex ) ;
if ( dev - > opened )
error = bcm5974_start_traffic ( dev ) ;
mutex_unlock ( & dev - > pm_mutex ) ;
return error ;
}
static int bcm5974_probe ( struct usb_interface * iface ,
const struct usb_device_id * id )
{
struct usb_device * udev = interface_to_usbdev ( iface ) ;
const struct bcm5974_config * cfg ;
struct bcm5974 * dev ;
struct input_dev * input_dev ;
int error = - ENOMEM ;
/* find the product index */
cfg = bcm5974_get_config ( udev ) ;
/* allocate memory for our device state and initialize it */
dev = kzalloc ( sizeof ( struct bcm5974 ) , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! dev | | ! input_dev ) {
2012-04-25 14:48:31 -07:00
dev_err ( & iface - > dev , " out of memory \n " ) ;
2008-08-08 14:59:30 -04:00
goto err_free_devs ;
}
dev - > udev = udev ;
2008-08-08 14:59:32 -04:00
dev - > intf = iface ;
2008-08-08 14:59:30 -04:00
dev - > input = input_dev ;
dev - > cfg = * cfg ;
mutex_init ( & dev - > pm_mutex ) ;
/* setup urbs */
2012-08-13 00:18:44 +02:00
if ( cfg - > tp_type = = TYPE1 ) {
dev - > bt_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! dev - > bt_urb )
goto err_free_devs ;
}
2008-08-08 14:59:30 -04:00
dev - > tp_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! dev - > tp_urb )
goto err_free_bt_urb ;
2012-08-13 00:18:44 +02:00
if ( dev - > bt_urb ) {
dev - > bt_data = usb_alloc_coherent ( dev - > udev ,
2010-04-12 13:17:25 +02:00
dev - > cfg . bt_datalen , GFP_KERNEL ,
& dev - > bt_urb - > transfer_dma ) ;
2012-08-13 00:18:44 +02:00
if ( ! dev - > bt_data )
goto err_free_urb ;
}
2008-08-08 14:59:30 -04:00
2010-04-12 13:17:25 +02:00
dev - > tp_data = usb_alloc_coherent ( dev - > udev ,
dev - > cfg . tp_datalen , GFP_KERNEL ,
& dev - > tp_urb - > transfer_dma ) ;
2008-08-08 14:59:30 -04:00
if ( ! dev - > tp_data )
goto err_free_bt_buffer ;
2012-08-13 00:18:44 +02:00
if ( dev - > bt_urb )
usb_fill_int_urb ( dev - > bt_urb , udev ,
usb_rcvintpipe ( udev , cfg - > bt_ep ) ,
dev - > bt_data , dev - > cfg . bt_datalen ,
bcm5974_irq_button , dev , 1 ) ;
2008-08-08 14:59:30 -04:00
usb_fill_int_urb ( dev - > tp_urb , udev ,
usb_rcvintpipe ( udev , cfg - > tp_ep ) ,
dev - > tp_data , dev - > cfg . tp_datalen ,
bcm5974_irq_trackpad , dev , 1 ) ;
/* create bcm5974 device */
usb_make_path ( udev , dev - > phys , sizeof ( dev - > phys ) ) ;
strlcat ( dev - > phys , " /input0 " , sizeof ( dev - > phys ) ) ;
input_dev - > name = " bcm5974 " ;
input_dev - > phys = dev - > phys ;
usb_to_input_id ( dev - > udev , & input_dev - > id ) ;
2009-04-28 07:03:54 -07:00
/* report driver capabilities via the version field */
input_dev - > id . version = cfg - > caps ;
2008-08-08 14:59:30 -04:00
input_dev - > dev . parent = & iface - > dev ;
input_set_drvdata ( input_dev , dev ) ;
input_dev - > open = bcm5974_open ;
input_dev - > close = bcm5974_close ;
setup_events_to_report ( input_dev , cfg ) ;
error = input_register_device ( dev - > input ) ;
if ( error )
goto err_free_buffer ;
/* save our data pointer in this interface device */
usb_set_intfdata ( iface , dev ) ;
return 0 ;
err_free_buffer :
2010-04-12 13:17:25 +02:00
usb_free_coherent ( dev - > udev , dev - > cfg . tp_datalen ,
2008-08-08 14:59:30 -04:00
dev - > tp_data , dev - > tp_urb - > transfer_dma ) ;
err_free_bt_buffer :
2012-08-13 00:18:44 +02:00
if ( dev - > bt_urb )
usb_free_coherent ( dev - > udev , dev - > cfg . bt_datalen ,
dev - > bt_data , dev - > bt_urb - > transfer_dma ) ;
2008-08-08 14:59:30 -04:00
err_free_urb :
usb_free_urb ( dev - > tp_urb ) ;
err_free_bt_urb :
usb_free_urb ( dev - > bt_urb ) ;
err_free_devs :
usb_set_intfdata ( iface , NULL ) ;
input_free_device ( input_dev ) ;
kfree ( dev ) ;
return error ;
}
static void bcm5974_disconnect ( struct usb_interface * iface )
{
struct bcm5974 * dev = usb_get_intfdata ( iface ) ;
usb_set_intfdata ( iface , NULL ) ;
input_unregister_device ( dev - > input ) ;
2010-04-12 13:17:25 +02:00
usb_free_coherent ( dev - > udev , dev - > cfg . tp_datalen ,
dev - > tp_data , dev - > tp_urb - > transfer_dma ) ;
2012-08-13 00:18:44 +02:00
if ( dev - > bt_urb )
usb_free_coherent ( dev - > udev , dev - > cfg . bt_datalen ,
dev - > bt_data , dev - > bt_urb - > transfer_dma ) ;
2008-08-08 14:59:30 -04:00
usb_free_urb ( dev - > tp_urb ) ;
usb_free_urb ( dev - > bt_urb ) ;
kfree ( dev ) ;
}
static struct usb_driver bcm5974_driver = {
. name = " bcm5974 " ,
. probe = bcm5974_probe ,
. disconnect = bcm5974_disconnect ,
. suspend = bcm5974_suspend ,
. resume = bcm5974_resume ,
. id_table = bcm5974_table ,
2008-08-08 14:59:32 -04:00
. supports_autosuspend = 1 ,
2008-08-08 14:59:30 -04:00
} ;
2011-11-18 09:48:31 -08:00
module_usb_driver ( bcm5974_driver ) ;