1 /* $NetBSD: adb_bus.c,v 1.9 2009/05/12 14:07:01 cegger 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.9 2009/05/12 14:07:01 cegger 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 50 static int nadb_match(device_t, cfdata_t, void *); 51 static void nadb_attach(device_t, device_t, void *); 52 53 struct 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 64 CFATTACH_DECL_NEW(nadb, sizeof(struct nadb_softc), 65 nadb_match, nadb_attach, NULL, NULL); 66 67 static void nadb_init(device_t); 68 static void nadb_handler(void *, int, uint8_t *); 69 static void nadb_send_sync(void *, int, int, uint8_t *); 70 static int nadb_register(struct nadb_softc *, int, int, int); 71 static void nadb_remove(struct nadb_softc *, int); 72 static int nadb_devprint(void *, const char *); 73 74 static int 75 nadb_match(device_t parent, cfdata_t cf, void *aux) 76 { 77 78 return 1; 79 } 80 81 static void 82 nadb_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 94 static void 95 nadb_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 devmask |= (1 << i); 173 } else { 174 printf(" not configured\n"); 175 } 176 } 177 } 178 /* now enable autopolling */ 179 DPRINTF("devmask: %04x\n", devmask); 180 sc->sc_ops->autopoll(sc->sc_ops->cookie, devmask); 181 } 182 183 int 184 nadb_print(void *aux, const char *what) 185 { 186 printf(": Apple Desktop Bus\n"); 187 return 0; 188 } 189 190 static int 191 nadb_devprint(void *aux, const char *what) 192 { 193 struct adb_attach_args *aaa = aux; 194 195 if (what == NULL) 196 return 0; 197 198 switch(aaa->dev->original_addr) { 199 case 2: 200 printf("%s: ADB Keyboard", what); 201 break; 202 case 3: 203 printf("%s: ADB relative pointing device", what); 204 break; 205 default: 206 printf("%s: something from address %d:%02x", 207 what, 208 aaa->dev->original_addr, 209 aaa->dev->handler_id); 210 break; 211 } 212 return 0; 213 } 214 215 static void 216 nadb_handler(void *cookie, int len, uint8_t *data) 217 { 218 struct nadb_softc *sc = cookie; 219 struct adb_device *dev; 220 int addr; 221 222 #ifdef ADB_DEBUG 223 int i; 224 printf("adb:"); 225 for (i = 0; i < len; i++) { 226 printf(" %02x", data[i]); 227 } 228 printf("\n"); 229 #endif 230 231 addr = data[1] >> 4; 232 dev = &sc->sc_devtable[addr]; 233 if ((dev->current_addr != 0) && (dev->handler != NULL)) { 234 235 dev->handler(dev->cookie, len, data); 236 } else { 237 sc->sc_msg = 1; 238 sc->sc_len = len; 239 memcpy(sc->sc_data, data, len); 240 wakeup(&sc->sc_event); 241 } 242 } 243 244 static void 245 nadb_send_sync(void *cookie, int command, int len, uint8_t *data) 246 { 247 struct nadb_softc *sc = cookie; 248 249 sc->sc_msg = 0; 250 sc->sc_ops->send(sc->sc_ops->cookie, 0, command, len, data); 251 while (sc->sc_msg == 0) { 252 tsleep(&sc->sc_event, 0, "adb_send", 100); 253 } 254 } 255 256 static int 257 nadb_register(struct nadb_softc *sc, int current, int orig, int handler) 258 { 259 struct adb_device *dev; 260 261 if ((current > 0) && (current < 16)) { 262 dev = &sc->sc_devtable[current]; 263 if (dev->current_addr != 0) 264 /* in use! */ 265 return -1; 266 dev->current_addr = current; 267 dev->original_addr = orig; 268 dev->handler_id = handler; 269 return 0; 270 } 271 return -1; 272 } 273 274 static void 275 nadb_remove(struct nadb_softc *sc, int addr) 276 { 277 278 if ((addr > 0) && (addr < 16)) { 279 sc->sc_devtable[addr].current_addr = 0; 280 sc->sc_devtable[addr].original_addr = 0; 281 sc->sc_devtable[addr].handler_id = 0; 282 } 283 } 284