This patchset significantly improves OpenBIOS's ability to boot OS 9, adding ESCC legacy ports, reworking ADB interaction with QEMU and fixing IDE controller interaction with the OS.
Note that the patch to interpreter.fs is a temporary measure pending a fix that Mark is writing, and that RTAS must be disabled for the OS to boot.
Signed-off-by: Cormac O'Brien cormac@c-obrien.org --- Changes from v1: * Initialize ESCC legacy ports * Don't enable non-implemented DBDMA interrupts * Update some commit messages and comments
Cormac O'Brien (5): interpreter.fs: allow evaluate to split words on CR as well as LF ppc: fix up power-mgt properties ppc: add ESCC legacy support ppc: fix CUDA ADB packet header format ppc: fix up IDE device tree and control transfer
arch/ppc/qemu/methods.c | 2 + drivers/cuda.c | 17 ++++++-- drivers/escc.c | 88 ++++++++++++++++++++++++++++++++++-------- drivers/escc.h | 2 + drivers/ide.c | 34 ++++++++++++++-- drivers/pci.c | 6 +++ forth/bootstrap/interpreter.fs | 2 +- 7 files changed, 126 insertions(+), 25 deletions(-)
Otherwise the Forth intepreter fails due to lack of buffer space when trying to execute large boot scripts on platforms that use CR instead of LF for line endings (particularly MacOS 9).
Signed-off-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk Signed-off-by: Cormac O'Brien cormac@c-obrien.org --- forth/bootstrap/interpreter.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/forth/bootstrap/interpreter.fs b/forth/bootstrap/interpreter.fs index 5187058..5c3e469 100644 --- a/forth/bootstrap/interpreter.fs +++ b/forth/bootstrap/interpreter.fs @@ -163,7 +163,7 @@ defer outer-interpreter : evaluate ( str len -- ?? ) 2dup + -rot over + over do - i c@ 0a = if + i c@ dup 0a = swap 0d = or if i over - (evaluate) i 1+
Modify some basic device tree properties to more accurately emulate a real Mac machine.
Signed-off-by: Cormac O'Brien cormac@c-obrien.org --- drivers/cuda.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/cuda.c b/drivers/cuda.c index 9555dea..f117b0b 100644 --- a/drivers/cuda.c +++ b/drivers/cuda.c @@ -380,7 +380,8 @@ powermgt_init(char *path)
ph = find_dev(buf); set_property(ph, "device_type", "power-mgt", 10); - set_property(ph, "compatible", "power-mgt", 10); + set_property(ph, "mgt-kind", "min-consumption-pwm-led", strlen("min-consumption-pwm-led") + 1); + set_property(ph, "compatible", "cuda", strlen("cuda") + 1); }
cuda_t *cuda_init (const char *path, phys_addr_t base)
On 21.08.15 00:45, Cormac O'Brien wrote:
Modify some basic device tree properties to more accurately emulate a real Mac machine.
Signed-off-by: Cormac O'Brien cormac@c-obrien.org
drivers/cuda.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/cuda.c b/drivers/cuda.c index 9555dea..f117b0b 100644 --- a/drivers/cuda.c +++ b/drivers/cuda.c @@ -380,7 +380,8 @@ powermgt_init(char *path)
ph = find_dev(buf); set_property(ph, "device_type", "power-mgt", 10);
- set_property(ph, "compatible", "power-mgt", 10);
- set_property(ph, "mgt-kind", "min-consumption-pwm-led", strlen("min-consumption-pwm-led") + 1);
- set_property(ph, "compatible", "cuda", strlen("cuda") + 1);
... and more candidates for a string helper function
Alex
Mac OS 9 requires that the host provide escc-legacy serial ports, so we add them here to get further in the boot process.
Signed-off-by: Cormac O'Brien cormac@c-obrien.org --- drivers/escc.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++------------ drivers/escc.h | 2 ++ drivers/pci.c | 6 ++++ 3 files changed, 79 insertions(+), 17 deletions(-)
diff --git a/drivers/escc.c b/drivers/escc.c index 240043b..afb97fa 100644 --- a/drivers/escc.c +++ b/drivers/escc.c @@ -380,12 +380,22 @@ ob_zs_init(phys_addr_t base, uint64_t offset, int intr, int slave, int keyboard)
static void escc_add_channel(const char *path, const char *node, phys_addr_t addr, - uint32_t offset) + int esnum) { char buf[64], tty[32]; phandle_t dnode, aliases; - int len; - cell props[2]; + + cell props[10]; + int offset; + int legacy; + + switch (esnum) { + case 2: offset = 1; legacy = 0; break; + case 3: offset = 0; legacy = 0; break; + case 4: offset = 1; legacy = 1; break; + case 5: offset = 0; legacy = 1; break; + default: return; + }
/* add device */
@@ -411,16 +421,28 @@ escc_add_channel(const char *path, const char *node, phys_addr_t addr, set_property(dnode, "device_type", "serial", strlen("serial") + 1);
- snprintf(buf, sizeof(buf), "ch-%s", node); - len = strlen(buf) + 1; - snprintf(buf + len, sizeof(buf) - len, "CHRP,es2"); - set_property(dnode, "compatible", buf, len + 9); - - props[0] = IO_ESCC_OFFSET + offset * 0x20; - props[1] = 0x00000020; - set_property(dnode, "reg", (char *)&props, 2 * sizeof(cell)); + snprintf(buf, sizeof(buf), "chrp,es%d", esnum); + set_property(dnode, "compatible", buf, 9); + + if (legacy) { + props[0] = IO_ESCC_LEGACY_OFFSET + offset * 0x4; + props[1] = 0x00000001; + props[2] = IO_ESCC_LEGACY_OFFSET + offset * 0x4 + 2; + props[3] = 0x00000001; + props[4] = IO_ESCC_LEGACY_OFFSET + offset * 0x4 + 6; + props[5] = 0x00000001; + set_property(dnode, "reg", (char *)&props, 6 * sizeof(cell)); + } else { + props[0] = IO_ESCC_OFFSET + offset * 0x20; + props[1] = 0x00000020; + set_property(dnode, "reg", (char *)&props, 2 * sizeof(cell)); + }
- props[0] = addr + IO_ESCC_OFFSET + offset * 0x20; + if (legacy) { + props[0] = addr + IO_ESCC_LEGACY_OFFSET + offset * 0x4; + } else { + props[0] = addr + IO_ESCC_OFFSET + offset * 0x20; + } OLDWORLD(set_property(dnode, "AAPL,address", (char *)&props, 1 * sizeof(cell)));
@@ -430,13 +452,21 @@ escc_add_channel(const char *path, const char *node, phys_addr_t addr,
props[0] = (0x24) + offset; props[1] = 0; + props[2] = 0; NEWWORLD(set_property(dnode, "interrupts", - (char *)&props, 2 * sizeof(cell))); + (char *)&props, 3 * sizeof(cell)));
device_end();
- uart_init_line((unsigned char*)addr + IO_ESCC_OFFSET + offset * 0x20, - CONFIG_SERIAL_SPEED); + if (legacy) { + uart_init_line( + (unsigned char*)addr + IO_ESCC_LEGACY_OFFSET + offset * 0x4, + CONFIG_SERIAL_SPEED); + } else { + uart_init_line( + (unsigned char*)addr + IO_ESCC_OFFSET + offset * 0x20, + CONFIG_SERIAL_SPEED); + } }
void @@ -467,10 +497,34 @@ escc_init(const char *path, phys_addr_t addr)
fword("finish-device");
- escc_add_channel(buf, "a", addr, 1); - escc_add_channel(buf, "b", addr, 0); + escc_add_channel(buf, "a", addr, 2); + escc_add_channel(buf, "b", addr, 3);
escc_serial_dev = (unsigned char *)addr + IO_ESCC_OFFSET + (CONFIG_SERIAL_PORT ? 0 : 0x20); + + push_str(path); + fword("find-device"); + fword("new-device"); + + push_str("escc-legacy"); + fword("device-name"); + + snprintf(buf, sizeof(buf), "%s/escc-legacy", path); + + dnode = find_dev(buf); + + set_int_property(dnode, "#address-cells", 1); + props[0] = __cpu_to_be32(IO_ESCC_LEGACY_OFFSET); + props[1] = __cpu_to_be32(IO_ESCC_LEGACY_SIZE); + set_property(dnode, "reg", (char *)&props, sizeof(props)); + set_property(dnode, "device_type", "escc-legacy", + strlen("escc-legacy") + 1); + set_property(dnode, "compatible", "chrp,es1", 9); + + fword("finish-device"); + + escc_add_channel(buf, "a", addr, 4); + escc_add_channel(buf, "b", addr, 5); } #endif diff --git a/drivers/escc.h b/drivers/escc.h index caaf00d..e73f267 100644 --- a/drivers/escc.h +++ b/drivers/escc.h @@ -1,6 +1,8 @@
#define IO_ESCC_SIZE 0x00001000 #define IO_ESCC_OFFSET 0x00013000 +#define IO_ESCC_LEGACY_SIZE 0x00001000 +#define IO_ESCC_LEGACY_OFFSET 0x00012000
#define ZS_REGS 8
diff --git a/drivers/pci.c b/drivers/pci.c index 366f4a1..a68e9dc 100644 --- a/drivers/pci.c +++ b/drivers/pci.c @@ -1420,6 +1420,12 @@ static void ob_pci_host_set_interrupt_map(phandle_t host) target_node = find_dev("/pci/mac-io/escc/ch-b"); set_int_property(target_node, "interrupt-parent", dnode);
+ target_node = find_dev("/pci/mac-io/escc-legacy/ch-a"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/escc-legacy/ch-b"); + set_int_property(target_node, "interrupt-parent", dnode); + /* QEMU only emulates 2 of the 3 ata buses currently */ /* On a new world Mac these are not numbered but named by the * ATA version they support. Thus we have: ata-3, ata-3, ata-4
On 21.08.15 00:45, Cormac O'Brien wrote:
Mac OS 9 requires that the host provide escc-legacy serial ports, so we add them here to get further in the boot process.
Signed-off-by: Cormac O'Brien cormac@c-obrien.org
drivers/escc.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++------------ drivers/escc.h | 2 ++ drivers/pci.c | 6 ++++ 3 files changed, 79 insertions(+), 17 deletions(-)
diff --git a/drivers/escc.c b/drivers/escc.c index 240043b..afb97fa 100644 --- a/drivers/escc.c +++ b/drivers/escc.c @@ -380,12 +380,22 @@ ob_zs_init(phys_addr_t base, uint64_t offset, int intr, int slave, int keyboard)
static void escc_add_channel(const char *path, const char *node, phys_addr_t addr,
uint32_t offset)
int esnum)
{ char buf[64], tty[32]; phandle_t dnode, aliases;
- int len;
- cell props[2];
cell props[10];
int offset;
int legacy;
switch (esnum) {
case 2: offset = 1; legacy = 0; break;
case 3: offset = 0; legacy = 0; break;
case 4: offset = 1; legacy = 1; break;
case 5: offset = 0; legacy = 1; break;
default: return;
}
/* add device */
@@ -411,16 +421,28 @@ escc_add_channel(const char *path, const char *node, phys_addr_t addr, set_property(dnode, "device_type", "serial", strlen("serial") + 1);
- snprintf(buf, sizeof(buf), "ch-%s", node);
- len = strlen(buf) + 1;
- snprintf(buf + len, sizeof(buf) - len, "CHRP,es2");
- set_property(dnode, "compatible", buf, len + 9);
- props[0] = IO_ESCC_OFFSET + offset * 0x20;
- props[1] = 0x00000020;
- set_property(dnode, "reg", (char *)&props, 2 * sizeof(cell));
- snprintf(buf, sizeof(buf), "chrp,es%d", esnum);
- set_property(dnode, "compatible", buf, 9);
- if (legacy) {
props[0] = IO_ESCC_LEGACY_OFFSET + offset * 0x4;
props[1] = 0x00000001;
props[2] = IO_ESCC_LEGACY_OFFSET + offset * 0x4 + 2;
props[3] = 0x00000001;
props[4] = IO_ESCC_LEGACY_OFFSET + offset * 0x4 + 6;
props[5] = 0x00000001;
set_property(dnode, "reg", (char *)&props, 6 * sizeof(cell));
- } else {
props[0] = IO_ESCC_OFFSET + offset * 0x20;
props[1] = 0x00000020;
set_property(dnode, "reg", (char *)&props, 2 * sizeof(cell));
- }
- props[0] = addr + IO_ESCC_OFFSET + offset * 0x20;
- if (legacy) {
props[0] = addr + IO_ESCC_LEGACY_OFFSET + offset * 0x4;
- } else {
props[0] = addr + IO_ESCC_OFFSET + offset * 0x20;
- } OLDWORLD(set_property(dnode, "AAPL,address", (char *)&props, 1 * sizeof(cell)));
@@ -430,13 +452,21 @@ escc_add_channel(const char *path, const char *node, phys_addr_t addr,
props[0] = (0x24) + offset; props[1] = 0;
- props[2] = 0; NEWWORLD(set_property(dnode, "interrupts",
(char *)&props, 2 * sizeof(cell)));
(char *)&props, 3 * sizeof(cell)));
device_end();
- uart_init_line((unsigned char*)addr + IO_ESCC_OFFSET + offset * 0x20,
CONFIG_SERIAL_SPEED);
- if (legacy) {
uart_init_line(
(unsigned char*)addr + IO_ESCC_LEGACY_OFFSET + offset * 0x4,
CONFIG_SERIAL_SPEED);
- } else {
uart_init_line(
(unsigned char*)addr + IO_ESCC_OFFSET + offset * 0x20,
CONFIG_SERIAL_SPEED);
- }
}
void @@ -467,10 +497,34 @@ escc_init(const char *path, phys_addr_t addr)
fword("finish-device");
- escc_add_channel(buf, "a", addr, 1);
- escc_add_channel(buf, "b", addr, 0);
escc_add_channel(buf, "a", addr, 2);
escc_add_channel(buf, "b", addr, 3);
escc_serial_dev = (unsigned char *)addr + IO_ESCC_OFFSET + (CONFIG_SERIAL_PORT ? 0 : 0x20);
push_str(path);
fword("find-device");
fword("new-device");
push_str("escc-legacy");
fword("device-name");
snprintf(buf, sizeof(buf), "%s/escc-legacy", path);
dnode = find_dev(buf);
set_int_property(dnode, "#address-cells", 1);
props[0] = __cpu_to_be32(IO_ESCC_LEGACY_OFFSET);
props[1] = __cpu_to_be32(IO_ESCC_LEGACY_SIZE);
set_property(dnode, "reg", (char *)&props, sizeof(props));
set_property(dnode, "device_type", "escc-legacy",
strlen("escc-legacy") + 1);
Same comment about the string helper. Otherwise looks good.
Alex
QEMU < 2.5 uses a 2-byte header for CUDA ADB packets where it should use a 3-byte one, so this commit allows cuda_adb_req() to differentiate between the two formats and act accordingly.
Signed-off-by: Cormac O'Brien cormac@c-obrien.org --- drivers/cuda.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/cuda.c b/drivers/cuda.c index f117b0b..9197729 100644 --- a/drivers/cuda.c +++ b/drivers/cuda.c @@ -144,8 +144,18 @@ static int cuda_adb_req (void *host, const uint8_t *snd_buf, int len, // CUDA_DPRINTF("len: %d %02x\n", len, snd_buf[0]); len = cuda_request(host, ADB_PACKET, snd_buf, len, buffer); if (len > 1 && buffer[0] == ADB_PACKET) { - pos = buffer + 2; - len -= 2; + /* QEMU < 2.5 uses a 2-byte header where it should use a 3-byte one, + * so we check to see what kind of header we ought to use. + */ + if (len > 2 && buffer[2] == snd_buf[0]) { + /* Correct 3-byte header */ + pos = buffer + 3; + len -= 3; + } else { + /* Old 2-byte header */ + pos = buffer + 2; + len -= 2; + } } else { pos = buffer + 1; len = -1;
Mac OS 9 is finicky about devices having the correct properties, so we change some IDE data to more closely model a real New World Mac. We also add ide_exit() to ciface_quiesce() to properly prepare the drives for transferring control to the operating system.
Signed-off-by: Cormac O'Brien cormac@c-obrien.org --- arch/ppc/qemu/methods.c | 2 ++ drivers/ide.c | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/arch/ppc/qemu/methods.c b/arch/ppc/qemu/methods.c index cb72bc1..adf3ea2 100644 --- a/arch/ppc/qemu/methods.c +++ b/arch/ppc/qemu/methods.c @@ -109,11 +109,13 @@ NODE_METHODS( tty ) = {
DECLARE_NODE( ciface, 0, 0, "+/openprom/client-services" );
+extern void ide_exit(void); /* ( -- ) */ static void ciface_quiesce( unsigned long args[], unsigned long ret[] ) { usb_exit(); + ide_exit(); #if 0 unsigned long msr; /* This seems to be the correct thing to do - but I'm not sure */ diff --git a/drivers/ide.c b/drivers/ide.c index 327c64a..8ef34cb 100644 --- a/drivers/ide.c +++ b/drivers/ide.c @@ -1581,6 +1581,12 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) set_property(dnode, "compatible", (is_oldworld() ? "heathrow-ata" : "keylargo-ata"), 13);
+ set_property(dnode, "model", ((current_channel == 3) ? + "ata-3" : "ata-4"), strlen("ata-*") + 1); + + set_property(dnode, "AAPL,connector", "ata", + strlen("ata") + 1); + props[0] = 0x00000526; props[1] = 0x00000085; props[2] = 0x00000025; @@ -1589,8 +1595,8 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) props[5] = 0x00000000; props[6] = 0x00000000; props[7] = 0x00000000; - OLDWORLD(set_property(dnode, "AAPL,pio-timing", - (char *)&props, 8*sizeof(props[0]))); + set_property(dnode, "AAPL,pio-timing", + (char *)&props, 8*sizeof(props[0]));
/* The first interrupt entry is the ide interrupt, the second the dbdma interrupt */ @@ -1634,8 +1640,8 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) (char *)&props, 2*sizeof(props[0])));
props[0] = 0; - OLDWORLD(set_property(dnode, "AAPL,bus-id", (char*)props, - 1 * sizeof(props[0]))); + set_property(dnode, "AAPL,bus-id", (char*)props, + 1 * sizeof(props[0])); IDE_DPRINTF(DEV_NAME": [io ports 0x%lx]\n", current_channel, chan->mmio);
@@ -1683,3 +1689,23 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) return 0; } #endif /* CONFIG_DRIVER_MACIO */ + +void ide_exit(void); +void ide_exit(void) +{ + struct ide_channel *current; + + current = channels; + while (current) { + struct ide_drive *drive; + int i; + + for (i = 0; i < 2; i++) { + drive = ¤t->drives[i]; + ob_ide_select_drive(drive); + ob_ide_software_reset(drive); + ob_ide_device_type_check(drive); + } + current = current->next; + } +}
On 21.08.15 00:45, Cormac O'Brien wrote:
Mac OS 9 is finicky about devices having the correct properties, so we change some IDE data to more closely model a real New World Mac.
Please split this into 2 separate patches.
We also add ide_exit() to ciface_quiesce() to properly prepare the drives for transferring control to the operating system.
Signed-off-by: Cormac O'Brien cormac@c-obrien.org
arch/ppc/qemu/methods.c | 2 ++ drivers/ide.c | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/arch/ppc/qemu/methods.c b/arch/ppc/qemu/methods.c index cb72bc1..adf3ea2 100644 --- a/arch/ppc/qemu/methods.c +++ b/arch/ppc/qemu/methods.c @@ -109,11 +109,13 @@ NODE_METHODS( tty ) = {
DECLARE_NODE( ciface, 0, 0, "+/openprom/client-services" );
+extern void ide_exit(void); /* ( -- ) */ static void ciface_quiesce( unsigned long args[], unsigned long ret[] ) { usb_exit();
- ide_exit();
#if 0 unsigned long msr; /* This seems to be the correct thing to do - but I'm not sure */ diff --git a/drivers/ide.c b/drivers/ide.c index 327c64a..8ef34cb 100644 --- a/drivers/ide.c +++ b/drivers/ide.c @@ -1581,6 +1581,12 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) set_property(dnode, "compatible", (is_oldworld() ? "heathrow-ata" : "keylargo-ata"), 13);
set_property(dnode, "model", ((current_channel == 3) ?
"ata-3" : "ata-4"), strlen("ata-*") + 1);
As Segher mentioned somewhere else, please introduce a helper for string properties.
set_property(dnode, "AAPL,connector", "ata",
strlen("ata") + 1);
- props[0] = 0x00000526; props[1] = 0x00000085; props[2] = 0x00000025;
@@ -1589,8 +1595,8 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) props[5] = 0x00000000; props[6] = 0x00000000; props[7] = 0x00000000;
OLDWORLD(set_property(dnode, "AAPL,pio-timing",
(char *)&props, 8*sizeof(props[0])));
set_property(dnode, "AAPL,pio-timing",
(char *)&props, 8*sizeof(props[0]));
/* The first interrupt entry is the ide interrupt, the second the dbdma interrupt */
@@ -1634,8 +1640,8 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) (char *)&props, 2*sizeof(props[0])));
props[0] = 0;
OLDWORLD(set_property(dnode, "AAPL,bus-id", (char*)props,
1 * sizeof(props[0])));
set_property(dnode, "AAPL,bus-id", (char*)props,
IDE_DPRINTF(DEV_NAME": [io ports 0x%lx]\n", current_channel, chan->mmio);1 * sizeof(props[0]));
@@ -1683,3 +1689,23 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) return 0; } #endif /* CONFIG_DRIVER_MACIO */
+void ide_exit(void);
This should go into a header file.
Alex
+void ide_exit(void) +{
- struct ide_channel *current;
- current = channels;
- while (current) {
struct ide_drive *drive;
int i;
for (i = 0; i < 2; i++) {
drive = ¤t->drives[i];
ob_ide_select_drive(drive);
ob_ide_software_reset(drive);
ob_ide_device_type_check(drive);
}
current = current->next;
- }
+}
On 20/08/15 23:45, Cormac O'Brien wrote:
This patchset significantly improves OpenBIOS's ability to boot OS 9, adding ESCC legacy ports, reworking ADB interaction with QEMU and fixing IDE controller interaction with the OS.
Note that the patch to interpreter.fs is a temporary measure pending a fix that Mark is writing, and that RTAS must be disabled for the OS to boot.
Signed-off-by: Cormac O'Brien cormac@c-obrien.org
Changes from v1:
- Initialize ESCC legacy ports
- Don't enable non-implemented DBDMA interrupts
- Update some commit messages and comments
Cormac O'Brien (5): interpreter.fs: allow evaluate to split words on CR as well as LF ppc: fix up power-mgt properties ppc: add ESCC legacy support ppc: fix CUDA ADB packet header format ppc: fix up IDE device tree and control transfer
arch/ppc/qemu/methods.c | 2 + drivers/cuda.c | 17 ++++++-- drivers/escc.c | 88 ++++++++++++++++++++++++++++++++++-------- drivers/escc.h | 2 + drivers/ide.c | 34 ++++++++++++++-- drivers/pci.c | 6 +++ forth/bootstrap/interpreter.fs | 2 +- 7 files changed, 126 insertions(+), 25 deletions(-)
While I need to revisit the interpreter.fs patch, I'm happy with the rest of this patchset and it doesn't appear to introduce any regressions in my tests here so:
Reviewed-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk
ATB,
Mark.