Home | History | Annotate | Line # | Download | only in pckbport
alps.c revision 1.10
      1 /* $NetBSD: alps.c,v 1.10 2018/06/19 23:25:59 uwe 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.10 2018/06/19 23:25:59 uwe 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 recieve;
    344 	int res;
    345 
    346 	sendparam = alps_v7_nibble_command_data_arr[nibble].sendparam;
    347 	recieve= alps_v7_nibble_command_data_arr[nibble].recieve;
    348 	cmd[0] = alps_v7_nibble_command_data_arr[nibble].command;
    349 	if (recieve) {
    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)) {
    718 		/* V2 device in NEC VJ22MF-7 (VersaPro JVF-7) */
    719 		sc->version = ALPS_PROTO_V2;
    720 	}
    721 
    722 	if (sc->version == ALPS_PROTO_V7) {
    723 		/* Initialize V7 device */
    724 		if ((res = pms_alps_init_v7(psc)) != 0)
    725 			goto err;
    726 		aprint_normal_dev(psc->sc_dev,
    727 			"ALPS PS/2 V7 pointing device\n");
    728 	} else if (sc->version == ALPS_PROTO_V2) {
    729 		/* Initialize V2 pointing device */
    730 		if ((res = pms_alps_init_v2(psc)) != 0)
    731 			goto err;
    732 		aprint_normal_dev(psc->sc_dev,
    733 			"ALPS PS/2 V2 pointing device\n");
    734 	} else {
    735 		res = EINVAL;
    736 		goto err;
    737 	}
    738 
    739 	/* From sysctl */
    740 	pms_sysctl_alps(&clog);
    741 	/* Register hundler */
    742 	if (sc->version == ALPS_PROTO_V7) {
    743 		pckbport_set_inputhandler(psc->sc_kbctag, psc->sc_kbcslot,
    744 			pms_alps_input_v7, psc, device_xname(psc->sc_dev));
    745 	} else if (sc->version == ALPS_PROTO_V2) {
    746 		pckbport_set_inputhandler(psc->sc_kbctag, psc->sc_kbcslot,
    747 			pms_alps_input_v2, psc, device_xname(psc->sc_dev));
    748 	} else {
    749 		res = EINVAL;
    750 		goto err;
    751 	}
    752 	/* Palm detection is enabled. */
    753 
    754 	return 0;
    755 
    756 err:
    757 	cmd[0] = PMS_RESET;
    758 	(void)pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    759 	    cmd, 1, 2, NULL, 1);
    760 	if (res != ENODEV)
    761 		aprint_error_dev(psc->sc_dev, "Failed to initialize an ALPS device.\n");
    762 	return res;
    763 }
    764 
    765 void
    766 pms_alps_enable(void *opaque)
    767 {
    768 	struct pms_softc *psc = opaque;
    769 	struct alps_softc *sc = &psc->u.alps;
    770 
    771 	sc->initializing = true;
    772 }
    773 
    774 void
    775 pms_alps_resume(void *opaque)
    776 {
    777 	struct pms_softc *psc = opaque;
    778 	struct alps_softc *sc = &psc->u.alps;
    779 	uint8_t cmd, resp[2];
    780 	int res;
    781 
    782 	cmd = PMS_RESET;
    783 	res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, &cmd,
    784 		1, 2, resp, 1);
    785 	if (res)
    786 	aprint_error_dev(psc->sc_dev,
    787 		"ALPS reset on resume failed\n");
    788 	else {
    789 		if (sc->version == ALPS_PROTO_V7) {
    790 			(void)pms_alps_init_v7(psc);
    791 		} else if (sc->version == ALPS_PROTO_V2) {
    792 			(void)pms_alps_init_v2(psc);
    793 		} else {
    794 			/* Not supported */
    795 		}
    796 		pms_alps_enable(psc);
    797 	}
    798 }
    799 
    800 static void
    801 pms_alps_decode_trackstick_packet_v7(struct pms_softc *psc)
    802 {
    803 	int s;
    804 
    805 	int x, y, z;
    806 	int dx, dy, dz;
    807 	int left, middle, right;
    808 	u_int buttons;
    809 
    810 	x = (int8_t)((psc->packet[2] & 0xbf) |
    811 		((psc->packet[3] & 0x10) << 2));
    812 	y = (int8_t)((psc->packet[3] & 0x07) | (psc->packet[4] & 0xb8) |
    813 		((psc->packet[3] & 0x20) << 1));
    814 	z = (int8_t)((psc->packet[5] & 0x3f) |
    815 		((psc->packet[3] & 0x80) >> 1));
    816 
    817 	dx = x * alps_trackstick_xy_precision;
    818 	dy = y * alps_trackstick_xy_precision;
    819 	dz = z * 1;
    820 
    821 	left = psc->packet[1] & 0x01;
    822 	middle = (psc->packet[1] & 0x04) >> 2;
    823 	right = (psc->packet[1] & 0x02) >> 1;
    824 	buttons = 0;
    825 	buttons = (u_int)((left << 0) | (middle << 1) | (right << 2));
    826 
    827 	s = spltty();
    828 	wsmouse_input(psc->sc_wsmousedev,
    829 		buttons,
    830 		dx, dy, dz, 0,
    831 		WSMOUSE_INPUT_DELTA);
    832 	splx(s);
    833 }
    834 
    835 static uint8_t
    836 pms_alps_decode_packetid_v7(struct pms_softc *psc)
    837 {
    838 	if (psc->packet[4] & 0x40)
    839 		return ALPS_V7_PACKETID_TWOFINGER;
    840 	else if (psc->packet[4] & 0x01)
    841 		return ALPS_V7_PACKETID_MULTIFINGER;
    842 	else if ((psc->packet[0] & 0x10) && !(psc->packet[4] & 0x43))
    843 		return ALPS_V7_PACKETID_NEWPACKET;
    844 	else if ((psc->packet[1] == 0x00) && (psc->packet[4] == 0x00))
    845 		return ALPS_V7_PACKETID_IDLE;
    846 	else
    847 		return ALPS_V7_PACKETID_UNKNOWN;
    848 }
    849 
    850 static void
    851 pms_alps_decode_touchpad_packet_v7(struct pms_softc *psc)
    852 {
    853 	int s;
    854 	struct alps_softc *sc = &psc->u.alps;
    855 	uint8_t packetid;
    856 
    857 	uint16_t cur_x1, cur_y1;
    858 	uint16_t cur_x2, cur_y2;
    859 	int dx1, dy1;
    860 	int button;
    861 	u_int buttons;
    862 
    863 	packetid = pms_alps_decode_packetid_v7(psc);
    864 	switch (packetid) {
    865 	case ALPS_V7_PACKETID_IDLE:
    866 		/* Accept meaningful packets only */
    867 		return;
    868 	case ALPS_V7_PACKETID_UNKNOWN:
    869 		/* Accept meaningful packets only */
    870 		return;
    871 	case ALPS_V7_PACKETID_NEWPACKET:
    872 		/* Sent new packet ID to reset status and not decoded */
    873 		sc->initializing = true;
    874 		return;
    875 	}
    876 
    877 	/* Decode a number of fingers and locations */
    878 	/* X0-11 ... X0-0 */
    879 	cur_x1 = (psc->packet[2] & 0x80) << 4; /* X0-11 */
    880 	cur_x1 |= (psc->packet[2] & 0x3f) << 5; /* X0-10 ... X0-5 */
    881 	cur_x1 |= (psc->packet[3] & 0x30) >> 1; /* X0-4, X0-3 */
    882 	cur_x1 |= psc->packet[3] & 0x07; /* X0-2 ... X0-0 */
    883 
    884 	/* Y0-10 ... Y0-0 */
    885 	cur_y1 = psc->packet[1] << 3; /* Y0-10 ... Y0-3 */
    886 	cur_y1 |= psc->packet[0] & 0x07; /* Y0-2 ... Y0-0 */
    887 
    888 	/* X1-11 ... X1-3 */
    889 	cur_x2 = (psc->packet[3] & 0x80) << 4; /* X1-11 */
    890 	cur_x2 |= (psc->packet[4] & 0x80) << 3; /* X1-10 */
    891 	cur_x2 |= (psc->packet[4] & 0x3f) << 4; /* X1-9 ... X1-4 */
    892 
    893 	/* Y1-10 ... Y1-4 */
    894 	cur_y2 = (psc->packet[5] & 0x80) << 3; /* Y1-10 */
    895 	cur_y2 |= (psc->packet[5] & 0x3f) << 4; /* Y1-9 .. Y1-4 */
    896 
    897 	switch (packetid) {
    898 	case ALPS_V7_PACKETID_TWOFINGER:
    899 		cur_x2 &= ~__BITS(3, 0); /* Clear undefined locations */
    900 		cur_y2 |= __BITS(3, 0); /* Fill undefined locations */
    901 		break;
    902 	case ALPS_V7_PACKETID_MULTIFINGER:
    903 		cur_x2 &= ~__BITS(5, 0); /* Clear undefined locations */
    904 		cur_y2 &= ~__BIT(5); /* Clear duplicate locations */
    905 		cur_y2 |= (psc->packet[4] & __BIT(1)) << 4; /* Set another */
    906 		cur_y2 |= __BITS(4, 0); /* Fill undefined locations */
    907 		break;
    908 	}
    909 
    910 	cur_y1 = 0x7ff - cur_y1;
    911 	cur_y2 = 0x7ff - cur_y2;
    912 
    913 	/* Handle finger touch reported in cur_x2/y2. only */
    914 	if (cur_x1 == 0 && cur_y1 == 0 && cur_x2 != 0 && cur_y2 != 0) {
    915 		cur_x1 = cur_x2;
    916 		cur_y1 = cur_y2;
    917 		cur_x2 = 0;
    918 		cur_y2 = 0;
    919 	}
    920 
    921 	switch (packetid) {
    922 	case ALPS_V7_PACKETID_TWOFINGER:
    923 		if ((cur_x2 == 0) && (cur_y2 == 0))
    924 			sc->nfingers = 1;
    925 		else
    926 			sc->nfingers = 2;
    927 		break;
    928 	case ALPS_V7_PACKETID_MULTIFINGER:
    929 		sc->nfingers = 3 + (psc->packet[5] & 0x03);
    930 		break;
    931 	}
    932 
    933 	button = (psc->packet[0] & 0x80) >> 7;
    934 	buttons = 0;
    935 	if (sc->nfingers == 1) {
    936 		if (button && (cur_y1 > 1700) && (cur_x1 < 1700))
    937 			buttons |= button << 0; /* Left button */
    938 		else if (button && (cur_y1 > 1700)
    939 				&& (1700 <= cur_x1) && (cur_x1 <= 2700))
    940 			buttons |= button << 1; /* Middle button */
    941 		else if (button && (cur_y1 > 1700) && (2700 < cur_x1))
    942 			buttons |= button << 2; /* Right button */
    943 	} else if (sc->nfingers > 1) {
    944 		if (button && (cur_y2 > 1700) && (cur_x2 < 1700))
    945 			buttons |= button << 0; /* Left button */
    946 		else if (button && (cur_y2 > 1700)
    947 				&& (1700 <= cur_x2) && (cur_x2 <= 2700))
    948 			buttons |= button << 1; /* Middle button */
    949 		else if (button && (cur_y2 > 1700) && (2700 < cur_x2))
    950 			buttons |= button << 2; /* Right button */
    951 	}
    952 
    953 	/* New touch */
    954 	if (sc->nfingers == 0 || sc->nfingers != sc->last_nfingers)
    955 		sc->initializing = true;
    956 
    957 	if (sc->initializing == true) {
    958 		dx1 = 0;
    959 		dy1 = 0;
    960 	} else {
    961 		dx1 = (int16_t)(cur_x1 - sc->last_x1);
    962 		dy1 = (int16_t)(sc->last_y1 - cur_y1);
    963 
    964 		dx1 = dx1 >> alps_touchpad_xy_unprecision;
    965 		dy1 = dy1 >> alps_touchpad_xy_unprecision;
    966 	}
    967 
    968 	/* Allow finger detouch during drag and drop */
    969 	if ((sc->nfingers < sc->last_nfingers)
    970 		&& (cur_x2 == sc->last_x1) && (cur_y2 == sc->last_y1)) {
    971 		sc->last_x1 = sc->last_x2;
    972 		sc->last_y1 = sc->last_y2;
    973 		dx1 = 0;
    974 		dy1 = 0;
    975 	}
    976 
    977 	s = spltty();
    978 	wsmouse_input(psc->sc_wsmousedev,
    979 		buttons,
    980 		dx1, dy1, 0, 0,
    981 		WSMOUSE_INPUT_DELTA);
    982 	splx(s);
    983 
    984 	if (sc->initializing == true || (dx1 != 0))
    985 		sc->last_x1 = cur_x1;
    986 	if (sc->initializing == true || (dy1 != 0))
    987 		sc->last_y1 = cur_y1;
    988 
    989 	if (sc->nfingers > 0)
    990 		sc->initializing = false;
    991 	sc->last_nfingers = sc->nfingers;
    992 }
    993 
    994 static void
    995 pms_alps_dispatch_packet_v7(struct pms_softc *psc)
    996 {
    997 	if ((psc->packet[0] == 0x48) && ((psc->packet[4] & 0x47) == 0x06))
    998 		pms_alps_decode_trackstick_packet_v7(psc);
    999 	else
   1000 		pms_alps_decode_touchpad_packet_v7(psc);
   1001 }
   1002 
   1003 static void
   1004 pms_alps_decode_touchpad_packet_v2(struct pms_softc *psc)
   1005 {
   1006 	int s;
   1007 	struct alps_softc *sc = &psc->u.alps;
   1008 	uint16_t cur_x, cur_y;
   1009 	int16_t dx, dy;
   1010 	u_int left, middle, right;
   1011 	u_int forward, back;
   1012 	u_int buttons;
   1013 	uint8_t ges;
   1014 
   1015 	sc->nfingers = (psc->packet[2] & 0x02) >> 1;
   1016 	if (sc->last_nfingers == 0)
   1017 		sc->initializing = true;
   1018 
   1019 	left = psc->packet[3] & 0x01;
   1020 	right = (psc->packet[3] & 0x02) >> 1;
   1021 	middle = (psc->packet[3] & 0x04) >> 2;
   1022 
   1023 	cur_x = psc->packet[1];
   1024 	cur_x |= (psc->packet[2] & 0x78) << 4;
   1025 
   1026 	cur_y = psc->packet[4];
   1027 	cur_y |= (psc->packet[3] & 0x70) << 3;
   1028 
   1029 #if 0
   1030 	cur_z = psc->packet[5];
   1031 #endif
   1032 
   1033 	forward = (psc->packet[2] & 0x04) >> 2;
   1034 	back = (psc->packet[3] & 0x04) >> 2;
   1035 	ges = psc->packet[2] & 0x01;
   1036 
   1037 	buttons = (left | ges) << 0;
   1038 	buttons |= (middle | forward | back) << 1;
   1039 	buttons |= right << 2;
   1040 
   1041 	if (sc->initializing == true) {
   1042 		dx = 0;
   1043 		dy = 0;
   1044 	} else {
   1045 		dx = (cur_x - sc->last_x1);
   1046 		dy = (sc->last_y1 - cur_y);
   1047 
   1048 		dx = dx >> alps_touchpad_xy_unprecision;
   1049 		dy = dy >> alps_touchpad_xy_unprecision;
   1050 	}
   1051 
   1052 	s = spltty();
   1053 	wsmouse_input(psc->sc_wsmousedev,
   1054 		buttons,
   1055 		dx, dy, 0, 0,
   1056 		WSMOUSE_INPUT_DELTA);
   1057 	splx(s);
   1058 
   1059 	if (sc->initializing == true || (dx != 0))
   1060 		sc->last_x1 = cur_x;
   1061 	if (sc->initializing == true || (dy != 0))
   1062 		sc->last_y1 = cur_y;
   1063 
   1064 	if (sc->nfingers > 0)
   1065 		sc->initializing = false;
   1066 	sc->last_nfingers = sc->nfingers;
   1067 }
   1068 
   1069 static void
   1070 pms_alps_input_v2(void *opaque, int data)
   1071 {
   1072 	struct pms_softc *psc = opaque;
   1073 
   1074 	if (!psc->sc_enabled)
   1075 		return;
   1076 
   1077 	psc->packet[psc->inputstate++] = data & 0xff;
   1078 	if (psc->inputstate < 6)
   1079 		return;
   1080 
   1081 	pms_alps_decode_touchpad_packet_v2(psc);
   1082 
   1083 	psc->inputstate = 0;
   1084 }
   1085 
   1086 static void
   1087 pms_alps_input_v7(void *opaque, int data)
   1088 {
   1089 	struct pms_softc *psc = opaque;
   1090 
   1091 	if (!psc->sc_enabled)
   1092 		return;
   1093 
   1094 	psc->packet[psc->inputstate++] = data & 0xff;
   1095 	if (psc->inputstate < 6)
   1096 		return;
   1097 
   1098 	pms_alps_dispatch_packet_v7(psc);
   1099 
   1100 	psc->inputstate = 0;
   1101 }
   1102