diff mbox

Dell ALPS patches for Dell Latitude 7250 & 7450

Message ID 201502101419.53861@pali (mailing list archive)
State New, archived
Headers show

Commit Message

Pali Rohár Feb. 10, 2015, 1:19 p.m. UTC
Hello,

now I found on Dell support website, that they also provide
psmouse/alps.c driver for new Dell Latitude 7250 & 7450 machines:
http://www.dell.com/support/home/gh/en/ghdhs1/Drivers/DriversDetails?driverId=N5V5D

It is again in Ubuntu support package and so users and developers
who do not use Ubuntu probably do not know about it...

Dell alps.c driver introduced ALPS protocol v8 and v9. I did not
found anything on linux-input ML about it, so to make sure that
support for new ALPS/Dell models for linux do not disappear I'm
sending diff against linux v3.14-rc1 version (which looks like to
be version of which Dell introduced they changes)...

Comments

Hans de Goede Feb. 11, 2015, 7:38 a.m. UTC | #1
Hi,

On 10-02-15 14:19, Pali Rohár wrote:
> Hello,
>
> now I found on Dell support website, that they also provide
> psmouse/alps.c driver for new Dell Latitude 7250 & 7450 machines:
> http://www.dell.com/support/home/gh/en/ghdhs1/Drivers/DriversDetails?driverId=N5V5D
>
> It is again in Ubuntu support package and so users and developers
> who do not use Ubuntu probably do not know about it...
>
> Dell alps.c driver introduced ALPS protocol v8 and v9. I did not
> found anything on linux-input ML about it, so to make sure that
> support for new ALPS/Dell models for linux do not disappear I'm
> sending diff against linux v3.14-rc1 version (which looks like to
> be version of which Dell introduced they changes)...

Thanks for this and the other related mail. I also have an alps.c
directly from alps themselves which adds support for v8 and v9
protocols. The problem is that like with v7 the code is not really
in a state where we want to take it as is, e.g. it does clickpad
softbutton emulation in the kernel instead of leaving it to userspace.

I spend a considerable amount of time cleaning up the alps code itself
and then adding a cleaner version of the v7 code, we should do the same
for the v8 and v9 code probably but that requires someone with hardware
access so that we can actually test the end result.

Regards,

Hans


