blob: 08189a948bc8a368fe7cabd3ebf9dd84cd3093c6 [file] [log] [blame]
Stefan Reinauer38cd29e2009-08-11 21:28:25 +00001/******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
3 * Copyright (c) 2009 Pattrick Hueper <phueper@hueper.net>
4 * All rights reserved.
5 * This program and the accompanying materials
6 * are made available under the terms of the BSD License
7 * which accompanies this distribution, and is available at
8 * http://www.opensource.org/licenses/bsd-license.php
9 *
10 * Contributors:
11 * IBM Corporation - initial implementation
12 *****************************************************************************/
13
14#include <types.h>
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000015#include "compat/rtas.h"
16#include "compat/time.h"
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000017#include "device.h"
18#include "debug.h"
19#include <x86emu/x86emu.h>
Stefan Reinauer91f14232012-12-07 16:55:12 -080020#include <device/oprom/include/io.h>
Uwe Hermann01ce6012010-03-05 10:03:50 +000021#include "io.h"
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000022
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000023#include <device/pci.h>
24#include <device/pci_ops.h>
Patrick Georgi91443042011-01-13 11:38:46 +000025#include <device/resource.h>
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000026
Stefan Reinauer074356e2009-10-25 19:50:47 +000027#include <arch/io.h>
Edward O'Callaghana173a622014-06-17 17:05:36 +100028
Stefan Reinauer1d888a92011-04-21 20:24:43 +000029#if CONFIG_YABEL_DIRECTHW
Stefan Reinauerd650e992010-02-22 04:33:13 +000030u8 my_inb(X86EMU_pioAddr addr)
31{
32 u8 val;
33
34 val = inb(addr);
Uwe Hermann01ce6012010-03-05 10:03:50 +000035 DEBUG_PRINTF_IO("inb(0x%04x) = 0x%02x\n", addr, val);
Stefan Reinauerd650e992010-02-22 04:33:13 +000036
37 return val;
38}
39
40u16 my_inw(X86EMU_pioAddr addr)
41{
42 u16 val;
43
44 val = inw(addr);
Uwe Hermann01ce6012010-03-05 10:03:50 +000045 DEBUG_PRINTF_IO("inw(0x%04x) = 0x%04x\n", addr, val);
Stefan Reinauerd650e992010-02-22 04:33:13 +000046
Stefan Reinauerd650e992010-02-22 04:33:13 +000047 return val;
48}
49
50u32 my_inl(X86EMU_pioAddr addr)
51{
52 u32 val;
53
54 val = inl(addr);
Uwe Hermann01ce6012010-03-05 10:03:50 +000055 DEBUG_PRINTF_IO("inl(0x%04x) = 0x%08x\n", addr, val);
Stefan Reinauerd650e992010-02-22 04:33:13 +000056
Stefan Reinauerd650e992010-02-22 04:33:13 +000057 return val;
58}
59
60void my_outb(X86EMU_pioAddr addr, u8 val)
61{
Uwe Hermann01ce6012010-03-05 10:03:50 +000062 DEBUG_PRINTF_IO("outb(0x%02x, 0x%04x)\n", val, addr);
Stefan Reinauerd650e992010-02-22 04:33:13 +000063 outb(val, addr);
64}
65
66void my_outw(X86EMU_pioAddr addr, u16 val)
67{
Uwe Hermann01ce6012010-03-05 10:03:50 +000068 DEBUG_PRINTF_IO("outw(0x%04x, 0x%04x)\n", val, addr);
Stefan Reinauerd650e992010-02-22 04:33:13 +000069 outw(val, addr);
70}
71
72void my_outl(X86EMU_pioAddr addr, u32 val)
73{
Uwe Hermann01ce6012010-03-05 10:03:50 +000074 DEBUG_PRINTF_IO("outl(0x%08x, 0x%04x)\n", val, addr);
Stefan Reinauerd650e992010-02-22 04:33:13 +000075 outl(val, addr);
76}
77
78#else
79
Stefan Reinauer5ae1db02010-04-13 21:29:43 +000080static unsigned int
81read_io(void *addr, size_t sz)
82{
83 unsigned int ret;
84 /* since we are using inb instructions, we need the port number as 16bit value */
85 u16 port = (u16)(u32) addr;
86
87 switch (sz) {
88 case 1:
Stefan Reinauer91f14232012-12-07 16:55:12 -080089 ret = inb(port);
Stefan Reinauer5ae1db02010-04-13 21:29:43 +000090 break;
91 case 2:
Stefan Reinauer91f14232012-12-07 16:55:12 -080092 ret = inw(port);
Stefan Reinauer5ae1db02010-04-13 21:29:43 +000093 break;
94 case 4:
Stefan Reinauer91f14232012-12-07 16:55:12 -080095 ret = inl(port);
Stefan Reinauer5ae1db02010-04-13 21:29:43 +000096 break;
97 default:
98 ret = 0;
99 }
100
101 return ret;
102}
103
104static int
105write_io(void *addr, unsigned int value, size_t sz)
106{
107 u16 port = (u16)(u32) addr;
108 switch (sz) {
109 /* since we are using inb instructions, we need the port number as 16bit value */
110 case 1:
Stefan Reinauer91f14232012-12-07 16:55:12 -0800111 outb(value, port);
Stefan Reinauer5ae1db02010-04-13 21:29:43 +0000112 break;
113 case 2:
Stefan Reinauer91f14232012-12-07 16:55:12 -0800114 outw(value, port);
Stefan Reinauer5ae1db02010-04-13 21:29:43 +0000115 break;
116 case 4:
Stefan Reinauer91f14232012-12-07 16:55:12 -0800117 outl(value, port);
Stefan Reinauer5ae1db02010-04-13 21:29:43 +0000118 break;
119 default:
120 return -1;
121 }
122
123 return 0;
124}
125
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000126u32 pci_cfg_read(X86EMU_pioAddr addr, u8 size);
127void pci_cfg_write(X86EMU_pioAddr addr, u32 val, u8 size);
128u8 handle_port_61h(void);
129
130u8
131my_inb(X86EMU_pioAddr addr)
132{
133 u8 rval = 0xFF;
134 unsigned long translated_addr = addr;
Patrick Georgi91443042011-01-13 11:38:46 +0000135 u8 translated = biosemu_dev_translate_address(IORESOURCE_IO, &translated_addr);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000136 if (translated != 0) {
Martin Roth63373ed2013-07-08 16:24:19 -0600137 //translation successful, access Device I/O (BAR or Legacy...)
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000138 DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __func__,
139 addr);
140 //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
141 rval = read_io((void *)translated_addr, 1);
142 DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %02x\n", __func__,
143 addr, rval);
144 return rval;
145 } else {
146 switch (addr) {
147 case 0x61:
148 //8254 KB Controller / Timer Port
Stefan Reinauer074356e2009-10-25 19:50:47 +0000149 // rval = handle_port_61h();
150 rval = inb(0x61);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000151 //DEBUG_PRINTF_IO("%s(%04x) KB / Timer Port B --> %02x\n", __func__, addr, rval);
152 return rval;
153 break;
154 case 0xCFC:
155 case 0xCFD:
156 case 0xCFE:
157 case 0xCFF:
158 // PCI Config Mechanism 1 Ports
159 return (u8) pci_cfg_read(addr, 1);
160 break;
161 case 0x0a:
162 CHECK_DBG(DEBUG_INTR) {
163 X86EMU_trace_on();
164 }
165 M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F;
166 //HALT_SYS();
167 // no break, intentional fall-through to default!!
168 default:
169 DEBUG_PRINTF_IO
170 ("%s(%04x) reading from bios_device.io_buffer\n",
171 __func__, addr);
172 rval = *((u8 *) (bios_device.io_buffer + addr));
173 DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %02x\n",
174 __func__, addr, rval);
175 return rval;
176 break;
177 }
178 }
179}
180
181u16
182my_inw(X86EMU_pioAddr addr)
183{
184 unsigned long translated_addr = addr;
Patrick Georgi91443042011-01-13 11:38:46 +0000185 u8 translated = biosemu_dev_translate_address(IORESOURCE_IO, &translated_addr);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000186 if (translated != 0) {
Martin Roth63373ed2013-07-08 16:24:19 -0600187 //translation successful, access Device I/O (BAR or Legacy...)
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000188 DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __func__,
189 addr);
190 //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
191 u16 rval;
192 if ((translated_addr & (u64) 0x1) == 0) {
193 // 16 bit aligned access...
194 u16 tempval = read_io((void *)translated_addr, 2);
195 //little endian conversion
196 rval = in16le((void *) &tempval);
197 } else {
198 // unaligned access, read single bytes, little-endian
199 rval = (read_io((void *)translated_addr, 1) << 8)
200 | (read_io((void *)(translated_addr + 1), 1));
201 }
202 DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %04x\n", __func__,
203 addr, rval);
204 return rval;
205 } else {
206 switch (addr) {
207 case 0xCFC:
208 case 0xCFE:
209 //PCI Config Mechanism 1
210 return (u16) pci_cfg_read(addr, 2);
211 break;
212 default:
213 DEBUG_PRINTF_IO
214 ("%s(%04x) reading from bios_device.io_buffer\n",
215 __func__, addr);
216 u16 rval =
217 in16le((void *) bios_device.io_buffer + addr);
218 DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %04x\n",
219 __func__, addr, rval);
220 return rval;
221 break;
222 }
223 }
224}
225
226u32
227my_inl(X86EMU_pioAddr addr)
228{
229 unsigned long translated_addr = addr;
Patrick Georgi91443042011-01-13 11:38:46 +0000230 u8 translated = biosemu_dev_translate_address(IORESOURCE_IO, &translated_addr);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000231 if (translated != 0) {
Martin Roth63373ed2013-07-08 16:24:19 -0600232 //translation successful, access Device I/O (BAR or Legacy...)
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000233 DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __func__,
234 addr);
235 //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
236 u32 rval;
237 if ((translated_addr & (u64) 0x3) == 0) {
238 // 32 bit aligned access...
239 u32 tempval = read_io((void *) translated_addr, 4);
240 //little endian conversion
241 rval = in32le((void *) &tempval);
242 } else {
243 // unaligned access, read single bytes, little-endian
244 rval = (read_io((void *)(translated_addr), 1) << 24)
245 | (read_io((void *)(translated_addr + 1), 1) << 16)
246 | (read_io((void *)(translated_addr + 2), 1) << 8)
247 | (read_io((void *)(translated_addr + 3), 1));
248 }
249 DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %08x\n", __func__,
250 addr, rval);
251 return rval;
252 } else {
253 switch (addr) {
254 case 0xCFC:
255 //PCI Config Mechanism 1
256 return pci_cfg_read(addr, 4);
257 break;
258 default:
259 DEBUG_PRINTF_IO
260 ("%s(%04x) reading from bios_device.io_buffer\n",
261 __func__, addr);
262 u32 rval =
263 in32le((void *) bios_device.io_buffer + addr);
264 DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %08x\n",
265 __func__, addr, rval);
266 return rval;
267 break;
268 }
269 }
270}
271
272void
273my_outb(X86EMU_pioAddr addr, u8 val)
274{
275 unsigned long translated_addr = addr;
Patrick Georgi91443042011-01-13 11:38:46 +0000276 u8 translated = biosemu_dev_translate_address(IORESOURCE_IO, &translated_addr);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000277 if (translated != 0) {
Martin Roth63373ed2013-07-08 16:24:19 -0600278 //translation successful, access Device I/O (BAR or Legacy...)
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000279 DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n",
280 __func__, addr, val);
281 //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
282 write_io((void *) translated_addr, val, 1);
283 DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %02x\n", __func__,
284 addr, val);
285 } else {
286 switch (addr) {
287 case 0xCFC:
288 case 0xCFD:
289 case 0xCFE:
290 case 0xCFF:
291 // PCI Config Mechanism 1 Ports
292 pci_cfg_write(addr, val, 1);
293 break;
294 default:
295 DEBUG_PRINTF_IO
296 ("%s(%04x,%02x) writing to bios_device.io_buffer\n",
297 __func__, addr, val);
298 *((u8 *) (bios_device.io_buffer + addr)) = val;
299 break;
300 }
301 }
302}
303
304void
305my_outw(X86EMU_pioAddr addr, u16 val)
306{
307 unsigned long translated_addr = addr;
Patrick Georgi91443042011-01-13 11:38:46 +0000308 u8 translated = biosemu_dev_translate_address(IORESOURCE_IO, &translated_addr);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000309 if (translated != 0) {
Martin Roth63373ed2013-07-08 16:24:19 -0600310 //translation successful, access Device I/O (BAR or Legacy...)
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000311 DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n",
312 __func__, addr, val);
313 //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
314 if ((translated_addr & (u64) 0x1) == 0) {
315 // little-endian conversion
316 u16 tempval = in16le((void *) &val);
317 // 16 bit aligned access...
318 write_io((void *) translated_addr, tempval, 2);
319 } else {
320 // unaligned access, write single bytes, little-endian
321 write_io(((void *) (translated_addr + 1)),
322 (u8) ((val & 0xFF00) >> 8), 1);
323 write_io(((void *) translated_addr),
324 (u8) (val & 0x00FF), 1);
325 }
326 DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %04x\n", __func__,
327 addr, val);
328 } else {
329 switch (addr) {
330 case 0xCFC:
331 case 0xCFE:
332 // PCI Config Mechanism 1 Ports
333 pci_cfg_write(addr, val, 2);
334 break;
335 default:
336 DEBUG_PRINTF_IO
337 ("%s(%04x,%04x) writing to bios_device.io_buffer\n",
338 __func__, addr, val);
339 out16le((void *) bios_device.io_buffer + addr, val);
340 break;
341 }
342 }
343}
344
345void
346my_outl(X86EMU_pioAddr addr, u32 val)
347{
348 unsigned long translated_addr = addr;
Patrick Georgi91443042011-01-13 11:38:46 +0000349 u8 translated = biosemu_dev_translate_address(IORESOURCE_IO, &translated_addr);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000350 if (translated != 0) {
Martin Roth63373ed2013-07-08 16:24:19 -0600351 //translation successful, access Device I/O (BAR or Legacy...)
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000352 DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n",
353 __func__, addr, val);
354 //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
355 if ((translated_addr & (u64) 0x3) == 0) {
356 // little-endian conversion
357 u32 tempval = in32le((void *) &val);
358 // 32 bit aligned access...
359 write_io((void *) translated_addr, tempval, 4);
360 } else {
361 // unaligned access, write single bytes, little-endian
362 write_io(((void *) translated_addr + 3),
363 (u8) ((val & 0xFF000000) >> 24), 1);
364 write_io(((void *) translated_addr + 2),
365 (u8) ((val & 0x00FF0000) >> 16), 1);
366 write_io(((void *) translated_addr + 1),
367 (u8) ((val & 0x0000FF00) >> 8), 1);
368 write_io(((void *) translated_addr),
369 (u8) (val & 0x000000FF), 1);
370 }
371 DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %08x\n", __func__,
372 addr, val);
373 } else {
374 switch (addr) {
375 case 0xCFC:
376 // PCI Config Mechanism 1 Ports
377 pci_cfg_write(addr, val, 4);
378 break;
379 default:
380 DEBUG_PRINTF_IO
381 ("%s(%04x,%08x) writing to bios_device.io_buffer\n",
382 __func__, addr, val);
383 out32le((void *) bios_device.io_buffer + addr, val);
384 break;
385 }
386 }
387}
388
389u32
390pci_cfg_read(X86EMU_pioAddr addr, u8 size)
391{
392 u32 rval = 0xFFFFFFFF;
393 struct device * dev;
394 if ((addr >= 0xCFC) && ((addr + size) <= 0xD00)) {
395 // PCI Configuration Mechanism 1 step 1
396 // write to 0xCF8, sets bus, device, function and Config Space offset
397 // later read from 0xCFC-0xCFF returns the value...
398 u8 bus, devfn, offs;
399 u32 port_cf8_val = my_inl(0xCF8);
400 if ((port_cf8_val & 0x80000000) != 0) {
401 //highest bit enables config space mapping
402 bus = (port_cf8_val & 0x00FF0000) >> 16;
403 devfn = (port_cf8_val & 0x0000FF00) >> 8;
404 offs = (port_cf8_val & 0x000000FF);
405 offs += (addr - 0xCFC); // if addr is not 0xcfc, the offset is moved accordingly
406 DEBUG_PRINTF_INTR("%s(): PCI Config Read from device: bus: %02x, devfn: %02x, offset: %02x\n",
407 __func__, bus, devfn, offs);
Stefan Reinauer1d888a92011-04-21 20:24:43 +0000408#if CONFIG_YABEL_PCI_ACCESS_OTHER_DEVICES
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000409 dev = dev_find_slot(bus, devfn);
410 DEBUG_PRINTF_INTR("%s(): dev_find_slot() returned: %s\n",
411 __func__, dev_path(dev));
412 if (dev == 0) {
413 // fail accesses to non-existent devices...
414#else
415 dev = bios_device.dev;
416 if ((bus != bios_device.bus)
417 || (devfn != bios_device.devfn)) {
418 // fail accesses to any device but ours...
419#endif
420 printf
421 ("%s(): Config read access invalid device! bus: %02x (%02x), devfn: %02x (%02x), offs: %02x\n",
422 __func__, bus, bios_device.bus, devfn,
423 bios_device.devfn, offs);
424 SET_FLAG(F_CF);
425 HALT_SYS();
426 return 0;
427 } else {
Stefan Reinauer1d888a92011-04-21 20:24:43 +0000428#if CONFIG_PCI_OPTION_ROM_RUN_YABEL
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000429 switch (size) {
430 case 1:
431 rval = pci_read_config8(dev, offs);
432 break;
433 case 2:
434 rval = pci_read_config16(dev, offs);
435 break;
436 case 4:
437 rval = pci_read_config32(dev, offs);
438 break;
439 }
440#else
441 rval =
442 (u32) rtas_pci_config_read(bios_device.
443 puid, size,
444 bus, devfn,
445 offs);
446#endif
447 DEBUG_PRINTF_IO
448 ("%s(%04x) PCI Config Read @%02x, size: %d --> 0x%08x\n",
449 __func__, addr, offs, size, rval);
450 }
451 }
452 }
453 return rval;
454}
455
456void
457pci_cfg_write(X86EMU_pioAddr addr, u32 val, u8 size)
458{
459 if ((addr >= 0xCFC) && ((addr + size) <= 0xD00)) {
460 // PCI Configuration Mechanism 1 step 1
461 // write to 0xCF8, sets bus, device, function and Config Space offset
462 // later write to 0xCFC-0xCFF sets the value...
463 u8 bus, devfn, offs;
464 u32 port_cf8_val = my_inl(0xCF8);
465 if ((port_cf8_val & 0x80000000) != 0) {
466 //highest bit enables config space mapping
467 bus = (port_cf8_val & 0x00FF0000) >> 16;
468 devfn = (port_cf8_val & 0x0000FF00) >> 8;
469 offs = (port_cf8_val & 0x000000FF);
470 offs += (addr - 0xCFC); // if addr is not 0xcfc, the offset is moved accordingly
471 if ((bus != bios_device.bus)
472 || (devfn != bios_device.devfn)) {
473 // fail accesses to any device but ours...
474 printf
475 ("Config write access invalid! PCI device %x:%x.%x, offs: %x\n",
476 bus, devfn >> 3, devfn & 7, offs);
Patrick Georgic4b2a1b2012-07-20 13:44:50 +0200477#if !CONFIG_YABEL_PCI_FAKE_WRITING_OTHER_DEVICES_CONFIG
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000478 HALT_SYS();
Patrick Georgic4b2a1b2012-07-20 13:44:50 +0200479#endif
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000480 } else {
Stefan Reinauer1d888a92011-04-21 20:24:43 +0000481#if CONFIG_PCI_OPTION_ROM_RUN_YABEL
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000482 switch (size) {
483 case 1:
484 pci_write_config8(bios_device.dev, offs, val);
485 break;
486 case 2:
487 pci_write_config16(bios_device.dev, offs, val);
488 break;
489 case 4:
490 pci_write_config32(bios_device.dev, offs, val);
491 break;
492 }
493#else
494 rtas_pci_config_write(bios_device.puid,
495 size, bus, devfn, offs,
496 val);
497#endif
498 DEBUG_PRINTF_IO
499 ("%s(%04x) PCI Config Write @%02x, size: %d <-- 0x%08x\n",
500 __func__, addr, offs, size, val);
501 }
502 }
503 }
504}
505
506u8
507handle_port_61h(void)
508{
509 static u64 last_time = 0;
510 u64 curr_time = get_time();
511 u64 time_diff; // time since last call
512 u32 period_ticks; // length of a period in ticks
513 u32 nr_periods; //number of periods passed since last call
514 // bit 4 should toggle with every (DRAM) refresh cycle... (66kHz??)
515 time_diff = curr_time - last_time;
516 // at 66kHz a period is ~ 15 ns long, converted to ticks: (tb_freq is ticks/second)
517 // TODO: as long as the frequency does not change, we should not calculate this every time
518 period_ticks = (15 * tb_freq) / 1000000;
519 nr_periods = time_diff / period_ticks;
520 // if the number if ticks passed since last call is odd, we toggle bit 4
521 if ((nr_periods % 2) != 0) {
522 *((u8 *) (bios_device.io_buffer + 0x61)) ^= 0x10;
523 }
524 //finally read the value from the io_buffer
525 return *((u8 *) (bios_device.io_buffer + 0x61));
526}
Stefan Reinauerd650e992010-02-22 04:33:13 +0000527#endif