lifebook.c 7.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * Fujitsu B-series Lifebook PS/2 TouchScreen driver
 *
 * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
 * Copyright (c) 2005 Kenan Esau <kenan.esau@conan.de>
 *
 * TouchScreen detection, absolute mode setting and packet layout is taken from
 * Harald Hoyer's description of the device.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 */

#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include <linux/dmi.h>

#include "psmouse.h"
#include "lifebook.h"

23 24 25 26 27
struct lifebook_data {
	struct input_dev *dev2;		/* Relative device */
	char phys[32];
};

28 29 30 31 32 33 34 35
static const char *desired_serio_phys;

static int lifebook_set_serio_phys(struct dmi_system_id *d)
{
	desired_serio_phys = d->driver_data;
	return 0;
}

36 37 38 39 40 41 42 43
static unsigned char lifebook_use_6byte_proto;

static int lifebook_set_6byte_proto(struct dmi_system_id *d)
{
	lifebook_use_6byte_proto = 1;
	return 0;
}

44
static struct dmi_system_id lifebook_dmi_table[] = {
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
	{
		.ident = "FLORA-ie 55mi",
		.matches = {
			DMI_MATCH(DMI_PRODUCT_NAME, "FLORA-ie 55mi"),
		},
	},
	{
		.ident = "LifeBook B",
		.matches = {
			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B Series"),
		},
	},
	{
		.ident = "Lifebook B",
		.matches = {
			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"),
		},
	},
	{
		.ident = "Lifebook B213x/B2150",
		.matches = {
			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"),
		},
	},
	{
		.ident = "Zephyr",
		.matches = {
			DMI_MATCH(DMI_PRODUCT_NAME, "ZEPHYR"),
		},
	},
	{
		.ident = "CF-18",
		.matches = {
			DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"),
		},
80 81
		.callback = lifebook_set_serio_phys,
		.driver_data = "isa0060/serio3",
82
	},
83 84 85 86 87 88 89 90
	{
		.ident = "Panasonic CF-28",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
			DMI_MATCH(DMI_PRODUCT_NAME, "CF-28"),
		},
		.callback = lifebook_set_6byte_proto,
	},
91 92 93 94 95 96 97 98
	{
		.ident = "Panasonic CF-29",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
			DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
		},
		.callback = lifebook_set_6byte_proto,
	},
99 100 101 102 103 104 105
	{
		.ident = "Lifebook B142",
		.matches = {
			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"),
		},
	},
	{ }
106 107
};

108
static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
109
{
110 111
	struct lifebook_data *priv = psmouse->private;
	struct input_dev *dev1 = psmouse->dev;
112
	struct input_dev *dev2 = priv ? priv->dev2 : NULL;
113 114
	unsigned char *packet = psmouse->packet;
	int relative_packet = packet[0] & 0x08;
115

116 117 118
	if (relative_packet || !lifebook_use_6byte_proto) {
		if (psmouse->pktcnt != 3)
			return PSMOUSE_GOOD_DATA;
119
	} else {
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
		switch (psmouse->pktcnt) {
		case 1:
			return (packet[0] & 0xf8) == 0x00 ?
				PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
		case 2:
			return PSMOUSE_GOOD_DATA;
		case 3:
			return ((packet[2] & 0x30) << 2) == (packet[2] & 0xc0) ?
				PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
		case 4:
			return (packet[3] & 0xf8) == 0xc0 ?
				PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
		case 5:
			return (packet[4] & 0xc0) == (packet[2] & 0xc0) ?
				PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
		case 6:
			if (((packet[5] & 0x30) << 2) != (packet[5] & 0xc0))
				return PSMOUSE_BAD_DATA;
			if ((packet[5] & 0xc0) != (packet[1] & 0xc0))
				return PSMOUSE_BAD_DATA;
			break; /* report data */
		}
	}

	if (relative_packet) {
145 146 147
		if (!dev2)
			printk(KERN_WARNING "lifebook.c: got relative packet "
				"but no relative device set up\n");
148
	} else if (lifebook_use_6byte_proto) {
149
		input_report_abs(dev1, ABS_X,
150
				 ((packet[1] & 0x3f) << 6) | (packet[2] & 0x3f));
151
		input_report_abs(dev1, ABS_Y,
152 153
				 4096 - (((packet[4] & 0x3f) << 6) | (packet[5] & 0x3f)));
	} else {
154
		input_report_abs(dev1, ABS_X,
155
				 (packet[1] | ((packet[0] & 0x30) << 4)));
156
		input_report_abs(dev1, ABS_Y,
157
				 1024 - (packet[2] | ((packet[0] & 0xC0) << 2)));
158 159
	}

160 161
	input_report_key(dev1, BTN_TOUCH, packet[0] & 0x04);
	input_sync(dev1);
162

163 164 165 166 167 168 169 170 171 172 173
	if (dev2) {
		if (relative_packet) {
			input_report_rel(dev2, REL_X,
				((packet[0] & 0x10) ? packet[1] - 256 : packet[1]));
			input_report_rel(dev2, REL_Y,
				 -(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2]));
		}
		input_report_key(dev2, BTN_LEFT, packet[0] & 0x01);
		input_report_key(dev2, BTN_RIGHT, packet[0] & 0x02);
		input_sync(dev2);
	}
