Home | History | Annotate | Line # | Download | only in pckbport
alps.c revision 1.5
      1 /* $NetBSD: alps.c,v 1.5 2018/06/03 15:02:56 jakllsch 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.5 2018/06/03 15:02:56 jakllsch 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_GET_SCALE; /* 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] != 0x00 || e6sig[1] != 0x00 ||
    169 		e6sig[2] != 0x64) {
    170 		return EINVAL;
    171 	}
    172 
    173 	aprint_debug_dev(psc->sc_dev,
    174 		"ALPS PS/2 E6 signature: 0x%X 0x%X 0x%X\n",
    175 		e6sig[0], e6sig[1], e6sig[2]);
    176 
    177 	return 0;
    178 err:
    179 	aprint_error_dev(psc->sc_dev, "Failed to get E6 signature.\n");
    180 	return res;
    181 }
    182 
    183 /*
    184  * Publish E7 report command and get E7 signature
    185  */
    186 static int
    187 pms_alps_e7sig(struct pms_softc *psc, uint8_t *e7sig)
    188 {
    189 	uint8_t cmd[2];
    190 	int res;
    191 
    192 	cmd[0] = PMS_SET_RES; /* E8 */
    193 	cmd[1] = 0;
    194 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    195 	    cmd, 2, 0, NULL, 0)) != 0)
    196 		goto err;
    197 	cmd[0] = PMS_SET_SCALE21; /* E7 */
    198 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    199 	    cmd, 1, 0, NULL, 0)) != 0)
    200 		goto err;
    201 	cmd[0] = PMS_SET_SCALE21; /* E7 */
    202 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    203 	    cmd, 1, 0, NULL, 0)) != 0)
    204 		goto err;
    205 	cmd[0] = PMS_SET_SCALE21; /* E7 */
    206 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    207 	    cmd, 1, 0, NULL, 0)) != 0)
    208 		goto err;
    209 	e7sig[0] = e7sig[1] = e7sig[2] = 0;
    210 	cmd[0] = PMS_GET_SCALE; /* E9 */
    211 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    212 	    cmd, 1, 3, e7sig, 0)) != 0)
    213 		goto err;
    214 
    215 	aprint_debug_dev(psc->sc_dev,
    216 		"ALPS PS/2 E7 signature: 0x%X 0x%X 0x%X\n",
    217 		e7sig[0], e7sig[1], e7sig[2]);
    218 
    219 	return 0;
    220 err:
    221 	aprint_error_dev(psc->sc_dev, "Failed to get E7 signature.\n");
    222 	return res;
    223 }
    224 
    225 /*
    226  * Publish EC command and get EC signature
    227  */
    228 static int
    229 pms_alps_ecsig(struct pms_softc *psc, uint8_t *ecsig)
    230 {
    231 	uint8_t cmd[2];
    232 	int res;
    233 
    234 	cmd[0] = PMS_SET_RES; /* E8 */
    235 	cmd[1] = 0;
    236 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    237 	    cmd, 2, 0, NULL, 0)) != 0)
    238 		goto err;
    239 	cmd[0] = PMS_RESET_WRAP_MODE; /* EC */
    240 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    241 	    cmd, 1, 0, NULL, 0)) != 0)
    242 		goto err;
    243 	cmd[0] = PMS_RESET_WRAP_MODE; /* EC */
    244 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    245 	    cmd, 1, 0, NULL, 0)) != 0)
    246 		goto err;
    247 	cmd[0] = PMS_RESET_WRAP_MODE; /* EC */
    248 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    249 	    cmd, 1, 0, NULL, 0)) != 0)
    250 		goto err;
    251 	ecsig[0] = ecsig[1] = ecsig[2] = 0;
    252 	cmd[0] = PMS_GET_SCALE; /* E9 */
    253 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    254 	    cmd, 1, 3, ecsig, 0)) != 0)
    255 		goto err;
    256 
    257 	aprint_debug_dev(psc->sc_dev,
    258 		"ALPS PS/2 EC signature: 0x%X 0x%X 0x%X\n",
    259 		ecsig[0], ecsig[1], ecsig[2]);
    260 
    261 	return 0;
    262 
    263 err:
    264 	aprint_debug_dev(psc->sc_dev, "Failed to get EC signature.\n");
    265 	return res;
    266 }
    267 
    268 /*
    269  * Enter to command mode
    270  */
    271 static int
    272 pms_alps_start_command_mode(struct pms_softc *psc)
    273 {
    274 	uint8_t cmd[1];
    275 	uint8_t resp[3];
    276 	int res;
    277 
    278 	cmd[0] = PMS_RESET_WRAP_MODE; /* EC */
    279 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    280 	    cmd, 1, 0, NULL, 0)) != 0)
    281 		goto err;
    282 	cmd[0] = PMS_RESET_WRAP_MODE; /* EC */
    283 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    284 	    cmd, 1, 0, NULL, 0)) != 0)
    285 		goto err;
    286 	cmd[0] = PMS_RESET_WRAP_MODE; /* EC */
    287 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    288 	    cmd, 1, 0, NULL, 0)) != 0)
    289 		goto err;
    290 	resp[0] = resp[1] = resp[2] = 0;
    291 	cmd[0] = PMS_GET_SCALE; /* E9 */
    292 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    293 	    cmd, 1, 3, resp, 0)) != 0)
    294 		goto err;
    295 
    296 	aprint_debug_dev(psc->sc_dev, "ALPS Firmware ID: 0x%x 0x%X 0x%X\n",
    297 		resp[0], resp[1], resp[2]);
    298 
    299 	if (resp[0] != 0x88 || (resp[1] & __BITS(7, 4)) != 0xb0)
    300 		return EINVAL;
    301 
    302 	return 0;
    303 err:
    304 	aprint_error_dev(psc->sc_dev, "Failed to start command mode.\n");
    305 	return res;
    306 }
    307 
    308 /*
    309  * End command mode
    310  */
    311 static int
    312 pms_alps_end_command_mode(struct pms_softc *psc)
    313 {
    314 	int res;
    315 	uint8_t cmd[1];
    316 
    317 	cmd[0] = PMS_SET_STREAM; /* EA */
    318 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    319 	    cmd, 1, 0, NULL, 0)) != 0)
    320 		goto err;
    321 
    322 	return res;
    323 
    324 err:
    325 	aprint_error_dev(psc->sc_dev, "Failed to end command mode.\n");
    326 	return res;
    327 }
    328 
    329 /*
    330  * Write nibble (4-bit) data
    331  */
    332 static int
    333 pms_alps_cm_write_nibble(pckbport_tag_t tag, pckbport_slot_t slot, uint8_t nibble)
    334 {
    335 	uint8_t cmd[2];
    336 	uint8_t resp[3];
    337 	int sendparam;
    338 	int recieve;
    339 	int res;
    340 
    341 	sendparam = alps_v7_nibble_command_data_arr[nibble].sendparam;
    342 	recieve= alps_v7_nibble_command_data_arr[nibble].recieve;
    343 	cmd[0] = alps_v7_nibble_command_data_arr[nibble].command;
    344 	if (recieve) {
    345 		if ((res = pckbport_poll_cmd(tag, slot, cmd, 1, 3, resp, 0)) != 0) {
    346 			aprint_error("send nibble error: %d\n", res);
    347 		}
    348 	} else if (sendparam) {
    349 		cmd[1] = alps_v7_nibble_command_data_arr[nibble].data;
    350 		if ((res = pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0)) != 0) {
    351 			aprint_error("send nibble error: %d\n", res);
    352 		}
    353 	} else {
    354 		if ((res = pckbport_poll_cmd(tag, slot, cmd, 1, 0, NULL, 0)) != 0) {
    355 			aprint_error("send nibble error: %d\n", res);
    356 		}
    357 	}
    358 
    359 	return res;
    360 }
    361 
    362 /*
    363  * Set an register address for read and write
    364  */
    365 static int
    366 pms_alps_set_address(pckbport_tag_t tag, pckbport_slot_t slot, uint16_t reg)
    367 {
    368 	uint8_t cmd[1];
    369 	uint8_t nibble;
    370 	int res;
    371 
    372 	cmd[0] = PMS_RESET_WRAP_MODE;
    373 	if ((res = pckbport_poll_cmd(tag, slot, cmd, 1, 0, NULL, 0)) != 0)
    374 		goto err;
    375 
    376 	/* Set address */
    377 	nibble = (reg >> 12) & __BITS(3, 0);
    378 	if ((res = pms_alps_cm_write_nibble(tag, slot, nibble)) != 0) {
    379 		goto err;
    380 	}
    381 	nibble = (reg >> 8) & __BITS(3, 0);
    382 	if ((res = pms_alps_cm_write_nibble(tag, slot, nibble)) != 0) {
    383 		goto err;
    384 	}
    385 	nibble = (reg >> 4) & __BITS(3, 0);
    386 	if ((res = pms_alps_cm_write_nibble(tag, slot, nibble)) != 0) {
    387 		goto err;
    388 	}
    389 	nibble = (reg >> 0) & __BITS(3, 0);
    390 	if ((res = pms_alps_cm_write_nibble(tag, slot, nibble)) != 0) {
    391 		goto err;
    392 	}
    393 	return res;
    394 
    395 err:
    396 	aprint_error("Failed to set addess.\n");
    397 	return res;
    398 }
    399 
    400 /*
    401  * Read one byte from register
    402  */
    403 static int
    404 pms_alps_cm_read_1(pckbport_tag_t tag, pckbport_slot_t slot, uint16_t reg,
    405 	uint8_t *val)
    406 {
    407 	uint8_t cmd[1];
    408 	uint8_t resp[3];
    409 	int res;
    410 
    411 	if ((res = pms_alps_set_address(tag, slot, reg)) != 0)
    412 		goto err;
    413 
    414 	cmd[0] = PMS_SEND_DEV_STATUS;
    415 	if ((res = pckbport_poll_cmd(tag, slot,
    416 	    cmd, 1, 3, resp, 0)) != 0) {
    417 		goto err;
    418 	}
    419 
    420 	if (reg != ((resp[0] << 8) | resp[1])) {
    421 		return EINVAL;
    422 	}
    423 
    424 	*val = resp[2];
    425 	return res;
    426 
    427 err:
    428 	aprint_error("Failed to read a value.\n");
    429 	*val = 0;
    430 	return res;
    431 }
    432 
    433 /*
    434  * Write one byte to register
    435  */
    436 static int
    437 pms_alps_cm_write_1(pckbport_tag_t tag, pckbport_slot_t slot, uint16_t reg,
    438 	uint8_t val)
    439 {
    440 	uint8_t nibble;
    441 	int res;
    442 
    443 	if ((res = pms_alps_set_address(tag, slot, reg)) != 0)
    444 		goto err;
    445 
    446 	nibble = __SHIFTOUT(val, __BITS(7, 4));
    447 	if ((res = pms_alps_cm_write_nibble(tag, slot, nibble)) != 0) {
    448 		goto err;
    449 	}
    450 
    451 	nibble = __SHIFTOUT(val, __BITS(3, 0));
    452 	if ((res = pms_alps_cm_write_nibble(tag, slot, nibble)) != 0) {
    453 		goto err;
    454 	}
    455 
    456 	return res;
    457 err:
    458 	aprint_error("Failed to write a value.\n");
    459 	return res;
    460 }
    461 
    462 /*
    463  * Not used practically for initialization
    464  */
    465 static int
    466 pms_alps_get_resolution_v7(struct pms_softc *psc)
    467 {
    468 #if 0
    469 	struct alps_softc *sc = &psc->u.alps;
    470 #endif
    471 	pckbport_tag_t tag = psc->sc_kbctag;
    472 	pckbport_slot_t slot = psc->sc_kbcslot;
    473 
    474 	int res;
    475 	uint8_t ret;
    476 #if 0
    477 	uint32_t x_pitch, y_pitch;
    478 	uint32_t x_elec, y_elec;
    479 	uint32_t x_phy, y_phy;
    480 #endif
    481 	/* X/Y pitch */
    482 	if ((res = pms_alps_cm_read_1(tag, slot, 0xc397, &ret)) != 0) {
    483 		goto err;
    484 	}
    485 #if 0
    486 	/* X pitch */
    487 	x_pitch = __SHIFTOUT(ret, __BITS(7, 4)); /* Higher 4-bit */
    488 	x_pitch = x_pitch * 2 + 50; /* Unit = 0.1mm */
    489 
    490 	/* Y pitch */
    491 	y_pitch = ret & __BITS(3, 0); /* Lower 4-bit */
    492 	y_pitch = y_pitch * 2 + 36; /* Unit = 0.1mm */
    493 
    494 	/* X/Y electrode */
    495 	if ((res = pms_alps_cm_read_1(tag, slot, 0xc397 + 1, &ret)) != 0) {
    496 		goto err;
    497 	}
    498 
    499 	/* X electrode */
    500 	x_elec = __SHIFTOUT(ret, __BITS(7, 4)); /* Higher 4-bit */
    501 	x_elec = x_elec + 17;
    502 
    503 	/* Y electrode */
    504 	y_elec = ret & __BITS(3, 0); /* Lower 4-bit */
    505 	y_elec = y_elec + 13;
    506 
    507 	/* X/Y physical in unit = 0.1mm */
    508 	/* X physical */
    509 	x_phy = (x_elec - 1) * x_pitch;
    510 	y_phy = (y_elec - 1) * y_pitch;
    511 
    512 	/* X/Y resolution (unit) */
    513 	sc->res_x = 0xfff * 10 / x_phy;
    514 	sc->res_y = 0x7ff * 10 / y_phy;
    515 #endif
    516 	return res;
    517 
    518 err:
    519 	aprint_error("Failed to get resolution.\n");
    520 	return res;
    521 }
    522 
    523 /*
    524  * Enable tap mode for V2 device
    525  */
    526 static int
    527 pms_alps_enable_tap_mode_v2(struct pms_softc *psc)
    528 {
    529 	uint8_t cmd[2];
    530 	uint8_t resp[3];
    531 	int res;
    532 
    533 	resp[0] = resp[1] = resp[2] = 0;
    534 	cmd[0] = PMS_SEND_DEV_STATUS; /* E9 */
    535 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    536 	    cmd, 1, 3, resp, 0)) != 0)
    537 		goto err;
    538 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    539 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    540 	    cmd, 1, 0, NULL, 0)) != 0)
    541 		goto err;
    542 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    543 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    544 	    cmd, 1, 0, NULL, 0)) != 0)
    545 		goto err;
    546 	cmd[0] = PMS_SET_SAMPLE;
    547 	cmd[1] = 0x0a; /* argument */
    548 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    549 	    cmd, 2, 0, NULL, 0)) != 0)
    550 		goto err;
    551 
    552 	/* Get status */
    553 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    554 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    555 	    cmd, 1, 0, NULL, 0)) != 0)
    556 		goto err;
    557 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    558 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    559 	    cmd, 1, 0, NULL, 0)) != 0)
    560 		goto err;
    561 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    562 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    563 	    cmd, 1, 0, NULL, 0)) != 0)
    564 		goto err;
    565 	resp[0] = resp[1] = resp[2] = 0;
    566 	cmd[0] = PMS_SEND_DEV_STATUS; /* E9 */
    567 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    568 	    cmd, 1, 3, resp, 0)) != 0)
    569 		goto err;
    570 
    571 	aprint_debug_dev(psc->sc_dev, "Tap mode is enabled.\n");
    572 
    573 	return 0;
    574 err:
    575 	aprint_error_dev(psc->sc_dev, "Failed to enable tap mode.\n");
    576 	return res;
    577 }
    578 
    579 static int
    580 pms_alps_init_v2(struct pms_softc *psc)
    581 {
    582 	uint8_t cmd[1];
    583 	int res;
    584 
    585 	if ((res = pms_alps_enable_tap_mode_v2(psc)) != 0)
    586 		goto err;
    587 
    588 	/* Enable absolute mode */
    589 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    590 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    591 	    cmd, 1, 0, NULL, 0)) != 0)
    592 		goto err;
    593 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    594 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    595 	    cmd, 1, 0, NULL, 0)) != 0)
    596 		goto err;
    597 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    598 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    599 	    cmd, 1, 0, NULL, 0)) != 0)
    600 		goto err;
    601 	cmd[0] = PMS_DEV_DISABLE; /* F5 */
    602 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    603 	    cmd, 1, 0, NULL, 0)) != 0)
    604 		goto err;
    605 	cmd[0] = PMS_DEV_ENABLE; /* F4 */
    606 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    607 	    cmd, 1, 0, NULL, 0)) != 0)
    608 		goto err;
    609 
    610 	/* Enable remote mode */
    611 	cmd[0] = PMS_SET_REMOTE_MODE; /* F0 */
    612 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    613 	    cmd, 1, 0, NULL, 0)) != 0)
    614 		goto err;
    615 
    616 	/* Start stream mode to get data */
    617 	cmd[0] = PMS_SET_STREAM; /* EA */
    618 	if ((res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    619 	    cmd, 1, 0, NULL, 0)) != 0)
    620 		goto err;
    621 
    622 
    623 	return 0;
    624 
    625 err:
    626 	aprint_error_dev(psc->sc_dev, "Failed to initialize V2 device.\n");
    627 	return res;
    628 }
    629 
    630 static int
    631 pms_alps_init_v7(struct pms_softc *psc)
    632 {
    633 	uint8_t val;
    634 	uint8_t nibble;
    635 	int res;
    636 
    637 	/* Start command mode */
    638 	if ((res = pms_alps_start_command_mode(psc)) != 0) {
    639 		goto err;
    640 	}
    641 	if ((res = pms_alps_cm_read_1(psc->sc_kbctag, psc->sc_kbcslot, 0xc2d9, &val)) != 0) {
    642 		goto err;
    643 	}
    644 	if ((res = pms_alps_get_resolution_v7(psc)) != 0) {
    645 		goto err;
    646 	}
    647 	if ((res = pms_alps_cm_write_1(psc->sc_kbctag, psc->sc_kbcslot, 0xc2c9, 0x64)) != 0) {
    648 		goto err;
    649 	}
    650 
    651 	/* Start absolute mode */
    652 	if ((res = pms_alps_cm_read_1(psc->sc_kbctag, psc->sc_kbcslot, 0xc2c4, &val)) != 0) {
    653 		goto err;
    654 	}
    655 	/* Do not set address before this, so do not use pms_cm_write_1() */
    656 	val = val | __BIT(1);
    657 	nibble = __SHIFTOUT(val, __BITS(7, 4));
    658 	if ((res = pms_alps_cm_write_nibble(psc->sc_kbctag, psc->sc_kbcslot, nibble)) != 0) {
    659 		goto err;
    660 	}
    661 	nibble = __SHIFTOUT(val, __BITS(3, 0));
    662 	if ((res = pms_alps_cm_write_nibble(psc->sc_kbctag, psc->sc_kbcslot, nibble)) != 0) {
    663 		goto err;
    664 	}
    665 
    666 	/* End command mode */
    667 	if ((res = pms_alps_end_command_mode(psc)) != 0)
    668 		goto err;
    669 
    670 	return res;
    671 
    672 err:
    673 	(void)pms_alps_end_command_mode(psc);
    674 	aprint_error_dev(psc->sc_dev, "Failed to initialize V7 device.\n");
    675 	return res;
    676 }
    677 
    678 int
    679 pms_alps_probe_init(void *opaque)
    680 {
    681 	struct pms_softc *psc = opaque;
    682 	struct alps_softc *sc = &psc->u.alps;
    683 	struct sysctllog *clog = NULL;
    684 	uint8_t e6sig[3];
    685 	uint8_t e7sig[3];
    686 	uint8_t ecsig[3];
    687 	int res;
    688 	u_char cmd[1], resp[3];
    689 
    690 	sc->last_x1 = 0;
    691 	sc->last_y1 = 0;
    692 	sc->last_x2 = 0;
    693 	sc->last_y2 = 0;
    694 	sc->last_nfingers = 0;
    695 
    696 	pckbport_flush(psc->sc_kbctag, psc->sc_kbcslot);
    697 
    698 	if ((res = pms_alps_e6sig(psc, e6sig)) != 0)
    699 		return res; /* This is not ALPS device */
    700 
    701 	if ((res = pms_alps_e7sig(psc, e7sig)) != 0)
    702 		goto err;
    703 
    704 	if ((res = pms_alps_ecsig(psc, ecsig)) != 0)
    705 		goto err;
    706 
    707 	/* Determine protocol version */
    708 	if ((ecsig[0] == 0x88) && (__SHIFTOUT(ecsig[1], __BITS(7, 4)) == 0x0b)) {
    709 		/* V7 device in Toshiba dynabook R63/PS */
    710 		sc->version = ALPS_PROTO_V7;
    711 	} else if ((e7sig[0] == 0x73) && (e7sig[1] == 0x02) &&
    712 		(e7sig[2] == 0x14)) {
    713 		/* V2 device in NEC VJ22MF-7 (VersaPro JVF-7) */
    714 		sc->version = ALPS_PROTO_V2;
    715 	}
    716 
    717 	if (sc->version == ALPS_PROTO_V7) {
    718 		/* Initialize V7 device */
    719 		if ((res = pms_alps_init_v7(psc)) != 0)
    720 			goto err;
    721 		aprint_normal_dev(psc->sc_dev,
    722 			"ALPS PS/2 V7 pointing device\n");
    723 	} else if (sc->version == ALPS_PROTO_V2) {
    724 		/* Initialize V2 pointing device */
    725 		if ((res = pms_alps_init_v2(psc)) != 0)
    726 			goto err;
    727 		aprint_normal_dev(psc->sc_dev,
    728 			"ALPS PS/2 V2 pointing device\n");
    729 	} else {
    730 		res = EINVAL;
    731 		goto err;
    732 	}
    733 
    734 	/* From sysctl */
    735 	pms_sysctl_alps(&clog);
    736 	/* Register hundler */
    737 	if (sc->version == ALPS_PROTO_V7) {
    738 		pckbport_set_inputhandler(psc->sc_kbctag, psc->sc_kbcslot,
    739 			pms_alps_input_v7, psc, device_xname(psc->sc_dev));
    740 	} else if (sc->version == ALPS_PROTO_V2) {
    741 		pckbport_set_inputhandler(psc->sc_kbctag, psc->sc_kbcslot,
    742 			pms_alps_input_v2, psc, device_xname(psc->sc_dev));
    743 	} else {
    744 		res = EINVAL;
    745 		goto err;
    746 	}
    747 	/* Palm detection is enabled. */
    748 
    749 	return 0;
    750 
    751 err:
    752 	cmd[0] = PMS_RESET;
    753 	(void)pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, cmd,
    754 	    1, 2, resp, 1);
    755 	aprint_verbose_dev(psc->sc_dev, "Failed to initialize an ALPS device.\n");
    756 	return res;
    757 }
    758 
    759 void
    760 pms_alps_enable(void *opaque)
    761 {
    762 	struct pms_softc *psc = opaque;
    763 	struct alps_softc *sc = &psc->u.alps;
    764 
    765 	sc->initializing = true;
    766 }
    767 
    768 void
    769 pms_alps_resume(void *opaque)
    770 {
    771 	struct pms_softc *psc = opaque;
    772 	struct alps_softc *sc = &psc->u.alps;
    773 	uint8_t cmd, resp[2];
    774 	int res;
    775 
    776 	cmd = PMS_RESET;
    777 	res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, &cmd,
    778 		1, 2, resp, 1);
    779 	if (res)
    780 	aprint_error_dev(psc->sc_dev,
    781 		"ALPS reset on resume failed\n");
    782 	else {
    783 		if (sc->version == ALPS_PROTO_V7) {
    784 			(void)pms_alps_init_v7(psc);
    785 		} else if (sc->version == ALPS_PROTO_V2) {
    786 			(void)pms_alps_init_v2(psc);
    787 		} else {
    788 			/* Not supported */
    789 		}
    790 		pms_alps_enable(psc);
    791 	}
    792 }
    793 
    794 static void
    795 pms_alps_decode_trackstick_packet_v7(struct pms_softc *psc)
    796 {
    797 	int s;
    798 
    799 	int x, y, z;
    800 	int dx, dy, dz;
    801 	int left, middle, right;
    802 	u_int buttons;
    803 
    804 	x = (int8_t)((psc->packet[2] & 0xbf) |
    805 		((psc->packet[3] & 0x10) << 2));
    806 	y = (int8_t)((psc->packet[3] & 0x07) | (psc->packet[4] & 0xb8) |
    807 		((psc->packet[3] & 0x20) << 1));
    808 	z = (int8_t)((psc->packet[5] & 0x3f) |
    809 		((psc->packet[3] & 0x80) >> 1));
    810 
    811 	dx = x * alps_trackstick_xy_precision;
    812 	dy = y * alps_trackstick_xy_precision;
    813 	dz = z * 1;
    814 
    815 	left = psc->packet[1] & 0x01;
    816 	middle = (psc->packet[1] & 0x04) >> 2;
    817 	right = (psc->packet[1] & 0x02) >> 1;
    818 	buttons = 0;
    819 	buttons = (u_int)((left << 0) | (middle << 1) | (right << 2));
    820 
    821 	s = spltty();
    822 	wsmouse_input(psc->sc_wsmousedev,
    823 		buttons,
    824 		dx, dy, dz, 0,
    825 		WSMOUSE_INPUT_DELTA);
    826 	splx(s);
    827 }
    828 
    829 static uint8_t
    830 pms_alps_decode_packetid_v7(struct pms_softc *psc)
    831 {
    832 	if (psc->packet[4] & 0x40)
    833 		return ALPS_V7_PACKETID_TWOFINGER;
    834 	else if (psc->packet[4] & 0x01)
    835 		return ALPS_V7_PACKETID_MULTIFINGER;
    836 	else if ((psc->packet[0] & 0x10) && !(psc->packet[4] & 0x43))
    837 		return ALPS_V7_PACKETID_NEWPACKET;
    838 	else if ((psc->packet[1] == 0x00) && (psc->packet[4] == 0x00))
    839 		return ALPS_V7_PACKETID_IDLE;
    840 	else
    841 		return ALPS_V7_PACKETID_UNKNOWN;
    842 }
    843 
    844 static void
    845 pms_alps_decode_touchpad_packet_v7(struct pms_softc *psc)
    846 {
    847 	int s;
    848 	struct alps_softc *sc = &psc->u.alps;
    849 	uint8_t packetid;
    850 
    851 	uint16_t cur_x1, cur_y1;
    852 	uint16_t cur_x2, cur_y2;
    853 	int dx1, dy1;
    854 	int button;
    855 	u_int buttons;
    856 
    857 	packetid = pms_alps_decode_packetid_v7(psc);
    858 	switch (packetid) {
    859 	case ALPS_V7_PACKETID_IDLE:
    860 		/* Accept meaningful packets only */
    861 		return;
    862 	case ALPS_V7_PACKETID_UNKNOWN:
    863 		/* Accept meaningful packets only */
    864 		return;
    865 	case ALPS_V7_PACKETID_NEWPACKET:
    866 		/* Sent new packet ID to reset status and not decoded */
    867 		sc->initializing = true;
    868 		return;
    869 	}
    870 
    871 	/* Decode a number of fingers and locations */
    872 	/* X0-11 ... X0-0 */
    873 	cur_x1 = (psc->packet[2] & 0x80) << 4; /* X0-11 */
    874 	cur_x1 |= (psc->packet[2] & 0x3f) << 5; /* X0-10 ... X0-5 */
    875 	cur_x1 |= (psc->packet[3] & 0x30) >> 1; /* X0-4, X0-3 */
    876 	cur_x1 |= psc->packet[3] & 0x07; /* X0-2 ... X0-0 */
    877 
    878 	/* Y0-10 ... Y0-0 */
    879 	cur_y1 = psc->packet[1] << 3; /* Y0-10 ... Y0-3 */
    880 	cur_y1 |= psc->packet[0] & 0x07; /* Y0-2 ... Y0-0 */
    881 
    882 	/* X1-11 ... X1-3 */
    883 	cur_x2 = (psc->packet[3] & 0x80) << 4; /* X1-11 */
    884 	cur_x2 |= (psc->packet[4] & 0x80) << 3; /* X1-10 */
    885 	cur_x2 |= (psc->packet[4] & 0x3f) << 4; /* X1-9 ... X1-4 */
    886 
    887 	/* Y1-10 ... Y1-4 */
    888 	cur_y2 = (psc->packet[5] & 0x80) << 3; /* Y1-10 */
    889 	cur_y2 |= (psc->packet[5] & 0x3f) << 4; /* Y1-9 .. Y1-4 */
    890 
    891 	switch (packetid) {
    892 	case ALPS_V7_PACKETID_TWOFINGER:
    893 		cur_x2 &= ~__BITS(3, 0); /* Clear undefined locations */
    894 		cur_y2 |= __BITS(3, 0); /* Fill undefined locations */
    895 		break;
    896 	case ALPS_V7_PACKETID_MULTIFINGER:
    897 		cur_x2 &= ~__BITS(5, 0); /* Clear undefined locations */
    898 		cur_y2 &= ~__BIT(5); /* Clear duplicate locations */
    899 		cur_y2 |= (psc->packet[4] & __BIT(1)) << 4; /* Set another */
    900 		cur_y2 |= __BITS(4, 0); /* Fill undefined locations */
    901 		break;
    902 	}
    903 
    904 	cur_y1 = 0x7ff - cur_y1;
    905 	cur_y2 = 0x7ff - cur_y2;
    906 
    907 	/* Handle finger touch reported in cur_x2/y2. only */
    908 	if (cur_x1 == 0 && cur_y1 == 0 && cur_x2 != 0 && cur_y2 != 0) {
    909 		cur_x1 = cur_x2;
    910 		cur_y1 = cur_y2;
    911 		cur_x2 = 0;
    912 		cur_y2 = 0;
    913 	}
    914 
    915 	switch (packetid) {
    916 	case ALPS_V7_PACKETID_TWOFINGER:
    917 		if ((cur_x2 == 0) && (cur_y2 == 0))
    918 			sc->nfingers = 1;
    919 		else
    920 			sc->nfingers = 2;
    921 		break;
    922 	case ALPS_V7_PACKETID_MULTIFINGER:
    923 		sc->nfingers = 3 + (psc->packet[5] & 0x03);
    924 		break;
    925 	}
    926 
    927 	button = (psc->packet[0] & 0x80) >> 7;
    928 	buttons = 0;
    929 	if (sc->nfingers == 1) {
    930 		if (button && (cur_y1 > 1700) && (cur_x1 < 1700))
    931 			buttons |= button << 0; /* Left button */
    932 		else if (button && (cur_y1 > 1700)
    933 				&& (1700 <= cur_x1) && (cur_x1 <= 2700))
    934 			buttons |= button << 1; /* Middle button */
    935 		else if (button && (cur_y1 > 1700) && (2700 < cur_x1))
    936 			buttons |= button << 2; /* Right button */
    937 	} else if (sc->nfingers > 1) {
    938 		if (button && (cur_y2 > 1700) && (cur_x2 < 1700))
    939 			buttons |= button << 0; /* Left button */
    940 		else if (button && (cur_y2 > 1700)
    941 				&& (1700 <= cur_x2) && (cur_x2 <= 2700))
    942 			buttons |= button << 1; /* Middle button */
    943 		else if (button && (cur_y2 > 1700) && (2700 < cur_x2))
    944 			buttons |= button << 2; /* Right button */
    945 	}
    946 
    947 	/* New touch */
    948 	if (sc->nfingers == 0 || sc->nfingers != sc->last_nfingers)
    949 		sc->initializing = true;
    950 
    951 	if (sc->initializing == true) {
    952 		dx1 = 0;
    953 		dy1 = 0;
    954 	} else {
    955 		dx1 = (int16_t)(cur_x1 - sc->last_x1);
    956 		dy1 = (int16_t)(sc->last_y1 - cur_y1);
    957 
    958 		dx1 = dx1 >> alps_touchpad_xy_unprecision;
    959 		dy1 = dy1 >> alps_touchpad_xy_unprecision;
    960 	}
    961 
    962 	/* Allow finger detouch during drag and drop */
    963 	if ((sc->nfingers < sc->last_nfingers)
    964 		&& (cur_x2 == sc->last_x1) && (cur_y2 == sc->last_y1)) {
    965 		sc->last_x1 = sc->last_x2;
    966 		sc->last_y1 = sc->last_y2;
    967 		dx1 = 0;
    968 		dy1 = 0;
    969 	}
    970 
    971 	s = spltty();
    972 	wsmouse_input(psc->sc_wsmousedev,
    973 		buttons,
    974 		dx1, dy1, 0, 0,
    975 		WSMOUSE_INPUT_DELTA);
    976 	splx(s);
    977 
    978 	if (sc->initializing == true || (dx1 != 0))
    979 		sc->last_x1 = cur_x1;
    980 	if (sc->initializing == true || (dy1 != 0))
    981 		sc->last_y1 = cur_y1;
    982 
    983 	if (sc->nfingers > 0)
    984 		sc->initializing = false;
    985 	sc->last_nfingers = sc->nfingers;
    986 }
    987 
    988 static void
    989 pms_alps_dispatch_packet_v7(struct pms_softc *psc)
    990 {
    991 	if ((psc->packet[0] == 0x48) && ((psc->packet[4] & 0x47) == 0x06))
    992 		pms_alps_decode_trackstick_packet_v7(psc);
    993 	else
    994 		pms_alps_decode_touchpad_packet_v7(psc);
    995 }
    996 
    997 static void
    998 pms_alps_decode_touchpad_packet_v2(struct pms_softc *psc)
    999 {
   1000 	int s;
   1001 	struct alps_softc *sc = &psc->u.alps;
   1002 	uint16_t cur_x, cur_y;
   1003 	int16_t dx, dy;
   1004 	u_int left, middle, right;
   1005 	u_int forward, back;
   1006 	u_int buttons;
   1007 	uint8_t ges;
   1008 
   1009 	sc->nfingers = (psc->packet[2] & 0x02) >> 1;
   1010 	if (sc->last_nfingers == 0)
   1011 		sc->initializing = true;
   1012 
   1013 	left = psc->packet[3] & 0x01;
   1014 	right = (psc->packet[3] & 0x02) >> 1;
   1015 	middle = (psc->packet[3] & 0x04) >> 2;
   1016 
   1017 	cur_x = psc->packet[1];
   1018 	cur_x |= (psc->packet[2] & 0x78) << 4;
   1019 
   1020 	cur_y = psc->packet[4];
   1021 	cur_y |= (psc->packet[3] & 0x70) << 3;
   1022 
   1023 #if 0
   1024 	cur_z = psc->packet[5];
   1025 #endif
   1026 
   1027 	forward = (psc->packet[2] & 0x04) >> 2;
   1028 	back = (psc->packet[3] & 0x04) >> 2;
   1029 	ges = psc->packet[2] & 0x01;
   1030 
   1031 	buttons = (left | ges) << 0;
   1032 	buttons |= (middle | forward | back) << 1;
   1033 	buttons |= right << 2;
   1034 
   1035 	if (sc->initializing == true) {
   1036 		dx = 0;
   1037 		dy = 0;
   1038 	} else {
   1039 		dx = (cur_x - sc->last_x1);
   1040 		dy = (sc->last_y1 - cur_y);
   1041 
   1042 		dx = dx >> alps_touchpad_xy_unprecision;
   1043 		dy = dy >> alps_touchpad_xy_unprecision;
   1044 	}
   1045 
   1046 	s = spltty();
   1047 	wsmouse_input(psc->sc_wsmousedev,
   1048 		buttons,
   1049 		dx, dy, 0, 0,
   1050 		WSMOUSE_INPUT_DELTA);
   1051 	splx(s);
   1052 
   1053 	if (sc->initializing == true || (dx != 0))
   1054 		sc->last_x1 = cur_x;
   1055 	if (sc->initializing == true || (dy != 0))
   1056 		sc->last_y1 = cur_y;
   1057 
   1058 	if (sc->nfingers > 0)
   1059 		sc->initializing = false;
   1060 	sc->last_nfingers = sc->nfingers;
   1061 }
   1062 
   1063 static void
   1064 pms_alps_input_v2(void *opaque, int data)
   1065 {
   1066 	struct pms_softc *psc = opaque;
   1067 
   1068 	if (!psc->sc_enabled)
   1069 		return;
   1070 
   1071 	psc->packet[psc->inputstate++] = data & 0xff;
   1072 	if (psc->inputstate < 6)
   1073 		return;
   1074 
   1075 	pms_alps_decode_touchpad_packet_v2(psc);
   1076 
   1077 	psc->inputstate = 0;
   1078 }
   1079 
   1080 static void
   1081 pms_alps_input_v7(void *opaque, int data)
   1082 {
   1083 	struct pms_softc *psc = opaque;
   1084 
   1085 	if (!psc->sc_enabled)
   1086 		return;
   1087 
   1088 	psc->packet[psc->inputstate++] = data & 0xff;
   1089 	if (psc->inputstate < 6)
   1090 		return;
   1091 
   1092 	pms_alps_dispatch_packet_v7(psc);
   1093 
   1094 	psc->inputstate = 0;
   1095 }
   1096