1/* $NetBSD: adb_bus.c,v 1.12 2021/08/07 16:19:09 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 2006 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: adb_bus.c,v 1.12 2021/08/07 16:19:09 thorpej Exp $"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/kernel.h> 35#include <sys/device.h> 36#include <sys/proc.h> 37 38#include <sys/bus.h> 39#include <machine/autoconf.h> 40#include <dev/adb/adbvar.h> 41 42#include "adbdebug.h" 43 44#ifdef ADB_DEBUG 45#define DPRINTF printf 46#else 47#define DPRINTF while (0) printf 48#endif 49 50static int nadb_match(device_t, cfdata_t, void *); 51static void nadb_attach(device_t, device_t, void *); 52 53struct nadb_softc { 54 device_t sc_dev; 55 struct adb_bus_accessops *sc_ops; 56 uint32_t sc_msg; 57 uint32_t sc_event; 58 struct adb_device sc_devtable[16]; 59 int sc_free; /* highest free address */ 60 int sc_len; /* length of received message */ 61 uint8_t sc_data[16]; 62}; 63 64CFATTACH_DECL_NEW(nadb, sizeof(struct nadb_softc), 65 nadb_match, nadb_attach, NULL, NULL); 66 67static void nadb_init(device_t); 68static void nadb_handler(void *, int, uint8_t *); 69static void nadb_send_sync(void *, int, int, uint8_t *); 70static int nadb_register(struct nadb_softc *, int, int, int); 71static void nadb_remove(struct nadb_softc *, int); 72static int nadb_devprint(void *, const char *); 73 74static int 75nadb_match(device_t parent, cfdata_t cf, void *aux) 76{ 77 78 return 1; 79} 80 81static void 82nadb_attach(device_t parent, device_t self, void *aux) 83{ 84 struct nadb_softc *sc = device_private(self); 85 struct adb_bus_accessops *ops = aux; 86 87 sc->sc_dev = self; 88 sc->sc_ops = ops; 89 sc->sc_ops->set_handler(sc->sc_ops->cookie, nadb_handler, sc); 90 91 config_interrupts(self, nadb_init); 92} 93 94static void 95nadb_init(device_t dev) 96{ 97 struct nadb_softc *sc = device_private(dev); 98 struct adb_attach_args aaa; 99 int i, last_moved_up, devmask = 0; 100 uint8_t cmd[2]; 101 102 sc->sc_free = 15; 103 for (i = 0; i < 16; i++) { 104 sc->sc_devtable[i].original_addr = 0; 105 sc->sc_devtable[i].current_addr = 0; 106 sc->sc_devtable[i].handler_id = 0; 107 sc->sc_devtable[i].cookie = NULL; 108 sc->sc_devtable[i].handler = NULL; 109 } 110 111 /* bus reset (?) */ 112 nadb_send_sync(sc, 0, 0, NULL); 113 delay(200000); 114 115 /* 116 * scan only addresses 1 - 7 117 * if something responds move it to >7 and see if something else is 118 * there. If not move the previous one back. 119 * XXX we don't check for collisions if we use up all addresses >7 120 */ 121 for (i = 1; i < 8; i++) { 122 DPRINTF("\n%d: ", i); 123 last_moved_up = 0; 124 nadb_send_sync(sc, ADBTALK(i, 3), 0, NULL); 125 /* found something? */ 126 while (sc->sc_len > 2) { 127 /* something answered, so move it up */ 128 129 DPRINTF("Found a device on address %d\n", i); 130 cmd[0] = sc->sc_free | 0x60; 131 cmd[1] = 0xfe; 132 nadb_send_sync(sc, ADBLISTEN(i, 3), 2, cmd); 133 134 /* see if it really moved */ 135 nadb_send_sync(sc, ADBTALK(sc->sc_free, 3), 0, NULL); 136 if (sc->sc_len > 2) { 137 /* ok */ 138 DPRINTF("moved it to %d\n", sc->sc_free); 139 nadb_register(sc, sc->sc_free, i, sc->sc_data[3]); 140 last_moved_up = sc->sc_free; 141 sc->sc_free--; 142 } 143 /* see if something else is there */ 144 nadb_send_sync(sc, ADBTALK(i, 3), 0, NULL); 145 } 146 if (last_moved_up != 0) { 147 /* move last one back to original address */ 148 cmd[0] = i | 0x60; 149 cmd[1] = 0xfe; 150 nadb_send_sync(sc, ADBLISTEN(last_moved_up, 3), 2, cmd); 151 152 nadb_send_sync(sc, ADBTALK(i, 3), 0, NULL); 153 if (sc->sc_len > 2) { 154 DPRINTF("moved %d back to %d\n", last_moved_up, i); 155 nadb_remove(sc, last_moved_up); 156 nadb_register(sc, i, i, sc->sc_data[3]); 157 sc->sc_free = last_moved_up; 158 } 159 } 160 } 161 162 /* now attach the buggers we've found */ 163 aaa.ops = sc->sc_ops; 164 for (i = 0; i < 16; i++) { 165 if (sc->sc_devtable[i].current_addr != 0) { 166 DPRINTF("dev: %d %d %02x\n", 167 sc->sc_devtable[i].current_addr, 168 sc->sc_devtable[i].original_addr, 169 sc->sc_devtable[i].handler_id); 170 aaa.dev = &sc->sc_devtable[i]; 171 if (config_found(sc->sc_dev, &aaa, nadb_devprint, 172 CFARGS_NONE)) { 173 devmask |= (1 << i); 174 } else { 175 aprint_normal(" not configured\n"); 176 } 177 } 178 } 179 /* now enable autopolling */ 180 DPRINTF("devmask: %04x\n", devmask); 181 sc->sc_ops->autopoll(sc->sc_ops->cookie, devmask); 182} 183 184int 185nadb_print(void *aux, const char *what) 186{ 187 aprint_normal(": Apple Desktop Bus\n"); 188 return 0; 189} 190 191static int 192nadb_devprint(void *aux, const char *what) 193{ 194 struct adb_attach_args *aaa = aux; 195 196 if (what == NULL) 197 return 0; 198 199 switch(aaa->dev->original_addr) { 200 case 2: 201 aprint_normal("%s: ADB Keyboard", what); 202 break; 203 case 3: 204 aprint_normal("%s: ADB relative pointing device", what); 205 break; 206 default: 207 aprint_normal("%s: something from address %d:%02x", 208 what, 209 aaa->dev->original_addr, 210 aaa->dev->handler_id); 211 break; 212 } 213 return 0; 214} 215 216static void 217nadb_handler(void *cookie, int len, uint8_t *data) 218{ 219 struct nadb_softc *sc = cookie; 220 struct adb_device *dev; 221 int addr; 222 223#ifdef ADB_DEBUG 224 int i; 225 printf("adb:"); 226 for (i = 0; i < len; i++) { 227 printf(" %02x", data[i]); 228 } 229 printf("\n"); 230#endif 231 232 addr = data[1] >> 4; 233 dev = &sc->sc_devtable[addr]; 234 if ((dev->current_addr != 0) && (dev->handler != NULL)) { 235 236 dev->handler(dev->cookie, len, data); 237 } else { 238 sc->sc_msg = 1; 239 sc->sc_len = len; 240 memcpy(sc->sc_data, data, len); 241 wakeup(&sc->sc_event); 242 } 243} 244 245static void 246nadb_send_sync(void *cookie, int command, int len, uint8_t *data) 247{ 248 struct nadb_softc *sc = cookie; 249 250 sc->sc_msg = 0; 251 sc->sc_ops->send(sc->sc_ops->cookie, 0, command, len, data); 252 while (sc->sc_msg == 0) { 253 tsleep(&sc->sc_event, 0, "adb_send", 100); 254 } 255} 256 257static int 258nadb_register(struct nadb_softc *sc, int current, int orig, int handler) 259{ 260 struct adb_device *dev; 261 262 if ((current > 0) && (current < 16)) { 263 dev = &sc->sc_devtable[current]; 264 if (dev->current_addr != 0) 265 /* in use! */ 266 return -1; 267 dev->current_addr = current; 268 dev->original_addr = orig; 269 dev->handler_id = handler; 270 return 0; 271 } 272 return -1; 273} 274 275static void 276nadb_remove(struct nadb_softc *sc, int addr) 277{ 278 279 if ((addr > 0) && (addr < 16)) { 280 sc->sc_devtable[addr].current_addr = 0; 281 sc->sc_devtable[addr].original_addr = 0; 282 sc->sc_devtable[addr].handler_id = 0; 283 } 284} 285