174 175 176 177

	return PSMOUSE_FULL_PACKET;
}

178
static int lifebook_absolute_mode(struct psmouse *psmouse)
179 180 181 182
{
	struct ps2dev *ps2dev = &psmouse->ps2dev;
	unsigned char param;

183
	if (psmouse_reset(psmouse))
184 185
		return -1;

186
	/*
187 188 189
	   Enable absolute output -- ps2_command fails always but if
	   you leave this call out the touchsreen will never send
	   absolute coordinates
190
	*/
191
	param = lifebook_use_6byte_proto ? 0x08 : 0x07;
192 193 194 195 196
	ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);

	return 0;
}

197 198 199 200 201 202 203 204
static void lifebook_relative_mode(struct psmouse *psmouse)
{
	struct ps2dev *ps2dev = &psmouse->ps2dev;
	unsigned char param = 0x06;

	ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
}

205 206
static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{
H
Helge Deller 已提交
207 208
	static const unsigned char params[] = { 0, 1, 2, 2, 3 };
	unsigned char p;
209 210 211 212

	if (resolution == 0 || resolution > 400)
		resolution = 400;

H
Helge Deller 已提交
213 214 215
	p = params[resolution / 100];
	ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES);
	psmouse->resolution = 50 << p;
216 217
}

218 219 220
static void lifebook_disconnect(struct psmouse *psmouse)
{
	psmouse_reset(psmouse);
221 222
	kfree(psmouse->private);
	psmouse->private = NULL;
223 224
}

225
int lifebook_detect(struct psmouse *psmouse, int set_properties)
226
{
227
        if (!dmi_check_system(lifebook_dmi_table))
228 229
                return -1;

230 231 232 233
	if (desired_serio_phys &&
	    strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys))
		return -1;

234
	if (set_properties) {
235 236
		psmouse->vendor = "Fujitsu";
		psmouse->name = "Lifebook TouchScreen";
237 238
	}

239
        return 0;
240
}
241

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
static int lifebook_create_relative_device(struct psmouse *psmouse)
{
	struct input_dev *dev2;
	struct lifebook_data *priv;
	int error = -ENOMEM;

	priv = kzalloc(sizeof(struct lifebook_data), GFP_KERNEL);
	dev2 = input_allocate_device();
	if (!priv || !dev2)
		goto err_out;

	priv->dev2 = dev2;
	snprintf(priv->phys, sizeof(priv->phys),
		 "%s/input1", psmouse->ps2dev.serio->phys);

	dev2->phys = priv->phys;
	dev2->name = "PS/2 Touchpad";
	dev2->id.bustype = BUS_I8042;
	dev2->id.vendor  = 0x0002;
	dev2->id.product = PSMOUSE_LIFEBOOK;
	dev2->id.version = 0x0000;
	dev2->dev.parent = &psmouse->ps2dev.serio->dev;

	dev2->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
	dev2->relbit[LONG(REL_X)] = BIT(REL_X) | BIT(REL_Y);
	dev2->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);

	error = input_register_device(priv->dev2);
	if (error)
		goto err_out;

	psmouse->private = priv;
	return 0;

 err_out:
	input_free_device(dev2);
	kfree(priv);
	return error;
}

282 283
int lifebook_init(struct psmouse *psmouse)
{
284
	struct input_dev *dev1 = psmouse->dev;
285
	int max_coord = lifebook_use_6byte_proto ? 1024 : 4096;
286

287 288 289
	if (lifebook_absolute_mode(psmouse))
		return -1;

290 291 292 293 294 295 296 297 298 299 300 301
	dev1->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
	dev1->relbit[0] = 0;
	dev1->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
	input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0);
	input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0);

	if (!desired_serio_phys) {
		if (lifebook_create_relative_device(psmouse)) {
			lifebook_relative_mode(psmouse);
			return -1;
		}
	}
302 303

	psmouse->protocol_handler = lifebook_process_byte;
304
	psmouse->set_resolution = lifebook_set_resolution;
305 306
	psmouse->disconnect = lifebook_disconnect;
	psmouse->reconnect  = lifebook_absolute_mode;
307

308 309
	psmouse->model = lifebook_use_6byte_proto ? 6 : 3;

310 311 312 313
	/*
	 * Use packet size = 3 even when using 6-byte protocol because
	 * that's what POLL will return on Lifebooks (according to spec).
	 */
314 315 316 317 318
	psmouse->pktsize = 3;

	return 0;
}