|  | /* | 
|  | *  Apple SMC controller | 
|  | * | 
|  | *  Copyright (c) 2007 Alexander Graf | 
|  | * | 
|  | *  Authors: Alexander Graf <agraf@suse.de> | 
|  | *           Susanne Graf <suse@csgraf.de> | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, see <http://www.gnu.org/licenses/>. | 
|  | * | 
|  | * ***************************************************************** | 
|  | * | 
|  | * In all Intel-based Apple hardware there is an SMC chip to control the | 
|  | * backlight, fans and several other generic device parameters. It also | 
|  | * contains the magic keys used to dongle Mac OS X to the device. | 
|  | * | 
|  | * This driver was mostly created by looking at the Linux AppleSMC driver | 
|  | * implementation and does not support IRQ. | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include "hw.h" | 
|  | #include "isa.h" | 
|  | #include "ui/console.h" | 
|  | #include "qemu/timer.h" | 
|  |  | 
|  | /* #define DEBUG_SMC */ | 
|  |  | 
|  | #define APPLESMC_DEFAULT_IOBASE        0x300 | 
|  | /* data port used by Apple SMC */ | 
|  | #define APPLESMC_DATA_PORT             0x0 | 
|  | /* command/status port used by Apple SMC */ | 
|  | #define APPLESMC_CMD_PORT              0x4 | 
|  | #define APPLESMC_NR_PORTS              32 | 
|  | #define APPLESMC_MAX_DATA_LENGTH       32 | 
|  |  | 
|  | #define APPLESMC_READ_CMD              0x10 | 
|  | #define APPLESMC_WRITE_CMD             0x11 | 
|  | #define APPLESMC_GET_KEY_BY_INDEX_CMD  0x12 | 
|  | #define APPLESMC_GET_KEY_TYPE_CMD      0x13 | 
|  |  | 
|  | #ifdef DEBUG_SMC | 
|  | #define smc_debug(...) fprintf(stderr, "AppleSMC: " __VA_ARGS__) | 
|  | #else | 
|  | #define smc_debug(...) do { } while(0) | 
|  | #endif | 
|  |  | 
|  | static char default_osk[64] = "This is a dummy key. Enter the real key " | 
|  | "using the -osk parameter"; | 
|  |  | 
|  | struct AppleSMCData { | 
|  | uint8_t len; | 
|  | const char *key; | 
|  | const char *data; | 
|  | QLIST_ENTRY(AppleSMCData) node; | 
|  | }; | 
|  |  | 
|  | struct AppleSMCStatus { | 
|  | ISADevice dev; | 
|  | uint32_t iobase; | 
|  | uint8_t cmd; | 
|  | uint8_t status; | 
|  | uint8_t key[4]; | 
|  | uint8_t read_pos; | 
|  | uint8_t data_len; | 
|  | uint8_t data_pos; | 
|  | uint8_t data[255]; | 
|  | uint8_t charactic[4]; | 
|  | char *osk; | 
|  | QLIST_HEAD(, AppleSMCData) data_def; | 
|  | }; | 
|  |  | 
|  | static void applesmc_io_cmd_writeb(void *opaque, uint32_t addr, uint32_t val) | 
|  | { | 
|  | struct AppleSMCStatus *s = opaque; | 
|  |  | 
|  | smc_debug("CMD Write B: %#x = %#x\n", addr, val); | 
|  | switch(val) { | 
|  | case APPLESMC_READ_CMD: | 
|  | s->status = 0x0c; | 
|  | break; | 
|  | } | 
|  | s->cmd = val; | 
|  | s->read_pos = 0; | 
|  | s->data_pos = 0; | 
|  | } | 
|  |  | 
|  | static void applesmc_fill_data(struct AppleSMCStatus *s) | 
|  | { | 
|  | struct AppleSMCData *d; | 
|  |  | 
|  | QLIST_FOREACH(d, &s->data_def, node) { | 
|  | if (!memcmp(d->key, s->key, 4)) { | 
|  | smc_debug("Key matched (%s Len=%d Data=%s)\n", d->key, | 
|  | d->len, d->data); | 
|  | memcpy(s->data, d->data, d->len); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void applesmc_io_data_writeb(void *opaque, uint32_t addr, uint32_t val) | 
|  | { | 
|  | struct AppleSMCStatus *s = opaque; | 
|  |  | 
|  | smc_debug("DATA Write B: %#x = %#x\n", addr, val); | 
|  | switch(s->cmd) { | 
|  | case APPLESMC_READ_CMD: | 
|  | if(s->read_pos < 4) { | 
|  | s->key[s->read_pos] = val; | 
|  | s->status = 0x04; | 
|  | } else if(s->read_pos == 4) { | 
|  | s->data_len = val; | 
|  | s->status = 0x05; | 
|  | s->data_pos = 0; | 
|  | smc_debug("Key = %c%c%c%c Len = %d\n", s->key[0], | 
|  | s->key[1], s->key[2], s->key[3], val); | 
|  | applesmc_fill_data(s); | 
|  | } | 
|  | s->read_pos++; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static uint32_t applesmc_io_data_readb(void *opaque, uint32_t addr1) | 
|  | { | 
|  | struct AppleSMCStatus *s = opaque; | 
|  | uint8_t retval = 0; | 
|  |  | 
|  | switch(s->cmd) { | 
|  | case APPLESMC_READ_CMD: | 
|  | if(s->data_pos < s->data_len) { | 
|  | retval = s->data[s->data_pos]; | 
|  | smc_debug("READ_DATA[%d] = %#hhx\n", s->data_pos, | 
|  | retval); | 
|  | s->data_pos++; | 
|  | if(s->data_pos == s->data_len) { | 
|  | s->status = 0x00; | 
|  | smc_debug("EOF\n"); | 
|  | } else | 
|  | s->status = 0x05; | 
|  | } | 
|  | } | 
|  | smc_debug("DATA Read b: %#x = %#x\n", addr1, retval); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | static uint32_t applesmc_io_cmd_readb(void *opaque, uint32_t addr1) | 
|  | { | 
|  | struct AppleSMCStatus *s = opaque; | 
|  |  | 
|  | smc_debug("CMD Read B: %#x\n", addr1); | 
|  | return s->status; | 
|  | } | 
|  |  | 
|  | static void applesmc_add_key(struct AppleSMCStatus *s, const char *key, | 
|  | int len, const char *data) | 
|  | { | 
|  | struct AppleSMCData *def; | 
|  |  | 
|  | def = g_malloc0(sizeof(struct AppleSMCData)); | 
|  | def->key = key; | 
|  | def->len = len; | 
|  | def->data = data; | 
|  |  | 
|  | QLIST_INSERT_HEAD(&s->data_def, def, node); | 
|  | } | 
|  |  | 
|  | static void qdev_applesmc_isa_reset(DeviceState *dev) | 
|  | { | 
|  | struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev.qdev, dev); | 
|  | struct AppleSMCData *d, *next; | 
|  |  | 
|  | /* Remove existing entries */ | 
|  | QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { | 
|  | QLIST_REMOVE(d, node); | 
|  | } | 
|  |  | 
|  | applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); | 
|  | applesmc_add_key(s, "OSK0", 32, s->osk); | 
|  | applesmc_add_key(s, "OSK1", 32, s->osk + 32); | 
|  | applesmc_add_key(s, "NATJ", 1, "\0"); | 
|  | applesmc_add_key(s, "MSSP", 1, "\0"); | 
|  | applesmc_add_key(s, "MSSD", 1, "\0x3"); | 
|  | } | 
|  |  | 
|  | static int applesmc_isa_init(ISADevice *dev) | 
|  | { | 
|  | struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev, dev); | 
|  |  | 
|  | register_ioport_read(s->iobase + APPLESMC_DATA_PORT, 4, 1, | 
|  | applesmc_io_data_readb, s); | 
|  | register_ioport_read(s->iobase + APPLESMC_CMD_PORT, 4, 1, | 
|  | applesmc_io_cmd_readb, s); | 
|  | register_ioport_write(s->iobase + APPLESMC_DATA_PORT, 4, 1, | 
|  | applesmc_io_data_writeb, s); | 
|  | register_ioport_write(s->iobase + APPLESMC_CMD_PORT, 4, 1, | 
|  | applesmc_io_cmd_writeb, s); | 
|  |  | 
|  | if (!s->osk || (strlen(s->osk) != 64)) { | 
|  | fprintf(stderr, "WARNING: Using AppleSMC with invalid key\n"); | 
|  | s->osk = default_osk; | 
|  | } | 
|  |  | 
|  | QLIST_INIT(&s->data_def); | 
|  | qdev_applesmc_isa_reset(&dev->qdev); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static Property applesmc_isa_properties[] = { | 
|  | DEFINE_PROP_HEX32("iobase", struct AppleSMCStatus, iobase, | 
|  | APPLESMC_DEFAULT_IOBASE), | 
|  | DEFINE_PROP_STRING("osk", struct AppleSMCStatus, osk), | 
|  | DEFINE_PROP_END_OF_LIST(), | 
|  | }; | 
|  |  | 
|  | static void qdev_applesmc_class_init(ObjectClass *klass, void *data) | 
|  | { | 
|  | DeviceClass *dc = DEVICE_CLASS(klass); | 
|  | ISADeviceClass *ic = ISA_DEVICE_CLASS(klass); | 
|  | ic->init = applesmc_isa_init; | 
|  | dc->reset = qdev_applesmc_isa_reset; | 
|  | dc->props = applesmc_isa_properties; | 
|  | } | 
|  |  | 
|  | static const TypeInfo applesmc_isa_info = { | 
|  | .name          = "isa-applesmc", | 
|  | .parent        = TYPE_ISA_DEVICE, | 
|  | .instance_size = sizeof(struct AppleSMCStatus), | 
|  | .class_init    = qdev_applesmc_class_init, | 
|  | }; | 
|  |  | 
|  | static void applesmc_register_types(void) | 
|  | { | 
|  | type_register_static(&applesmc_isa_info); | 
|  | } | 
|  |  | 
|  | type_init(applesmc_register_types) |