Index: dev/adb/adb_mouse.c =================================================================== --- dev/adb/adb_mouse.c (revision 0) +++ dev/adb/adb_mouse.c (revision 0) @@ -0,0 +1,538 @@ +/*- + * Copyright (C) 2008 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/powerpc/include/cudavar.h,v 1.7 2008/02/12 18:14:45 marcel Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "adb.h" + +#define CDEV_GET_SOFTC(x) devclass_get_softc(adb_mouse_devclass, minor(x) & 0x1f) + +static int adb_mouse_probe(device_t dev); +static int adb_mouse_attach(device_t dev); +static int adb_mouse_detach(device_t dev); + +static d_open_t ams_open; +static d_close_t ams_close; +static d_read_t ams_read; +static d_ioctl_t ams_ioctl; +static d_poll_t ams_poll; + +static u_int adb_mouse_receive_packet(device_t dev, u_char status, u_char command, u_char reg, int len, u_char *data); + +struct adb_mouse_softc { + device_t sc_dev; + + struct mtx sc_mtx; + struct cv sc_cv; + + int extended; + uint16_t dpi; + + mousehw_t hw; + mousemode_t mode; + u_char id[4]; + + int buttons; + int last_buttons; + int xdelta, ydelta; + + int8_t packet[8]; + size_t packet_read_len; + + struct cdev *cdev; + struct selinfo rsel; +}; + +static device_method_t adb_mouse_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, adb_mouse_probe), + DEVMETHOD(device_attach, adb_mouse_attach), + DEVMETHOD(device_detach, adb_mouse_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* ADB interface */ + DEVMETHOD(adb_receive_packet, adb_mouse_receive_packet), + + { 0, 0 } +}; + +static driver_t adb_mouse_driver = { + "ams", + adb_mouse_methods, + sizeof(struct adb_mouse_softc), +}; + +devclass_t adb_mouse_devclass; + +DRIVER_MODULE(ams, adb, adb_mouse_driver, adb_mouse_devclass, 0, 0); + +static struct cdevsw ams_cdevsw = { + .d_version = D_VERSION, + .d_flags = 0, + .d_open = ams_open, + .d_close = ams_close, + .d_read = ams_read, + .d_ioctl = ams_ioctl, + .d_poll = ams_poll, + .d_name = "ams", +}; + +static int +adb_mouse_probe(device_t dev) +{ + uint8_t type; + + type = adb_get_device_type(dev); + + if (type != ADB_DEVICE_MOUSE) + return (ENXIO); + + device_set_desc(dev,"ADB Mouse"); + return (0); +} + +static int +adb_mouse_attach(device_t dev) +{ + struct adb_mouse_softc *sc; + char *description = "Unknown Pointing Device"; + + size_t r1_len; + u_char r1[8]; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + mtx_init(&sc->sc_mtx,"ams",MTX_DEF,0); + cv_init(&sc->sc_cv,"ams"); + + sc->extended = 0; + + sc->hw.buttons = 2; + sc->hw.iftype = MOUSE_IF_UNKNOWN; + sc->hw.type = MOUSE_UNKNOWN; + sc->hw.model = sc->hw.hwid = 0; + + sc->mode.protocol = MOUSE_PROTO_SYSMOUSE; + sc->mode.rate = -1; + sc->mode.resolution = 100; + sc->mode.accelfactor = 0; + sc->mode.level = 0; + sc->mode.packetsize = 5; + + sc->buttons = 0; + sc->last_buttons = 0; + sc->packet_read_len = 0; + + /* Try to switch to extended protocol */ + adb_set_device_handler(dev,4); + + switch(adb_get_device_handler(dev)) { + case 1: + sc->mode.resolution = 100; + break; + case 2: + sc->mode.resolution = 200; + break; + case 4: + adb_read_register(dev,1,&r1_len,r1); + if (r1_len < 8) + break; + + sc->extended = 1; + memcpy(&sc->hw.hwid,r1,4); + sc->mode.resolution = (r1[4] << 8) | r1[5]; + + switch (r1[6]) { + case 0: + sc->hw.type = MOUSE_PAD; + description = "Tablet"; + break; + case 1: + sc->hw.type = MOUSE_MOUSE; + description = "Mouse"; + break; + case 2: + sc->hw.type = MOUSE_TRACKBALL; + description = "Trackball"; + break; + } + + sc->hw.buttons = r1[7]; + + device_printf(dev,"%d-button %d-dpi %s\n", + sc->hw.buttons, sc->mode.resolution,description); + + /* + * Check for one of MacAlly's non-compliant 2-button mice. + * These claim to speak the extended mouse protocol, but + * instead speak the standard protocol and only when their + * handler is set to 0x42. + */ + + if (sc->hw.hwid == 0x4b4f4954) { + adb_set_device_handler(dev,0x42); + + if (adb_get_device_handler(dev) == 0x42) { + device_printf(dev, "MacAlly 2-Button Mouse\n"); + sc->extended = 0; + } + } + + break; + } + + sc->cdev = make_dev(&ams_cdevsw, device_get_unit(dev), + UID_ROOT, GID_OPERATOR, 0644, "ams%d", + device_get_unit(dev)); + + adb_set_autopoll(dev,1); + + return (0); +} + +static int +adb_mouse_detach(device_t dev) +{ + struct adb_mouse_softc *sc; + + adb_set_autopoll(dev,0); + + sc = device_get_softc(dev); + destroy_dev(sc->cdev); + + mtx_destroy(&sc->sc_mtx); + cv_destroy(&sc->sc_cv); + + return (0); +} + +static u_int +adb_mouse_receive_packet(device_t dev, u_char status, u_char command, + u_char reg, int len, u_char *data) +{ + struct adb_mouse_softc *sc; + int i = 0; + int xdelta, ydelta; + int buttons; + + sc = device_get_softc(dev); + + if (command != ADB_COMMAND_TALK || reg != 0 || len < 2) + return (0); + + ydelta = data[0] & 0x7f; + xdelta = data[1] & 0x7f; + + buttons = 0; + buttons |= !(data[0] & 0x80); + buttons |= !(data[1] & 0x80) << 1; + + if (sc->extended) { + for (i = 2; i < len && i < 5; i++) { + xdelta |= (data[i] & 0x07) << (3*i + 1); + ydelta |= (data[i] & 0x70) << (3*i - 3); + + buttons |= !(data[i] & 0x08) << (2*i - 2); + buttons |= !(data[i] & 0x80) << (2*i - 1); + } + } else { + len = 2; /* Ignore extra data */ + } + + /* Do sign extension as necessary */ + if (xdelta & (0x40 << 3*(len-2))) + xdelta |= 0xffffffc0 << 3*(len - 2); + if (ydelta & (0x40 << 3*(len-2))) + ydelta |= 0xffffffc0 << 3*(len - 2); + + /* + * Some mice report high-numbered buttons on the wrong button number, + * so set the highest-numbered real button as pressed if there are + * mysterious high-numbered ones set. + */ + + if (buttons & ~((1 << sc->hw.buttons) - 1)) { + buttons |= 1 << (sc->hw.buttons - 1); + buttons &= (1 << sc->hw.buttons) - 1; + } + + mtx_lock(&sc->sc_mtx); + + /* Add in our new deltas, and take into account + Apple's opposite meaning for Y axis motion */ + + sc->xdelta += xdelta; + sc->ydelta -= ydelta; + + sc->buttons = buttons; + + mtx_unlock(&sc->sc_mtx); + + cv_broadcast(&sc->sc_cv); + selwakeuppri(&sc->rsel, PZERO); + + return (0); +} + +static int +ams_open(struct cdev *dev, int flag, int fmt, struct thread *p) +{ + struct adb_mouse_softc *sc; + + sc = CDEV_GET_SOFTC(dev); + if (sc == NULL) + return (ENXIO); + + mtx_lock(&sc->sc_mtx); + sc->packet_read_len = 0; + sc->xdelta = 0; + sc->ydelta = 0; + sc->buttons = 0; + mtx_unlock(&sc->sc_mtx); + + return (0); +} + +static int +ams_close(struct cdev *dev, int flag, int fmt, struct thread *p) +{ + struct adb_mouse_softc *sc; + + sc = CDEV_GET_SOFTC(dev); + + cv_broadcast(&sc->sc_cv); + selwakeuppri(&sc->rsel, PZERO); + return (0); +} + +static int +ams_poll(struct cdev *dev, int events, struct thread *p) +{ + struct adb_mouse_softc *sc; + + sc = CDEV_GET_SOFTC(dev); + if (sc == NULL) + return (EIO); + + if (events & (POLLIN | POLLRDNORM)) { + mtx_lock(&sc->sc_mtx); + + if (sc->xdelta == 0 && sc->ydelta == 0 && + sc->buttons == sc->last_buttons) { + selrecord(p, &sc->rsel); + events = 0; + } else { + events &= (POLLIN | POLLRDNORM); + } + + mtx_unlock(&sc->sc_mtx); + } + + return events; +} + +static int +ams_read(struct cdev *dev, struct uio *uio, int flag) +{ + struct adb_mouse_softc *sc; + size_t len; + int8_t outpacket[8]; + + sc = CDEV_GET_SOFTC(dev); + if (sc == NULL) + return (EIO); + + if (uio->uio_resid <= 0) + return (0); + + mtx_lock(&sc->sc_mtx); + + if (!sc->packet_read_len) { + if (sc->xdelta == 0 && sc->ydelta == 0 && + sc->buttons == sc->last_buttons) { + + if (flag & O_NONBLOCK) { + mtx_unlock(&sc->sc_mtx); + return EWOULDBLOCK; + } + + + /* Otherwise, block on new data */ + cv_wait(&sc->sc_cv,&sc->sc_mtx); + } + + sc->packet[0] = 1 << 7; + sc->packet[0] |= (!(sc->buttons & 1)) << 2; + sc->packet[0] |= (!(sc->buttons & 4)) << 1; + sc->packet[0] |= (!(sc->buttons & 2)); + + if (sc->xdelta > 127) { + sc->packet[1] = 127; + sc->packet[3] = sc->xdelta - 127; + } else if (sc->xdelta < -127) { + sc->packet[1] = -127; + sc->packet[3] = sc->xdelta + 127; + } else { + sc->packet[1] = sc->xdelta; + sc->packet[3] = 0; + } + + if (sc->ydelta > 127) { + sc->packet[2] = 127; + sc->packet[4] = sc->ydelta - 127; + } else if (sc->ydelta < -127) { + sc->packet[2] = -127; + sc->packet[4] = sc->ydelta + 127; + } else { + sc->packet[2] = sc->ydelta; + sc->packet[4] = 0; + } + + /* No Z movement */ + sc->packet[5] = 0; + sc->packet[6] = 0; + + sc->packet[7] = ~((uint8_t)(sc->buttons >> 3)) & 0x7f; + + + sc->last_buttons = sc->buttons; + sc->xdelta = 0; + sc->ydelta = 0; + + sc->packet_read_len = sc->mode.packetsize; + } + + len = (sc->packet_read_len > uio->uio_resid) ? + uio->uio_resid : sc->packet_read_len; + + memcpy(outpacket,sc->packet + + (sc->mode.packetsize - sc->packet_read_len),len); + sc->packet_read_len -= len; + + mtx_unlock(&sc->sc_mtx); + + uiomove(outpacket,len,uio); + + return (0); +} + + +static int +ams_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, + struct thread *p) +{ + struct adb_mouse_softc *sc; + mousemode_t mode; + + sc = CDEV_GET_SOFTC(dev); + if (sc == NULL) + return (EIO); + + switch (cmd) { + case MOUSE_GETHWINFO: + *(mousehw_t *)addr = sc->hw; + break; + case MOUSE_GETMODE: + *(mousemode_t *)addr = sc->mode; + break; + case MOUSE_SETMODE: + mode = *(mousemode_t *)addr; + addr = (caddr_t)&mode.level; + + /* Fallthrough */ + + case MOUSE_SETLEVEL: + if (*(int *)addr == -1) + break; + else if (*(int *)addr == 1) { + sc->mode.level = 1; + sc->mode.packetsize = 8; + break; + } else if (*(int *)addr == 0) { + sc->mode.level = 0; + sc->mode.packetsize = 5; + break; + } + + return EINVAL; + case MOUSE_GETLEVEL: + *(int *)addr = sc->mode.level; + break; + + case MOUSE_GETSTATUS: { + mousestatus_t *status = (mousestatus_t *) addr; + + mtx_lock(&sc->sc_mtx); + + status->button = sc->buttons; + status->obutton = sc->last_buttons; + + status->flags = status->button ^ status->obutton; + + if (sc->xdelta != 0 || sc->ydelta) + status->flags |= MOUSE_POSCHANGED; + if (status->button != status->obutton) + status->flags |= MOUSE_BUTTONSCHANGED; + + status->dx = sc->xdelta; + status->dy = sc->ydelta; + status->dz = 0; + + sc->xdelta = 0; + sc->ydelta = 0; + sc->last_buttons = sc->buttons; + + mtx_unlock(&sc->sc_mtx); + + break; } + default: + return ENOTTY; + } + + return (0); +} + Index: dev/adb/adb_if.m =================================================================== --- dev/adb/adb_if.m (revision 0) +++ dev/adb/adb_if.m (revision 0) @@ -0,0 +1,49 @@ +#- +# Copyright (c) 2008 Nathan Whitehorn +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD: src/sys/dev/ppbus/ppbus_if.m,v 1.3 2005/01/06 01:43:06 imp Exp $ +# + +#include + +#include + +INTERFACE adb; + +# +# Driver level operations +# + +METHOD u_int receive_packet { + device_t dev; + + u_char status; + u_char command; + u_char reg; + + int len; + u_char *data; +}; + Index: dev/adb/adb_bus.c =================================================================== --- dev/adb/adb_bus.c (revision 0) +++ dev/adb/adb_bus.c (revision 0) @@ -0,0 +1,367 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "adb.h" +#include "adbvar.h" + +static int adb_bus_probe(device_t dev); +static int adb_bus_attach(device_t dev); +static int adb_bus_detach(device_t dev); +static void adb_probe_nomatch(device_t dev, device_t child); +static int adb_print_child(device_t dev, device_t child); + +static int adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command, uint8_t reg, int len, u_char *data); + +static char *adb_device_string[] = { + "HOST", "dongle", "keyboard", "mouse", "tablet", "modem", "RESERVED", "misc" +}; + +static device_method_t adb_bus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, adb_bus_probe), + DEVMETHOD(device_attach, adb_bus_attach), + DEVMETHOD(device_detach, adb_bus_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* Bus Interface */ + DEVMETHOD(bus_probe_nomatch, adb_probe_nomatch), + DEVMETHOD(bus_print_child, adb_print_child), + + { 0, 0 }, +}; + +driver_t adb_driver = { + "adb", + adb_bus_methods, + sizeof(struct adb_softc), +}; + +devclass_t adb_devclass; + +static int +adb_bus_probe(device_t dev) +{ + device_set_desc(dev, "Apple Desktop Bus"); + return (0); +} + +static int +adb_bus_attach(device_t dev) +{ + struct adb_softc *sc = device_get_softc(dev); + uint8_t i, next_free; + uint16_t r3; + + sc->sc_dev = dev; + sc->parent = device_get_parent(dev); + + sc->packet_reply = 0; + sc->autopoll_mask = 0; + + mtx_init(&sc->sc_sync_mtx,"adbsyn",NULL,MTX_DEF | MTX_RECURSE); + + /* Initialize devinfo */ + for (i = 0; i < 16; i++) { + sc->devinfo[i].address = i; + sc->devinfo[i].default_address = 0; + } + + /* Reset ADB bus */ + adb_send_raw_packet_sync(dev,0,ADB_COMMAND_BUS_RESET,0,0,NULL); + DELAY(1500); + + /* Enumerate bus */ + next_free = 8; + + for (i = 1; i < 7; i++) { + int8_t first_relocated = -1; + int reply = 0; + + do { + reply = adb_send_raw_packet_sync(dev,i, + ADB_COMMAND_TALK,3,0,NULL); + + if (reply) { + /* If we got a response, relocate to next_free */ +//device_printf(dev,"relocating %s to ID %d\n",adb_device_string[i],next_free); + + r3 = sc->devinfo[i].register3; + r3 &= 0xf000; + r3 |= ((uint16_t)(next_free) & 0x000f) << 8; + r3 |= 0x00fe; + + adb_send_raw_packet_sync(dev,i, ADB_COMMAND_LISTEN,3, + sizeof(uint16_t),(u_char *)(&r3)); + + adb_send_raw_packet_sync(dev,next_free, + ADB_COMMAND_TALK,3,0,NULL); + + sc->devinfo[next_free].default_address = i; + if (first_relocated < 0) + first_relocated = next_free; + + next_free++; + } else if (first_relocated > 0) { + /* Collisions removed, relocate first device back */ +//device_printf(dev,"relocating %s back from %d\n",adb_device_string[i],first_relocated); + + r3 = sc->devinfo[i].register3; + r3 &= 0xf000; + r3 |= ((uint16_t)(i) & 0x000f) << 8; + + adb_send_raw_packet_sync(dev,first_relocated, + ADB_COMMAND_LISTEN,3, + sizeof(uint16_t),(u_char *)(&r3)); + adb_send_raw_packet_sync(dev,i, + ADB_COMMAND_TALK,3,0,NULL); + + sc->devinfo[i].default_address = i; + sc->devinfo[(int)(first_relocated)].default_address = 0; + break; + } + } while (reply); + } + + for (i = 0; i < 16; i++) { + if (sc->devinfo[i].default_address) { + sc->children[i] = device_add_child(dev, NULL, -1); + device_set_ivars(sc->children[i], &sc->devinfo[i]); + } + } + + return (bus_generic_attach(dev)); +} + +static int adb_bus_detach(device_t dev) +{ + struct adb_softc *sc = device_get_softc(dev); + + mtx_destroy(&sc->sc_sync_mtx); + + return (bus_generic_detach(dev)); +} + + +static void +adb_probe_nomatch(device_t dev, device_t child) +{ + struct adb_devinfo *dinfo; + + if (bootverbose) { + dinfo = device_get_ivars(child); + + device_printf(dev,"ADB %s at device %d (no driver attached)\n",adb_device_string[dinfo->default_address],dinfo->address); + } +} + +u_int +adb_receive_raw_packet(device_t dev, u_char status, u_char command, int len, u_char *data) +{ + struct adb_softc *sc = device_get_softc(dev); + u_char addr = command >> 4; + + /* + int i = 0; + + device_printf(dev,"waiting %02x packet length %d from %d (%02x): ",sc->sync_packet,len,addr,command); + for (i = 0; i < len; i++) + printf(" %02x", data[i]); + printf("\n"); + */ + + if (len > 0 && (command & 0x0f) == ((ADB_COMMAND_TALK << 2) | 3)) { + memcpy(&sc->devinfo[addr].register3,data,2); + sc->devinfo[addr].handler_id = data[1]; + } + + if (sc->sync_packet == command) { + memcpy(sc->syncreg,data,(len > 8) ? 8 : len); + atomic_store_rel_int(&sc->packet_reply,len + 1); + } + + if (sc->children[addr] != NULL) { + ADB_RECEIVE_PACKET(sc->children[addr],status, + (command & 0x0f) >> 2,command & 0x03,len,data); + } + + return (0); +} + +static int +adb_print_child(device_t dev, device_t child) +{ + struct adb_devinfo *dinfo; + int retval = 0; + + dinfo = device_get_ivars(child); + + retval += bus_print_child_header(dev,child); + printf(" at device %d",dinfo->address); + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +u_int +adb_send_packet(device_t dev, u_char command, u_char reg, int len, u_char *data) +{ + u_char command_byte = 0; + struct adb_devinfo *dinfo; + struct adb_softc *sc; + + sc = device_get_softc(device_get_parent(dev)); + dinfo = device_get_ivars(dev); + + command_byte |= dinfo->address << 4; + command_byte |= command << 2; + command_byte |= reg; + + ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1); + + return (0); +} + +u_int +adb_set_autopoll(device_t dev, u_char enable) +{ + struct adb_devinfo *dinfo; + struct adb_softc *sc; + uint16_t mod = 0; + + sc = device_get_softc(device_get_parent(dev)); + dinfo = device_get_ivars(dev); + + mod = enable << dinfo->address; + if (enable) { + sc->autopoll_mask |= mod; + } else { + mod = ~mod; + sc->autopoll_mask &= mod; + } + + ADB_HB_SET_AUTOPOLL_MASK(sc->parent,sc->autopoll_mask); + + return (0); +} + +uint8_t +adb_get_device_type(device_t dev) +{ + struct adb_devinfo *dinfo; + + dinfo = device_get_ivars(dev); + return (dinfo->default_address); +} + +uint8_t +adb_get_device_handler(device_t dev) +{ + struct adb_devinfo *dinfo; + + dinfo = device_get_ivars(dev); + return (dinfo->handler_id); +} + +static int +adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command, + uint8_t reg, int len, u_char *data) +{ + u_char command_byte = 0; + struct adb_softc *sc; + int result = -1; + int i = 0; + + sc = device_get_softc(dev); + + command_byte |= to << 4; + command_byte |= command << 2; + command_byte |= reg; + + /* Wait if someone else has a synchronous request pending */ + mtx_lock(&sc->sc_sync_mtx); + + sc->packet_reply = 0; + sc->sync_packet = command_byte; + + ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1); + + while (!atomic_fetchadd_int(&sc->packet_reply,0)) { + /* Sometimes CUDA controllers hang up during cold boots. + Try poking them. */ + if (i > 10) + ADB_HB_CONTROLLER_POLL(sc->parent); + + DELAY(100); + i++; + } + + result = sc->packet_reply - 1; + + /* Clear packet sync */ + sc->packet_reply = 0; + sc->sync_packet = 0xffff; /* We can't match a 16 bit value */ + + mtx_unlock(&sc->sc_sync_mtx); + + return (result); +} + +uint8_t +adb_set_device_handler(device_t dev, uint8_t newhandler) +{ + struct adb_softc *sc; + struct adb_devinfo *dinfo; + uint16_t newr3; + + dinfo = device_get_ivars(dev); + sc = device_get_softc(device_get_parent(dev)); + + newr3 = dinfo->register3 & 0xff00; + newr3 |= (uint16_t)(newhandler); + + adb_send_raw_packet_sync(sc->sc_dev,dinfo->address, + ADB_COMMAND_LISTEN, 3, sizeof(uint16_t), (u_char *)(&newr3)); + adb_send_raw_packet_sync(sc->sc_dev,dinfo->address, + ADB_COMMAND_TALK, 3, 0, NULL); + + return (dinfo->handler_id); +} + +uint8_t +adb_read_register(device_t dev, u_char reg, + size_t *len, void *data) +{ + struct adb_softc *sc; + struct adb_devinfo *dinfo; + size_t orig_len; + + dinfo = device_get_ivars(dev); + sc = device_get_softc(device_get_parent(dev)); + + orig_len = *len; + + mtx_lock(&sc->sc_sync_mtx); + + *len = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address, + ADB_COMMAND_TALK, reg, 0, NULL); + + if (*len > 0) + memcpy(data,sc->syncreg,*len); + + mtx_unlock(&sc->sc_sync_mtx); + + return ((*len > 0) ? 0 : -1); +} + Index: dev/adb/adbvar.h =================================================================== --- dev/adb/adbvar.h (revision 0) +++ dev/adb/adbvar.h (revision 0) @@ -0,0 +1,56 @@ +/*- + * Copyright (C) 2008 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/powerpc/include/cudavar.h,v 1.7 2008/02/12 18:14:45 marcel Exp $ + */ + +#ifndef _POWERPC_ADBVAR_H_ +#define _POWERPC_ADBVAR_H_ + +#include "adb.h" + +enum { + ADB_COMMAND_BUS_RESET = 0 +}; + + +struct adb_softc { + device_t sc_dev; + + device_t parent; + + struct mtx sc_sync_mtx; + + volatile int sync_packet; + volatile int packet_reply; + + uint16_t autopoll_mask; + + uint8_t syncreg[8]; + + device_t children[16]; + struct adb_devinfo devinfo[16]; +}; + +#endif /* _POWERPC_ADBVAR_H_ */ Index: dev/adb/adb_kbd.c =================================================================== --- dev/adb/adb_kbd.c (revision 0) +++ dev/adb/adb_kbd.c (revision 0) @@ -0,0 +1,700 @@ +/*- + * Copyright (C) 2008 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/powerpc/include/cudavar.h,v 1.7 2008/02/12 18:14:45 marcel Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "opt_kbd.h" +#include +#include + +#include +#include + +#include "adb.h" + +#define KBD_DRIVER_NAME "akbd" + +#define AKBD_EMULATE_ATKBD 1 + +#define TRACE device_printf(((struct adb_kbd_softc *)(kbd))->sc_dev,"%s: %s %d\n",__FILE__,__func__,__LINE__); + +static int adb_kbd_probe(device_t dev); +static int adb_kbd_attach(device_t dev); +static int adb_kbd_detach(device_t dev); +static void akbd_repeat(void *xsc); + +static u_int adb_kbd_receive_packet(device_t dev, u_char status, + u_char command, u_char reg, int len, u_char *data); + +struct adb_kbd_softc { + keyboard_t sc_kbd; + + device_t sc_dev; + struct mtx sc_mutex; + struct cv sc_cv; + + int sc_mode; + int sc_state; + + int have_led_control; + + uint8_t buffer[8]; + volatile int buffers; + + struct callout sc_repeater; + int sc_repeatstart; + int sc_repeatcontinue; + uint8_t last_press; +}; + +static device_method_t adb_kbd_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, adb_kbd_probe), + DEVMETHOD(device_attach, adb_kbd_attach), + DEVMETHOD(device_detach, adb_kbd_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* ADB interface */ + DEVMETHOD(adb_receive_packet, adb_kbd_receive_packet), + + { 0, 0 } +}; + +static driver_t adb_kbd_driver = { + "akbd", + adb_kbd_methods, + sizeof(struct adb_kbd_softc), +}; + +devclass_t adb_kbd_devclass; + +DRIVER_MODULE(akbd, adb, adb_kbd_driver, adb_kbd_devclass, 0, 0); + +static const uint8_t adb_to_at_scancode_map[128] = { 30, 31, 32, 33, 35, 34, + 44, 45, 46, 47, 0, 48, 16, 17, 18, 19, 21, 20, 2, 3, 4, 5, 7, 6, 13, + 10, 8, 12, 9, 11, 27, 24, 22, 26, 23, 25, 28, 38, 36, 40, 37, 39, 43, + 51, 53, 49, 50, 52, 15, 57, 41, 14, 0, 1, 29, 0, 42, 58, 56, 97, 98, + 100, 95, 0, 0, 83, 0, 55, 0, 78, 0, 69, 0, 0, 0, 91, 89, 0, 74, 13, 0, + 0, 82, 79, 80, 81, 75, 76, 77, 71, 0, 72, 73, 0, 0, 0, 63, 64, 65, 61, + 66, 67, 0, 87, 0, 105, 0, 70, 0, 68, 0, 88, 0, 107, 102, 94, 96, 103, + 62, 99, 60, 101, 59, 54, 93, 90, 0, 0 }; + +/* keyboard driver declaration */ +static int akbd_configure(int flags); +static kbd_probe_t akbd_probe; +static kbd_init_t akbd_init; +static kbd_term_t akbd_term; +static kbd_intr_t akbd_interrupt; +static kbd_test_if_t akbd_test_if; +static kbd_enable_t akbd_enable; +static kbd_disable_t akbd_disable; +static kbd_read_t akbd_read; +static kbd_check_t akbd_check; +static kbd_read_char_t akbd_read_char; +static kbd_check_char_t akbd_check_char; +static kbd_ioctl_t akbd_ioctl; +static kbd_lock_t akbd_lock; +static kbd_clear_state_t akbd_clear_state; +static kbd_get_state_t akbd_get_state; +static kbd_set_state_t akbd_set_state; +static kbd_poll_mode_t akbd_poll; + +keyboard_switch_t akbdsw = { + akbd_probe, + akbd_init, + akbd_term, + akbd_interrupt, + akbd_test_if, + akbd_enable, + akbd_disable, + akbd_read, + akbd_check, + akbd_read_char, + akbd_check_char, + akbd_ioctl, + akbd_lock, + akbd_clear_state, + akbd_get_state, + akbd_set_state, + genkbd_get_fkeystr, + akbd_poll, + genkbd_diag, +}; + +KEYBOARD_DRIVER(akbd, akbdsw, akbd_configure); + +static int +adb_kbd_probe(device_t dev) +{ + uint8_t type; + + type = adb_get_device_type(dev); + + if (type != ADB_DEVICE_KEYBOARD) + return (ENXIO); + + switch(adb_get_device_handler(dev)) { + case 1: + device_set_desc(dev,"Apple Standard Keyboard"); + break; + case 2: + device_set_desc(dev,"Apple Extended Keyboard"); + break; + case 4: + device_set_desc(dev,"Apple ISO Keyboard"); + break; + case 5: + device_set_desc(dev,"Apple Extended ISO Keyboard"); + break; + case 8: + device_set_desc(dev,"Apple Keyboard II"); + break; + case 9: + device_set_desc(dev,"Apple ISO Keyboard II"); + break; + case 12: + device_set_desc(dev,"PowerBook Keyboard"); + break; + case 13: + device_set_desc(dev,"PowerBook ISO Keyboard"); + break; + case 24: + device_set_desc(dev,"PowerBook Extended Keyboard"); + break; + case 27: + device_set_desc(dev,"Apple Design Keyboard"); + break; + case 195: + device_set_desc(dev,"PowerBook G3 Keyboard"); + break; + case 196: + device_set_desc(dev,"iBook Keyboard"); + break; + default: + device_set_desc(dev,"ADB Keyboard"); + break; + } + + return (0); +} + +static int +ms_to_ticks(int ms) +{ + if (hz > 1000) + return ms*(hz/1000); + + return ms/(1000/hz); +} + +static int +adb_kbd_attach(device_t dev) +{ + struct adb_kbd_softc *sc; + keyboard_switch_t *sw; + + sw = kbd_get_switch(KBD_DRIVER_NAME); + if (sw == NULL) { + return ENXIO; + } + + sc = device_get_softc(dev); + sc->sc_dev = dev; + sc->sc_mode = K_RAW; + sc->sc_state = 0; + sc->have_led_control = 0; + sc->buffers = 0; + + /* Try stepping forward to the extended keyboard protocol */ + adb_set_device_handler(dev,3); + + mtx_init(&sc->sc_mutex,KBD_DRIVER_NAME,MTX_DEF,0); + cv_init(&sc->sc_cv,KBD_DRIVER_NAME); + callout_init(&sc->sc_repeater, 0); + +#ifdef AKBD_EMULATE_ATKBD + kbd_init_struct(&sc->sc_kbd, KBD_DRIVER_NAME, KB_101, 0, 0, 0, 0); + kbd_set_maps(&sc->sc_kbd, &key_map, &accent_map, fkey_tab, + sizeof(fkey_tab) / sizeof(fkey_tab[0])); +#else + #error ADB raw mode not implemented +#endif + + KBD_FOUND_DEVICE(&sc->sc_kbd); + KBD_PROBE_DONE(&sc->sc_kbd); + KBD_INIT_DONE(&sc->sc_kbd); + KBD_CONFIG_DONE(&sc->sc_kbd); + + (*sw->enable)(&sc->sc_kbd); + + kbd_register(&sc->sc_kbd); + +#ifdef KBD_INSTALL_CDEV + if (kbd_attach(&sc->sc_kbd)) { + adb_kbd_detach(dev); + return ENXIO; + } +#endif + + adb_set_autopoll(dev,1); + + /* Check (asynchronously) if we can read out the LED state from + this keyboard by reading the key state register */ + adb_send_packet(dev,ADB_COMMAND_TALK,2,0,NULL); + + return (0); +} + +static int +adb_kbd_detach(device_t dev) +{ + struct adb_kbd_softc *sc; + keyboard_t *kbd; + + sc = device_get_softc(dev); + + adb_set_autopoll(dev,0); + callout_stop(&sc->sc_repeater); + + mtx_lock(&sc->sc_mutex); + + kbd = kbd_get_keyboard(kbd_find_keyboard(KBD_DRIVER_NAME, + device_get_unit(dev))); + + kbdd_disable(kbd); + +#ifdef KBD_INSTALL_CDEV + kbd_detach(kbd); +#endif + + kbdd_term(kbd); + + mtx_unlock(&sc->sc_mutex); + + mtx_destroy(&sc->sc_mutex); + cv_destroy(&sc->sc_cv); + + return (0); +} + +static u_int +adb_kbd_receive_packet(device_t dev, u_char status, + u_char command, u_char reg, int len, u_char *data) +{ + struct adb_kbd_softc *sc; + + sc = device_get_softc(dev); + + if (command != ADB_COMMAND_TALK) + return 0; + + if (reg == 2 && len == 2) { + sc->have_led_control = 1; + return 0; + } + + if (reg != 0 || len != 2) + return (0); + + mtx_lock(&sc->sc_mutex); + if ((data[0] & 0x7f) == 57 && sc->buffers < 7) { + /* Fake the down/up cycle for caps lock */ + sc->buffer[sc->buffers++] = data[0] & 0x7f; + sc->buffer[sc->buffers++] = (data[0] & 0x7f) | (1 << 7); + } else { + sc->buffer[sc->buffers++] = data[0]; + } + + if (sc->buffer[sc->buffers-1] < 0xff) + sc->last_press = sc->buffer[sc->buffers-1]; + + if ((data[1] & 0x7f) == 57 && sc->buffers < 7) { + /* Fake the down/up cycle for caps lock */ + sc->buffer[sc->buffers++] = data[1] & 0x7f; + sc->buffer[sc->buffers++] = (data[1] & 0x7f) | (1 << 7); + } else { + sc->buffer[sc->buffers++] = data[1]; + } + + if (sc->buffer[sc->buffers-1] < 0xff) + sc->last_press = sc->buffer[sc->buffers-1]; + + /* Stop any existing key repeating */ + callout_stop(&sc->sc_repeater); + + /* Schedule a repeat callback on keydown */ + if (!(sc->last_press & (1 << 7))) { + callout_reset(&sc->sc_repeater, + ms_to_ticks(sc->sc_kbd.kb_delay1), akbd_repeat, sc); + } + mtx_unlock(&sc->sc_mutex); + + cv_broadcast(&sc->sc_cv); + + //device_printf(dev,"key %03d %s, key %03d %s\n",data[0] & 0x7f,data[0] & 0x80 ? "UP" : "DOWN", data[1] & 0x7f, data[1] & 0x80 ? "UP" : "DOWN"); + + if (KBD_IS_ACTIVE(&sc->sc_kbd) && KBD_IS_BUSY(&sc->sc_kbd)) { + sc->sc_kbd.kb_callback.kc_func(&sc->sc_kbd, + KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg); + } + + return (0); +} + +static void +akbd_repeat(void *xsc) { + struct adb_kbd_softc *sc = xsc; + int notify_kbd = 0; + + /* Fake an up/down key repeat so long as we have the + free buffers */ + mtx_lock(&sc->sc_mutex); + if (sc->buffers < 7) { + sc->buffer[sc->buffers++] = sc->last_press | (1 << 7); + sc->buffer[sc->buffers++] = sc->last_press; + + notify_kbd = 1; + } + mtx_unlock(&sc->sc_mutex); + + if (notify_kbd && KBD_IS_ACTIVE(&sc->sc_kbd) + && KBD_IS_BUSY(&sc->sc_kbd)) { + sc->sc_kbd.kb_callback.kc_func(&sc->sc_kbd, + KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg); + } + + /* Reschedule the callout */ + callout_reset(&sc->sc_repeater, ms_to_ticks(sc->sc_kbd.kb_delay2), + akbd_repeat, sc); +} + +static int +akbd_configure(int flags) +{ + return 0; +} + +static int +akbd_probe(int unit, void *arg, int flags) +{ + return 0; +} + +static int +akbd_init(int unit, keyboard_t **kbdp, void *arg, int flags) +{ + return 0; +} + +static int +akbd_term(keyboard_t *kbd) +{ + return 0; +} + +static int +akbd_interrupt(keyboard_t *kbd, void *arg) +{ + return 0; +} + +static int +akbd_test_if(keyboard_t *kbd) +{ + return 0; +} + +static int +akbd_enable(keyboard_t *kbd) +{ + KBD_ACTIVATE(kbd); + return (0); +} + +static int +akbd_disable(keyboard_t *kbd) +{ + struct adb_kbd_softc *sc; + sc = (struct adb_kbd_softc *)(kbd); + + callout_stop(&sc->sc_repeater); + KBD_DEACTIVATE(kbd); + return (0); +} + +static int +akbd_read(keyboard_t *kbd, int wait) +{ + return (0); +} + +static int +akbd_check(keyboard_t *kbd) +{ + struct adb_kbd_softc *sc; + + if (!KBD_IS_ACTIVE(kbd)) + return (FALSE); + + sc = (struct adb_kbd_softc *)(kbd); + + mtx_lock(&sc->sc_mutex); + if (sc->buffers > 0) { + mtx_unlock(&sc->sc_mutex); + return (TRUE); + } + mtx_unlock(&sc->sc_mutex); + + return (FALSE); +} + +static u_int +akbd_read_char(keyboard_t *kbd, int wait) +{ + struct adb_kbd_softc *sc; + uint8_t adb_code, final_scancode; + int i; + + sc = (struct adb_kbd_softc *)(kbd); + + mtx_lock(&sc->sc_mutex); + if (!sc->buffers && wait) + cv_wait(&sc->sc_cv,&sc->sc_mutex); + + if (!sc->buffers) { + mtx_unlock(&sc->sc_mutex); + return (0); + } + + adb_code = sc->buffer[0]; + + for (i = 1; i < sc->buffers; i++) + sc->buffer[i-1] = sc->buffer[i]; + + sc->buffers--; + mtx_unlock(&sc->sc_mutex); + + #ifdef AKBD_EMULATE_ATKBD + final_scancode = adb_to_at_scancode_map[adb_code & 0x7f]; + final_scancode |= adb_code & 0x80; + #else + final_scancode = adb_code; + #endif + + return (final_scancode); +} + +static int +akbd_check_char(keyboard_t *kbd) +{ + if (!KBD_IS_ACTIVE(kbd)) + return (FALSE); + + return (akbd_check(kbd)); +} + +static int +set_typematic(keyboard_t *kbd, int code) +{ + /* These numbers are in microseconds, so convert to ticks */ + + static int delays[] = { 250, 500, 750, 1000 }; + static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63, + 68, 76, 84, 92, 100, 110, 118, 126, + 136, 152, 168, 184, 200, 220, 236, 252, + 272, 304, 336, 368, 400, 440, 472, 504 }; + + if (code & ~0x7f) + return EINVAL; + kbd->kb_delay1 = delays[(code >> 5) & 3]; + kbd->kb_delay2 = rates[code & 0x1f]; + return 0; +} + +static int akbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t data) +{ + struct adb_kbd_softc *sc; + uint16_t r2; + int error; + + sc = (struct adb_kbd_softc *)(kbd); + error = 0; + + switch (cmd) { + case KDGKBMODE: + *(int *)data = sc->sc_mode; + break; + case KDSKBMODE: + switch (*(int *)data) { + case K_XLATE: + if (sc->sc_mode != K_XLATE) { + /* make lock key state and LED state match */ + sc->sc_state &= ~LOCK_MASK; + sc->sc_state |= KBD_LED_VAL(kbd); + } + /* FALLTHROUGH */ + case K_RAW: + case K_CODE: + if (sc->sc_mode != *(int *)data) { + sc->sc_mode = *(int *)data; + TRACE; + } + break; + default: + error = EINVAL; + break; + } + + break; + + case KDGETLED: + *(int *)data = KBD_LED_VAL(kbd); + break; + + case KDSKBSTATE: + if (*(int *)data & ~LOCK_MASK) { + TRACE; + error = EINVAL; + break; + } + sc->sc_state &= ~LOCK_MASK; + sc->sc_state |= *(int *)data; + + /* FALLTHROUGH */ + + case KDSETLED: + KBD_LED_VAL(kbd) = *(int *)data; + + if (!sc->have_led_control) + break; + + r2 = (~0 & 0x04) | 3; + + if (*(int *)data & NLKED) + r2 &= ~1; + if (*(int *)data & CLKED) + r2 &= ~2; + if (*(int *)data & SLKED) + r2 &= ~4; + + adb_send_packet(sc->sc_dev,ADB_COMMAND_LISTEN,2, + sizeof(uint16_t),(u_char *)&r2); + + break; + + case KDGKBSTATE: + *(int *)data = sc->sc_state & LOCK_MASK; + break; + + case KDSETREPEAT: + if (!KBD_HAS_DEVICE(kbd)) + return 0; + if (((int *)data)[1] < 0) + return EINVAL; + if (((int *)data)[0] < 0) + return EINVAL; + else if (((int *)data)[0] == 0) /* fastest possible value */ + kbd->kb_delay1 = 200; + else + kbd->kb_delay1 = ((int *)data)[0]; + kbd->kb_delay2 = ((int *)data)[1]; + + break; + + case KDSETRAD: + error = set_typematic(kbd, *(int *)data); + break; + + case PIO_KEYMAP: + case PIO_KEYMAPENT: + case PIO_DEADKEYMAP: + default: + return (genkbd_commonioctl(kbd, cmd, data)); + } + + return (error); +} + +static int akbd_lock(keyboard_t *kbd, int lock) +{ + return (0); +} + +static void akbd_clear_state(keyboard_t *kbd) +{ +} + +static int akbd_get_state(keyboard_t *kbd, void *buf, size_t len) +{ + return (0); +} + +static int akbd_set_state(keyboard_t *kbd, void *buf, size_t len) +{ + return (0); +} + +static int akbd_poll(keyboard_t *kbd, int on) +{ + //TRACE; + return (0); +} + +static int +akbd_modevent(module_t mod, int type, void *data) +{ + switch (type) { + case MOD_LOAD: + kbd_add_driver(&akbd_kbd_driver); + break; + + case MOD_UNLOAD: + kbd_delete_driver(&akbd_kbd_driver); + break; + + default: + return (EOPNOTSUPP); + } + + return (0); +} + +DEV_MODULE(akbd, akbd_modevent, NULL); + Index: dev/adb/adb_hb_if.m =================================================================== --- dev/adb/adb_hb_if.m (revision 0) +++ dev/adb/adb_hb_if.m (revision 0) @@ -0,0 +1,58 @@ +#- +# Copyright (c) 2008 Nathan Whitehorn +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD: src/sys/dev/ppbus/ppbus_if.m,v 1.3 2005/01/06 01:43:06 imp Exp $ +# + +#include + +#include + +INTERFACE adb_hb; + +# +# Bus level operations. These are used by the ADB bus manager, and are +# required to inplement an ADB interface. +# + +METHOD u_int send_raw_packet { + device_t dev; + u_char command_byte; + + int len; + u_char *data; + u_char poll; +}; + +METHOD u_int controller_poll { + device_t dev; +}; + +METHOD u_int set_autopoll_mask { + device_t dev; + + uint16_t mask; +}; + Index: dev/adb/adb.h =================================================================== --- dev/adb/adb.h (revision 0) +++ dev/adb/adb.h (revision 0) @@ -0,0 +1,78 @@ +/*- + * Copyright (C) 2008 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/powerpc/include/cudavar.h,v 1.7 2008/02/12 18:14:45 marcel Exp $ + */ + +#ifndef _POWERPC_ADB_H_ +#define _POWERPC_ADB_H_ + +#include "adb_hb_if.h" +#include "adb_if.h" + +enum { + ADB_COMMAND_FLUSH = 0, + ADB_COMMAND_LISTEN = 2, + ADB_COMMAND_TALK = 3, +}; + +enum { + ADB_DEVICE_DONGLE = 0x01, + ADB_DEVICE_KEYBOARD = 0x02, + ADB_DEVICE_MOUSE = 0x03, + ADB_DEVICE_TABLET = 0x04, + ADB_DEVICE_MODEM = 0x05, + + ADB_DEVICE_MISC = 0x07 +}; + +struct adb_devinfo { + uint8_t address; + uint8_t default_address; + uint8_t handler_id; + + uint16_t register3; +}; + +/* Pass packets down through the bus manager */ +extern u_int adb_send_packet(device_t dev, u_char command, + u_char reg, int len, u_char *data); +extern u_int adb_set_autopoll(device_t dev, u_char enable); + +/* Pass packets up from the interface */ +extern u_int adb_receive_raw_packet(device_t dev, u_char status, + u_char command, int len, u_char *data); + +extern uint8_t adb_get_device_type(device_t dev); +extern uint8_t adb_get_device_handler(device_t dev); +extern uint8_t adb_set_device_handler(device_t dev, uint8_t newhandler); + +extern uint8_t adb_read_register(device_t dev, u_char reg, + size_t *len, void *data); + +extern devclass_t adb_devclass; +extern driver_t adb_driver; + +#endif + Index: conf/files.powerpc =================================================================== --- conf/files.powerpc (revision 183451) +++ conf/files.powerpc (working copy) @@ -28,6 +28,11 @@ crypto/blowfish/bf_enc.c optional crypto | ipsec crypto/des/des_enc.c optional crypto | ipsec | netsmb dev/bm/if_bm.c optional bm powermac +dev/adb/adb_bus.c optional adb +dev/adb/adb_kbd.c optional adb +dev/adb/adb_mouse.c optional adb +dev/adb/adb_hb_if.m optional adb +dev/adb/adb_if.m optional adb dev/fb/fb.c optional sc dev/hwpmc/hwpmc_powerpc.c optional hwpmc dev/kbd/kbd.c optional sc @@ -117,5 +123,8 @@ powerpc/powermac/openpic_macio.c optional powermac pci powerpc/powermac/pswitch.c optional powermac pswitch powerpc/powermac/uninorth.c optional powermac pci +powerpc/powermac/cuda.c optional powermac cuda +powerpc/powermac/pmu.c optional powermac pmu powerpc/powerpc/atomic.S standard powerpc/powerpc/autoconf.c standard powerpc/powerpc/bcopy.c standard Index: powerpc/powermac/pmu.c =================================================================== --- powerpc/powermac/pmu.c (revision 0) +++ powerpc/powermac/pmu.c (revision 0) @@ -0,0 +1,551 @@ +/*- + * Copyright 2003 by Nathan Whitehorn. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD: src/sys/powerpc/powermac/pmu_macio.c,v 1.11 2008/02/12 18:14:46 marcel Exp $"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include "pmuvar.h" +#include "viareg.h" + +#define DPRINTF(x) //device_printf(dev,x) +#define TRACE device_printf(dev,"%s: %d\n",__FILE__,__LINE__) + +/* + * MacIO interface + */ +static int pmu_probe(device_t); +static int pmu_attach(device_t); +static int pmu_detach(device_t); + +static u_int pmu_adb_send(device_t dev, u_char command_byte, int len, u_char *data, u_char poll); +static u_int pmu_adb_autopoll(device_t dev, uint16_t mask); +static void pmu_poll(device_t dev); + +static device_method_t pmu_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pmu_probe), + DEVMETHOD(device_attach, pmu_attach), + DEVMETHOD(device_detach, pmu_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* bus interface, for ADB root */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* ADB bus interface */ + DEVMETHOD(adb_hb_send_raw_packet, pmu_adb_send), + DEVMETHOD(adb_hb_controller_poll, pmu_poll), + DEVMETHOD(adb_hb_set_autopoll_mask, pmu_adb_autopoll), + + { 0, 0 }, +}; + +static driver_t pmu_driver = { + "pmu", + pmu_methods, + sizeof(struct pmu_softc), +}; + +DRIVER_MODULE(pmu, macio, pmu_driver, pmu_devclass, 0, 0); +DRIVER_MODULE(adb, pmu, adb_driver, adb_devclass, 0, 0); + +/* Make sure uhid is loaded, as it turns off some of the ADB emulation */ +MODULE_DEPEND(pmu, usb, 1, 1, 1); + +devclass_t pmu_devclass; + +static void pmu_intr(void *arg); +static void pmu_in(struct pmu_softc *sc); +static void pmu_out(struct pmu_softc *sc); +static void pmu_ack_on(struct pmu_softc *sc); +static void pmu_ack_off(struct pmu_softc *sc); +static int pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, + int rlen, uint8_t *out_msg); +static uint8_t pmu_read_reg(struct pmu_softc *sc, u_int offset); +static void pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value); +static int pmu_intr_state(struct pmu_softc *); + +/* these values shows that number of data returned after 'send' cmd is sent */ +static signed char pm_send_cmd_type[] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 0x01, 0x01, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, -1, -1, -1, -1, -1, 0x00, + -1, 0x00, 0x02, 0x01, 0x01, -1, -1, -1, + 0x00, -1, -1, -1, -1, -1, -1, -1, + 0x04, 0x14, -1, 0x03, -1, -1, -1, -1, + 0x00, 0x00, 0x02, 0x02, -1, -1, -1, -1, + 0x01, 0x01, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, -1, -1, 0x01, -1, -1, -1, + 0x01, 0x00, 0x02, 0x02, -1, 0x01, 0x03, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x00, -1, -1, -1, + 0x02, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1, + 0x01, 0x01, 0x01, -1, -1, -1, -1, -1, + 0x00, 0x00, -1, -1, -1, -1, 0x04, 0x04, + 0x04, -1, 0x00, -1, -1, -1, -1, -1, + 0x00, -1, -1, -1, -1, -1, -1, -1, + 0x01, 0x02, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, -1, -1, -1, -1, -1, -1, + 0x02, 0x02, 0x02, 0x04, -1, 0x00, -1, -1, + 0x01, 0x01, 0x03, 0x02, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 0x00, -1, -1, -1, -1, -1, -1, -1, + 0x01, 0x01, -1, -1, 0x00, 0x00, -1, -1, + -1, 0x04, 0x00, -1, -1, -1, -1, -1, + 0x03, -1, 0x00, -1, 0x00, -1, -1, 0x00, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +/* these values shows that number of data returned after 'receive' cmd is sent */ +static signed char pm_receive_cmd_type[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, -1, -1, -1, -1, -1, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x15, -1, 0x02, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x03, 0x03, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x04, 0x03, 0x09, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, -1, -1, -1, -1, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, -1, -1, 0x02, -1, -1, -1, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, 0x02, -1, -1, -1, -1, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static int +pmu_probe(device_t dev) +{ + const char *type = ofw_bus_get_type(dev); + + if (strcmp(type, "via-pmu") != 0) + return (ENXIO); + + device_set_desc(dev, "Apple PMU99 Controller"); + return (0); +} + +static int +pmu_attach(device_t dev) +{ + struct pmu_softc *sc; + + uint8_t reg; + uint8_t cmd[2] = {2, 0}; + uint8_t resp[16]; + phandle_t node,child; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + sc->sc_memrid = 0; + sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_memrid, RF_ACTIVE); + + mtx_init(&sc->sc_mutex,"pmu",NULL,MTX_DEF | MTX_RECURSE); + + if (sc->sc_memr == NULL) { + device_printf(dev, "Could not alloc mem resource!\n"); + return (ENXIO); + } + + sc->sc_irqrid = 1; + sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_irqrid, + 0x2f,0x2f,1,RF_ACTIVE); + + //sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqrid, + // RF_ACTIVE); + if (sc->sc_irq == NULL) { + device_printf(dev, "could not allocate interrupt\n"); + return (ENXIO); + } + + if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE | INTR_ENTROPY, + NULL, pmu_intr, dev, &sc->sc_ih) != 0) { + device_printf(dev, "could not setup interrupt\n"); + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, + sc->sc_irq); + return (ENXIO); + } + + + sc->sc_error = 0; + sc->sc_polling = 0; + sc->sc_autopoll = 0; + + /* Init PMU */ + + reg = PMU_INT_TICK | PMU_INT_ADB | PMU_INT_PCEJECT | PMU_INT_SNDBRT; + reg |= PMU_INT_BATTERY; + reg |= PMU_INT_ENVIRONMENT; + pmu_send(sc, PMU_SET_IMASK, 1, ®, 16, resp); + + pmu_write_reg(sc, vIER, 0x90); /* make sure VIA interrupts are on */ + + pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp); + pmu_send(sc, PMU_GET_VERSION, 1, cmd, 16, resp); + + /* Initialize child buses (ADB) */ + node = ofw_bus_get_node(dev); + + for (child = OF_child(node); child != 0; child = OF_peer(child)) { + char name[32]; + + memset(name, 0, sizeof(name)); + OF_getprop(child, "name", name, sizeof(name)); + + if (bootverbose) + device_printf(dev, "PMU child <%s>\n",name); + + if (strncmp(name, "adb", 4) == 0) { + sc->adb_bus = device_add_child(dev,"adb",-1); + } + } + + return (bus_generic_attach(dev)); +} + +static int +pmu_detach(device_t dev) +{ + struct pmu_softc *sc; + + sc = device_get_softc(dev); + + bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih); + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq); + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr); + mtx_destroy(&sc->sc_mutex); + + return (bus_generic_detach(dev)); +} + +static uint8_t +pmu_read_reg(struct pmu_softc *sc, u_int offset) +{ + return (bus_read_1(sc->sc_memr, offset)); +} + +static void +pmu_write_reg(struct pmu_softc *sc, u_int offset, uint8_t value) +{ + bus_write_1(sc->sc_memr, offset, value); +} + +static int +pmu_send_byte(struct pmu_softc *sc, uint8_t data) +{ + + pmu_out(sc); + pmu_write_reg(sc, vSR, data); + pmu_ack_off(sc); + /* wait for intr to come up */ + /* XXX should add a timeout and bail if it expires */ + do {} while (pmu_intr_state(sc) == 0); + pmu_ack_on(sc); + do {} while (pmu_intr_state(sc)); + pmu_ack_on(sc); + return 0; +} + +static inline int +pmu_read_byte(struct pmu_softc *sc, uint8_t *data) +{ + volatile uint8_t scratch; + pmu_in(sc); + scratch = pmu_read_reg(sc, vSR); + pmu_ack_off(sc); + /* wait for intr to come up */ + do {} while (pmu_intr_state(sc) == 0); + pmu_ack_on(sc); + do {} while (pmu_intr_state(sc)); + *data = pmu_read_reg(sc, vSR); + return 0; +} + +static int +pmu_intr_state(struct pmu_softc *sc) +{ + return ((pmu_read_reg(sc, vBufB) & vPB3) == 0); +} + +static int +pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, int rlen, + uint8_t *out_msg) +{ + struct pmu_softc *sc = cookie; + int i, rcv_len = -1; + uint8_t out_len, intreg; + + intreg = pmu_read_reg(sc, vIER); + intreg &= 0x10; + pmu_write_reg(sc, vIER, intreg); + + /* wait idle */ + do {} while (pmu_intr_state(sc)); + sc->sc_error = 0; + + /* send command */ + pmu_send_byte(sc, cmd); + + /* send length if necessary */ + if (pm_send_cmd_type[cmd] < 0) { + pmu_send_byte(sc, length); + } + + for (i = 0; i < length; i++) { + pmu_send_byte(sc, in_msg[i]); + } + + /* see if there's data to read */ + rcv_len = pm_receive_cmd_type[cmd]; + if (rcv_len == 0) + goto done; + + /* read command */ + if (rcv_len == 1) { + pmu_read_byte(sc, out_msg); + goto done; + } else + out_msg[0] = cmd; + if (rcv_len < 0) { + pmu_read_byte(sc, &out_len); + rcv_len = out_len + 1; + } + for (i = 1; i < min(rcv_len, rlen); i++) + pmu_read_byte(sc, &out_msg[i]); + +done: + pmu_write_reg(sc, vIER, (intreg == 0) ? 0 : 0x90); + + return rcv_len; +} + + +static void +pmu_poll(device_t dev) +{ + pmu_intr(dev); +} + +static void +pmu_in(struct pmu_softc *sc) +{ + uint8_t reg; + + reg = pmu_read_reg(sc, vACR); + reg &= ~vSR_OUT; + reg |= 0x0c; + pmu_write_reg(sc, vACR, reg); +} + +static void +pmu_out(struct pmu_softc *sc) +{ + uint8_t reg; + + reg = pmu_read_reg(sc, vACR); + reg |= vSR_OUT; + reg |= 0x0c; + pmu_write_reg(sc, vACR, reg); +} + +static void +pmu_ack_off(struct pmu_softc *sc) +{ + uint8_t reg; + + reg = pmu_read_reg(sc, vBufB); + reg &= ~vPB4; + pmu_write_reg(sc, vBufB, reg); +} + +static void +pmu_ack_on(struct pmu_softc *sc) +{ + uint8_t reg; + + reg = pmu_read_reg(sc, vBufB); + reg |= vPB4; + pmu_write_reg(sc, vBufB, reg); +} + +static void +pmu_intr(void *arg) +{ + device_t dev; + struct pmu_softc *sc; + + unsigned int len; + uint8_t resp[16]; + uint8_t junk[16]; + + dev = (device_t)arg; + sc = device_get_softc(dev); + + mtx_lock(&sc->sc_mutex); + pmu_write_reg(sc, vIFR, 0x90); /* Clear 'em */ + len = pmu_send(sc, PMU_INT_ACK, 0, NULL, 16, resp); + mtx_unlock(&sc->sc_mutex); + + if ((len < 1) || (resp[1] == 0)) { + return; + } + + if (resp[1] & PMU_INT_ADB) { + adb_receive_raw_packet(sc->adb_bus,resp[1],resp[2], + len - 3,&resp[3]); + + /* + * the PMU will turn off autopolling after each command that + * it did not issue, so we assume any but TALK R0 is ours and + * re-enable autopoll here whenever we receive an ACK for a + * non TR0 command. + */ + mtx_lock(&sc->sc_mutex); + + if ((resp[2] & 0x0f) != (ADB_COMMAND_TALK << 2)) { + if (sc->sc_autopoll) { + uint8_t cmd[] = {0, PMU_SET_POLL_MASK, + (sc->sc_autopoll >> 8) & 0xff, sc->sc_autopoll & 0xff}; + + pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, junk); + } + } + + mtx_unlock(&sc->sc_mutex); + } +} + +static u_int +pmu_adb_send(device_t dev, u_char command_byte, int len, u_char *data, + u_char poll) +{ + struct pmu_softc *sc = device_get_softc(dev); + int i,replen; + uint8_t packet[16], resp[16]; + + /* construct an ADB command packet and send it */ + + packet[0] = command_byte; + + packet[1] = 0; + packet[2] = len; + for (i = 0; i < len; i++) + packet[i + 3] = data[i]; + + mtx_lock(&sc->sc_mutex); + replen = pmu_send(sc, PMU_ADB_CMD, len + 3, packet, 16, resp); + mtx_unlock(&sc->sc_mutex); + + if (poll) + pmu_poll(dev); + + return 0; +} + +static u_int +pmu_adb_autopoll(device_t dev, uint16_t mask) +{ + struct pmu_softc *sc = device_get_softc(dev); + + mask = 0xffff; + + /* magical incantation to re-enable autopolling */ + uint8_t cmd[] = {0, PMU_SET_POLL_MASK, (mask >> 8) & 0xff, mask & 0xff}; + uint8_t resp[16]; + + mtx_lock(&sc->sc_mutex); + + if (sc->sc_autopoll == mask) { + mtx_unlock(&sc->sc_mutex); + return 0; + } + + sc->sc_autopoll = mask & 0xffff; + + if (mask) + pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, resp); + else + pmu_send(sc, PMU_ADB_POLL_OFF, 0, NULL, 16, resp); + + mtx_unlock(&sc->sc_mutex); + + return 0; +} Index: powerpc/powermac/pmuvar.h =================================================================== --- powerpc/powermac/pmuvar.h (revision 0) +++ powerpc/powermac/pmuvar.h (revision 0) @@ -0,0 +1,169 @@ +/*- + * Copyright (c) 2008 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PMUVAR_H +#define PMUVAR_H + +/* PMU commands */ +#define PMU_POWER_CTRL0 0x10 /* control power of some devices */ +#define PMU_POWER_CTRL 0x11 /* control power of some devices */ + +#define PMU_POWER_OFF 0x7e /* Turn Power off */ +#define PMU_RESET_CPU 0xd0 /* Reset CPU */ + +#define PMU_SET_RTC 0x30 /* Set realtime clock */ +#define PMU_READ_RTC 0x38 /* Read realtime clock */ + +#define PMU_WRITE_PRAM 0x32 /* Write PRAM */ +#define PMU_READ_PRAM 0x3a /* Read PRAM */ + +#define PMU_WRITE_NVRAM 0x33 /* Write NVRAM */ +#define PMU_READ_NVRAM 0x3b /* Read NVRAM */ + +#define PMU_EJECT_PCMCIA 0x4c /* Eject PCMCIA slot */ + +#define PMU_SET_BRIGHTNESS 0x41 /* Set backlight brightness */ +#define PMU_READ_BRIGHTNESS 0xd9 /* Read brightness button position */ + +#define PMU_POWER_EVENTS 0x8f /* Send power-event commands to PMU */ +#define PMU_SYSTEM_READY 0xdf /* tell PMU we are awake */ + +#define PMU_BATTERY_STATE 0x6b /* Read old battery state */ +#define PMU_SMART_BATTERY_STATE 0x6f /* Read battery state */ + +#define PMU_ADB_CMD 0x20 /* Send ADB packet */ +#define PMU_ADB_POLL_OFF 0x21 /* Disable ADB auto-poll */ +#define PMU_SET_VOL 0x40 /* Set volume button position */ +#define PMU_GET_VOL 0x48 /* Get volume button position */ +#define PMU_SET_IMASK 0x70 /* Set interrupt mask */ +#define PMU_INT_ACK 0x78 /* Read interrupt bits */ +#define PMU_CPU_SPEED 0x7d /* Control CPU speed on some models */ +#define PMU_SLEEP 0x7f /* Put CPU to sleep */ +#define PMU_SET_POLL_MASK 0x86 /* + * 16bit mask enables autopolling per + * device + */ +#define PMU_I2C_CMD 0x9a /* i2c commands */ +#define PMU_GET_LID_STATE 0xdc /* Report lid state */ +#define PMU_GET_VERSION 0xea /* Identify thyself */ + +/* Bits in PMU interrupt and interrupt mask bytes */ +#define PMU_INT_ADB_AUTO 0x04 /* ADB autopoll, when PMU_INT_ADB */ +#define PMU_INT_PCEJECT 0x04 /* PC-card eject buttons */ +#define PMU_INT_SNDBRT 0x08 /* sound/brightness up/down buttons */ +#define PMU_INT_ADB 0x10 /* ADB autopoll or reply data */ +#define PMU_INT_BATTERY 0x20 +#define PMU_INT_ENVIRONMENT 0x40 +#define PMU_INT_TICK 0x80 /* 1-second tick interrupt */ + +/* Bits to use with the PMU_POWER_CTRL0 command */ +#define PMU_POW0_ON 0x80 /* OR this to power ON the device */ +#define PMU_POW0_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ +#define PMU_POW0_HARD_DRIVE 0x04 /* wallstreet/lombard? */ + +/* Bits to use with the PMU_POWER_CTRL command */ +#define PMU_POW_ON 0x80 /* OR this to power ON the device */ +#define PMU_POW_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ +#define PMU_POW_BACKLIGHT 0x01 /* backlight power */ +#define PMU_POW_CHARGER 0x02 /* battery charger power */ +#define PMU_POW_IRLED 0x04 /* IR led power (on wallstreet) */ +#define PMU_POW_MEDIABAY 0x08 /* media bay power (wallstreet/lombard ?) */ + +/* Bits from PMU_GET_LID_STATE or PMU_INT_ENVIRONMENT on core99 */ +#define PMU_ENV_LID_CLOSED 0x01 /* The lid is closed */ + +/* PMU PMU_POWER_EVENTS commands */ +enum { + PMU_PWR_GET_POWERUP_EVENTS = 0x00, + PMU_PWR_SET_POWERUP_EVENTS = 0x01, + PMU_PWR_CLR_POWERUP_EVENTS = 0x02, + PMU_PWR_GET_WAKEUP_EVENTS = 0x03, + PMU_PWR_SET_WAKEUP_EVENTS = 0x04, + PMU_PWR_CLR_WAKEUP_EVENTS = 0x05, +}; + +/* PMU Power Information */ + +#define PMU_PWR_AC_PRESENT (1 << 0) +#define PMU_PWR_BATT_CHARGING (1 << 1) +#define PMU_PWR_BATT_PRESENT (1 << 2) +#define PMU_PWR_BATT_FULL (1 << 5) +#define PMU_PWR_PCHARGE_RESET (1 << 6) +#define PMU_PWR_BATT_EXIST (1 << 7) + + +/* I2C related definitions */ +#define PMU_I2C_MODE_SIMPLE 0 +#define PMU_I2C_MODE_STDSUB 1 +#define PMU_I2C_MODE_COMBINED 2 + +#define PMU_I2C_BUS_STATUS 0 +#define PMU_I2C_BUS_SYSCLK 1 +#define PMU_I2C_BUS_POWER 2 + +#define PMU_I2C_STATUS_OK 0 +#define PMU_I2C_STATUS_DATAREAD 1 +#define PMU_I2C_STATUS_BUSY 0xfe + +/* Power events wakeup bits */ +enum { + PMU_PWR_WAKEUP_KEY = 0x01, /* Wake on key press */ + PMU_PWR_WAKEUP_AC_INSERT = 0x02, /* Wake on AC adapter plug */ + PMU_PWR_WAKEUP_AC_CHANGE = 0x04, + PMU_PWR_WAKEUP_LID_OPEN = 0x08, + PMU_PWR_WAKEUP_RING = 0x10, +}; + +#define PMU_NOTREADY 0x1 /* has not been initialized yet */ +#define PMU_IDLE 0x2 /* the bus is currently idle */ +#define PMU_OUT 0x3 /* sending out a command */ +#define PMU_IN 0x4 /* receiving data */ + +struct pmu_softc { + device_t sc_dev; + int sc_memrid; + struct resource *sc_memr; + int sc_irqrid; + struct resource *sc_irq; + void *sc_ih; + + struct mtx sc_mutex; + + device_t adb_bus; + + int sc_node; + volatile int sc_state; + int sc_polling; + int sc_error; + volatile int sc_autopoll; +}; + +extern devclass_t pmu_devclass; + +#endif /* PMUVAR_H */ Index: powerpc/powermac/cuda.c =================================================================== --- powerpc/powermac/cuda.c (revision 0) +++ powerpc/powermac/cuda.c (revision 0) @@ -0,0 +1,652 @@ +/*- + * Copyright 2008 by Nathan Whitehorn. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD: src/sys/powerpc/powermac/cuda_macio.c,v 1.11 2008/02/12 18:14:46 marcel Exp $"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include "cudavar.h" +#include "viareg.h" + +#define DPRINTF(x) //device_printf(dev,x) +#define TRACE device_printf(dev,"%s: %d\n",__FILE__,__LINE__) + +/* + * MacIO interface + */ +static int cuda_probe(device_t); +static int cuda_attach(device_t); +static int cuda_detach(device_t); + +static u_int cuda_adb_send(device_t dev, u_char command_byte, int len, u_char *data, u_char poll); +static u_int cuda_adb_autopoll(device_t dev, uint16_t mask); +static void cuda_poll(device_t dev); + +static device_method_t cuda_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, cuda_probe), + DEVMETHOD(device_attach, cuda_attach), + DEVMETHOD(device_detach, cuda_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* bus interface, for ADB root */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* ADB bus interface */ + DEVMETHOD(adb_hb_send_raw_packet, cuda_adb_send), + DEVMETHOD(adb_hb_controller_poll, cuda_poll), + DEVMETHOD(adb_hb_set_autopoll_mask, cuda_adb_autopoll), + + { 0, 0 }, +}; + +static driver_t cuda_driver = { + "cuda", + cuda_methods, + sizeof(struct cuda_softc), +}; + +DRIVER_MODULE(cuda, macio, cuda_driver, cuda_devclass, 0, 0); +DRIVER_MODULE(adb, cuda, adb_driver, adb_devclass, 0, 0); + +devclass_t cuda_devclass; + +static void cuda_intr(void *arg); +static uint8_t cuda_read_reg(struct cuda_softc *sc, u_int offset); +static void cuda_write_reg(struct cuda_softc *sc, u_int offset, uint8_t value); +static void cuda_idle(struct cuda_softc *); +static void cuda_tip(struct cuda_softc *); +static void cuda_clear_tip(struct cuda_softc *); +static void cuda_in(struct cuda_softc *); +static void cuda_out(struct cuda_softc *); +static void cuda_toggle_ack(struct cuda_softc *); +static void cuda_ack_off(struct cuda_softc *); +static int cuda_intr_state(struct cuda_softc *); + +static int +cuda_probe(device_t dev) +{ + const char *type = ofw_bus_get_type(dev); + + if (strcmp(type, "via-cuda") != 0) + return (ENXIO); + + device_set_desc(dev, CUDA_DEVSTR); + return (0); +} + +static int +cuda_attach(device_t dev) +{ + struct cuda_softc *sc; + + volatile int i; + uint8_t reg; + phandle_t node,child; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + sc->sc_memrid = 0; + sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_memrid, RF_ACTIVE); + + if (sc->sc_memr == NULL) { + device_printf(dev, "Could not alloc mem resource!\n"); + return (ENXIO); + } + + sc->sc_irqrid = 0; + sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqrid, + RF_ACTIVE); + if (sc->sc_irq == NULL) { + device_printf(dev, "could not allocate interrupt\n"); + return (ENXIO); + } + + if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE | INTR_ENTROPY, + NULL, cuda_intr, dev, &sc->sc_ih) != 0) { + device_printf(dev, "could not setup interrupt\n"); + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, + sc->sc_irq); + return (ENXIO); + } + + mtx_init(&sc->sc_mutex,"cuda",NULL,MTX_DEF | MTX_RECURSE); + + sc->sc_sent = 0; + sc->sc_received = 0; + sc->sc_waiting = 0; + sc->sc_polling = 0; + sc->sc_state = CUDA_NOTREADY; + sc->sc_error = 0; + sc->sc_autopoll = 0; + + /* Init CUDA */ + + reg = cuda_read_reg(sc, vDirB); + reg |= 0x30; /* register B bits 4 and 5: outputs */ + cuda_write_reg(sc, vDirB, reg); + + reg = cuda_read_reg(sc, vDirB); + reg &= 0xf7; /* register B bit 3: input */ + cuda_write_reg(sc, vDirB, reg); + + reg = cuda_read_reg(sc, vACR); + reg &= ~vSR_OUT; /* make sure SR is set to IN */ + cuda_write_reg(sc, vACR, reg); + + cuda_write_reg(sc, vACR, (cuda_read_reg(sc, vACR) | 0x0c) & ~0x10); + + sc->sc_state = CUDA_IDLE; /* used by all types of hardware */ + + cuda_write_reg(sc, vIER, 0x84); /* make sure VIA interrupts are on */ + + cuda_idle(sc); /* reset ADB */ + + /* Reset CUDA */ + + i = cuda_read_reg(sc, vSR); /* clear interrupt */ + cuda_write_reg(sc, vIER, 0x04); /* no interrupts while clearing */ + cuda_idle(sc); /* reset state to idle */ + DELAY(150); + cuda_tip(sc); /* signal start of frame */ + DELAY(150); + cuda_toggle_ack(sc); + DELAY(150); + cuda_clear_tip(sc); + DELAY(150); + cuda_idle(sc); /* back to idle state */ + i = cuda_read_reg(sc, vSR); /* clear interrupt */ + cuda_write_reg(sc, vIER, 0x84); /* ints ok now */ + + /* Initialize child buses (ADB) */ + node = ofw_bus_get_node(dev); + + for (child = OF_child(node); child != 0; child = OF_peer(child)) { + char name[32]; + + memset(name, 0, sizeof(name)); + OF_getprop(child, "name", name, sizeof(name)); + + if (bootverbose) + device_printf(dev, "CUDA child <%s>\n",name); + + if (strncmp(name, "adb", 4) == 0) { + sc->adb_bus = device_add_child(dev,"adb",-1); + } + } + + return (bus_generic_attach(dev)); +} + +static int cuda_detach(device_t dev) { + struct cuda_softc *sc; + + sc = device_get_softc(dev); + + bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih); + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid, sc->sc_irq); + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_memrid, sc->sc_memr); + mtx_destroy(&sc->sc_mutex); + + return (bus_generic_detach(dev)); +} + +static uint8_t +cuda_read_reg(struct cuda_softc *sc, u_int offset) { + return (bus_read_1(sc->sc_memr, offset)); +} + +static void +cuda_write_reg(struct cuda_softc *sc, u_int offset, uint8_t value) { + bus_write_1(sc->sc_memr, offset, value); +} + +static void +cuda_idle(struct cuda_softc *sc) +{ + uint8_t reg; + + reg = cuda_read_reg(sc, vBufB); + reg |= (vPB4 | vPB5); + cuda_write_reg(sc, vBufB, reg); +} + +static void +cuda_tip(struct cuda_softc *sc) +{ + uint8_t reg; + + reg = cuda_read_reg(sc, vBufB); + reg &= ~vPB5; + cuda_write_reg(sc, vBufB, reg); +} + +static void +cuda_clear_tip(struct cuda_softc *sc) +{ + uint8_t reg; + + reg = cuda_read_reg(sc, vBufB); + reg |= vPB5; + cuda_write_reg(sc, vBufB, reg); +} + +static void +cuda_in(struct cuda_softc *sc) +{ + uint8_t reg; + + reg = cuda_read_reg(sc, vACR); + reg &= ~vSR_OUT; + cuda_write_reg(sc, vACR, reg); +} + +static void +cuda_out(struct cuda_softc *sc) +{ + uint8_t reg; + + reg = cuda_read_reg(sc, vACR); + reg |= vSR_OUT; + cuda_write_reg(sc, vACR, reg); +} + +static void +cuda_toggle_ack(struct cuda_softc *sc) +{ + uint8_t reg; + + reg = cuda_read_reg(sc, vBufB); + reg ^= vPB4; + cuda_write_reg(sc, vBufB, reg); +} + +static void +cuda_ack_off(struct cuda_softc *sc) +{ + uint8_t reg; + + reg = cuda_read_reg(sc, vBufB); + reg |= vPB4; + cuda_write_reg(sc, vBufB, reg); +} + +static int +cuda_intr_state(struct cuda_softc *sc) +{ + return ((cuda_read_reg(sc, vBufB) & vPB3) == 0); +} + +static int +cuda_send(void *cookie, int poll, int length, uint8_t *msg) +{ + struct cuda_softc *sc = cookie; + device_t dev = sc->sc_dev; + + if (sc->sc_state == CUDA_NOTREADY) + return -1; + + mtx_lock(&sc->sc_mutex); + + if ((sc->sc_state == CUDA_IDLE) /*&& + ((cuda_read_reg(sc, vBufB) & vPB3) == vPB3)*/) { + /* fine */ + //DPRINTF("chip is idle\n"); + } else { + if (sc->sc_waiting == 0) { + sc->sc_waiting = 1; + } else { + mtx_unlock(&sc->sc_mutex); + return -1; + } + } + + sc->sc_error = 0; + memcpy(sc->sc_out, msg, length); + sc->sc_out_length = length; + sc->sc_sent = 0; + + if (sc->sc_waiting != 1) { + DELAY(150); + sc->sc_state = CUDA_OUT; + cuda_out(sc); + cuda_write_reg(sc, vSR, sc->sc_out[0]); + cuda_ack_off(sc); + cuda_tip(sc); + } + sc->sc_waiting = 1; + mtx_unlock(&sc->sc_mutex); + + if (sc->sc_polling || poll || cold) { + cuda_poll(dev); + } + + return 0; +} + +static void +cuda_poll(device_t dev) +{ + struct cuda_softc *sc = device_get_softc(dev); + + while ((sc->sc_state != CUDA_IDLE) || + (cuda_intr_state(sc)) || + (sc->sc_waiting == 1)) { + if ((cuda_read_reg(sc, vIFR) & vSR_INT) == vSR_INT) + cuda_intr(dev); + } +} + +static void +cuda_intr(void *arg) +{ + device_t dev; + struct cuda_softc *sc; + + int i, ending, type, restart_send; + uint8_t reg; + + dev = (device_t)arg; + sc = device_get_softc(dev); + + mtx_lock(&sc->sc_mutex); + + restart_send = 0; + reg = cuda_read_reg(sc, vIFR); + cuda_write_reg(sc, vIFR, 0x7f); /* Clear interrupt */ + +switch_start: + switch (sc->sc_state) { + case CUDA_IDLE: + /* + * This is an unexpected packet, so grab the first (dummy) + * byte, set up the proper vars, and tell the chip we are + * starting to receive the packet by setting the TIP bit. + */ + sc->sc_in[1] = cuda_read_reg(sc, vSR); + + if (cuda_intr_state(sc) == 0) { + /* must have been a fake start */ + + if (sc->sc_waiting) { + /* start over */ + DELAY(150); + sc->sc_state = CUDA_OUT; + sc->sc_sent = 0; + cuda_out(sc); + cuda_write_reg(sc, vSR, sc->sc_out[1]); + cuda_ack_off(sc); + cuda_tip(sc); + } + break; + } + + cuda_in(sc); + cuda_tip(sc); + + sc->sc_received = 1; + sc->sc_state = CUDA_IN; + break; + + case CUDA_IN: + sc->sc_in[sc->sc_received] = cuda_read_reg(sc, vSR); + ending = 0; + + if (sc->sc_received > 255) { + /* bitch only once */ + if (sc->sc_received == 256) { + device_printf(dev,"input overflow\n"); + ending = 1; + } + } else + sc->sc_received++; + + if (sc->sc_received > 3) { + if ((sc->sc_in[3] == CMD_IIC) && + (sc->sc_received > (sc->sc_i2c_read_len + 4))) { + ending = 1; + } + } + + /* intr off means this is the last byte (end of frame) */ + if (cuda_intr_state(sc) == 0) { + ending = 1; + } else { + cuda_toggle_ack(sc); + } + + if (ending == 1) { /* end of message? */ + sc->sc_in[0] = sc->sc_received - 1; + + /* reset vars and signal the end of this frame */ + cuda_idle(sc); + + /* check if we have a handler for this message */ + type = sc->sc_in[1]; + + switch (type) { + case CUDA_ADB: + if (sc->sc_received > 4) { + adb_receive_raw_packet(sc->adb_bus, + sc->sc_in[2],sc->sc_in[3], + sc->sc_received - 4,&sc->sc_in[4]); + } else { + adb_receive_raw_packet(sc->adb_bus, + sc->sc_in[2],sc->sc_in[3],0,NULL); + } + break; + case CUDA_PSEUDO: + if (sc->sc_in[3] == CMD_AUTOPOLL) + sc->sc_autopoll = 1; + break; + case CUDA_ERROR: + device_printf(dev,"CUDA Error\n"); + sc->sc_error = 1; + break; + default: + device_printf(dev,"unknown CUDA command %d\n",type); + break; + } + + sc->sc_state = CUDA_IDLE; + + sc->sc_received = 0; + + /* + * If there is something waiting to be sent out, + * set everything up and send the first byte. + */ + if (sc->sc_waiting == 1) { + DPRINTF("pending write\n"); + + DELAY(1500); /* required */ + sc->sc_sent = 0; + sc->sc_state = CUDA_OUT; + + /* + * If the interrupt is on, we were too slow + * and the chip has already started to send + * something to us, so back out of the write + * and start a read cycle. + */ + if (cuda_intr_state(sc)) { + cuda_in(sc); + cuda_idle(sc); + sc->sc_sent = 0; + sc->sc_state = CUDA_IDLE; + sc->sc_received = 0; + DELAY(150); + DPRINTF("too slow - incoming message\n"); + goto switch_start; + } + /* + * If we got here, it's ok to start sending + * so load the first byte and tell the chip + * we want to send. + */ + DPRINTF("sending\n"); + + cuda_out(sc); + cuda_write_reg(sc, vSR, + sc->sc_out[sc->sc_sent]); + cuda_ack_off(sc); + cuda_tip(sc); + } + } + break; + + case CUDA_OUT: + i = cuda_read_reg(sc, vSR); /* reset SR-intr in IFR */ + + sc->sc_sent++; + if (cuda_intr_state(sc)) { /* ADB intr low during write */ + + DPRINTF("incoming msg during send\n"); + + cuda_in(sc); /* make sure SR is set to IN */ + cuda_idle(sc); + sc->sc_sent = 0; /* must start all over */ + sc->sc_state = CUDA_IDLE; /* new state */ + sc->sc_received = 0; + sc->sc_waiting = 1; /* must retry when done with + * read */ + DELAY(150); + goto switch_start; /* process next state right + * now */ + break; + } + if (sc->sc_out_length == sc->sc_sent) { /* check for done */ + + sc->sc_waiting = 0; /* done writing */ + sc->sc_state = CUDA_IDLE; /* signal bus is idle */ + cuda_in(sc); + cuda_idle(sc); + //DPRINTF("done sending\n"); + } else { + /* send next byte */ + cuda_write_reg(sc, vSR, sc->sc_out[sc->sc_sent]); + cuda_toggle_ack(sc); /* signal byte ready to + * shift */ + } + break; + + case CUDA_NOTREADY: + DPRINTF("adb: not yet initialized\n"); + break; + + default: + DPRINTF("intr: unknown ADB state\n"); + break; + } + + mtx_unlock(&sc->sc_mutex); +} + +static u_int +cuda_adb_send(device_t dev, u_char command_byte, int len, u_char *data, u_char poll) +{ + struct cuda_softc *sc = device_get_softc(dev); + int i; + uint8_t packet[16]; + + /* construct an ADB command packet and send it */ + packet[0] = CUDA_ADB; + packet[1] = command_byte; + for (i = 0; i < len; i++) + packet[i + 2] = data[i]; + + if (poll) + cuda_poll(dev); + + cuda_send(sc, poll, len + 2, packet); + + if (poll) + cuda_poll(dev); + + return 0; +} + +static u_int +cuda_adb_autopoll(device_t dev, uint16_t mask) { + struct cuda_softc *sc = device_get_softc(dev); + + uint8_t cmd[] = {CUDA_PSEUDO, CMD_AUTOPOLL, mask != 0}; + + mtx_lock(&sc->sc_mutex); + if (cmd[2] == sc->sc_autopoll) { + mtx_unlock(&sc->sc_mutex); + return 0; + } + + while (sc->sc_state != CUDA_IDLE) + mtx_sleep(dev,&sc->sc_mutex,0,"cuda",1); + + sc->sc_autopoll = -1; + mtx_unlock(&sc->sc_mutex); + + cuda_send(sc, 0, 3, cmd); + + mtx_lock(&sc->sc_mutex); + while(sc->sc_autopoll == -1) { + mtx_sleep(dev,&sc->sc_mutex,0,"cuda",100); + cuda_poll(dev); + } + + mtx_unlock(&sc->sc_mutex); + + return 0; +} + Index: powerpc/powermac/cudavar.h =================================================================== --- powerpc/powermac/cudavar.h (revision 0) +++ powerpc/powermac/cudavar.h (revision 0) @@ -0,0 +1,90 @@ +/*- + * Copyright (C) 2002 Benno Rice. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/powerpc/include/cudavar.h,v 1.7 2008/02/12 18:14:45 marcel Exp $ + */ + +#ifndef _POWERPC_CUDAVAR_H_ +#define _POWERPC_CUDAVAR_H_ + +#define CUDA_DEVSTR "Apple CUDA I/O Controller" + +/* Cuda addresses */ +#define CUDA_ADB 0 +#define CUDA_PSEUDO 1 +#define CUDA_ERROR 2 /* error codes? */ +#define CUDA_TIMER 3 +#define CUDA_POWER 4 +#define CUDA_IIC 5 /* XXX ??? */ +#define CUDA_PMU 6 +#define CUDA_ADB_QUERY 7 + +/* Cuda commands */ +#define CMD_AUTOPOLL 1 +#define CMD_READ_RTC 3 +#define CMD_WRITE_RTC 9 +#define CMD_POWEROFF 10 +#define CMD_RESET 17 +#define CMD_IIC 34 + +/* Cuda state codes */ +#define CUDA_NOTREADY 0x1 /* has not been initialized yet */ +#define CUDA_IDLE 0x2 /* the bus is currently idle */ +#define CUDA_OUT 0x3 /* sending out a command */ +#define CUDA_IN 0x4 /* receiving data */ +#define CUDA_POLLING 0x5 /* polling - II only */ + +struct cuda_softc { + device_t sc_dev; + int sc_memrid; + struct resource *sc_memr; + int sc_irqrid; + struct resource *sc_irq; + void *sc_ih; + + struct mtx sc_mutex; + + device_t adb_bus; + + int sc_node; + volatile int sc_state; + int sc_waiting; + int sc_polling; + int sc_sent; + int sc_out_length; + int sc_received; + int sc_iic_done; + int sc_error; + volatile int sc_autopoll; + + int sc_i2c_read_len; + + /* internal buffers */ + uint8_t sc_in[256]; + uint8_t sc_out[256]; +}; + +extern devclass_t cuda_devclass; + +#endif /* _POWERPC_CUDAVAR_H_ */