root / hw / applesmc.c @ f57a5160
History | View | Annotate | Download (7.3 kB)
1 |
/*
|
---|---|
2 |
* Apple SMC controller
|
3 |
*
|
4 |
* Copyright (c) 2007 Alexander Graf
|
5 |
*
|
6 |
* Authors: Alexander Graf <agraf@suse.de>
|
7 |
* Susanne Graf <suse@csgraf.de>
|
8 |
*
|
9 |
* This library is free software; you can redistribute it and/or
|
10 |
* modify it under the terms of the GNU Lesser General Public
|
11 |
* License as published by the Free Software Foundation; either
|
12 |
* version 2 of the License, or (at your option) any later version.
|
13 |
*
|
14 |
* This library is distributed in the hope that it will be useful,
|
15 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
17 |
* Lesser General Public License for more details.
|
18 |
*
|
19 |
* You should have received a copy of the GNU Lesser General Public
|
20 |
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
21 |
*
|
22 |
* *****************************************************************
|
23 |
*
|
24 |
* In all Intel-based Apple hardware there is an SMC chip to control the
|
25 |
* backlight, fans and several other generic device parameters. It also
|
26 |
* contains the magic keys used to dongle Mac OS X to the device.
|
27 |
*
|
28 |
* This driver was mostly created by looking at the Linux AppleSMC driver
|
29 |
* implementation and does not support IRQ.
|
30 |
*
|
31 |
*/
|
32 |
|
33 |
#include "hw.h" |
34 |
#include "isa.h" |
35 |
#include "console.h" |
36 |
#include "qemu-timer.h" |
37 |
|
38 |
/* #define DEBUG_SMC */
|
39 |
|
40 |
#define APPLESMC_DEFAULT_IOBASE 0x300 |
41 |
/* data port used by Apple SMC */
|
42 |
#define APPLESMC_DATA_PORT 0x0 |
43 |
/* command/status port used by Apple SMC */
|
44 |
#define APPLESMC_CMD_PORT 0x4 |
45 |
#define APPLESMC_NR_PORTS 32 |
46 |
#define APPLESMC_MAX_DATA_LENGTH 32 |
47 |
|
48 |
#define APPLESMC_READ_CMD 0x10 |
49 |
#define APPLESMC_WRITE_CMD 0x11 |
50 |
#define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12 |
51 |
#define APPLESMC_GET_KEY_TYPE_CMD 0x13 |
52 |
|
53 |
#ifdef DEBUG_SMC
|
54 |
#define smc_debug(...) fprintf(stderr, "AppleSMC: " __VA_ARGS__) |
55 |
#else
|
56 |
#define smc_debug(...) do { } while(0) |
57 |
#endif
|
58 |
|
59 |
static char default_osk[64] = "This is a dummy key. Enter the real key " |
60 |
"using the -osk parameter";
|
61 |
|
62 |
struct AppleSMCData {
|
63 |
uint8_t len; |
64 |
const char *key; |
65 |
const char *data; |
66 |
QLIST_ENTRY(AppleSMCData) node; |
67 |
}; |
68 |
|
69 |
struct AppleSMCStatus {
|
70 |
ISADevice dev; |
71 |
uint32_t iobase; |
72 |
uint8_t cmd; |
73 |
uint8_t status; |
74 |
uint8_t key[4];
|
75 |
uint8_t read_pos; |
76 |
uint8_t data_len; |
77 |
uint8_t data_pos; |
78 |
uint8_t data[255];
|
79 |
uint8_t charactic[4];
|
80 |
char *osk;
|
81 |
QLIST_HEAD(, AppleSMCData) data_def; |
82 |
}; |
83 |
|
84 |
static void applesmc_io_cmd_writeb(void *opaque, uint32_t addr, uint32_t val) |
85 |
{ |
86 |
struct AppleSMCStatus *s = opaque;
|
87 |
|
88 |
smc_debug("CMD Write B: %#x = %#x\n", addr, val);
|
89 |
switch(val) {
|
90 |
case APPLESMC_READ_CMD:
|
91 |
s->status = 0x0c;
|
92 |
break;
|
93 |
} |
94 |
s->cmd = val; |
95 |
s->read_pos = 0;
|
96 |
s->data_pos = 0;
|
97 |
} |
98 |
|
99 |
static void applesmc_fill_data(struct AppleSMCStatus *s) |
100 |
{ |
101 |
struct AppleSMCData *d;
|
102 |
|
103 |
QLIST_FOREACH(d, &s->data_def, node) { |
104 |
if (!memcmp(d->key, s->key, 4)) { |
105 |
smc_debug("Key matched (%s Len=%d Data=%s)\n", d->key,
|
106 |
d->len, d->data); |
107 |
memcpy(s->data, d->data, d->len); |
108 |
return;
|
109 |
} |
110 |
} |
111 |
} |
112 |
|
113 |
static void applesmc_io_data_writeb(void *opaque, uint32_t addr, uint32_t val) |
114 |
{ |
115 |
struct AppleSMCStatus *s = opaque;
|
116 |
|
117 |
smc_debug("DATA Write B: %#x = %#x\n", addr, val);
|
118 |
switch(s->cmd) {
|
119 |
case APPLESMC_READ_CMD:
|
120 |
if(s->read_pos < 4) { |
121 |
s->key[s->read_pos] = val; |
122 |
s->status = 0x04;
|
123 |
} else if(s->read_pos == 4) { |
124 |
s->data_len = val; |
125 |
s->status = 0x05;
|
126 |
s->data_pos = 0;
|
127 |
smc_debug("Key = %c%c%c%c Len = %d\n", s->key[0], |
128 |
s->key[1], s->key[2], s->key[3], val); |
129 |
applesmc_fill_data(s); |
130 |
} |
131 |
s->read_pos++; |
132 |
break;
|
133 |
} |
134 |
} |
135 |
|
136 |
static uint32_t applesmc_io_data_readb(void *opaque, uint32_t addr1) |
137 |
{ |
138 |
struct AppleSMCStatus *s = opaque;
|
139 |
uint8_t retval = 0;
|
140 |
|
141 |
switch(s->cmd) {
|
142 |
case APPLESMC_READ_CMD:
|
143 |
if(s->data_pos < s->data_len) {
|
144 |
retval = s->data[s->data_pos]; |
145 |
smc_debug("READ_DATA[%d] = %#hhx\n", s->data_pos,
|
146 |
retval); |
147 |
s->data_pos++; |
148 |
if(s->data_pos == s->data_len) {
|
149 |
s->status = 0x00;
|
150 |
smc_debug("EOF\n");
|
151 |
} else
|
152 |
s->status = 0x05;
|
153 |
} |
154 |
} |
155 |
smc_debug("DATA Read b: %#x = %#x\n", addr1, retval);
|
156 |
|
157 |
return retval;
|
158 |
} |
159 |
|
160 |
static uint32_t applesmc_io_cmd_readb(void *opaque, uint32_t addr1) |
161 |
{ |
162 |
struct AppleSMCStatus *s = opaque;
|
163 |
|
164 |
smc_debug("CMD Read B: %#x\n", addr1);
|
165 |
return s->status;
|
166 |
} |
167 |
|
168 |
static void applesmc_add_key(struct AppleSMCStatus *s, const char *key, |
169 |
int len, const char *data) |
170 |
{ |
171 |
struct AppleSMCData *def;
|
172 |
|
173 |
def = g_malloc0(sizeof(struct AppleSMCData)); |
174 |
def->key = key; |
175 |
def->len = len; |
176 |
def->data = data; |
177 |
|
178 |
QLIST_INSERT_HEAD(&s->data_def, def, node); |
179 |
} |
180 |
|
181 |
static void qdev_applesmc_isa_reset(DeviceState *dev) |
182 |
{ |
183 |
struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev.qdev, dev); |
184 |
struct AppleSMCData *d, *next;
|
185 |
|
186 |
/* Remove existing entries */
|
187 |
QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { |
188 |
QLIST_REMOVE(d, node); |
189 |
} |
190 |
|
191 |
applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); |
192 |
applesmc_add_key(s, "OSK0", 32, s->osk); |
193 |
applesmc_add_key(s, "OSK1", 32, s->osk + 32); |
194 |
applesmc_add_key(s, "NATJ", 1, "\0"); |
195 |
applesmc_add_key(s, "MSSP", 1, "\0"); |
196 |
applesmc_add_key(s, "MSSD", 1, "\0x3"); |
197 |
} |
198 |
|
199 |
static int applesmc_isa_init(ISADevice *dev) |
200 |
{ |
201 |
struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev, dev); |
202 |
|
203 |
register_ioport_read(s->iobase + APPLESMC_DATA_PORT, 4, 1, |
204 |
applesmc_io_data_readb, s); |
205 |
register_ioport_read(s->iobase + APPLESMC_CMD_PORT, 4, 1, |
206 |
applesmc_io_cmd_readb, s); |
207 |
register_ioport_write(s->iobase + APPLESMC_DATA_PORT, 4, 1, |
208 |
applesmc_io_data_writeb, s); |
209 |
register_ioport_write(s->iobase + APPLESMC_CMD_PORT, 4, 1, |
210 |
applesmc_io_cmd_writeb, s); |
211 |
|
212 |
if (!s->osk || (strlen(s->osk) != 64)) { |
213 |
fprintf(stderr, "WARNING: Using AppleSMC with invalid key\n");
|
214 |
s->osk = default_osk; |
215 |
} |
216 |
|
217 |
QLIST_INIT(&s->data_def); |
218 |
qdev_applesmc_isa_reset(&dev->qdev); |
219 |
|
220 |
return 0; |
221 |
} |
222 |
|
223 |
static Property applesmc_isa_properties[] = {
|
224 |
DEFINE_PROP_HEX32("iobase", struct AppleSMCStatus, iobase, |
225 |
APPLESMC_DEFAULT_IOBASE), |
226 |
DEFINE_PROP_STRING("osk", struct AppleSMCStatus, osk), |
227 |
DEFINE_PROP_END_OF_LIST(), |
228 |
}; |
229 |
|
230 |
static void qdev_applesmc_class_init(ObjectClass *klass, void *data) |
231 |
{ |
232 |
DeviceClass *dc = DEVICE_CLASS(klass); |
233 |
ISADeviceClass *ic = ISA_DEVICE_CLASS(klass); |
234 |
ic->init = applesmc_isa_init; |
235 |
dc->reset = qdev_applesmc_isa_reset; |
236 |
dc->props = applesmc_isa_properties; |
237 |
} |
238 |
|
239 |
static TypeInfo applesmc_isa_info = {
|
240 |
.name = "isa-applesmc",
|
241 |
.parent = TYPE_ISA_DEVICE, |
242 |
.instance_size = sizeof(struct AppleSMCStatus), |
243 |
.class_init = qdev_applesmc_class_init, |
244 |
}; |
245 |
|
246 |
static void applesmc_register_types(void) |
247 |
{ |
248 |
type_register_static(&applesmc_isa_info); |
249 |
} |
250 |
|
251 |
type_init(applesmc_register_types) |