>
>
> --- linux-3.14-rc1/drivers/input/mouse/alps.c	2015-02-08 12:38:06.507282067 +0100
> +++ goliad-mlk-14_A03.fish.tar.gz/debs/touchpad-alps-trusty-dkms_20140325_all.deb/usr/src/touchpad-alps-trusty-20140325/alps.c	2014-06-06 04:21:48.000000000 +0200
> @@ -20,6 +20,7 @@
>   #include <linux/input/mt.h>
>   #include <linux/serio.h>
>   #include <linux/libps2.h>
> +#include <linux/kernel.h>
>
>   #include "psmouse.h"
>   #include "alps.h"
> @@ -32,6 +33,11 @@
>   #define ALPS_REG_BASE_RUSHMORE	0xc2c0
>   #define ALPS_REG_BASE_PINNACLE	0x0000
>
> +#define LEFT_BUTTON_BIT			0x01
> +#define RIGHT_BUTTON_BIT		0x02
> +
> +#define V7_LARGE_MOVEMENT		130
> +
>   static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
>   	{ PSMOUSE_CMD_SETPOLL,		0x00 }, /* 0 */
>   	{ PSMOUSE_CMD_RESET_DIS,	0x00 }, /* 1 */
> @@ -99,6 +105,10 @@ static const struct alps_nibble_commands
>   #define ALPS_FOUR_BUTTONS	0x40	/* 4 direction button present */
>   #define ALPS_PS2_INTERLEAVED	0x80	/* 3-byte PS/2 packet interleaved with
>   					   6-byte ALPS packet */
> +#define ALPS_BTNLESS			0x100	/* ALPS ClickPad flag */
> +
> +#define	DOL_IS_APDATA(_BY)			((_BY[0]&0x01)==0x01)
> +#define	DOL_IS_PROFDATA(_BY)		((_BY[0]&0x20)==0x20)
>
>   static const struct alps_model_info alps_model_data[] = {
>   	{ { 0x32, 0x02, 0x14 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },	/* Toshiba Salellite Pro M10 */
> @@ -140,6 +150,20 @@ static void alps_set_abs_params_mt(struc
>    * isn't valid per PS/2 spec.
>    */
>
> +static unsigned int alps_pt_distance(struct alps_abs_data *pt0,
> +				    struct alps_abs_data *pt1)
> +{
> +	int vect_x, vect_y;
> +
> +	if (!pt0 || !pt1)
> +		return 0;
> +
> +	vect_x = pt0->x - pt1->x;
> +	vect_y = pt0->y - pt1->y;
> +
> +	return int_sqrt(vect_x * vect_x + vect_y * vect_y);
> +}
> +
>   /* Packet formats are described in Documentation/input/alps.txt */
>
>   static bool alps_is_valid_first_byte(struct alps_data *priv,
> @@ -296,8 +320,8 @@ static void alps_process_bitmap_dolphin(
>   	y_map = fields->y_map;
>
>   	if (!x_map || !y_map)
> -		return;
> -
> +        return;
> +	
>   	/* Get Most-significant and Least-significant bit */
>   	x_msb = fls(x_map);
>   	x_lsb = ffs(x_map);
> @@ -306,8 +330,8 @@ static void alps_process_bitmap_dolphin(
>
>   	/* Most-significant bit should never exceed max sensor line number */
>   	if (x_msb > priv->x_bits || y_msb > priv->y_bits)
> -		return;
> -
> +        return;
> +	
>   	*x1 = *y1 = *x2 = *y2 = 0;
>
>   	if (fields->fingers > 1) {
> @@ -320,11 +344,11 @@ static void alps_process_bitmap_dolphin(
>   		end_bit = y_msb - 1;
>   		box_middle_y = (priv->y_max * (start_bit + end_bit)) /
>   				(2 * (priv->y_bits - 1));
> -		*x1 = fields->x;
> -		*y1 = fields->y;
> +		*x1 = fields->pt.x;
> +		*y1 = fields->pt.y;
>   		*x2 = 2 * box_middle_x - *x1;
>   		*y2 = 2 * box_middle_y - *y1;
> -	}
> +	}	
>   }
>
>   /*
> @@ -461,6 +485,55 @@ static void alps_report_semi_mt_data(str
>   	alps_set_slot(dev, 1, num_fingers == 2, x2, y2);
>   }
>
> +static void alps_report_semi_mt_data_ex(struct input_dev *dev, int num_fingers,
> +				     struct alps_abs_data coord[])
> +{
> +	unsigned char i;
> +
> +	for (i = 0; i < num_fingers; i++) {
> +		if (!coord[i].x || !coord[i].y || !coord[i].z) {
> +			alps_set_slot(dev, i, 0, coord[i].x, coord[i].y);
> +		}
> +		else {
> +			alps_set_slot(dev, i, 1, coord[i].x, coord[i].y);
> +		}
> +	}
> +}
> +
> +static void alps_report_coord_and_btn(struct psmouse *psmouse,
> +				      struct alps_fields *f)
> +{
> +	struct input_dev *dev;
> +
> +	if (!psmouse || !f)
> +		return;
> +
> +	dev = psmouse->dev;
> +
> +	if (f->fingers) {
> +		input_report_key(dev, BTN_TOUCH, 1);
> +
> +		alps_report_semi_mt_data(dev, f->fingers,
> +			f->pt_img[0].x, f->pt_img[0].y,
> +			f->pt_img[1].x, f->pt_img[1].y);
> +		input_mt_report_finger_count(dev, f->fingers);
> +
> +		input_report_abs(dev, ABS_X, f->pt_img[0].x);
> +		input_report_abs(dev, ABS_Y, f->pt_img[0].y);
> +		input_report_abs(dev, ABS_PRESSURE, f->pt_img[0].z);
> +	} else {
> +		input_report_key(dev, BTN_TOUCH, 0);
> +		input_mt_report_finger_count(dev, 0);
> +		input_report_abs(dev, ABS_PRESSURE, 0);
> +	}
> +
> +	input_report_key(dev, BTN_LEFT, f->btn.left);
> +	input_report_key(dev, BTN_RIGHT, f->btn.right);
> +	input_report_key(dev, BTN_MIDDLE, f->btn.middle);
> +
> +	input_sync(dev);
> +}
> +
>   static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
>   {
>   	struct alps_data *priv = psmouse->private;
> @@ -523,13 +596,22 @@ static void alps_process_trackstick_pack
>
>   static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p)
>   {
> -	f->left = !!(p[3] & 0x01);
> -	f->right = !!(p[3] & 0x02);
> -	f->middle = !!(p[3] & 0x04);
> -
> -	f->ts_left = !!(p[3] & 0x10);
> -	f->ts_right = !!(p[3] & 0x20);
> -	f->ts_middle = !!(p[3] & 0x40);
> +	f->btn.left = !!(p[3] & 0x01);
> +	f->btn.right = !!(p[3] & 0x02);
> +	f->btn.middle = !!(p[3] & 0x04);
> +
> +	f->btn.ts_left = !!(p[3] & 0x10);
> +	f->btn.ts_right = !!(p[3] & 0x20);
> +	f->btn.ts_middle = !!(p[3] & 0x40);
> +}
> +
> +/* proto_v9 */
> +static void alps_decode_button_ss3(struct alps_fields *f, unsigned char *p,
> +				struct alps_data *priv)
> +{
> +	if(f->dol_packet_type == DOL_GPDATA ||
> +	   f->dol_packet_type == DOL_APDATA)
> +		f->btn.left = !!(p[3]&0x01);
>   }
>
>   static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
> @@ -546,10 +628,10 @@ static void alps_decode_pinnacle(struct
>   		   ((p[2] & 0x7f) << 1) |
>   		   (p[4] & 0x01);
>
> -	f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
> +	f->pt.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
>   	       ((p[0] & 0x30) >> 4);
> -	f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
> -	f->z = p[5] & 0x7f;
> +	f->pt.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
> +	f->pt.z = p[5] & 0x7f;
>
>   	alps_decode_buttons_v3(f, p);
>   }
> @@ -559,7 +641,7 @@ static void alps_decode_rushmore(struct
>   {
>   	alps_decode_pinnacle(f, p, psmouse);
>
> -	/* Rushmore's packet decode has a bit difference with Pinnacle's */
> +	/* rushmore's packet decode has a bit difference with pinnacle's */
>   	f->is_mp = !!(p[5] & 0x40);
>   	f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1;
>   	f->x_map |= (p[5] & 0x10) << 11;
> @@ -572,18 +654,14 @@ static void alps_decode_dolphin(struct a
>   	u64 palm_data = 0;
>   	struct alps_data *priv = psmouse->private;
>
> -	f->first_mp = !!(p[0] & 0x02);
> -	f->is_mp = !!(p[0] & 0x20);
> -
> -	if (!f->is_mp) {
> -		f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
> -		f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
> -		f->z = (p[0] & 4) ? 0 : p[5] & 0x7f;
> -		alps_decode_buttons_v3(f, p);
> -	} else {
> +	f->is_mp = 0;
> +	f->first_mp = 0;
> +	
> +	if ( DOL_IS_PROFDATA(p) ){
> +		f->is_mp = 1;
> +		f->dol_packet_type = DOL_PROFDATA;
>   		f->fingers = ((p[0] & 0x6) >> 1 |
>   		     (p[0] & 0x10) >> 2);
> -
>   		palm_data = (p[1] & 0x7f) |
>   			    ((p[2] & 0x7f) << 7) |
>   			    ((p[4] & 0x7f) << 14) |
> @@ -598,6 +676,400 @@ static void alps_decode_dolphin(struct a
>   		/* X-profile is stored in p(n) to p(n+m-1), m = x_bits; */
>   		f->x_map = (palm_data >> priv->y_bits) &
>   			   (BIT(priv->x_bits) - 1);
> +	}else{
> +		if ( DOL_IS_APDATA(p) ){
> +			f->dol_packet_type = DOL_APDATA;
> +			f->fingers = 2;
> +			f->pt_img[0].x = p[1]<<3;
> +			f->pt_img[0].y = p[2]<<2;
> +			f->pt_img[0].z = 64;
> +			f->pt_img[1].x = p[4]<<3;
> +			f->pt_img[1].y = p[5]<<2;
> +			f->pt_img[1].z = 64;
> +		}else{// is gp data
> +			f->dol_packet_type = DOL_GPDATA;
> +			f->first_mp = !!(p[0]&0x02);
> +			f->pt.x = f->pt_img[0].x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
> +			f->pt.y = f->pt_img[0].y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
> +			f->pt.z = f->pt_img[0].z = (p[0] & 4) ? 0 : p[5] & 0x7f;
> +			/*
> +				Button number will be included in the PROFILE data for a 3-f
> +				packet. So do not change .fingers because it will be updated
> +				in Profile data packet.
> +			*/
> +			if(!f->first_mp)
> +				f->fingers =
> +					(f->pt_img[0].x && f->pt_img[0].y && f->pt_img[0].z)?1:0;
> +		}
> +		
> +		if(priv->proto_version == ALPS_PROTO_V9){
> +			alps_decode_button_ss3(f, p, priv);
> +		}else{
> +			alps_decode_buttons_v3(f, p);
> +		}	
> +	}
> +}
> +
> +unsigned char alps_get_pkt_id_ss4_v1(char *byte)
> +{
> +	unsigned char pkt_id = SS4_PACKET_ID_IDLE;
> +
> +	if (((byte[0] & 0xFF) == 0x08) && ((byte[1] & 0xFF) == 0x10) &&
> +	     ((byte[2] & 0xFF) == 0x00) && ((byte[3] & 0x8F) == 0x08) &&
> +	     ((byte[4] & 0xFF) == 0x01) && ((byte[5] & 0xFF) == 0x00)) {
> +		pkt_id = SS4_PACKET_ID_IDLE;
> +	}
> +	else if (((byte[0] & 0x08) == 0x08) && ((byte[1] & 0x10) == 0x10) &&
> +		((byte[3] & 0x8E) == 0x08) && ((byte[4] & 0x81) == 0x01) ) {
> +		pkt_id = SS4_PACKET_ID_ONE;
> +	}
> +	else if(((byte[0] & 0x08) == 0x08) && ((byte[3] & 0x08) == 0x08) &&
> +		  ((byte[4] & 0x01) == 0x01) ){
> +		if(((byte[5] & 0x01) == 0x01))
> +			pkt_id = SS4_PACKET_ID_TWO;
> +		else
> +			pkt_id = SS4_PACKET_ID_MULTI;
> +	}
> +
> +	return pkt_id;
> +}
> +
> +unsigned char alps_get_pkt_id_ss4_v2(char *byte)
> +{
> +	unsigned char pkt_id = SS4_PACKET_ID_IDLE;
> +
> +	if (((byte[0] & 0xFF) == 0x18) && ((byte[1] & 0xFF) == 0x10) &&
> +	     ((byte[2] & 0xFF) == 0x00) && ((byte[3] & 0x88) == 0x08) &&
> +	     ((byte[4] & 0xFF) == 0x10) && ((byte[5] & 0xFF) == 0x00)) {
> +		pkt_id = SS4_PACKET_ID_IDLE;
> +	}
> +	else if (!(byte[3] & 0x10)) {
> +		pkt_id = SS4_PACKET_ID_ONE;
> +	}
> +	else {
> +		if (!(byte[3] & 0x20)) {
> +			pkt_id = SS4_PACKET_ID_TWO;
> +		}
> +		else {
> +			pkt_id = SS4_PACKET_ID_MULTI;
> +		}
> +	}
> +
> +	return pkt_id;
> +}
> +
> +static void alps_process_btnless_click(struct psmouse *psmouse, struct alps_fields *f)
> +{
> +	struct alps_data *priv = psmouse->private;
> +	
> +	if (!f->btn.left)
> +		return;
> +
> +	// Clear button flag
> +	f->btn.left = 0;
> +
> +	switch (f->fingers) {
> +	case 1:
> +		// In Left Resting Area
> +		if (PT_IN_LEFT_BTN_AREA(f->pt_img[0].x, f->pt_img[0].y, priv->x_max, priv->y_max)) {
> +			f->btn.left = 1;
> +		}
> +		// In Right Resting Area
> +		else if (PT_IN_RIGHT_BTN_AREA(f->pt_img[0].x, f->pt_img[0].y, priv->x_max, priv->y_max)) {
> +			f->btn.right = 1;
> +		}
> +		// In Normal area
> +		else {
> +			f->btn.left = 1;
> +		}
> +		break;
> +
> +	case 2:
> +		// Both two fingers are in Normal area
> +		if (!PT_IN_BTN_AREA(f->pt_img[0].x, f->pt_img[0].y, priv->x_max, priv->y_max) &&
> +			!PT_IN_BTN_AREA(f->pt_img[1].x, f->pt_img[1].y, priv->x_max, priv->y_max)) {
> +			f->btn.right = 1;
> +		}
> +		// Either one finger is in Right Area
> +		else if (PT_IN_RIGHT_BTN_AREA(f->pt_img[0].x, f->pt_img[0].y, priv->x_max, priv->y_max) ||
> +				 PT_IN_RIGHT_BTN_AREA(f->pt_img[1].x, f->pt_img[1].y, priv->x_max, priv->y_max)) {
> +			f->btn.right = 1;
> +		}
> +		else {
> +			f->btn.left = 1;
> +		}
> +		break;
> +	
> +	case 3:
> +		f->btn.middle = 1;
> +		break;
> +
> +	case 0:
> +	default:
> +		break;
> +	}
> +}
> +
> +static void alps_process_resting_finger(struct psmouse *psmouse, struct alps_fields *f, struct alps_abs_data *output, unsigned char *p_fn)
> +{
> +	struct alps_data *priv = psmouse->private;
> +	static struct alps_abs_data prev_pt[10];
> +	static struct alps_abs_data init_pt[10];
> +	static unsigned char is_moved[10];
> +	static unsigned char prev_fn = 0;
> +	static unsigned char prev_coord_is_output[10];
> +	unsigned char cur_coord_is_output[10];
> +	unsigned char in_resting_area[10];
> +	unsigned char i, index;
> +	unsigned char output_fn = 0;
> +
> +	memset(in_resting_area, 0, sizeof(in_resting_area));
> +	memset(cur_coord_is_output, 0, sizeof(cur_coord_is_output));
> +
> +	// Clear "is_moved" flag when finger number changed
> +	if (f->fingers != prev_fn) {
> +		memset(is_moved, 0, sizeof(is_moved));
> +		memcpy(init_pt, f->pt_img, sizeof(f->pt_img));
> +	}
> +
> +	// Calculate output finger
> +	for (i = 0, index = 0; i < f->fingers; i++) {
> +		if (is_moved[i] == 0 && (abs(f->pt_img[i].x - init_pt[i].x) > RESTING_FN_LARGE_MOVEMENT) ) {
> +			is_moved[i] = 1;
> +		}
> +
> +		// Check if in resting area
> +		if (PT_IN_BTN_AREA(f->pt_img[i].x, f->pt_img[i].y, priv->x_max, priv->y_max)) {
> +			in_resting_area[i] = 1;
> +		}
> +
> +		if (!in_resting_area[i] || (in_resting_area[i] && is_moved[i])) {
> +			memcpy(&output[index++], &f->pt_img[i], sizeof(struct alps_abs_data));
> +			cur_coord_is_output[i] = 1;
> +			output_fn ++;	
> +        }
> +	}
> +	
> +	// A normal finger becomes a resting finger
> +	for (i = 0; i < f->fingers; i++) {
> +		if (prev_coord_is_output[i] && !cur_coord_is_output[i] && f->pt_img[i].z) {
> +			output_fn = 0;
> +			memset(output, 0, sizeof(struct alps_abs_data)*f->fingers);
> +		}
> +	}
> +
> +	memcpy(prev_pt, f->pt_img, sizeof(f->pt_img));
> +	memcpy(prev_coord_is_output, cur_coord_is_output, sizeof(cur_coord_is_output));
> +	prev_fn = f->fingers;
> +	*p_fn = output_fn;
> +}
> +
> +static void alps_decode_ss4_v1(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse)
> +{
> +	struct alps_data *priv = psmouse->private;
> +	unsigned char pkt_id;
> +	unsigned int no_data_x, no_data_y;
> +
> +	if (!psmouse || !f || !p )
> +		return;
> +
> +	pkt_id = alps_get_pkt_id_ss4_v1(p);
> +
> +	// Current packet is 1Finger coordinate packet
> +	switch (pkt_id) {
> +	case SS4_PACKET_ID_ONE:
> +		f->pt_img[0].x = SS4_1F_X_V1(p);
> +		f->pt_img[0].y = SS4_1F_Y_V1(p);
> +		f->pt_img[0].z = ((SS4_1F_Z_V1(p)) * 2) & 0x7f;	// Keep Z-value in 0-127
> +
> +		f->large_fn |= COSMO_1F_LFB(p) ? 0x01 : 0x00;
> +
> +		f->fingers = 1;
> +		f->first_mp = 0;
> +		f->is_mp = 0;
> +		break;
> +
> +	case SS4_PACKET_ID_TWO:
> +		if (priv->flags & ALPS_BTNLESS) {
> +			f->pt_img[0].x = SS4_BTL_MF_X_V1( p, 0 ) ;
> +			f->pt_img[0].y = SS4_BTL_MF_Y_V1( p, 0 ) ;
> +			f->pt_img[1].x = SS4_BTL_MF_X_V1( p, 1 ) ;
> +			f->pt_img[1].y = SS4_BTL_MF_Y_V1( p, 1 ) ;
> +		}
> +		else {
> +			f->pt_img[0].x = SS4_STD_MF_X_V1( p, 0 ) ;
> +			f->pt_img[0].y = SS4_STD_MF_Y_V1( p, 0 ) ;
> +			f->pt_img[1].x = SS4_STD_MF_X_V1( p, 1 ) ;
> +			f->pt_img[1].y = SS4_STD_MF_Y_V1( p, 1 ) ;
> +		}
> +		f->pt_img[0].z = SS4_MF_Z_V1( p, 0 ) ? 0x30 : 0;
> +		f->pt_img[1].z = SS4_MF_Z_V1( p, 1 ) ? 0x30 : 0;
> +
> +		if (SS4_IS_MF_CONTINUE_V1(p)) {
> +			f->first_mp = 1;
> +		}
> +		else {
> +			f->fingers = 2;
> +			f->first_mp = 0;
> +		}
> +		f->is_mp = 0;
> +	
> +		break;
> +
> +	case SS4_PACKET_ID_MULTI:
> +		if (priv->flags & ALPS_BTNLESS) {
> +			f->pt_img[2].x = SS4_BTL_MF_X_V1( p, 0 ) ;
> +			f->pt_img[2].y = SS4_BTL_MF_Y_V1( p, 0 ) ;
> +			f->pt_img[3].x = SS4_BTL_MF_X_V1( p, 1 ) ;
> +			f->pt_img[3].y = SS4_BTL_MF_Y_V1( p, 1 ) ;
> +			no_data_x = SS4_MFPACKET_NO_AX_BL;
> +			no_data_y = SS4_MFPACKET_NO_AY_BL;
> +		}
> +		else {
> +			f->pt_img[2].x = SS4_STD_MF_X_V1( p, 0 ) ;
> +			f->pt_img[2].y = SS4_STD_MF_Y_V1( p, 0 ) ;
> +			f->pt_img[3].x = SS4_STD_MF_X_V1( p, 1 ) ;
> +			f->pt_img[3].y = SS4_STD_MF_Y_V1( p, 1 ) ;
> +			no_data_x = SS4_MFPACKET_NO_AX;
> +			no_data_y = SS4_MFPACKET_NO_AY;
> +		}
> +		f->pt_img[2].z = SS4_MF_Z_V1( p, 0 ) ? 0x30 : 0;
> +		f->pt_img[3].z = SS4_MF_Z_V1( p, 1 ) ? 0x30 : 0;
> +
> +		f->first_mp = 0;
> +		f->is_mp = 1;
> +
> +		if (SS4_IS_5F_DETECTED_V1(p)) {
> +			f->fingers = 5;
> +		}
> +		else if (f->pt_img[3].x == no_data_x &&
> +			     f->pt_img[3].y == no_data_y) {
> +			f->fingers = 3;
> +			f->pt_img[3].x = 0;
> +			f->pt_img[3].y = 0;
> +			f->pt_img[3].z = 0;
> +		}
> +		else {
> +			f->fingers = 4;
> +		}
> +		break;
> +
> +	case SS4_PACKET_ID_IDLE:
> +	default:
> +		memset(f, 0, sizeof(struct alps_fields));
> +		break;
> +	}
> +
> +	f->btn.left = !!(SS4_BTN_V1(p) & 0x01) ;
> +	if (!(priv->flags & ALPS_BTNLESS)) {
> +		f->btn.right = !!(SS4_BTN_V1(p) & 0x02) ;
> +		f->btn.middle = !!(SS4_BTN_V1(p) & 0x04) ;
> +	}
> +}
> +
> +static void alps_decode_ss4_v2(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse)
> +{
> +	struct alps_data *priv = psmouse->private;
> +	unsigned char pkt_id;
> +	unsigned int no_data_x, no_data_y;
> +
> +	if (!psmouse || !f || !p )
> +		return;
> +
> +	pkt_id = alps_get_pkt_id_ss4_v2(p);
> +
> +	// Current packet is 1Finger coordinate packet
> +	switch (pkt_id) {
> +	case SS4_PACKET_ID_ONE:
> +		f->pt_img[0].x = SS4_1F_X_V2(p);
> +		f->pt_img[0].y = SS4_1F_Y_V2(p);
> +		f->pt_img[0].z = ((SS4_1F_Z_V2(p)) * 2) & 0x7f;	// Keep Z-value in 0-127
> +		f->large_fn |= COSMO_1F_LFB_V2(p) ? 0x01 : 0x00;
> +
> +		f->fingers = 1;
> +		f->first_mp = 0;
> +		f->is_mp = 0;
> +		break;
> +
> +	case SS4_PACKET_ID_TWO:
> +		if (priv->flags & ALPS_BTNLESS) {
> +			f->pt_img[0].x = SS4_BTL_MF_X_V2( p, 0 ) ;
> +			f->pt_img[0].y = SS4_BTL_MF_Y_V2( p, 0 ) ;
> +			f->pt_img[1].x = SS4_BTL_MF_X_V2( p, 1 ) ;
> +			f->pt_img[1].y = SS4_BTL_MF_Y_V2( p, 1 ) ;
> +		}
> +		else {
> +			f->pt_img[0].x = SS4_STD_MF_X_V2( p, 0 ) ;
> +			f->pt_img[0].y = SS4_STD_MF_Y_V2( p, 0 ) ;
> +			f->pt_img[1].x = SS4_STD_MF_X_V2( p, 1 ) ;
> +			f->pt_img[1].y = SS4_STD_MF_Y_V2( p, 1 ) ;
> +		}
> +		f->pt_img[0].z = SS4_MF_Z_V2( p, 0 ) ? 0x30 : 0;
> +		f->pt_img[1].z = SS4_MF_Z_V2( p, 1 ) ? 0x30 : 0;
> +
> +		f->large_fn |= COSMO_MF_LF_V2( p, 0 ) ? 0x01 : 0 ;
> +		f->large_fn |= COSMO_MF_LF_V2( p, 1 ) ? 0x02 : 0 ;
> +
> +		if (SS4_IS_MF_CONTINUE_V2(p)) {
> +			f->first_mp = 1;
> +		}
> +		else {
> +			f->fingers = 2;
> +			f->first_mp = 0;
> +		}
> +		f->is_mp = 0;
> +		break;
> +
> +	case SS4_PACKET_ID_MULTI:
> +		if (priv->flags & ALPS_BTNLESS) {
> +			f->pt_img[2].x = SS4_BTL_MF_X_V2( p, 0 ) ;
> +			f->pt_img[2].y = SS4_BTL_MF_Y_V2( p, 0 ) ;
> +			f->pt_img[3].x = SS4_BTL_MF_X_V2( p, 1 ) ;
> +			f->pt_img[3].y = SS4_BTL_MF_Y_V2( p, 1 ) ;
> +			no_data_x = SS4_MFPACKET_NO_AX_BL;
> +			no_data_y = SS4_MFPACKET_NO_AY_BL;
> +		}
> +		else {
> +			f->pt_img[2].x = SS4_STD_MF_X_V2( p, 0 ) ;
> +			f->pt_img[2].y = SS4_STD_MF_Y_V2( p, 0 ) ;
> +			f->pt_img[3].x = SS4_STD_MF_X_V2( p, 1 ) ;
> +			f->pt_img[3].y = SS4_STD_MF_Y_V2( p, 1 ) ;
> +			no_data_x = SS4_MFPACKET_NO_AX;
> +			no_data_y = SS4_MFPACKET_NO_AY;
> +		}
> +		f->pt_img[2].z = SS4_MF_Z_V2( p, 0 ) ? 0x30 : 0;
> +		f->pt_img[3].z = SS4_MF_Z_V2( p, 1 ) ? 0x30 : 0;
> +
> +		f->large_fn |= COSMO_MF_LF_V2( p, 0 ) ? 0x04 : 0 ;
> +		f->large_fn |= COSMO_MF_LF_V2( p, 1 ) ? 0x08 : 0 ;
> +		f->first_mp = 0;
> +		f->is_mp = 1;
> +
> +		if (SS4_IS_5F_DETECTED_V2(p)) {
> +			f->fingers = 5;
> +		}
> +		else if (f->pt_img[3].x == no_data_x &&
> +			     f->pt_img[3].y == no_data_y) {
> +			f->fingers = 3;
> +			f->pt_img[3].x = 0;
> +			f->pt_img[3].y = 0;
> +			f->pt_img[3].z = 0;
> +		}
> +		else {
> +			f->fingers = 4;
> +		}
> +		break;
> +
> +	case SS4_PACKET_ID_IDLE:
> +	default:
> +		memset(f, 0, sizeof(struct alps_fields));
> +		break;
> +	}
> +
> +	f->btn.left = !!(SS4_BTN_V2(p) & 0x01) ;
> +	if (!(priv->flags & ALPS_BTNLESS)) {
> +		f->btn.right = !!(SS4_BTN_V2(p) & 0x02) ;
> +		f->btn.middle = !!(SS4_BTN_V2(p) & 0x04) ;
>   	}
>   }
>
> @@ -658,7 +1130,7 @@ static void alps_process_touchpad_packet
>   				 * there is no need to compare with bmap_fn.
>   				 */
>   				alps_process_bitmap_dolphin(priv, &f, &x1, &y1,
> -							    &x2, &y2);
> +							    &x2, &y2);			
>   			}
>   		} else {
>   			priv->multi_packet = 0;
> @@ -690,7 +1162,7 @@ static void alps_process_touchpad_packet
>   	 * with x, y, and z all zero, so these seem to be flukes.
>   	 * Ignore them.
>   	 */
> -	if (f.x && f.y && !f.z)
> +	if (f.pt.x && f.pt.y && !f.pt.z)
>   		return;
>
>   	/*
> @@ -698,12 +1170,12 @@ static void alps_process_touchpad_packet
>   	 * to rely on ST data.
>   	 */
>   	if (!fingers) {
> -		x1 = f.x;
> -		y1 = f.y;
> -		fingers = f.z > 0 ? 1 : 0;
> +		x1 = f.pt.x;
> +		y1 = f.pt.y;
> +		fingers = f.pt.z > 0 ? 1 : 0;
>   	}
> -
> -	if (f.z >= 64)
> +	
> +	if (f.pt.z >= 64)
>   		input_report_key(dev, BTN_TOUCH, 1);
>   	else
>   		input_report_key(dev, BTN_TOUCH, 0);
> @@ -712,22 +1184,22 @@ static void alps_process_touchpad_packet
>
>   	input_mt_report_finger_count(dev, fingers);
>
> -	input_report_key(dev, BTN_LEFT, f.left);
> -	input_report_key(dev, BTN_RIGHT, f.right);
> -	input_report_key(dev, BTN_MIDDLE, f.middle);
> -
> -	if (f.z > 0) {
> -		input_report_abs(dev, ABS_X, f.x);
> -		input_report_abs(dev, ABS_Y, f.y);
> +	input_report_key(dev, BTN_LEFT, f.btn.left);
> +	input_report_key(dev, BTN_RIGHT, f.btn.right);
> +	input_report_key(dev, BTN_MIDDLE, f.btn.middle);
> +
> +	if (f.pt.z > 0) {
> +		input_report_abs(dev, ABS_X, f.pt.x);
> +		input_report_abs(dev, ABS_Y, f.pt.y);
>   	}
> -	input_report_abs(dev, ABS_PRESSURE, f.z);
> +	input_report_abs(dev, ABS_PRESSURE, f.pt.z);
>
>   	input_sync(dev);
>
>   	if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
> -		input_report_key(dev2, BTN_LEFT, f.ts_left);
> -		input_report_key(dev2, BTN_RIGHT, f.ts_right);
> -		input_report_key(dev2, BTN_MIDDLE, f.ts_middle);
> +		input_report_key(dev2, BTN_LEFT, f.btn.ts_left);
> +		input_report_key(dev2, BTN_RIGHT, f.btn.ts_right);
> +		input_report_key(dev2, BTN_MIDDLE, f.btn.ts_middle);
>   		input_sync(dev2);
>   	}
>   }
> @@ -752,6 +1224,66 @@ static void alps_process_packet_v3(struc
>   	alps_process_touchpad_packet_v3_v5(psmouse);
>   }
>
> +/* proto_v9 */
> +static void alps_process_touchpad_packet_ss3(struct psmouse *psmouse)
> +{
> +	struct alps_data *priv = psmouse->private;
> +	unsigned char *packet = psmouse->packet;
> +	struct input_dev *dev = psmouse->dev;
> +	int fingers = 0;
> +	struct alps_fields f = {0};
> +	struct alps_abs_data 	pt_output[2] = {{0,0,0},{0,0,0}};
> +	unsigned char output_fn_num = 0;
> +	
> +	priv->decode_fields(&f,packet,psmouse);
> +
> +	if ((!!priv->multi_packet) != (!!f.is_mp)){
> +		priv->multi_packet = 0;
> +		return ;
> +	}
> +
> +	/* When f.first_mp is 1, next packet should be a
> +	 *	bitmap packet(when there is no error).
> +	 */
> +	priv->multi_packet = f.first_mp;
> +	
> +	/* Don't process any 3-f data */
> +	if(f.first_mp || f.is_mp)
> +	{
> +		return;
> +	}	
> +
> +	/*
> +	 * If we don't have MT data or the bitmaps were empty, we have
> +	 * to rely on ST data.
> +	 */
> +	fingers = f.fingers;
> +	
> +	alps_process_resting_finger(psmouse, &f, pt_output, &output_fn_num);
> +	alps_process_btnless_click(psmouse, &f);
> +
> +	if (pt_output[0].z || pt_output[1].z)
> +		input_report_key(dev, BTN_TOUCH, 1);
> +	else
> +		input_report_key(dev, BTN_TOUCH, 0);
> +
> +	alps_report_semi_mt_data_ex(dev, 2, pt_output);
> +
> +	input_mt_report_finger_count(dev, output_fn_num);
> +
> +	input_report_key(dev, BTN_LEFT, f.btn.left);
> +	input_report_key(dev, BTN_RIGHT, f.btn.right);
> +	input_report_key(dev, BTN_MIDDLE, f.btn.middle);
> +
> +	if (pt_output[0].z > 0) {
> +		input_report_abs(dev, ABS_X, pt_output[0].x);
> +		input_report_abs(dev, ABS_Y, pt_output[0].y);
> +	}
> +	input_report_abs(dev, ABS_PRESSURE, pt_output[0].z?16:0);
> +
> +	input_sync(dev);
> +}
> +
>   static void alps_process_packet_v6(struct psmouse *psmouse)
>   {
>   	struct alps_data *priv = psmouse->private;
> @@ -919,6 +1451,421 @@ static void alps_process_packet_v4(struc
>   	input_sync(dev);
>   }
>
> +static bool alps_is_valid_package_v7(struct psmouse *psmouse)
> +{
> +	if ((psmouse->pktcnt == 3) && ((psmouse->packet[2] & 0x40) != 0x40))
> +		return false;
> +	if ((psmouse->pktcnt == 4) && ((psmouse->packet[3] & 0x48) != 0x48))
> +		return false;
> +	if ((psmouse->pktcnt == 6) && ((psmouse->packet[5] & 0x40) != 0x0))
> +		return false;
> +	return true;
> +}
> +
> +static int alps_drop_unsupported_packet_v7(struct psmouse *psmouse)
> +{
> +	struct alps_data *priv = psmouse->private;
> +	int drop = 1;
> +
> +	if (priv->r.v7.pkt_id == V7_PACKET_ID_NEW ||
> +	    priv->r.v7.pkt_id == V7_PACKET_ID_TWO ||
> +	    priv->r.v7.pkt_id == V7_PACKET_ID_MULTI ||
> +	    priv->r.v7.pkt_id == V7_PACKET_ID_IDLE)
> +		drop = 0;
> +
> +	return drop;
> +}
> +
> +static unsigned char alps_get_packet_id_v7(char *byte)
> +{
> +	unsigned char packet_id;
> +
> +	if (byte[4] & 0x40)
> +		packet_id = V7_PACKET_ID_TWO;
> +	else if (byte[4] & 0x01)
> +		packet_id = V7_PACKET_ID_MULTI;
> +	else if ((byte[0] & 0x10) && !(byte[4] & 0x43))
> +		packet_id = V7_PACKET_ID_NEW;
> +	else if (byte[1] == 0x00 && byte[4] == 0x00)
> +		packet_id = V7_PACKET_ID_IDLE;
> +	else
> +		packet_id = V7_PACKET_ID_UNKNOWN;
> +
> +	return packet_id;
> +}
> +
> +static void alps_get_finger_coordinate_v7(struct alps_abs_data *pt,
> +					  unsigned char *pkt,
> +					  unsigned char pkt_id)
> +{
> +	if ((pkt_id == V7_PACKET_ID_TWO) ||
> +	   (pkt_id == V7_PACKET_ID_MULTI) ||
> +	   (pkt_id == V7_PACKET_ID_NEW)) {
> +		pt[0].x = ((pkt[2] & 0x80) << 4);
> +		pt[0].x |= ((pkt[2] & 0x3F) << 5);
> +		pt[0].x |= ((pkt[3] & 0x30) >> 1);
> +		pt[0].x |= (pkt[3] & 0x07);
> +		pt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07);
> +
> +		pt[1].x = ((pkt[3] & 0x80) << 4);
> +		pt[1].x |= ((pkt[4] & 0x80) << 3);
> +		pt[1].x |= ((pkt[4] & 0x3F) << 4);
> +		pt[1].y = ((pkt[5] & 0x80) << 3);
> +		pt[1].y |= ((pkt[5] & 0x3F) << 4);
> +
> +		if (pkt_id == V7_PACKET_ID_TWO) {
> +			pt[1].x &= ~0x000F;
> +			pt[1].y |= 0x000F;
> +		} else if (pkt_id == V7_PACKET_ID_MULTI) {
> +			pt[1].x &= ~0x003F;
> +			pt[1].y &= ~0x0020;
> +			pt[1].y |= ((pkt[4] & 0x02) << 4);
> +			pt[1].y |= 0x001F;
> +		} else if (pkt_id == V7_PACKET_ID_NEW) {
> +			pt[1].x &= ~0x003F;
> +			pt[1].x |= (pkt[0] & 0x20);
> +			pt[1].y |= 0x000F;
> +		}
> +
> +		pt[0].y = 0x7FF - pt[0].y;
> +		pt[1].y = 0x7FF - pt[1].y;
> +
> +		pt[0].z = (pt[0].x && pt[0].y) ? 62 : 0;
> +		pt[1].z = (pt[1].x && pt[1].y) ? 62 : 0;
> +	}
> +}
> +
> +static void alps_decode_packet_v7(struct alps_fields *f,
> +				  unsigned char *p,
> +				  struct psmouse *psmouse)
> +{
> +	struct alps_data *priv = psmouse->private;
> +
> +	priv->r.v7.pkt_id = alps_get_packet_id_v7(p);
> +
> +	alps_get_finger_coordinate_v7(f->pt_img, p, priv->r.v7.pkt_id);
> +
> +	priv->r.v7.rest_left = 0;
> +	priv->r.v7.rest_right = 0;
> +	priv->r.v7.additional_fingers = 0;
> +	memset(&f->btn, 0, sizeof(struct alps_btn));
> +	
> +	if (priv->r.v7.pkt_id == V7_PACKET_ID_TWO ||
> +	    priv->r.v7.pkt_id == V7_PACKET_ID_MULTI) {
> +
> +		if (priv->flags & ALPS_BTNLESS) {
> +			priv->r.v7.rest_left = (p[0] & 0x10) >> 4;
> +			priv->r.v7.rest_right = (p[0] & 0x20) >> 5;
> +			f->btn.middle = (p[0] & 0x80) >> 7;
> +		}
> +		else {
> +			f->btn.left = (p[0] & 0x80) >> 7;
> +			f->btn.right = (p[0] & 0x20) >> 5;
> +			f->btn.middle = (p[0] & 0x10) >> 4;
> +		}
> +	}
> +
> +	if (priv->r.v7.pkt_id == V7_PACKET_ID_MULTI)
> +		priv->r.v7.additional_fingers = p[5] & 0x03;
> +}
> +
> +static void alps_set_each_pt_attr_v7(struct psmouse *psmouse,
> +				     struct alps_abs_data *pt,
> +				     struct alps_bl_pt_attr *pt_attr)
> +{
> +	struct alps_data *priv = psmouse->private;
> +	unsigned int dist;
> +
> +	if (!pt_attr->is_init_pt_got && pt->z != 0) {
> +		pt_attr->is_init_pt_got = 1;
> +		pt_attr->is_counted = 0;
> +		memcpy(&pt_attr->init_pt, pt, sizeof(pt_attr->init_pt));
> +	}
> +
> +	if (pt->z != 0) {
> +		if (pt->y < priv->resting_zone_y_min) {
> +			/* A finger is recognized as a non-resting finger if it's
> +			position is outside the resting finger zone.*/
> +			pt_attr->zone = ZONE_NORMAL;
> +			pt_attr->is_counted = 1;
> +		} else {
> +			/* A finger is recognized as a resting finger if it's position
> +			is inside the resting finger zone and there's no large movement
> +			from it's touch down position.*/
> +			pt_attr->zone = ZONE_RESTING;
> +
> +			if (pt->x > priv->x_max / 2)
> +				pt_attr->zone |= ZONE_RIGHT_BTN;
> +			else
> +				pt_attr->zone |= ZONE_LEFT_BTN;
> +			
> +			/* A resting finger will turn to be a non-resting finger if it
> +			has made large movement from it's touch down position. A
> +			non-resting finger will never turn to a resting finger before
> +			it leaves the touchpad surface.*/
> +			if (pt_attr->is_init_pt_got) {
> +				dist = alps_pt_distance(pt, &pt_attr->init_pt);
> +
> +				if (dist > V7_LARGE_MOVEMENT)
> +					pt_attr->is_counted = 1;
> +			}
> +		}
> +	}
> +}
> +
> +static void alps_set_pt_attr_v7(struct psmouse *psmouse,
> +				       struct alps_fields *f)
> +{
> +	struct alps_data *priv = psmouse->private;
> +	int i;
> +
> +	switch (priv->r.v7.pkt_id) {
> +	case  V7_PACKET_ID_TWO:
> +	case  V7_PACKET_ID_MULTI:
> +		for (i = 0; i < V7_IMG_PT_NUM; i++)
> +			alps_set_each_pt_attr_v7(psmouse,
> +						 &f->pt_img[i],
> +						 &priv->pt_attr[i]);
> +		break;
> +	default:
> +		/*All finger attributes are cleared when packet ID is 'IDLE', 'New'
> +		or other unknown IDs. An 'IDLE' packet indicates that there's no
> +		finger and no button activity. A 'NEW' packet indicates the finger
> +		position in packet is not continues from previous packet. Such as
> +		the condition there's finger placed or lifted. In these cases,
> +		finger attributes will be reset.*/
> +
> +		memset(priv->pt_attr, 0, sizeof(priv->pt_attr[0]) * V7_IMG_PT_NUM);
> +		break;
> +	}
> +}
> +
> +static void alps_cal_output_finger_num_v7(struct psmouse *psmouse,
> +					struct alps_fields *f)
> +{
> +	struct alps_data *priv = psmouse->private;
> +	unsigned int fn = 0;
> +	int i;
> +
> +	switch (priv->r.v7.pkt_id) {
> +	case V7_PACKET_ID_IDLE:
> +	case V7_PACKET_ID_NEW:
> +		/*No finger is reported when packet ID is 'IDLE' or 'New'.
> +		An 'IDLE' packet indicates that there's no finger on touchpad.
> +		A 'NEW' packet indicates there's finger placed or lifted. Finger
> +		position of 'New' packet is not continues from the previous packet.*/
> +		fn = 0;
> +		break;
> +	case V7_PACKET_ID_TWO:
> +		if (f->pt_img[0].z == 0) {
> +			/*The first finger slot is zero when a non-resting finger
> +			lifted and remaining only one resting finger on touchpad.
> +			Hardware report the remaining resting finger in second slot.
> +			This resting is ignored*/
> +			fn = 0;
> +		} else if (f->pt_img[1].z == 0) {
> +			/* The second finger slot is zero if there's only one finger*/
> +			fn = 1;
> +		} else {
> +			/*All non-resting fingers will be counted to report*/
> +			fn = 0;
> +			for (i = 0; i < V7_IMG_PT_NUM; i++) {
> +				if (priv->pt_attr[i].is_counted)
> +					fn++;
> +			}
> +
> +			/*In the case that both fingers are
> +			resting fingers, report the first one*/
> +			if (!priv->pt_attr[0].is_counted &&
> +			    !priv->pt_attr[1].is_counted) {
> +				fn = 1;
> +			}
> +		}
> +		break;
> +	case V7_PACKET_ID_MULTI:
> +		/*A packet ID 'MULTI' indicats that at least 3 non-resting
> +		finger exist.*/
> +		fn = 3 + priv->r.v7.additional_fingers;
> +		break;
> +	}
> +
> +	f->fingers = fn;
> +}
> +
> +static void alps_assign_buttons_v7(struct psmouse *psmouse,
> +				   struct alps_fields *f)
> +{
> +	struct alps_data *priv = psmouse->private;
> +
> +	/* It's ClickPad */
> +	if (priv->flags & ALPS_BTNLESS) {
> +		if (!f->btn.middle)
> +			goto exit;
> +
> +		f->btn.middle = 0;
> +		if (priv->prev_btn.left || priv->prev_btn.middle || priv->prev_btn.right) {
> +			memcpy(&f->btn, &priv->prev_btn, sizeof(struct alps_btn));
> +			goto exit;
> +		}
> +
> +		if (priv->r.v7.rest_right ||
> +		    priv->pt_attr[0].zone & ZONE_RIGHT_BTN ||
> +		    priv->pt_attr[1].zone & ZONE_RIGHT_BTN) {
> +			f->btn.right = 1;
> +		} else {
> +			f->btn.left = 1;
> +		}
> +
> +		goto exit;
> +	}
> +
> +	/* It's Standard, do nothing */
> +
> +exit:
> +	memcpy(&priv->prev_btn, &f->btn, sizeof(struct alps_btn));
> +}
> +
> +static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
> +{
> +	struct alps_data *priv = psmouse->private;
> +	unsigned char *packet = psmouse->packet;
> +	struct input_dev *dev2 = priv->dev2;
> +	int x, y, z, left, right, middle;
> +
> +	x = ((packet[2] & 0xBF)) | ((packet[3] & 0x10) << 2);
> +	y = (packet[3] & 0x03) | (packet[4] & 0xB8) | ((packet[3] & 0x20) << 1);
> +	z = (packet[5] & 0x3F) | ((packet[3] & 0x80) >> 1);
> +
> +	left = (packet[1] & 0x01);
> +	right = (packet[1] & 0x02) >> 1;
> +	middle = (packet[1] & 0x04) >> 2;
> +
> +	/* Divide 2 since trackpoint's speed is too fast */
> +	input_report_rel(dev2, REL_X, (char)x / 2);
> +	input_report_rel(dev2, REL_Y, -((char)y / 2));
> +
> +	input_report_key(dev2, BTN_LEFT, left);
> +	input_report_key(dev2, BTN_RIGHT, right);
> +	input_report_key(dev2, BTN_MIDDLE, middle);
> +
> +	input_sync(dev2);
> +
> +}
> +
> +static void alps_process_touchpad_packet_v7(struct psmouse *psmouse)
> +{
> +	struct alps_data *priv = psmouse->private;
> +	struct alps_fields f = {0};
> +	unsigned char *packet = psmouse->packet;
> +
> +	priv->decode_fields(&f, packet, psmouse);
> +
> +	if (alps_drop_unsupported_packet_v7(psmouse))
> +		return;
> +
> +	alps_set_pt_attr_v7(psmouse, &f);
> +
> +	alps_cal_output_finger_num_v7(psmouse, &f);
> +
> +	alps_assign_buttons_v7(psmouse, &f);
> +
> +	alps_report_coord_and_btn(psmouse, &f);
> +}
> +
> +static void alps_process_packet_v7(struct psmouse *psmouse)
> +{
> +	unsigned char *packet = psmouse->packet;
> +
> +	if ((packet[0] == 0x48) && ((packet[4] & 0x47) == 0x06)) {
> +		alps_process_trackstick_packet_v7(psmouse);
> +	} else {
> +		alps_process_touchpad_packet_v7(psmouse);
> +	}
> +
> +}
> +
> +static void alps_process_packet_ss4(struct psmouse *psmouse)
> +{
> +	struct alps_data *priv = psmouse->private;
> +	unsigned char *packet = psmouse->packet;
> +	struct input_dev *dev = psmouse->dev;
> +	struct alps_fields f;
> +	struct alps_abs_data output_data[5];
> +	unsigned char output_fn;
> +
> +	memset(&f, 0, sizeof(struct alps_fields));
> +	priv->decode_fields(&f, packet, psmouse);
> +
> +	if (priv->multi_packet) {
> +		/*
> +		 * Sometimes the first packet will indicate a multi-packet
> +		 * sequence, but sometimes the next multi-packet would not come.
> +		 * Check for this, and when it happens process the
> +		 * position packet as usual.
> +		 */
> +		if (f.is_mp) {
> +			/* Now process the 1st packet */
> +			priv->decode_fields(&f, priv->multi_data, psmouse);
> +		} else {
> +			priv->multi_packet = 0;
> +		}
> +	}
> +
> +	/*
> +	 * "f.is_mp" would always be '0' after merging the 1st and 2nd packet.
> +	 * When it is set, it means 2nd packet comes without 1st packet come.
> +	 */
> +	if (f.is_mp)
> +	{
> +		return;
> +	}
> +
> +	// Save the first packet
> +	if (!priv->multi_packet && f.first_mp) {
> +		priv->multi_packet = 1;
> +		memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
> +		return;
> +	}
> +	
> +	priv->multi_packet = 0;
> +
> +	// Set "output_data" and "output_fn"
> +	memset(&output_data[0], 0, sizeof(output_data));
> +	if (priv->flags & ALPS_BTNLESS) {
> +		alps_process_resting_finger(psmouse, &f, output_data, &output_fn);
> +		alps_process_btnless_click(psmouse, &f);
> +	}
> +	else {
> +		memcpy(&output_data[0], &f.pt_img[0], sizeof(struct alps_abs_data) * f.fingers);
> +		output_fn = f.fingers;
> +	}
> +
> +	f.pt.x = output_data[0].x;
> +	f.pt.y = output_data[0].y;
> +	f.pt.z = output_data[0].z;
> +
> +	if (output_data[0].z || output_data[1].z || output_data[2].z || output_data[3].z)
> +		input_report_key(dev, BTN_TOUCH, 1);
> +	else
> +		input_report_key(dev, BTN_TOUCH, 0);
> +
> +	alps_report_semi_mt_data_ex(dev, 4, output_data);
> +
> +	input_mt_report_finger_count(dev, output_fn);
> +
> +	input_report_key(dev, BTN_LEFT, f.btn.left);
> +	input_report_key(dev, BTN_RIGHT, f.btn.right);
> +	input_report_key(dev, BTN_MIDDLE, f.btn.middle);
> +
> +	if (f.pt.z > 0) {
> +		input_report_abs(dev, ABS_X, f.pt.x);
> +		input_report_abs(dev, ABS_Y, f.pt.y);
> +	}
> +	input_report_abs(dev, ABS_PRESSURE, f.pt.z);
> +
> +	input_sync(dev);
> +}
> +
>   static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
>   					unsigned char packet[],
>   					bool report_buttons)
> @@ -1046,11 +1993,20 @@ static void alps_flush_packet(unsigned l
>   	serio_continue_rx(psmouse->ps2dev.serio);
>   }
>
> +static bool alps_is_valid_package_ss4(struct psmouse *psmouse)
> +{
> +	if(psmouse->pktcnt == 4 && ((psmouse->packet[3] & 0x08) != 0x08))
> +		return false;
> +	if(psmouse->pktcnt == 6 && ((psmouse->packet[5] & 0x10) != 0x0))
> +		return false;
> +	return true;
> +}
> +
>   static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
>   {
>   	struct alps_data *priv = psmouse->private;
>
> -	if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
> +	if ((psmouse->packet[0] & 0xc8) == 0x08 && priv->proto_version != ALPS_PROTO_V8) { /* Can not distinguish V8's first byte from PS/2 packet's */
>   		if (psmouse->pktcnt == 3) {
>   			alps_report_bare_ps2_packet(psmouse, psmouse->packet,
>   						    true);
> @@ -1083,6 +2039,15 @@ static psmouse_ret_t alps_process_byte(s
>   		return PSMOUSE_BAD_DATA;
>   	}
>
> +	if ((priv->proto_version == ALPS_PROTO_V7 && !alps_is_valid_package_v7(psmouse)) ||
> +		(priv->proto_version == ALPS_PROTO_V8 && !alps_is_valid_package_ss4(psmouse)))
> +	{
> +		psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
> +			    psmouse->pktcnt - 1,
> +			    psmouse->packet[psmouse->pktcnt - 1]);
> +		return PSMOUSE_BAD_DATA;
> +	}
> +
>   	if (psmouse->pktcnt == psmouse->pktsize) {
>   		priv->process_packet(psmouse);
>   		return PSMOUSE_FULL_PACKET;
> @@ -1195,6 +2160,22 @@ static int alps_rpt_cmd(struct psmouse *
>   	return 0;
>   }
>
> +static int alps_check_valid_firmware_id(unsigned char id[])
> +{
> +	int valid = 1;
> +
> +	if (id[0] == 0x73)
> +		valid = 1;
> +	else if (id[0] == 0x88) {
> +		if ((id[1] == 0x07) ||
> +		    (id[1] == 0x08) ||
> +		    ((id[1] & 0xf0) == 0xB0))
> +			valid = 1;
> +	}
> +
> +	return valid;
> +}
> +
>   static int alps_enter_command_mode(struct psmouse *psmouse)
>   {
>   	unsigned char param[4];
> @@ -1204,8 +2185,7 @@ static int alps_enter_command_mode(struc
>   		return -1;
>   	}
>
> -	if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) &&
> -	    param[0] != 0x73) {
> +	if (!alps_check_valid_firmware_id(param)) {
>   		psmouse_dbg(psmouse,
>   			    "unknown response while entering command mode\n");
>   		return -1;
> @@ -1707,6 +2687,32 @@ error:
>   	return ret;
>   }
>
> +static int alps_hw_init_v7(struct psmouse *psmouse)
> +{
> +	struct ps2dev *ps2dev = &psmouse->ps2dev;
> +	int reg_val, ret = -1;
> +
> +	if (alps_enter_command_mode(psmouse) ||
> +	    alps_command_mode_read_reg(psmouse, 0xc2d9) == -1)
> +		goto error;
> +
> +	if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
> +		goto error;
> +
> +	reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4);
> +	if (reg_val == -1)
> +		goto error;
> +	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02))
> +		goto error;
> +
> +	alps_exit_command_mode(psmouse);
> +	return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
> +
> +error:
> +	alps_exit_command_mode(psmouse);
> +	return ret;
> +}
> +
>   /* Must be in command mode when calling this function */
>   static int alps_absolute_mode_v4(struct psmouse *psmouse)
>   {
> @@ -1796,6 +2802,97 @@ error:
>   	return -1;
>   }
>
> +static int alps_get_otp_values_ss4(struct psmouse *psmouse, unsigned char index, unsigned char otp[])
> +{
> +	struct ps2dev *ps2dev = &psmouse->ps2dev;
> +	switch (index) {
> +	case 0:
> +		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)  ||
> +		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)  ||
> +		    ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO) )
> +		    	return -1;
> +		
> +		break;
> +
> +	case 1:
> +		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL)  ||
> +		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL)  ||
> +		    ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO) )
> +			return -1;
> +
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +int alps_update_device_area_ss4_v1(unsigned char otp[][4], struct alps_data *priv)
> +{
> +	int	num_x_electrode ;
> +	int	num_y_electrode ;
> +
> +	num_x_electrode = SS4_NUMSENSOR_XOFFSET + (otp[1][0]		& 0x0F);
> +	num_y_electrode = ((otp[1][0] >> 4)	& 0x0F);
> +
> +	priv->x_max = (num_x_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
> +	priv->y_max = (num_y_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
> +
> +	return 0;
> +}
> +
> +int alps_update_device_area_ss4_v2(unsigned char otp[][4], struct alps_data *priv)
> +{
> +	int	num_x_electrode ;
> +	int	num_y_electrode ;
> +
> +	num_x_electrode = SS4_NUMSENSOR_XOFFSET + (otp[1][0] & 0x0F);
> +	if ((priv->fw_ver[1] > 2) || (priv->fw_ver[1] == 2 && priv->fw_ver[2] > 0x33))
> +		num_y_electrode = SS4_NUMSENSOR_YOFFSET + ((otp[1][0] >> 4) & 0x07);
> +	else
> +		num_y_electrode = ((otp[1][0] >> 4) & 0x0F);
> +
> +	priv->x_max = (num_x_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
> +	priv->y_max = (num_y_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
> +
> +	return 0;
> +}
> +
> +int alps_update_btn_info_ss4(unsigned char otp[][4], struct alps_data *priv)
> +{
> +
> +	unsigned char is_btnless = 0;
> +
> +	is_btnless = (otp[1][1] >> 3) &  0x01;
> +
> +	if (is_btnless) {
> +		priv->flags |= ALPS_BTNLESS;
> +	}
> +	
> +	return 0;
> +}
> +
> +static int alps_set_defaults_ss4(struct psmouse *psmouse, struct alps_data *priv)
> +{
> +	unsigned char otp[2][4];
> +
> +	memset(otp, 0, sizeof(otp));
> +
> +	if (alps_get_otp_values_ss4(psmouse, 0, &otp[0][0]) ||
> +	    alps_get_otp_values_ss4(psmouse, 1, &otp[1][0]))
> +		return -1;
> +
> +	if (priv->fw_ver[1] >= 2) {
> +		alps_update_device_area_ss4_v2(otp, priv);
> +	}
> +	else {
> +		alps_update_device_area_ss4_v1(otp, priv);
> +	}
> +
> +	alps_update_btn_info_ss4(otp, priv);
> +
> +	return 0;
> +}
> +
>   static int alps_dolphin_get_device_area(struct psmouse *psmouse,
>   					struct alps_data *priv)
>   {
> @@ -1859,6 +2956,61 @@ static int alps_hw_init_dolphin_v1(struc
>   	return 0;
>   }
>
> +static int alps_hw_init_ss3(struct psmouse *psmouse)
> +{
> +	struct ps2dev *ps2dev 	= &psmouse->ps2dev;
> +	unsigned char 	f3param0 = 0x00,
> +					f3param1 = 0x00;
> +	
> +	if (alps_enter_command_mode(psmouse))
> +        goto error;
> +	
> +	/* Set to 2 pt-mode */
> +	f3param0 = 0x50;
> +	f3param1 = 0x3c;
> +	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
> +	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
> +	    ps2_command(ps2dev, &f3param0, PSMOUSE_CMD_SETRATE) ||
> +	    ps2_command(ps2dev, &f3param1, PSMOUSE_CMD_SETRATE))
> +		goto error;
> +
> +	/* output APDATA when 1 finger is in resting area */
> +	f3param0 = 0xc8;
> +	f3param1 = 0x28;
> +	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
> +	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
> +	    ps2_command(ps2dev, &f3param0, PSMOUSE_CMD_SETRATE) ||
> +	    ps2_command(ps2dev, &f3param1, PSMOUSE_CMD_SETRATE))
> +			goto error;
> +
> +	return 0;
> +	
> +error:
> +	return -1;
> +}
> +
> +
> +static int alps_hw_init_ss4(struct psmouse *psmouse)
> +{
> +	struct ps2dev *ps2dev = &psmouse->ps2dev;
> +	char param[2] = {0x64, 0x28};
> +	int ret = -1;
> +
> +	/* enter absolute mode */
> +	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
> +	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
> +	    ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
> +	    ps2_command(ps2dev, &param[1], PSMOUSE_CMD_SETRATE)) {
> +		goto error;
> +	}
> +
> +	return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
> +
> +error:	
> +	alps_exit_command_mode(psmouse);
> +	return ret;
> +}
> +
>   static void alps_set_defaults(struct alps_data *priv)
>   {
>   	priv->byte0 = 0x8f;
> @@ -1878,6 +3030,7 @@ static void alps_set_defaults(struct alp
>   		priv->set_abs_params = alps_set_abs_params_st;
>   		priv->x_max = 1023;
>   		priv->y_max = 767;
> +		priv->slot_number = 1;
>   		break;
>   	case ALPS_PROTO_V3:
>   		priv->hw_init = alps_hw_init_v3;
> @@ -1886,6 +3039,7 @@ static void alps_set_defaults(struct alp
>   		priv->decode_fields = alps_decode_pinnacle;
>   		priv->nibble_commands = alps_v3_nibble_commands;
>   		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
> +		priv->slot_number = 2;
>   		break;
>   	case ALPS_PROTO_V4:
>   		priv->hw_init = alps_hw_init_v4;
> @@ -1893,6 +3047,7 @@ static void alps_set_defaults(struct alp
>   		priv->set_abs_params = alps_set_abs_params_mt;
>   		priv->nibble_commands = alps_v4_nibble_commands;
>   		priv->addr_command = PSMOUSE_CMD_DISABLE;
> +		priv->slot_number = 2;
>   		break;
>   	case ALPS_PROTO_V5:
>   		priv->hw_init = alps_hw_init_dolphin_v1;
> @@ -1908,6 +3063,7 @@ static void alps_set_defaults(struct alp
>   		priv->y_max = 660;
>   		priv->x_bits = 23;
>   		priv->y_bits = 12;
> +		priv->slot_number = 2;
>   		break;
>   	case ALPS_PROTO_V6:
>   		priv->hw_init = alps_hw_init_v6;
> @@ -1916,6 +3072,58 @@ static void alps_set_defaults(struct alp
>   		priv->nibble_commands = alps_v6_nibble_commands;
>   		priv->x_max = 2047;
>   		priv->y_max = 1535;
> +		priv->slot_number = 1;
> +		break;
> +	case ALPS_PROTO_V7:
> +		priv->hw_init = alps_hw_init_v7;
> +		priv->process_packet = alps_process_packet_v7;
> +		priv->decode_fields = alps_decode_packet_v7;
> +		priv->set_abs_params = alps_set_abs_params_mt;
> +		priv->nibble_commands = alps_v3_nibble_commands;
> +		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
> +		priv->x_max = 0xfff;
> +		priv->y_max = 0x7ff;
> +		priv->byte0 = 0x48;
> +		priv->mask0 = 0x48;
> +
> +		if (priv->fw_ver[1] == 0xBA) {
> +			priv->flags = 0;
> +			priv->resting_zone_y_min = priv->y_max;	/* No resting finger area */
> +		}
> +		else {
> +			priv->flags = ALPS_BTNLESS;
> +			priv->resting_zone_y_min = 0x654;
> +		}
> +
> +		priv->slot_number = 2;
> +		break;
> +	case ALPS_PROTO_V8:
> +		if (priv->fw_ver[1] >= 2) {
> +			priv->decode_fields = alps_decode_ss4_v2;
> +		} else {
> +			priv->decode_fields = alps_decode_ss4_v1;
> +		}
> +		priv->hw_init = alps_hw_init_ss4;
> +		priv->process_packet = alps_process_packet_ss4;
> +		priv->set_abs_params = alps_set_abs_params_mt;
> +		priv->nibble_commands = alps_v3_nibble_commands;
> +		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
> +		priv->x_max = 0xfff;
> +		priv->y_max = 0x7ff;
> +		priv->byte0 = 0x18;
> +		priv->mask0 = 0x18;
> +		priv->flags = 0;
> +		priv->slot_number = 4;
> +		break;
> +	case ALPS_PROTO_V9:
> +		priv->hw_init = alps_hw_init_ss3;
> +		priv->process_packet = alps_process_touchpad_packet_ss3;
> +		priv->decode_fields = alps_decode_dolphin;
> +		priv->set_abs_params = alps_set_abs_params_mt;
> +		priv->nibble_commands = alps_v3_nibble_commands;
> +		priv->byte0 = 0xc8;
> +		priv->mask0 = 0xc8;
> +		priv->slot_number = 2;		
>   		break;
>   	}
>   }
> @@ -1975,6 +3183,9 @@ static int alps_identify(struct psmouse
>   	    alps_exit_command_mode(psmouse))
>   		return -EIO;
>
> +	/* Save the Firmware version */
> +	memcpy(priv->fw_ver, ec, 3);
> +
>   	if (alps_match_table(psmouse, priv, e7, ec) == 0) {
>   		return 0;
>   	} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
> @@ -1985,6 +3196,11 @@ static int alps_identify(struct psmouse
>   			return -EIO;
>   		else
>   			return 0;
> +	} else if (ec[0] == 0x88 && (ec[1] & 0xf0) == 0xB0) {
> +		priv->proto_version = ALPS_PROTO_V7;
> +		alps_set_defaults(priv);
> +
> +		return 0;
>   	} else if (ec[0] == 0x88 && ec[1] == 0x08) {
>   		priv->proto_version = ALPS_PROTO_V3;
>   		alps_set_defaults(priv);
> @@ -2007,6 +3223,22 @@ static int alps_identify(struct psmouse
>   		alps_set_defaults(priv);
>
>   		return 0;
> +	} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x14) {
> +		priv->proto_version = ALPS_PROTO_V8;
> +		alps_set_defaults(priv);
> +
> +		if(alps_set_defaults_ss4(psmouse, priv))
> +			return -EIO;
> +		else
> +			return 0;
> +	} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0xc8){
> +		priv->proto_version = ALPS_PROTO_V9;
> +		alps_set_defaults(priv);
> +		
> +		if (alps_dolphin_get_device_area(psmouse, priv))
> +			return -EIO;
> +		else
> +			return 0;
>   	}
>
>   	psmouse_info(psmouse,
> @@ -2048,7 +3280,7 @@ static void alps_set_abs_params_mt(struc
>   				   struct input_dev *dev1)
>   {
>   	set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
> -	input_mt_init_slots(dev1, 2, 0);
> +	input_mt_init_slots(dev1, priv->slot_number, 0);
>   	input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
>   	input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
>
> @@ -2164,16 +3396,17 @@ init_fail:
>   int alps_detect(struct psmouse *psmouse, bool set_properties)
>   {
>   	struct alps_data dummy;
> -
> -	if (alps_identify(psmouse, &dummy) < 0)
> -		return -1;
> -
> +	
> +    if (alps_identify(psmouse, &dummy) < 0)
> +        return -1;
> +
>   	if (set_properties) {
>   		psmouse->vendor = "ALPS";
>   		psmouse->name = dummy.flags & ALPS_DUALPOINT ?
>   				"DualPoint TouchPad" : "GlidePoint";
>   		psmouse->model = dummy.proto_version << 8;
>   	}
> +	
>   	return 0;
>   }
>
> --- linux-3.14-rc1/drivers/input/mouse/alps.h	2015-02-08 12:39:28.367799685 +0100
> +++ goliad-mlk-14_A03.fish.tar.gz/debs/touchpad-alps-trusty-dkms_20140325_all.deb/usr/src/touchpad-alps-trusty-20140325/alps.h	2014-06-06 04:21:48.000000000 +0200
> @@ -18,11 +18,140 @@
>   #define ALPS_PROTO_V4	4
>   #define ALPS_PROTO_V5	5
>   #define ALPS_PROTO_V6	6
> +#define ALPS_PROTO_V7	7	/* t3btl t4s */
> +#define ALPS_PROTO_V8	8	/* ss4 */
> +#define ALPS_PROTO_V9	9	/* ss3btl */
> +
> +
> +#define MAX_IMG_PT_NUM		5
> +#define V7_IMG_PT_NUM		2
> +
> +#define ZONE_NORMAL				0x01
> +#define ZONE_RESTING			0x02
> +#define ZONE_LEFT_BTN			0x04
> +#define ZONE_RIGHT_BTN			0x08
>
>   #define DOLPHIN_COUNT_PER_ELECTRODE	64
>   #define DOLPHIN_PROFILE_XOFFSET		8	/* x-electrode offset */
>   #define DOLPHIN_PROFILE_YOFFSET		1	/* y-electrode offset */
>
> +/*
> + * enum V7_PACKET_ID - defines the packet type for V7
> + * V7_PACKET_ID_IDLE: There's no finger and no button activity.
> + * V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad
> + *  or there's button activities.
> + * V7_PACKET_ID_MULTI: There are at least three non-resting fingers.
> + * V7_PACKET_ID_NEW: The finger position in slot is not continues from
> + *  previous packet.
> +*/
> +enum V7_PACKET_ID {
> +	 V7_PACKET_ID_IDLE,
> +	 V7_PACKET_ID_TWO,
> +	 V7_PACKET_ID_MULTI,
> +	 V7_PACKET_ID_NEW,
> +	 V7_PACKET_ID_UNKNOWN,
> +};
> +
> +enum SS4_PACKET_ID {
> +	SS4_PACKET_ID_IDLE = 0,
> +	SS4_PACKET_ID_ONE,
> +	SS4_PACKET_ID_TWO,
> +	SS4_PACKET_ID_MULTI,
> +};
> +
> +#define SS4_COUNT_PER_ELECTRODE			256
> +#define SS4_NUMSENSOR_XOFFSET				7
> +#define SS4_NUMSENSOR_YOFFSET				7
> +#define SS4_BUTTONLESS_BUTTONS	0x01			// SWC status			( 0:SW OFF,	  1: SW ON	)
> +
> +#define SS4_MASK_NORMAL_BUTTONS	0x07
> +
> +#define SS4_1F_X_V1(_b)			( ( ( _b[ 0 ] << 5 ) & 0x1E00 ) | \
> +								  ( ( _b[ 0 ] << 6 ) & 0x01C0 ) | \
> +								  ( ( _b[ 1 ] >> 2 ) & 0x0038 ) | \
> +								  ( ( _b[ 1 ] >> 1 ) & 0x0007 ) \
> +								)
> +#define SS4_1F_Y_V1(_b)			( ( ( _b[ 2 ] >> 1 ) & 0x007F ) | \
> +								  ( ( _b[ 4 ] << 5 ) & 0x0F80 ) \
> +								)
> +#define SS4_1F_Z_V1(_b)			( _b[ 5 ] & 0xFF )
> +
> +#define	COSMO_1F_LFB(_b)		( ( _b[ 4 ] >> 1 ) & 0x01 )
> +
> +#define SS4_BTN_V1(_b)		( _b[ 3 ] & SS4_BUTTONLESS_BUTTONS )
> +
> +#define SS4_STD_MF_X_V1(_b,_i)	( ( ( _b[ 0 + _i * 3 ] >> 2 ) & 0x0020 ) | \
> +								  ( ( _b[ 1 + _i * 3 ] << 5 ) & 0x1FC0 ) \
> +								)
> +
> +#define SS4_STD_MF_Y_V1(_b,_i)	( ( ( _b[ 0 + _i * 3 ] >> 2 ) & 0x0010 ) | \
> +								  ( ( _b[ 2 + _i * 3 ] << 4 ) & 0x0FE0 ) \
> +								)
> +
> +#define SS4_BTL_BTN_V1(_b)		( _b[ 3 ] &SS4_BUTTONLESS_BUTTONS )
> +
> +#define SS4_BTL_MF_X_V1(_b,_i) 	( SS4_STD_MF_X_V1(_b,_i) | ( ( _b[ 0 + _i * 3 ] << 2 ) & 0x0010 ) )
> +
> +#define SS4_BTL_MF_Y_V1(_b,_i)	 ( SS4_STD_MF_Y_V1(_b,_i) | ( ( _b[ 0 + _i * 3 ] << 2 ) & 0x0008 ) )
> +
> +#define SS4_MF_Z_V1(_b,_i)		( ( _b[ 0 + _i * 3 ] >> 4 ) & 0x0003 )
> +
> +#define SS4_IS_MF_CONTINUE_V1(_b)	( ( _b[ 0 ] & 0x01 ) == 0x01 )
> +#define SS4_IS_5F_DETECTED_V1(_b)	( ( _b[ 0 ] & 0x01 ) == 0x01 )
> +
> +#define SS4_1F_X_V2(_b)			( (   _b[ 0 ]        & 0x0007 ) | \
> +								  ( ( _b[ 1 ] << 3 ) & 0x0078 ) | \
> +								  ( ( _b[ 1 ] << 2 ) & 0x0380 ) | \
> +								  ( ( _b[ 2 ] << 5 ) & 0x1C00 ) \
> +								)
> +
> +#define SS4_1F_Y_V2(_b)			( ( ( _b[ 2 ]      ) & 0x000F ) | \
> +								  ( ( _b[ 3 ] >> 2 ) & 0x0030 ) | \
> +								  ( ( _b[ 4 ] << 6 ) & 0x03C0 ) | \
> +								  ( ( _b[ 4 ] << 5 ) & 0x0C00 ) \
> +								)
> +
> +#define SS4_1F_Z_V2(_b)			( ( ( _b[ 5 ]      ) & 0x0F   ) | \
> +	                              			( ( _b[ 5 ] >> 1 ) & 0x70   ) | \
> +								  ( ( _b[ 4 ]      ) & 0x80   ) \
> +                                				)
> +
> +#define	COSMO_1F_LFB_V2(_b)	( ( ( _b[ 2 ] >> 4 ) & 0x01 ) == 0x01 )
> +
> +#define	COSMO_MF_LF_V2(_b,_i)	( ( _b[ 1 + _i * 3 ] & 0x0004 ) == 0x0004 )
> +
> +#define SS4_BTN_V2(_b)		( (_b[ 0 ] >> 5 ) & SS4_MASK_NORMAL_BUTTONS )
> +#define SS4_STD_MF_X_V2(_b,_i)	( ( ( _b[ 0 + _i * 3 ]  << 5  ) & 0x00E0 ) | \
> +								      ( ( _b[ 1 + _i * 3 ]  << 5  ) & 0x1F00 ) \
> +								    )
> +
> +#define SS4_STD_MF_Y_V2(_b,_i)	( ( ( _b[ 1 + _i * 3 ] << 3 ) & 0x0010 ) | \
> +								      ( ( _b[ 2 + _i * 3 ] << 5 ) & 0x01E0 ) | \
> +								      ( ( _b[ 2 + _i * 3 ] << 4 ) & 0x0E00 ) \
> +								    )
> +
> +#define SS4_BTL_MF_X_V2(_b,_i)	( SS4_STD_MF_X_V2(_b,_i) | ( ( _b[ 0 + _i * 3 ] >> 3 ) & 0x0010 ) )
> +
> +#define SS4_BTL_MF_Y_V2(_b,_i)	( SS4_STD_MF_Y_V2(_b,_i) | ( ( _b[ 0 + _i * 3 ] >> 3 ) & 0x0008 ) )
> +
> +#define SS4_MF_Z_V2(_b,_i)		( ( ( _b[ 1 + _i * 3 ]      ) & 0x0001 ) | \
> +								  ( ( _b[ 1 + _i * 3 ] >> 1 ) & 0x0002 ) \
> +								)
> +								
> +#define SS4_IS_MF_CONTINUE_V2(_b)	( ( _b[ 2 ] & 0x10 ) == 0x10 )
> +#define SS4_IS_5F_DETECTED_V2(_b)	( ( _b[ 2 ] & 0x10 ) == 0x10 )
> +
> +
> +#define SS4_MFPACKET_NO_AX				8160		// 8192-32 = 8160  Standard X-Coordinate value at no-finger
> +#define SS4_MFPACKET_NO_AY				4080		// 4096-16 = 4080  Standard Y-Coordinate value at no-finger
> +#define SS4_MFPACKET_NO_AX_BL			8176		// 8192-16 = 8176  Buttonless X-Coordinate value at no-finger
> +#define SS4_MFPACKET_NO_AY_BL			4088		// 4096-8  = 4088  Buttonless Y-Coordinate value at no-finger
> +
> +#define RESTING_FN_LARGE_MOVEMENT		50			// Threshold for resting finger's large movement
> +#define PT_IN_LEFT_BTN_AREA(_x,_y,_x_max,_y_max)	( ((_x)<(_x_max)/2)&&((_y)>(_y_max)*4/5) )
> +#define PT_IN_RIGHT_BTN_AREA(_x,_y,_x_max,_y_max)	( ((_x)>=(_x_max)/2)&&((_y)>(_y_max)*4/5) )
> +#define PT_IN_BTN_AREA(_x,_y,_x_max,_y_max)			( PT_IN_LEFT_BTN_AREA(_x,_y,_x_max,_y_max) || PT_IN_RIGHT_BTN_AREA(_x,_y,_x_max,_y_max) )
> +
>   /**
>    * struct alps_model_info - touchpad ID table
>    * @signature: E7 response string to match.
> @@ -46,7 +175,7 @@ struct alps_model_info {
>   	unsigned char command_mode_resp;
>   	unsigned char proto_version;
>   	unsigned char byte0, mask0;
> -	unsigned char flags;
> +	unsigned int flags;
>   };
>
>   /**
> @@ -66,15 +195,7 @@ struct alps_nibble_commands {
>   };
>
>   /**
> - * struct alps_fields - decoded version of the report packet
> - * @x_map: Bitmap of active X positions for MT.
> - * @y_map: Bitmap of active Y positions for MT.
> - * @fingers: Number of fingers for MT.
> - * @x: X position for ST.
> - * @y: Y position for ST.
> - * @z: Z position for ST.
> - * @first_mp: Packet is the first of a multi-packet report.
> - * @is_mp: Packet is part of a multi-packet report.
> + * struct alps_btn - decoded version of the button status
>    * @left: Left touchpad button is active.
>    * @right: Right touchpad button is active.
>    * @middle: Middle touchpad button is active.
> @@ -82,16 +203,7 @@ struct alps_nibble_commands {
>    * @ts_right: Right trackstick button is active.
>    * @ts_middle: Middle trackstick button is active.
>    */
> -struct alps_fields {
> -	unsigned int x_map;
> -	unsigned int y_map;
> -	unsigned int fingers;
> -	unsigned int x;
> -	unsigned int y;
> -	unsigned int z;
> -	unsigned int first_mp:1;
> -	unsigned int is_mp:1;
> -
> +struct alps_btn {
>   	unsigned int left:1;
>   	unsigned int right:1;
>   	unsigned int middle:1;
> @@ -102,6 +214,79 @@ struct alps_fields {
>   };
>
>   /**
> + * struct alps_btn - decoded version of the X Y Z postion for ST.
> + * @x: X position for ST.
> + * @y: Y position for ST.
> + * @z: Z position for ST.
> + */
> +struct alps_abs_data {
> +	unsigned int x;
> +	unsigned int y;
> +	unsigned int z;
> +};
> +
> +typedef enum _DOL_PACKET_TYPE
> +{
> +	DOL_UNKNOWN,
> +	DOL_GPDATA,
> +	DOL_PROFDATA,
> +	DOL_APDATA
> +}DOL_PACKET_TYPE;
> +
> +/**
> + * struct alps_fields - decoded version of the report packet
> + * @fingers: Number of fingers for MT.
> + * @pt: X Y Z postion for ST.
> + * @pt: X Y Z postion for image MT.
> + * @x_map: Bitmap of active X positions for MT.
> + * @y_map: Bitmap of active Y positions for MT.
> + * @first_mp: Packet is the first of a multi-packet report.
> + * @is_mp: Packet is part of a multi-packet report.
> + * @btn: Button activity status
> + */
> +struct alps_fields {
> +	unsigned int fingers;
> +	struct alps_abs_data pt;
> +	struct alps_abs_data pt_img[MAX_IMG_PT_NUM];
> +	unsigned int x_map;
> +	unsigned int y_map;
> +	unsigned int large_fn;
> +	unsigned int first_mp:1;
> +	unsigned int is_mp:1;
> +	DOL_PACKET_TYPE	dol_packet_type;
> +	struct alps_btn btn;
> +};
> +
> +/**
> + * struct v7_raw - data decoded from raw packet for V7.
> + * @pkt_id: An id that specifies the type of packet.
> + * @additional_fingers: Number of additional finger that is neighter included
> + *  in pt slot nor reflected in rest_left and rest_right flag of data packet.
> + * @rest_left: There are fingers on left resting zone.
> + * @rest_right: There are fingers on right resting zone.
> + */
> +struct v7_raw {
> +	unsigned char pkt_id;
> +	unsigned int additional_fingers;
> +	unsigned char rest_left;
> +	unsigned char rest_right;
> +};
> +
> +/**
> + * struct alps_bl_pt_attr - generic attributes of touch points for buttonless device
> + * @zone: The part of touchpad that the touch point locates
> + * @is_counted: The touch point is not a resting finger.
> + * @is_init_pt_got: The touch down point is got.
> + * @init_pt: The X Y Z position of the touch down point.
> + */
> +struct alps_bl_pt_attr {
> +	unsigned char zone;
> +	unsigned char is_counted;
> +	unsigned char is_init_pt_got;
> +	struct alps_abs_data init_pt;
> +};
> +
> +/**
>    * struct alps_data - private data structure for the ALPS driver
>    * @dev2: "Relative" device used to report trackstick or mouse activity.
>    * @phys: Physical path for the relative device.
> @@ -116,8 +301,10 @@ struct alps_fields {
>    * @flags: Additional device capabilities (passthrough port, trackstick, etc.).
>    * @x_max: Largest possible X position value.
>    * @y_max: Largest possible Y position value.
> + * @resting_zone_y_min: Smallest Y postion value of the bottom resting zone.
>    * @x_bits: Number of X bits in the MT bitmap.
>    * @y_bits: Number of Y bits in the MT bitmap.
> + * @img_fingers: Number of image fingers.
>    * @hw_init: Protocol-specific hardware init function.
>    * @process_packet: Protocol-specific function to process a report packet.
>    * @decode_fields: Protocol-specific function to read packet bitfields.
> @@ -132,6 +319,11 @@ struct alps_fields {
>    * @fingers: Number of fingers from last MT report.
>    * @quirks: Bitmap of ALPS_QUIRK_*.
>    * @timer: Timer for flushing out the final report packet in the stream.
> + * @v7: Data decoded from raw packet for V7
> + * @phy_btn: Physical button is active.
> + * @prev_phy_btn: Physical button of previous packet is active.
> + * @pressed_btn_bits: Pressed positon of button zone
> + * @pt_attr: Generic attributes of touch points for buttonless device.
>    */
>   struct alps_data {
>   	struct input_dev *dev2;
> @@ -142,11 +334,14 @@ struct alps_data {
>   	int addr_command;
>   	unsigned char proto_version;
>   	unsigned char byte0, mask0;
> -	unsigned char flags;
> +	unsigned int flags;
> +	unsigned char fw_ver[3];
>   	int x_max;
>   	int y_max;
> +	int resting_zone_y_min;
>   	int x_bits;
>   	int y_bits;
> +	unsigned char slot_number;
>
>   	int (*hw_init)(struct psmouse *psmouse);
>   	void (*process_packet)(struct psmouse *psmouse);
> @@ -161,6 +356,13 @@ struct alps_data {
>   	int fingers;
>   	u8 quirks;
>   	struct timer_list timer;
> +
> +	/* these are used for buttonless touchpad*/
> +	union {
> +		struct v7_raw v7;
> +	} r;
> +	struct alps_btn prev_btn;
> +	struct alps_bl_pt_attr pt_attr[MAX_IMG_PT_NUM];
>   };
>
>   #define ALPS_QUIRK_TRACKSTICK_BUTTONS	1 /* trakcstick buttons in trackstick packet */
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Pali Rohár Feb. 15, 2015, 4:38 p.m. UTC | #2
On Wednesday 11 February 2015 08:38:58 Hans de Goede wrote:
> Thanks for this and the other related mail. I also have an
> alps.c directly from alps themselves which adds support for
> v8 and v9 protocols. The problem is that like with v7 the
> code is not really in a state where we want to take it as is,
> e.g. it does clickpad softbutton emulation in the kernel
> instead of leaving it to userspace.
> 
> I spend a considerable amount of time cleaning up the alps
> code itself and then adding a cleaner version of the v7 code,
> we should do the same for the v8 and v9 code probably but
> that requires someone with hardware access so that we can
> actually test the end result.
> 
> Regards,
> 
> Hans

Ok. Hans, can you look at least on second one patch?
http://www.spinics.net/lists/linux-input/msg36554.html

This is a bit shorter patch and maybe easier to review and decide 
if is needed or not...
Hans de Goede Feb. 15, 2015, 4:40 p.m. UTC | #3
Hi,

On 15-02-15 17:38, Pali Rohár wrote:
> On Wednesday 11 February 2015 08:38:58 Hans de Goede wrote:
>> Thanks for this and the other related mail. I also have an
>> alps.c directly from alps themselves which adds support for
>> v8 and v9 protocols. The problem is that like with v7 the
>> code is not really in a state where we want to take it as is,
>> e.g. it does clickpad softbutton emulation in the kernel
>> instead of leaving it to userspace.
>>
>> I spend a considerable amount of time cleaning up the alps
>> code itself and then adding a cleaner version of the v7 code,
>> we should do the same for the v8 and v9 code probably but
>> that requires someone with hardware access so that we can
>> actually test the end result.
>>
>> Regards,
>>
>> Hans
>
> Ok. Hans, can you look at least on second one patch?
> http://www.spinics.net/lists/linux-input/msg36554.html
>
> This is a bit shorter patch and maybe easier to review and decide
> if is needed or not...

I think you no that particular bit of the code better then me know,
given the recent fixes you've been doing there. I think it is best
to not take that patch / hack until we actually have reports from
someone with a machine where such a fix is needed.

Regards,

Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

--- linux-3.14-rc1/drivers/input/mouse/alps.c	2015-02-08 12:38:06.507282067 +0100
+++ goliad-mlk-14_A03.fish.tar.gz/debs/touchpad-alps-trusty-dkms_20140325_all.deb/usr/src/touchpad-alps-trusty-20140325/alps.c	2014-06-06 04:21:48.000000000 +0200
@@ -20,6 +20,7 @@ 
 #include <linux/input/mt.h>
 #include <linux/serio.h>
 #include <linux/libps2.h>
+#include <linux/kernel.h>
 
 #include "psmouse.h"
 #include "alps.h"
@@ -32,6 +33,11 @@ 
 #define ALPS_REG_BASE_RUSHMORE	0xc2c0
 #define ALPS_REG_BASE_PINNACLE	0x0000
 
+#define LEFT_BUTTON_BIT			0x01
+#define RIGHT_BUTTON_BIT		0x02
+
+#define V7_LARGE_MOVEMENT		130
+
 static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
 	{ PSMOUSE_CMD_SETPOLL,		0x00 }, /* 0 */
 	{ PSMOUSE_CMD_RESET_DIS,	0x00 }, /* 1 */
@@ -99,6 +105,10 @@  static const struct alps_nibble_commands
 #define ALPS_FOUR_BUTTONS	0x40	/* 4 direction button present */
 #define ALPS_PS2_INTERLEAVED	0x80	/* 3-byte PS/2 packet interleaved with
 					   6-byte ALPS packet */
+#define ALPS_BTNLESS			0x100	/* ALPS ClickPad flag */
+
+#define	DOL_IS_APDATA(_BY)			((_BY[0]&0x01)==0x01)
+#define	DOL_IS_PROFDATA(_BY)		((_BY[0]&0x20)==0x20)
 
 static const struct alps_model_info alps_model_data[] = {
 	{ { 0x32, 0x02, 0x14 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },	/* Toshiba Salellite Pro M10 */
@@ -140,6 +150,20 @@  static void alps_set_abs_params_mt(struc
  * isn't valid per PS/2 spec.
  */
 
+static unsigned int alps_pt_distance(struct alps_abs_data *pt0,
+				    struct alps_abs_data *pt1)
+{
+	int vect_x, vect_y;
+
+	if (!pt0 || !pt1)
+		return 0;
+
+	vect_x = pt0->x - pt1->x;
+	vect_y = pt0->y - pt1->y;
+
+	return int_sqrt(vect_x * vect_x + vect_y * vect_y);
+}
+
 /* Packet formats are described in Documentation/input/alps.txt */
 
 static bool alps_is_valid_first_byte(struct alps_data *priv,
@@ -296,8 +320,8 @@  static void alps_process_bitmap_dolphin(
 	y_map = fields->y_map;
 
 	if (!x_map || !y_map)
-		return;
-
+        return;
+	
 	/* Get Most-significant and Least-significant bit */
 	x_msb = fls(x_map);
 	x_lsb = ffs(x_map);
@@ -306,8 +330,8 @@  static void alps_process_bitmap_dolphin(
 
 	/* Most-significant bit should never exceed max sensor line number */
 	if (x_msb > priv->x_bits || y_msb > priv->y_bits)
-		return;
-
+        return;
+	
 	*x1 = *y1 = *x2 = *y2 = 0;
 
 	if (fields->fingers > 1) {
@@ -320,11 +344,11 @@  static void alps_process_bitmap_dolphin(
 		end_bit = y_msb - 1;
 		box_middle_y = (priv->y_max * (start_bit + end_bit)) /
 				(2 * (priv->y_bits - 1));
-		*x1 = fields->x;
-		*y1 = fields->y;
+		*x1 = fields->pt.x;
+		*y1 = fields->pt.y;
 		*x2 = 2 * box_middle_x - *x1;
 		*y2 = 2 * box_middle_y - *y1;
-	}
+	}	
 }
 
 /*
@@ -461,6 +485,55 @@  static void alps_report_semi_mt_data(str
 	alps_set_slot(dev, 1, num_fingers == 2, x2, y2);
 }
 
+static void alps_report_semi_mt_data_ex(struct input_dev *dev, int num_fingers,
+				     struct alps_abs_data coord[])
+{
+	unsigned char i;
+
+	for (i = 0; i < num_fingers; i++) {
+		if (!coord[i].x || !coord[i].y || !coord[i].z) {
+			alps_set_slot(dev, i, 0, coord[i].x, coord[i].y);
+		}
+		else {
+			alps_set_slot(dev, i, 1, coord[i].x, coord[i].y);
+		}
+	}
+}
+
+static void alps_report_coord_and_btn(struct psmouse *psmouse,
+				      struct alps_fields *f)
+{
+	struct input_dev *dev;
+
+	if (!psmouse || !f)
+		return;
+
+	dev = psmouse->dev;
+
+	if (f->fingers) {
+		input_report_key(dev, BTN_TOUCH, 1);
+
+		alps_report_semi_mt_data(dev, f->fingers,
+			f->pt_img[0].x, f->pt_img[0].y,
+			f->pt_img[1].x, f->pt_img[1].y);
+		input_mt_report_finger_count(dev, f->fingers);
+
+		input_report_abs(dev, ABS_X, f->pt_img[0].x);
+		input_report_abs(dev, ABS_Y, f->pt_img[0].y);
+		input_report_abs(dev, ABS_PRESSURE, f->pt_img[0].z);
+	} else {
+		input_report_key(dev, BTN_TOUCH, 0);
+		input_mt_report_finger_count(dev, 0);
+		input_report_abs(dev, ABS_PRESSURE, 0);
+	}
+
+	input_report_key(dev, BTN_LEFT, f->btn.left);
+	input_report_key(dev, BTN_RIGHT, f->btn.right);
+	input_report_key(dev, BTN_MIDDLE, f->btn.middle);
+
+	input_sync(dev);
+}
+
 static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
 {
 	struct alps_data *priv = psmouse->private;
@@ -523,13 +596,22 @@  static void alps_process_trackstick_pack
 
 static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p)
 {
-	f->left = !!(p[3] & 0x01);
-	f->right = !!(p[3] & 0x02);
-	f->middle = !!(p[3] & 0x04);
-
-	f->ts_left = !!(p[3] & 0x10);
-	f->ts_right = !!(p[3] & 0x20);
-	f->ts_middle = !!(p[3] & 0x40);
+	f->btn.left = !!(p[3] & 0x01);
+	f->btn.right = !!(p[3] & 0x02);
+	f->btn.middle = !!(p[3] & 0x04);
+
+	f->btn.ts_left = !!(p[3] & 0x10);
+	f->btn.ts_right = !!(p[3] & 0x20);
+	f->btn.ts_middle = !!(p[3] & 0x40);
+}
+
+/* proto_v9 */
+static void alps_decode_button_ss3(struct alps_fields *f, unsigned char *p,
+				struct alps_data *priv)
+{
+	if(f->dol_packet_type == DOL_GPDATA || 
+	   f->dol_packet_type == DOL_APDATA)
+		f->btn.left = !!(p[3]&0x01);
 }
 
 static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
@@ -546,10 +628,10 @@  static void alps_decode_pinnacle(struct
 		   ((p[2] & 0x7f) << 1) |
 		   (p[4] & 0x01);
 
-	f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
+	f->pt.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
 	       ((p[0] & 0x30) >> 4);
-	f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
-	f->z = p[5] & 0x7f;
+	f->pt.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
+	f->pt.z = p[5] & 0x7f;
 
 	alps_decode_buttons_v3(f, p);
 }
@@ -559,7 +641,7 @@  static void alps_decode_rushmore(struct
 {
 	alps_decode_pinnacle(f, p, psmouse);
 
-	/* Rushmore's packet decode has a bit difference with Pinnacle's */
+	/* rushmore's packet decode has a bit difference with pinnacle's */
 	f->is_mp = !!(p[5] & 0x40);
 	f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1;
 	f->x_map |= (p[5] & 0x10) << 11;
@@ -572,18 +654,14 @@  static void alps_decode_dolphin(struct a
 	u64 palm_data = 0;
 	struct alps_data *priv = psmouse->private;
 
-	f->first_mp = !!(p[0] & 0x02);
-	f->is_mp = !!(p[0] & 0x20);
-
-	if (!f->is_mp) {
-		f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
-		f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
-		f->z = (p[0] & 4) ? 0 : p[5] & 0x7f;
-		alps_decode_buttons_v3(f, p);
-	} else {
+	f->is_mp = 0;
+	f->first_mp = 0;
+	
+	if ( DOL_IS_PROFDATA(p) ){
+		f->is_mp = 1;
+		f->dol_packet_type = DOL_PROFDATA;
 		f->fingers = ((p[0] & 0x6) >> 1 |
 		     (p[0] & 0x10) >> 2);
-
 		palm_data = (p[1] & 0x7f) |
 			    ((p[2] & 0x7f) << 7) |
 			    ((p[4] & 0x7f) << 14) |
@@ -598,6 +676,400 @@  static void alps_decode_dolphin(struct a
 		/* X-profile is stored in p(n) to p(n+m-1), m = x_bits; */
 		f->x_map = (palm_data >> priv->y_bits) &
 			   (BIT(priv->x_bits) - 1);
+	}else{
+		if ( DOL_IS_APDATA(p) ){
+			f->dol_packet_type = DOL_APDATA;
+			f->fingers = 2;
+			f->pt_img[0].x = p[1]<<3;
+			f->pt_img[0].y = p[2]<<2;
+			f->pt_img[0].z = 64;
+			f->pt_img[1].x = p[4]<<3;
+			f->pt_img[1].y = p[5]<<2;
+			f->pt_img[1].z = 64;
+		}else{// is gp data
+			f->dol_packet_type = DOL_GPDATA;
+			f->first_mp = !!(p[0]&0x02);
+			f->pt.x = f->pt_img[0].x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
+			f->pt.y = f->pt_img[0].y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
+			f->pt.z = f->pt_img[0].z = (p[0] & 4) ? 0 : p[5] & 0x7f;
+			/*
+				Button number will be included in the PROFILE data for a 3-f 
+				packet. So do not change .fingers because it will be updated
+				in Profile data packet.
+			*/
+			if(!f->first_mp)
+				f->fingers = 
+					(f->pt_img[0].x && f->pt_img[0].y && f->pt_img[0].z)?1:0;
+		}
+		
+		if(priv->proto_version == ALPS_PROTO_V9){
+			alps_decode_button_ss3(f, p, priv);
+		}else{
+			alps_decode_buttons_v3(f, p);
+		}	
+	}
+}
+
+unsigned char alps_get_pkt_id_ss4_v1(char *byte)
+{
+	unsigned char pkt_id = SS4_PACKET_ID_IDLE;
+
+	if (((byte[0] & 0xFF) == 0x08) && ((byte[1] & 0xFF) == 0x10) &&
+	     ((byte[2] & 0xFF) == 0x00) && ((byte[3] & 0x8F) == 0x08) &&
+	     ((byte[4] & 0xFF) == 0x01) && ((byte[5] & 0xFF) == 0x00)) {
+		pkt_id = SS4_PACKET_ID_IDLE;
+	}
+	else if (((byte[0] & 0x08) == 0x08) && ((byte[1] & 0x10) == 0x10) &&
+		((byte[3] & 0x8E) == 0x08) && ((byte[4] & 0x81) == 0x01) ) {
+		pkt_id = SS4_PACKET_ID_ONE;
+	}
+	else if(((byte[0] & 0x08) == 0x08) && ((byte[3] & 0x08) == 0x08) &&
+		  ((byte[4] & 0x01) == 0x01) ){
+		if(((byte[5] & 0x01) == 0x01))
+			pkt_id = SS4_PACKET_ID_TWO;
+		else
+			pkt_id = SS4_PACKET_ID_MULTI;
+	}
+
+	return pkt_id;
+}
+
+unsigned char alps_get_pkt_id_ss4_v2(char *byte)
+{
+	unsigned char pkt_id = SS4_PACKET_ID_IDLE;
+
+	if (((byte[0] & 0xFF) == 0x18) && ((byte[1] & 0xFF) == 0x10) &&
+	     ((byte[2] & 0xFF) == 0x00) && ((byte[3] & 0x88) == 0x08) &&
+	     ((byte[4] & 0xFF) == 0x10) && ((byte[5] & 0xFF) == 0x00)) {
+		pkt_id = SS4_PACKET_ID_IDLE;
+	}
+	else if (!(byte[3] & 0x10)) {
+		pkt_id = SS4_PACKET_ID_ONE;
+	}
+	else {
+		if (!(byte[3] & 0x20)) {
+			pkt_id = SS4_PACKET_ID_TWO;
+		}
+		else {
+			pkt_id = SS4_PACKET_ID_MULTI;
+		}
+	}
+
+	return pkt_id;
+}
+
+static void alps_process_btnless_click(struct psmouse *psmouse, struct alps_fields *f)
+{
+	struct alps_data *priv = psmouse->private;
+	
+	if (!f->btn.left)
+		return;
+
+	// Clear button flag
+	f->btn.left = 0;
+
+	switch (f->fingers) {
+	case 1:
+		// In Left Resting Area
+		if (PT_IN_LEFT_BTN_AREA(f->pt_img[0].x, f->pt_img[0].y, priv->x_max, priv->y_max)) {
+			f->btn.left = 1;
+		}
+		// In Right Resting Area
+		else if (PT_IN_RIGHT_BTN_AREA(f->pt_img[0].x, f->pt_img[0].y, priv->x_max, priv->y_max)) {
+			f->btn.right = 1;
+		}
+		// In Normal area
+		else {
+			f->btn.left = 1;
+		}
+		break;
+
+	case 2:
+		// Both two fingers are in Normal area
+		if (!PT_IN_BTN_AREA(f->pt_img[0].x, f->pt_img[0].y, priv->x_max, priv->y_max) && 
+			!PT_IN_BTN_AREA(f->pt_img[1].x, f->pt_img[1].y, priv->x_max, priv->y_max)) {
+			f->btn.right = 1;
+		}
+		// Either one finger is in Right Area
+		else if (PT_IN_RIGHT_BTN_AREA(f->pt_img[0].x, f->pt_img[0].y, priv->x_max, priv->y_max) ||
+				 PT_IN_RIGHT_BTN_AREA(f->pt_img[1].x, f->pt_img[1].y, priv->x_max, priv->y_max)) {
+			f->btn.right = 1;
+		}
+		else {
+			f->btn.left = 1;
+		}
+		break;
+	
+	case 3:
+		f->btn.middle = 1;
+		break;
+
+	case 0:
+	default:
+		break;
+	}
+}
+
+static void alps_process_resting_finger(struct psmouse *psmouse, struct alps_fields *f, struct alps_abs_data *output, unsigned char *p_fn)
+{
+	struct alps_data *priv = psmouse->private;
+	static struct alps_abs_data prev_pt[10];
+	static struct alps_abs_data init_pt[10];
+	static unsigned char is_moved[10];
+	static unsigned char prev_fn = 0;
+	static unsigned char prev_coord_is_output[10];
+	unsigned char cur_coord_is_output[10];
+	unsigned char in_resting_area[10];
+	unsigned char i, index;
+	unsigned char output_fn = 0;
+
+	memset(in_resting_area, 0, sizeof(in_resting_area));
+	memset(cur_coord_is_output, 0, sizeof(cur_coord_is_output));
+
+	// Clear "is_moved" flag when finger number changed
+	if (f->fingers != prev_fn) {
+		memset(is_moved, 0, sizeof(is_moved));
+		memcpy(init_pt, f->pt_img, sizeof(f->pt_img));
+	}
+
+	// Calculate output finger
+	for (i = 0, index = 0; i < f->fingers; i++) {
+		if (is_moved[i] == 0 && (abs(f->pt_img[i].x - init_pt[i].x) > RESTING_FN_LARGE_MOVEMENT) ) {
+			is_moved[i] = 1;
+		}
+
+		// Check if in resting area
+		if (PT_IN_BTN_AREA(f->pt_img[i].x, f->pt_img[i].y, priv->x_max, priv->y_max)) {
+			in_resting_area[i] = 1;
+		}
+
+		if (!in_resting_area[i] || (in_resting_area[i] && is_moved[i])) {
+			memcpy(&output[index++], &f->pt_img[i], sizeof(struct alps_abs_data));
+			cur_coord_is_output[i] = 1;
+			output_fn ++;	
+        }
+	}
+	
+	// A normal finger becomes a resting finger
+	for (i = 0; i < f->fingers; i++) {
+		if (prev_coord_is_output[i] && !cur_coord_is_output[i] && f->pt_img[i].z) {
+			output_fn = 0;
+			memset(output, 0, sizeof(struct alps_abs_data)*f->fingers);
+		}
+	}
+
+	memcpy(prev_pt, f->pt_img, sizeof(f->pt_img));
+	memcpy(prev_coord_is_output, cur_coord_is_output, sizeof(cur_coord_is_output));
+	prev_fn = f->fingers;
+	*p_fn = output_fn;
+}
+
+static void alps_decode_ss4_v1(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char pkt_id;
+	unsigned int no_data_x, no_data_y;
+
+	if (!psmouse || !f || !p )
+		return;
+
+	pkt_id = alps_get_pkt_id_ss4_v1(p);
+
+	// Current packet is 1Finger coordinate packet
+	switch (pkt_id) {
+	case SS4_PACKET_ID_ONE:
+		f->pt_img[0].x = SS4_1F_X_V1(p);
+		f->pt_img[0].y = SS4_1F_Y_V1(p);
+		f->pt_img[0].z = ((SS4_1F_Z_V1(p)) * 2) & 0x7f;	// Keep Z-value in 0-127
+
+		f->large_fn |= COSMO_1F_LFB(p) ? 0x01 : 0x00;
+
+		f->fingers = 1;
+		f->first_mp = 0;
+		f->is_mp = 0;
+		break;
+
+	case SS4_PACKET_ID_TWO:
+		if (priv->flags & ALPS_BTNLESS) {
+			f->pt_img[0].x = SS4_BTL_MF_X_V1( p, 0 ) ;
+			f->pt_img[0].y = SS4_BTL_MF_Y_V1( p, 0 ) ;
+			f->pt_img[1].x = SS4_BTL_MF_X_V1( p, 1 ) ;
+			f->pt_img[1].y = SS4_BTL_MF_Y_V1( p, 1 ) ;
+		}
+		else {
+			f->pt_img[0].x = SS4_STD_MF_X_V1( p, 0 ) ;
+			f->pt_img[0].y = SS4_STD_MF_Y_V1( p, 0 ) ;
+			f->pt_img[1].x = SS4_STD_MF_X_V1( p, 1 ) ;
+			f->pt_img[1].y = SS4_STD_MF_Y_V1( p, 1 ) ;
+		}
+		f->pt_img[0].z = SS4_MF_Z_V1( p, 0 ) ? 0x30 : 0;
+		f->pt_img[1].z = SS4_MF_Z_V1( p, 1 ) ? 0x30 : 0;
+
+		if (SS4_IS_MF_CONTINUE_V1(p)) {
+			f->first_mp = 1;
+		}
+		else {
+			f->fingers = 2;
+			f->first_mp = 0;
+		}
+		f->is_mp = 0;
+	
+		break;
+
+	case SS4_PACKET_ID_MULTI:
+		if (priv->flags & ALPS_BTNLESS) {
+			f->pt_img[2].x = SS4_BTL_MF_X_V1( p, 0 ) ;
+			f->pt_img[2].y = SS4_BTL_MF_Y_V1( p, 0 ) ;
+			f->pt_img[3].x = SS4_BTL_MF_X_V1( p, 1 ) ;
+			f->pt_img[3].y = SS4_BTL_MF_Y_V1( p, 1 ) ;
+			no_data_x = SS4_MFPACKET_NO_AX_BL;
+			no_data_y = SS4_MFPACKET_NO_AY_BL;
+		}
+		else {
+			f->pt_img[2].x = SS4_STD_MF_X_V1( p, 0 ) ;
+			f->pt_img[2].y = SS4_STD_MF_Y_V1( p, 0 ) ;
+			f->pt_img[3].x = SS4_STD_MF_X_V1( p, 1 ) ;
+			f->pt_img[3].y = SS4_STD_MF_Y_V1( p, 1 ) ;
+			no_data_x = SS4_MFPACKET_NO_AX;
+			no_data_y = SS4_MFPACKET_NO_AY;
+		}
+		f->pt_img[2].z = SS4_MF_Z_V1( p, 0 ) ? 0x30 : 0;
+		f->pt_img[3].z = SS4_MF_Z_V1( p, 1 ) ? 0x30 : 0;
+
+		f->first_mp = 0;
+		f->is_mp = 1;
+
+		if (SS4_IS_5F_DETECTED_V1(p)) {
+			f->fingers = 5;
+		}
+		else if (f->pt_img[3].x == no_data_x &&
+			     f->pt_img[3].y == no_data_y) {
+			f->fingers = 3;
+			f->pt_img[3].x = 0;
+			f->pt_img[3].y = 0;
+			f->pt_img[3].z = 0;
+		}
+		else {
+			f->fingers = 4;
+		}
+		break;
+
+	case SS4_PACKET_ID_IDLE:
+	default:
+		memset(f, 0, sizeof(struct alps_fields));
+		break;
+	}
+
+	f->btn.left = !!(SS4_BTN_V1(p) & 0x01) ;
+	if (!(priv->flags & ALPS_BTNLESS)) {
+		f->btn.right = !!(SS4_BTN_V1(p) & 0x02) ;
+		f->btn.middle = !!(SS4_BTN_V1(p) & 0x04) ;
+	}
+}
+
+static void alps_decode_ss4_v2(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char pkt_id;
+	unsigned int no_data_x, no_data_y;
+
+	if (!psmouse || !f || !p )
+		return;
+
+	pkt_id = alps_get_pkt_id_ss4_v2(p);
+
+	// Current packet is 1Finger coordinate packet
+	switch (pkt_id) {
+	case SS4_PACKET_ID_ONE:
+		f->pt_img[0].x = SS4_1F_X_V2(p);
+		f->pt_img[0].y = SS4_1F_Y_V2(p);
+		f->pt_img[0].z = ((SS4_1F_Z_V2(p)) * 2) & 0x7f;	// Keep Z-value in 0-127
+		f->large_fn |= COSMO_1F_LFB_V2(p) ? 0x01 : 0x00;
+
+		f->fingers = 1;
+		f->first_mp = 0;
+		f->is_mp = 0;
+		break;
+
+	case SS4_PACKET_ID_TWO:
+		if (priv->flags & ALPS_BTNLESS) {
+			f->pt_img[0].x = SS4_BTL_MF_X_V2( p, 0 ) ;
+			f->pt_img[0].y = SS4_BTL_MF_Y_V2( p, 0 ) ;
+			f->pt_img[1].x = SS4_BTL_MF_X_V2( p, 1 ) ;
+			f->pt_img[1].y = SS4_BTL_MF_Y_V2( p, 1 ) ;
+		}
+		else {
+			f->pt_img[0].x = SS4_STD_MF_X_V2( p, 0 ) ;
+			f->pt_img[0].y = SS4_STD_MF_Y_V2( p, 0 ) ;
+			f->pt_img[1].x = SS4_STD_MF_X_V2( p, 1 ) ;
+			f->pt_img[1].y = SS4_STD_MF_Y_V2( p, 1 ) ;
+		}
+		f->pt_img[0].z = SS4_MF_Z_V2( p, 0 ) ? 0x30 : 0;
+		f->pt_img[1].z = SS4_MF_Z_V2( p, 1 ) ? 0x30 : 0;
+
+		f->large_fn |= COSMO_MF_LF_V2( p, 0 ) ? 0x01 : 0 ;
+		f->large_fn |= COSMO_MF_LF_V2( p, 1 ) ? 0x02 : 0 ;
+
+		if (SS4_IS_MF_CONTINUE_V2(p)) {
+			f->first_mp = 1;
+		}
+		else {
+			f->fingers = 2;
+			f->first_mp = 0;
+		}
+		f->is_mp = 0;
+		break;
+
+	case SS4_PACKET_ID_MULTI:
+		if (priv->flags & ALPS_BTNLESS) {
+			f->pt_img[2].x = SS4_BTL_MF_X_V2( p, 0 ) ;
+			f->pt_img[2].y = SS4_BTL_MF_Y_V2( p, 0 ) ;
+			f->pt_img[3].x = SS4_BTL_MF_X_V2( p, 1 ) ;
+			f->pt_img[3].y = SS4_BTL_MF_Y_V2( p, 1 ) ;
+			no_data_x = SS4_MFPACKET_NO_AX_BL;
+			no_data_y = SS4_MFPACKET_NO_AY_BL;
+		}
+		else {
+			f->pt_img[2].x = SS4_STD_MF_X_V2( p, 0 ) ;
+			f->pt_img[2].y = SS4_STD_MF_Y_V2( p, 0 ) ;
+			f->pt_img[3].x = SS4_STD_MF_X_V2( p, 1 ) ;
+			f->pt_img[3].y = SS4_STD_MF_Y_V2( p, 1 ) ;
+			no_data_x = SS4_MFPACKET_NO_AX;
+			no_data_y = SS4_MFPACKET_NO_AY;
+		}
+		f->pt_img[2].z = SS4_MF_Z_V2( p, 0 ) ? 0x30 : 0;
+		f->pt_img[3].z = SS4_MF_Z_V2( p, 1 ) ? 0x30 : 0;
+
+		f->large_fn |= COSMO_MF_LF_V2( p, 0 ) ? 0x04 : 0 ;
+		f->large_fn |= COSMO_MF_LF_V2( p, 1 ) ? 0x08 : 0 ;
+		f->first_mp = 0;
+		f->is_mp = 1;
+
+		if (SS4_IS_5F_DETECTED_V2(p)) {
+			f->fingers = 5;
+		}
+		else if (f->pt_img[3].x == no_data_x &&
+			     f->pt_img[3].y == no_data_y) {
+			f->fingers = 3;
+			f->pt_img[3].x = 0;
+			f->pt_img[3].y = 0;
+			f->pt_img[3].z = 0;
+		}
+		else {
+			f->fingers = 4;
+		}
+		break;
+
+	case SS4_PACKET_ID_IDLE:
+	default:
+		memset(f, 0, sizeof(struct alps_fields));
+		break;
+	}
+
+	f->btn.left = !!(SS4_BTN_V2(p) & 0x01) ;
+	if (!(priv->flags & ALPS_BTNLESS)) {
+		f->btn.right = !!(SS4_BTN_V2(p) & 0x02) ;
+		f->btn.middle = !!(SS4_BTN_V2(p) & 0x04) ;
 	}
 }
 
@@ -658,7 +1130,7 @@  static void alps_process_touchpad_packet
 				 * there is no need to compare with bmap_fn.
 				 */
 				alps_process_bitmap_dolphin(priv, &f, &x1, &y1,
-							    &x2, &y2);
+							    &x2, &y2);			
 			}
 		} else {
 			priv->multi_packet = 0;
@@ -690,7 +1162,7 @@  static void alps_process_touchpad_packet
 	 * with x, y, and z all zero, so these seem to be flukes.
 	 * Ignore them.
 	 */
-	if (f.x && f.y && !f.z)
+	if (f.pt.x && f.pt.y && !f.pt.z)
 		return;
 
 	/*
@@ -698,12 +1170,12 @@  static void alps_process_touchpad_packet
 	 * to rely on ST data.
 	 */
 	if (!fingers) {
-		x1 = f.x;
-		y1 = f.y;
-		fingers = f.z > 0 ? 1 : 0;
+		x1 = f.pt.x;
+		y1 = f.pt.y;
+		fingers = f.pt.z > 0 ? 1 : 0;
 	}
-
-	if (f.z >= 64)
+	
+	if (f.pt.z >= 64)
 		input_report_key(dev, BTN_TOUCH, 1);
 	else
 		input_report_key(dev, BTN_TOUCH, 0);
@@ -712,22 +1184,22 @@  static void alps_process_touchpad_packet
 
 	input_mt_report_finger_count(dev, fingers);
 
-	input_report_key(dev, BTN_LEFT, f.left);
-	input_report_key(dev, BTN_RIGHT, f.right);
-	input_report_key(dev, BTN_MIDDLE, f.middle);
-
-	if (f.z > 0) {
-		input_report_abs(dev, ABS_X, f.x);
-		input_report_abs(dev, ABS_Y, f.y);
+	input_report_key(dev, BTN_LEFT, f.btn.left);
+	input_report_key(dev, BTN_RIGHT, f.btn.right);
+	input_report_key(dev, BTN_MIDDLE, f.btn.middle);
+
+	if (f.pt.z > 0) {
+		input_report_abs(dev, ABS_X, f.pt.x);
+		input_report_abs(dev, ABS_Y, f.pt.y);
 	}
-	input_report_abs(dev, ABS_PRESSURE, f.z);
+	input_report_abs(dev, ABS_PRESSURE, f.pt.z);
 
 	input_sync(dev);
 
 	if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
-		input_report_key(dev2, BTN_LEFT, f.ts_left);
-		input_report_key(dev2, BTN_RIGHT, f.ts_right);
-		input_report_key(dev2, BTN_MIDDLE, f.ts_middle);
+		input_report_key(dev2, BTN_LEFT, f.btn.ts_left);
+		input_report_key(dev2, BTN_RIGHT, f.btn.ts_right);
+		input_report_key(dev2, BTN_MIDDLE, f.btn.ts_middle);
 		input_sync(dev2);
 	}
 }
@@ -752,6 +1224,66 @@  static void alps_process_packet_v3(struc
 	alps_process_touchpad_packet_v3_v5(psmouse);
 }
 
+/* proto_v9 */
+static void alps_process_touchpad_packet_ss3(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev = psmouse->dev;
+	int fingers = 0;
+	struct alps_fields f = {0};
+	struct alps_abs_data 	pt_output[2] = {{0,0,0},{0,0,0}};
+	unsigned char output_fn_num = 0;
+	
+	priv->decode_fields(&f,packet,psmouse);
+
+	if ((!!priv->multi_packet) != (!!f.is_mp)){
+		priv->multi_packet = 0;
+		return ;
+	}
+
+	/* When f.first_mp is 1, next packet should be a 
+	 *	bitmap packet(when there is no error).
+	 */
+	priv->multi_packet = f.first_mp;
+	
+	/* Don't process any 3-f data */
+	if(f.first_mp || f.is_mp)
+	{
+		return;
+	}	
+
+	/*
+	 * If we don't have MT data or the bitmaps were empty, we have
+	 * to rely on ST data.
+	 */
+	fingers = f.fingers;
+	
+	alps_process_resting_finger(psmouse, &f, pt_output, &output_fn_num);
+	alps_process_btnless_click(psmouse, &f);
+
+	if (pt_output[0].z || pt_output[1].z)
+		input_report_key(dev, BTN_TOUCH, 1);
+	else
+		input_report_key(dev, BTN_TOUCH, 0);
+
+	alps_report_semi_mt_data_ex(dev, 2, pt_output);
+
+	input_mt_report_finger_count(dev, output_fn_num);
+
+	input_report_key(dev, BTN_LEFT, f.btn.left);
+	input_report_key(dev, BTN_RIGHT, f.btn.right);
+	input_report_key(dev, BTN_MIDDLE, f.btn.middle);
+
+	if (pt_output[0].z > 0) {
+		input_report_abs(dev, ABS_X, pt_output[0].x);
+		input_report_abs(dev, ABS_Y, pt_output[0].y);
+	}
+	input_report_abs(dev, ABS_PRESSURE, pt_output[0].z?16:0);
+
+	input_sync(dev);
+}
+
 static void alps_process_packet_v6(struct psmouse *psmouse)
 {
 	struct alps_data *priv = psmouse->private;
@@ -919,6 +1451,421 @@  static void alps_process_packet_v4(struc
 	input_sync(dev);
 }
 
+static bool alps_is_valid_package_v7(struct psmouse *psmouse)
+{
+	if ((psmouse->pktcnt == 3) && ((psmouse->packet[2] & 0x40) != 0x40))
+		return false;
+	if ((psmouse->pktcnt == 4) && ((psmouse->packet[3] & 0x48) != 0x48))
+		return false;
+	if ((psmouse->pktcnt == 6) && ((psmouse->packet[5] & 0x40) != 0x0))
+		return false;
+	return true;
+}
+
+static int alps_drop_unsupported_packet_v7(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	int drop = 1;
+
+	if (priv->r.v7.pkt_id == V7_PACKET_ID_NEW ||
+	    priv->r.v7.pkt_id == V7_PACKET_ID_TWO ||
+	    priv->r.v7.pkt_id == V7_PACKET_ID_MULTI ||
+	    priv->r.v7.pkt_id == V7_PACKET_ID_IDLE)
+		drop = 0;
+
+	return drop;
+}
+
+static unsigned char alps_get_packet_id_v7(char *byte)
+{
+	unsigned char packet_id;
+
+	if (byte[4] & 0x40)
+		packet_id = V7_PACKET_ID_TWO;
+	else if (byte[4] & 0x01)
+		packet_id = V7_PACKET_ID_MULTI;
+	else if ((byte[0] & 0x10) && !(byte[4] & 0x43))
+		packet_id = V7_PACKET_ID_NEW;
+	else if (byte[1] == 0x00 && byte[4] == 0x00)
+		packet_id = V7_PACKET_ID_IDLE;
+	else
+		packet_id = V7_PACKET_ID_UNKNOWN;
+
+	return packet_id;
+}
+
+static void alps_get_finger_coordinate_v7(struct alps_abs_data *pt,
+					  unsigned char *pkt,
+					  unsigned char pkt_id)
+{
+	if ((pkt_id == V7_PACKET_ID_TWO) ||
+	   (pkt_id == V7_PACKET_ID_MULTI) ||
+	   (pkt_id == V7_PACKET_ID_NEW)) {
+		pt[0].x = ((pkt[2] & 0x80) << 4);
+		pt[0].x |= ((pkt[2] & 0x3F) << 5);
+		pt[0].x |= ((pkt[3] & 0x30) >> 1);
+		pt[0].x |= (pkt[3] & 0x07);
+		pt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07);
+
+		pt[1].x = ((pkt[3] & 0x80) << 4);
+		pt[1].x |= ((pkt[4] & 0x80) << 3);
+		pt[1].x |= ((pkt[4] & 0x3F) << 4);
+		pt[1].y = ((pkt[5] & 0x80) << 3);
+		pt[1].y |= ((pkt[5] & 0x3F) << 4);
+
+		if (pkt_id == V7_PACKET_ID_TWO) {
+			pt[1].x &= ~0x000F;
+			pt[1].y |= 0x000F;
+		} else if (pkt_id == V7_PACKET_ID_MULTI) {
+			pt[1].x &= ~0x003F;
+			pt[1].y &= ~0x0020;
+			pt[1].y |= ((pkt[4] & 0x02) << 4);
+			pt[1].y |= 0x001F;
+		} else if (pkt_id == V7_PACKET_ID_NEW) {
+			pt[1].x &= ~0x003F;
+			pt[1].x |= (pkt[0] & 0x20);
+			pt[1].y |= 0x000F;
+		}
+
+		pt[0].y = 0x7FF - pt[0].y;
+		pt[1].y = 0x7FF - pt[1].y;
+
+		pt[0].z = (pt[0].x && pt[0].y) ? 62 : 0;
+		pt[1].z = (pt[1].x && pt[1].y) ? 62 : 0;
+	}
+}
+
+static void alps_decode_packet_v7(struct alps_fields *f,
+				  unsigned char *p,
+				  struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+
+	priv->r.v7.pkt_id = alps_get_packet_id_v7(p);
+
+	alps_get_finger_coordinate_v7(f->pt_img, p, priv->r.v7.pkt_id);
+
+	priv->r.v7.rest_left = 0;
+	priv->r.v7.rest_right = 0;
+	priv->r.v7.additional_fingers = 0;
+	memset(&f->btn, 0, sizeof(struct alps_btn));
+	
+	if (priv->r.v7.pkt_id == V7_PACKET_ID_TWO ||
+	    priv->r.v7.pkt_id == V7_PACKET_ID_MULTI) {
+
+		if (priv->flags & ALPS_BTNLESS) {
+			priv->r.v7.rest_left = (p[0] & 0x10) >> 4;
+			priv->r.v7.rest_right = (p[0] & 0x20) >> 5;
+			f->btn.middle = (p[0] & 0x80) >> 7;
+		}
+		else {
+			f->btn.left = (p[0] & 0x80) >> 7;
+			f->btn.right = (p[0] & 0x20) >> 5;
+			f->btn.middle = (p[0] & 0x10) >> 4;
+		}
+	}
+
+	if (priv->r.v7.pkt_id == V7_PACKET_ID_MULTI)
+		priv->r.v7.additional_fingers = p[5] & 0x03;
+}
+
+static void alps_set_each_pt_attr_v7(struct psmouse *psmouse,
+				     struct alps_abs_data *pt,
+				     struct alps_bl_pt_attr *pt_attr)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned int dist;
+
+	if (!pt_attr->is_init_pt_got && pt->z != 0) {
+		pt_attr->is_init_pt_got = 1;
+		pt_attr->is_counted = 0;
+		memcpy(&pt_attr->init_pt, pt, sizeof(pt_attr->init_pt));
+	}
+
+	if (pt->z != 0) {
+		if (pt->y < priv->resting_zone_y_min) {
+			/* A finger is recognized as a non-resting finger if it's
+			position is outside the resting finger zone.*/
+			pt_attr->zone = ZONE_NORMAL;
+			pt_attr->is_counted = 1;
+		} else {
+			/* A finger is recognized as a resting finger if it's position
+			is inside the resting finger zone and there's no large movement
+			from it's touch down position.*/
+			pt_attr->zone = ZONE_RESTING;
+
+			if (pt->x > priv->x_max / 2)
+				pt_attr->zone |= ZONE_RIGHT_BTN;
+			else
+				pt_attr->zone |= ZONE_LEFT_BTN;
+			
+			/* A resting finger will turn to be a non-resting finger if it
+			has made large movement from it's touch down position. A
+			non-resting finger will never turn to a resting finger before
+			it leaves the touchpad surface.*/
+			if (pt_attr->is_init_pt_got) {
+				dist = alps_pt_distance(pt, &pt_attr->init_pt);
+
+				if (dist > V7_LARGE_MOVEMENT)
+					pt_attr->is_counted = 1;
+			}
+		}
+	}
+}
+
+static void alps_set_pt_attr_v7(struct psmouse *psmouse,
+				       struct alps_fields *f)
+{
+	struct alps_data *priv = psmouse->private;
+	int i;
+
+	switch (priv->r.v7.pkt_id) {
+	case  V7_PACKET_ID_TWO:
+	case  V7_PACKET_ID_MULTI:
+		for (i = 0; i < V7_IMG_PT_NUM; i++)
+			alps_set_each_pt_attr_v7(psmouse,
+						 &f->pt_img[i],
+						 &priv->pt_attr[i]);
+		break;
+	default:
+		/*All finger attributes are cleared when packet ID is 'IDLE', 'New'
+		or other unknown IDs. An 'IDLE' packet indicates that there's no
+		finger and no button activity. A 'NEW' packet indicates the finger
+		position in packet is not continues from previous packet. Such as 
+		the condition there's finger placed or lifted. In these cases,
+		finger attributes will be reset.*/
+
+		memset(priv->pt_attr, 0, sizeof(priv->pt_attr[0]) * V7_IMG_PT_NUM);
+		break;
+	}
+}
+
+static void alps_cal_output_finger_num_v7(struct psmouse *psmouse,
+					struct alps_fields *f)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned int fn = 0;
+	int i;
+
+	switch (priv->r.v7.pkt_id) {
+	case V7_PACKET_ID_IDLE:
+	case V7_PACKET_ID_NEW:
+		/*No finger is reported when packet ID is 'IDLE' or 'New'.
+		An 'IDLE' packet indicates that there's no finger on touchpad.
+		A 'NEW' packet indicates there's finger placed or lifted. Finger
+		position of 'New' packet is not continues from the previous packet.*/
+		fn = 0;
+		break;
+	case V7_PACKET_ID_TWO:
+		if (f->pt_img[0].z == 0) {
+			/*The first finger slot is zero when a non-resting finger
+			lifted and remaining only one resting finger on touchpad. 
+			Hardware report the remaining resting finger in second slot. 
+			This resting is ignored*/
+			fn = 0;
+		} else if (f->pt_img[1].z == 0) {
+			/* The second finger slot is zero if there's only one finger*/
+			fn = 1;
+		} else {
+			/*All non-resting fingers will be counted to report*/
+			fn = 0;
+			for (i = 0; i < V7_IMG_PT_NUM; i++) {
+				if (priv->pt_attr[i].is_counted)
+					fn++;
+			}
+
+			/*In the case that both fingers are
+			resting fingers, report the first one*/
+			if (!priv->pt_attr[0].is_counted &&
+			    !priv->pt_attr[1].is_counted) {
+				fn = 1;
+			}
+		}
+		break;
+	case V7_PACKET_ID_MULTI:
+		/*A packet ID 'MULTI' indicats that at least 3 non-resting
+		finger exist.*/
+		fn = 3 + priv->r.v7.additional_fingers;
+		break;
+	}
+
+	f->fingers = fn;
+}
+
+static void alps_assign_buttons_v7(struct psmouse *psmouse,
+				   struct alps_fields *f)
+{
+	struct alps_data *priv = psmouse->private;
+
+	/* It's ClickPad */
+	if (priv->flags & ALPS_BTNLESS) {
+		if (!f->btn.middle)
+			goto exit;
+
+		f->btn.middle = 0;
+		if (priv->prev_btn.left || priv->prev_btn.middle || priv->prev_btn.right) {
+			memcpy(&f->btn, &priv->prev_btn, sizeof(struct alps_btn));
+			goto exit;
+		}
+
+		if (priv->r.v7.rest_right ||
+		    priv->pt_attr[0].zone & ZONE_RIGHT_BTN ||
+		    priv->pt_attr[1].zone & ZONE_RIGHT_BTN) {
+			f->btn.right = 1;
+		} else {
+			f->btn.left = 1;
+		}
+
+		goto exit;
+	}
+
+	/* It's Standard, do nothing */
+
+exit:
+	memcpy(&priv->prev_btn, &f->btn, sizeof(struct alps_btn));
+}
+
+static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev2 = priv->dev2;
+	int x, y, z, left, right, middle;
+
+	x = ((packet[2] & 0xBF)) | ((packet[3] & 0x10) << 2);
+	y = (packet[3] & 0x03) | (packet[4] & 0xB8) | ((packet[3] & 0x20) << 1);
+	z = (packet[5] & 0x3F) | ((packet[3] & 0x80) >> 1); 
+
+	left = (packet[1] & 0x01);
+	right = (packet[1] & 0x02) >> 1;
+	middle = (packet[1] & 0x04) >> 2;
+
+	/* Divide 2 since trackpoint's speed is too fast */
+	input_report_rel(dev2, REL_X, (char)x / 2);
+	input_report_rel(dev2, REL_Y, -((char)y / 2));
+
+	input_report_key(dev2, BTN_LEFT, left);
+	input_report_key(dev2, BTN_RIGHT, right);
+	input_report_key(dev2, BTN_MIDDLE, middle);
+
+	input_sync(dev2);
+
+}
+
+static void alps_process_touchpad_packet_v7(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	struct alps_fields f = {0};
+	unsigned char *packet = psmouse->packet;
+
+	priv->decode_fields(&f, packet, psmouse);
+
+	if (alps_drop_unsupported_packet_v7(psmouse))
+		return;
+
+	alps_set_pt_attr_v7(psmouse, &f);
+
+	alps_cal_output_finger_num_v7(psmouse, &f);
+
+	alps_assign_buttons_v7(psmouse, &f);
+
+	alps_report_coord_and_btn(psmouse, &f);
+}
+
+static void alps_process_packet_v7(struct psmouse *psmouse)
+{
+	unsigned char *packet = psmouse->packet;
+
+	if ((packet[0] == 0x48) && ((packet[4] & 0x47) == 0x06)) {
+		alps_process_trackstick_packet_v7(psmouse);
+	} else {
+		alps_process_touchpad_packet_v7(psmouse);
+	}
+
+}
+
+static void alps_process_packet_ss4(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev = psmouse->dev;
+	struct alps_fields f;
+	struct alps_abs_data output_data[5];
+	unsigned char output_fn;
+
+	memset(&f, 0, sizeof(struct alps_fields));
+	priv->decode_fields(&f, packet, psmouse);
+
+	if (priv->multi_packet) {
+		/*
+		 * Sometimes the first packet will indicate a multi-packet
+		 * sequence, but sometimes the next multi-packet would not come.
+		 * Check for this, and when it happens process the
+		 * position packet as usual.
+		 */
+		if (f.is_mp) {
+			/* Now process the 1st packet */
+			priv->decode_fields(&f, priv->multi_data, psmouse);
+		} else {
+			priv->multi_packet = 0;
+		}
+	}
+
+	/*
+	 * "f.is_mp" would always be '0' after merging the 1st and 2nd packet.
+	 * When it is set, it means 2nd packet comes without 1st packet come.
+	 */
+	if (f.is_mp)
+	{
+		return;
+	}
+
+	// Save the first packet
+	if (!priv->multi_packet && f.first_mp) {
+		priv->multi_packet = 1;
+		memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
+		return;
+	}
+	
+	priv->multi_packet = 0;
+
+	// Set "output_data" and "output_fn"
+	memset(&output_data[0], 0, sizeof(output_data));
+	if (priv->flags & ALPS_BTNLESS) {
+		alps_process_resting_finger(psmouse, &f, output_data, &output_fn);
+		alps_process_btnless_click(psmouse, &f);
+	}
+	else {
+		memcpy(&output_data[0], &f.pt_img[0], sizeof(struct alps_abs_data) * f.fingers);
+		output_fn = f.fingers;
+	}
+
+	f.pt.x = output_data[0].x;
+	f.pt.y = output_data[0].y;
+	f.pt.z = output_data[0].z;
+
+	if (output_data[0].z || output_data[1].z || output_data[2].z || output_data[3].z)
+		input_report_key(dev, BTN_TOUCH, 1);
+	else
+		input_report_key(dev, BTN_TOUCH, 0);
+
+	alps_report_semi_mt_data_ex(dev, 4, output_data);
+
+	input_mt_report_finger_count(dev, output_fn);
+
+	input_report_key(dev, BTN_LEFT, f.btn.left);
+	input_report_key(dev, BTN_RIGHT, f.btn.right);
+	input_report_key(dev, BTN_MIDDLE, f.btn.middle);
+
+	if (f.pt.z > 0) {
+		input_report_abs(dev, ABS_X, f.pt.x);
+		input_report_abs(dev, ABS_Y, f.pt.y);
+	}
+	input_report_abs(dev, ABS_PRESSURE, f.pt.z);
+
+	input_sync(dev);
+}
+
 static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
 					unsigned char packet[],
 					bool report_buttons)
@@ -1046,11 +1993,20 @@  static void alps_flush_packet(unsigned l
 	serio_continue_rx(psmouse->ps2dev.serio);
 }
 
+static bool alps_is_valid_package_ss4(struct psmouse *psmouse)
+{
+	if(psmouse->pktcnt == 4 && ((psmouse->packet[3] & 0x08) != 0x08))
+		return false;
+	if(psmouse->pktcnt == 6 && ((psmouse->packet[5] & 0x10) != 0x0))
+		return false;
+	return true;
+}
+
 static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
 {
 	struct alps_data *priv = psmouse->private;
 
-	if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
+	if ((psmouse->packet[0] & 0xc8) == 0x08 && priv->proto_version != ALPS_PROTO_V8) { /* Can not distinguish V8's first byte from PS/2 packet's */
 		if (psmouse->pktcnt == 3) {
 			alps_report_bare_ps2_packet(psmouse, psmouse->packet,
 						    true);
@@ -1083,6 +2039,15 @@  static psmouse_ret_t alps_process_byte(s
 		return PSMOUSE_BAD_DATA;
 	}
 
+	if ((priv->proto_version == ALPS_PROTO_V7 && !alps_is_valid_package_v7(psmouse)) ||
+		(priv->proto_version == ALPS_PROTO_V8 && !alps_is_valid_package_ss4(psmouse)))
+	{
+		psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
+			    psmouse->pktcnt - 1,
+			    psmouse->packet[psmouse->pktcnt - 1]);
+		return PSMOUSE_BAD_DATA;
+	}
+
 	if (psmouse->pktcnt == psmouse->pktsize) {
 		priv->process_packet(psmouse);
 		return PSMOUSE_FULL_PACKET;
@@ -1195,6 +2160,22 @@  static int alps_rpt_cmd(struct psmouse *
 	return 0;
 }
 
+static int alps_check_valid_firmware_id(unsigned char id[])
+{
+	int valid = 1;
+
+	if (id[0] == 0x73)
+		valid = 1;
+	else if (id[0] == 0x88) {
+		if ((id[1] == 0x07) ||
+		    (id[1] == 0x08) ||
+		    ((id[1] & 0xf0) == 0xB0))
+			valid = 1;
+	}
+
+	return valid;
+}
+
 static int alps_enter_command_mode(struct psmouse *psmouse)
 {
 	unsigned char param[4];
@@ -1204,8 +2185,7 @@  static int alps_enter_command_mode(struc
 		return -1;
 	}
 
-	if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) &&
-	    param[0] != 0x73) {
+	if (!alps_check_valid_firmware_id(param)) {
 		psmouse_dbg(psmouse,
 			    "unknown response while entering command mode\n");
 		return -1;
@@ -1707,6 +2687,32 @@  error:
 	return ret;
 }
 
+static int alps_hw_init_v7(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	int reg_val, ret = -1;
+
+	if (alps_enter_command_mode(psmouse) ||
+	    alps_command_mode_read_reg(psmouse, 0xc2d9) == -1)
+		goto error;
+
+	if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
+		goto error;
+
+	reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4);
+	if (reg_val == -1)
+		goto error;
+	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02))
+		goto error;
+
+	alps_exit_command_mode(psmouse);
+	return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+error:
+	alps_exit_command_mode(psmouse);
+	return ret;
+}
+
 /* Must be in command mode when calling this function */
 static int alps_absolute_mode_v4(struct psmouse *psmouse)
 {
@@ -1796,6 +2802,97 @@  error:
 	return -1;
 }
 
+static int alps_get_otp_values_ss4(struct psmouse *psmouse, unsigned char index, unsigned char otp[])
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	switch (index) {
+	case 0:
+		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)  ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)  ||
+		    ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO) )
+		    	return -1;
+		
+		break;
+
+	case 1:
+		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL)  ||
+		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL)  ||
+		    ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO) )
+			return -1;
+
+		break;
+	}
+
+	return 0;
+}
+
+int alps_update_device_area_ss4_v1(unsigned char otp[][4], struct alps_data *priv)
+{
+	int	num_x_electrode ;
+	int	num_y_electrode ;
+
+	num_x_electrode = SS4_NUMSENSOR_XOFFSET + (otp[1][0]		& 0x0F);
+	num_y_electrode = ((otp[1][0] >> 4)	& 0x0F);
+
+	priv->x_max = (num_x_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
+	priv->y_max = (num_y_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
+
+	return 0;
+}
+
+int alps_update_device_area_ss4_v2(unsigned char otp[][4], struct alps_data *priv)
+{
+	int	num_x_electrode ;
+	int	num_y_electrode ;
+
+	num_x_electrode = SS4_NUMSENSOR_XOFFSET + (otp[1][0] & 0x0F);
+	if ((priv->fw_ver[1] > 2) || (priv->fw_ver[1] == 2 && priv->fw_ver[2] > 0x33))
+		num_y_electrode = SS4_NUMSENSOR_YOFFSET + ((otp[1][0] >> 4) & 0x07);
+	else
+		num_y_electrode = ((otp[1][0] >> 4) & 0x0F);
+
+	priv->x_max = (num_x_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
+	priv->y_max = (num_y_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
+
+	return 0;
+}
+
+int alps_update_btn_info_ss4(unsigned char otp[][4], struct alps_data *priv)
+{
+
+	unsigned char is_btnless = 0;
+
+	is_btnless = (otp[1][1] >> 3) &  0x01;
+
+	if (is_btnless) {
+		priv->flags |= ALPS_BTNLESS;
+	}
+	
+	return 0;
+}
+
+static int alps_set_defaults_ss4(struct psmouse *psmouse, struct alps_data *priv)
+{
+	unsigned char otp[2][4];
+
+	memset(otp, 0, sizeof(otp));
+
+	if (alps_get_otp_values_ss4(psmouse, 0, &otp[0][0]) ||
+	    alps_get_otp_values_ss4(psmouse, 1, &otp[1][0]))
+		return -1;
+
+	if (priv->fw_ver[1] >= 2) {
+		alps_update_device_area_ss4_v2(otp, priv);
+	}
+	else {
+		alps_update_device_area_ss4_v1(otp, priv);
+	}
+
+	alps_update_btn_info_ss4(otp, priv);
+
+	return 0;
+}
+
 static int alps_dolphin_get_device_area(struct psmouse *psmouse,
 					struct alps_data *priv)
 {
@@ -1859,6 +2956,61 @@  static int alps_hw_init_dolphin_v1(struc
 	return 0;
 }
 
+static int alps_hw_init_ss3(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev 	= &psmouse->ps2dev;
+	unsigned char 	f3param0 = 0x00,
+					f3param1 = 0x00;
+	
+	if (alps_enter_command_mode(psmouse))
+        goto error;
+	
+	/* Set to 2 pt-mode */
+	f3param0 = 0x50;
+	f3param1 = 0x3c;
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+	    ps2_command(ps2dev, &f3param0, PSMOUSE_CMD_SETRATE) ||
+	    ps2_command(ps2dev, &f3param1, PSMOUSE_CMD_SETRATE))
+		goto error;
+
+	/* output APDATA when 1 finger is in resting area */
+	f3param0 = 0xc8;
+	f3param1 = 0x28;
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+	    ps2_command(ps2dev, &f3param0, PSMOUSE_CMD_SETRATE) ||
+	    ps2_command(ps2dev, &f3param1, PSMOUSE_CMD_SETRATE))
+			goto error;
+
+	return 0;
+	
+error:
+	return -1;
+}
+
+
+static int alps_hw_init_ss4(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	char param[2] = {0x64, 0x28};
+	int ret = -1;
+
+	/* enter absolute mode */
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+	    ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
+	    ps2_command(ps2dev, &param[1], PSMOUSE_CMD_SETRATE)) {
+		goto error;
+	}
+
+	return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+error:	
+	alps_exit_command_mode(psmouse);
+	return ret;
+}
+
 static void alps_set_defaults(struct alps_data *priv)
 {
 	priv->byte0 = 0x8f;
@@ -1878,6 +3030,7 @@  static void alps_set_defaults(struct alp
 		priv->set_abs_params = alps_set_abs_params_st;
 		priv->x_max = 1023;
 		priv->y_max = 767;
+		priv->slot_number = 1;
 		break;
 	case ALPS_PROTO_V3:
 		priv->hw_init = alps_hw_init_v3;
@@ -1886,6 +3039,7 @@  static void alps_set_defaults(struct alp
 		priv->decode_fields = alps_decode_pinnacle;
 		priv->nibble_commands = alps_v3_nibble_commands;
 		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+		priv->slot_number = 2;
 		break;
 	case ALPS_PROTO_V4:
 		priv->hw_init = alps_hw_init_v4;
@@ -1893,6 +3047,7 @@  static void alps_set_defaults(struct alp
 		priv->set_abs_params = alps_set_abs_params_mt;
 		priv->nibble_commands = alps_v4_nibble_commands;
 		priv->addr_command = PSMOUSE_CMD_DISABLE;
+		priv->slot_number = 2;
 		break;
 	case ALPS_PROTO_V5:
 		priv->hw_init = alps_hw_init_dolphin_v1;
@@ -1908,6 +3063,7 @@  static void alps_set_defaults(struct alp
 		priv->y_max = 660;
 		priv->x_bits = 23;
 		priv->y_bits = 12;
+		priv->slot_number = 2;
 		break;
 	case ALPS_PROTO_V6:
 		priv->hw_init = alps_hw_init_v6;
@@ -1916,6 +3072,58 @@  static void alps_set_defaults(struct alp
 		priv->nibble_commands = alps_v6_nibble_commands;
 		priv->x_max = 2047;
 		priv->y_max = 1535;
+		priv->slot_number = 1;
+		break;
+	case ALPS_PROTO_V7:
+		priv->hw_init = alps_hw_init_v7;
+		priv->process_packet = alps_process_packet_v7;
+		priv->decode_fields = alps_decode_packet_v7;
+		priv->set_abs_params = alps_set_abs_params_mt;
+		priv->nibble_commands = alps_v3_nibble_commands;
+		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+		priv->x_max = 0xfff;
+		priv->y_max = 0x7ff;
+		priv->byte0 = 0x48;
+		priv->mask0 = 0x48;
+
+		if (priv->fw_ver[1] == 0xBA) {
+			priv->flags = 0;
+			priv->resting_zone_y_min = priv->y_max;	/* No resting finger area */
+		}
+		else {
+			priv->flags = ALPS_BTNLESS;
+			priv->resting_zone_y_min = 0x654;
+		}
+
+		priv->slot_number = 2;
+		break;
+	case ALPS_PROTO_V8:
+		if (priv->fw_ver[1] >= 2) {
+			priv->decode_fields = alps_decode_ss4_v2;
+		} else {
+			priv->decode_fields = alps_decode_ss4_v1;
+		}
+		priv->hw_init = alps_hw_init_ss4;
+		priv->process_packet = alps_process_packet_ss4;
+		priv->set_abs_params = alps_set_abs_params_mt;
+		priv->nibble_commands = alps_v3_nibble_commands;
+		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+		priv->x_max = 0xfff;
+		priv->y_max = 0x7ff;
+		priv->byte0 = 0x18;
+		priv->mask0 = 0x18;
+		priv->flags = 0;
+		priv->slot_number = 4;
+		break;
+	case ALPS_PROTO_V9:
+		priv->hw_init = alps_hw_init_ss3;
+		priv->process_packet = alps_process_touchpad_packet_ss3;
+		priv->decode_fields = alps_decode_dolphin;
+		priv->set_abs_params = alps_set_abs_params_mt;
+		priv->nibble_commands = alps_v3_nibble_commands;
+		priv->byte0 = 0xc8;
+		priv->mask0 = 0xc8;
+		priv->slot_number = 2;		
 		break;
 	}
 }
@@ -1975,6 +3183,9 @@  static int alps_identify(struct psmouse
 	    alps_exit_command_mode(psmouse))
 		return -EIO;
 
+	/* Save the Firmware version */
+	memcpy(priv->fw_ver, ec, 3);
+
 	if (alps_match_table(psmouse, priv, e7, ec) == 0) {
 		return 0;
 	} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
@@ -1985,6 +3196,11 @@  static int alps_identify(struct psmouse
 			return -EIO;
 		else
 			return 0;
+	} else if (ec[0] == 0x88 && (ec[1] & 0xf0) == 0xB0) {
+		priv->proto_version = ALPS_PROTO_V7;
+		alps_set_defaults(priv);
+
+		return 0;
 	} else if (ec[0] == 0x88 && ec[1] == 0x08) {
 		priv->proto_version = ALPS_PROTO_V3;
 		alps_set_defaults(priv);
@@ -2007,6 +3223,22 @@  static int alps_identify(struct psmouse
 		alps_set_defaults(priv);
 
 		return 0;
+	} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x14) {
+		priv->proto_version = ALPS_PROTO_V8;
+		alps_set_defaults(priv);
+
+		if(alps_set_defaults_ss4(psmouse, priv))
+			return -EIO;
+		else
+			return 0;
+	} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0xc8){
+		priv->proto_version = ALPS_PROTO_V9;
+		alps_set_defaults(priv);
+		
+		if (alps_dolphin_get_device_area(psmouse, priv))
+			return -EIO;
+		else
+			return 0;
 	}
 
 	psmouse_info(psmouse,
@@ -2048,7 +3280,7 @@  static void alps_set_abs_params_mt(struc
 				   struct input_dev *dev1)
 {
 	set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
-	input_mt_init_slots(dev1, 2, 0);
+	input_mt_init_slots(dev1, priv->slot_number, 0);
 	input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
 	input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
 
@@ -2164,16 +3396,17 @@  init_fail:
 int alps_detect(struct psmouse *psmouse, bool set_properties)
 {
 	struct alps_data dummy;
-
-	if (alps_identify(psmouse, &dummy) < 0)
-		return -1;
-
+	
+    if (alps_identify(psmouse, &dummy) < 0)
+        return -1;
+        
 	if (set_properties) {
 		psmouse->vendor = "ALPS";
 		psmouse->name = dummy.flags & ALPS_DUALPOINT ?
 				"DualPoint TouchPad" : "GlidePoint";
 		psmouse->model = dummy.proto_version << 8;
 	}
+	
 	return 0;
 }
 
--- linux-3.14-rc1/drivers/input/mouse/alps.h	2015-02-08 12:39:28.367799685 +0100
+++ goliad-mlk-14_A03.fish.tar.gz/debs/touchpad-alps-trusty-dkms_20140325_all.deb/usr/src/touchpad-alps-trusty-20140325/alps.h	2014-06-06 04:21:48.000000000 +0200
@@ -18,11 +18,140 @@ 
 #define ALPS_PROTO_V4	4
 #define ALPS_PROTO_V5	5
 #define ALPS_PROTO_V6	6
+#define ALPS_PROTO_V7	7	/* t3btl t4s */
+#define ALPS_PROTO_V8	8	/* ss4 */
+#define ALPS_PROTO_V9	9	/* ss3btl */
+
+
+#define MAX_IMG_PT_NUM		5
+#define V7_IMG_PT_NUM		2
+
+#define ZONE_NORMAL				0x01
+#define ZONE_RESTING			0x02
+#define ZONE_LEFT_BTN			0x04
+#define ZONE_RIGHT_BTN			0x08
 
 #define DOLPHIN_COUNT_PER_ELECTRODE	64
 #define DOLPHIN_PROFILE_XOFFSET		8	/* x-electrode offset */
 #define DOLPHIN_PROFILE_YOFFSET		1	/* y-electrode offset */
 
+/*
+ * enum V7_PACKET_ID - defines the packet type for V7
+ * V7_PACKET_ID_IDLE: There's no finger and no button activity. 
+ * V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad
+ *  or there's button activities.
+ * V7_PACKET_ID_MULTI: There are at least three non-resting fingers.
+ * V7_PACKET_ID_NEW: The finger position in slot is not continues from 
+ *  previous packet.
+*/
+enum V7_PACKET_ID {
+	 V7_PACKET_ID_IDLE,
+	 V7_PACKET_ID_TWO,
+	 V7_PACKET_ID_MULTI,
+	 V7_PACKET_ID_NEW,
+	 V7_PACKET_ID_UNKNOWN,
+};
+
+enum SS4_PACKET_ID {
+	SS4_PACKET_ID_IDLE = 0,
+	SS4_PACKET_ID_ONE,
+	SS4_PACKET_ID_TWO,
+	SS4_PACKET_ID_MULTI,
+};
+
+#define SS4_COUNT_PER_ELECTRODE			256
+#define SS4_NUMSENSOR_XOFFSET				7
+#define SS4_NUMSENSOR_YOFFSET				7
+#define SS4_BUTTONLESS_BUTTONS	0x01			// SWC status			( 0:SW OFF,	  1: SW ON	) 
+
+#define SS4_MASK_NORMAL_BUTTONS	0x07
+
+#define SS4_1F_X_V1(_b)			( ( ( _b[ 0 ] << 5 ) & 0x1E00 ) | \
+								  ( ( _b[ 0 ] << 6 ) & 0x01C0 ) | \
+								  ( ( _b[ 1 ] >> 2 ) & 0x0038 ) | \
+								  ( ( _b[ 1 ] >> 1 ) & 0x0007 ) \
+								)
+#define SS4_1F_Y_V1(_b)			( ( ( _b[ 2 ] >> 1 ) & 0x007F ) | \
+								  ( ( _b[ 4 ] << 5 ) & 0x0F80 ) \
+								)
+#define SS4_1F_Z_V1(_b)			( _b[ 5 ] & 0xFF )
+
+#define	COSMO_1F_LFB(_b)		( ( _b[ 4 ] >> 1 ) & 0x01 ) 
+
+#define SS4_BTN_V1(_b)		( _b[ 3 ] & SS4_BUTTONLESS_BUTTONS )
+
+#define SS4_STD_MF_X_V1(_b,_i)	( ( ( _b[ 0 + _i * 3 ] >> 2 ) & 0x0020 ) | \
+								  ( ( _b[ 1 + _i * 3 ] << 5 ) & 0x1FC0 ) \
+								)
+
+#define SS4_STD_MF_Y_V1(_b,_i)	( ( ( _b[ 0 + _i * 3 ] >> 2 ) & 0x0010 ) | \
+								  ( ( _b[ 2 + _i * 3 ] << 4 ) & 0x0FE0 ) \
+								)
+
+#define SS4_BTL_BTN_V1(_b)		( _b[ 3 ] &SS4_BUTTONLESS_BUTTONS )
+
+#define SS4_BTL_MF_X_V1(_b,_i) 	( SS4_STD_MF_X_V1(_b,_i) | ( ( _b[ 0 + _i * 3 ] << 2 ) & 0x0010 ) )
+
+#define SS4_BTL_MF_Y_V1(_b,_i)	 ( SS4_STD_MF_Y_V1(_b,_i) | ( ( _b[ 0 + _i * 3 ] << 2 ) & 0x0008 ) )
+
+#define SS4_MF_Z_V1(_b,_i)		( ( _b[ 0 + _i * 3 ] >> 4 ) & 0x0003 )
+
+#define SS4_IS_MF_CONTINUE_V1(_b)	( ( _b[ 0 ] & 0x01 ) == 0x01 )
+#define SS4_IS_5F_DETECTED_V1(_b)	( ( _b[ 0 ] & 0x01 ) == 0x01 )
+
+#define SS4_1F_X_V2(_b)			( (   _b[ 0 ]        & 0x0007 ) | \
+								  ( ( _b[ 1 ] << 3 ) & 0x0078 ) | \
+								  ( ( _b[ 1 ] << 2 ) & 0x0380 ) | \
+								  ( ( _b[ 2 ] << 5 ) & 0x1C00 ) \
+								)
+
+#define SS4_1F_Y_V2(_b)			( ( ( _b[ 2 ]      ) & 0x000F ) | \
+								  ( ( _b[ 3 ] >> 2 ) & 0x0030 ) | \
+								  ( ( _b[ 4 ] << 6 ) & 0x03C0 ) | \
+								  ( ( _b[ 4 ] << 5 ) & 0x0C00 ) \
+								)
+
+#define SS4_1F_Z_V2(_b)			( ( ( _b[ 5 ]      ) & 0x0F   ) | \
+	                              			( ( _b[ 5 ] >> 1 ) & 0x70   ) | \
+								  ( ( _b[ 4 ]      ) & 0x80   ) \
+                                				)
+
+#define	COSMO_1F_LFB_V2(_b)	( ( ( _b[ 2 ] >> 4 ) & 0x01 ) == 0x01 )
+
+#define	COSMO_MF_LF_V2(_b,_i)	( ( _b[ 1 + _i * 3 ] & 0x0004 ) == 0x0004 )
+
+#define SS4_BTN_V2(_b)		( (_b[ 0 ] >> 5 ) & SS4_MASK_NORMAL_BUTTONS )
+#define SS4_STD_MF_X_V2(_b,_i)	( ( ( _b[ 0 + _i * 3 ]  << 5  ) & 0x00E0 ) | \
+								      ( ( _b[ 1 + _i * 3 ]  << 5  ) & 0x1F00 ) \
+								    )
+
+#define SS4_STD_MF_Y_V2(_b,_i)	( ( ( _b[ 1 + _i * 3 ] << 3 ) & 0x0010 ) | \
+								      ( ( _b[ 2 + _i * 3 ] << 5 ) & 0x01E0 ) | \
+								      ( ( _b[ 2 + _i * 3 ] << 4 ) & 0x0E00 ) \
+								    )
+
+#define SS4_BTL_MF_X_V2(_b,_i)	( SS4_STD_MF_X_V2(_b,_i) | ( ( _b[ 0 + _i * 3 ] >> 3 ) & 0x0010 ) )
+
+#define SS4_BTL_MF_Y_V2(_b,_i)	( SS4_STD_MF_Y_V2(_b,_i) | ( ( _b[ 0 + _i * 3 ] >> 3 ) & 0x0008 ) )
+
+#define SS4_MF_Z_V2(_b,_i)		( ( ( _b[ 1 + _i * 3 ]      ) & 0x0001 ) | \
+								  ( ( _b[ 1 + _i * 3 ] >> 1 ) & 0x0002 ) \
+								)
+								
+#define SS4_IS_MF_CONTINUE_V2(_b)	( ( _b[ 2 ] & 0x10 ) == 0x10 )
+#define SS4_IS_5F_DETECTED_V2(_b)	( ( _b[ 2 ] & 0x10 ) == 0x10 )
+
+
+#define SS4_MFPACKET_NO_AX				8160		// 8192-32 = 8160  Standard X-Coordinate value at no-finger 
+#define SS4_MFPACKET_NO_AY				4080		// 4096-16 = 4080  Standard Y-Coordinate value at no-finger 
+#define SS4_MFPACKET_NO_AX_BL			8176		// 8192-16 = 8176  Buttonless X-Coordinate value at no-finger 
+#define SS4_MFPACKET_NO_AY_BL			4088		// 4096-8  = 4088  Buttonless Y-Coordinate value at no-finger
+
+#define RESTING_FN_LARGE_MOVEMENT		50			// Threshold for resting finger's large movement
+#define PT_IN_LEFT_BTN_AREA(_x,_y,_x_max,_y_max)	( ((_x)<(_x_max)/2)&&((_y)>(_y_max)*4/5) )
+#define PT_IN_RIGHT_BTN_AREA(_x,_y,_x_max,_y_max)	( ((_x)>=(_x_max)/2)&&((_y)>(_y_max)*4/5) )
+#define PT_IN_BTN_AREA(_x,_y,_x_max,_y_max)			( PT_IN_LEFT_BTN_AREA(_x,_y,_x_max,_y_max) || PT_IN_RIGHT_BTN_AREA(_x,_y,_x_max,_y_max) )
+
 /**
  * struct alps_model_info - touchpad ID table
  * @signature: E7 response string to match.
@@ -46,7 +175,7 @@  struct alps_model_info {
 	unsigned char command_mode_resp;
 	unsigned char proto_version;
 	unsigned char byte0, mask0;
-	unsigned char flags;
+	unsigned int flags;
 };
 
 /**
@@ -66,15 +195,7 @@  struct alps_nibble_commands {
 };
 
 /**
- * struct alps_fields - decoded version of the report packet
- * @x_map: Bitmap of active X positions for MT.
- * @y_map: Bitmap of active Y positions for MT.
- * @fingers: Number of fingers for MT.
- * @x: X position for ST.
- * @y: Y position for ST.
- * @z: Z position for ST.
- * @first_mp: Packet is the first of a multi-packet report.
- * @is_mp: Packet is part of a multi-packet report.
+ * struct alps_btn - decoded version of the button status
  * @left: Left touchpad button is active.
  * @right: Right touchpad button is active.
  * @middle: Middle touchpad button is active.
@@ -82,16 +203,7 @@  struct alps_nibble_commands {
  * @ts_right: Right trackstick button is active.
  * @ts_middle: Middle trackstick button is active.
  */
