1 /* $NetBSD: adb_bus.c,v 1.6 2007/10/19 11:59:36 ad 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.6 2007/10/19 11:59:36 ad 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(struct device *, struct cfdata *, void *); 54 static void nadb_attach(struct device *, struct device *, void *); 55 56 struct nadb_softc { 57 struct device 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(nadb, sizeof(struct nadb_softc), 68 nadb_match, nadb_attach, NULL, NULL); 69 70 static void nadb_init(struct device *); 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(struct device *parent, struct cfdata *cf, void *aux) 79 { 80 81 return 1; 82 } 83 84 static void 85 nadb_attach(struct device *parent, struct device *us, void *aux) 86 { 87 struct nadb_softc *sc = (struct nadb_softc *)us; 88 struct adb_bus_accessops *ops = aux; 89 90 sc->sc_ops = ops; 91 sc->sc_ops->set_handler(sc->sc_ops->cookie, nadb_handler, sc); 92 93 config_interrupts(us, nadb_init); 94 } 95 96 static void 97 nadb_init(struct device *dev) 98 { 99 struct nadb_softc *sc = (struct nadb_softc *)dev; 100 struct adb_attach_args aaa; 101 int i, last_moved_up, devmask = 0; 102 uint8_t cmd[2]; 103 104 sc->sc_free = 15; 105 for (i = 0; i < 16; i++) { 106 sc->sc_devtable[i].original_addr = 0; 107 sc->sc_devtable[i].current_addr = 0; 108 sc->sc_devtable[i].handler_id = 0; 109 sc->sc_devtable[i].cookie = NULL; 110 sc->sc_devtable[i].handler = NULL; 111 } 112 113 /* bus reset (?) */ 114 nadb_send_sync(sc, 0, 0, NULL); 115 delay(200000); 116 117 /* 118 * scan only addresses 1 - 7 119 * if something responds move it to >7 and see if something else is 120 * there. If not move the previous one back. 121 * XXX we don't check for collisions if we use up all addresses >7 122 */ 123 for (i = 1; i < 8; i++) { 124 DPRINTF("\n%d: ", i); 125 last_moved_up = 0; 126 nadb_send_sync(sc, ADBTALK(i, 3), 0, NULL); 127 /* found something? */ 128 while (sc->sc_len > 2) { 129 /* something answered, so move it up */ 130 131 DPRINTF("Found a device on address %d\n", i); 132 cmd[0] = sc->sc_free | 0x60; 133 cmd[1] = 0xfe; 134 nadb_send_sync(sc, ADBLISTEN(i, 3), 2, cmd); 135 136 /* see if it really moved */ 137 nadb_send_sync(sc, ADBTALK(sc->sc_free, 3), 0, NULL); 138 if (sc->sc_len > 2) { 139 /* ok */ 140 DPRINTF("moved it to %d\n", sc->sc_free); 141 nadb_register(sc, sc->sc_free, i, sc->sc_data[3]); 142 last_moved_up = sc->sc_free; 143 sc->sc_free--; 144 } 145 /* see if something else is there */ 146 nadb_send_sync(sc, ADBTALK(i, 3), 0, NULL); 147 } 148 if (last_moved_up != 0) { 149 /* move last one back to original address */ 150 cmd[0] = i | 0x60; 151 cmd[1] = 0xfe; 152 nadb_send_sync(sc, ADBLISTEN(last_moved_up, 3), 2, cmd); 153 154 nadb_send_sync(sc, ADBTALK(i, 3), 0, NULL); 155 if (sc->sc_len > 2) { 156 DPRINTF("moved %d back to %d\n", last_moved_up, i); 157 nadb_remove(sc, last_moved_up); 158 nadb_register(sc, i, i, sc->sc_data[3]); 159 sc->sc_free = last_moved_up; 160 } 161 } 162 } 163 164 /* now attach the buggers we've found */ 165 aaa.ops = sc->sc_ops; 166 for (i = 0; i < 16; i++) { 167 if (sc->sc_devtable[i].current_addr != 0) { 168 DPRINTF("dev: %d %d %02x\n", 169 sc->sc_devtable[i].current_addr, 170 sc->sc_devtable[i].original_addr, 171 sc->sc_devtable[i].handler_id); 172 aaa.dev = &sc->sc_devtable[i]; 173 if (config_found(&sc->sc_dev, &aaa, nadb_devprint)) { 174 devmask |= (1 << i); 175 } else { 176 printf(" not configured\n"); 177 } 178 } 179 } 180 /* now enable autopolling */ 181 DPRINTF("devmask: %04x\n", devmask); 182 sc->sc_ops->autopoll(sc->sc_ops->cookie, devmask); 183 } 184 185 int 186 nadb_print(void *aux, const char *what) 187 { 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