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