-struct alps_fields {
-	unsigned int x_map;
-	unsigned int y_map;
-	unsigned int fingers;
-	unsigned int x;
-	unsigned int y;
-	unsigned int z;
-	unsigned int first_mp:1;
-	unsigned int is_mp:1;
-
+struct alps_btn {
 	unsigned int left:1;
 	unsigned int right:1;
 	unsigned int middle:1;
@@ -102,6 +214,79 @@  struct alps_fields {
 };
 
 /**
+ * struct alps_btn - decoded version of the X Y Z postion for ST.
+ * @x: X position for ST.
+ * @y: Y position for ST.
+ * @z: Z position for ST.
+ */
+struct alps_abs_data {
+	unsigned int x;
+	unsigned int y;
+	unsigned int z;
+};
+
+typedef enum _DOL_PACKET_TYPE
+{
+	DOL_UNKNOWN,
+	DOL_GPDATA,
+	DOL_PROFDATA,
+	DOL_APDATA
+}DOL_PACKET_TYPE;
+
+/**
+ * struct alps_fields - decoded version of the report packet
+ * @fingers: Number of fingers for MT.
+ * @pt: X Y Z postion for ST.
+ * @pt: X Y Z postion for image MT.
+ * @x_map: Bitmap of active X positions for MT.
+ * @y_map: Bitmap of active Y positions for MT.
+ * @first_mp: Packet is the first of a multi-packet report.
+ * @is_mp: Packet is part of a multi-packet report.
+ * @btn: Button activity status
+ */
+struct alps_fields {
+	unsigned int fingers;
+	struct alps_abs_data pt;
+	struct alps_abs_data pt_img[MAX_IMG_PT_NUM];
+	unsigned int x_map;
+	unsigned int y_map;
+	unsigned int large_fn;
+	unsigned int first_mp:1;
+	unsigned int is_mp:1;
+	DOL_PACKET_TYPE	dol_packet_type;
+	struct alps_btn btn;
+};
+
+/**
+ * struct v7_raw - data decoded from raw packet for V7.
+ * @pkt_id: An id that specifies the type of packet.
+ * @additional_fingers: Number of additional finger that is neighter included
+ *  in pt slot nor reflected in rest_left and rest_right flag of data packet.
+ * @rest_left: There are fingers on left resting zone.
+ * @rest_right: There are fingers on right resting zone.
+ */
+struct v7_raw {
+	unsigned char pkt_id;
+	unsigned int additional_fingers;
+	unsigned char rest_left;
+	unsigned char rest_right;
+};
+
+/**
+ * struct alps_bl_pt_attr - generic attributes of touch points for buttonless device
+ * @zone: The part of touchpad that the touch point locates
+ * @is_counted: The touch point is not a resting finger.
+ * @is_init_pt_got: The touch down point is got.
+ * @init_pt: The X Y Z position of the touch down point.
+ */
+struct alps_bl_pt_attr {
+	unsigned char zone;
+	unsigned char is_counted;
+	unsigned char is_init_pt_got;
+	struct alps_abs_data init_pt;
+};
+
+/**
  * struct alps_data - private data structure for the ALPS driver
  * @dev2: "Relative" device used to report trackstick or mouse activity.
  * @phys: Physical path for the relative device.
@@ -116,8 +301,10 @@  struct alps_fields {
  * @flags: Additional device capabilities (passthrough port, trackstick, etc.).
  * @x_max: Largest possible X position value.
  * @y_max: Largest possible Y position value.
+ * @resting_zone_y_min: Smallest Y postion value of the bottom resting zone.
  * @x_bits: Number of X bits in the MT bitmap.
  * @y_bits: Number of Y bits in the MT bitmap.
+ * @img_fingers: Number of image fingers.
  * @hw_init: Protocol-specific hardware init function.
  * @process_packet: Protocol-specific function to process a report packet.
  * @decode_fields: Protocol-specific function to read packet bitfields.
@@ -132,6 +319,11 @@  struct alps_fields {
  * @fingers: Number of fingers from last MT report.
  * @quirks: Bitmap of ALPS_QUIRK_*.
  * @timer: Timer for flushing out the final report packet in the stream.
+ * @v7: Data decoded from raw packet for V7
+ * @phy_btn: Physical button is active.
+ * @prev_phy_btn: Physical button of previous packet is active.
+ * @pressed_btn_bits: Pressed positon of button zone
+ * @pt_attr: Generic attributes of touch points for buttonless device.
  */
 struct alps_data {
 	struct input_dev *dev2;
@@ -142,11 +334,14 @@  struct alps_data {
 	int addr_command;
 	unsigned char proto_version;
 	unsigned char byte0, mask0;
-	unsigned char flags;
+	unsigned int flags;
+	unsigned char fw_ver[3];
 	int x_max;
 	int y_max;
+	int resting_zone_y_min;
 	int x_bits;
 	int y_bits;
+	unsigned char slot_number;
 
 	int (*hw_init)(struct psmouse *psmouse);
 	void (*process_packet)(struct psmouse *psmouse);
@@ -161,6 +356,13 @@  struct alps_data {
 	int fingers;
 	u8 quirks;
 	struct timer_list timer;
+
+	/* these are used for buttonless touchpad*/
+	union {
+		struct v7_raw v7;
+	} r;
+	struct alps_btn prev_btn;
+	struct alps_bl_pt_attr pt_attr[MAX_IMG_PT_NUM];
 };
 
 #define ALPS_QUIRK_TRACKSTICK_BUTTONS	1 /* trakcstick buttons in trackstick packet */