static unsigned char param = 0xc8;
struct synaptics_data *priv = psmouse->private;
- if (!SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
+ if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
+ SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)))
return 0;
if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
* Functions to interpret the absolute mode packets
****************************************************************************/
+static void synaptics_parse_agm(const unsigned char buf[],
+ struct synaptics_data *priv)
+{
+ struct synaptics_hw_state *agm = &priv->agm;
+
+ /* Gesture packet: (x, y, z) at half resolution */
+ agm->x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
+ agm->y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
+ agm->z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
+}
+
static int synaptics_parse_hw_state(const unsigned char buf[],
struct synaptics_data *priv,
struct synaptics_hw_state *hw)
hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
}
- if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
- /* Gesture packet: (x, y, z) at half resolution */
- priv->mt.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
- priv->mt.y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
- priv->mt.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
+ if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
+ SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) &&
+ hw->w == 2) {
+ synaptics_parse_agm(buf, priv);
return 1;
}
}
}
+static void synaptics_report_buttons(struct psmouse *psmouse,
+ const struct synaptics_hw_state *hw)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct synaptics_data *priv = psmouse->private;
+ int i;
+
+ input_report_key(dev, BTN_LEFT, hw->left);
+ input_report_key(dev, BTN_RIGHT, hw->right);
+
+ if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+ input_report_key(dev, BTN_MIDDLE, hw->middle);
+
+ if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+ input_report_key(dev, BTN_FORWARD, hw->up);
+ input_report_key(dev, BTN_BACK, hw->down);
+ }
+
+ for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+ input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i));
+}
+
+static void synaptics_report_slot(struct input_dev *dev, int slot,
+ const struct synaptics_hw_state *hw)
+{
+ input_mt_slot(dev, slot);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, (hw != NULL));
+ if (!hw)
+ return;
+
+ input_report_abs(dev, ABS_MT_POSITION_X, hw->x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(hw->y));
+ input_report_abs(dev, ABS_MT_PRESSURE, hw->z);
+}
+
+static void synaptics_report_mt_data(struct psmouse *psmouse,
+ int count,
+ const struct synaptics_hw_state *sgm)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct synaptics_data *priv = psmouse->private;
+ struct synaptics_hw_state *agm = &priv->agm;
+
+ switch (count) {
+ case 0:
+ synaptics_report_slot(dev, 0, NULL);
+ synaptics_report_slot(dev, 1, NULL);
+ break;
+ case 1:
+ synaptics_report_slot(dev, 0, sgm);
+ synaptics_report_slot(dev, 1, NULL);
+ break;
+ case 2:
+ case 3: /* Fall-through case */
+ synaptics_report_slot(dev, 0, sgm);
+ synaptics_report_slot(dev, 1, agm);
+ break;
+ }
+
+ /* Don't use active slot count to generate BTN_TOOL events. */
+ input_mt_report_pointer_emulation(dev, false);
+
+ /* Send the number of fingers reported by touchpad itself. */
+ input_mt_report_finger_count(dev, count);
+
+ synaptics_report_buttons(psmouse, sgm);
+
+ input_sync(dev);
+}
+
+static void synaptics_image_sensor_process(struct psmouse *psmouse,
+ struct synaptics_hw_state *sgm)
+{
+ int count;
+
+ if (sgm->z == 0)
+ count = 0;
+ else if (sgm->w >= 4)
+ count = 1;
+ else if (sgm->w == 0)
+ count = 2;
+ else
+ count = 3;
+
+ /* Send resulting input events to user space */
+ synaptics_report_mt_data(psmouse, count, sgm);
+}
+
/*
* called for each full received packet from the touchpad
*/
struct synaptics_hw_state hw;
int num_fingers;
int finger_width;
- int i;
if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
return;
+ if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+ synaptics_image_sensor_process(psmouse, &hw);
+ return;
+ }
+
if (hw.scroll) {
priv->scroll += hw.scroll;
}
if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
- synaptics_report_semi_mt_data(dev, &hw, &priv->mt, num_fingers);
+ synaptics_report_semi_mt_data(dev, &hw, &priv->agm,
+ num_fingers);
/* Post events
* BTN_TOUCH has to be first as mousedev relies on it when doing
input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
- input_report_key(dev, BTN_LEFT, hw.left);
- input_report_key(dev, BTN_RIGHT, hw.right);
-
if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
}
- if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
- input_report_key(dev, BTN_MIDDLE, hw.middle);
-
- if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
- input_report_key(dev, BTN_FORWARD, hw.up);
- input_report_key(dev, BTN_BACK, hw.down);
- }
-
- for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
- input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i));
+ synaptics_report_buttons(psmouse, &hw);
input_sync(dev);
}
/*****************************************************************************
* Driver initialization/cleanup functions
****************************************************************************/
-static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
+static void set_abs_position_params(struct input_dev *dev,
+ struct synaptics_data *priv, int x_code,
+ int y_code)
{
- int i;
+ int x_min = priv->x_min ?: XMIN_NOMINAL;
+ int x_max = priv->x_max ?: XMAX_NOMINAL;
+ int y_min = priv->y_min ?: YMIN_NOMINAL;
+ int y_max = priv->y_max ?: YMAX_NOMINAL;
int fuzz = SYN_CAP_REDUCED_FILTERING(priv->ext_cap_0c) ?
SYN_REDUCED_FILTER_FUZZ : 0;
+ input_set_abs_params(dev, x_code, x_min, x_max, fuzz, 0);
+ input_set_abs_params(dev, y_code, y_min, y_max, fuzz, 0);
+ input_abs_set_res(dev, x_code, priv->x_res);
+ input_abs_set_res(dev, y_code, priv->y_res);
+}
+
+static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
+{
+ int i;
+
__set_bit(INPUT_PROP_POINTER, dev->propbit);
__set_bit(EV_ABS, dev->evbit);
- input_set_abs_params(dev, ABS_X,
- priv->x_min ?: XMIN_NOMINAL,
- priv->x_max ?: XMAX_NOMINAL,
- fuzz, 0);
- input_set_abs_params(dev, ABS_Y,
- priv->y_min ?: YMIN_NOMINAL,
- priv->y_max ?: YMAX_NOMINAL,
- fuzz, 0);
+ set_abs_position_params(dev, priv, ABS_X, ABS_Y);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
- if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
+ if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+ input_mt_init_slots(dev, 2);
+ set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
+ ABS_MT_POSITION_Y);
+ /* Image sensors can report per-contact pressure */
+ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+ } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
+ /* Non-image sensors with AGM use semi-mt */
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
input_mt_init_slots(dev, 2);
- input_set_abs_params(dev, ABS_MT_POSITION_X,
- priv->x_min ?: XMIN_NOMINAL,
- priv->x_max ?: XMAX_NOMINAL,
- fuzz, 0);
- input_set_abs_params(dev, ABS_MT_POSITION_Y,
- priv->y_min ?: YMIN_NOMINAL,
- priv->y_max ?: YMAX_NOMINAL,
- fuzz, 0);
-
- input_abs_set_res(dev, ABS_MT_POSITION_X, priv->x_res);
- input_abs_set_res(dev, ABS_MT_POSITION_Y, priv->y_res);
+ set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
+ ABS_MT_POSITION_Y);
}
if (SYN_CAP_PALMDETECT(priv->capabilities))
__clear_bit(REL_X, dev->relbit);
__clear_bit(REL_Y, dev->relbit);
- input_abs_set_res(dev, ABS_X, priv->x_res);
- input_abs_set_res(dev, ABS_Y, priv->y_res);
-
if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
/* Clickpads report only left button */