aboutsummaryrefslogtreecommitdiff
path: root/emu/FreeRTOS
diff options
context:
space:
mode:
Diffstat (limited to 'emu/FreeRTOS')
-rw-r--r--emu/FreeRTOS/arm-tas-v5.S18
-rw-r--r--emu/FreeRTOS/arm-tas-v7.S30
-rw-r--r--emu/FreeRTOS/asm-386.S51
-rw-r--r--emu/FreeRTOS/asm-arm.S62
-rw-r--r--emu/FreeRTOS/asm-mips.S28
-rw-r--r--emu/FreeRTOS/asm-power.S61
-rw-r--r--emu/FreeRTOS/asm-spim.S29
-rw-r--r--emu/FreeRTOS/audio-oss.c441
-rw-r--r--emu/FreeRTOS/cmd.c213
-rw-r--r--emu/FreeRTOS/deveia.c44
-rw-r--r--emu/FreeRTOS/devfs-posix.c1096
-rw-r--r--emu/FreeRTOS/devfs.c26
-rw-r--r--emu/FreeRTOS/emu112
-rw-r--r--emu/FreeRTOS/emu-g101
-rw-r--r--emu/FreeRTOS/emu-wrt111
-rw-r--r--emu/FreeRTOS/kproc-xthreads.c264
-rw-r--r--emu/FreeRTOS/mk-wrt8
-rw-r--r--emu/FreeRTOS/mkfile62
-rw-r--r--emu/FreeRTOS/mkfile-3860
-rw-r--r--emu/FreeRTOS/mkfile-arm2
-rw-r--r--emu/FreeRTOS/mkfile-mips0
-rw-r--r--emu/FreeRTOS/mkfile-power0
-rw-r--r--emu/FreeRTOS/mkfile-riscv640
-rw-r--r--emu/FreeRTOS/os.c332
-rw-r--r--emu/FreeRTOS/segflush-386.c11
-rw-r--r--emu/FreeRTOS/segflush-arm.c14
-rw-r--r--emu/FreeRTOS/segflush-mips.S16
-rw-r--r--emu/FreeRTOS/segflush-power.c34
-rw-r--r--emu/FreeRTOS/segflush-riscv64.c11
-rw-r--r--emu/FreeRTOS/segflush-spim.S16
-rw-r--r--emu/FreeRTOS/win-fb.c1881
31 files changed, 5074 insertions, 0 deletions
diff --git a/emu/FreeRTOS/arm-tas-v5.S b/emu/FreeRTOS/arm-tas-v5.S
new file mode 100644
index 0000000..4bd13c2
--- /dev/null
+++ b/emu/FreeRTOS/arm-tas-v5.S
@@ -0,0 +1,18 @@
+
+ .file "arm-tas-v5.S"
+/*
+ * ulong _tas(ulong*);
+ */
+ .align 2
+ .global _tas
+ .type _tas, %function
+_tas:
+ @ args = 0, pretend = 0, frame = 0
+ @ frame_needed = 0, uses_anonymous_args = 0
+ @ link register save eliminated.
+ @ lr needed for prologue
+ mov r3, #1
+ mov r1, r0
+ swp r0, r3, [r1]
+ bx lr
+ .size _tas, .-_tas
diff --git a/emu/FreeRTOS/arm-tas-v7.S b/emu/FreeRTOS/arm-tas-v7.S
new file mode 100644
index 0000000..584b31f
--- /dev/null
+++ b/emu/FreeRTOS/arm-tas-v7.S
@@ -0,0 +1,30 @@
+ .file "arm-tas-v7.S"
+#ifndef ARMv7
+#define DMB mcr p15, 0, r0, c7, c10, 5
+#else
+#define DMB dmb
+#endif
+.align 2
+.global _tas
+.type _tas, %function
+_tas:
+ @ args = 0, pretend = 0, frame = 0
+ @ frame_needed = 0, uses_anonymous_args = 0
+ @ link register save eliminated.
+ @ lr needed for prologue
+ DMB
+ mov r1, r0
+ mov r2, #0xaa
+tas1:
+ ldrex r0, [r1]
+ cmp r0, #0
+ bne lockbusy
+ strex r3, r2, [r1]
+ cmp r3, #0
+ bne tas1
+ DMB
+ bx lr
+lockbusy:
+ clrex
+ bx lr
+ .size _tas, .-_tas
diff --git a/emu/FreeRTOS/asm-386.S b/emu/FreeRTOS/asm-386.S
new file mode 100644
index 0000000..fe5ce6c
--- /dev/null
+++ b/emu/FreeRTOS/asm-386.S
@@ -0,0 +1,51 @@
+ .file "asm-Linux-386.S"
+ .text
+
+/*
+ * umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .type umult,@function
+ .global umult
+umult:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+
+ movl 8(%ebp), %eax
+ movl 12(%ebp), %ebx
+ mull %ebx
+ movl 16(%ebp), %ebx
+ movl %edx, (%ebx)
+
+ popl %ebx
+ popl %ebp
+ ret
+
+ .type FPsave,@function
+ .global FPsave
+FPsave:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fstenv (%eax)
+ popl %ebp
+ ret
+
+ .type FPrestore,@function
+ .global FPrestore
+FPrestore:
+ pushl %ebp
+ movl %esp, %ebp
+ movl 8(%ebp), %eax
+ fldenv (%eax)
+ popl %ebp
+ ret
+
+ .type _tas,@function
+ .globl _tas
+_tas:
+ movl $1, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
diff --git a/emu/FreeRTOS/asm-arm.S b/emu/FreeRTOS/asm-arm.S
new file mode 100644
index 0000000..5446dde
--- /dev/null
+++ b/emu/FreeRTOS/asm-arm.S
@@ -0,0 +1,62 @@
+ .file "asm-Linux-arm.S"
+ .text
+
+/*
+ * ulong umult(ulong m1, ulong m2, ulong *hi)
+ */
+
+ .align 2
+ .global umult
+ .type umult, %function
+umult:
+ @ args = 0, pretend = 0, frame = 12
+ @ frame_needed = 1, uses_anonymous_args = 0
+ mov ip, sp
+ stmfd sp!, {fp, ip, lr, pc}
+ sub fp, ip, #4
+ sub sp, sp, #12
+ str r0, [fp, #-16]
+ str r1, [fp, #-20]
+ str r2, [fp, #-24]
+ ldr r1, [fp, #-16]
+ ldr r2, [fp, #-20]
+ umull r0, r3, r1, r2
+ ldr r1, [fp, #-24]
+ str r3, [r1]
+ ldmea fp, {fp, sp, pc}
+ .size umult, .-umult
+
+/*
+ * void FPsave(void*);
+ */
+
+ .align 2
+ .global FPsave
+ .type FPsave, %function
+FPsave:
+ @ args = 0, pretend = 0, frame = 4
+ @ frame_needed = 1, uses_anonymous_args = 0
+ mov ip, sp
+ stmfd sp!, {fp, ip, lr, pc}
+ sub fp, ip, #4
+ sub sp, sp, #4
+ str r0, [fp, #-16]
+ ldmea fp, {fp, sp, pc}
+ .size FPsave, .-FPsave
+
+/*
+ * void FPrestore(void*);
+ */
+ .align 2
+ .global FPrestore
+ .type FPrestore, %function
+FPrestore:
+ @ args = 0, pretend = 0, frame = 4
+ @ frame_needed = 1, uses_anonymous_args = 0
+ mov ip, sp
+ stmfd sp!, {fp, ip, lr, pc}
+ sub fp, ip, #4
+ sub sp, sp, #4
+ str r0, [fp, #-16]
+ ldmea fp, {fp, sp, pc}
+ .size FPrestore, .-FPrestore
diff --git a/emu/FreeRTOS/asm-mips.S b/emu/FreeRTOS/asm-mips.S
new file mode 100644
index 0000000..d2780f6
--- /dev/null
+++ b/emu/FreeRTOS/asm-mips.S
@@ -0,0 +1,28 @@
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+LEAF(FPsave)
+ cfc1 t0, $31
+ sw t0, 0(a0) /* a0 is argument */
+ j $31
+ END(FPsave)
+
+LEAF(FPrestore)
+ lw t0, 0(a0) /* a0 is argument */
+ ctc1 t0, $31
+ j $31
+ END(FPrestore)
+
+LEAF(_tas)
+ .set noreorder
+1:
+ ll v0,0(a0) /* a0 is argument */
+ or t1, v0, 1
+ sc t1,0(a0)
+ beq t1,zero,1b
+ nop
+ j $31 /* lock held */
+ nop
+ .set reorder
+ END(_tas)
diff --git a/emu/FreeRTOS/asm-power.S b/emu/FreeRTOS/asm-power.S
new file mode 100644
index 0000000..7187f93
--- /dev/null
+++ b/emu/FreeRTOS/asm-power.S
@@ -0,0 +1,61 @@
+ .align 2
+ .global FPsave
+FPsave:
+ stfd %f14,0*8(%r3)
+ stfd %f15,1*8(%r3)
+ stfd %f16,2*8(%r3)
+ stfd %f17,3*8(%r3)
+ stfd %f18,4*8(%r3)
+ stfd %f19,5*8(%r3)
+ stfd %f20,6*8(%r3)
+ stfd %f21,7*8(%r3)
+ stfd %f22,8*8(%r3)
+ stfd %f23,9*8(%r3)
+ stfd %f24,10*8(%r3)
+ stfd %f25,11*8(%r3)
+ stfd %f26,12*8(%r3)
+ stfd %f27,13*8(%r3)
+ stfd %f28,14*8(%r3)
+ stfd %f29,15*8(%r3)
+ stfd %f30,16*8(%r3)
+ stfd %f31,17*8(%r3)
+ blr
+
+ .align 2
+ .global FPrestore
+FPrestore:
+ lfd %f14,0*8(%r3)
+ lfd %f15,1*8(%r3)
+ lfd %f16,2*8(%r3)
+ lfd %f17,3*8(%r3)
+ lfd %f18,4*8(%r3)
+ lfd %f19,5*8(%r3)
+ lfd %f20,6*8(%r3)
+ lfd %f21,7*8(%r3)
+ lfd %f22,8*8(%r3)
+ lfd %f23,9*8(%r3)
+ lfd %f24,10*8(%r3)
+ lfd %f25,11*8(%r3)
+ lfd %f26,12*8(%r3)
+ lfd %f27,13*8(%r3)
+ lfd %f28,14*8(%r3)
+ lfd %f29,15*8(%r3)
+ lfd %f30,16*8(%r3)
+ lfd %f31,17*8(%r3)
+ blr
+
+ .align 2
+ .global _tas
+_tas:
+ sync
+ mr %r4, %r3
+ addi %r5,0,0x1
+1:
+ lwarx %r3, 0, %r4
+ cmpwi %r3, 0
+ bne- 2f
+ stwcx. %r5, 0, %r4
+ bne- 1b
+2:
+ sync
+ blr
diff --git a/emu/FreeRTOS/asm-spim.S b/emu/FreeRTOS/asm-spim.S
new file mode 100644
index 0000000..77ae3d4
--- /dev/null
+++ b/emu/FreeRTOS/asm-spim.S
@@ -0,0 +1,29 @@
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+
+LEAF(FPsave)
+ cfc1 t0, $31
+ sw t0, 0(a0) /* a0 is argument */
+ j $31
+ END(FPsave)
+
+LEAF(FPrestore)
+ lw t0, 0(a0) /* a0 is argument */
+ ctc1 t0, $31
+ j $31
+ END(FPrestore)
+
+LEAF(_tas)
+ .set noreorder
+1:
+ ll v0,0(a0) /* a0 is argument */
+ or t1, v0, 1
+ sc t1,0(a0)
+ beq t1,zero,1b
+ nop
+ j $31 /* lock held */
+ nop
+ .set reorder
+ END(_tas)
diff --git a/emu/FreeRTOS/audio-oss.c b/emu/FreeRTOS/audio-oss.c
new file mode 100644
index 0000000..696e37c
--- /dev/null
+++ b/emu/FreeRTOS/audio-oss.c
@@ -0,0 +1,441 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "audio.h"
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#define Audio_Mic_Val SOUND_MIXER_MIC
+#define Audio_Linein_Val SOUND_MIXER_LINE
+
+#define Audio_Speaker_Val SOUND_MIXER_PCM // SOUND_MIXER_VOLUME
+#define Audio_Headphone_Val SOUND_MIXER_ALTPCM
+#define Audio_Lineout_Val SOUND_MIXER_CD
+
+#define Audio_Pcm_Val AFMT_S16_LE
+#define Audio_Ulaw_Val AFMT_MU_LAW
+#define Audio_Alaw_Val AFMT_A_LAW
+
+#include "audio-tbls.c"
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+#define DEVAUDIO "/dev/dsp"
+#define DEVMIXER "/dev/mixer"
+
+#define DPRINT if(1)print
+
+enum {
+ A_Pause,
+ A_UnPause,
+ A_In,
+ A_Out,
+};
+
+static struct {
+ int data; /* dsp data fd */
+ int ctl; /* mixer fd */
+ int pause;
+ QLock lk;
+} afd = {.data = -1, .ctl = -1, .pause =A_UnPause };
+
+static Audio_t av;
+static QLock inlock;
+static QLock outlock;
+
+static int audio_open(int);
+static int audio_pause(int, int);
+static int audio_set_info(int, Audio_d*, int);
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}
+
+void
+audio_file_init(void)
+{
+ audio_info_init(&av);
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ USED(c);
+ DPRINT("audio_file_open %d %d %x\n", afd.data, afd.ctl, omode);
+ qlock(&afd.lk);
+ if(waserror()){
+ qunlock(&afd.lk);
+ nexterror();
+ }
+ if(afd.data >= 0)
+ error(Einuse);
+ if(afd.ctl < 0){
+ afd.ctl = open(DEVMIXER, ORDWR);
+ if(afd.ctl < 0)
+ oserror();
+ }
+ afd.data = audio_open(omode);
+ if(afd.data < 0)
+ oserror();
+ poperror();
+ qunlock(&afd.lk);
+}
+
+void
+audio_file_close(Chan *c)
+{
+ USED(c);
+ DPRINT("audio_file_close %d %d\n", afd.data, afd.ctl);
+ qlock(&afd.lk);
+ if(waserror()){
+ qunlock(&afd.lk);
+ nexterror();
+ }
+ close(afd.data);
+ afd.data = -1;
+ qunlock(&afd.lk);
+ poperror();
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ long ba, status, chunk, total;
+
+ USED(c);
+ USED(offset);
+ DPRINT("audio_file_read %d %d\n", afd.data, afd.ctl);
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(afd.data < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(!audio_pause(afd.data, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count){
+ chunk = count - total;
+ status = read (afd.data, va + total, chunk);
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ poperror();
+ qunlock(&inlock);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ USED(c);
+ USED(offset);
+ DPRINT("audio_file_write %d %d\n", afd.data, afd.ctl);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(afd.data < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count){
+ chunk = min(bufsz, count - total);
+ status = write(afd.data, va, chunk);
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ return count;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ Audio_t tmpav = av;
+ int tfd;
+
+ USED(c);
+ USED(offset);
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ DPRINT ("audio_ctl_write %X %X\n", afd.data, afd.ctl);
+ if(!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if(!canqlock(&inlock))
+ error("device busy");
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+ if(!canqlock(&outlock))
+ error("device busy");
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ /* DEVAUDIO needs to be open to issue an ioctl */
+ tfd = afd.data;
+ if(tfd < 0){
+ tfd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK);
+ if(tfd < 0)
+ oserror();
+ }
+ if(waserror()){
+ if(tfd != afd.data)
+ close(tfd);
+ nexterror();
+ }
+
+ if(tmpav.in.flags & AUDIO_MOD_FLAG){
+ if(!audio_pause(tfd, A_Pause))
+ error(Ebadarg);
+ if(!audio_set_info(tfd, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+
+ poperror();
+ if(tfd != afd.data)
+ close(tfd);
+
+ tmpav.in.flags = 0;
+ av = tmpav;
+
+ poperror();
+ qunlock(&outlock);
+ poperror();
+ qunlock(&inlock);
+ return count;
+}
+
+/* Linux/OSS specific stuff */
+
+static int
+choosefmt(Audio_d *i)
+{
+ switch(i->bits){
+ case 8:
+ switch(i->enc){
+ case Audio_Alaw_Val:
+ return AFMT_A_LAW;
+ case Audio_Ulaw_Val:
+ return AFMT_MU_LAW;
+ case Audio_Pcm_Val:
+ return AFMT_U8;
+ }
+ break;
+ case 16:
+ if(i->enc == Audio_Pcm_Val)
+ return AFMT_S16_LE;
+ break;
+ }
+ return -1;
+}
+
+static int
+setvolume(int fd, int what, int left, int right)
+{
+ int can, v;
+
+ if(ioctl(fd, SOUND_MIXER_READ_DEVMASK, &can) < 0)
+ can = ~0;
+
+ DPRINT("setvolume fd%d %X can mix 0x%X (mask %X)\n", fd, what, (can & (1<<what)), can);
+ if(!(can & (1<<what)))
+ return 0;
+ v = left | (right<<8);
+ if(ioctl(afd.ctl, MIXER_WRITE(what), &v) < 0)
+ return 0;
+ return 1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status, arg;
+ int oldfmt, newfmt;
+
+ USED(d);
+ DPRINT("audio_set_info (%d) %d %d\n", fd, afd.data, afd.ctl);
+ if(fd < 0)
+ return 0;
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG){
+ arg = i->rate;
+ if(ioctl(fd, SNDCTL_DSP_SPEED, &arg) < 0)
+ return 0;
+ }
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG){
+ arg = i->chan;
+ if(ioctl(fd, SNDCTL_DSP_CHANNELS, &arg) < 0)
+ return 0;
+ }
+
+ /* precision */
+ if(i->flags & AUDIO_BITS_FLAG){
+ arg = i->bits;
+ if(ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &arg) < 0)
+ return 0;
+ }
+
+ /* encoding */
+ if(i->flags & AUDIO_ENC_FLAG){
+ ioctl(fd, SNDCTL_DSP_GETFMTS, &oldfmt);
+
+ newfmt = choosefmt(i);
+ if(newfmt < 0)
+ return 0;
+ if(newfmt != oldfmt){
+ status = ioctl(fd, SNDCTL_DSP_SETFMT, &arg);
+ DPRINT ("enc oldfmt newfmt %x status %d\n", oldfmt, newfmt, status);
+ }
+ }
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG|AUDIO_VOL_FLAG))
+ return setvolume(afd.ctl, i->dev, i->left, i->right);
+
+ return 1;
+}
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+audio_open(int omode)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ switch (omode){
+ case OREAD:
+ fd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK);
+ break;
+ case OWRITE:
+ fd = open(DEVAUDIO, O_WRONLY|O_NONBLOCK);
+ break;
+ case ORDWR:
+ fd = open(DEVAUDIO, O_RDWR|O_NONBLOCK);
+ break;
+ }
+
+ DPRINT("audio_open %d\n", fd);
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)){
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ if(!audio_pause(fd, A_Pause)){
+ close(fd);
+ error(Eio);
+ }
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = ~0;
+
+ if(!audio_set_info(fd, &av.in, A_In)){
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+static int
+dspsync(int fd)
+{
+ return ioctl(fd, SNDCTL_DSP_RESET, NULL) >= 0 &&
+ ioctl(fd, SNDCTL_DSP_SYNC, NULL) >= 0;
+}
+
+static int
+audio_pause(int fd, int f)
+{
+ int status;
+
+// DPRINT ("audio_pause (%d) %d %d\n", fd, afd.data, afd.ctl);
+ if(fd < 0)
+ return 0;
+ if(fd != afd.data)
+ return dspsync(fd);
+ qlock(&afd.lk);
+ if(afd.pause == f){
+ qunlock(&afd.lk);
+ return 1;
+ }
+ if(waserror()){
+ qunlock(&afd.lk);
+ nexterror();
+ }
+ status = dspsync(afd.data);
+ if(status)
+ afd.pause = f;
+ poperror();
+ qunlock(&afd.lk);
+ return status;
+}
diff --git a/emu/FreeRTOS/cmd.c b/emu/FreeRTOS/cmd.c
new file mode 100644
index 0000000..2671796
--- /dev/null
+++ b/emu/FreeRTOS/cmd.c
@@ -0,0 +1,213 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Debug = 0
+};
+
+/*
+ * os-specific devcmd support.
+ * this version should be reasonably portable across Unix systems.
+ */
+typedef struct Targ Targ;
+struct Targ
+{
+ int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
+ char** args;
+ char* dir;
+ int pid;
+ int wfd; /* child writes errors that occur after the fork or on exec */
+ int uid;
+ int gid;
+};
+
+extern int gidnobody;
+extern int uidnobody;
+
+static int
+childproc(Targ *t)
+{
+ int i, nfd;
+
+ if(Debug)
+ print("devcmd: '%s'", t->args[0]);
+
+ nfd = getdtablesize();
+ for(i = 0; i < nfd; i++)
+ if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
+ close(i);
+
+ dup2(t->fd[0], 0);
+ dup2(t->fd[1], 1);
+ dup2(t->fd[2], 2);
+ close(t->fd[0]);
+ close(t->fd[1]);
+ close(t->fd[2]);
+
+ /* should have an auth file to do host-specific authorisation? */
+ if(t->gid != -1){
+ if(setgid(t->gid) < 0 && getegid() == 0){
+ fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->uid != -1){
+ if(setuid(t->uid) < 0 && geteuid() == 0){
+ fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
+ _exit(1);
+ }
+ }
+
+ if(t->dir != nil && chdir(t->dir) < 0){
+ fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
+ _exit(1);
+ }
+
+ signal(SIGPIPE, SIG_DFL);
+
+ execvp(t->args[0], t->args);
+ if(Debug)
+ print("execvp: %s\n",strerror(errno));
+ fprint(t->wfd, "exec failed: %s", strerror(errno));
+
+ _exit(1);
+}
+
+void*
+oscmd(char **args, int nice, char *dir, int *fd)
+{
+ Targ *t;
+ int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
+
+ t = mallocz(sizeof(*t), 1);
+ if(t == nil)
+ return nil;
+
+ fd0[0] = fd0[1] = -1;
+ fd1[0] = fd1[1] = -1;
+ fd2[0] = fd2[1] = -1;
+ wfd[0] = wfd[1] = -1;
+ if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
+ goto Error;
+ if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */
+ goto Error;
+
+ t->fd[0] = fd0[0];
+ t->fd[1] = fd1[1];
+ t->fd[2] = fd2[1];
+ t->wfd = wfd[1];
+ t->args = args;
+ t->dir = dir;
+ t->gid = up->env->gid;
+ if(t->gid == -1)
+ t->gid = gidnobody;
+ t->uid = up->env->uid;
+ if(t->uid == -1)
+ t->uid = uidnobody;
+
+ signal(SIGCHLD, SIG_DFL);
+ switch(pid = fork()) {
+ case -1:
+ goto Error;
+ case 0:
+ setpgrp();
+// if(nice)
+// setpriority(PRIO_PROCESS, 0, 19);
+ childproc(t);
+ _exit(1);
+ default:
+ t->pid = pid;
+ if(Debug)
+ print("cmd pid %d\n", t->pid);
+ break;
+ }
+
+ close(fd0[0]);
+ close(fd1[1]);
+ close(fd2[1]);
+ close(wfd[1]);
+
+ n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
+ close(wfd[0]);
+ if(n > 0){
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd2[0]);
+ free(t);
+ up->genbuf[n] = 0;
+ if(Debug)
+ print("oscmd: bad exec: %q\n", up->genbuf);
+ error(up->genbuf);
+ return nil;
+ }
+
+ fd[0] = fd0[1];
+ fd[1] = fd1[0];
+ fd[2] = fd2[0];
+ return t;
+
+Error:
+ r = errno;
+ if(Debug)
+ print("oscmd: %q\n",strerror(r));
+ close(fd0[0]);
+ close(fd0[1]);
+ close(fd1[0]);
+ close(fd1[1]);
+ close(fd2[0]);
+ close(fd2[1]);
+ close(wfd[0]);
+ close(wfd[1]);
+ error(strerror(r));
+ return nil;
+}
+
+int
+oscmdkill(void *a)
+{
+ Targ *t = a;
+
+ if(Debug)
+ print("kill: %d\n", t->pid);
+ return kill(-t->pid, SIGTERM);
+}
+
+int
+oscmdwait(void *a, char *buf, int n)
+{
+ Targ *t = a;
+ int s;
+
+ if(waitpid(t->pid, &s, 0) == -1){
+ if(Debug)
+ print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(s)){
+ if(WEXITSTATUS(s) == 0)
+ return snprint(buf, n, "%d 0 0 0 ''", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
+ }
+ if(WIFSIGNALED(s)){
+ if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
+ return snprint(buf, n, "%d 0 0 0 killed", t->pid);
+ return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
+ }
+ return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
+}
+
+void
+oscmdfree(void *a)
+{
+ free(a);
+}
diff --git a/emu/FreeRTOS/deveia.c b/emu/FreeRTOS/deveia.c
new file mode 100644
index 0000000..a1f0951
--- /dev/null
+++ b/emu/FreeRTOS/deveia.c
@@ -0,0 +1,44 @@
+/*
+ * Linux serial port definitions
+ */
+
+static char *sysdev[] = {
+ "/dev/ttyS0",
+ "/dev/ttyS1",
+ "/dev/ttyS2",
+ "/dev/ttyS3",
+ "/dev/ttyS4",
+ "/dev/ttyS5",
+ "/dev/ttyS6",
+ "/dev/ttyS7",
+};
+
+#include <sys/ioctl.h>
+#include "deveia-posix.c"
+#include "deveia-bsd.c"
+
+
+static struct tcdef_t bps[] = {
+ {0, B0},
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {57600, B57600},
+ {115200, B115200},
+ {230400, B230400},
+ {460800, B460800},
+ {-1, -1}
+};
+
diff --git a/emu/FreeRTOS/devfs-posix.c b/emu/FreeRTOS/devfs-posix.c
new file mode 100644
index 0000000..1f53ac8
--- /dev/null
+++ b/emu/FreeRTOS/devfs-posix.c
@@ -0,0 +1,1096 @@
+/*
+ * Unix file system interface
+ */
+#define _LARGEFILE64_SOURCE 1
+#define _FILE_OFFSET_BITS 64
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#if 0 //{}
+#include <sys/un.h>
+#endif //{}
+#include <utime.h>
+#include <dirent.h>
+#include <stdio.h>
+#define __EXTENSIONS__
+#undef getwd
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
+typedef struct Fsinfo Fsinfo;
+struct Fsinfo
+{
+ int uid;
+ int gid;
+ int mode; /* Unix mode */
+ DIR* dir; /* open directory */
+ struct dirent* de; /* directory reading */
+ int fd; /* open files */
+ ulong offset; /* offset when reading directory */
+ int eod; /* end of directory */
+ int issocket;
+ QLock oq; /* mutex for offset */
+ char* spec;
+ Cname* name; /* Unix's name for file */
+ Qid rootqid; /* Plan 9's qid for Inferno's root */
+};
+
+#define FS(c) ((Fsinfo*)(c)->aux)
+
+enum
+{
+ IDSHIFT = 8,
+ NID = 1 << IDSHIFT,
+ IDMASK = NID - 1,
+ MAXPATH = 1024 /* TO DO: eliminate this */
+};
+
+typedef struct User User;
+struct User
+{
+ int id; /* might be user or group ID */
+ int gid; /* if it's a user not a group, the group ID (only for setid) */
+ char* name;
+ int nmem;
+ int* mem; /* member array, if nmem != 0 */
+ User* next;
+};
+
+char rootdir[MAXROOT] = ROOT;
+
+static User* uidmap[NID];
+static User* gidmap[NID];
+static QLock idl;
+static User* name2user(User**, char*, User* (*get)(char*));
+static User* id2user(User**, int, User* (*get)(int));
+static User* newuid(int);
+static User* newgid(int);
+static User* newuname(char*);
+static User* newgname(char*);
+
+static Qid fsqid(struct stat *);
+static void fspath(Cname*, char*, char*);
+static int fsdirconv(Chan*, char*, char*, struct stat*, uchar*, int, int);
+static Cname* fswalkpath(Cname*, char*, int);
+static char* fslastelem(Cname*);
+static int ingroup(int id, int gid);
+static void fsperm(Chan*, int);
+static long fsdirread(Chan*, uchar*, int, vlong);
+static int fsomode(int);
+static void fsremove(Chan*);
+static vlong osdisksize(int); /* defined by including file */
+
+/*
+ * make invalid symbolic links visible; less confusing, and at least you can then delete them.
+ */
+static int
+xstat(char *f, struct stat *sb)
+{
+ if(stat(f, sb) >= 0)
+ return 0;
+ /* could possibly generate ->name as rob once suggested */
+ return lstat(f, sb);
+}
+
+static void
+fsfree(Chan *c)
+{
+ cnameclose(FS(c)->name);
+ free(FS(c));
+}
+
+Chan*
+fsattach(char *spec)
+{
+ Chan *c;
+ struct stat st;
+ static int devno;
+ static Lock l;
+
+ if(!emptystr(spec) && strcmp(spec, "*") != 0)
+ error(Ebadspec);
+ if(stat(rootdir, &st) < 0)
+ oserror();
+ if(!S_ISDIR(st.st_mode))
+ error(Enotdir);
+
+ c = devattach('U', spec);
+ c->qid = fsqid(&st);
+ c->aux = smalloc(sizeof(Fsinfo));
+ FS(c)->dir = nil;
+ FS(c)->de = nil;
+ FS(c)->fd = -1;
+ FS(c)->issocket = 0;
+ FS(c)->gid = st.st_gid;
+ FS(c)->uid = st.st_uid;
+ FS(c)->mode = st.st_mode;
+ lock(&l);
+ c->dev = devno++;
+ unlock(&l);
+ if(!emptystr(spec)){
+ FS(c)->spec = "/";
+ FS(c)->name = newcname(FS(c)->spec);
+ }else
+ FS(c)->name = newcname(rootdir);
+ FS(c)->rootqid = c->qid;
+
+ return c;
+}
+
+Walkqid*
+fswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ int j;
+ volatile int alloc;
+ Walkqid *wq;
+ struct stat st;
+ char *n;
+ Cname *next;
+ Cname *volatile current;
+ Qid rootqid;
+
+ if(nname > 0)
+ isdir(c);
+
+ alloc = 0;
+ current = nil;
+ wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ if(waserror()){
+ if(alloc && wq->clone != nil)
+ cclose(wq->clone);
+ cnameclose(current);
+ free(wq);
+ return nil;
+ }
+ if(nc == nil){
+ nc = devclone(c);
+ nc->type = 0;
+ alloc = 1;
+ }
+ wq->clone = nc;
+ rootqid = FS(c)->rootqid;
+ current = FS(c)->name;
+ if(current != nil)
+ incref(&current->r);
+ for(j = 0; j < nname; j++){
+ if(!(nc->qid.type&QTDIR)){
+ if(j==0)
+ error(Enotdir);
+ break;
+ }
+ n = name[j];
+ if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){
+ next = current;
+ incref(&next->r);
+ next = addelem(current, n);
+ //print("** ufs walk '%s' -> %s [%s]\n", current->s, n, next->s);
+ if(xstat(next->s, &st) < 0){
+ cnameclose(next);
+ if(j == 0)
+ error(Enonexist);
+ strcpy(up->env->errstr, Enonexist);
+ break;
+ }
+ nc->qid = fsqid(&st);
+ cnameclose(current);
+ current = next;
+ }
+ wq->qid[wq->nqid++] = nc->qid;
+ }
+ poperror();
+ if(wq->nqid < nname){
+ cnameclose(current);
+ if(alloc)
+ cclose(wq->clone);
+ wq->clone = nil;
+ }else if(wq->clone){
+ nc->aux = smalloc(sizeof(Fsinfo));
+ nc->type = c->type;
+ if(nname > 0) {
+ FS(nc)->gid = st.st_gid;
+ FS(nc)->uid = st.st_uid;
+ FS(nc)->mode = st.st_mode;
+ FS(nc)->issocket = S_ISSOCK(st.st_mode);
+ } else {
+ FS(nc)->gid = FS(c)->gid;
+ FS(nc)->uid = FS(c)->uid;
+ FS(nc)->mode = FS(c)->mode;
+ FS(nc)->issocket = FS(c)->issocket;
+ }
+ FS(nc)->name = current;
+ FS(nc)->spec = FS(c)->spec;
+ FS(nc)->rootqid = rootqid;
+ FS(nc)->fd = -1;
+ FS(nc)->dir = nil;
+ FS(nc)->de = nil;
+ }
+ return wq;
+}
+
+static int
+fsstat(Chan *c, uchar *dp, int n)
+{
+ struct stat st;
+ char *p;
+
+ if(FS(c)->fd >= 0){
+ if(fstat(FS(c)->fd, &st) < 0)
+ oserror();
+ }else{
+ if(xstat(FS(c)->name->s, &st) < 0)
+ oserror();
+ }
+ p = fslastelem(FS(c)->name);
+ if(*p == 0)
+ p = "/";
+ qlock(&idl);
+ n = fsdirconv(c, FS(c)->name->s, p, &st, dp, n, 0);
+ qunlock(&idl);
+ return n;
+}
+
+static int
+opensocket(char *path)
+{
+ int fd;
+ struct sockaddr_un su;
+
+ memset(&su, 0, sizeof su);
+ su.sun_family = AF_UNIX;
+ if(strlen(path)+1 > sizeof su.sun_path)
+ error("unix socket name too long");
+ strcpy(su.sun_path, path);
+ if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ return -1;
+ if(connect(fd, (struct sockaddr*)&su, sizeof su) >= 0)
+ return fd;
+ close(fd);
+ if((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
+ return -1;
+ if(connect(fd, (struct sockaddr*)&su, sizeof su) >= 0)
+ return fd;
+ close(fd);
+ return -1;
+}
+
+static Chan*
+fsopen(Chan *c, int mode)
+{
+ int m, isdir;
+
+ m = mode & (OTRUNC|3);
+ switch(m) {
+ case 0:
+ fsperm(c, 4);
+ break;
+ case 1:
+ case 1|16:
+ fsperm(c, 2);
+ break;
+ case 2:
+ case 0|16:
+ case 2|16:
+ fsperm(c, 4);
+ fsperm(c, 2);
+ break;
+ case 3:
+ fsperm(c, 1);
+ break;
+ default:
+ error(Ebadarg);
+ }
+
+ isdir = c->qid.type & QTDIR;
+
+ if(isdir && mode != OREAD)
+ error(Eperm);
+
+ m = fsomode(m & 3);
+ c->mode = openmode(mode);
+
+ if(isdir) {
+ FS(c)->dir = opendir(FS(c)->name->s);
+ if(FS(c)->dir == nil)
+ oserror();
+ FS(c)->eod = 0;
+ }
+ else {
+ if(!FS(c)->issocket){
+ if(mode & OTRUNC)
+ m |= O_TRUNC;
+ FS(c)->fd = open(FS(c)->name->s, m, 0666);
+ }else
+ FS(c)->fd = opensocket(FS(c)->name->s);
+ if(FS(c)->fd < 0)
+ oserror();
+ }
+
+ c->offset = 0;
+ FS(c)->offset = 0;
+ c->flag |= COPEN;
+ return c;
+}
+
+static void
+fscreate(Chan *c, char *name, int mode, ulong perm)
+{
+ int fd, m, o;
+ struct stat st;
+ Cname *n;
+
+ fsperm(c, 2);
+
+ m = fsomode(mode&3);
+ openmode(mode); /* get the errors out of the way */
+
+ if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+ error(Efilename);
+ n = fswalkpath(FS(c)->name, name, 1);
+ if(waserror()){
+ cnameclose(n);
+ nexterror();
+ }
+ if(perm & DMDIR) {
+ if(m)
+ error(Eperm);
+
+ perm &= ~0777 | (FS(c)->mode & 0777);
+ if(mkdir(n->s, perm) < 0)
+ oserror();
+
+ fd = open(n->s, 0);
+ if(fd < 0)
+ oserror();
+ fchmod(fd, perm);
+ fchown(fd, up->env->uid, FS(c)->gid);
+ if(fstat(fd, &st) <0){
+ close(fd);
+ oserror();
+ }
+ close(fd);
+ FS(c)->dir = opendir(n->s);
+ if(FS(c)->dir == nil)
+ oserror();
+ FS(c)->eod = 0;
+ } else {
+ o = (O_CREAT | O_EXCL) | (mode&3);
+ if(mode & OTRUNC)
+ o |= O_TRUNC;
+ perm &= ~0666 | (FS(c)->mode & 0666);
+ fd = open(n->s, o, perm);
+ if(fd < 0)
+ oserror();
+ fchmod(fd, perm);
+ fchown(fd, up->env->uid, FS(c)->gid);
+ if(fstat(fd, &st) < 0){
+ close(fd);
+ oserror();
+ }
+ FS(c)->fd = fd;
+ }
+ cnameclose(FS(c)->name);
+ FS(c)->name = n;
+ poperror();
+
+ c->qid = fsqid(&st);
+ FS(c)->gid = st.st_gid;
+ FS(c)->uid = st.st_uid;
+ FS(c)->mode = st.st_mode;
+ c->mode = openmode(mode);
+ c->offset = 0;
+ FS(c)->offset = 0;
+ FS(c)->issocket = 0;
+ c->flag |= COPEN;
+}
+
+static void
+fsclose(Chan *c)
+{
+ if((c->flag & COPEN) != 0){
+ if(c->qid.type & QTDIR)
+ closedir(FS(c)->dir);
+ else
+ close(FS(c)->fd);
+ }
+ if(c->flag & CRCLOSE) {
+ if(!waserror()) {
+ fsremove(c);
+ poperror();
+ }
+ return;
+ }
+ fsfree(c);
+}
+
+static long
+fsread(Chan *c, void *va, long n, vlong offset)
+{
+ long r;
+
+ if(c->qid.type & QTDIR){
+ qlock(&FS(c)->oq);
+ if(waserror()) {
+ qunlock(&FS(c)->oq);
+ nexterror();
+ }
+ r = fsdirread(c, va, n, offset);
+ poperror();
+ qunlock(&FS(c)->oq);
+ }else{
+ if(!FS(c)->issocket){
+ r = pread(FS(c)->fd, va, n, offset);
+ if(r >= 0)
+ return r;
+ if(errno != ESPIPE && errno != EPIPE)
+ oserror();
+ }
+ r = read(FS(c)->fd, va, n);
+ if(r < 0)
+ oserror();
+ }
+ return r;
+}
+
+static long
+fswrite(Chan *c, void *va, long n, vlong offset)
+{
+ long r;
+
+ if(!FS(c)->issocket){
+ r = pwrite(FS(c)->fd, va, n, offset);
+ if(r >= 0)
+ return r;
+ if(errno != ESPIPE && errno != EPIPE)
+ oserror();
+ }
+ r = write(FS(c)->fd, va, n);
+ if(r < 0)
+ oserror();
+ return r;
+}
+
+static void
+fswchk(Cname *c)
+{
+ struct stat st;
+
+ if(stat(c->s, &st) < 0)
+ oserror();
+
+ if(st.st_uid == up->env->uid)
+ st.st_mode >>= 6;
+ else if(st.st_gid == up->env->gid || ingroup(up->env->uid, st.st_gid))
+ st.st_mode >>= 3;
+
+ if(st.st_mode & S_IWOTH)
+ return;
+
+ error(Eperm);
+}
+
+static void
+fsremove(Chan *c)
+{
+ int n;
+ Cname *volatile dir;
+
+ if(waserror()){
+ fsfree(c);
+ nexterror();
+ }
+ dir = fswalkpath(FS(c)->name, "..", 1);
+ if(waserror()){
+ cnameclose(dir);
+ nexterror();
+ }
+ fswchk(dir);
+ cnameclose(dir);
+ poperror();
+ if(c->qid.type & QTDIR)
+ n = rmdir(FS(c)->name->s);
+ else
+ n = remove(FS(c)->name->s);
+ if(n < 0)
+ oserror();
+ poperror();
+ fsfree(c);
+}
+
+static int
+fswstat(Chan *c, uchar *buf, int nb)
+{
+ Dir *d;
+ User *p;
+ Cname *volatile ph;
+ struct stat st;
+ struct utimbuf utbuf;
+ int tsync;
+
+ if(FS(c)->fd >= 0){
+ if(fstat(FS(c)->fd, &st) < 0)
+ oserror();
+ }else{
+ if(stat(FS(c)->name->s, &st) < 0)
+ oserror();
+ }
+ d = malloc(sizeof(*d)+nb);
+ if(d == nil)
+ error(Enomem);
+ if(waserror()){
+ free(d);
+ nexterror();
+ }
+ tsync = 1;
+ nb = convM2D(buf, nb, d, (char*)&d[1]);
+ if(nb == 0)
+ error(Eshortstat);
+ if(!emptystr(d->name) && strcmp(d->name, fslastelem(FS(c)->name)) != 0) {
+ tsync = 0;
+ validname(d->name, 0);
+ ph = fswalkpath(FS(c)->name, "..", 1);
+ if(waserror()){
+ cnameclose(ph);
+ nexterror();
+ }
+ fswchk(ph);
+ ph = fswalkpath(ph, d->name, 0);
+ if(rename(FS(c)->name->s, ph->s) < 0)
+ oserror();
+ cnameclose(FS(c)->name);
+ poperror();
+ FS(c)->name = ph;
+ }
+
+ if(d->mode != ~0 && (d->mode&0777) != (st.st_mode&0777)) {
+ tsync = 0;
+ if(up->env->uid != st.st_uid)
+ error(Eowner);
+ if(FS(c)->fd >= 0){
+ if(fchmod(FS(c)->fd, d->mode&0777) < 0)
+ oserror();
+ }else{
+ if(chmod(FS(c)->name->s, d->mode&0777) < 0)
+ oserror();
+ }
+ FS(c)->mode &= ~0777;
+ FS(c)->mode |= d->mode&0777;
+ }
+
+ if(d->atime != ~0 && d->atime != st.st_atime ||
+ d->mtime != ~0 && d->mtime != st.st_mtime) {
+ tsync = 0;
+ if(up->env->uid != st.st_uid)
+ error(Eowner);
+ if(d->mtime != ~0)
+ utbuf.modtime = d->mtime;
+ else
+ utbuf.modtime = st.st_mtime;
+ if(d->atime != ~0)
+ utbuf.actime = d->atime;
+ else
+ utbuf.actime = st.st_atime;
+ if(utime(FS(c)->name->s, &utbuf) < 0) /* TO DO: futimes isn't portable */
+ oserror();
+ }
+
+ if(*d->gid){
+ tsync = 0;
+ qlock(&idl);
+ if(waserror()){
+ qunlock(&idl);
+ nexterror();
+ }
+ p = name2user(gidmap, d->gid, newgname);
+ if(p == 0)
+ error(Eunknown);
+ if(p->id != st.st_gid) {
+ if(up->env->uid != st.st_uid)
+ error(Eowner);
+ if(FS(c)->fd >= 0){
+ if(fchown(FS(c)->fd, st.st_uid, p->id) < 0)
+ oserror();
+ }else{
+ if(chown(FS(c)->name->s, st.st_uid, p->id) < 0)
+ oserror();
+ }
+ FS(c)->gid = p->id;
+ }
+ poperror();
+ qunlock(&idl);
+ }
+
+ if(d->length != ~(uvlong)0){
+ tsync = 0;
+ if(FS(c)->fd >= 0){
+ fsperm(c, 2);
+ if(ftruncate(FS(c)->fd, d->length) < 0)
+ oserror();
+ }else{
+ fswchk(FS(c)->name);
+ if(truncate(FS(c)->name->s, d->length) < 0)
+ oserror();
+ }
+ }
+
+ poperror();
+ free(d);
+ if(tsync && FS(c)->fd >= 0 && fsync(FS(c)->fd) < 0)
+ oserror();
+ return nb;
+}
+
+static Qid
+fsqid(struct stat *st)
+{
+ Qid q;
+ u16int dev;
+
+ q.type = QTFILE;
+ if(S_ISDIR(st->st_mode))
+ q.type = QTDIR;
+
+ dev = (u16int)st->st_dev;
+ if(dev & 0x8000){
+ static int aware = 1;
+ if(aware==0){
+ aware = 1;
+ fprint(2, "fs: fsqid: top-bit dev: %#4.4ux\n", dev);
+ }
+ dev ^= 0x8080;
+ }
+
+ q.path = (uvlong)dev<<48;
+ q.path ^= st->st_ino;
+ q.vers = st->st_mtime;
+
+ return q;
+}
+
+static void
+fspath(Cname *c, char *name, char *path)
+{
+ int n;
+
+ if(c->len+strlen(name) >= MAXPATH)
+ panic("fspath: name too long");
+ memmove(path, c->s, c->len);
+ n = c->len;
+ if(path[n-1] != '/')
+ path[n++] = '/';
+ strcpy(path+n, name);
+ if(isdotdot(name))
+ cleanname(path);
+/*print("->%s\n", path);*/
+}
+
+static Cname *
+fswalkpath(Cname *c, char *name, int dup)
+{
+ if(dup)
+ c = newcname(c->s);
+ c = addelem(c, name);
+ if(isdotdot(name))
+ cleancname(c);
+ return c;
+}
+
+static char *
+fslastelem(Cname *c)
+{
+ char *p;
+
+ p = c->s + c->len;
+ while(p > c->s && p[-1] != '/')
+ p--;
+ return p;
+}
+
+static void
+fsperm(Chan *c, int mask)
+{
+ int m;
+
+ m = FS(c)->mode;
+/*
+ print("fsperm: %o %o uuid %d ugid %d cuid %d cgid %d\n",
+ m, mask, up->env->uid, up->env->gid, FS(c)->uid, FS(c)->gid);
+*/
+ if(FS(c)->uid == up->env->uid)
+ m >>= 6;
+ else if(FS(c)->gid == up->env->gid || ingroup(up->env->uid, FS(c)->gid))
+ m >>= 3;
+
+ m &= mask;
+ if(m == 0)
+ error(Eperm);
+}
+
+static int
+isdots(char *name)
+{
+ return name[0] == '.' && (name[1] == '\0' || name[1] == '.' && name[2] == '\0');
+}
+
+static int
+fsdirconv(Chan *c, char *path, char *name, struct stat *s, uchar *va, int nb, int indir)
+{
+ Dir d;
+ char uidbuf[NUMSIZE], gidbuf[NUMSIZE];
+ User *u;
+ int fd;
+
+ memset(&d, 0, sizeof(d));
+ d.name = name;
+ u = id2user(uidmap, s->st_uid, newuid);
+ if(u == nil){
+ snprint(uidbuf, sizeof(uidbuf), "#%lud", (long)s->st_uid);
+ d.uid = uidbuf;
+ }else
+ d.uid = u->name;
+ u = id2user(gidmap, s->st_gid, newgid);
+ if(u == nil){
+ snprint(gidbuf, sizeof(gidbuf), "#%lud", (long)s->st_gid);
+ d.gid = gidbuf;
+ }else
+ d.gid = u->name;
+ d.muid = "";
+ d.qid = fsqid(s);
+ d.mode = (d.qid.type<<24)|(s->st_mode&0777);
+ d.atime = s->st_atime;
+ d.mtime = s->st_mtime;
+ d.length = s->st_size;
+ if(d.mode&DMDIR)
+ d.length = 0;
+ else if(S_ISBLK(s->st_mode) && s->st_size == 0){
+ fd = open(path, O_RDONLY);
+ if(fd >= 0){
+ d.length = osdisksize(fd);
+ close(fd);
+ }
+ }
+ d.type = 'U';
+ d.dev = c->dev;
+ if(indir && sizeD2M(&d) > nb)
+ return -1; /* directory reader needs to know it didn't fit */
+ return convD2M(&d, va, nb);
+}
+
+static long
+fsdirread(Chan *c, uchar *va, int count, vlong offset)
+{
+ int i;
+ long n, r;
+ struct stat st;
+ char path[MAXPATH], *ep;
+ struct dirent *de;
+ static uchar slop[8192];
+
+ i = 0;
+ fspath(FS(c)->name, "", path);
+ ep = path+strlen(path);
+ if(FS(c)->offset != offset) {
+ seekdir(FS(c)->dir, 0);
+ FS(c)->de = nil;
+ FS(c)->eod = 0;
+ for(n=0; n<offset; ) {
+ de = readdir(FS(c)->dir);
+ if(de == 0) {
+ /* EOF, so stash offset and return 0 */
+ FS(c)->offset = n;
+ FS(c)->eod = 1;
+ return 0;
+ }
+ if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
+ continue;
+ strecpy(ep, path+sizeof(path), de->d_name);
+ if(xstat(path, &st) < 0) {
+ fprint(2, "dir: bad path %s\n", path);
+ continue;
+ }
+ qlock(&idl);
+ if(waserror()){
+ qunlock(&idl);
+ nexterror();
+ }
+ r = fsdirconv(c, path, de->d_name, &st, slop, sizeof(slop), 1);
+ poperror();
+ qunlock(&idl);
+ if(r <= 0) {
+ FS(c)->offset = n;
+ return 0;
+ }
+ n += r;
+ }
+ FS(c)->offset = offset;
+ }
+
+ if(FS(c)->eod)
+ return 0;
+
+ /*
+ * Take idl on behalf of id2name. Stalling attach, which is a
+ * rare operation, until the readdir completes is probably
+ * preferable to adding lock round-trips.
+ */
+ qlock(&idl);
+ while(i < count){
+ de = FS(c)->de;
+ FS(c)->de = nil;
+ if(de == nil)
+ de = readdir(FS(c)->dir);
+ if(de == nil){
+ FS(c)->eod = 1;
+ break;
+ }
+
+ if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
+ continue;
+
+ strecpy(ep, path+sizeof(path), de->d_name);
+ if(xstat(path, &st) < 0) {
+ fprint(2, "dir: bad path %s\n", path);
+ continue;
+ }
+ r = fsdirconv(c, path, de->d_name, &st, va+i, count-i, 1);
+ if(r <= 0){
+ FS(c)->de = de;
+ break;
+ }
+ i += r;
+ FS(c)->offset += r;
+ }
+ qunlock(&idl);
+ return i;
+}
+
+static int
+fsomode(int m)
+{
+ if(m < 0 || m > 3)
+ error(Ebadarg);
+ return m == 3? 0: m;
+}
+
+void
+setid(char *name, int owner)
+{
+ User *u;
+
+ if(owner && !iseve())
+ return;
+ kstrdup(&up->env->user, name);
+
+ qlock(&idl);
+ u = name2user(uidmap, name, newuname);
+ if(u == nil){
+ qunlock(&idl);
+ up->env->uid = -1;
+ up->env->gid = -1;
+ return;
+ }
+
+ up->env->uid = u->id;
+ up->env->gid = u->gid;
+ qunlock(&idl);
+}
+
+static User**
+hashuser(User** tab, int id)
+{
+ int i;
+
+ i = (id>>IDSHIFT) ^ id;
+ return &tab[i & IDMASK];
+}
+
+/*
+ * the caller of the following functions must hold QLock idl.
+ */
+
+/*
+ * we could keep separate maps of user and group names to Users to
+ * speed this up, but the reverse lookup currently isn't common (ie, change group by wstat and setid)
+ */
+static User*
+name2user(User **tab, char *name, User* (*get)(char*))
+{
+ int i;
+ User *u, **h;
+ static User *prevu;
+ static User **prevtab;
+
+ if(prevu != nil && prevtab == tab && strcmp(name, prevu->name) == 0)
+ return prevu; /* it's often the one we've just seen */
+
+ for(i=0; i<NID; i++)
+ for(u = tab[i]; u != nil; u = u->next)
+ if(strcmp(name, u->name) == 0) {
+ prevtab = tab;
+ prevu = u;
+ return u;
+ }
+
+ u = get(name);
+ if(u == nil)
+ return nil;
+ h = hashuser(tab, u->id);
+ u->next = *h;
+ *h = u;
+ prevtab = tab;
+ prevu = u;
+ return u;
+}
+
+static void
+freeuser(User *u)
+{
+ if(u != nil){
+ free(u->name);
+ free(u->mem);
+ free(u);
+ }
+}
+
+static User*
+newuser(int id, int gid, char *name, int nmem)
+{
+ User *u;
+
+ u = malloc(sizeof(*u));
+ if(u == nil)
+ return nil;
+ u->name = strdup(name);
+ if(u->name == nil){
+ free(u);
+ return nil;
+ }
+ u->nmem = nmem;
+ if(nmem){
+ u->mem = malloc(nmem*sizeof(*u->mem));
+ if(u->mem == nil){
+ free(u->name);
+ free(u);
+ return nil;
+ }
+ }else
+ u->mem = nil;
+ u->id = id;
+ u->gid = gid;
+ u->next = nil;
+ return u;
+}
+
+static User*
+newuname(char *name)
+{
+ struct passwd *p;
+
+ p = getpwnam(name);
+ if(p == nil)
+ return nil;
+ return newuser(p->pw_uid, p->pw_gid, name, 0);
+}
+
+static User*
+newuid(int id)
+{
+ struct passwd *p;
+
+ p = getpwuid(id);
+ if(p == nil)
+ return nil;
+ return newuser(p->pw_uid, p->pw_gid, p->pw_name, 0);
+}
+
+static User*
+newgroup(struct group *g)
+{
+ User *u, *gm;
+ int n, o;
+
+ if(g == nil)
+ return nil;
+ for(n=0; g->gr_mem[n] != nil; n++)
+ ;
+ u = newuser(g->gr_gid, g->gr_gid, g->gr_name, n);
+ if(u == nil)
+ return nil;
+ o = 0;
+ for(n=0; g->gr_mem[n] != nil; n++){
+ gm = name2user(uidmap, g->gr_mem[n], newuname);
+ if(gm != nil)
+ u->mem[o++] = gm->id;
+ /* ignore names that don't map to IDs */
+ }
+ u->nmem = o;
+ return u;
+}
+
+static User*
+newgid(int id)
+{
+ return newgroup(getgrgid(id));
+}
+
+static User*
+newgname(char *name)
+{
+ return newgroup(getgrnam(name));
+}
+
+static User*
+id2user(User **tab, int id, User* (*get)(int))
+{
+ User *u, **h;
+
+ h = hashuser(tab, id);
+ for(u = *h; u != nil; u = u->next)
+ if(u->id == id)
+ return u;
+ u = get(id);
+ if(u == nil)
+ return nil;
+ u->next = *h;
+ *h = u;
+ return u;
+}
+
+static int
+ingroup(int id, int gid)
+{
+ int i;
+ User *g;
+
+ g = id2user(gidmap, gid, newgid);
+ if(g == nil || g->mem == nil)
+ return 0;
+ for(i = 0; i < g->nmem; i++)
+ if(g->mem[i] == id)
+ return 1;
+ return 0;
+}
+
+Dev fsdevtab = {
+ 'U',
+ "fs",
+
+ devinit,
+ fsattach,
+ fswalk,
+ fsstat,
+ fsopen,
+ fscreate,
+ fsclose,
+ fsread,
+ devbread,
+ fswrite,
+ devbwrite,
+ fsremove,
+ fswstat
+};
diff --git a/emu/FreeRTOS/devfs.c b/emu/FreeRTOS/devfs.c
new file mode 100644
index 0000000..d697d7b
--- /dev/null
+++ b/emu/FreeRTOS/devfs.c
@@ -0,0 +1,26 @@
+#include "devfs-posix.c"
+
+#include <linux/hdreg.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+
+static vlong
+osdisksize(int fd)
+{
+ uvlong u64;
+ long l;
+ struct hd_geometry geo;
+
+ memset(&geo, 0, sizeof geo);
+ l = 0;
+ u64 = 0;
+#ifdef BLKGETSIZE64
+ if(ioctl(fd, BLKGETSIZE64, &u64) >= 0)
+ return u64;
+#endif
+ if(ioctl(fd, BLKGETSIZE, &l) >= 0)
+ return l*512;
+ if(ioctl(fd, HDIO_GETGEO, &geo) >= 0)
+ return (vlong)geo.heads*geo.sectors*geo.cylinders*512;
+ return 0;
+}
diff --git a/emu/FreeRTOS/emu b/emu/FreeRTOS/emu
new file mode 100644
index 0000000..3931b81
--- /dev/null
+++ b/emu/FreeRTOS/emu
@@ -0,0 +1,112 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+# draw win-$WINDOW_BACKEND
+# pointer
+# snarf
+
+# gui
+
+# ip ipif6-posix ipaux
+# ip ipif-posix ipaux
+ eia
+# audio audio-oss
+ mem
+
+lib
+ interp
+# tk
+# freetype
+ math
+# draw
+
+# memlayer
+# memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+# lodepng
+
+link
+
+mod
+ sys
+# draw
+
+# tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+# freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ print
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /gui /
+ /n /
+# /dis
+# /n
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
diff --git a/emu/FreeRTOS/emu-g b/emu/FreeRTOS/emu-g
new file mode 100644
index 0000000..8df0b4c
--- /dev/null
+++ b/emu/FreeRTOS/emu-g
@@ -0,0 +1,101 @@
+env
+ X11LIBS=
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+ ip ipif6-posix ipaux
+ eia
+# audio audio
+ mem
+
+lib
+ interp
+ math
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+
+port
+ alloc
+ cache
+ chan
+ dev
+ devtab
+
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ print
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ void setpointer(int x, int y){USED(x); USED(y);}
+ ulong strtochan(char *s){USED(s); return ~0;}
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
diff --git a/emu/FreeRTOS/emu-wrt b/emu/FreeRTOS/emu-wrt
new file mode 100644
index 0000000..0eac3af
--- /dev/null
+++ b/emu/FreeRTOS/emu-wrt
@@ -0,0 +1,111 @@
+dev
+ root
+ cons
+ env
+ mnt
+ pipe
+ prog
+ prof
+ srv
+ dup
+ ssl
+ cap
+ fs
+ cmd cmd
+ indir
+
+# draw
+# pointer
+# snarf
+
+ ip ipif6-posix ipaux
+# eia
+# audio audio
+ mem
+
+lib
+ interp
+# tk
+# freetype
+ math
+# draw
+
+# memlayer
+# memdraw
+ keyring
+ sec
+ mp
+
+ 9
+
+link
+
+mod
+ sys
+# draw
+
+# tk
+ math
+ srv srv
+ keyring
+ crypt
+ ipints
+ loader
+# freetype
+
+port
+ alloc
+ cache
+ chan
+ dev
+ dial
+ dis
+ discall
+ env
+ error
+ errstr
+ exception
+ exportfs
+ inferno
+ latin1
+ main
+ parse
+ pgrp
+ print
+ proc
+ qio
+ random
+ sysfile
+ uqid
+
+code
+ int dontcompile = 1;
+ unsigned long strtochan(char *s) {return 0;}
+
+init
+ emuinit
+
+root
+ /dev /
+ /fd /
+ /prog /
+ /prof /
+ /net /
+ /net.alt /
+ /chan /
+ /nvfs /
+ /env /
+# /chan
+# /dev
+# /dis
+# /env
+# /n
+# /net
+# /nvfs /
+# /prog
+# /icons
+# /osinit.dis
+# /dis/emuinit.dis
+# /dis/lib/auth.dis
+# /dis/lib/ssl.dis
+# /n/local /
diff --git a/emu/FreeRTOS/kproc-xthreads.c b/emu/FreeRTOS/kproc-xthreads.c
new file mode 100644
index 0000000..ade5e35
--- /dev/null
+++ b/emu/FreeRTOS/kproc-xthreads.c
@@ -0,0 +1,264 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#undef _POSIX_C_SOURCE
+#undef getwd
+
+#include <unistd.h>
+#include <signal.h>
+#include <pthread.h>
+#include <limits.h>
+#include <errno.h>
+//{}#include <semaphore.h>
+
+
+#include <FreeRTOS.h>
+
+#include <semphr.h>
+
+typedef SemaphoreHandle_t sem_t;
+
+typedef struct Osdep Osdep;
+struct Osdep {
+ sem_t sem;
+ pthread_t self;
+};
+
+static pthread_key_t prdakey;
+
+extern int dflag;
+
+Proc*
+getup(void)
+{
+ return pvGetLuaState();
+// return pthread_getspecific(prdakey);
+}
+
+void
+pexit(char *msg, int t)
+{
+ Osenv *e;
+ Proc *p;
+ Osdep *os;
+
+ USED(t);
+
+ lock(&procs.l);
+ p = up;
+ if(p->prev)
+ p->prev->next = p->next;
+ else
+ procs.head = p->next;
+
+ if(p->next)
+ p->next->prev = p->prev;
+ else
+ procs.tail = p->prev;
+ unlock(&procs.l);
+
+ if(0)
+ print("pexit: %s: %s\n", p->text, msg);
+
+ e = p->env;
+ if(e != nil) {
+ closefgrp(e->fgrp);
+ closepgrp(e->pgrp);
+ closeegrp(e->egrp);
+ closesigs(e->sigs);
+ free(e->user);
+ }
+ free(p->prog);
+ os = p->os;
+ if(os != nil){
+// sem_destroy(&os->sem);
+ vSemaphoreDelete(os->sem);
+ os->sem = NULL;
+ free(os);
+ }
+ free(p);
+ pthread_exit(0);
+}
+
+static void*
+tramp(void *arg)
+{
+ Proc *p;
+ Osdep *os;
+
+ p = arg;
+ os = p->os;
+ os->self = pthread_self();
+
+ uxSetLuaState(arg);
+// if(pthread_setspecific(prdakey, arg))
+// panic("set specific data failed in tramp\n");
+#if 0 //{}
+ if(0){
+ pthread_attr_t attr;
+ memset(&attr, 0, sizeof(attr));
+ pthread_getattr_np(pthread_self(), &attr);
+ size_t s;
+ pthread_attr_getstacksize(&attr, &s);
+ print("stack size = %d\n", s);
+ }
+#endif //{}
+ p->func(p->arg);
+ pexit("{Tramp}", 0);
+ return nil;
+}
+
+void
+kproc(char *name, void (*func)(void*), void *arg, int flags)
+{
+ pthread_t thread;
+ Proc *p;
+ Pgrp *pg;
+ Fgrp *fg;
+ Egrp *eg;
+ pthread_attr_t attr;
+ Osdep *os;
+
+ p = newproc();
+ if(p == nil)
+ panic("kproc: no memory");
+
+ os = malloc(sizeof(*os));
+ if(os == nil)
+ panic("kproc: no memory");
+ os->self = 0; /* set by tramp */
+// sem_init(&os->sem, 0, 0);
+ os->sem = xSemaphoreCreateMutex();
+// os->sem = xSemaphoreCreateCounting(1024, 0);
+ p->os = os;
+
+ if(flags & KPDUPPG) {
+ pg = up->env->pgrp;
+ incref(&pg->r);
+ p->env->pgrp = pg;
+ }
+ if(flags & KPDUPFDG) {
+ fg = up->env->fgrp;
+ incref(&fg->r);
+ p->env->fgrp = fg;
+ }
+ if(flags & KPDUPENVG) {
+ eg = up->env->egrp;
+ incref(&eg->r);
+ p->env->egrp = eg;
+ }
+
+ p->env->uid = up->env->uid;
+ p->env->gid = up->env->gid;
+ kstrdup(&p->env->user, up->env->user);
+
+ strcpy(p->text, name);
+
+ p->func = func;
+ p->arg = arg;
+
+ lock(&procs.l);
+ if(procs.tail != nil) {
+ p->prev = procs.tail;
+ procs.tail->next = p;
+ } else {
+ procs.head = p;
+ p->prev = nil;
+ }
+ procs.tail = p;
+ unlock(&procs.l);
+
+ memset(&attr, 0, sizeof(attr));
+ if(pthread_attr_init(&attr) == -1)
+ panic("pthread_attr_init failed");
+ if(flags & KPX11)
+ pthread_attr_setstacksize(&attr, 512*1024); /* could be a parameter */
+ else if(KSTACK > 0)
+ pthread_attr_setstacksize(&attr, (KSTACK < /*PTHREAD_STACK_MIN*/configMINIMAL_STACK_SIZE ? /*PTHREAD_STACK_MIN*/configMINIMAL_STACK_SIZE : KSTACK)+1024);
+#ifndef ANDROID
+// pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
+#endif
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if(pthread_create(&thread, &attr, tramp, p))
+ panic("thr_create failed\n");
+ pthread_attr_destroy(&attr);
+}
+
+/* called to wake up kproc blocked on a syscall */
+void
+oshostintr(Proc *p)
+{
+ Osdep *os;
+
+ os = p->os;
+ if(os != nil && os->self != 0)
+ pthread_kill(os->self, SIGUSR1);
+}
+
+void
+osblock(void)
+{
+ Osdep *os;
+
+ os = up->os;
+// while(sem_wait(&os->sem))
+ while(xSemaphoreTake(os->sem, ( TickType_t )512) != pdTRUE)
+ {} /* retry on signals (which shouldn't happen) */
+}
+
+void
+osready(Proc *p)
+{
+ Osdep *os;
+
+ os = p->os;
+// sem_post(&os->sem);
+ xSemaphoreGive(os->sem);
+}
+
+void
+kprocinit(Proc *p)
+{
+ if(pthread_key_create(&prdakey, NULL))
+ panic("key_create failed");
+ if(pthread_setspecific(prdakey, p))
+ panic("set specific thread data failed");
+}
+
+#ifdef ANDROID
+#include <sched.h>
+#endif
+void
+osyield(void)
+{
+// pthread_yield_np();
+ /* define pthread_yield to be sched_yield or pthread_yield_np if required */
+#ifndef ANDROID
+ pthread_yield();
+#else
+ sched_yield();
+#endif
+}
+
+void
+ospause(void)
+{
+ /* main just wants this thread to go away */
+ pthread_exit(0);
+}
+
+void
+oslopri(void)
+{
+#if 0 //{}
+ struct sched_param param;
+ int policy;
+ pthread_t self;
+
+ self = pthread_self();
+ pthread_getschedparam(self, &policy, &param);
+ param.sched_priority = sched_get_priority_min(policy);
+ pthread_setschedparam(self, policy, &param);
+#endif //{}
+}
diff --git a/emu/FreeRTOS/mk-wrt b/emu/FreeRTOS/mk-wrt
new file mode 100644
index 0000000..f301dde
--- /dev/null
+++ b/emu/FreeRTOS/mk-wrt
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+OPENWRT=$HOME/OpenWrt-SDK-Linux-i686-1
+INFERNO=/usr/inferno
+
+PATH=$OPENWRT/staging_dir_mipsel/bin:$INFERNO/Linux/386/bin:$PATH
+
+mk OBJTYPE=spim CONF=emu-wrt CONFLIST=emu-wrt SYSLIBS=-lm WIN= $*
diff --git a/emu/FreeRTOS/mkfile b/emu/FreeRTOS/mkfile
new file mode 100644
index 0000000..88a2620
--- /dev/null
+++ b/emu/FreeRTOS/mkfile
@@ -0,0 +1,62 @@
+SYSTARG=FreeRTOS
+<../../mkconfig
+SYSTARG=FreeRTOS
+
+#Configurable parameters
+
+CONF=emu #default configuration
+CONFLIST=emu
+CLEANCONFLIST=
+
+INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed
+
+#end configurable parameters
+
+#X11LIBS= -lX11 -lXext # can remove or override using env section in config files
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS
+<mkfile-$OBJTYPE # sets $ARCHFILES
+
+OBJ=\
+ $ARCHFILES\
+ os.$O\
+ kproc-xthreads.$O\
+ segflush-$OBJTYPE.$O\
+ $CONF.root.$O\
+ lock.$O\
+ $DEVS\
+ $PORT\
+
+# asm-$OBJTYPE.$O\
+
+LIBNAMES=${LIBS:%=lib%.a}
+#libs=${LIBS:%=$ROOT/$OBJDIR/lib/lib%.a}
+
+HFILES=\
+
+KERNDATE=1234567 #`{$NDATE}
+CFLAGS='-DROOT="'$ROOT'"' -DEMU \
+ -DTOUCHSCREEN_CAPACITIVE=$TOUCHSCREEN_CAPACITIVE \
+ -I. \
+ -I../port \
+ -I$ROOT/$SYSTARG/$OBJTYPE/include \
+ -I$ROOT/include \
+ -I$ROOT/libinterp \
+ $CTHREADFLAGS $CFLAGS $EMUOPTIONS
+#SYSLIBS= $X11LIBS -lm -lpthread
+SYSLIBS= -lm -lpthread
+
+default:V: $O.$CONF
+
+$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS
+
+install:V: $O.$CONF
+ cp $O.$CONF $INSTALLDIR/$CONF
+
+<../port/portmkfile
+
+devfs.$O: devfs-posix.c
diff --git a/emu/FreeRTOS/mkfile-386 b/emu/FreeRTOS/mkfile-386
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emu/FreeRTOS/mkfile-386
diff --git a/emu/FreeRTOS/mkfile-arm b/emu/FreeRTOS/mkfile-arm
new file mode 100644
index 0000000..01cdd7c
--- /dev/null
+++ b/emu/FreeRTOS/mkfile-arm
@@ -0,0 +1,2 @@
+ARCHFILES=\
+ arm-tas-v7.$O\
diff --git a/emu/FreeRTOS/mkfile-mips b/emu/FreeRTOS/mkfile-mips
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emu/FreeRTOS/mkfile-mips
diff --git a/emu/FreeRTOS/mkfile-power b/emu/FreeRTOS/mkfile-power
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emu/FreeRTOS/mkfile-power
diff --git a/emu/FreeRTOS/mkfile-riscv64 b/emu/FreeRTOS/mkfile-riscv64
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emu/FreeRTOS/mkfile-riscv64
diff --git a/emu/FreeRTOS/os.c b/emu/FreeRTOS/os.c
new file mode 100644
index 0000000..468b232
--- /dev/null
+++ b/emu/FreeRTOS/os.c
@@ -0,0 +1,332 @@
+#include <sys/types.h>
+#include <time.h>
+//#include <termios.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+//#include <sys/time.h>
+#include <machine/time.h>
+
+#include <stdint.h>
+
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+
+
+#include <FreeRTOS.h>
+#include <task.h>
+
+#include <pthread.h>
+#include <semphr.h>
+
+
+
+
+//#include <semaphore.h>
+
+#include <raise.h>
+
+/* glibc 2.3.3-NTPL messes up getpid() by trying to cache the result, so we'll do it ourselves */
+//#include <sys/syscall.h>
+//#define getpid() syscall(SYS_getpid)
+
+enum
+{
+ DELETE = 0x7f,
+ CTRLC = 'C'-'@',
+ NSTACKSPERALLOC = 16,
+ X11STACK= 256*1024
+};
+char *hosttype = "FreeRTOS";
+
+//typedef sem_t Sem;
+
+extern int dflag;
+
+int gidnobody = -1;
+int uidnobody = -1;
+//{} static struct termios tinit;
+
+static void
+sysfsa_flagsault(char *what, void *addr)
+{
+ char buf[64];
+
+ snprint(buf, sizeof(buf), "sys: %s%#p", what, addr);
+ disfault(nil, buf);
+}
+
+#if 0 //{}
+static void
+trapILL(int signo, siginfo_t *si, void *a)
+{
+ char buf[64];
+
+ USED(signo);
+ USED(a);
+// sysfault("illegal instruction pc=", si->si_addr);
+
+ snprint(buf, sizeof(buf), "illegal instruction sig: no=%d, co=%d, va=%d", si->si_signo, si->si_code, si->si_value.sival_int);
+ sysfault(buf, NULL);
+}
+
+static int
+isnilref(siginfo_t *si)
+{
+ return si != 0 ; // && (si->si_addr == (void*)~(uintptr_t)0 || (uintptr_t)si->si_addr < 512);
+}
+
+static void
+trapmemref(int signo, siginfo_t *si, void *a)
+{
+ USED(a); /* ucontext_t*, could fetch pc in machine-dependent way */
+ if(isnilref(si))
+ disfault(nil, exNilref);
+// else if(signo == SIGBUS)
+// sysfault("bad address addr=", si->si_addr); /* eg, misaligned */
+// else
+// sysfault("segmentation violation addr=", si->si_addr);
+}
+
+static void
+trapFPE(int signo, siginfo_t *si, void *a)
+{
+ char buf[64];
+
+ USED(signo);
+ USED(a);
+// snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux pc=%#p", getfsr(), si->si_addr);
+ snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux pc=%#p", getfsr(), si->si_value.sival_ptr);
+ disfault(nil, buf);
+}
+#endif //{}
+
+static void
+trapUSR1(int signo)
+{
+ int intwait;
+
+ USED(signo);
+
+ intwait = up->intwait;
+ up->intwait = 0; /* clear it to let proc continue in osleave */
+
+ if(up->type != Interp) /* Used to unblock pending I/O */
+ return;
+
+ if(intwait == 0) /* Not posted so it's a sync error */
+ disfault(nil, Eintr); /* Should never happen */
+}
+
+void
+oslongjmp(void *regs, osjmpbuf env, int val)
+{
+ USED(regs);
+// siglongjmp(env, val);
+ longjmp(env, val);
+}
+
+static void
+termset(void)
+{
+
+#if 0
+ struct termios t;
+
+ tcgetattr(0, &t);
+ tinit = t;
+ t.c_lflag &= ~(ICANON|ECHO|ISIG);
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &t);
+#endif
+}
+
+static void
+termrestore(void)
+{
+#if 0
+ tcsetattr(0, TCSANOW, &tinit);
+#endif
+}
+
+void
+cleanexit(int x)
+{
+ USED(x);
+
+ if(up->intwait) {
+ up->intwait = 0;
+ return;
+ }
+
+ if(dflag == 0)
+ termrestore();
+
+ kill(0, SIGKILL);
+ exit(0);
+}
+
+void
+osreboot(char *file, char **argv)
+{
+ if(dflag == 0)
+ termrestore();
+ execvp(file, argv);
+ error("reboot failure");
+}
+
+void
+libinit(char *imod)
+{
+ struct sigaction act;
+ struct passwd *pw;
+ Proc *p;
+ char sys[64];
+
+ setsid();
+
+ gethostname(sys, sizeof(sys));
+ kstrdup(&ossysname, sys);
+ pw = getpwnam("nobody");
+ if(pw != nil) {
+ uidnobody = pw->pw_uid;
+ gidnobody = pw->pw_gid;
+ }
+
+ if(dflag == 0)
+ termset();
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = trapUSR1;
+ sigaction(SIGUSR1, &act, nil);
+
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &act, nil);
+
+ /*
+ * For the correct functioning of devcmd in the
+ * face of exiting slaves
+ */
+ signal(SIGPIPE, SIG_IGN);
+ if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanexit);
+ if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanexit);
+
+ if(sflag == 0) {
+/* //{}
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = trapILL;
+ sigaction(SIGILL, &act, nil);
+ act.sa_sigaction = trapFPE;
+ sigaction(SIGFPE, &act, nil);
+ act.sa_sigaction = trapmemref;
+ sigaction(SIGBUS, &act, nil);
+ sigaction(SIGSEGV, &act, nil);
+ act.sa_flags &= ~SA_SIGINFO;
+*/
+ }
+
+ p = newproc();
+ kprocinit(p);
+
+ pw = getpwuid(getuid());
+ if(pw != nil)
+ kstrdup(&eve, pw->pw_name);
+ else
+ print("cannot getpwuid\n");
+
+ p->env->uid = getuid();
+ p->env->gid = getgid();
+
+ emuinit(imod);
+}
+
+int
+readkbd(void)
+{
+ int n;
+ char buf[1];
+
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ print("keyboard close (n=%d, %s)\n", n, strerror(errno));
+ if(n <= 0)
+ pexit("keyboard thread", 0);
+
+ switch(buf[0]) {
+ case '\r':
+ buf[0] = '\n';
+ break;
+ case DELETE:
+ buf[0] = 'H' - '@';
+ break;
+ case CTRLC:
+ cleanexit(0);
+ break;
+ }
+ return buf[0];
+}
+
+/*
+ * Return an abitrary millisecond clock time
+ */
+long
+osmillisec(void)
+{
+ static long sec0 = 0, usec0;
+ struct timeval t;
+
+ if(gettimeofday(&t,(struct timezone*)0)<0)
+ return 0;
+
+ if(sec0 == 0) {
+ sec0 = t.tv_sec;
+ usec0 = t.tv_usec;
+ }
+ return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
+}
+
+/*
+ * Return the time since the epoch in nanoseconds and microseconds
+ * The epoch is defined at 1 Jan 1970
+ */
+vlong
+osnsec(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
+}
+
+vlong
+osusectime(void)
+{
+ struct timeval t;
+
+ gettimeofday(&t, nil);
+ return (vlong)t.tv_sec * 1000000 + t.tv_usec;
+}
+
+int
+osmillisleep(ulong milsec)
+{
+ struct timespec time;
+
+ time.tv_sec = milsec/1000;
+ time.tv_nsec= (milsec%1000)*1000000;
+ nanosleep(&time, NULL);
+ return 0;
+}
+
+int
+limbosleep(ulong milsec)
+{
+ return osmillisleep(milsec);
+}
diff --git a/emu/FreeRTOS/segflush-386.c b/emu/FreeRTOS/segflush-386.c
new file mode 100644
index 0000000..18c940d
--- /dev/null
+++ b/emu/FreeRTOS/segflush-386.c
@@ -0,0 +1,11 @@
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "dat.h"
+
+int
+segflush(void *a, ulong n)
+{
+ USED(a); USED(n);
+ return 0;
+}
diff --git a/emu/FreeRTOS/segflush-arm.c b/emu/FreeRTOS/segflush-arm.c
new file mode 100644
index 0000000..0392977
--- /dev/null
+++ b/emu/FreeRTOS/segflush-arm.c
@@ -0,0 +1,14 @@
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "dat.h"
+
+#define SYS_cacheflush __ARM_NR_cacheflush
+
+int
+segflush(void *a, ulong n)
+{
+ if(n)
+ syscall(SYS_cacheflush, a, (char*)a+n-1, 1);
+ return 0;
+}
diff --git a/emu/FreeRTOS/segflush-mips.S b/emu/FreeRTOS/segflush-mips.S
new file mode 100644
index 0000000..458172f
--- /dev/null
+++ b/emu/FreeRTOS/segflush-mips.S
@@ -0,0 +1,16 @@
+#include "syscall.h"
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+/*
+ * int segflush(void *p, ulong len)
+ */
+
+LEAF(segflush)
+ li a2,BCACHE
+ li v0,SYS_cacheflush
+ syscall
+ li v0,0
+ j $31
+ END(segflush)
diff --git a/emu/FreeRTOS/segflush-power.c b/emu/FreeRTOS/segflush-power.c
new file mode 100644
index 0000000..0804d68
--- /dev/null
+++ b/emu/FreeRTOS/segflush-power.c
@@ -0,0 +1,34 @@
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "dat.h"
+
+
+/*
+ * from geoff collyer's port
+ * invalidate instruction cache and write back data cache from a to a+n-1,
+ * at least.
+ */
+int
+segflush(void *a, ulong n)
+{
+ ulong *p;
+
+ // cache blocks are often eight words (32 bytes) long, sometimes 16 bytes.
+ // need to determine it dynamically?
+ for (p = (ulong *)((ulong)a & ~7UL); (char *)p < (char *)a + n; p++)
+ __asm__("dcbst 0,%0\n\t" // not dcbf, which writes back, then invalidates
+ "icbi 0,%0\n\t"
+ : // no output
+ : "ar" (p)
+ );
+ __asm__("sync\n\t"
+ : // no output
+ :
+ );
+ __asm__("isync\n\t"
+ : // no output
+ :
+ );
+ return 0;
+}
diff --git a/emu/FreeRTOS/segflush-riscv64.c b/emu/FreeRTOS/segflush-riscv64.c
new file mode 100644
index 0000000..bdc3cb9
--- /dev/null
+++ b/emu/FreeRTOS/segflush-riscv64.c
@@ -0,0 +1,11 @@
+#include <sys/types.h>
+//#include <sys/syscall.h>
+
+#include "dat.h"
+
+int
+segflush(void *a, ulong n)
+{
+ USED(a); USED(n);
+ return 0;
+}
diff --git a/emu/FreeRTOS/segflush-spim.S b/emu/FreeRTOS/segflush-spim.S
new file mode 100644
index 0000000..458172f
--- /dev/null
+++ b/emu/FreeRTOS/segflush-spim.S
@@ -0,0 +1,16 @@
+#include "syscall.h"
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <asm/cachectl.h>
+
+/*
+ * int segflush(void *p, ulong len)
+ */
+
+LEAF(segflush)
+ li a2,BCACHE
+ li v0,SYS_cacheflush
+ syscall
+ li v0,0
+ j $31
+ END(segflush)
diff --git a/emu/FreeRTOS/win-fb.c b/emu/FreeRTOS/win-fb.c
new file mode 100644
index 0000000..a05af68
--- /dev/null
+++ b/emu/FreeRTOS/win-fb.c
@@ -0,0 +1,1881 @@
+/*
+ * This implementation of the screen functions for X11 uses the
+ * portable implementation of the Inferno drawing operations (libmemdraw)
+ * to do the work, then has flushmemscreen copy the result to the X11 display.
+ * Thus it potentially supports all colour depths but with a possible
+ * performance penalty (although it tries to use the X11 shared memory extension
+ * to copy the result to the screen, which might reduce the latter).
+ *
+ * CraigN
+ */
+
+#define _GNU_SOURCE 1
+//#define XTHREADS
+#include "dat.h"
+#include "fns.h"
+#undef log2
+#include <draw.h>
+#include "cursor.h"
+#include "keyboard.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <linux/fb.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <dirent.h>
+
+#include <linux/input.h>
+
+//#include "keysym2ucs.h"
+
+#include <sys/ipc.h>
+//#include <sys/shm.h>
+
+#define LOG_TAG "Inferno WIN"
+#define LOGI(...) //printf(__VA_ARGS__)
+#define LOGW(...) //printf(__VA_ARGS__)
+#define LOGE(...) //printf(__VA_ARGS__)
+
+#define DBG(...) printf(__VA_ARGS__)
+
+
+static int displaydepth = 32;
+static ulong displaychan = CHAN4(CAlpha, 8, CRed, 8, CGreen, 8, CBlue, 8);
+
+enum
+{
+ DblTime = 300 /* double click time in msec */
+};
+
+/* screen data .... */
+static uchar* gscreendata = NULL;
+static uchar* xscreendata = NULL;
+
+//typedef unsigned int XColor;
+
+uchar map7to8[128][2];
+
+/* for copy/paste, lifted from plan9ports via drawterm */
+//static Atom clipboard;
+//static Atom utf8string;
+//static Atom targets;
+//static Atom text;
+//static Atom compoundtext;
+
+//static Atom cursorchange;
+
+static int triedscreen = 0;
+
+void xexpose(); //XEvent*);
+static void xmouse(); //XEvent*);
+static void xkeyboard(); ///*XEvent*/void*);
+//static void xsetcursor(/*XEvent*/void*);
+static void xkbdproc(void*);
+//static void xdestroy(XEvent*);
+//static void xselect(XEvent*, XDisplay*);
+static void xproc_expose(void*);
+static void xproc_mouse(void*);
+
+static int xscreendepth;
+
+static int putsnarf, assertsnarf;
+//char *gkscanid = "emu_x11";
+
+
+extern int Xsize;
+extern int Ysize;
+
+static int Xmou_max = 0;
+static int Ymou_max = 0;
+
+static int fd_mou = -1;
+static int is_mt = 0;
+
+
+static char *supported_mouse_devs[] = {
+ "Goodix Capacitive TouchScreen",
+ "TSC2003 Touchscreen",
+ "TSC2007 Touchscreen",
+ NULL
+};
+
+
+/*
+ * The documentation for the XSHM extension implies that if the server
+ * supports XSHM but is not the local machine, the XShm calls will
+ * return False; but this turns out not to be the case. Instead, the
+ * server throws a BadAccess error. So, we need to catch X errors
+ * around all of our XSHM calls, sigh.
+ */
+//static int shm_got_x_error = 0;
+//static XErrorHandler old_handler = 0;
+//static XErrorHandler old_io_handler = 0;
+
+
+
+struct {
+ int fd;
+ uchar* base;
+ uchar* data;
+
+ int width;
+ int height;
+ int bpp;
+ int stride;
+
+ int alloc;
+} fb;
+
+
+
+static uchar*
+attach_fb()
+{
+ struct fb_var_screeninfo fb_var;
+ struct fb_fix_screeninfo fb_fix;
+ int off;
+
+ fb.alloc = 0;
+ fb.fd = -1;
+
+ if ((fb.fd = open ("/dev/fb0", O_RDWR)) < 0)
+ {
+ LOGE ("Error opening /dev/fb0");
+ return NULL;
+ }
+
+ if (ioctl (fb.fd, FBIOGET_VSCREENINFO, &fb_var) == -1)
+ {
+ LOGE ("Error getting variable framebuffer info");
+ return NULL;
+ }
+
+ if (fb_var.bits_per_pixel < 16)
+ {
+ fprintf(stderr,
+ "Error, no support currently for %i bpp frame buffers\n"
+ "Trying to change pixel format...\n",
+ fb_var.bits_per_pixel);
+ return NULL;
+ }
+
+ if (ioctl (fb.fd, FBIOGET_VSCREENINFO, &fb_var) == -1)
+ {
+ LOGE ("Error getting variable framebuffer info (2)");
+ return NULL;
+ }
+
+ /* NB: It looks like the fbdev concept of fixed vs variable screen info is
+ * broken. The line_length is part of the fixed info but it can be changed
+ * if you set a new pixel format. */
+ if (ioctl (fb.fd, FBIOGET_FSCREENINFO, &fb_fix) == -1)
+ {
+ LOGE ("Error getting fixed framebuffer info");
+ return NULL;
+ }
+ /*fb.real_width =*/ Xsize = fb.width = fb_var.xres;
+ /*fb.real_height =*/ Ysize = fb.height = fb_var.yres;
+
+ fb.bpp = fb_var.bits_per_pixel;
+ fb.stride = fb_fix.line_length;
+
+/*
+ fb.type = fb_fix.type;
+ fb.visual = fb_fix.visual;
+
+ fb.red_offset = fb_var.red.offset;
+ fb.red_length = fb_var.red.length;
+ fb.green_offset = fb_var.green.offset;
+ fb.green_length = fb_var.green.length;
+ fb.blue_offset = fb_var.blue.offset;
+ fb.blue_length = fb_var.blue.length;
+
+ if (fb.red_offset == 11 && fb.red_length == 5 &&
+ fb.green_offset == 5 && fb.green_length == 6 &&
+ fb.blue_offset == 0 && fb.blue_length == 5) {
+ fb.rgbmode = RGB565;
+ } else if (fb.red_offset == 0 && fb.red_length == 5 &&
+ fb.green_offset == 5 && fb.green_length == 6 &&
+ fb.blue_offset == 11 && fb.blue_length == 5) {
+ fb.rgbmode = BGR565;
+ } else if (fb.red_offset == 16 && fb.red_length == 8 &&
+ fb.green_offset == 8 && fb.green_length == 8 &&
+ fb.blue_offset == 0 && fb.blue_length == 8) {
+ fb.rgbmode = fb.bpp == 32 ? ARGB888 : RGB888;
+ } else if (fb.red_offset == 0 && fb.red_length == 8 &&
+ fb.green_offset == 8 && fb.green_length == 8 &&
+ fb.blue_offset == 8 && fb.blue_length == 8) {
+ fb.rgbmode = fb.bpp == 32 ? ABGR888 : BGR888;
+ } else {
+ fb.rgbmode = GENERIC;
+ }
+*/
+ LOGI("width: %i, height: %i, bpp: %i, stride: %i\n",
+ fb.width, fb.height, fb.bpp, fb.stride);
+
+ size_t size = fb.stride * fb.height;
+
+ fb.base = (uchar*) mmap ((caddr_t) NULL,
+ /*fb_fix.smem_len */
+ size,
+ PROT_READ|PROT_WRITE,
+ MAP_SHARED,
+ fb.fd, 0);
+
+ if (fb.base == (uchar*)-1)
+ {
+ LOGE("Error cannot mmap framebuffer. Using malloc instead.\n");
+ fb.base = (char*)malloc(size);
+ if (!fb.base)
+ {
+ LOGE("Error cannot allocate memory.");
+ return NULL;
+ }
+ fb.alloc = 1;
+ }
+
+ off = (unsigned long) fb_fix.smem_start % (unsigned long) getpagesize();
+
+ fb.data = fb.base + off;
+
+ return fb.data;
+}
+
+
+
+
+
+#define BITS_PER_LONG (sizeof(long) * 8)
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define OFF(x) ((x)%BITS_PER_LONG)
+#define BIT(x) (1UL<<OFF(x))
+#define LONG(x) ((x)/BITS_PER_LONG)
+#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
+
+static int is_event_device(const struct dirent *dir) {
+ return strncmp("event", dir->d_name, 5) == 0;
+}
+
+static int
+scan_mouse()
+{
+ struct dirent **namelist;
+ int i, j, ndev;
+ int fd = -1, fd_cap = -1, fd_res = -1;
+ char fname[64];
+ char *dname = malloc(256);
+
+ unsigned long bits[NBITS(KEY_MAX)];
+ struct input_absinfo abs;
+
+ int touch_res;
+ int touch_cap;
+
+ ndev = scandir("/dev/input", &namelist, is_event_device, versionsort);
+ if (ndev <= 0)
+ return -1;
+DBG("ndev=%d\n", ndev);
+
+ for(i = 0; i < ndev; i++){
+ int is_supported = 0;
+
+ snprintf(fname, sizeof(fname), "/dev/input/%s", namelist[i]->d_name);
+DBG("test file: %s\n", fname);
+
+ fd = open(fname, O_RDWR);
+ if (fd < 0)
+ continue;
+
+ ioctl(fd, EVIOCGNAME(256), dname);
+DBG("test dev: %s\n", dname);
+ for(j = 0; supported_mouse_devs[j] != NULL; j++){
+ if(!strcmp(dname, supported_mouse_devs[j])){
+ is_supported = 1;
+ break;
+ }
+ }
+
+ if(!is_supported){
+ close(fd);
+ continue;
+ }
+DBG("supported: %s\n", fname);
+
+ ioctl(fd, EVIOCGBIT(0, EV_MAX), bits);
+ if (!test_bit(EV_ABS, bits)){
+ close(fd);
+ continue;
+ }
+
+ ioctl(fd, EVIOCGBIT(EV_ABS, KEY_MAX), bits);
+ if(!(
+ test_bit(ABS_MT_POSITION_X, bits) &&
+ test_bit(ABS_MT_POSITION_Y, bits)
+ )
+ ){
+ if(fd_res >= 0)
+ close(fd_res);
+ fd_res = fd;
+DBG("RESISTIVE\n");
+ }else{
+ if(fd_cap >= 0)
+ close(fd_cap);
+ fd_cap = fd;
+DBG("CAPACITIVE\n");
+ }
+ }
+
+ if(fd_cap < 0){
+ ioctl(fd, EVIOCGABS (ABS_X), &abs);
+ Xmou_max = abs.maximum;
+#if 0
+ context->min.x = abs.minimum;
+ context->max.x = abs.maximum;
+#endif
+ ioctl (fd, EVIOCGABS (ABS_Y), &abs);
+ Ymou_max = abs.maximum;
+#if 0
+ context->max.y = abs.maximum;
+ context->have_multitouch = 0;
+ context->min.id = -1;
+ context->max.id = -1;
+#endif
+ is_mt = 0;
+ fd = fd_res;
+ }else{
+ if(fd_res >= 0)
+ close(fd_res);
+
+ ioctl (fd, EVIOCGABS (ABS_MT_POSITION_X), &abs);
+ Xmou_max = abs.maximum;
+ printf(">O X = (%d -- %d)\n", abs.minimum, abs.maximum);
+/*
+ abs.maximum = 800;
+ ioctl (fd, EVIOCSABS (ABS_MT_POSITION_X), &abs);
+ ioctl (fd, EVIOCGABS (ABS_MT_POSITION_X), &abs);
+ printf(">N X = (%d -- %d)\n", abs.minimum, abs.maximum);
+*/
+ // ----
+ ioctl (fd, EVIOCGABS (ABS_MT_POSITION_Y), &abs);
+ Ymou_max = abs.maximum;
+ printf(">O Y = (%d -- %d)\n", abs.minimum, abs.maximum);
+/*
+ abs.maximum = 480;
+ ioctl (fd, EVIOCSABS (ABS_MT_POSITION_Y), &abs);
+ ioctl (fd, EVIOCGABS (ABS_MT_POSITION_Y), &abs);
+ printf(">N Y = (%d -- %d)\n", abs.minimum, abs.maximum);
+*/
+ // ----
+ is_mt = 1;
+ fd = fd_cap;
+ }
+DBG("is_mt=%d\n", is_mt);
+
+ free(dname);
+
+ return fd;
+}
+
+
+
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+ int depth;
+LOGE("attachscreen\n");
+
+ if(triedscreen)
+ return gscreendata;
+
+ // stop cursor blinking
+ int f = open("/dev/tty1", O_WRONLY);
+ if(f >= 0){
+ char s[]="\033[?17;0;0c";
+ write(f, s, strlen(s));
+ close(f);
+ }
+
+ // prepare mouse
+ if(fd_mou == -1){
+ fd_mou = scan_mouse(); //open("/dev/input/event1", O_RDWR);
+ }
+
+ triedscreen = 1;
+
+ //if(gscreendata != xscreendata)
+ kproc("xproc_expose", xproc_expose, NULL/*xmcon*/, 0);
+ kproc("xproc_mouse", xproc_mouse, NULL/*xmcon*/, 0);
+// kproc("xkbdproc", xkbdproc, NULL/*xkbdcon*/, 0/*KPX11*/); /* silly stack size for bloated X11 */
+
+ xscreendata = attach_fb(); //malloc(Xsize * Ysize * 4);
+ xscreendepth = fb.bpp; //32;
+
+ Xsize &= ~0x3; /* ensure multiple of 4 */
+
+ r->min.x = 0;
+ r->min.y = 0;
+ r->max.x = Xsize;
+ r->max.y = Ysize;
+
+ /*
+ * moved xproc from here to end since it could cause an expose event and
+ * hence a flushmemscreen before xscreendata is initialized
+ */
+
+ *chan = displaychan; /* not every channel description will work */
+ *d = chantodepth(displaychan);
+ displaydepth = *d;
+
+ printf("%s:%d displaychan=%X, displaydepth=%d\n", __func__, __LINE__, displaychan, displaydepth);
+
+// gscreendata = xscreendata;
+ gscreendata = malloc(Xsize * (Ysize+1) * (displaydepth >> 3));
+
+ LOGE("attachscreen: gscreendata=%x, (displaydepth>>3)=%d\n", gscreendata, (displaydepth>>3));
+
+ *width = (Xsize/4)*(*d/8);
+ *softscreen = 1;
+
+ return gscreendata;
+}
+
+
+static void
+copy32to32(Rectangle r)
+{
+ int dx, width;
+ uchar *p, *ep, *cp;
+ u32int v, w, *dp, *wp, *edp, *lp;
+
+LOGE("copy32to32");
+ width = Dx(r);
+ dx = Xsize - width;
+ dp = (u32int*)(gscreendata + (r.min.y * Xsize + r.min.x) * 4);
+ wp = (u32int*)(xscreendata + (r.min.y * Xsize + r.min.x) * 4);
+ edp = (u32int*)(gscreendata + (r.max.y * Xsize + r.max.x) * 4);
+ while(dp < edp) {
+ lp = dp + width;
+ while(dp < lp){
+ v = *dp++;
+ //w = v
+// infernortox11[ (v >> 16) & 0xff ] << 16
+// | infernogtox11[ (v >> 8) & 0xff ] << 8
+// | infernobtox11[ (v >> 0) & 0xff ] << 0
+ //;
+ *wp++ = v; //w;
+ }
+ dp += dx;
+ wp += dx;
+ }
+}
+
+static void
+copy8to32(Rectangle r)
+{
+ int dx, width;
+ uchar *p, *ep, *lp;
+ u32int *wp;
+
+LOGE("copy8to32");
+ width = Dx(r);
+ dx = Xsize - width;
+ p = gscreendata + r.min.y * Xsize + r.min.x;
+ wp = (u32int *)(xscreendata + (r.min.y * Xsize + r.min.x) * 4);
+ ep = gscreendata + r.max.y * Xsize + r.max.x;
+ while(p < ep) {
+ lp = p + width;
+ while(p < lp)
+ *wp++ = *p++; //infernotox11[*p++];
+ p += dx;
+ wp += dx;
+ }
+}
+
+static void
+copy8to24(Rectangle r)
+{
+ int dx, width, v;
+ uchar *p, *cp, *ep, *lp;
+
+LOGE("copy8to24");
+ width = Dx(r);
+ dx = Xsize - width;
+ p = gscreendata + r.min.y * Xsize + r.min.x;
+ cp = xscreendata + (r.min.y * Xsize + r.min.x) * 3;
+ ep = gscreendata + r.max.y * Xsize + r.max.x;
+ while(p < ep) {
+ lp = p + width;
+ while(p < lp){
+ v = *p++; //infernotox11[*p++];
+ cp[0] = (v>>16)&0xff;
+ cp[1] = (v>>8)&0xff;
+ cp[2] = (v>>0)&0xff;
+ cp += 3;
+ }
+ p += dx;
+ cp += 3*dx;
+ }
+}
+
+static void
+copy8to16(Rectangle r)
+{
+ int dx, width;
+ uchar *p, *ep, *lp;
+ u16int *sp;
+
+LOGE("copy8to16");
+ width = Dx(r);
+ dx = Xsize - width;
+ p = gscreendata + r.min.y * Xsize + r.min.x;
+ sp = (unsigned short *)(xscreendata + (r.min.y * Xsize + r.min.x) * 2);
+ ep = gscreendata + r.max.y * Xsize + r.max.x;
+ while(p < ep) {
+ lp = p + width;
+ while(p < lp)
+ *sp++ = *p++; //infernotox11[*p++];
+ p += dx;
+ sp += dx;
+ }
+}
+
+static void
+copy8to8(Rectangle r)
+{
+ int dx, width;
+ uchar *p, *cp, *ep, *lp;
+
+LOGE("copy8to8");
+ width = Dx(r);
+ dx = Xsize - width;
+ p = gscreendata + r.min.y * Xsize + r.min.x;
+ cp = xscreendata + r.min.y * Xsize + r.min.x;
+ ep = gscreendata + r.max.y * Xsize + r.max.x;
+ while(p < ep) {
+ lp = p + width;
+ while(p < lp)
+ *cp++ = *p++; //infernotox11[*p++];
+ p += dx;
+ cp += dx;
+ }
+}
+
+static void
+copy8topixel(Rectangle r)
+{
+ int x, y;
+ uchar *p;
+
+LOGE("copy8topixel");
+ if(xscreendata == NULL)
+ return;
+
+ /* mainly for 4-bit greyscale */
+ for (y = r.min.y; y < r.max.y; y++) {
+ x = r.min.x;
+ p = gscreendata + y * Xsize + x;
+ while (x < r.max.x){
+ xscreendata[y * Xsize * 4 + x] = *p++;
+ x++;
+// XPutPixel(img, x++, y, infernotox11[*p++]);
+ }
+ }
+}
+
+
+//extern void int_refresh(int xb_, int yb_, int xe_, int ye_);
+
+void
+flushmemscreen(Rectangle r)
+{
+ char chanbuf[16];
+
+ // Clip to screen
+ if(r.min.x < 0)
+ r.min.x = 0;
+ if(r.min.y < 0)
+ r.min.y = 0;
+ if(r.max.x >= Xsize)
+ r.max.x = Xsize - 1;
+ if(r.max.y >= Ysize)
+ r.max.y = Ysize - 1;
+
+ if(r.max.x <= r.min.x || r.max.y <= r.min.y)
+ return;
+
+ if(xscreendata == NULL)
+ return;
+
+ LOGE("flushmemscreen: rect=(%d, %d)-(%d, %d), xscreendata=%x, displaydepth=%d, xscreendepth=%d\n",
+ r.min.x, r.min.y, r.max.x, r.max.y, xscreendata, displaydepth, xscreendepth
+ );
+
+#if 1
+ if(gscreendata != xscreendata)
+ switch(displaydepth){
+ case 32:
+ copy32to32(r);
+ break;
+ case 8:
+ switch(xscreendepth){
+ case 24:
+ /* copy8to24(r); */ /* doesn't happen? */
+ /* break */
+ case 32:
+ copy8to32(r);
+ break;
+ case 16:
+ copy8to16(r);
+ break;
+ case 8:
+ copy8to8(r);
+ break;
+ default:
+ copy8topixel(r);
+ break;
+ }
+ break;
+ default:
+ fprint(2, "emu: bad display depth %d chan %s xscreendepth %d\n", displaydepth,
+ chantostr(chanbuf, displaychan), xscreendepth);
+ cleanexit(0);
+ }
+#endif
+
+// int_refresh(r.min.x, r.min.y, r.max.x, r.max.y);
+}
+
+static int
+revbyte(int b)
+{
+ int r;
+
+ r = 0;
+ r |= (b&0x01) << 7;
+ r |= (b&0x02) << 5;
+ r |= (b&0x04) << 3;
+ r |= (b&0x08) << 1;
+ r |= (b&0x10) >> 1;
+ r |= (b&0x20) >> 3;
+ r |= (b&0x40) >> 5;
+ r |= (b&0x80) >> 7;
+ return r;
+}
+
+void
+setpointer(int x, int y)
+{
+/*
+ drawqlock();
+ XLockDisplay(xdisplay);
+ XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, x, y);
+ XFlush(xdisplay);
+ XUnlockDisplay(xdisplay);
+ drawqunlock();
+*/
+}
+
+static void
+xkbdproc(void *arg)
+{
+// XEvent event;
+// XDisplay *xd;
+
+// xd = arg;
+
+ /* BEWARE: the value of up is not defined for this proc on some systems */
+
+// XLockDisplay(xd); /* should be ours alone */
+// XSelectInput(xd, xdrawable, KeyPressMask | KeyReleaseMask);
+ for(;;){
+// XNextEvent(xd, &event);
+ xkeyboard(); //&event);
+// xsetcursor(); //&event);
+ }
+}
+
+static void
+xproc_expose(void *arg)
+{
+// ulong mask;
+// XEvent event;
+// XDisplay *xd;
+
+ closepgrp(up->env->pgrp);
+ closefgrp(up->env->fgrp);
+ closeegrp(up->env->egrp);
+ closesigs(up->env->sigs);
+
+#if 0
+ xd = arg;
+ mask = ButtonPressMask|
+ ButtonReleaseMask|
+ PointerMotionMask|
+ Button1MotionMask|
+ Button2MotionMask|
+ Button3MotionMask|
+ Button4MotionMask|
+ Button5MotionMask|
+ ExposureMask|
+ StructureNotifyMask;
+
+ XLockDisplay(xd); /* should be ours alone */
+ XSelectInput(xd, xdrawable, mask);
+#endif
+ for(;;){
+ osmillisleep(10);
+// XNextEvent(xd, &event);
+// xselect(&event, xd);
+ //xmouse(); //&event);
+ xexpose(); //&event);
+// xdestroy(&event);
+ }
+}
+
+static void
+xproc_mouse(void *arg)
+{
+// ulong mask;
+// XEvent event;
+// XDisplay *xd;
+
+ closepgrp(up->env->pgrp);
+ closefgrp(up->env->fgrp);
+ closeegrp(up->env->egrp);
+ closesigs(up->env->sigs);
+
+#if 0
+ xd = arg;
+ mask = ButtonPressMask|
+ ButtonReleaseMask|
+ PointerMotionMask|
+ Button1MotionMask|
+ Button2MotionMask|
+ Button3MotionMask|
+ Button4MotionMask|
+ Button5MotionMask|
+ ExposureMask|
+ StructureNotifyMask;
+
+ XLockDisplay(xd); /* should be ours alone */
+ XSelectInput(xd, xdrawable, mask);
+#endif
+ for(;;){
+ osmillisleep(100);
+// XNextEvent(xd, &event);
+// xselect(&event, xd);
+ xmouse(); //&event);
+ //xexpose(); //&event);
+// xdestroy(&event);
+ }
+}
+
+/*
+ * this crud is here because X11 can put huge amount of data
+ * on the stack during keyboard translation and cursor changing(!).
+ * we do both in a dedicated process with lots of stack, perhaps even enough.
+ */
+
+enum {
+ CursorSize= 32 /* biggest cursor size */
+};
+
+typedef struct ICursor ICursor;
+struct ICursor {
+ int inuse;
+ int modify;
+ int hotx;
+ int hoty;
+ int w;
+ int h;
+ uchar src[(CursorSize/8)*CursorSize]; /* image and mask bitmaps */
+ uchar mask[(CursorSize/8)*CursorSize];
+};
+static ICursor icursor;
+
+static void
+xcurslock(void)
+{
+ while(_tas(&icursor.inuse) != 0)
+ osyield();
+}
+
+static void
+xcursunlock(void)
+{
+ coherence();
+ icursor.inuse = 0;
+}
+
+#if 0
+static void
+xcursnotify(void)
+{
+ XClientMessageEvent e;
+
+ memset(&e, 0, sizeof e);
+ e.type = ClientMessage;
+ e.window = xdrawable;
+ e.message_type = cursorchange;
+ e.format = 8;
+ XLockDisplay(xdisplay);
+ XSendEvent(xdisplay, xdrawable, True, KeyPressMask, (XEvent*)&e);
+ XFlush(xdisplay);
+ XUnlockDisplay(xdisplay);
+}
+#endif
+
+void
+drawcursor(Drawcursor* c)
+{
+#if 0
+ uchar *bs, *bc, *ps, *pm;
+ int i, j, w, h, bpl;
+
+ if(c->data == nil){
+ drawqlock();
+ if(icursor.h != 0){
+ xcurslock();
+ icursor.h = 0;
+ icursor.modify = 1;
+ xcursunlock();
+ }
+ xcursnotify();
+ drawqunlock();
+ return;
+ }
+
+ drawqlock();
+ xcurslock();
+ icursor.modify = 0; /* xsetcursor will now ignore it */
+ xcursunlock();
+
+ h = (c->maxy-c->miny)/2; /* image, then mask */
+ if(h > CursorSize)
+ h = CursorSize;
+ bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
+ w = bpl;
+ if(w > CursorSize/8)
+ w = CursorSize/8;
+
+ ps = icursor.src;
+ pm = icursor.mask;
+ bc = c->data;
+ bs = c->data + h*bpl;
+ for(i = 0; i < h; i++){
+ for(j = 0; j < bpl && j < w; j++) {
+ *ps++ = revbyte(bs[j]);
+ *pm++ = revbyte(bs[j] | bc[j]);
+ }
+ bs += bpl;
+ bc += bpl;
+ }
+ icursor.h = h;
+ icursor.w = w*8;
+ icursor.hotx = c->hotx;
+ icursor.hoty = c->hoty;
+ icursor.modify = 1;
+ xcursnotify();
+ drawqunlock();
+#endif
+}
+
+#if 0
+static void
+xsetcursor(XEvent *e)
+{
+ ICursor ic;
+ XCursor xc;
+ XColor fg, bg;
+ Pixmap xsrc, xmask;
+ static XCursor xcursor;
+
+ if(e->type != ClientMessage || !e->xclient.send_event || e->xclient.message_type != cursorchange)
+ return;
+
+ xcurslock();
+ if(icursor.modify == 0){
+ xcursunlock();
+ return;
+ }
+ icursor.modify = 0;
+ if(icursor.h == 0){
+ xcursunlock();
+ /* set the default system cursor */
+ if(xcursor != 0) {
+ XFreeCursor(xkbdcon, xcursor);
+ xcursor = 0;
+ }
+ XUndefineCursor(xkbdcon, xdrawable);
+ XFlush(xkbdcon);
+ return;
+ }
+ ic = icursor;
+ xcursunlock();
+
+ xsrc = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.src, ic.w, ic.h);
+ xmask = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.mask, ic.w, ic.h);
+
+ fg = map[0];
+ bg = map[255];
+ fg.pixel = infernotox11[0];
+ bg.pixel = infernotox11[255];
+ xc = XCreatePixmapCursor(xkbdcon, xsrc, xmask, &fg, &bg, -ic.hotx, -ic.hoty);
+ if(xc != 0) {
+ XDefineCursor(xkbdcon, xdrawable, xc);
+ if(xcursor != 0)
+ XFreeCursor(xkbdcon, xcursor);
+ xcursor = xc;
+ }
+ XFreePixmap(xkbdcon, xsrc);
+ XFreePixmap(xkbdcon, xmask);
+ XFlush(xkbdcon);
+}
+#endif
+
+typedef struct Mg Mg;
+struct Mg
+{
+ int code;
+ int bit;
+ int len;
+ ulong mask;
+};
+
+static int
+maskx(Mg* g, int code, ulong mask)
+{
+ int i;
+
+ for(i=0; i<32; i++)
+ if(mask & (1<<i))
+ break;
+ if(i == 32)
+ return 0;
+ g->code = code;
+ g->bit = i;
+ g->mask = mask;
+ for(g->len = 0; i<32 && (mask & (1<<i))!=0; i++)
+ g->len++;
+ return 1;
+}
+
+/*
+ * for a given depth, we need to check the available formats
+ * to find how many actual bits are used per pixel.
+ */
+#if 0
+static int
+xactualdepth(int screenno, int depth)
+{
+ XPixmapFormatValues *pfmt;
+ int i, n;
+
+ pfmt = XListPixmapFormats(xdisplay, &n);
+ for(i=0; i<n; i++)
+ if(pfmt[i].depth == depth)
+ return pfmt[i].bits_per_pixel;
+ return -1;
+}
+
+static int
+xtruevisual(int screenno, int reqdepth, XVisualInfo *vi, ulong *chan)
+{
+ XVisual *xv;
+ Mg r, g, b;
+ int pad, d;
+ ulong c;
+ char buf[30];
+
+ if(XMatchVisualInfo(xdisplay, screenno, reqdepth, TrueColor, vi) ||
+ XMatchVisualInfo(xdisplay, screenno, reqdepth, DirectColor, vi)){
+ xv = vi->visual;
+ if(maskx(&r, CRed, xv->red_mask) &&
+ maskx(&g, CGreen, xv->green_mask) &&
+ maskx(&b, CBlue, xv->blue_mask)){
+ d = xactualdepth(screenno, reqdepth);
+ if(d < 0)
+ return 0;
+ pad = d - (r.len + g.len + b.len);
+ if(0){
+ fprint(2, "r: %8.8lux %d %d\ng: %8.8lux %d %d\nb: %8.8lux %d %d\n",
+ xv->red_mask, r.bit, r.len, xv->green_mask, g.bit, g.len, xv->blue_mask, b.bit, b.len);
+ }
+ if(r.bit > b.bit)
+ c = CHAN3(CRed, r.len, CGreen, g.len, CBlue, b.len);
+ else
+ c = CHAN3(CBlue, b.len, CGreen, g.len, CRed, r.len);
+ if(pad > 0)
+ c |= CHAN1(CIgnore, pad) << 24;
+ *chan = c;
+ xscreendepth = reqdepth;
+ if(0)
+ fprint(2, "chan=%s reqdepth=%d bits=%d\n", chantostr(buf, c), reqdepth, d);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+xmapvisual(int screenno, XVisualInfo *vi, ulong *chan)
+{
+ if(XMatchVisualInfo(xdisplay, screenno, 8, PseudoColor, vi) ||
+ XMatchVisualInfo(xdisplay, screenno, 8, StaticColor, vi)){
+ *chan = CMAP8;
+ xscreendepth = 8;
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+
+/*
+ * Initialize and install the Inferno colormap as a private colormap for this
+ * application. Inferno gets the best colors here when it has the cursor focus.
+ */
+#if 0
+static void
+xdestroy(XEvent *e)
+{
+ XDestroyWindowEvent *xe;
+ if(e->type != DestroyNotify)
+ return;
+ xe = (XDestroyWindowEvent*)e;
+ if(xe->window == xdrawable)
+ cleanexit(0);
+}
+#endif
+
+void
+xexpose() //XEvent *e)
+{
+ static int cntr = 0;
+ Rectangle r;
+// XExposeEvent *xe;
+
+// cntr++;
+// if(cntr == 100000)
+// cntr = 0;
+
+// if(cntr != 1) // e->type != Expose)
+// return;
+// xe = (XExposeEvent*)e;
+ r.min.x = 0; //xe->x;
+ r.min.y = 0; //xe->y;
+ r.max.x = Xsize; //xe->x + xe->width;
+ r.max.y = Ysize; //xe->y + xe->height;
+
+LOGE("xexpose");
+ drawqlock();
+ flushmemscreen(r);
+ drawqunlock();
+}
+
+static void
+xkeyboard() ///*XEvent*/void *e)
+{
+ int ind, md;
+// KeySym k;
+ int k;
+
+// if(gkscanq != nil && (e->type == KeyPress || e->type == KeyRelease)){
+// uchar ch = e->xkey.keycode;
+// if(e->xany.type == KeyRelease)
+// ch |= 0x80;
+// qproduce(gkscanq, &ch, 1);
+// return;
+// }
+
+#if 0
+ /*
+ * I tried using XtGetActionKeysym, but it didn't seem to
+ * do case conversion properly
+ * (at least, with Xterminal servers and R4 intrinsics)
+ */
+ if(e->xany.type != KeyPress)
+ return;
+
+ md = e->xkey.state;
+ ind = 0;
+ if(md & ShiftMask)
+ ind = 1;
+ if(0){
+ k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, ind);
+
+ /* May have to try unshifted version */
+ if(k == NoSymbol && ind == 1)
+ k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, 0);
+ }else
+ XLookupString((XKeyEvent*)e, NULL, 0, &k, NULL);
+
+ if(k == XK_Multi_key || k == NoSymbol)
+ return;
+ if(k&0xFF00){
+ switch(k){
+ case XK_BackSpace:
+ case XK_Tab:
+ case XK_Escape:
+ case XK_Delete:
+ case XK_KP_0:
+ case XK_KP_1:
+ case XK_KP_2:
+ case XK_KP_3:
+ case XK_KP_4:
+ case XK_KP_5:
+ case XK_KP_6:
+ case XK_KP_7:
+ case XK_KP_8:
+ case XK_KP_9:
+ case XK_KP_Divide:
+ case XK_KP_Multiply:
+ case XK_KP_Subtract:
+ case XK_KP_Add:
+ case XK_KP_Decimal:
+ k &= 0x7F;
+ break;
+ case XK_Linefeed:
+ k = '\r';
+ break;
+ case XK_KP_Space:
+ k = ' ';
+ break;
+ case XK_Home:
+ case XK_KP_Home:
+ k = Home;
+ break;
+ case XK_Left:
+ case XK_KP_Left:
+ k = Left;
+ break;
+ case XK_Up:
+ case XK_KP_Up:
+ k = Up;
+ break;
+ case XK_Down:
+ case XK_KP_Down:
+ k = Down;
+ break;
+ case XK_Right:
+ case XK_KP_Right:
+ k = Right;
+ break;
+ case XK_Page_Down:
+ case XK_KP_Page_Down:
+ k = Pgdown;
+ break;
+ case XK_End:
+ case XK_KP_End:
+ k = End;
+ break;
+ case XK_Page_Up:
+ case XK_KP_Page_Up:
+ k = Pgup;
+ break;
+ case XK_Insert:
+ case XK_KP_Insert:
+ k = Ins;
+ break;
+ case XK_KP_Enter:
+ case XK_Return:
+ k = '\n';
+ break;
+ case XK_Alt_L:
+ case XK_Alt_R:
+ k = Latin;
+ break;
+ case XK_Shift_L:
+ case XK_Shift_R:
+ case XK_Control_L:
+ case XK_Control_R:
+ case XK_Caps_Lock:
+ case XK_Shift_Lock:
+
+ case XK_Meta_L:
+ case XK_Meta_R:
+ case XK_Super_L:
+ case XK_Super_R:
+ case XK_Hyper_L:
+ case XK_Hyper_R:
+ return;
+ default: /* not ISO-1 or tty control */
+ if(k>0xff){
+ k = keysym2ucs(k); /* supplied by X */
+ if(k == -1)
+ return;
+ }
+ break;
+ }
+ }
+
+ /* Compensate for servers that call a minus a hyphen */
+ if(k == XK_hyphen)
+ k = XK_minus;
+ /* Do control mapping ourselves if translator doesn't */
+ if(md & ControlMask)
+ k &= 0x9f;
+#endif
+/*
+ if(0){
+ if(k == '\t' && ind)
+ k = BackTab;
+
+ if(md & Mod1Mask)
+ k = APP|(k&0xff);
+ }
+ if(k == NoSymbol)
+ return;
+
+*/
+ gkbdputc(gkbdq, k);
+}
+
+
+
+
+typedef struct {
+ int x;
+ int y;
+ int b;
+} touch_evt;
+
+static touch_evt touch_events[10] = {0};
+static int touch_btns = 0;
+
+static int capacitive_events(int fd, int* b, int* x, int* y)
+{
+ struct input_event ev[64];
+ int i, j, rd;
+ fd_set rdfs;
+ struct timeval to;
+
+ int is_changed = 0;
+
+// static touch_evt touch_event[10] = {0};
+ static int cur_event_n = 0;
+
+ FD_ZERO(&rdfs);
+ FD_SET(fd, &rdfs);
+
+ *b = touch_btns;
+
+ *x = touch_events[cur_event_n].x;
+ *y = touch_events[cur_event_n].y;
+
+ int rv = select(fd + 1, &rdfs, NULL, NULL, &to);
+ if(rv == -1){
+ /* an error accured */
+ return 1;
+ }
+ else if(rv == 0){
+ //printf("timeout\n");
+ /* a timeout occured */
+ //break;
+ return 1;
+ }
+
+ rd = read(fd, ev, sizeof(ev));
+ if (rd < (int) sizeof(struct input_event)) {
+ //printf("expected %d bytes, got %d\n", (int) sizeof(struct input_event), rd);
+ //perror("\nevtest: error reading");
+ return 1;
+ }
+
+ for (i = 0; i < rd / sizeof(struct input_event); i++) {
+ unsigned int type, code;
+ type = ev[i].type;
+ code = ev[i].code;
+
+ if(type == EV_ABS){
+ int v = ev[i].value;
+
+ switch(code){
+ case ABS_MT_SLOT:
+ cur_event_n = v;
+ //if(cur_event_n >= 0 && cur_event_n < 10)
+ // touch_events[cur_event_n].b = 1;
+ break;
+
+ case ABS_MT_TRACKING_ID:
+ if(cur_event_n >= 0 && cur_event_n < 10){
+ touch_events[cur_event_n].b = v;
+
+ is_changed = 1;
+
+ if(v > 0)
+ touch_btns |= 1 << cur_event_n;
+ else
+ touch_btns &= ~(1 << cur_event_n);
+ }
+ //printf("ABS_MT_TRACKING_ID id=%d, btns=%d\n", v, touch_btns);
+ break;
+
+ case ABS_MT_POSITION_X:
+ is_changed = 1;
+ if(cur_event_n >= 0 && cur_event_n < 10)
+ touch_events[cur_event_n].x = v;
+ break;
+
+ case ABS_MT_POSITION_Y:
+ is_changed = 1;
+ if(cur_event_n >= 0 && cur_event_n < 10)
+ touch_events[cur_event_n].y = v;
+ break;
+
+ }
+ }
+ }
+
+ *x = touch_events[0].x;
+ *y = touch_events[0].y;
+
+ *b = touch_btns;
+
+ return !is_changed;
+}
+
+
+/*
+** calibration (perfect = 0 0 4096 4096)
+*/
+//#define NOMINAL_CALIBRATION
+//#ifdef NOMINAL_CALIBRATION
+//#define MIN_X 0
+//#define MIN_Y 0
+//#define MAX_X 4096
+//#define MAX_Y 4096
+//#else
+#define MIN_X 200
+#define MIN_Y 260
+#define MAX_X 3900
+#define MAX_Y 3800
+//#endif
+
+//#define NOT_MUCH 200 /* small dx or dy ; about 3% of full width */
+#define SHORT_CLICK (150) /* 200 ms */
+
+static int tsc2003_events(int fd, int* b, int* x, int* y)
+{
+ struct input_event ev[64];
+ int i, rd;
+ fd_set rdfs;
+ struct timeval to;
+ static int ox=0, oy = 0, ob = 0;
+ int jx, jy, nx = 0, ny = 0;
+ int st = 0;
+
+ to.tv_sec = 0;
+ to.tv_usec = SHORT_CLICK * 1000;
+
+ FD_ZERO(&rdfs);
+ FD_SET(fd, &rdfs);
+
+ *b = 0;
+
+ if(ob == 0){
+ ox = 0;
+ oy = 0;
+ }
+ *x = ox;
+ *y = oy;
+
+ for(jx = jy = 0; jx < 5 || jy < 5; )
+ {
+ int rv = select(fd + 1, &rdfs, NULL, NULL, &to);
+ if(rv == -1){
+ /* an error accured */
+ return 1;
+ }
+ else if(rv == 0){
+ //printf("timeout\n");
+ /* a timeout occured */
+ break;
+ }
+
+ rd = read(fd, ev, sizeof(ev));
+ if (rd < (int) sizeof(struct input_event)) {
+ //printf("expected %d bytes, got %d\n", (int) sizeof(struct input_event), rd);
+ //perror("\nevtest: error reading");
+ return 1;
+ }
+
+ int xt = -1, yt = -1;
+ for (i = 0; i < rd / sizeof(struct input_event); i++) {
+ unsigned int type, code;
+ type = ev[i].type;
+ code = ev[i].code;
+
+ if(type == EV_ABS){
+ int v = ev[i].value;
+
+ if(code == ABS_PRESSURE)
+ *b |= v > 10 ? 1 : 0;
+ else if(code == ABS_X){
+ int t = Xsize * (v - MIN_X) / (MAX_X-MIN_X);
+#if 0
+ if(/* *b && j > 0 &&*/ ox > 0){
+ //int t = nx/j;
+ if(t < ox - 100)
+ t = ox - 100;
+ else
+ if(t > ox + 100)
+ t = ox + 100;
+ }
+#endif
+ if(t >= 0 && t < Xsize){
+ xt = t;
+ jx++;
+ nx += t;
+ // st |= 1;
+ //}else{
+ // j--;
+ // break;
+ }
+ }else if(code == ABS_Y){
+ int t = Ysize * (v - MIN_Y) / (MAX_Y-MIN_Y);
+#if 0
+ if(/* *b && j > 0 &&*/ oy > 0){
+ //int t = ny/j;
+ if(t < oy - 100)
+ t = oy - 100;
+ else
+ if(t > oy + 100)
+ t = oy + 100;
+ }
+#endif
+ if(t >= 0 && t < Ysize){
+ yt = t;
+ jy++;
+ ny += t;
+ // st |=2;
+ //}else{
+ // j--;
+ // break;
+ }
+ }
+ }
+ }
+ //printf("xyt=(%d, %d)\n", xt, yt);
+
+ /*
+ if(xt >= 0 && yt >= 0){
+ nx += xt;
+ ny += yt
+ }
+ */
+// if(st | 3 != 3)
+// j--;
+ }
+
+ //printf("nxy=(%d, %d) jxy=(%d, %d)\n", nx, ny, jx, jy);
+
+ if(jx < 1 || jy < 1){
+ *b = ob = 0;
+ return 0;
+ }
+
+ *x = nx/jx;
+ *y = ny/jy;
+
+ if(*x < 0)
+ *x = 0;
+ if(*y < 0)
+ *y = 0;
+
+ if(*x > Xsize)
+ *x = Xsize;
+ if(*y > Ysize)
+ *y = Ysize;
+
+ ox = *x;
+ oy = *y;
+
+ ob = *b;
+
+ //ioctl(fd, EVIOCGRAB, (void*)0);
+ return 0;
+}
+
+
+static void
+xmouse() //XEvent *e)
+{
+ int s, dbl;
+// XButtonEvent *be;
+// XMotionEvent *me;
+// XEvent motion;
+ int x=0, y=0, b=0;
+ //char buf[64];
+ static ob = 0;
+// static ulong lastb, lastt;
+
+// if(putsnarf != assertsnarf){
+// assertsnarf = putsnarf;
+// XSetSelectionOwner(xmcon, XA_PRIMARY, xdrawable, CurrentTime);
+// if(clipboard != None)
+// XSetSelectionOwner(xmcon, clipboard, xdrawable, CurrentTime);
+// XFlush(xmcon);
+// }
+
+ if(fd_mou >= 0){
+//#if TOUCHSCREEN_CAPACITIVE
+ if(is_mt){
+ if(Xmou_max > 0 && Ymou_max && !capacitive_events(fd_mou, &b, &x, &y)){
+ x = (x * Xsize) / Xmou_max;
+ y = (y * Ysize) / Ymou_max;
+ //printf("capt x=%d, y=%d, b=%x\n", x, y, b);
+ mousetrack(b, x, y, 0);
+ }
+ }else{
+//#else
+ tsc2003_events(fd_mou, &b, &x, &y);
+ if(b > 0 || (b == 0 && ob > 0)){
+ ob = b;
+ //printf("rest x=%d, y=%d, b=%x\n", x, y, b);
+ mousetrack(b, x, y, 0);
+ //printf("ob = %d, b=%d, xy=(%d, %d)\n", ob, b, x, y);
+ }
+ }
+//#endif
+ }
+#if 0
+ dbl = 0;
+ switch(e->type){
+ case ButtonPress:
+ be = (XButtonEvent *)e;
+ /*
+ * Fake message, just sent to make us announce snarf.
+ * Apparently state and button are 16 and 8 bits on
+ * the wire, since they are truncated by the time they
+ * get to us.
+ */
+ if(be->send_event
+ && (~be->state&0xFFFF)==0
+ && (~be->button&0xFF)==0)
+ return;
+ x = be->x;
+ y = be->y;
+ s = be->state;
+ if(be->button == lastb && be->time - lastt < DblTime)
+ dbl = 1;
+ lastb = be->button;
+ lastt = be->time;
+ switch(be->button){
+ case 1:
+ s |= Button1Mask;
+ break;
+ case 2:
+ s |= Button2Mask;
+ break;
+ case 3:
+ s |= Button3Mask;
+ break;
+ case 4:
+ s |= Button4Mask;
+ break;
+ case 5:
+ s |= Button5Mask;
+ break;
+ }
+ break;
+ case ButtonRelease:
+ be = (XButtonEvent *)e;
+ x = be->x;
+ y = be->y;
+ s = be->state;
+ switch(be->button){
+ case 1:
+ s &= ~Button1Mask;
+ break;
+ case 2:
+ s &= ~Button2Mask;
+ break;
+ case 3:
+ s &= ~Button3Mask;
+ break;
+ case 4:
+ s &= ~Button4Mask;
+ break;
+ case 5:
+ s &= ~Button5Mask;
+ break;
+ }
+ break;
+ case MotionNotify:
+ me = (XMotionEvent *) e;
+
+ /* remove excess MotionNotify events from queue and keep last one */
+ while(XCheckTypedWindowEvent(xmcon, xdrawable, MotionNotify, &motion) == True)
+ me = (XMotionEvent *) &motion;
+
+ s = me->state;
+ x = me->x;
+ y = me->y;
+ break;
+ default:
+ return;
+ }
+
+ b = 0;
+ if(s & Button1Mask)
+ b |= 1;
+ if(s & Button2Mask)
+ b |= 2;
+ if(s & Button3Mask)
+ b |= 4;
+ if(s & Button4Mask)
+ b |= 8;
+ if(s & Button5Mask)
+ b |= 16;
+ if(dbl)
+ b |= 1<<8;
+#endif
+
+ //mousetrack(b, x, y, 0);
+}
+
+
+void xmouse_btn(int x, int y, int btn){
+ mousetrack(btn, x, y, 0);
+}
+
+
+//#include "x11-keysym2ucs.c"
+
+/*
+ * Cut and paste. Just couldn't stand to make this simple...
+ */
+
+enum{
+ SnarfSize= 100*1024
+};
+
+typedef struct Clip Clip;
+struct Clip
+{
+ char buf[SnarfSize];
+ QLock lk;
+};
+Clip clip;
+
+#undef long /* sic */
+#undef ulong
+
+#if 0
+static char*
+_xgetsnarf(XDisplay *xd)
+{
+ uchar *data, *xdata;
+ Atom clipboard, type, prop;
+ unsigned long len, lastlen, dummy;
+ int fmt, i;
+ XWindow w;
+
+ qlock(&clip.lk);
+ /*
+ * Have we snarfed recently and the X server hasn't caught up?
+ */
+ if(putsnarf != assertsnarf)
+ goto mine;
+
+ /*
+ * Is there a primary selection (highlighted text in an xterm)?
+ */
+ clipboard = XA_PRIMARY;
+ w = XGetSelectionOwner(xd, XA_PRIMARY);
+ if(w == xdrawable){
+ mine:
+ data = (uchar*)strdup(clip.buf);
+ goto out;
+ }
+
+ /*
+ * If not, is there a clipboard selection?
+ */
+ if(w == None && clipboard != None){
+ clipboard = clipboard;
+ w = XGetSelectionOwner(xd, clipboard);
+ if(w == xdrawable)
+ goto mine;
+ }
+
+ /*
+ * If not, give up.
+ */
+ if(w == None){
+ data = nil;
+ goto out;
+ }
+
+ /*
+ * We should be waiting for SelectionNotify here, but it might never
+ * come, and we have no way to time out. Instead, we will clear
+ * local property #1, request our buddy to fill it in for us, and poll
+ * until he's done or we get tired of waiting.
+ *
+ * We should try to go for utf8string instead of XA_STRING,
+ * but that would add to the polling.
+ */
+ prop = 1;
+ XChangeProperty(xd, xdrawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0);
+ XConvertSelection(xd, clipboard, XA_STRING, prop, xdrawable, CurrentTime);
+ XFlush(xd);
+ lastlen = 0;
+ for(i=0; i<10 || (lastlen!=0 && i<30); i++){
+ osmillisleep(100);
+ XGetWindowProperty(xd, xdrawable, prop, 0, 0, 0, AnyPropertyType,
+ &type, &fmt, &dummy, &len, &data);
+ if(lastlen == len && len > 0)
+ break;
+ lastlen = len;
+ }
+ if(i == 10){
+ data = nil;
+ goto out;
+ }
+ /* get the property */
+ data = nil;
+ XGetWindowProperty(xd, xdrawable, prop, 0, SnarfSize/sizeof(unsigned long), 0,
+ AnyPropertyType, &type, &fmt, &len, &dummy, &xdata);
+ if((type != XA_STRING && type != utf8string) || len == 0){
+ if(xdata)
+ XFree(xdata);
+ data = nil;
+ }else{
+ if(xdata){
+ data = (uchar*)strdup((char*)xdata);
+ XFree(xdata);
+ }else
+ data = nil;
+ }
+out:
+ qunlock(&clip.lk);
+ return (char*)data;
+}
+
+static void
+_xputsnarf(XDisplay *xd, char *data)
+{
+ XButtonEvent e;
+
+ if(strlen(data) >= SnarfSize)
+ return;
+ qlock(&clip.lk);
+ strcpy(clip.buf, data);
+
+ /* leave note for mouse proc to assert selection ownership */
+ putsnarf++;
+
+ /* send mouse a fake event so snarf is announced */
+ memset(&e, 0, sizeof e);
+ e.type = ButtonPress;
+ e.window = xdrawable;
+ e.state = ~0;
+ e.button = ~0;
+ XSendEvent(xd, xdrawable, True, ButtonPressMask, (XEvent*)&e);
+ XFlush(xd);
+ qunlock(&clip.lk);
+}
+
+static void
+xselect(XEvent *e, XDisplay *xd)
+{
+ char *name;
+ XEvent r;
+ XSelectionRequestEvent *xe;
+ Atom a[4];
+
+ if(e->xany.type != SelectionRequest)
+ return;
+
+ memset(&r, 0, sizeof r);
+ xe = (XSelectionRequestEvent*)e;
+if(0) iprint("xselect target=%d requestor=%d property=%d selection=%d\n",
+ xe->target, xe->requestor, xe->property, xe->selection);
+ r.xselection.property = xe->property;
+ if(xe->target == targets){
+ a[0] = XA_STRING;
+ a[1] = utf8string;
+ a[2] = text;
+ a[3] = compoundtext;
+
+ XChangeProperty(xd, xe->requestor, xe->property, xe->target,
+ 8, PropModeReplace, (uchar*)a, sizeof a);
+ }else if(xe->target == XA_STRING || xe->target == utf8string || xe->target == text || xe->target == compoundtext){
+ /* if the target is STRING we're supposed to reply with Latin1 XXX */
+ qlock(&clip.lk);
+ XChangeProperty(xd, xe->requestor, xe->property, xe->target,
+ 8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf));
+ qunlock(&clip.lk);
+ }else{
+ iprint("get %d\n", xe->target);
+ name = XGetAtomName(xd, xe->target);
+ if(name == nil)
+ iprint("XGetAtomName failed\n");
+ else if(strcmp(name, "TIMESTAMP") != 0)
+ iprint("%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target);
+ r.xselection.property = None;
+ }
+
+ r.xselection.display = xe->display;
+ /* r.xselection.property filled above */
+ r.xselection.target = xe->target;
+ r.xselection.type = SelectionNotify;
+ r.xselection.requestor = xe->requestor;
+ r.xselection.time = xe->time;
+ r.xselection.send_event = True;
+ r.xselection.selection = xe->selection;
+ XSendEvent(xd, xe->requestor, False, 0, &r);
+ XFlush(xd);
+}
+#endif
+
+
+char *snarf_buf = nil;
+
+char*
+clipread(void)
+{
+ char *p = NULL;
+
+// if(xsnarfcon == nil)
+// return nil;
+// XLockDisplay(xsnarfcon);
+// p = _xgetsnarf(xsnarfcon);
+// XUnlockDisplay(xsnarfcon);
+
+ p = strdup(snarf_buf);
+
+ return p;
+}
+
+int
+clipwrite(char *buf)
+{
+// buf = NULL;
+
+// if(xsnarfcon == nil)
+// return 0;
+// XLockDisplay(xsnarfcon);
+// _xputsnarf(xsnarfcon, buf);
+// XUnlockDisplay(xsnarfcon);
+ int l = strlen(buf);
+
+ if(snarf_buf)
+ free(snarf_buf);
+ snarf_buf = nil;
+
+ if(l >= SnarfSize)
+ return 0;
+
+ snarf_buf = strdup(buf);
+
+ /* leave note for mouse proc to assert selection ownership */
+ putsnarf++;
+
+ return 0;
+}