Home | History | Annotate | Line # | Download | only in pckbport
alps.c revision 1.12
      1 /* $NetBSD: alps.c,v 1.12 2019/05/28 08:59:35 msaitoh Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2017 Ryo ONODERA <ryo (at) tetera.org>
      5  * Copyright (c) 2008 Jared D. McNeill <jmcneill (at) invisible.ca>
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     27  * POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 #include "opt_pms.h"
     31 
     32 #include <sys/cdefs.h>
     33 __KERNEL_RCSID(0, "$NetBSD: alps.c,v 1.12 2019/05/28 08:59:35 msaitoh Exp $");
     34 
     35 #include <sys/param.h>
     36 #include <sys/systm.h>
     37 #include <sys/device.h>
     38 #include <sys/kernel.h>
     39 #include <sys/sysctl.h>
     40 #include <sys/bus.h>
     41 #include <sys/bitops.h>
     42 
     43 #include <dev/wscons/wsconsio.h>
     44 #include <dev/wscons/wsmousevar.h>
     45 
     46 #include <dev/pckbport/pckbportvar.h>
     47 #include <dev/pckbport/pmsreg.h>
     48 #include <dev/pckbport/pmsvar.h>
     49 #include <dev/pckbport/alpsreg.h>
     50 #include <dev/pckbport/alpsvar.h>
     51 
     52 /* #define ALPS_DEBUG */
     53 
     54 static int alps_touchpad_xy_unprecision_nodenum;
     55 static int alps_trackstick_xy_precision_nodenum;
     56 
     57 static int alps_touchpad_xy_unprecision = 2;
     58 static int alps_trackstick_xy_precision = 1;
     59 
     60 static void pms_alps_input_v7(void *, int);
     61 static void pms_alps_input_v2(void *, int);
     62 
     63 static int
     64 pms_sysctl_alps_verify(SYSCTLFN_ARGS)
     65 {
     66 	int error, t;
     67 	struct sysctlnode node;
     68 
     69 	node = *rnode;
     70 	t = *(int *)rnode->sysctl_data;
     71 	node.sysctl_data = &t;
     72 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
     73 	if (error || newp == NULL)
     74 		return error;
     75 
     76 	if (node.sysctl_num == alps_touchpad_xy_unprecision_nodenum ||
     77 		node.sysctl_num == alps_trackstick_xy_precision_nodenum) {
     78 		if (t < 0 || t > 7)
     79 			return EINVAL;
     80 	} else
     81 		return EINVAL;
     82 
     83 	*(int *)rnode->sysctl_data = t;
     84 
     85 	return 0;
     86 
     87 }
     88 
     89 static void
     90 pms_sysctl_alps(struct sysctllog **clog)
     91 {
     92 	const struct sysctlnode *node;
     93 	int rc, root_num;
     94 
     95 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
     96 		CTLFLAG_PERMANENT, CTLTYPE_NODE, "alps",
     97 		SYSCTL_DESCR("ALPS touchpad controls"),
     98 		NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0)
     99 		goto err;
    100 
    101 	root_num = node->sysctl_num;
    102 
    103 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    104 		CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    105 		CTLTYPE_INT, "touchpad_xy_precision_shift",
    106 		SYSCTL_DESCR("Touchpad X/Y-axis precision shift value"),
    107 		pms_sysctl_alps_verify, 0,
    108 		&alps_touchpad_xy_unprecision,
    109 		0, CTL_HW, root_num, CTL_CREATE,
    110 		CTL_EOL)) != 0)
    111 			goto err;
    112 	alps_touchpad_xy_unprecision_nodenum = node->sysctl_num;
    113 
    114 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    115 		CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    116 		CTLTYPE_INT, "tackstick_xy_precision_shift",
    117 		SYSCTL_DESCR("Trackstick X/Y-axis precision value"),
    118 		pms_sysctl_alps_verify, 0,
    119 		&alps_trackstick_xy_precision,
    120 		0, CTL_HW, root_num, CTL_CREATE,
    121 		CTL_EOL)) != 0)
    122 			goto err;
    123 	alps_trackstick_xy_precision_nodenum = node->sysctl_num;
    124 
    125 	return;
    126 
    127 err:
    128 	aprint_error("%s: sysctl_createv failed (rc = %d)\n",
    129 		__func__, rc);
    130 }
    131 
    132 /*
    133  * Publish E6 report command and get E6 signature,
    134  * then check the signature
    135  */
    136 static int
    137 pms_alps_e6sig(struct pms_softc *psc, uint8_t *e6sig)
    138 {
    139 	uint8_t cmd[2];
    140 	int res;
    141 
    142 	e6sig[0] = 0;
    143 	cmd[0] = PMS_SET_RES; /* E8 */
    144 	cmd[1] = 0;
    145 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    146 	    cmd, 2, 0, NULL, 0)) != 0)
    147 		goto err;
    148 	cmd[0] = PMS_SET_SCALE11; /* E6 */
    149 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    150 	    cmd, 1, 0, NULL, 0)) != 0)
    151 		goto err;
    152 	cmd[0] = PMS_SET_SCALE11; /* E6 */
    153 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    154 	    cmd, 1, 0, NULL, 0)) != 0)
    155 		goto err;
    156 	cmd[0] = PMS_SET_SCALE11; /* E6 */
    157 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    158 	    cmd, 1, 0, NULL, 0)) != 0)
    159 		goto err;
    160 	e6sig[0] = e6sig[1] = e6sig[2] = 0;
    161 	/* Get E6 signature */
    162 	cmd[0] = PMS_SEND_DEV_STATUS; /* E9 */
    163 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    164 	    cmd, 1, 3, e6sig, 0)) != 0)
    165 		goto err;
    166 
    167 	/* ALPS input device returns 00-00-64 as E6 signature */
    168 	if ((e6sig[0] & ~0x07u) != 0x00 || /* ignore buttons */
    169 	    e6sig[1] != 0x00 ||
    170 	    e6sig[2] != 0x64)
    171 	{
    172 		return ENODEV;	/* This is not an ALPS device */
    173 	}
    174 
    175 	aprint_debug_dev(psc->sc_dev,
    176 		"ALPS PS/2 E6 signature: 0x%X 0x%X 0x%X\n",
    177 		e6sig[0], e6sig[1], e6sig[2]);
    178 
    179 	return 0;
    180 err:
    181 	aprint_error_dev(psc->sc_dev, "Failed to get E6 signature.\n");
    182 	return res;
    183 }
    184 
    185 /*
    186  * Publish E7 report command and get E7 signature
    187  */
    188 static int
    189 pms_alps_e7sig(struct pms_softc *psc, uint8_t *e7sig)
    190 {
    191 	uint8_t cmd[2];
    192 	int res;
    193 
    194 	cmd[0] = PMS_SET_RES; /* E8 */
    195 	cmd[1] = 0;
    196 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    197 	    cmd, 2, 0, NULL, 0)) != 0)
    198 		goto err;
    199 	cmd[0] = PMS_SET_SCALE21; /* E7 */
    200 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    201 	    cmd, 1, 0, NULL, 0)) != 0)
    202 		goto err;
    203 	cmd[0] = PMS_SET_SCALE21; /* E7 */
    204 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    205 	    cmd, 1, 0, NULL, 0)) != 0)
    206 		goto err;
    207 	cmd[0] = PMS_SET_SCALE21; /* E7 */
    208 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    209 	    cmd, 1, 0, NULL, 0)) != 0)
    210 		goto err;
    211 	e7sig[0] = e7sig[1] = e7sig[2] = 0;
    212 	cmd[0] = PMS_SEND_DEV_STATUS; /* E9 */
    213 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    214 	    cmd, 1, 3, e7sig, 0)) != 0)
    215 		goto err;
    216 
    217 	aprint_debug_dev(psc->sc_dev,
    218 		"ALPS PS/2 E7 signature: 0x%X 0x%X 0x%X\n",
    219 		e7sig[0], e7sig[1], e7sig[2]);
    220 
    221 	if (e7sig[0] != 0x73)
    222 		return ENODEV;	/* This is not an ALPS device */
    223 
    224 	return 0;
    225 err:
    226 	aprint_error_dev(psc->sc_dev, "Failed to get E7 signature.\n");
    227 	return res;
    228 }
    229 
    230 /*
    231  * Publish EC command and get EC signature
    232  */
    233 static int
    234 pms_alps_ecsig(struct pms_softc *psc, uint8_t *ecsig)
    235 {
    236 	uint8_t cmd[2];
    237 	int res;
    238 
    239 	cmd[0] = PMS_SET_RES; /* E8 */
    240 	cmd[1] = 0;
    241 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    242 	    cmd, 2, 0, NULL, 0)) != 0)
    243 		goto err;
    244 	cmd[0] = PMS_RESET_WRAP_MODE; /* EC */
    245 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    246 	    cmd, 1, 0, NULL, 0)) != 0)
    247 		goto err;
    248 	cmd[0] = PMS_RESET_WRAP_MODE; /* EC */
    249 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    250 	    cmd, 1, 0, NULL, 0)) != 0)
    251 		goto err;
    252 	cmd[0] = PMS_RESET_WRAP_MODE; /* EC */
    253 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    254 	    cmd, 1, 0, NULL, 0)) != 0)
    255 		goto err;
    256 	ecsig[0] = ecsig[1] = ecsig[2] = 0;
    257 	cmd[0] = PMS_SEND_DEV_STATUS; /* E9 */
    258 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    259 	    cmd, 1, 3, ecsig, 0)) != 0)
    260 		goto err;
    261 
    262 	aprint_debug_dev(psc->sc_dev,
    263 		"ALPS PS/2 EC signature: 0x%X 0x%X 0x%X\n",
    264 		ecsig[0], ecsig[1], ecsig[2]);
    265 
    266 	return 0;
    267 
    268 err:
    269 	aprint_debug_dev(psc->sc_dev, "Failed to get EC signature.\n");
    270 	return res;
    271 }
    272 
    273 /*
    274  * Enter to command mode
    275  */
    276 static int
    277 pms_alps_start_command_mode(struct pms_softc *psc)
    278 {
    279 	uint8_t cmd[1];
    280 	uint8_t resp[3];
    281 	int res;
    282 
    283 	cmd[0] = PMS_RESET_WRAP_MODE; /* EC */
    284 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    285 	    cmd, 1, 0, NULL, 0)) != 0)
    286 		goto err;
    287 	cmd[0] = PMS_RESET_WRAP_MODE; /* EC */
    288 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    289 	    cmd, 1, 0, NULL, 0)) != 0)
    290 		goto err;
    291 	cmd[0] = PMS_RESET_WRAP_MODE; /* EC */
    292 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    293 	    cmd, 1, 0, NULL, 0)) != 0)
    294 		goto err;
    295 	resp[0] = resp[1] = resp[2] = 0;
    296 	cmd[0] = PMS_SEND_DEV_STATUS; /* E9 */
    297 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    298 	    cmd, 1, 3, resp, 0)) != 0)
    299 		goto err;
    300 
    301 	aprint_debug_dev(psc->sc_dev, "ALPS Firmware ID: 0x%x 0x%X 0x%X\n",
    302 		resp[0], resp[1], resp[2]);
    303 
    304 	if (resp[0] != 0x88 || (resp[1] & __BITS(7, 4)) != 0xb0)
    305 		return EINVAL;
    306 
    307 	return 0;
    308 err:
    309 	aprint_error_dev(psc->sc_dev, "Failed to start command mode.\n");
    310 	return res;
    311 }
    312 
    313 /*
    314  * End command mode
    315  */
    316 static int
    317 pms_alps_end_command_mode(struct pms_softc *psc)
    318 {
    319 	int res;
    320 	uint8_t cmd[1];
    321 
    322 	cmd[0] = PMS_SET_STREAM; /* EA */
    323 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    324 	    cmd, 1, 0, NULL, 0)) != 0)
    325 		goto err;
    326 
    327 	return res;
    328 
    329 err:
    330 	aprint_error_dev(psc->sc_dev, "Failed to end command mode.\n");
    331 	return res;
    332 }
    333 
    334 /*
    335  * Write nibble (4-bit) data
    336  */
    337 static int
    338 pms_alps_cm_write_nibble(pckbport_tag_t tag, pckbport_slot_t slot, uint8_t nibble)
    339 {
    340 	uint8_t cmd[2];
    341 	uint8_t resp[3];
    342 	int sendparam;
    343 	int receive;
    344 	int res;
    345 
    346 	sendparam = alps_v7_nibble_command_data_arr[nibble].sendparam;
    347 	receive= alps_v7_nibble_command_data_arr[nibble].receive;
    348 	cmd[0] = alps_v7_nibble_command_data_arr[nibble].command;
    349 	if (receive) {
    350 		if ((res = pckbport_poll_cmd(tag, slot, cmd, 1, 3, resp, 0)) != 0) {
    351 			aprint_error("send nibble error: %d\n", res);
    352 		}
    353 	} else if (sendparam) {
    354 		cmd[1] = alps_v7_nibble_command_data_arr[nibble].data;
    355 		if ((res = pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0)) != 0) {
    356 			aprint_error("send nibble error: %d\n", res);
    357 		}
    358 	} else {
    359 		if ((res = pckbport_poll_cmd(tag, slot, cmd, 1, 0, NULL, 0)) != 0) {
    360 			aprint_error("send nibble error: %d\n", res);
    361 		}
    362 	}
    363 
    364 	return res;
    365 }
    366 
    367 /*
    368  * Set an register address for read and write
    369  */
    370 static int
    371 pms_alps_set_address(pckbport_tag_t tag, pckbport_slot_t slot, uint16_t reg)
    372 {
    373 	uint8_t cmd[1];
    374 	uint8_t nibble;
    375 	int res;
    376 
    377 	cmd[0] = PMS_RESET_WRAP_MODE;
    378 	if ((res = pckbport_poll_cmd(tag, slot, cmd, 1, 0, NULL, 0)) != 0)
    379 		goto err;
    380 
    381 	/* Set address */
    382 	nibble = (reg >> 12) & __BITS(3, 0);
    383 	if ((res = pms_alps_cm_write_nibble(tag, slot, nibble)) != 0) {
    384 		goto err;
    385 	}
    386 	nibble = (reg >> 8) & __BITS(3, 0);
    387 	if ((res = pms_alps_cm_write_nibble(tag, slot, nibble)) != 0) {
    388 		goto err;
    389 	}
    390 	nibble = (reg >> 4) & __BITS(3, 0);
    391 	if ((res = pms_alps_cm_write_nibble(tag, slot, nibble)) != 0) {
    392 		goto err;
    393 	}
    394 	nibble = (reg >> 0) & __BITS(3, 0);
    395 	if ((res = pms_alps_cm_write_nibble(tag, slot, nibble)) != 0) {
    396 		goto err;
    397 	}
    398 	return res;
    399 
    400 err:
    401 	aprint_error("Failed to set addess.\n");
    402 	return res;
    403 }
    404 
    405 /*
    406  * Read one byte from register
    407  */
    408 static int
    409 pms_alps_cm_read_1(pckbport_tag_t tag, pckbport_slot_t slot, uint16_t reg,
    410 	uint8_t *val)
    411 {
    412 	uint8_t cmd[1];
    413 	uint8_t resp[3];
    414 	int res;
    415 
    416 	if ((res = pms_alps_set_address(tag, slot, reg)) != 0)
    417 		goto err;
    418 
    419 	cmd[0] = PMS_SEND_DEV_STATUS;
    420 	if ((res = pckbport_poll_cmd(tag, slot,
    421 	    cmd, 1, 3, resp, 0)) != 0) {
    422 		goto err;
    423 	}
    424 
    425 	if (reg != ((resp[0] << 8) | resp[1])) {
    426 		return EINVAL;
    427 	}
    428 
    429 	*val = resp[2];
    430 	return res;
    431 
    432 err:
    433 	aprint_error("Failed to read a value.\n");
    434 	*val = 0;
    435 	return res;
    436 }
    437 
    438 /*
    439  * Write one byte to register
    440  */
    441 static int
    442 pms_alps_cm_write_1(pckbport_tag_t tag, pckbport_slot_t slot, uint16_t reg,
    443 	uint8_t val)
    444 {
    445 	uint8_t nibble;
    446 	int res;
    447 
    448 	if ((res = pms_alps_set_address(tag, slot, reg)) != 0)
    449 		goto err;
    450 
    451 	nibble = __SHIFTOUT(val, __BITS(7, 4));
    452 	if ((res = pms_alps_cm_write_nibble(tag, slot, nibble)) != 0) {
    453 		goto err;
    454 	}
    455 
    456 	nibble = __SHIFTOUT(val, __BITS(3, 0));
    457 	if ((res = pms_alps_cm_write_nibble(tag, slot, nibble)) != 0) {
    458 		goto err;
    459 	}
    460 
    461 	return res;
    462 err:
    463 	aprint_error("Failed to write a value.\n");
    464 	return res;
    465 }
    466 
    467 /*
    468  * Not used practically for initialization
    469  */
    470 static int
    471 pms_alps_get_resolution_v7(struct pms_softc *psc)
    472 {
    473 #if 0
    474 	struct alps_softc *sc = &psc->u.alps;
    475 #endif
    476 	pckbport_tag_t tag = psc->sc_kbctag;
    477 	pckbport_slot_t slot = psc->sc_kbcslot;
    478 
    479 	int res;
    480 	uint8_t ret;
    481 #if 0
    482 	uint32_t x_pitch, y_pitch;
    483 	uint32_t x_elec, y_elec;
    484 	uint32_t x_phy, y_phy;
    485 #endif
    486 	/* X/Y pitch */
    487 	if ((res = pms_alps_cm_read_1(tag, slot, 0xc397, &ret)) != 0) {
    488 		goto err;
    489 	}
    490 #if 0
    491 	/* X pitch */
    492 	x_pitch = __SHIFTOUT(ret, __BITS(7, 4)); /* Higher 4-bit */
    493 	x_pitch = x_pitch * 2 + 50; /* Unit = 0.1mm */
    494 
    495 	/* Y pitch */
    496 	y_pitch = ret & __BITS(3, 0); /* Lower 4-bit */
    497 	y_pitch = y_pitch * 2 + 36; /* Unit = 0.1mm */
    498 
    499 	/* X/Y electrode */
    500 	if ((res = pms_alps_cm_read_1(tag, slot, 0xc397 + 1, &ret)) != 0) {
    501 		goto err;
    502 	}
    503 
    504 	/* X electrode */
    505 	x_elec = __SHIFTOUT(ret, __BITS(7, 4)); /* Higher 4-bit */
    506 	x_elec = x_elec + 17;
    507 
    508 	/* Y electrode */
    509 	y_elec = ret & __BITS(3, 0); /* Lower 4-bit */
    510 	y_elec = y_elec + 13;
    511 
    512 	/* X/Y physical in unit = 0.1mm */
    513 	/* X physical */
    514 	x_phy = (x_elec - 1) * x_pitch;
    515 	y_phy = (y_elec - 1) * y_pitch;
    516 
    517 	/* X/Y resolution (unit) */
    518 	sc->res_x = 0xfff * 10 / x_phy;
    519 	sc->res_y = 0x7ff * 10 / y_phy;
    520 #endif
    521 	return res;
    522 
    523 err:
    524 	aprint_error("Failed to get resolution.\n");
    525 	return res;
    526 }
    527 
    528 /*
    529  * Enable tap mode for V2 device
    530  */
    531 static int
    532 pms_alps_enable_tap_mode_v2(struct pms_softc *psc)
    533 {
    534 	uint8_t cmd[2];
    535 	uint8_t resp[3];
    536 	int res;
    537 
    538 	resp[0] = resp[1] = resp[2] = 0;
    539 	cmd[0] = PMS_SEND_DEV_STATUS; /* E9 */
    540 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    541 	    cmd, 1, 3, resp, 0)) != 0)
    542 		goto err;
    543 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    544 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    545 	    cmd, 1, 0, NULL, 0)) != 0)
    546 		goto err;
    547 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    548 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    549 	    cmd, 1, 0, NULL, 0)) != 0)
    550 		goto err;
    551 	cmd[0] = PMS_SET_SAMPLE;
    552 	cmd[1] = 0x0a; /* argument */
    553 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    554 	    cmd, 2, 0, NULL, 0)) != 0)
    555 		goto err;
    556 
    557 	/* Get status */
    558 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    559 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    560 	    cmd, 1, 0, NULL, 0)) != 0)
    561 		goto err;
    562 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    563 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    564 	    cmd, 1, 0, NULL, 0)) != 0)
    565 		goto err;
    566 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    567 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    568 	    cmd, 1, 0, NULL, 0)) != 0)
    569 		goto err;
    570 	resp[0] = resp[1] = resp[2] = 0;
    571 	cmd[0] = PMS_SEND_DEV_STATUS; /* E9 */
    572 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    573 	    cmd, 1, 3, resp, 0)) != 0)
    574 		goto err;
    575 
    576 	aprint_debug_dev(psc->sc_dev, "Tap mode is enabled.\n");
    577 
    578 	return 0;
    579 err:
    580 	aprint_error_dev(psc->sc_dev, "Failed to enable tap mode.\n");
    581 	return res;
    582 }
    583 
    584 static int
    585 pms_alps_init_v2(struct pms_softc *psc)
    586 {
    587 	uint8_t cmd[1];
    588 	int res;
    589 
    590 	if ((res = pms_alps_enable_tap_mode_v2(psc)) != 0)
    591 		goto err;
    592 
    593 	/* Enable absolute mode */
    594 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    595 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    596 	    cmd, 1, 0, NULL, 0)) != 0)
    597 		goto err;
    598 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    599 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    600 	    cmd, 1, 0, NULL, 0)) != 0)
    601 		goto err;
    602 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    603 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    604 	    cmd, 1, 0, NULL, 0)) != 0)
    605 		goto err;
    606 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    607 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    608 	    cmd, 1, 0, NULL, 0)) != 0)
    609 		goto err;
    610 	cmd[0] = PMS_DEV_ENABLE; /* F4 */
    611 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    612 	    cmd, 1, 0, NULL, 0)) != 0)
    613 		goto err;
    614 
    615 	/* Enable remote mode */
    616 	cmd[0] = PMS_SET_REMOTE_MODE; /* F0 */
    617 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    618 	    cmd, 1, 0, NULL, 0)) != 0)
    619 		goto err;
    620 
    621 	/* Start stream mode to get data */
    622 	cmd[0] = PMS_SET_STREAM; /* EA */
    623 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    624 	    cmd, 1, 0, NULL, 0)) != 0)
    625 		goto err;
    626 
    627 
    628 	return 0;
    629 
    630 err:
    631 	aprint_error_dev(psc->sc_dev, "Failed to initialize V2 device.\n");
    632 	return res;
    633 }
    634 
    635 static int
    636 pms_alps_init_v7(struct pms_softc *psc)
    637 {
    638 	uint8_t val;
    639 	uint8_t nibble;
    640 	int res;
    641 
    642 	/* Start command mode */
    643 	if ((res = pms_alps_start_command_mode(psc)) != 0) {
    644 		goto err;
    645 	}
    646 	if ((res = pms_alps_cm_read_1(psc->sc_kbctag, psc->sc_kbcslot, 0xc2d9, &val)) != 0) {
    647 		goto err;
    648 	}
    649 	if ((res = pms_alps_get_resolution_v7(psc)) != 0) {
    650 		goto err;
    651 	}
    652 	if ((res = pms_alps_cm_write_1(psc->sc_kbctag, psc->sc_kbcslot, 0xc2c9, 0x64)) != 0) {
    653 		goto err;
    654 	}
    655 
    656 	/* Start absolute mode */
    657 	if ((res = pms_alps_cm_read_1(psc->sc_kbctag, psc->sc_kbcslot, 0xc2c4, &val)) != 0) {
    658 		goto err;
    659 	}
    660 	/* Do not set address before this, so do not use pms_cm_write_1() */
    661 	val = val | __BIT(1);
    662 	nibble = __SHIFTOUT(val, __BITS(7, 4));
    663 	if ((res = pms_alps_cm_write_nibble(psc->sc_kbctag, psc->sc_kbcslot, nibble)) != 0) {
    664 		goto err;
    665 	}
    666 	nibble = __SHIFTOUT(val, __BITS(3, 0));
    667 	if ((res = pms_alps_cm_write_nibble(psc->sc_kbctag, psc->sc_kbcslot, nibble)) != 0) {
    668 		goto err;
    669 	}
    670 
    671 	/* End command mode */
    672 	if ((res = pms_alps_end_command_mode(psc)) != 0)
    673 		goto err;
    674 
    675 	return res;
    676 
    677 err:
    678 	(void)pms_alps_end_command_mode(psc);
    679 	aprint_error_dev(psc->sc_dev, "Failed to initialize V7 device.\n");
    680 	return res;
    681 }
    682 
    683 int
    684 pms_alps_probe_init(void *opaque)
    685 {
    686 	struct pms_softc *psc = opaque;
    687 	struct alps_softc *sc = &psc->u.alps;
    688 	struct sysctllog *clog = NULL;
    689 	uint8_t e6sig[3];
    690 	uint8_t e7sig[3];
    691 	uint8_t ecsig[3];
    692 	int res;
    693 	u_char cmd[1];
    694 
    695 	sc->last_x1 = 0;
    696 	sc->last_y1 = 0;
    697 	sc->last_x2 = 0;
    698 	sc->last_y2 = 0;
    699 	sc->last_nfingers = 0;
    700 
    701 	pckbport_flush(psc->sc_kbctag, psc->sc_kbcslot);
    702 
    703 	if ((res = pms_alps_e6sig(psc, e6sig)) != 0)
    704 		goto err;
    705 
    706 	if ((res = pms_alps_e7sig(psc, e7sig)) != 0)
    707 		goto err;
    708 
    709 	if ((res = pms_alps_ecsig(psc, ecsig)) != 0)
    710 		goto err;
    711 
    712 	/* Determine protocol version */
    713 	if ((ecsig[0] == 0x88) && (__SHIFTOUT(ecsig[1], __BITS(7, 4)) == 0x0b)) {
    714 		/* V7 device in Toshiba dynabook R63/PS */
    715 		sc->version = ALPS_PROTO_V7;
    716 	} else if ((e7sig[0] == 0x73) && (e7sig[1] == 0x02) &&
    717 		((e7sig[2] == 0x14) || (e7sig[2] == 0x0a))) {
    718 		/* 0x14: V2 device in NEC VJ22MF-7 (VersaPro JVF-7) */
    719 		/* 0x0a: V2 devices in Toshiba dynabook satellite B551/D
    720 			 and dynabook SS RX1 */
    721 		sc->version = ALPS_PROTO_V2;
    722 	}
    723 
    724 	if (sc->version == ALPS_PROTO_V7) {
    725 		/* Initialize V7 device */
    726 		if ((res = pms_alps_init_v7(psc)) != 0)
    727 			goto err;
    728 		aprint_normal_dev(psc->sc_dev,
    729 			"ALPS PS/2 V7 pointing device\n");
    730 	} else if (sc->version == ALPS_PROTO_V2) {
    731 		/* Initialize V2 pointing device */
    732 		if ((res = pms_alps_init_v2(psc)) != 0)
    733 			goto err;
    734 		aprint_normal_dev(psc->sc_dev,
    735 			"ALPS PS/2 V2 pointing device\n");
    736 	} else {
    737 		res = EINVAL;
    738 		goto err;
    739 	}
    740 
    741 	/* From sysctl */
    742 	pms_sysctl_alps(&clog);
    743 	/* Register hundler */
    744 	if (sc->version == ALPS_PROTO_V7) {
    745 		pckbport_set_inputhandler(psc->sc_kbctag, psc->sc_kbcslot,
    746 			pms_alps_input_v7, psc, device_xname(psc->sc_dev));
    747 	} else if (sc->version == ALPS_PROTO_V2) {
    748 		pckbport_set_inputhandler(psc->sc_kbctag, psc->sc_kbcslot,
    749 			pms_alps_input_v2, psc, device_xname(psc->sc_dev));
    750 	} else {
    751 		res = EINVAL;
    752 		goto err;
    753 	}
    754 	/* Palm detection is enabled. */
    755 
    756 	return 0;
    757 
    758 err:
    759 	cmd[0] = PMS_RESET;
    760 	(void)pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    761 	    cmd, 1, 2, NULL, 1);
    762 	if (res != ENODEV)
    763 		aprint_error_dev(psc->sc_dev, "Failed to initialize an ALPS device.\n");
    764 	return res;
    765 }
    766 
    767 void
    768 pms_alps_enable(void *opaque)
    769 {
    770 	struct pms_softc *psc = opaque;
    771 	struct alps_softc *sc = &psc->u.alps;
    772 
    773 	sc->initializing = true;
    774 }
    775 
    776 void
    777 pms_alps_resume(void *opaque)
    778 {
    779 	struct pms_softc *psc = opaque;
    780 	struct alps_softc *sc = &psc->u.alps;
    781 	uint8_t cmd, resp[2];
    782 	int res;
    783 
    784 	cmd = PMS_RESET;
    785 	res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, &cmd,
    786 		1, 2, resp, 1);
    787 	if (res)
    788 	aprint_error_dev(psc->sc_dev,
    789 		"ALPS reset on resume failed\n");
    790 	else {
    791 		if (sc->version == ALPS_PROTO_V7) {
    792 			(void)pms_alps_init_v7(psc);
    793 		} else if (sc->version == ALPS_PROTO_V2) {
    794 			(void)pms_alps_init_v2(psc);
    795 		} else {
    796 			/* Not supported */
    797 		}
    798 		pms_alps_enable(psc);
    799 	}
    800 }
    801 
    802 static void
    803 pms_alps_decode_trackstick_packet_v7(struct pms_softc *psc)
    804 {
    805 	int s;
    806 
    807 	int x, y, z;
    808 	int dx, dy, dz;
    809 	int left, middle, right;
    810 	u_int buttons;
    811 
    812 	x = (int8_t)((psc->packet[2] & 0xbf) |
    813 		((psc->packet[3] & 0x10) << 2));
    814 	y = (int8_t)((psc->packet[3] & 0x07) | (psc->packet[4] & 0xb8) |
    815 		((psc->packet[3] & 0x20) << 1));
    816 	z = (int8_t)((psc->packet[5] & 0x3f) |
    817 		((psc->packet[3] & 0x80) >> 1));
    818 
    819 	dx = x * alps_trackstick_xy_precision;
    820 	dy = y * alps_trackstick_xy_precision;
    821 	dz = z * 1;
    822 
    823 	left = psc->packet[1] & 0x01;
    824 	middle = (psc->packet[1] & 0x04) >> 2;
    825 	right = (psc->packet[1] & 0x02) >> 1;
    826 	buttons = 0;
    827 	buttons = (u_int)((left << 0) | (middle << 1) | (right << 2));
    828 
    829 	s = spltty();
    830 	wsmouse_input(psc->sc_wsmousedev,
    831 		buttons,
    832 		dx, dy, dz, 0,
    833 		WSMOUSE_INPUT_DELTA);
    834 	splx(s);
    835 }
    836 
    837 static uint8_t
    838 pms_alps_decode_packetid_v7(struct pms_softc *psc)
    839 {
    840 	if (psc->packet[4] & 0x40)
    841 		return ALPS_V7_PACKETID_TWOFINGER;
    842 	else if (psc->packet[4] & 0x01)
    843 		return ALPS_V7_PACKETID_MULTIFINGER;
    844 	else if ((psc->packet[0] & 0x10) && !(psc->packet[4] & 0x43))
    845 		return ALPS_V7_PACKETID_NEWPACKET;
    846 	else if ((psc->packet[1] == 0x00) && (psc->packet[4] == 0x00))
    847 		return ALPS_V7_PACKETID_IDLE;
    848 	else
    849 		return ALPS_V7_PACKETID_UNKNOWN;
    850 }
    851 
    852 static void
    853 pms_alps_decode_touchpad_packet_v7(struct pms_softc *psc)
    854 {
    855 	int s;
    856 	struct alps_softc *sc = &psc->u.alps;
    857 	uint8_t packetid;
    858 
    859 	uint16_t cur_x1, cur_y1;
    860 	uint16_t cur_x2, cur_y2;
    861 	int dx1, dy1;
    862 	int button;
    863 	u_int buttons;
    864 
    865 	packetid = pms_alps_decode_packetid_v7(psc);
    866 	switch (packetid) {
    867 	case ALPS_V7_PACKETID_IDLE:
    868 		/* Accept meaningful packets only */
    869 		return;
    870 	case ALPS_V7_PACKETID_UNKNOWN:
    871 		/* Accept meaningful packets only */
    872 		return;
    873 	case ALPS_V7_PACKETID_NEWPACKET:
    874 		/* Sent new packet ID to reset status and not decoded */
    875 		sc->initializing = true;
    876 		return;
    877 	}
    878 
    879 	/* Decode a number of fingers and locations */
    880 	/* X0-11 ... X0-0 */
    881 	cur_x1 = (psc->packet[2] & 0x80) << 4; /* X0-11 */
    882 	cur_x1 |= (psc->packet[2] & 0x3f) << 5; /* X0-10 ... X0-5 */
    883 	cur_x1 |= (psc->packet[3] & 0x30) >> 1; /* X0-4, X0-3 */
    884 	cur_x1 |= psc->packet[3] & 0x07; /* X0-2 ... X0-0 */
    885 
    886 	/* Y0-10 ... Y0-0 */
    887 	cur_y1 = psc->packet[1] << 3; /* Y0-10 ... Y0-3 */
    888 	cur_y1 |= psc->packet[0] & 0x07; /* Y0-2 ... Y0-0 */
    889 
    890 	/* X1-11 ... X1-3 */
    891 	cur_x2 = (psc->packet[3] & 0x80) << 4; /* X1-11 */
    892 	cur_x2 |= (psc->packet[4] & 0x80) << 3; /* X1-10 */
    893 	cur_x2 |= (psc->packet[4] & 0x3f) << 4; /* X1-9 ... X1-4 */
    894 
    895 	/* Y1-10 ... Y1-4 */
    896 	cur_y2 = (psc->packet[5] & 0x80) << 3; /* Y1-10 */
    897 	cur_y2 |= (psc->packet[5] & 0x3f) << 4; /* Y1-9 .. Y1-4 */
    898 
    899 	switch (packetid) {
    900 	case ALPS_V7_PACKETID_TWOFINGER:
    901 		cur_x2 &= ~__BITS(3, 0); /* Clear undefined locations */
    902 		cur_y2 |= __BITS(3, 0); /* Fill undefined locations */
    903 		break;
    904 	case ALPS_V7_PACKETID_MULTIFINGER:
    905 		cur_x2 &= ~__BITS(5, 0); /* Clear undefined locations */
    906 		cur_y2 &= ~__BIT(5); /* Clear duplicate locations */
    907 		cur_y2 |= (psc->packet[4] & __BIT(1)) << 4; /* Set another */
    908 		cur_y2 |= __BITS(4, 0); /* Fill undefined locations */
    909 		break;
    910 	}
    911 
    912 	cur_y1 = 0x7ff - cur_y1;
    913 	cur_y2 = 0x7ff - cur_y2;
    914 
    915 	/* Handle finger touch reported in cur_x2/y2. only */
    916 	if (cur_x1 == 0 && cur_y1 == 0 && cur_x2 != 0 && cur_y2 != 0) {
    917 		cur_x1 = cur_x2;
    918 		cur_y1 = cur_y2;
    919 		cur_x2 = 0;
    920 		cur_y2 = 0;
    921 	}
    922 
    923 	switch (packetid) {
    924 	case ALPS_V7_PACKETID_TWOFINGER:
    925 		if ((cur_x2 == 0) && (cur_y2 == 0))
    926 			sc->nfingers = 1;
    927 		else
    928 			sc->nfingers = 2;
    929 		break;
    930 	case ALPS_V7_PACKETID_MULTIFINGER:
    931 		sc->nfingers = 3 + (psc->packet[5] & 0x03);
    932 		break;
    933 	}
    934 
    935 	button = (psc->packet[0] & 0x80) >> 7;
    936 	buttons = 0;
    937 	if (sc->nfingers == 1) {
    938 		if (button && (cur_y1 > 1700) && (cur_x1 < 1700))
    939 			buttons |= button << 0; /* Left button */
    940 		else if (button && (cur_y1 > 1700)
    941 				&& (1700 <= cur_x1) && (cur_x1 <= 2700))
    942 			buttons |= button << 1; /* Middle button */
    943 		else if (button && (cur_y1 > 1700) && (2700 < cur_x1))
    944 			buttons |= button << 2; /* Right button */
    945 	} else if (sc->nfingers > 1) {
    946 		if (button && (cur_y2 > 1700) && (cur_x2 < 1700))
    947 			buttons |= button << 0; /* Left button */
    948 		else if (button && (cur_y2 > 1700)
    949 				&& (1700 <= cur_x2) && (cur_x2 <= 2700))
    950 			buttons |= button << 1; /* Middle button */
    951 		else if (button && (cur_y2 > 1700) && (2700 < cur_x2))
    952 			buttons |= button << 2; /* Right button */
    953 	}
    954 
    955 	/* New touch */
    956 	if (sc->nfingers == 0 || sc->nfingers != sc->last_nfingers)
    957 		sc->initializing = true;
    958 
    959 	if (sc->initializing == true) {
    960 		dx1 = 0;
    961 		dy1 = 0;
    962 	} else {
    963 		dx1 = (int16_t)(cur_x1 - sc->last_x1);
    964 		dy1 = (int16_t)(sc->last_y1 - cur_y1);
    965 
    966 		dx1 = dx1 >> alps_touchpad_xy_unprecision;
    967 		dy1 = dy1 >> alps_touchpad_xy_unprecision;
    968 	}
    969 
    970 	/* Allow finger detouch during drag and drop */
    971 	if ((sc->nfingers < sc->last_nfingers)
    972 		&& (cur_x2 == sc->last_x1) && (cur_y2 == sc->last_y1)) {
    973 		sc->last_x1 = sc->last_x2;
    974 		sc->last_y1 = sc->last_y2;
    975 		dx1 = 0;
    976 		dy1 = 0;
    977 	}
    978 
    979 	s = spltty();
    980 	wsmouse_input(psc->sc_wsmousedev,
    981 		buttons,
    982 		dx1, dy1, 0, 0,
    983 		WSMOUSE_INPUT_DELTA);
    984 	splx(s);
    985 
    986 	if (sc->initializing == true || (dx1 != 0))
    987 		sc->last_x1 = cur_x1;
    988 	if (sc->initializing == true || (dy1 != 0))
    989 		sc->last_y1 = cur_y1;
    990 
    991 	if (sc->nfingers > 0)
    992 		sc->initializing = false;
    993 	sc->last_nfingers = sc->nfingers;
    994 }
    995 
    996 static void
    997 pms_alps_dispatch_packet_v7(struct pms_softc *psc)
    998 {
    999 	if ((psc->packet[0] == 0x48) && ((psc->packet[4] & 0x47) == 0x06))
   1000 		pms_alps_decode_trackstick_packet_v7(psc);
   1001 	else
   1002 		pms_alps_decode_touchpad_packet_v7(psc);
   1003 }
   1004 
   1005 static void
   1006 pms_alps_decode_touchpad_packet_v2(struct pms_softc *psc)
   1007 {
   1008 	int s;
   1009 	struct alps_softc *sc = &psc->u.alps;
   1010 	uint16_t cur_x, cur_y;
   1011 	int16_t dx, dy;
   1012 	u_int left, middle, right;
   1013 	u_int forward, back;
   1014 	u_int buttons;
   1015 	uint8_t ges;
   1016 
   1017 	sc->nfingers = (psc->packet[2] & 0x02) >> 1;
   1018 	if (sc->last_nfingers == 0)
   1019 		sc->initializing = true;
   1020 
   1021 	left = psc->packet[3] & 0x01;
   1022 	right = (psc->packet[3] & 0x02) >> 1;
   1023 	middle = (psc->packet[3] & 0x04) >> 2;
   1024 
   1025 	cur_x = psc->packet[1];
   1026 	cur_x |= (psc->packet[2] & 0x78) << 4;
   1027 
   1028 	cur_y = psc->packet[4];
   1029 	cur_y |= (psc->packet[3] & 0x70) << 3;
   1030 
   1031 #if 0
   1032 	cur_z = psc->packet[5];
   1033 #endif
   1034 
   1035 	forward = (psc->packet[2] & 0x04) >> 2;
   1036 	back = (psc->packet[3] & 0x04) >> 2;
   1037 	ges = psc->packet[2] & 0x01;
   1038 
   1039 	buttons = (left | ges) << 0;
   1040 	buttons |= (middle | forward | back) << 1;
   1041 	buttons |= right << 2;
   1042 
   1043 	if (sc->initializing == true) {
   1044 		dx = 0;
   1045 		dy = 0;
   1046 	} else {
   1047 		dx = (cur_x - sc->last_x1);
   1048 		dy = (sc->last_y1 - cur_y);
   1049 
   1050 		dx = dx >> alps_touchpad_xy_unprecision;
   1051 		dy = dy >> alps_touchpad_xy_unprecision;
   1052 	}
   1053 
   1054 	s = spltty();
   1055 	wsmouse_input(psc->sc_wsmousedev,
   1056 		buttons,
   1057 		dx, dy, 0, 0,
   1058 		WSMOUSE_INPUT_DELTA);
   1059 	splx(s);
   1060 
   1061 	if (sc->initializing == true || (dx != 0))
   1062 		sc->last_x1 = cur_x;
   1063 	if (sc->initializing == true || (dy != 0))
   1064 		sc->last_y1 = cur_y;
   1065 
   1066 	if (sc->nfingers > 0)
   1067 		sc->initializing = false;
   1068 	sc->last_nfingers = sc->nfingers;
   1069 }
   1070 
   1071 static void
   1072 pms_alps_input_v2(void *opaque, int data)
   1073 {
   1074 	struct pms_softc *psc = opaque;
   1075 
   1076 	if (!psc->sc_enabled)
   1077 		return;
   1078 
   1079 	psc->packet[psc->inputstate++] = data & 0xff;
   1080 	if (psc->inputstate < 6)
   1081 		return;
   1082 
   1083 	pms_alps_decode_touchpad_packet_v2(psc);
   1084 
   1085 	psc->inputstate = 0;
   1086 }
   1087 
   1088 static void
   1089 pms_alps_input_v7(void *opaque, int data)
   1090 {
   1091 	struct pms_softc *psc = opaque;
   1092 
   1093 	if (!psc->sc_enabled)
   1094 		return;
   1095 
   1096 	psc->packet[psc->inputstate++] = data & 0xff;
   1097 	if (psc->inputstate < 6)
   1098 		return;
   1099 
   1100 	pms_alps_dispatch_packet_v7(psc);
   1101 
   1102 	psc->inputstate = 0;
   1103 }
   1104