diff options
| author | bhgv <bhgv.empire@gmail.com> | 2020-05-12 12:50:49 +0300 |
|---|---|---|
| committer | bhgv <bhgv.empire@gmail.com> | 2020-05-12 12:50:49 +0300 |
| commit | 9b5d5f8a4640dbecdc87e5b6e7e95f71018632cf (patch) | |
| tree | d3135c3861ef93ed2523642d3c5f64c7819b7def /emu | |
| parent | 73c13e732072c17f3e584e11a51d1f7dc8d88e32 (diff) | |
| parent | 31b4edc67b75658ce5e2d41f2fc87331f4b26d49 (diff) | |
Merge branch 'master' of https://github.com/bhgv/Inferno-OS-bhgv
Diffstat (limited to 'emu')
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(¤t->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, ¶m); + param.sched_priority = sched_get_priority_min(policy); + pthread_setschedparam(self, policy, ¶m); +#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; +} |
