summaryrefslogtreecommitdiff
path: root/os/ipaq1110
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
commit74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch)
treec6e220ba61db3a6ea4052e6841296d829654e664 /os/ipaq1110
parent46439007cf417cbd9ac8049bb4122c890097a0fa (diff)
20060303
Diffstat (limited to 'os/ipaq1110')
-rwxr-xr-xos/ipaq1110/Mk8
-rw-r--r--os/ipaq1110/NOTICE6
-rw-r--r--os/ipaq1110/README65
-rw-r--r--os/ipaq1110/archipaq.c376
-rw-r--r--os/ipaq1110/dat.h135
-rw-r--r--os/ipaq1110/defont.c290
-rw-r--r--os/ipaq1110/devaudio.c1056
-rw-r--r--os/ipaq1110/devipaq.c762
-rw-r--r--os/ipaq1110/etherwavelan.c1307
-rw-r--r--os/ipaq1110/fns.h179
-rw-r--r--os/ipaq1110/inflatebin0 -> 20480 bytes
-rw-r--r--os/ipaq1110/io.h60
-rw-r--r--os/ipaq1110/ipaq172
-rw-r--r--os/ipaq1110/lcd.c185
-rw-r--r--os/ipaq1110/main.c255
-rw-r--r--os/ipaq1110/mem.h200
-rw-r--r--os/ipaq1110/mkfile103
-rw-r--r--os/ipaq1110/screen.c917
-rw-r--r--os/ipaq1110/screen.h64
-rw-r--r--os/ipaq1110/tstdraw.b107
-rwxr-xr-xos/ipaq1110/upd4
21 files changed, 6251 insertions, 0 deletions
diff --git a/os/ipaq1110/Mk b/os/ipaq1110/Mk
new file mode 100755
index 00000000..51d69a44
--- /dev/null
+++ b/os/ipaq1110/Mk
@@ -0,0 +1,8 @@
+#!/bin/rc
+rfork ne
+ROOT=/usr/inferno
+fn cd
+NPROC=3
+path=(/usr/inferno/Plan9/$cputype/bin $path)
+#bind /usr/inferno/mkconfig.dist /usr/inferno/mkconfig
+exec mk $*
diff --git a/os/ipaq1110/NOTICE b/os/ipaq1110/NOTICE
new file mode 100644
index 00000000..11f5df58
--- /dev/null
+++ b/os/ipaq1110/NOTICE
@@ -0,0 +1,6 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved.
+iPAQ 36xx Inferno port Copyright © 2000-2003 Vita Nuova Holdings Limited. All rights reserved.
+
+This software is subject to the Vita Nuova Liberal Source Licence, except for the following:
+ devaudio.c came from Plan 9 and is covered by the Lucent Public License 1.02
+ The etherwavelan.c driver was contributed to Plan 9 by nemo@gsyc.escet.urjc.es
diff --git a/os/ipaq1110/README b/os/ipaq1110/README
new file mode 100644
index 00000000..7639e96a
--- /dev/null
+++ b/os/ipaq1110/README
@@ -0,0 +1,65 @@
+Installing 4th Edition Inferno on an IPAQ (21 August 2003)
+
+This Inferno kernel software is only for the iPAQ 36xx,
+colour models only.
+
+Of course, since it's Inferno, the existing
+applications, and yours, will run (give or take kernel bugs).
+Suspend/resume is implemented except for PCMCIA.
+(We are doing the work to make it more general.)
+Otherwise the system tries to conserve power in the usual
+ways by going into idle to wait for interrupts and powering
+devices up and down on open and close.
+
+The following describes loading the Inferno kernel in to the iPAQ.
+This preliminary version has some things hard-wired in to the code
+to run on our wavelan network (see the end of archipaq.c).
+
+os/init/ipaqinit.b will support a file system (currently dossrv for simplicity)
+running over a Flash Translation Layer on the iPAQ flash.
+Setting up a local file system and loading that onto the iPAQ
+is not discussed here. A separate package will deal with that.
+
+For development, we generally take the software over the net from
+an Inferno file service (ie, svc/net) running in emu.
+We are providing this iPAQ kernel package to subscribers earlier,
+for the benefit of those subscribers that can make use of it now
+(eg, modify the networking setup to suit their own network).
+A basic local file system package should be available shortly.
+Until it is, if you are not confident you can set up the networking
+or prepare a local file system, you should wait.
+
+1. You must first prepare the iPAQ with the handhelds.org bootloader,
+version 2.18.54 (later ones probably work but we haven't yet tried them).
+Use the iPAQ H3600 Linux installation instructions:
+ ftp://ftp.handhelds.org/pub/linux/compaq/ipaq/stable/install.html
+
+Following that procedure will eliminate Windows/CE from the device: if you will need it
+in future, be sure to save the flash images as
+described in the handhelds.org instructions.
+Note that you will also be trusting that they can get you
+back to a working WinCE machine, so be sure to read the handhelds.org
+documents thoroughly in that regard.
+
+2. At the end, the instructions say ``At this point you have a working
+bootloader and you are ready to install a Linux distribution''.
+You can install Inferno instead, or several other systems, and
+you can later install them instead of Inferno, since they all use
+the same boot loader.
+
+3. A ready-made Inferno kernel is in the file k.gz in this directory
+(os/ipaq1110/k.gz in the Inferno structure). With a 115k serial connection
+to the bootloader established, as described for loading Linux,
+when you tell the bootloader to `load kernel', send k.gz as the
+data file (using the XMODEM protocol as described by Handhelds.org).
+
+4. Once the system is running, you can update kernels using Inferno,
+when your file system is taken as `remote' over a wireless connection.
+In an Inferno shell on the device (perhaps using the console serial port
+as a keyboard):
+ bind -a '#F' /dev
+ echo erase all >/dev/flash/kernelctl
+ dd </os/ipaq1110/k.gz -conv sync >/dev/flash/kernel
+or just run
+ /os/ipaq1110/upd
+which does that.
diff --git a/os/ipaq1110/archipaq.c b/os/ipaq1110/archipaq.c
new file mode 100644
index 00000000..0e3c726b
--- /dev/null
+++ b/os/ipaq1110/archipaq.c
@@ -0,0 +1,376 @@
+/*
+ * ipaq
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "draw.h"
+#include <memdraw.h>
+#include "screen.h"
+
+#include "../port/netif.h"
+#include "etherif.h"
+#include "../port/flashif.h"
+
+#define EGPIOADDR 0x49000000 /* physical and virtual address of write only register in CS5 space */
+
+static ulong egpiocopy;
+
+static void
+egpiosc(ulong set, ulong clr)
+{
+ int s;
+
+ s = splhi();
+ egpiocopy = (egpiocopy & ~clr) | set;
+ *(ulong*)EGPIOADDR = egpiocopy;
+ splx(s);
+}
+
+void
+archreset(void)
+{
+ GpioReg *g;
+ PpcReg *p;
+
+ g = GPIOREG;
+ g->grer = 0;
+ g->gfer = 0;
+ g->gedr = g->gedr;
+ g->gpcr = ~0;
+ g->gpdr = GPIO_CLK_SET0_o | GPIO_CLK_SET1_o; // | GPIO_LDD8_15_o;
+ g->gafr |= GPIO_SYS_CLK_i; // | GPIO_LDD8_15_o;
+ p = PPCREG;
+ p->ppdr |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; /* not sure about PPC_TXD4 here */
+ p->ppsr &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+
+ archuartpower(3, 1); /* allow console to work sooner rather than later */
+ L3init();
+}
+
+void
+archpowerdown(void)
+{
+ PmgrReg *p = PMGRREG;
+
+ p->pwer = GPIO_PWR_ON_i; /* only power button for now, not RTC */
+ p->pcfr = PCFR_opde; /* stop 3.6MHz oscillator, and drive pcmcia and chip select low */
+ p->pgsr = 0; /* drive all outputs to zero in sleep */
+ PPCREG->psdr = 0; /* all peripheral pins as outputs during sleep */
+}
+
+void
+archpowerup(void)
+{
+ GpioReg *g;
+ int i;
+
+ g = GPIOREG;
+ g->gpdr |= GPIO_COM_RTS_o; /* just in case it's off */
+ g->gpsr = GPIO_COM_RTS_o;
+ *(ulong*)EGPIOADDR = egpiocopy;
+ for(i=0; i<50*1000; i++)
+ ;
+ while((g->gplr & GPIO_PWR_ON_i) == 0)
+ ; /* wait for it to come up */
+}
+
+void
+archconfinit(void)
+{
+ int w;
+
+ conf.topofmem = 0xC0000000+32*MB;
+ w = PMGRREG->ppcr & 0x1f;
+ m->cpuhz = CLOCKFREQ*(w*4+16);
+
+ conf.useminicache = 1;
+ conf.portrait = 1; /* should take from param flash or allow dynamic change */
+}
+
+void
+kbdinit(void)
+{
+ addclock0link(kbdclock, MS2HZ);
+}
+
+static LCDmode lcd320x240x16tft =
+{
+// .x = 320, .y = 240, .depth = 16, .hz = 70,
+// .pbs = 2, .dual = 0, .mono = 0, .active = 1,
+// .hsync_wid = 4-2, .sol_wait = 12-1, .eol_wait = 17-1,
+// .vsync_hgt = 3-1, .soft_wait = 10, .eof_wait = 1,
+// .lines_per_int = 0, .palette_delay = 0, .acbias_lines = 0,
+// .obits = 16,
+// .vsynclow = 1, .hsynclow = 1,
+ 320, 240, 16, 70,
+ 2, 0, 0, 1,
+ 4-2, 12-1, 17-1,
+ 3-1, 10, 1,
+ 0, 0, 0,
+ 16,
+ 1, 1,
+};
+
+int
+archlcdmode(LCDmode *m)
+{
+ *m = lcd320x240x16tft;
+ return 0;
+}
+
+void
+archlcdenable(int on)
+{
+ if(on)
+ egpiosc(EGPIO_LCD_ON|EGPIO_LCD_PCI|EGPIO_LCD_5V_ON|EGPIO_LVDD_ON, 0);
+ else
+ egpiosc(0, EGPIO_LCD_ON|EGPIO_LCD_PCI|EGPIO_LCD_5V_ON|EGPIO_LVDD_ON);
+}
+
+void
+archconsole(void)
+{
+ uartspecial(0, 115200, 'n', &kbdq, &printq, kbdcr2nl);
+}
+
+void
+archuartpower(int port, int on)
+{
+ int s;
+
+ if(port != 3)
+ return;
+ s = splhi();
+ GPIOREG->gpdr |= GPIO_COM_RTS_o; /* should be done elsewhere */
+ GPIOREG->gpsr = GPIO_COM_RTS_o;
+ splx(s);
+ if(on)
+ egpiosc(EGPIO_RS232_ON, 0);
+ else
+ egpiosc(0, EGPIO_RS232_ON);
+}
+
+void
+archreboot(void)
+{
+ dcflushall();
+ GPIOREG->gedr = 1<<0;
+ mmuputctl(mmugetctl() & ~CpCaltivec); /* restore bootstrap's vectors */
+ RESETREG->rsrr = 1; /* software reset */
+ for(;;)
+ spllo();
+}
+
+void
+archflashwp(Flash*, int wp)
+{
+ if(wp)
+ egpiosc(0, EGPIO_VPEN);
+ else
+ egpiosc(EGPIO_VPEN, 0);
+}
+
+/*
+ * for ../port/devflash.c:/^flashreset
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(int bank, Flash *f)
+{
+ if(bank != 0)
+ return -1;
+ f->type = "cfi16";
+ f->addr = KADDR(FLASHMEM);
+ if((MEMCFGREG->msc0 & (1<<2)) == 0){
+ f->interleave = 1;
+ f->width = 4;
+ }else
+ f->width = 2;
+ return 0;
+}
+
+int
+archaudiopower(int on)
+{
+ int s;
+
+ if(on)
+ egpiosc(EGPIO_CODEC_RESET | EGPIO_AUD_PWR_ON, 0);
+ else
+ egpiosc(0, EGPIO_CODEC_RESET | EGPIO_AUD_ON | EGPIO_AUD_PWR_ON | EGPIO_QMUTE);
+ s = splhi();
+ GPIOREG->gafr |= GPIO_SYS_CLK_i;
+ GPIOREG->gpdr |= GPIO_CLK_SET0_o | GPIO_CLK_SET1_o;
+ GPIOREG->gpsr = GPIO_CLK_SET0_o;
+ GPIOREG->gpcr = GPIO_CLK_SET1_o;
+ splx(s);
+ return 0;
+}
+
+void
+archcodecreset(void)
+{
+// egpiosc(0, EGPIO_CODEC_RESET);
+// egpiosc(EGPIO_CODEC_RESET, 0);
+}
+
+void
+archaudiomute(int on)
+{
+ if(on)
+ egpiosc(EGPIO_QMUTE, 0);
+ else
+ egpiosc(0, EGPIO_QMUTE);
+}
+
+void
+archaudioamp(int on)
+{
+ if(on)
+ egpiosc(EGPIO_AUD_ON, 0);
+ else
+ egpiosc(0, EGPIO_AUD_ON);
+}
+
+enum {
+ Fs512 = 0,
+ Fs384 = 1,
+ Fs256 = 2,
+
+ MHz4_096 = GPIO_CLK_SET1_o,
+ MHz5_6245 = GPIO_CLK_SET1_o|GPIO_CLK_SET0_o,
+ MHz11_2896 = GPIO_CLK_SET0_o,
+ MHz12_288 = 0
+};
+
+typedef struct Csel Csel;
+struct Csel{
+ int speed;
+ int cfs; /* codec system clock multiplier */
+ int gclk; /* gpio clock generator setting */
+ int div; /* ssp clock divisor */
+};
+static Csel csel[] = {
+ {8000, Fs512, MHz4_096, 16},
+ {11025, Fs512, MHz5_6245, 16},
+ {16000, Fs256 , MHz4_096, 8},
+ {22050, Fs512, MHz11_2896, 16},
+ {32000, Fs384, MHz12_288, 12},
+ {44100, Fs256, MHz11_2896, 8},
+ {48000, Fs256, MHz12_288, 8},
+ {0},
+};
+
+int
+archaudiospeed(int clock, int set)
+{
+ GpioReg *g;
+ SspReg *ssp;
+ Csel *cs;
+ int s, div, cr0;
+
+ for(cs = csel; cs->speed > 0; cs++)
+ if(cs->speed == clock){
+ if(!set)
+ return cs->cfs;
+ div = cs->div;
+ if(div == 0)
+ div = 4;
+ div = div/2 - 1;
+ s = splhi();
+ g = GPIOREG;
+ g->gpsr = cs->gclk;
+ g->gpcr = ~cs->gclk & (GPIO_CLK_SET0_o|GPIO_CLK_SET1_o);
+ ssp = SSPREG;
+ cr0 = (div<<8) | 0x1f; /* 16 bits, TI frames, not enabled */
+ ssp->sscr0 = cr0;
+ ssp->sscr1 = 0x0020; /* ext clock */
+ ssp->sscr0 = cr0 | 0x80; /* enable */
+ splx(s);
+ return cs->cfs;
+ }
+ return -1;
+}
+
+/*
+ * pcmcia
+ */
+int
+pcmpowered(int slotno)
+{
+ if(slotno)
+ return 0;
+ if(egpiocopy & EGPIO_OPT_PWR_ON)
+ return 3;
+ return 0;
+}
+
+void
+pcmpower(int slotno, int on)
+{
+ USED(slotno); /* the pack powers both or none */
+ if(on){
+ if((egpiocopy & EGPIO_OPT_PWR_ON) == 0){
+ egpiosc(EGPIO_OPT_PWR_ON | EGPIO_OPT_ON, 0);
+ delay(200);
+ }
+ }else
+ egpiosc(0, EGPIO_OPT_PWR_ON | EGPIO_OPT_ON);
+}
+
+void
+pcmreset(int slot)
+{
+ USED(slot);
+ egpiosc(EGPIO_CARD_RESET, 0);
+ delay(100); // microdelay(10);
+ egpiosc(0, EGPIO_CARD_RESET);
+}
+
+int
+pcmpin(int slot, int type)
+{
+ switch(type){
+ case PCMready:
+ return slot==0? 21: 11;
+ case PCMeject:
+ return slot==0? 17: 10;
+ case PCMstschng:
+ return -1;
+ }
+}
+
+void
+pcmsetvpp(int slot, int vpp)
+{
+ USED(slot, vpp);
+}
+
+/*
+ * set ether parameters: the contents should be derived from EEPROM or NVRAM
+ */
+int
+archether(int ctlno, Ether *ether)
+{
+ static char opt[128];
+
+ if(ctlno == 1){
+ sprint(ether->type, "EC2T");
+ return 1;
+ }
+ if(ctlno > 0)
+ return -1;
+ sprint(ether->type, "wavelan");
+ if(0)
+ strcpy(opt, "mode=0 essid=Limbo station=ipaq1 crypt=off"); /* peertopeer */
+ else
+ strcpy(opt, "mode=managed essid=Vitanuova1 station=ipaq1 crypt=off");
+ ether->nopt = tokenize(opt, ether->opt, nelem(ether->opt));
+ return 1;
+}
diff --git a/os/ipaq1110/dat.h b/os/ipaq1110/dat.h
new file mode 100644
index 00000000..553c0cb0
--- /dev/null
+++ b/os/ipaq1110/dat.h
@@ -0,0 +1,135 @@
+typedef struct Conf Conf;
+typedef struct Dma Dma;
+typedef struct FPU FPU;
+typedef struct FPenv FPenv;
+typedef struct Label Label;
+typedef struct Lock Lock;
+typedef struct Mach Mach;
+typedef struct Ureg Ureg;
+typedef struct ISAConf ISAConf;
+typedef struct PCMmap PCMmap;
+typedef struct PCMslot PCMslot;
+
+typedef ulong Instr;
+
+struct Conf
+{
+ ulong nmach; /* processors */
+ ulong nproc; /* processes */
+ ulong npage0; /* total physical pages of memory */
+ ulong npage1; /* total physical pages of memory */
+ ulong topofmem; /* highest physical address + 1 */
+ ulong npage; /* total physical pages of memory */
+ ulong base0; /* base of bank 0 */
+ ulong base1; /* base of bank 1 */
+ ulong ialloc; /* max interrupt time allocation in bytes */
+
+ int useminicache; /* use mini cache: screen.c/lcd.c */
+ int textwrite; /* writeable text segment, for debug */
+ int portrait; /* display orientation */
+};
+
+#define NISAOPT 8
+struct ISAConf {
+ char type[KNAMELEN];
+ ulong port;
+ ulong irq;
+ int itype;
+ ulong dma;
+ ulong mem;
+ ulong size;
+ ulong freq;
+
+ int nopt;
+ char *opt[NISAOPT];
+};
+
+/*
+ * FPenv.status
+ */
+enum
+{
+ FPINIT,
+ FPACTIVE,
+ FPINACTIVE,
+};
+
+struct FPenv
+{
+ ulong status;
+ ulong control;
+ ushort fpistate; /* emulated fp */
+ ulong regs[8][3]; /* emulated fp */
+};
+
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct FPU
+{
+ FPenv env;
+};
+
+struct Label
+{
+ ulong sp;
+ ulong pc;
+};
+
+struct Lock
+{
+ ulong key;
+ ulong sr;
+ ulong pc;
+ int pri;
+};
+
+#include "../port/portdat.h"
+
+/*
+ * machine dependent definitions not used by ../port/portdat.h
+ */
+struct Mach
+{
+ /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+ ulong splpc; /* pc of last caller to splhi */
+
+ /* ordering from here on irrelevant */
+
+ int machno; /* physical id of processor */
+ ulong ticks; /* of the clock since boot time */
+ Proc *proc; /* current process on this processor */
+ Label sched; /* scheduler wakeup */
+ Lock alarmlock; /* access to alarm list */
+ void *alarm; /* alarms bound to this clock */
+ ulong cpuhz;
+
+ /* stacks for exceptions */
+ ulong fiqstack[4];
+ ulong irqstack[4];
+ ulong abtstack[4];
+ ulong undstack[4];
+
+ int stack[1];
+};
+
+#define MACHP(n) (n == 0 ? (Mach*)(MACHADDR) : (Mach*)0)
+
+extern Mach *m;
+extern Proc *up;
+
+typedef struct MemBank {
+ uint pbase;
+ uint plimit;
+ uint vbase;
+ uint vlimit;
+} MemBank;
+
+/*
+ * Layout at virtual address 0.
+ */
+typedef struct Vectorpage {
+ void (*vectors[8])(void);
+ uint vtable[8];
+} Vectorpage;
+extern Vectorpage *page0;
diff --git a/os/ipaq1110/defont.c b/os/ipaq1110/defont.c
new file mode 100644
index 00000000..285f464c
--- /dev/null
+++ b/os/ipaq1110/defont.c
@@ -0,0 +1,290 @@
+#include "lib9.h"
+#include "draw.h"
+
+/*
+ * /tmp/latin1.6x13, in uncompressed form
+ */
+uchar
+defontdata[] =
+{
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+ 0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x35,0x33,0x36,0x20,
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x33,0x20,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2a,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x41,0x0e,0x00,0x60,0xc2,0x0a,0x00,
+ 0x00,0x00,0x60,0xc2,0x00,0x60,0xc2,0x00,0x00,0xa6,0x0c,0x20,0xa0,0x00,0x01,0x83,
+ 0x08,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0x0c,0x00,0x00,0x00,0x00,0x0c,0x00,0x03,0x00,0x00,0x00,0x3f,
+ 0x30,0x03,0x1c,0x30,0x00,0x00,0x00,0x83,0x00,0x41,0x02,0x00,0x31,0x85,0x14,0x51,
+ 0xc0,0x00,0x31,0x85,0x14,0x31,0x85,0x14,0x01,0x43,0x18,0x51,0x45,0x00,0x08,0xc6,
+ 0x14,0x51,0x86,0x1c,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,
+ 0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x08,0x00,0x0c,0x00,0x73,0xe0,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x85,0x00,0x71,0x24,0x0c,0x11,0x00,0x00,0x00,0x00,0x02,
+ 0x20,0x87,0x3e,0x13,0xe7,0x3e,0x71,0xc0,0x00,0x08,0x08,0x1c,0x70,0x8f,0x1c,0xf3,
+ 0xef,0x9c,0x89,0xc3,0xa2,0x82,0x28,0x9c,0xf1,0xcf,0x1c,0xfa,0x28,0xa2,0x8a,0x2f,
+ 0x9c,0x81,0xc2,0x00,0x30,0x08,0x00,0x08,0x03,0x00,0x80,0x00,0x20,0x60,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x23,0x04,0xaa,0x8f,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0x0c,0x80,0x00,0x02,0x22,0x12,0x00,0xc0,0x80,0x00,0x03,0x00,
+ 0x48,0x04,0x82,0x60,0x06,0xc0,0x00,0x84,0x80,0x49,0x24,0x08,0x00,0x08,0x80,0x01,
+ 0x40,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0x00,0x88,0x00,0x00,0x70,0x00,
+ 0x00,0x00,0x04,0x22,0x60,0xc5,0x0a,0x00,0x00,0x00,0x60,0xc5,0x00,0x60,0xc5,0x00,
+ 0x09,0x46,0x0c,0x51,0x40,0x00,0x01,0x83,0x14,0x00,0xc8,0x00,0x1b,0xe0,0x00,0x03,
+ 0x84,0x00,0xc0,0x00,0x00,0xf0,0x0e,0x38,0xc3,0x0c,0x30,0xc2,0x4e,0x38,0x63,0x8e,
+ 0x38,0xe3,0x8c,0x28,0x00,0x85,0x14,0xa2,0xaa,0x08,0x20,0x82,0x00,0x00,0x00,0x02,
+ 0x51,0x88,0x82,0x12,0x08,0x82,0x8a,0x20,0x00,0x10,0x04,0x22,0x89,0x44,0xa2,0x4a,
+ 0x08,0x22,0x88,0x81,0x22,0x82,0x2c,0xa2,0x8a,0x28,0xa2,0x22,0x28,0xa2,0x8a,0x20,
+ 0x90,0x80,0x45,0x00,0x10,0x08,0x00,0x08,0x04,0x80,0x80,0x81,0x20,0x20,0x00,0x00,
+ 0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x08,0x20,0x8a,0x94,0x77,0xff,0xff,0x1e,
+ 0xff,0xcf,0xff,0xff,0xc3,0xfc,0x71,0xcf,0x3c,0xf3,0xcf,0x6c,0x71,0xe7,0x1c,0x71,
+ 0xc7,0x3c,0x73,0xd7,0x00,0x02,0x00,0x8a,0x22,0x10,0x51,0x23,0x82,0x00,0x04,0x80,
+ 0x48,0x01,0x0c,0x00,0x0a,0x80,0x00,0x84,0xa0,0x51,0x42,0x80,0x71,0xc7,0x1c,0x71,
+ 0xc7,0xa2,0xfb,0xef,0xbe,0x71,0xc7,0x1c,0x4a,0x27,0x1c,0x71,0xc7,0x00,0x9a,0x28,
+ 0xa2,0x8a,0x27,0x22,0x31,0x88,0x94,0x51,0xc0,0x00,0x31,0x88,0x94,0x31,0x88,0x94,
+ 0x7e,0x83,0x18,0x8a,0x85,0x0c,0x00,0xc6,0x22,0x51,0x88,0x14,0x9f,0xee,0x38,0xe2,
+ 0x0a,0x08,0xa2,0x88,0x22,0x81,0xc8,0x20,0xa2,0x8a,0x28,0xa3,0x48,0x20,0x92,0x08,
+ 0x20,0x82,0x0a,0x28,0x00,0x85,0x14,0xa1,0x4a,0x10,0x20,0x8a,0x88,0x00,0x00,0x04,
+ 0x8a,0x88,0x84,0x32,0x08,0x04,0x8a,0x22,0x08,0x20,0x02,0x22,0x8a,0x24,0xa0,0x4a,
+ 0x08,0x20,0x88,0x81,0x24,0x83,0x6c,0xa2,0x8a,0x28,0xa0,0x22,0x28,0xa2,0x51,0x41,
+ 0x10,0x40,0x48,0x80,0x08,0x08,0x00,0x08,0x04,0x00,0x80,0x00,0x20,0x20,0x00,0x00,
+ 0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x08,0x20,0x89,0x2a,0x74,0x71,0xc7,0x7d,
+ 0x7d,0xd7,0x5d,0xf7,0x5f,0x8d,0xf7,0xd7,0x5d,0x75,0xd7,0x2d,0xf7,0xdb,0x7d,0xf7,
+ 0xdf,0x5d,0xf5,0xd7,0x00,0x82,0x0c,0x71,0x42,0x10,0x02,0x14,0x84,0x00,0x08,0x40,
+ 0x48,0x82,0x02,0x00,0x0a,0x80,0x00,0x83,0x10,0x20,0x8d,0x08,0x8a,0x28,0xa2,0x8a,
+ 0x2a,0x20,0x82,0x08,0x20,0x20,0x82,0x08,0x4b,0x28,0xa2,0x8a,0x28,0x80,0x9a,0x28,
+ 0xa2,0x89,0x44,0xa6,0x00,0x00,0x00,0x01,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x08,0x00,0x00,0x00,0x00,0x0c,0x08,0x00,0x00,0x00,0x08,0x00,0xff,0xe8,0x20,0x83,
+ 0x8a,0x1c,0xc2,0x88,0x22,0xe2,0x0e,0x38,0xa2,0x8a,0x28,0xa2,0xce,0x30,0x83,0x8e,
+ 0x38,0xe2,0x8c,0x28,0x00,0x80,0x3e,0x70,0x44,0x00,0x40,0x4f,0x88,0x00,0x00,0x04,
+ 0x88,0x80,0x88,0x52,0xc8,0x04,0x8a,0x27,0x1c,0x43,0xe1,0x02,0x9a,0x24,0xa0,0x4a,
+ 0x08,0x20,0x88,0x81,0x28,0x82,0xaa,0xa2,0x8a,0x28,0xa0,0x22,0x28,0xa2,0x51,0x41,
+ 0x10,0x40,0x40,0x00,0x01,0xcf,0x1c,0x79,0xc4,0x1c,0xb1,0x83,0x24,0x23,0x4b,0x1c,
+ 0xf1,0xeb,0x1c,0xf2,0x28,0xa2,0x8a,0x2f,0x88,0x20,0x80,0x14,0xf5,0xf7,0xdf,0x1d,
+ 0x78,0xcf,0x5d,0xf7,0x47,0x7c,0x71,0xd7,0x5d,0x75,0xd7,0x4c,0x73,0xdf,0x1c,0x71,
+ 0xc7,0x3d,0x73,0xd7,0x00,0x87,0x12,0x51,0x42,0x1c,0x02,0xd4,0x8a,0xf8,0x0b,0x40,
+ 0x30,0x87,0x9c,0x01,0x2a,0x80,0x00,0x80,0x28,0x49,0x42,0x88,0x8a,0x28,0xa2,0x8a,
+ 0x2a,0x20,0x82,0x08,0x20,0x20,0x82,0x08,0x4b,0x28,0xa2,0x8a,0x28,0xa1,0xaa,0x28,
+ 0xa2,0x89,0x44,0xac,0x71,0xc7,0x1c,0x71,0xcf,0x1c,0x71,0xc7,0x1c,0x61,0x86,0x18,
+ 0x7a,0xc7,0x1c,0x71,0xc7,0x00,0x72,0x28,0xa2,0x8a,0x2f,0x22,0x1b,0xee,0x38,0xc2,
+ 0x0e,0x1c,0xa3,0x88,0x14,0x82,0x02,0x08,0xa2,0x8a,0x28,0xa2,0x42,0x20,0x92,0x02,
+ 0x20,0x82,0x8a,0x28,0x00,0x80,0x14,0x28,0x8a,0x00,0x40,0x47,0x3e,0x03,0xe0,0x08,
+ 0x88,0x81,0x1c,0x53,0x2f,0x08,0x71,0xe2,0x08,0x80,0x00,0x84,0xaa,0x27,0x20,0x4b,
+ 0xcf,0x20,0xf8,0x81,0x30,0x82,0xaa,0xa2,0xf2,0x2f,0x1c,0x22,0x25,0x2a,0x20,0x82,
+ 0x10,0x20,0x40,0x00,0x00,0x28,0xa2,0x8a,0x2f,0x22,0xc8,0x81,0x28,0x22,0xac,0xa2,
+ 0x8a,0x2c,0xa2,0x42,0x28,0xa2,0x52,0x21,0x30,0x20,0x60,0x2a,0xec,0x71,0xcf,0x7c,
+ 0x78,0xd7,0x1d,0xfa,0xdf,0x7f,0x7d,0xd7,0x5d,0x75,0xd7,0x6f,0x77,0xdb,0x7f,0x77,
+ 0xdf,0x5d,0x75,0xd7,0x00,0x8a,0x90,0x50,0x80,0x12,0x02,0x93,0x94,0x09,0xea,0x40,
+ 0x03,0xe0,0x00,0x01,0x26,0x8c,0x00,0x07,0x94,0x9a,0xa5,0x90,0x8a,0x28,0xa2,0x8a,
+ 0x2b,0xa0,0xf3,0xcf,0x3c,0x20,0x82,0x08,0xea,0xa8,0xa2,0x8a,0x28,0x92,0xaa,0x28,
+ 0xa2,0x88,0x85,0xa6,0x08,0x20,0x82,0x08,0x22,0xa2,0x8a,0x28,0xa2,0x20,0x82,0x08,
+ 0x8b,0x28,0xa2,0x8a,0x28,0x9e,0x9a,0x28,0xa2,0x8a,0x28,0xa2,0x3b,0xe2,0x20,0x83,
+ 0x8a,0x14,0xc2,0x8e,0x08,0x81,0xce,0x38,0xc3,0x0c,0x30,0xc2,0x4e,0x38,0x63,0x8e,
+ 0x38,0x83,0x8a,0x10,0x00,0x80,0x3e,0x29,0x09,0x80,0x40,0x4f,0x88,0x00,0x00,0x10,
+ 0x88,0x82,0x02,0x90,0x28,0x88,0x88,0x20,0x00,0x40,0x01,0x08,0xab,0xe4,0xa0,0x4a,
+ 0x08,0x26,0x88,0x81,0x28,0x82,0x29,0xa2,0x82,0x2a,0x02,0x22,0x25,0x2a,0x50,0x84,
+ 0x10,0x10,0x40,0x00,0x01,0xe8,0xa0,0x8b,0xe4,0x22,0x88,0x81,0x30,0x22,0xa8,0xa2,
+ 0x8a,0x28,0x18,0x42,0x28,0xaa,0x22,0x22,0x08,0x20,0x80,0x14,0xdf,0x77,0xdf,0x1d,
+ 0x7a,0xcf,0x5c,0x7d,0xdf,0x8c,0x71,0xcf,0x3c,0xf3,0xcf,0x6c,0x71,0xe7,0x1c,0x71,
+ 0xdf,0x5c,0x75,0xef,0x00,0x8a,0x3c,0x53,0xe2,0x0e,0x02,0xd0,0x28,0x09,0xea,0x40,
+ 0x00,0x80,0x00,0x01,0x22,0x8c,0x00,0x00,0x0a,0x28,0x2a,0xa0,0xfb,0xef,0xbe,0xfb,
+ 0xee,0x20,0x82,0x08,0x20,0x20,0x82,0x08,0x4a,0xa8,0xa2,0x8a,0x28,0x8c,0xaa,0x28,
+ 0xa2,0x88,0x86,0x22,0x79,0xe7,0x9e,0x79,0xe7,0xa0,0xfb,0xef,0xbe,0x20,0x82,0x08,
+ 0x8a,0x28,0xa2,0x8a,0x28,0x9e,0xaa,0x28,0xa2,0x8a,0x28,0xa2,0x33,0xee,0x38,0xf0,
+ 0xc5,0x3e,0x72,0x87,0x00,0x79,0xc0,0x00,0x20,0x01,0x0e,0x18,0xa4,0x98,0x49,0x16,
+ 0x1c,0x71,0xc7,0x1c,0x00,0x80,0x14,0x71,0x49,0x00,0x20,0x8a,0x88,0x00,0x00,0x10,
+ 0x88,0x84,0x02,0xf8,0x28,0x90,0x88,0x20,0x00,0x23,0xe2,0x08,0xb2,0x24,0xa0,0x4a,
+ 0x08,0x22,0x88,0x81,0x24,0x82,0x29,0xa2,0x82,0x29,0x02,0x22,0x25,0x2a,0x50,0x84,
+ 0x10,0x10,0x40,0x00,0x02,0x28,0xa0,0x8a,0x04,0x22,0x88,0x81,0x28,0x22,0xa8,0xa2,
+ 0x8a,0x28,0x04,0x42,0x25,0x2a,0x22,0x64,0x08,0x20,0x80,0x2a,0xdc,0x71,0xc3,0xce,
+ 0xb0,0x63,0x5e,0x3f,0xe1,0x8f,0xff,0xf7,0xff,0xbc,0x79,0xd6,0xd9,0xed,0xba,0x78,
+ 0xe3,0x8e,0x38,0xe3,0x00,0x8a,0x08,0x50,0x82,0x02,0x02,0x17,0x94,0x08,0x08,0x40,
+ 0x00,0x80,0x00,0x01,0x22,0x80,0x00,0x00,0x14,0x48,0x44,0xa2,0x8a,0x28,0xa2,0x8a,
+ 0x2a,0x20,0x82,0x08,0x20,0x20,0x82,0x08,0x4a,0x68,0xa2,0x8a,0x28,0x8c,0xca,0x28,
+ 0xa2,0x88,0x84,0x22,0x8a,0x28,0xa2,0x8a,0x2a,0x20,0x82,0x08,0x20,0x20,0x82,0x08,
+ 0x8a,0x28,0xa2,0x8a,0x28,0x80,0xaa,0x28,0xa2,0x8a,0x68,0xa6,0x33,0xe4,0x92,0x41,
+ 0x25,0x08,0x41,0xc4,0x3e,0x41,0x23,0x04,0x20,0x42,0x82,0x28,0xa6,0x94,0x69,0xb5,
+ 0x10,0x41,0x04,0x10,0x00,0x00,0x14,0x22,0xa6,0x80,0x20,0x82,0x00,0x30,0x02,0x20,
+ 0x50,0x88,0x22,0x12,0x28,0x90,0x8a,0x22,0x0c,0x10,0x04,0x00,0x82,0x24,0xa2,0x4a,
+ 0x08,0x22,0x88,0x89,0x22,0x82,0x28,0xa2,0x82,0xa8,0xa2,0x22,0x22,0x36,0x88,0x88,
+ 0x10,0x08,0x40,0x00,0x02,0x28,0xa2,0x8a,0x24,0x1e,0x88,0x81,0x24,0x22,0xa8,0xa2,
+ 0xf1,0xe8,0x22,0x4a,0x65,0x2a,0x51,0xa8,0x08,0x20,0x80,0x14,0xfe,0xdb,0x6f,0xb6,
+ 0xbd,0xef,0x8e,0xf0,0x6f,0xb7,0x3e,0xf7,0xef,0x5f,0x75,0xd6,0x5a,0xe5,0x92,0xbb,
+ 0xef,0xbe,0xfb,0xef,0x00,0x8a,0xbc,0x73,0xe2,0x02,0x01,0x20,0x0a,0x00,0x04,0x80,
+ 0x03,0xe0,0x00,0x01,0x62,0x80,0x00,0x00,0x28,0x78,0x87,0xa2,0x8a,0x28,0xa2,0x8a,
+ 0x2a,0x22,0x82,0x08,0x20,0x20,0x82,0x08,0x4a,0x68,0xa2,0x8a,0x28,0x92,0xca,0x28,
+ 0xa2,0x88,0x84,0x26,0x8a,0x28,0xa2,0x8a,0x2a,0xa2,0x8a,0x28,0xa2,0x20,0x82,0x08,
+ 0x8a,0x28,0xa2,0x8a,0x28,0x8c,0xca,0x69,0xa6,0x99,0xa8,0x9a,0x03,0xe3,0x0c,0x61,
+ 0x26,0x00,0x70,0x86,0x08,0x71,0xc4,0x84,0x20,0x41,0x04,0x48,0xc5,0x98,0x59,0x56,
+ 0x1c,0x71,0xc7,0x1c,0x00,0x80,0x00,0x02,0x40,0x00,0x11,0x00,0x00,0x20,0x07,0x20,
+ 0x23,0xef,0x9c,0x11,0xc7,0x10,0x71,0xc7,0x08,0x08,0x08,0x08,0x7a,0x2f,0x1c,0xf3,
+ 0xe8,0x1c,0x89,0xc6,0x22,0xfa,0x28,0x9c,0x81,0xc8,0x9c,0x21,0xc2,0x22,0x88,0x8f,
+ 0x9c,0x09,0xc0,0x00,0x01,0xef,0x1c,0x79,0xc4,0x02,0x89,0xc9,0x22,0x72,0x28,0x9c,
+ 0x80,0x28,0x1c,0x31,0xa2,0x14,0x88,0x2f,0x86,0x23,0x00,0x2a,0xdf,0x3c,0xe7,0xb6,
+ 0x7f,0xe3,0xde,0x7d,0xe3,0x8e,0xde,0xf7,0xef,0xbe,0xed,0xce,0x99,0xe9,0xaa,0x78,
+ 0xe3,0x8e,0x38,0xe3,0xc0,0x87,0x2a,0x88,0x82,0x12,0x00,0xc0,0x04,0x00,0x03,0x00,
+ 0x00,0x00,0x00,0x01,0xa2,0x80,0x10,0x00,0x10,0x08,0xe0,0x9c,0x8a,0x28,0xa2,0x8a,
+ 0x2b,0x9c,0xfb,0xef,0xbe,0x71,0xc7,0x1c,0xf2,0x27,0x1c,0x71,0xc7,0x21,0x71,0xc7,
+ 0x1c,0x70,0x84,0x2c,0x79,0xe7,0x9e,0x79,0xe7,0x1c,0x71,0xc7,0x1c,0x71,0xc7,0x1c,
+ 0x7a,0x27,0x1c,0x71,0xc7,0x0c,0x71,0xa6,0x9a,0x68,0x2f,0x02,0x03,0xe3,0x0c,0x40,
+ 0xc5,0x00,0x10,0x84,0x08,0x41,0x44,0x84,0x20,0x42,0x02,0x7c,0xa4,0x94,0x49,0x15,
+ 0x04,0x10,0x41,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x02,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x00,0x00,0x22,0x00,0x09,0x00,0x00,0x00,0x00,
+ 0x80,0x20,0x00,0x00,0x00,0x00,0x02,0x20,0x00,0x00,0x00,0x14,0xff,0x3c,0xef,0xce,
+ 0xbf,0xfb,0xde,0xfd,0xef,0xae,0xde,0xf7,0xef,0x7f,0x60,0xd6,0xda,0xed,0xba,0xbe,
+ 0xfb,0xef,0xbe,0xfb,0xc0,0x02,0x38,0x00,0x00,0x0c,0x00,0x00,0x02,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x02,0x00,0x00,0x10,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,
+ 0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x02,0x28,0x22,0x00,0x04,0x92,0x40,
+ 0x24,0x80,0x70,0x84,0x08,0x41,0x23,0x04,0x38,0x43,0x8e,0x08,0x94,0x98,0x49,0x16,
+ 0x1c,0x71,0xc7,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x06,0x00,0x00,0x00,0x00,
+ 0x80,0x20,0x00,0x00,0x00,0x00,0x01,0xc0,0x00,0x00,0x00,0x2a,0x02,0xdb,0x6f,0xf6,
+ 0xdf,0xe3,0xde,0xfd,0xef,0xb7,0x3e,0xf1,0xef,0x1c,0x7d,0xda,0xd9,0xed,0xba,0x78,
+ 0xe3,0x8e,0x38,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xc8,0x1c,
+ 0x20,0x20,0x20,
+ 0x20,0x20,0x20,0x20,0x20,0x32,0x35,0x36,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+ 0x20,0x20,0x31,0x33,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x31,
+ 0x20,0x00,0x00,0x02,0x0a,0x00,0x06,0x06,0x00,0x02,0x0c,0x00,0x06,0x0c,0x00,0x04,
+ 0x0d,0x00,0x06,0x12,0x00,0x04,0x0d,0x00,0x06,0x18,0x00,0x04,0x0d,0x00,0x06,0x1e,
+ 0x00,0x03,0x0d,0x00,0x06,0x24,0x00,0x03,0x0d,0x00,0x06,0x2a,0x00,0x04,0x0a,0x00,
+ 0x06,0x30,0x00,0x03,0x0d,0x00,0x06,0x36,0x00,0x04,0x0d,0x00,0x06,0x3c,0x00,0x04,
+ 0x0d,0x00,0x06,0x42,0x00,0x04,0x0d,0x00,0x06,0x48,0x00,0x03,0x0d,0x00,0x06,0x4e,
+ 0x00,0x04,0x0d,0x00,0x06,0x54,0x00,0x03,0x0d,0x00,0x06,0x5a,0x00,0x03,0x0d,0x00,
+ 0x06,0x60,0x00,0x03,0x0d,0x00,0x06,0x66,0x00,0x03,0x0d,0x00,0x06,0x6c,0x00,0x03,
+ 0x0d,0x00,0x06,0x72,0x00,0x03,0x0d,0x00,0x06,0x78,0x00,0x03,0x0d,0x00,0x06,0x7e,
+ 0x00,0x03,0x0d,0x00,0x06,0x84,0x00,0x03,0x0d,0x00,0x06,0x8a,0x00,0x03,0x0d,0x00,
+ 0x06,0x90,0x00,0x03,0x0d,0x00,0x06,0x96,0x00,0x03,0x0d,0x00,0x06,0x9c,0x00,0x03,
+ 0x0d,0x00,0x06,0xa2,0x00,0x03,0x0d,0x00,0x06,0xa8,0x00,0x03,0x0d,0x00,0x06,0xae,
+ 0x00,0x03,0x0d,0x00,0x06,0xb4,0x00,0x03,0x0d,0x00,0x06,0xba,0x00,0x03,0x0d,0x00,
+ 0x06,0xc0,0x00,0x00,0x00,0x00,0x06,0xc6,0x00,0x02,0x0b,0x00,0x06,0xcc,0x00,0x02,
+ 0x05,0x00,0x06,0xd2,0x00,0x03,0x0a,0x00,0x06,0xd8,0x00,0x01,0x0a,0x00,0x06,0xde,
+ 0x00,0x02,0x0b,0x00,0x06,0xe4,0x00,0x02,0x0a,0x00,0x06,0xea,0x00,0x02,0x05,0x00,
+ 0x06,0xf0,0x00,0x02,0x0b,0x00,0x06,0xf6,0x00,0x02,0x0b,0x00,0x06,0xfc,0x00,0x03,
+ 0x0a,0x00,0x06,0x02,0x01,0x04,0x09,0x00,0x06,0x08,0x01,0x09,0x0c,0x00,0x06,0x0e,
+ 0x01,0x06,0x07,0x00,0x06,0x14,0x01,0x09,0x0c,0x00,0x06,0x1a,0x01,0x02,0x0b,0x00,
+ 0x06,0x20,0x01,0x02,0x0b,0x00,0x06,0x26,0x01,0x02,0x0b,0x00,0x06,0x2c,0x01,0x02,
+ 0x0b,0x00,0x06,0x32,0x01,0x02,0x0b,0x00,0x06,0x38,0x01,0x02,0x0b,0x00,0x06,0x3e,
+ 0x01,0x02,0x0b,0x00,0x06,0x44,0x01,0x02,0x0b,0x00,0x06,0x4a,0x01,0x02,0x0b,0x00,
+ 0x06,0x50,0x01,0x02,0x0b,0x00,0x06,0x56,0x01,0x02,0x0b,0x00,0x06,0x5c,0x01,0x00,
+ 0x0d,0x00,0x06,0x62,0x01,0x00,0x0d,0x00,0x06,0x68,0x01,0x02,0x0b,0x00,0x06,0x6e,
+ 0x01,0x00,0x0d,0x00,0x06,0x74,0x01,0x02,0x0b,0x00,0x06,0x7a,0x01,0x02,0x0b,0x00,
+ 0x06,0x80,0x01,0x02,0x0b,0x00,0x06,0x86,0x01,0x02,0x0b,0x00,0x06,0x8c,0x01,0x02,
+ 0x0b,0x00,0x06,0x92,0x01,0x02,0x0b,0x00,0x06,0x98,0x01,0x02,0x0b,0x00,0x06,0x9e,
+ 0x01,0x02,0x0b,0x00,0x06,0xa4,0x01,0x02,0x0b,0x00,0x06,0xaa,0x01,0x02,0x0b,0x00,
+ 0x06,0xb0,0x01,0x02,0x0b,0x00,0x06,0xb6,0x01,0x02,0x0b,0x00,0x06,0xbc,0x01,0x02,
+ 0x0b,0x00,0x06,0xc2,0x01,0x02,0x0b,0x00,0x06,0xc8,0x01,0x02,0x0b,0x00,0x06,0xce,
+ 0x01,0x02,0x0b,0x00,0x06,0xd4,0x01,0x02,0x0b,0x00,0x06,0xda,0x01,0x02,0x0b,0x00,
+ 0x06,0xe0,0x01,0x02,0x0b,0x00,0x06,0xe6,0x01,0x02,0x0c,0x00,0x06,0xec,0x01,0x02,
+ 0x0b,0x00,0x06,0xf2,0x01,0x02,0x0b,0x00,0x06,0xf8,0x01,0x02,0x0b,0x00,0x06,0xfe,
+ 0x01,0x02,0x0b,0x00,0x06,0x04,0x02,0x02,0x0b,0x00,0x06,0x0a,0x02,0x02,0x0b,0x00,
+ 0x06,0x10,0x02,0x02,0x0b,0x00,0x06,0x16,0x02,0x02,0x0b,0x00,0x06,0x1c,0x02,0x02,
+ 0x0b,0x00,0x06,0x22,0x02,0x02,0x0b,0x00,0x06,0x28,0x02,0x02,0x0b,0x00,0x06,0x2e,
+ 0x02,0x02,0x0b,0x00,0x06,0x34,0x02,0x02,0x05,0x00,0x06,0x3a,0x02,0x0b,0x0c,0x00,
+ 0x06,0x40,0x02,0x02,0x05,0x00,0x06,0x46,0x02,0x05,0x0b,0x00,0x06,0x4c,0x02,0x02,
+ 0x0b,0x00,0x06,0x52,0x02,0x05,0x0b,0x00,0x06,0x58,0x02,0x02,0x0b,0x00,0x06,0x5e,
+ 0x02,0x05,0x0b,0x00,0x06,0x64,0x02,0x02,0x0b,0x00,0x06,0x6a,0x02,0x05,0x0d,0x00,
+ 0x06,0x70,0x02,0x02,0x0b,0x00,0x06,0x76,0x02,0x03,0x0b,0x00,0x06,0x7c,0x02,0x03,
+ 0x0d,0x00,0x06,0x82,0x02,0x02,0x0b,0x00,0x06,0x88,0x02,0x02,0x0b,0x00,0x06,0x8e,
+ 0x02,0x05,0x0b,0x00,0x06,0x94,0x02,0x05,0x0b,0x00,0x06,0x9a,0x02,0x05,0x0b,0x00,
+ 0x06,0xa0,0x02,0x05,0x0d,0x00,0x06,0xa6,0x02,0x05,0x0d,0x00,0x06,0xac,0x02,0x05,
+ 0x0b,0x00,0x06,0xb2,0x02,0x05,0x0b,0x00,0x06,0xb8,0x02,0x03,0x0b,0x00,0x06,0xbe,
+ 0x02,0x05,0x0b,0x00,0x06,0xc4,0x02,0x05,0x0b,0x00,0x06,0xca,0x02,0x05,0x0b,0x00,
+ 0x06,0xd0,0x02,0x05,0x0b,0x00,0x06,0xd6,0x02,0x05,0x0d,0x00,0x06,0xdc,0x02,0x05,
+ 0x0b,0x00,0x06,0xe2,0x02,0x02,0x0b,0x00,0x06,0xe8,0x02,0x02,0x0b,0x00,0x06,0xee,
+ 0x02,0x02,0x0b,0x00,0x06,0xf4,0x02,0x02,0x05,0x00,0x06,0xfa,0x02,0x00,0x0d,0x00,
+ 0x06,0x00,0x03,0x00,0x0c,0x00,0x06,0x06,0x03,0x00,0x0d,0x00,0x06,0x0c,0x03,0x00,
+ 0x0d,0x00,0x06,0x12,0x03,0x00,0x0d,0x00,0x06,0x18,0x03,0x00,0x0d,0x00,0x06,0x1e,
+ 0x03,0x00,0x0d,0x00,0x06,0x24,0x03,0x00,0x0d,0x00,0x06,0x2a,0x03,0x00,0x0d,0x00,
+ 0x06,0x30,0x03,0x00,0x0d,0x00,0x06,0x36,0x03,0x00,0x0d,0x00,0x06,0x3c,0x03,0x00,
+ 0x0d,0x00,0x06,0x42,0x03,0x00,0x0d,0x00,0x06,0x48,0x03,0x00,0x0d,0x00,0x06,0x4e,
+ 0x03,0x00,0x0d,0x00,0x06,0x54,0x03,0x00,0x0d,0x00,0x06,0x5a,0x03,0x00,0x0d,0x00,
+ 0x06,0x60,0x03,0x00,0x0d,0x00,0x06,0x66,0x03,0x00,0x0d,0x00,0x06,0x6c,0x03,0x00,
+ 0x0d,0x00,0x06,0x72,0x03,0x00,0x0d,0x00,0x06,0x78,0x03,0x00,0x0d,0x00,0x06,0x7e,
+ 0x03,0x00,0x0d,0x00,0x06,0x84,0x03,0x00,0x0d,0x00,0x06,0x8a,0x03,0x00,0x0d,0x00,
+ 0x06,0x90,0x03,0x00,0x0d,0x00,0x06,0x96,0x03,0x00,0x0d,0x00,0x06,0x9c,0x03,0x00,
+ 0x0d,0x00,0x06,0xa2,0x03,0x00,0x0d,0x00,0x06,0xa8,0x03,0x00,0x0d,0x00,0x00,0xae,
+ 0x03,0x00,0x0d,0x00,0x00,0xb4,0x03,0x00,0x0d,0x00,0x00,0xba,0x03,0x00,0x0d,0x00,
+ 0x00,0xc0,0x03,0x01,0x0c,0x00,0x06,0xc6,0x03,0x02,0x0b,0x00,0x06,0xcc,0x03,0x03,
+ 0x0c,0x00,0x06,0xd2,0x03,0x04,0x0c,0x00,0x06,0xd8,0x03,0x03,0x0b,0x00,0x06,0xde,
+ 0x03,0x02,0x0b,0x00,0x06,0xe4,0x03,0x02,0x0b,0x00,0x06,0xea,0x03,0x01,0x0c,0x00,
+ 0x06,0xf0,0x03,0x03,0x04,0x00,0x06,0xf6,0x03,0x02,0x0b,0x00,0x06,0xfc,0x03,0x01,
+ 0x09,0x00,0x06,0x02,0x04,0x03,0x0c,0x00,0x06,0x08,0x04,0x05,0x09,0x00,0x06,0x0e,
+ 0x04,0x06,0x08,0x00,0x06,0x14,0x04,0x02,0x0b,0x00,0x06,0x1a,0x04,0x01,0x02,0x00,
+ 0x06,0x20,0x04,0x01,0x06,0x00,0x06,0x26,0x04,0x04,0x0a,0x00,0x06,0x2c,0x04,0x01,
+ 0x06,0x00,0x06,0x32,0x04,0x01,0x06,0x00,0x06,0x38,0x04,0x00,0x03,0x00,0x06,0x3e,
+ 0x04,0x05,0x0c,0x00,0x06,0x44,0x04,0x02,0x0b,0x00,0x06,0x4a,0x04,0x06,0x08,0x00,
+ 0x06,0x50,0x04,0x0a,0x0d,0x00,0x06,0x56,0x04,0x01,0x06,0x00,0x06,0x5c,0x04,0x01,
+ 0x07,0x00,0x06,0x62,0x04,0x03,0x0c,0x00,0x06,0x68,0x04,0x00,0x0b,0x00,0x06,0x6e,
+ 0x04,0x00,0x0b,0x00,0x06,0x74,0x04,0x00,0x0b,0x00,0x06,0x7a,0x04,0x02,0x0b,0x00,
+ 0x06,0x80,0x04,0x00,0x0b,0x00,0x06,0x86,0x04,0x00,0x0b,0x00,0x06,0x8c,0x04,0x00,
+ 0x0b,0x00,0x06,0x92,0x04,0x00,0x0b,0x00,0x06,0x98,0x04,0x01,0x0b,0x00,0x06,0x9e,
+ 0x04,0x01,0x0b,0x00,0x06,0xa4,0x04,0x03,0x0b,0x00,0x06,0xaa,0x04,0x02,0x0d,0x00,
+ 0x06,0xb0,0x04,0x00,0x0b,0x00,0x06,0xb6,0x04,0x00,0x0b,0x00,0x06,0xbc,0x04,0x00,
+ 0x0b,0x00,0x06,0xc2,0x04,0x01,0x0b,0x00,0x06,0xc8,0x04,0x00,0x0b,0x00,0x06,0xce,
+ 0x04,0x00,0x0b,0x00,0x06,0xd4,0x04,0x00,0x0b,0x00,0x06,0xda,0x04,0x01,0x0b,0x00,
+ 0x06,0xe0,0x04,0x02,0x0b,0x00,0x06,0xe6,0x04,0x00,0x0b,0x00,0x06,0xec,0x04,0x00,
+ 0x0b,0x00,0x06,0xf2,0x04,0x00,0x0b,0x00,0x06,0xf8,0x04,0x00,0x0b,0x00,0x06,0xfe,
+ 0x04,0x00,0x0b,0x00,0x06,0x04,0x05,0x01,0x0b,0x00,0x06,0x0a,0x05,0x05,0x0b,0x00,
+ 0x06,0x10,0x05,0x01,0x0c,0x00,0x06,0x16,0x05,0x00,0x0b,0x00,0x06,0x1c,0x05,0x00,
+ 0x0b,0x00,0x06,0x22,0x05,0x00,0x0b,0x00,0x06,0x28,0x05,0x01,0x0b,0x00,0x06,0x2e,
+ 0x05,0x00,0x0b,0x00,0x06,0x34,0x05,0x01,0x0b,0x00,0x06,0x3a,0x05,0x01,0x0c,0x00,
+ 0x06,0x40,0x05,0x02,0x0b,0x00,0x06,0x46,0x05,0x02,0x0b,0x00,0x06,0x4c,0x05,0x01,
+ 0x0b,0x00,0x06,0x52,0x05,0x02,0x0b,0x00,0x06,0x58,0x05,0x03,0x0b,0x00,0x06,0x5e,
+ 0x05,0x03,0x0b,0x00,0x06,0x64,0x05,0x05,0x0b,0x00,0x06,0x6a,0x05,0x05,0x0d,0x00,
+ 0x06,0x70,0x05,0x02,0x0b,0x00,0x06,0x76,0x05,0x02,0x0b,0x00,0x06,0x7c,0x05,0x01,
+ 0x0b,0x00,0x06,0x82,0x05,0x03,0x0b,0x00,0x06,0x88,0x05,0x02,0x0b,0x00,0x06,0x8e,
+ 0x05,0x02,0x0b,0x00,0x06,0x94,0x05,0x01,0x0b,0x00,0x06,0x9a,0x05,0x03,0x0b,0x00,
+ 0x06,0xa0,0x05,0x02,0x0b,0x00,0x06,0xa6,0x05,0x02,0x0b,0x00,0x06,0xac,0x05,0x02,
+ 0x0b,0x00,0x06,0xb2,0x05,0x02,0x0b,0x00,0x06,0xb8,0x05,0x01,0x0b,0x00,0x06,0xbe,
+ 0x05,0x02,0x0b,0x00,0x06,0xc4,0x05,0x03,0x0b,0x00,0x06,0xca,0x05,0x03,0x0b,0x00,
+ 0x06,0xd0,0x05,0x04,0x0c,0x00,0x06,0xd6,0x05,0x02,0x0b,0x00,0x06,0xdc,0x05,0x02,
+ 0x0b,0x00,0x06,0xe2,0x05,0x01,0x0b,0x00,0x06,0xe8,0x05,0x03,0x0b,0x00,0x06,0xee,
+ 0x05,0x02,0x0d,0x00,0x06,0xf4,0x05,0x01,0x0d,0x00,0x06,0xfa,0x05,0x03,0x0d,0x00,
+ 0x06,0x00,0x06,0x00,0x00,0x00,0x00,
+
+
+};
+
+int sizeofdefont = sizeof defontdata;
+
+void
+_unpackinfo(Fontchar *fc, uchar *p, int n)
+{
+ int j;
+
+ for(j=0; j<=n; j++){
+ fc->x = p[0]|(p[1]<<8);
+ fc->top = p[2];
+ fc->bottom = p[3];
+ fc->left = p[4];
+ fc->width = p[5];
+ fc++;
+ p += 6;
+ }
+}
diff --git a/os/ipaq1110/devaudio.c b/os/ipaq1110/devaudio.c
new file mode 100644
index 00000000..66fb5cdc
--- /dev/null
+++ b/os/ipaq1110/devaudio.c
@@ -0,0 +1,1056 @@
+/*
+ * SAC/UDA 1341 Audio driver for the Bitsy
+ *
+ * This code is covered by the Lucent Public Licence 1.02 (http://plan9.bell-labs.com/plan9dist/license.html);
+ * see the file NOTICE in the current directory. Modifications for the Inferno environment by Vita Nuova.
+ *
+ * The Philips UDA 1341 sound chip is accessed through the Serial Audio
+ * Controller (SAC) of the StrongARM SA-1110.
+ *
+ * The code morphs Nicolas Pitre's <nico@cam.org> Linux controller
+ * and Ken's Soundblaster controller.
+ *
+ * The interface should be identical to that of devaudio.c
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+static int debug = 0;
+
+/* UDA 1341 Registers */
+enum {
+ /* Status0 register */
+ UdaStatusDC = 0, /* 1 bit */
+ UdaStatusIF = 1, /* 3 bits */
+ UdaStatusSC = 4, /* 2 bits */
+ UdaStatusRST = 6, /* 1 bit */
+};
+
+enum {
+ /* Status1 register */
+ UdaStatusPC = 0, /* 2 bits */
+ UdaStatusDS = 2, /* 1 bit */
+ UdaStatusPDA = 3, /* 1 bit */
+ UdaStatusPAD = 4, /* 1 bit */
+ UdaStatusIGS = 5, /* 1 bit */
+ UdaStatusOGS = 6, /* 1 bit */
+};
+
+/*
+ * UDA1341 L3 address and command types
+ */
+
+enum {
+ UDA1341_DATA0 = 0,
+ UDA1341_DATA1,
+ UDA1341_STATUS,
+ UDA1341_L3Addr = 0x14,
+};
+
+typedef struct AQueue AQueue;
+typedef struct Buf Buf;
+typedef struct IOstate IOstate;
+
+enum
+{
+ Qdir = 0,
+ Qaudio,
+ Qvolume,
+ Qstatus,
+ Qaudioctl,
+
+ Fmono = 1,
+ Fin = 2,
+ Fout = 4,
+
+ Aclosed = 0,
+ Aread,
+ Awrite,
+
+ Vaudio = 0,
+ Vmic,
+ Vtreb,
+ Vbass,
+ Vspeed,
+ Vfilter,
+ Vinvert,
+ Nvol,
+
+ Bufsize = 4*1024, /* 46 ms each */
+ Nbuf = 32, /* 1.5 seconds total */
+
+ Speed = 44100,
+ Ncmd = 50, /* max volume command words */
+};
+
+Dirtab
+audiodir[] =
+{
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "audio", {Qaudio}, 0, 0666,
+ "volume", {Qvolume}, 0, 0666,
+ "audioctl", {Qaudioctl}, 0, 0666,
+ "audiostat",{Qstatus}, 0, 0444,
+};
+
+struct Buf
+{
+ uchar* virt;
+ ulong phys;
+ uint nbytes;
+};
+
+struct IOstate
+{
+ QLock;
+ Lock ilock;
+ Rendez vous;
+ Chan *chan; /* chan of open */
+ Dma* dma; /* dma chan, alloc on open, free on close */
+ int bufinit; /* boolean, if buffers allocated */
+ Buf buf[Nbuf]; /* buffers and queues */
+ volatile Buf *current; /* next dma to finish */
+ volatile Buf *next; /* next candidate for dma */
+ volatile Buf *filling; /* buffer being filled */
+/* just be be cute (and to have defines like linux, a real operating system) */
+#define emptying filling
+};
+
+static struct
+{
+ QLock;
+ int amode; /* Aclosed/Aread/Awrite for /audio */
+ int intr; /* boolean an interrupt has happened */
+ int rivol[Nvol]; /* right/left input/output volumes */
+ int livol[Nvol];
+ int rovol[Nvol];
+ int lovol[Nvol];
+ uvlong totcount; /* how many bytes processed since open */
+ vlong tottime; /* time at which totcount bytes were processed */
+ int clockout; /* need steady output to provide input clock */
+ IOstate i;
+ IOstate o;
+} audio;
+
+static struct
+{
+ ulong bytes;
+ ulong totaldma;
+ ulong idledma;
+ ulong faildma;
+ ulong samedma;
+} iostats;
+
+static struct
+{
+ char* name;
+ int flag;
+ int ilval; /* initial values */
+ int irval;
+} volumes[] =
+{
+[Vaudio] {"audio", Fout|Fmono, 80, 80},
+[Vmic] {"mic", Fin|Fmono, 0, 0},
+[Vtreb] {"treb", Fout|Fmono, 50, 50},
+[Vbass] {"bass", Fout|Fmono, 50, 50},
+[Vspeed] {"speed", Fin|Fout|Fmono, Speed, Speed},
+[Vfilter] {"filter", Fout|Fmono, 0, 0},
+[Vinvert] {"invert", Fin|Fout|Fmono, 0, 0},
+[Nvol] {0}
+};
+
+static void setreg(char *name, int val, int n);
+
+static char Emode[] = "illegal open mode";
+static char Evolume[] = "illegal volume specifier";
+
+static void
+bufinit(IOstate *b)
+{
+ int i;
+
+ if (debug) print("bufinit\n");
+ for (i = 0; i < Nbuf; i++) {
+ b->buf[i].virt = xspanalloc(Bufsize, CACHELINESZ, 0);
+ b->buf[i].phys = PADDR(b->buf[i].virt);
+ }
+ b->bufinit = 1;
+};
+
+static void
+setempty(IOstate *b)
+{
+ int i;
+
+ if (debug) print("setempty\n");
+ for (i = 0; i < Nbuf; i++) {
+ b->buf[i].nbytes = 0;
+ }
+ b->filling = b->buf;
+ b->current = b->buf;
+ b->next = b->buf;
+}
+
+static int
+audioqnotempty(void *x)
+{
+ IOstate *s = x;
+
+ return dmaidle(s->dma) || s->emptying != s->current;
+}
+
+static int
+audioqnotfull(void *x)
+{
+ IOstate *s = x;
+
+ return dmaidle(s->dma) || s->filling != s->current;
+}
+
+static void
+audioreset(void)
+{
+ /* Turn MCP operations off */
+ MCPREG->mccr = 0;
+}
+
+uchar status0[1] = {0x22};
+uchar status1[1] = {0x80};
+uchar data00[1] = {0x00}; /* volume control, bits 0 – 5 */
+uchar data01[1] = {0x40};
+uchar data02[1] = {0x80};
+uchar data0e0[2] = {0xc0, 0xe0};
+uchar data0e1[2] = {0xc1, 0xe0};
+uchar data0e2[2] = {0xc2, 0xf2};
+/* there is no data0e3 */
+uchar data0e4[2] = {0xc4, 0xe0};
+uchar data0e5[2] = {0xc5, 0xe0};
+uchar data0e6[2] = {0xc6, 0xe3};
+
+static void
+enable(void)
+{
+ uchar data[1];
+ int cs;
+
+ L3init();
+
+ PPCREG->ppar &= ~PPAR_SPR;
+
+ /* external clock and ssp configured for current samples/sec */
+ cs = archaudiospeed(audio.livol[Vspeed], 1);
+ status0[0] = (status0[0] & ~(3<<4)) | (cs<<4);
+
+ /* Enable the audio power */
+ archaudiopower(1);
+// egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 1);
+
+ /* Wait for the UDA1341 to wake up */
+ delay(100);
+
+ /* Reset the chip */
+ data[0] = status0[0] | 1<<UdaStatusRST;
+ L3write(UDA1341_L3Addr | UDA1341_STATUS, data, 1 );
+ archcodecreset();
+
+ /* write uda 1341 status[0] */
+ L3write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1 );
+ L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+ L3write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);
+ L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e2, 2);
+ L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e6, 2 );
+
+ if (debug) {
+ print("enable: status0 = 0x%2.2ux\n", status0[0]);
+ print("enable: status1 = 0x%2.2ux\n", status1[0]);
+ print("enable: data02 = 0x%2.2ux\n", data02[0]);
+ print("enable: data0e2 = 0x%4.4ux\n", data0e2[0] | data0e2[1]<<8);
+ print("enable: data0e4 = 0x%4.4ux\n", data0e4[0] | data0e4[1]<<8);
+ print("enable: data0e6 = 0x%4.4ux\n", data0e6[0] | data0e6[1]<<8);
+ }
+}
+
+static void
+disable(void)
+{
+ SSPREG->sscr0 = 0x031f; /* disable */
+}
+
+static void
+resetlevel(void)
+{
+ int i;
+
+ for(i=0; volumes[i].name; i++) {
+ audio.lovol[i] = volumes[i].ilval;
+ audio.rovol[i] = volumes[i].irval;
+ audio.livol[i] = volumes[i].ilval;
+ audio.rivol[i] = volumes[i].irval;
+ }
+}
+
+static void
+mxvolume(void) {
+ int *left, *right;
+ int cs;
+
+ cs = archaudiospeed(audio.livol[Vspeed], 1);
+ status0[0] = (status0[0] & ~(3<<4)) | (cs<<4);
+ L3write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1);
+ if(debug)
+ print("mxvolume: status0 = %2.2ux\n", status0[0]);
+ if(audio.amode & Aread){
+ left = audio.livol;
+ right = audio.rivol;
+ if (left[Vmic]+right[Vmic] == 0) {
+ /* Turn on automatic gain control (AGC) */
+ data0e4[1] |= 0x10;
+ L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );
+ } else {
+ int v;
+ /* Turn on manual gain control */
+ v = ((left[Vmic]+right[Vmic])*0x7f/200)&0x7f;
+ data0e4[1] &= ~0x13;
+ data0e5[1] &= ~0x1f;
+ data0e4[1] |= v & 0x3;
+ data0e5[0] |= (v & 0x7c)<<6;
+ data0e5[1] |= (v & 0x7c)>>2;
+ L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );
+ L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e5, 2 );
+ }
+ if (left[Vinvert]+right[Vinvert] == 0)
+ status1[0] &= ~0x10;
+ else
+ status1[0] |= 0x10;
+ L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+ if (debug) {
+ print("mxvolume: status1 = 0x%2.2ux\n", status1[0]);
+ print("mxvolume: data0e4 = 0x%4.4ux\n", data0e4[0]|data0e4[0]<<8);
+ print("mxvolume: data0e5 = 0x%4.4ux\n", data0e5[0]|data0e5[0]<<8);
+ }
+ }
+ if(audio.amode & Awrite){
+ left = audio.lovol;
+ right = audio.rovol;
+ data00[0] &= ~0x3f;
+ data00[0] |= ((200-left[Vaudio]-right[Vaudio])*0x3f/200)&0x3f;
+ if (left[Vtreb]+right[Vtreb] <= 100
+ && left[Vbass]+right[Vbass] <= 100)
+ /* settings neutral */
+ data02[0] &= ~0x03;
+ else {
+ data02[0] |= 0x03;
+ data01[0] &= ~0x3f;
+ data01[0] |= ((left[Vtreb]+right[Vtreb]-100)*0x3/100)&0x03;
+ data01[0] |= (((left[Vbass]+right[Vbass]-100)*0xf/100)&0xf)<<2;
+ }
+ if (left[Vfilter]+right[Vfilter] == 0)
+ data02[0] &= ~0x10;
+ else
+ data02[0]|= 0x10;
+ if (left[Vinvert]+right[Vinvert] == 0)
+ status1[0] &= ~0x8;
+ else
+ status1[0] |= 0x8;
+ L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+ L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);
+ L3write(UDA1341_L3Addr | UDA1341_DATA0, data01, 1);
+ L3write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);
+ if (debug) {
+ print("mxvolume: status1 = 0x%2.2ux\n", status1[0]);
+ print("mxvolume: data00 = 0x%2.2ux\n", data00[0]);
+ print("mxvolume: data01 = 0x%2.2ux\n", data01[0]);
+ print("mxvolume: data02 = 0x%2.2ux\n", data02[0]);
+ }
+ }
+}
+
+static void
+setreg(char *name, int val, int n)
+{
+ uchar x[2];
+ int i;
+
+ if(strcmp(name, "pause") == 0){
+ for(i = 0; i < n; i++)
+ microdelay(val);
+ return;
+ }
+
+ x[0] = val;
+ x[1] = val>>8;
+
+ switch(n){
+ case 1:
+ case 2:
+ break;
+ default:
+ error("setreg");
+ }
+
+ if(strcmp(name, "status") == 0){
+ L3write(UDA1341_L3Addr | UDA1341_STATUS, x, n);
+ } else if(strcmp(name, "data0") == 0){
+ L3write(UDA1341_L3Addr | UDA1341_DATA0, x, n);
+ } else if(strcmp(name, "data1") == 0){
+ L3write(UDA1341_L3Addr | UDA1341_DATA1, x, n);
+ } else
+ error("setreg");
+}
+
+static void
+outenable(void) {
+ /* turn on DAC, set output gain switch */
+ archaudioamp(1);
+ archaudiomute(0);
+ status1[0] |= 0x41;
+ L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+ /* set volume */
+ data00[0] |= 0xf;
+ L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);
+ if (debug) {
+ print("outenable: status1 = 0x%2.2ux\n", status1[0]);
+ print("outenable: data00 = 0x%2.2ux\n", data00[0]);
+ }
+}
+
+static void
+outdisable(void) {
+ archaudiomute(1);
+ dmastop(audio.o.dma);
+ /* turn off DAC, clear output gain switch */
+ archaudioamp(0);
+ status1[0] &= ~0x41;
+ L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+ if (debug) {
+ print("outdisable: status1 = 0x%2.2ux\n", status1[0]);
+ }
+// egpiobits(EGPIO_audio_power, 0);
+}
+
+static void
+inenable(void) {
+ /* turn on ADC, set input gain switch */
+ status1[0] |= 0x22;
+ L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+ if (debug) {
+ print("inenable: status1 = 0x%2.2ux\n", status1[0]);
+ }
+}
+
+static void
+indisable(void) {
+ dmastop(audio.i.dma);
+ /* turn off ADC, clear input gain switch */
+ status1[0] &= ~0x22;
+ L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
+ if (debug) {
+ print("indisable: status1 = 0x%2.2ux\n", status1[0]);
+ }
+}
+
+static void
+sendaudio(IOstate *s) {
+ /* interrupt routine calls this too */
+ int n;
+
+ if (debug > 1) print("#A: sendaudio\n");
+ ilock(&s->ilock);
+ while (s->next != s->filling) {
+ assert(s->next->nbytes);
+ if ((n = dmastart(s->dma, (void*)s->next->phys, s->next->nbytes)) == 0) {
+ iostats.faildma++;
+ break;
+ }
+ iostats.totaldma++;
+ switch (n) {
+ case 1:
+ iostats.idledma++;
+ break;
+ case 3:
+ iostats.faildma++;
+ break;
+ }
+ if (debug) {
+ if (debug > 1)
+ print("dmastart @%p\n", s->next);
+ else
+ iprint("+");
+ }
+ s->next->nbytes = 0;
+ s->next++;
+ if (s->next == &s->buf[Nbuf])
+ s->next = &s->buf[0];
+ }
+ iunlock(&s->ilock);
+}
+
+static void
+recvaudio(IOstate *s) {
+ /* interrupt routine calls this too */
+ int n;
+
+ if (debug > 1) print("#A: recvaudio\n");
+ ilock(&s->ilock);
+ while (s->next != s->emptying) {
+ assert(s->next->nbytes == 0);
+ if ((n = dmastart(s->dma, (void*)s->next->phys, Bufsize)) == 0) {
+ iostats.faildma++;
+ break;
+ }
+ iostats.totaldma++;
+ switch (n) {
+ case 1:
+ iostats.idledma++;
+ break;
+ case 3:
+ iostats.faildma++;
+ break;
+ }
+ if (debug) {
+ if (debug > 1)
+ print("dmastart @%p\n", s->next);
+ else
+ iprint("+");
+ }
+ s->next++;
+ if (s->next == &s->buf[Nbuf])
+ s->next = &s->buf[0];
+ }
+ iunlock(&s->ilock);
+}
+
+static void
+audiopower(int flag) {
+ IOstate *s;
+
+ if (debug) {
+ iprint("audiopower %d\n", flag);
+ }
+ if (flag) {
+ /* power on only when necessary */
+ if (audio.amode) {
+ archaudiopower(1);
+ enable();
+ if (audio.amode & Aread) {
+ inenable();
+ s = &audio.i;
+ dmastop(s->dma);
+ recvaudio(s);
+ }
+ if (audio.amode & Awrite) {
+ outenable();
+ s = &audio.o;
+ dmastop(s->dma);
+ sendaudio(s);
+ }
+ mxvolume();
+ }
+ } else {
+ /* power off */
+ if (audio.amode & Aread)
+ indisable();
+ if (audio.amode & Awrite)
+ outdisable();
+ disable();
+ archaudiopower(0);
+ }
+}
+
+static void
+audiointr(void *x, ulong ndma) {
+ IOstate *s = x;
+
+ if (debug) {
+ if (debug > 1)
+ iprint("#A: audio interrupt @%p\n", s->current);
+ else
+ iprint("-");
+ }
+ /* Only interrupt routine touches s->current */
+ s->current++;
+ if (s->current == &s->buf[Nbuf])
+ s->current = &s->buf[0];
+ if (ndma > 0) {
+ if (s == &audio.o)
+ sendaudio(s);
+ else if (s == &audio.i)
+ recvaudio(s);
+ }
+ wakeup(&s->vous);
+}
+
+static void
+audioinit(void)
+{
+ audio.amode = Aclosed;
+ resetlevel();
+// powerenable(audiopower);
+}
+
+static Chan*
+audioattach(char *param)
+{
+ return devattach('A', param);
+}
+
+static Walkqid*
+audiowalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
+}
+
+static int
+audiostat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
+}
+
+static Chan*
+audioopen(Chan *c, int mode)
+{
+ IOstate *s;
+ int omode = mode;
+
+ switch((ulong)c->qid.path) {
+ default:
+ error(Eperm);
+ break;
+
+ case Qstatus:
+ if((omode&7) != OREAD)
+ error(Eperm);
+ case Qvolume:
+ case Qaudioctl:
+ case Qdir:
+ break;
+
+ case Qaudio:
+ omode = (omode & 0x7) + 1;
+ if (omode & ~(Aread | Awrite))
+ error(Ebadarg);
+ qlock(&audio);
+ if(audio.amode & omode){
+ qunlock(&audio);
+ error(Einuse);
+ }
+ enable();
+ memset(&iostats, 0, sizeof(iostats));
+ if (omode & Aread) {
+ inenable();
+ s = &audio.i;
+ if(s->bufinit == 0)
+ bufinit(s);
+ setempty(s);
+ s->emptying = &s->buf[Nbuf-1];
+ s->chan = c;
+ s->dma = dmasetup(DmaSSP, 1, 0, audiointr, (void*)s);
+ audio.amode |= Aread;
+ audio.clockout = 1;
+ }
+ if (omode & Awrite) {
+ outenable();
+ s = &audio.o;
+ audio.amode |= Awrite;
+ if(s->bufinit == 0)
+ bufinit(s);
+ setempty(s);
+ s->chan = c;
+ s->dma = dmasetup(DmaSSP, 0, 0, audiointr, (void*)s);
+ audio.amode |= Awrite;
+ }
+ mxvolume();
+ qunlock(&audio);
+ if (debug) print("open done\n");
+ break;
+ }
+ c = devopen(c, mode, audiodir, nelem(audiodir), devgen);
+ c->mode = openmode(mode);
+ c->flag |= COPEN;
+ c->offset = 0;
+
+ return c;
+}
+
+static void
+audioclose(Chan *c)
+{
+ IOstate *s;
+
+ switch((ulong)c->qid.path) {
+ default:
+ error(Eperm);
+ break;
+
+ case Qdir:
+ case Qvolume:
+ case Qaudioctl:
+ case Qstatus:
+ break;
+
+ case Qaudio:
+ if (debug > 1) print("#A: close\n");
+ if(c->flag & COPEN) {
+ qlock(&audio);
+ if(waserror()){
+ qunlock(&audio);
+ nexterror();
+ }
+ if (audio.o.chan == c) {
+ /* closing the write end */
+ audio.amode &= ~Awrite;
+ s = &audio.o;
+ qlock(s);
+ if(waserror()){
+ qunlock(s);
+ nexterror();
+ }
+ if (s->filling->nbytes) {
+ /* send remaining partial buffer */
+ s->filling++;
+ if (s->filling == &s->buf[Nbuf])
+ s->filling = &s->buf[0];
+ sendaudio(s);
+ }
+ dmawait(s->dma);
+ outdisable();
+ setempty(s);
+ dmafree(s->dma);
+ qunlock(s);
+ poperror();
+ }
+ if (audio.i.chan == c) {
+ /* closing the read end */
+ audio.amode &= ~Aread;
+ s = &audio.i;
+ qlock(s);
+ if(waserror()){
+ qunlock(s);
+ nexterror();
+ }
+ indisable();
+ setempty(s);
+ dmafree(s->dma);
+ qunlock(s);
+ poperror();
+ }
+ if (audio.amode == 0) {
+ /* turn audio off */
+ archaudiopower(0);
+ }
+ qunlock(&audio);
+ poperror();
+ if (debug) {
+ print("total dmas: %lud\n", iostats.totaldma);
+ print("dmas while idle: %lud\n", iostats.idledma);
+ print("dmas while busy: %lud\n", iostats.faildma);
+ print("out of order dma: %lud\n", iostats.samedma);
+ }
+ }
+ break;
+ }
+}
+
+static long
+audioread(Chan *c, void *v, long n, vlong off)
+{
+ int liv, riv, lov, rov;
+ long m, n0;
+ char buf[300];
+ int j;
+ ulong offset = off;
+ char *p;
+ IOstate *s;
+
+ n0 = n;
+ p = v;
+ switch((ulong)c->qid.path) {
+ default:
+ error(Eperm);
+ break;
+
+ case Qdir:
+ return devdirread(c, p, n, audiodir, nelem(audiodir), devgen);
+
+ case Qaudio:
+ if (debug > 1) print("#A: read %ld\n", n);
+ if((audio.amode & Aread) == 0)
+ error(Emode);
+ s = &audio.i;
+ qlock(s);
+ if(waserror()){
+ qunlock(s);
+ nexterror();
+ }
+ while(n > 0) {
+ if(s->emptying->nbytes == 0) {
+ if (debug > 1) print("#A: emptied @%p\n", s->emptying);
+ recvaudio(s);
+ s->emptying++;
+ if (s->emptying == &s->buf[Nbuf])
+ s->emptying = s->buf;
+ }
+ /* wait if dma in progress */
+ while (!dmaidle(s->dma) && s->emptying == s->current) {
+ if (debug > 1) print("#A: sleep\n");
+ sleep(&s->vous, audioqnotempty, s);
+ }
+
+ m = Bufsize - s->emptying->nbytes;
+ if(m > n)
+ m = n;
+ memmove(p, s->emptying->virt + s->emptying->nbytes, m);
+
+ s->emptying->nbytes -= m;
+ n -= m;
+ p += m;
+ }
+ poperror();
+ qunlock(s);
+ break;
+ break;
+
+ case Qstatus:
+ buf[0] = 0;
+ snprint(buf, sizeof(buf), "bytes %llud\ntime %lld\n",
+ audio.totcount, audio.tottime);
+ return readstr(offset, p, n, buf);
+
+ case Qvolume:
+ case Qaudioctl:
+ j = 0;
+ buf[0] = 0;
+ for(m=0; volumes[m].name; m++){
+ liv = audio.livol[m];
+ riv = audio.rivol[m];
+ lov = audio.lovol[m];
+ rov = audio.rovol[m];
+ j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
+ if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
+ if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
+ j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
+ else{
+ if(volumes[m].flag & Fin)
+ j += snprint(buf+j, sizeof(buf)-j,
+ " in %d", liv);
+ if(volumes[m].flag & Fout)
+ j += snprint(buf+j, sizeof(buf)-j,
+ " out %d", lov);
+ }
+ }else{
+ if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
+ liv==lov && riv==rov)
+ j += snprint(buf+j, sizeof(buf)-j,
+ " left %d right %d",
+ liv, riv);
+ else{
+ if(volumes[m].flag & Fin)
+ j += snprint(buf+j, sizeof(buf)-j,
+ " in left %d right %d",
+ liv, riv);
+ if(volumes[m].flag & Fout)
+ j += snprint(buf+j, sizeof(buf)-j,
+ " out left %d right %d",
+ lov, rov);
+ }
+ }
+ j += snprint(buf+j, sizeof(buf)-j, "\n");
+ }
+ return readstr(offset, p, n, buf);
+ }
+ return n0-n;
+}
+
+static long
+audiowrite(Chan *c, void *vp, long n, vlong)
+{
+ long m, n0;
+ int i, nf, v, left, right, in, out;
+ char buf[255], *field[Ncmd];
+ char *p;
+ IOstate *a;
+
+ p = vp;
+ n0 = n;
+ switch((ulong)c->qid.path) {
+ default:
+ error(Eperm);
+ break;
+
+ case Qvolume:
+ case Qaudioctl:
+ v = Vaudio;
+ left = 1;
+ right = 1;
+ in = 1;
+ out = 1;
+ if(n > sizeof(buf)-1)
+ n = sizeof(buf)-1;
+ memmove(buf, p, n);
+ buf[n] = '\0';
+ n = 0;
+
+ nf = getfields(buf, field, Ncmd, 1, " \t\n");
+ for(i = 0; i < nf; i++){
+ /*
+ * a number is volume
+ */
+ if(field[i][0] >= '0' && field[i][0] <= '9') {
+ m = strtoul(field[i], 0, 10);
+ if(v == Vspeed){
+ if(archaudiospeed(m, 0) < 0)
+ error(Evolume);
+ }else
+ if(m < 0 || m > 100)
+ error(Evolume);
+ if(left && out)
+ audio.lovol[v] = m;
+ if(left && in)
+ audio.livol[v] = m;
+ if(right && out)
+ audio.rovol[v] = m;
+ if(right && in)
+ audio.rivol[v] = m;
+ goto cont0;
+ }
+ if(strcmp(field[i], "rate") == 0)
+ field[i] = "speed"; /* honestly ... */
+
+ for(m=0; volumes[m].name; m++) {
+ if(strcmp(field[i], volumes[m].name) == 0) {
+ v = m;
+ in = 1;
+ out = 1;
+ left = 1;
+ right = 1;
+ goto cont0;
+ }
+ }
+ if(strcmp(field[i], "enc") == 0) {
+ if(++i >= nf)
+ error(Evolume);
+ if(strcmp(field[i], "pcm") != 0)
+ error(Evolume);
+ goto cont0;
+ }
+ if(strcmp(field[i], "bits") == 0) {
+ if(++i >= nf)
+ error(Evolume);
+ if(strtol(field[i], 0, 0) != 16)
+ error(Evolume);
+ goto cont0;
+ }
+ if(strcmp(field[i], "chans") == 0) {
+ if(++i >= nf)
+ error(Evolume);
+ if(strtol(field[i], 0, 0) != 2)
+ error(Evolume);
+ goto cont0;
+ }
+ if(strcmp(field[i], "reset") == 0) {
+ resetlevel();
+ goto cont0;
+ }
+ if(strcmp(field[i], "debug") == 0) {
+ debug = debug?0:1;
+ goto cont0;
+ }
+ if(strcmp(field[i], "in") == 0) {
+ in = 1;
+ out = 0;
+ goto cont0;
+ }
+ if(strcmp(field[i], "out") == 0) {
+ in = 0;
+ out = 1;
+ goto cont0;
+ }
+ if(strcmp(field[i], "left") == 0) {
+ left = 1;
+ right = 0;
+ goto cont0;
+ }
+ if(strcmp(field[i], "right") == 0) {
+ left = 0;
+ right = 1;
+ goto cont0;
+ }
+ if(strcmp(field[i], "reg") == 0) {
+ if(nf < 3)
+ error(Evolume);
+ setreg(field[1], atoi(field[2]), nf == 4 ? atoi(field[3]):1);
+ return n0;
+ }
+ error(Evolume);
+ break;
+ cont0:;
+ }
+ mxvolume();
+ break;
+
+ case Qaudio:
+ if (debug > 1) print("#A: write %ld\n", n);
+ if((audio.amode & Awrite) == 0)
+ error(Emode);
+ a = &audio.o;
+ qlock(a);
+ if(waserror()){
+ qunlock(a);
+ nexterror();
+ }
+ while(n > 0) {
+ /* wait if dma in progress */
+ while (!dmaidle(a->dma) && a->filling == a->current) {
+ if (debug > 1) print("#A: sleep\n");
+ sleep(&a->vous, audioqnotfull, a);
+ }
+
+ m = Bufsize - a->filling->nbytes;
+ if(m > n)
+ m = n;
+ memmove(a->filling->virt + a->filling->nbytes, p, m);
+
+ a->filling->nbytes += m;
+ n -= m;
+ p += m;
+ if(a->filling->nbytes >= Bufsize) {
+ if (debug > 1) print("#A: filled @%p\n", a->filling);
+ a->filling++;
+ if (a->filling == &a->buf[Nbuf])
+ a->filling = a->buf;
+ sendaudio(a);
+ }
+ }
+ poperror();
+ qunlock(a);
+ break;
+ }
+ return n0 - n;
+}
+
+Dev audiodevtab = {
+ 'A',
+ "audio",
+
+ audioreset,
+ audioinit,
+ devshutdown,
+ audioattach,
+ audiowalk,
+ audiostat,
+ audioopen,
+ devcreate,
+ audioclose,
+ audioread,
+ devbread,
+ audiowrite,
+ devbwrite,
+ devremove,
+ devwstat,
+ audiopower,
+};
diff --git a/os/ipaq1110/devipaq.c b/os/ipaq1110/devipaq.c
new file mode 100644
index 00000000..cc432cb5
--- /dev/null
+++ b/os/ipaq1110/devipaq.c
@@ -0,0 +1,762 @@
+/*
+ * iPAQ H3650 touch screen and other devices
+ *
+ * Inferno driver derived from sketchy documentation and
+ * information gleaned from linux/char/h3650_ts.c
+ * by Charles Flynn.
+ *
+ * Copyright © 2000,2001 Vita Nuova Holdings Limited. All rights reserved.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#include "keyboard.h"
+#include <kernel.h>
+
+#include <draw.h>
+#include <memdraw.h>
+#include "screen.h"
+
+#define DEBUG 0
+
+/*
+ * packet format
+ *
+ * SOF (0x02)
+ * (id<<4) | len byte length
+ * data[len] bytes
+ * chk checksum mod 256 excluding SOF
+ */
+
+enum {
+ Csof = 0x02,
+ Ceof = 0x03,
+ Hdrlen = 3,
+
+ /* opcodes */
+
+ Oversion = 0,
+ Okeys = 2,
+ Otouch = 3,
+ Ordeeprom = 4,
+ Owreeprom = 5,
+ Othermal = 6,
+ Oled = 8,
+ Obattery = 9,
+ Ospiread = 11,
+ Ospiwrite = 12,
+ Obacklight = 13,
+ Oextstatus = 0xA1,
+ };
+
+enum {
+ Powerbit = 0, /* GPIO bit for power on/off key */
+};
+
+enum{
+ Qdir,
+ Qctl,
+ Qtouchctl,
+ Qbattery,
+ Qversion,
+};
+
+static
+Dirtab ipaqtab[]={
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "ipaqctl", {Qctl}, 0, 0600,
+ "battery", {Qbattery}, 0, 0444,
+ "version", {Qversion}, 0, 0444,
+ "touchctl", {Qtouchctl}, 0, 0644,
+};
+
+static struct {
+ QLock;
+ Chan* c;
+
+ Lock rl; /* protect cmd, reply */
+ int cmd;
+ Block* reply;
+ Rendez r;
+} atmel;
+
+/* to and from fixed point */
+#define FX(a,b) (((a)<<16)/(b))
+#define XF(v) ((v)>>16)
+
+static struct {
+ Lock;
+ int rate;
+ int m[2][3]; /* transformation matrix */
+ Point avg;
+ Point diff;
+ Point pts[4];
+ int n; /* number of points in pts */
+ int p; /* current index in pts */
+ int down;
+ int nout;
+} touch = {
+ {0},
+ .m {{-FX(1,3), 0, FX(346,1)},{0, -FX(1,4), FX(256, 1)}},
+};
+
+/*
+ * map rocker positions to same codes as plan 9
+ */
+static Rune rockermap[2][4] ={
+ {Right, Down, Up, Left}, /* landscape */
+ {Up, Right, Left, Down}, /* portrait */
+};
+
+static Rendez powerevent;
+
+static void cmdack(int, void*, int);
+static int cmdio(int, void*, int, void*, int);
+static void ipaqreadproc(void*);
+static void powerwaitproc(void*);
+static Block* rdevent(Block**);
+static long touchctl(char*, long);
+static void touched(Block*, int);
+static int wrcmd(int, void*, int, void*, int);
+static char* acstatus(int);
+static char* batstatus(int);
+static void powerintr(Ureg*, void*);
+
+static void
+ipaqreset(void)
+{
+ intrenable(Powerbit, powerintr, nil, BusGPIOfalling, "power off");
+}
+
+static void
+ipaqinit(void)
+{
+ kproc("powerwait", powerwaitproc, nil, 0);
+}
+
+static Chan*
+ipaqattach(char* spec)
+{
+ int fd;
+
+ qlock(&atmel);
+ if(waserror()){
+ qunlock(&atmel);
+ nexterror();
+ }
+ if(atmel.c == nil){
+ fd = kopen("#t/eia1ctl", ORDWR);
+ if(fd < 0)
+ error(up->env->errstr);
+ kwrite(fd, "b115200", 7); /* it's already pn, l8 */
+ kclose(fd);
+ fd = kopen("#t/eia1", ORDWR);
+ if(fd < 0)
+ error(up->env->errstr);
+ atmel.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
+ kclose(fd);
+ atmel.cmd = -1;
+ kproc("ipaqread", ipaqreadproc, nil, 0);
+ }
+ poperror();
+ qunlock(&atmel);
+ return devattach('T', spec);
+}
+
+static Walkqid*
+ipaqwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, ipaqtab, nelem(ipaqtab), devgen);
+}
+
+static int
+ipaqstat(Chan* c, uchar *db, int n)
+{
+ return devstat(c, db, n, ipaqtab, nelem(ipaqtab), devgen);
+}
+
+static Chan*
+ipaqopen(Chan* c, int omode)
+{
+ return devopen(c, omode, ipaqtab, nelem(ipaqtab), devgen);
+}
+
+static void
+ipaqclose(Chan*)
+{
+}
+
+static long
+ipaqread(Chan* c, void* a, long n, vlong offset)
+{
+ char *tmp, buf[64];
+ uchar reply[12];
+ int v, p, l;
+
+ switch((ulong)c->qid.path){
+ case Qdir:
+ return devdirread(c, a, n, ipaqtab, nelem(ipaqtab), devgen);
+ case Qtouchctl:
+ tmp = malloc(READSTR);
+ if(waserror()){
+ free(tmp);
+ nexterror();
+ }
+ snprint(tmp, READSTR, "s%d\nr%d\nR%d\nX %d %d %d\nY %d %d %d\n",
+ 1000, 0, 1,
+ touch.m[0][0], touch.m[0][1], touch.m[0][2],
+ touch.m[1][0], touch.m[1][1], touch.m[1][2]);
+ n = readstr(offset, a, n, tmp);
+ poperror();
+ free(tmp);
+ break;
+ case Qbattery:
+ cmdio(Obattery, reply, 0, reply, sizeof(reply));
+ tmp = malloc(READSTR);
+ if(waserror()){
+ free(tmp);
+ nexterror();
+ }
+ v = (reply[4]<<8)|reply[3];
+ p = 425*v/1000 - 298;
+ snprint(tmp, READSTR, "voltage: %d %dmV %d%% %d\nac: %s\nstatus: %d %s\nchem: %d\n",
+ v, 1000*v/228, p, 300*p/100, acstatus(reply[1]), reply[5], batstatus(reply[5]), reply[2]);
+ n = readstr(offset, a, n, tmp);
+ poperror();
+ free(tmp);
+ break;
+ case Qversion:
+ l = cmdio(Oversion, reply, 0, reply, sizeof(reply));
+ if(l > 4){
+ l--;
+ memmove(buf, reply+1, 4);
+ if(l > 8){
+ buf[4] = ' ';
+ memmove(buf+5, reply+5, 4); /* pack version */
+ sprint(buf+9, " %.2x\n", reply[9]); /* ``boot type'' */
+ }else{
+ buf[4] = '\n';
+ buf[5] = 0;
+ }
+ return readstr(offset, a, n, buf);
+ }
+ n=0;
+ break;
+ default:
+ n=0;
+ break;
+ }
+ return n;
+}
+
+static long
+ipaqwrite(Chan* c, void* a, long n, vlong)
+{
+ char cmd[64], op[32], *fields[6];
+ int nf;
+
+ switch((ulong)c->qid.path){
+ case Qctl:
+ if(n >= sizeof(cmd)-1)
+ n = sizeof(cmd)-1;
+ memmove(cmd, a, n);
+ cmd[n] = 0;
+ nf = getfields(cmd, fields, nelem(fields), 1, " \t\n");
+ if(nf <= 0)
+ error(Ebadarg);
+ if(nf >= 4 && strcmp(fields[0], "light") == 0){
+ op[0] = atoi(fields[1]); /* mode */
+ op[1] = atoi(fields[2]); /* power */
+ op[2] = atoi(fields[3]); /* brightness */
+ cmdack(Obacklight, op, 3);
+ }else if(nf >= 5 && strcmp(fields[0], "led") == 0){
+ op[0] = atoi(fields[1]);
+ op[1] = atoi(fields[2]);
+ op[2] = atoi(fields[3]);
+ op[3] = atoi(fields[4]);
+ cmdack(Oled, op, 4);
+ }else if(strcmp(fields[0], "suspend") == 0){
+ /* let the kproc do it */
+ wakeup(&powerevent);
+ }else
+ error(Ebadarg);
+ break;
+ case Qtouchctl:
+ return touchctl(a, n);
+ default:
+ error(Ebadusefd);
+ }
+ return n;
+}
+
+static void
+powerintr(Ureg*, void*)
+{
+ wakeup(&powerevent);
+}
+
+static void
+cmdack(int id, void *a, int n)
+{
+ uchar reply[16];
+
+ cmdio(id, a, n, reply, sizeof(reply));
+}
+
+static int
+cmdio(int id, void *a, int n, void *reply, int lim)
+{
+ qlock(&atmel);
+ if(waserror()){
+ qunlock(&atmel);
+ nexterror();
+ }
+ n = wrcmd(id, a, n, reply, lim);
+ poperror();
+ qunlock(&atmel);
+ return n;
+}
+
+static int
+havereply(void*)
+{
+ return atmel.reply != nil;
+}
+
+static int
+wrcmd(int id, void *a, int n, void *b, int lim)
+{
+ uchar buf[32];
+ int i, sum;
+ Block *e;
+
+ if(n >= 16)
+ error(Eio);
+ lock(&atmel.rl);
+ atmel.cmd = id;
+ unlock(&atmel.rl);
+ buf[0] = Csof;
+ buf[1] = (id<<4) | (n&0xF);
+ if(n)
+ memmove(buf+2, a, n);
+ sum = 0;
+ for(i=1; i<n+2; i++)
+ sum += buf[i];
+ buf[i++] = sum;
+ if(0){
+ iprint("msg=");
+ for(sum=0; sum<i; sum++)
+ iprint(" %2.2ux", buf[sum]);
+ iprint("\n");
+ }
+ if(kchanio(atmel.c, buf, i, OWRITE) != i)
+ error(Eio);
+ tsleep(&atmel.r, havereply, nil, 500);
+ lock(&atmel.rl);
+ e = atmel.reply;
+ atmel.reply = nil;
+ atmel.cmd = -1;
+ unlock(&atmel.rl);
+ if(e == nil){
+ print("ipaq: no reply\n");
+ error(Eio);
+ }
+ if(waserror()){
+ freeb(e);
+ nexterror();
+ }
+ if(e->rp[0] != id){
+ print("ipaq: rdreply: mismatched reply %d :: %d\n", id, e->rp[0]);
+ error(Eio);
+ }
+ n = BLEN(e);
+ if(n < lim)
+ lim = n;
+ memmove(b, e->rp, lim);
+ poperror();
+ freeb(e);
+ return lim;
+}
+
+static void
+ipaqreadproc(void*)
+{
+ Block *e, *b, *partial;
+ int c, mousemod;
+
+ while(waserror())
+ print("ipaqread: %r\n");
+ partial = nil;
+ mousemod = 0;
+ for(;;){
+ e = rdevent(&partial);
+ if(e == nil){
+ print("ipaqread: rdevent: %r\n");
+ continue;
+ }
+ switch(e->rp[0]){
+ case Otouch:
+ touched(e, mousemod);
+ freeb(e);
+ break;
+ case Okeys:
+ //print("key %2.2ux\n", e->rp[1]);
+ c = e->rp[1] & 0xF;
+ if(c >= 6 && c < 10){ /* rocker */
+ if((e->rp[1] & 0x80) == 0){
+ kbdrepeat(0);
+ kbdputc(kbdq, rockermap[conf.portrait&1][c-6]);
+ }else
+ kbdrepeat(0);
+ }else{
+ /* TO DO: change tkmouse and mousetrack to allow extra buttons */
+ if(--c == 0)
+ c = 5;
+ if(e->rp[1] & 0x80)
+ mousemod &= ~(1<<c);
+ else
+ mousemod |= 1<<c;
+ }
+ freeb(e);
+ break;
+ default:
+ lock(&atmel.rl);
+ if(atmel.cmd == e->rp[0]){
+ b = atmel.reply;
+ atmel.reply = e;
+ unlock(&atmel.rl);
+ wakeup(&atmel.r);
+ if(b != nil)
+ freeb(b);
+ }else{
+ unlock(&atmel.rl);
+ print("ipaqread: discard op %d\n", e->rp[0]);
+ freeb(e);
+ }
+ }
+ }
+}
+
+static Block *
+rdevent(Block **bp)
+{
+ Block *b, *e;
+ int s, c, len, csum;
+ enum {Ssof=16, Sid, Ssum};
+
+ s = Ssof;
+ csum = 0;
+ len = 0;
+ e = nil;
+ if(waserror()){
+ if(e != nil)
+ freeb(e);
+ nexterror();
+ }
+ for(;;){
+ b = *bp;
+ *bp = nil;
+ if(b == nil){
+ b = devtab[atmel.c->type]->bread(atmel.c, 128, 0);
+ if(b == nil)
+ error(Eio);
+ if(DEBUG)
+ iprint("r: %ld\n", BLEN(b));
+ }
+ while(b->rp < b->wp){
+ c = *b->rp++;
+ switch(s){
+ case Ssof:
+ if(c == Csof)
+ s = Sid;
+ else if(1)
+ iprint("!sof: %2.2ux %d\n", c, s);
+ break;
+ case Sid:
+ csum = c;
+ len = c & 0xF;
+ e = allocb(len+1);
+ if(e == nil)
+ error(Eio);
+ *e->wp++ = c>>4; /* id */
+ if(len)
+ s = 0;
+ else
+ s = Ssum;
+ break;
+ case Ssum:
+ csum &= 0xFF;
+ if(c != csum){
+ iprint("cksum: %2.2ux != %2.2ux\n", c, csum);
+ s = Ssof; /* try to resynchronise */
+ if(e != nil){
+ freeb(e);
+ e = nil;
+ }
+ break;
+ }
+ if(b->rp < b->wp)
+ *bp = b;
+ else
+ freeb(b);
+ if(DEBUG){
+ int i;
+ iprint("event: [%ld]", BLEN(e));
+ for(i=0; i<BLEN(e);i++)
+ iprint(" %2.2ux", e->rp[i]);
+ iprint("\n");
+ }
+ poperror();
+ return e;
+ default:
+ csum += c;
+ *e->wp++ = c;
+ if(++s >= len)
+ s = Ssum;
+ break;
+ }
+ }
+ freeb(b);
+ }
+ return 0; /* not reached */
+}
+
+static char *
+acstatus(int x)
+{
+ switch(x){
+ case 0: return "offline";
+ case 1: return "online";
+ case 2: return "backup";
+ }
+ return "unknown";
+}
+
+static char *
+batstatus(int x)
+{
+ if(x & 0x40)
+ return "charging"; /* not in linux but seems to be on mine */
+ switch(x){
+ case 0: return "ok";
+ case 1: return "high";
+ case 2: return "low";
+ case 4: return "critical";
+ case 8: return "charging";
+ case 0x80: return "none";
+ }
+ return "unknown";
+}
+
+static int
+ptmap(int *m, int x, int y)
+{
+ return XF(m[0]*x + m[1]*y + m[2]);
+}
+
+static void
+touched(Block *b, int buttons)
+{
+ int rx, ry, x, y, dx, dy, n;
+ Point op, *lp, cur;
+
+ if(BLEN(b) == 5){
+ /* id Xhi Xlo Yhi Ylo */
+ if(touch.down < 0){
+ touch.down = 0;
+ return;
+ }
+ rx = (b->rp[1]<<8)|b->rp[2];
+ ry = (b->rp[3]<<8)|b->rp[4];
+ if(conf.portrait){
+ dx = rx; rx = ry; ry = dx;
+ }
+ if(touch.down == 0){
+ touch.nout = 0;
+ touch.p = 1;
+ touch.n = 1;
+ touch.avg = Pt(rx, ry);
+ touch.pts[0] = touch.avg;
+ touch.down = 1;
+ return;
+ }
+ n = touch.p-1;
+ if(n < 0)
+ n = nelem(touch.pts)-1;
+ lp = &touch.pts[n]; /* last point */
+ if(touch.n > 0 && (rx-lp->x)*(ry-lp->y) > 50*50){ /* far out */
+ if(++touch.nout > 3){
+ touch.down = 0;
+ touch.n = 0;
+ }
+ return;
+ }
+ op = touch.pts[touch.p];
+ touch.pts[touch.p] = Pt(rx, ry);
+ touch.p = (touch.p+1) % nelem(touch.pts);
+ touch.avg.x += rx;
+ touch.avg.y += ry;
+ if(touch.n < nelem(touch.pts)){
+ touch.n++;
+ return;
+ }
+ touch.avg.x -= op.x;
+ touch.avg.y -= op.y;
+ cur = mousexy();
+ rx = touch.avg.x/touch.n;
+ ry = touch.avg.y/touch.n;
+ x = ptmap(touch.m[0], rx, ry);
+ dx = x-cur.x;
+ y = ptmap(touch.m[1], rx, ry);
+ dy = y-cur.y;
+ if(dx*dx + dy*dy <= 2){
+ dx = 0;
+ dy = 0;
+ }
+ if(buttons == 0)
+ buttons = 1<<0; /* by default, stylus down implies button 1 */
+ mousetrack(buttons&0x1f, dx, dy, 1); /* TO DO: allow more than 3 buttons */
+ /* TO DO: swcursupdate(oldx, oldy, x, y); */
+ touch.down = 1;
+ }else{
+ if(touch.down){
+ mousetrack(0, 0, 0, 1); /* stylus up */
+ touch.down = 0;
+ }else
+ touch.down = -1;
+ touch.n = 0;
+ touch.p = 0;
+ touch.avg.x = 0;
+ touch.avg.y = 0;
+ }
+}
+
+/*
+ * touchctl commands:
+ * X a b c - set X transformation
+ * Y d e f - set Y transformation
+ * s<delay> - set sample delay in millisec per sample
+ * r<delay> - set read delay in microsec
+ * R<l2nr> - set log2 of number of readings to average
+ */
+static long
+touchctl(char* a, long n)
+{
+ char buf[64];
+ char *cp;
+ int n0 = n;
+ int bn;
+ char *field[8];
+ int nf, cmd, pn, m[2][3];
+
+ while(n) {
+ bn = (cp = memchr(a, '\n', n))!=nil ? cp-a+1 : n;
+ n -= bn;
+ cp = a;
+ a += bn;
+ bn = bn > sizeof(buf)-1 ? sizeof(buf)-1 : bn;
+ memmove(buf, cp, bn);
+ buf[bn] = '\0';
+ nf = getfields(buf, field, nelem(field), 1, " \t\n");
+ if(nf <= 0)
+ continue;
+ if(strcmp(field[0], "calibrate") == 0){
+ if(nf == 1){
+ lock(&touch);
+ memset(touch.m, 0, sizeof(touch.m));
+ touch.m[0][0] = FX(1,1);
+ touch.m[1][1] = FX(1,1);
+ unlock(&touch);
+ }else if(nf >= 5){
+ memset(m, 0, sizeof(m));
+ m[0][0] = strtol(field[1], 0, 0);
+ m[1][1] = strtol(field[2], 0, 0);
+ m[0][2] = strtol(field[3], 0, 0);
+ m[1][2] = strtol(field[4], 0, 0);
+ if(nf > 5)
+ m[0][1] = strtol(field[5], 0, 0);
+ if(nf > 6)
+ m[1][0] = strtol(field[6], 0, 0);
+ lock(&touch);
+ memmove(touch.m, m, sizeof(touch.m[0]));
+ unlock(&touch);
+ }else
+ error(Ebadarg);
+ continue;
+ }
+ cmd = *field[0]++;
+ pn = *field[0] == 0;
+ switch(cmd) {
+ case 's':
+ pn = strtol(field[pn], 0, 0);
+ if(pn <= 0)
+ error(Ebadarg);
+ touch.rate = pn;
+ break;
+ case 'r':
+ /* touch read delay */
+ break;
+ case 'X':
+ case 'Y':
+ if(nf < pn+2)
+ error(Ebadarg);
+ m[0][0] = strtol(field[pn], 0, 0);
+ m[0][1] = strtol(field[pn+1], 0, 0);
+ m[0][2] = strtol(field[pn+2], 0, 0);
+ lock(&touch);
+ memmove(touch.m[cmd=='Y'], m[0], sizeof(touch.m[0]));
+ unlock(&touch);
+ break;
+ default:
+ error(Ebadarg);
+ }
+ }
+ return n0-n;
+}
+
+/*
+ * this might belong elsewhere
+ */
+static int
+powerwait(void*)
+{
+ return (GPIOREG->gplr & GPIO_PWR_ON_i) == 0;
+}
+
+static void
+powerwaitproc(void*)
+{
+ for(;;){
+ sleep(&powerevent, powerwait, nil);
+ do{
+ tsleep(&up->sleep, return0, nil, 50);
+ }while((GPIOREG->gplr & GPIO_PWR_ON_i) == 0);
+ powersuspend();
+ }
+}
+
+Dev ipaqdevtab = {
+ 'T',
+ "ipaq",
+
+ ipaqreset,
+ ipaqinit,
+ devshutdown,
+ ipaqattach,
+ ipaqwalk,
+ ipaqstat,
+ ipaqopen,
+ devcreate,
+ ipaqclose,
+ ipaqread,
+ devbread,
+ ipaqwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
diff --git a/os/ipaq1110/etherwavelan.c b/os/ipaq1110/etherwavelan.c
new file mode 100644
index 00000000..519e40f2
--- /dev/null
+++ b/os/ipaq1110/etherwavelan.c
@@ -0,0 +1,1307 @@
+/*
+ Lucent Wavelan IEEE 802.11 pcmcia.
+ There is almost no documentation for the card.
+ the driver is done using both the FreeBSD, Linux and
+ original Plan 9 drivers as `documentation'.
+
+ Has been used with the card plugged in during all up time.
+ no cards removals/insertions yet.
+
+ For known BUGS see the comments below. Besides,
+ the driver keeps interrupts disabled for just too
+ long. When it gets robust, locks should be revisited.
+
+ BUGS: check endian, alignment and mem/io issues;
+ multicast;
+ receive watchdog interrupts.
+ TODO: automatic power management;
+ improve locking.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+#include "etherif.h"
+
+#define print iprint
+#define DEBUG if(1)iprint
+
+#define SEEKEYS 1
+
+typedef struct Ctlr Ctlr;
+typedef struct Wltv Wltv;
+typedef struct WFrame WFrame;
+typedef struct Stats Stats;
+typedef struct WStats WStats;
+typedef struct WKey WKey;
+
+struct WStats
+{
+ ulong ntxuframes; // unicast frames
+ ulong ntxmframes; // multicast frames
+ ulong ntxfrags; // fragments
+ ulong ntxubytes; // unicast bytes
+ ulong ntxmbytes; // multicast bytes
+ ulong ntxdeferred; // deferred transmits
+ ulong ntxsretries; // single retries
+ ulong ntxmultiretries; // multiple retries
+ ulong ntxretrylimit;
+ ulong ntxdiscards;
+ ulong nrxuframes; // unicast frames
+ ulong nrxmframes; // multicast frames
+ ulong nrxfrags; // fragments
+ ulong nrxubytes; // unicast bytes
+ ulong nrxmbytes; // multicast bytes
+ ulong nrxfcserr;
+ ulong nrxdropnobuf;
+ ulong nrxdropnosa;
+ ulong nrxcantdecrypt;
+ ulong nrxmsgfrag;
+ ulong nrxmsgbadfrag;
+ ulong end;
+};
+
+struct WFrame
+{
+ ushort sts;
+ ushort rsvd0;
+ ushort rsvd1;
+ ushort qinfo;
+ ushort rsvd2;
+ ushort rsvd3;
+ ushort txctl;
+ ushort framectl;
+ ushort id;
+ uchar addr1[Eaddrlen];
+ uchar addr2[Eaddrlen];
+ uchar addr3[Eaddrlen];
+ ushort seqctl;
+ uchar addr4[Eaddrlen];
+ ushort dlen;
+ uchar dstaddr[Eaddrlen];
+ uchar srcaddr[Eaddrlen];
+ ushort len;
+ ushort dat[3];
+ ushort type;
+};
+
+// Lucent's Length-Type-Value records to talk to the wavelan.
+// most operational parameters are read/set using this.
+enum
+{
+ WTyp_Stats = 0xf100,
+ WTyp_Ptype = 0xfc00,
+ WTyp_Mac = 0xfc01,
+ WTyp_WantName = 0xfc02,
+ WTyp_Chan = 0xfc03,
+ WTyp_NetName = 0xfc04,
+ WTyp_ApDens = 0xfc06,
+ WTyp_MaxLen = 0xfc07,
+ WTyp_PM = 0xfc09,
+ WTyp_PMWait = 0xfc0c,
+ WTyp_NodeName = 0xfc0e,
+ WTyp_Crypt = 0xfc20,
+ WTyp_XClear = 0xfc22,
+ WTyp_Tick = 0xfce0,
+ WTyp_RtsThres = 0xfc83,
+ WTyp_TxRate = 0xfc84,
+ WTx1Mbps = 0x0,
+ WTx2Mbps = 0x1,
+ WTxAuto = 0x3,
+ WTyp_Prom = 0xfc85,
+ WTyp_Keys = 0xfcb0,
+ WTyp_TxKey = 0xfcb1,
+ WTyp_StationID = 0xfd20,
+ WTyp_CurName = 0xfd41,
+ WTyp_BaseID = 0xfd42, // ID of the currently connected-to base station
+ WTyp_CurTxRate = 0xfd44, // Current TX rate
+ WTyp_HasCrypt = 0xfd4f,
+};
+
+// Controller
+enum
+{
+ WDfltIRQ = 3, // default irq
+ WDfltIOB = 0x180, // default IO base
+
+ WIOLen = 0x40, // Hermes IO length
+
+ WTmOut = 65536, // Cmd time out
+
+ WPTypePeerToPeer = 0,
+ WPTypeManaged = 1,
+ WPTypeWDS = 2,
+ WPTypeAdHoc = 3,
+ WDfltPType = WPTypeManaged,
+
+ WDfltApDens = 1,
+ WDfltRtsThres = 2347, // == disabled
+ WDfltTxRate = WTxAuto, // 2Mbps
+
+ WMaxLen = 2304,
+ WNameLen = 32,
+
+ WNKeys = 4,
+ WKeyLen = 14,
+ WMinKeyLen = 5,
+
+ // Wavelan hermes registers
+ WR_Cmd = 0x00,
+ WCmdIni = 0x0000,
+ WCmdEna = 0x0001,
+ WCmdDis = 0x0002,
+ WCmdTx = 0x000b,
+ WCmdMalloc = 0x000a,
+ WCmdAskStats = 0x0011,
+ WCmdMsk = 0x003f,
+ WCmdAccRd = 0x0021,
+ WCmdReclaim = 0x0100,
+ WCmdAccWr = 0x0121,
+ WCmdBusy = 0x8000,
+ WR_Parm0 = 0x02,
+ WR_Parm1 = 0x04,
+ WR_Parm2 = 0x06,
+ WR_Sts = 0x08,
+ WR_InfoId = 0x10,
+ WR_Sel0 = 0x18,
+ WR_Sel1 = 0x1a,
+ WR_Off0 = 0x1c,
+ WR_Off1 = 0x1e,
+ WBusyOff = 0x8000,
+ WErrOff = 0x4000,
+ WResSts = 0x7f00,
+ WR_RXId = 0x20,
+ WR_Alloc = 0x22,
+ WR_EvSts = 0x30,
+ WR_IntEna = 0x32,
+ WCmdEv = 0x0010,
+ WRXEv = 0x0001,
+ WTXEv = 0x0002,
+ WTxErrEv = 0x0004,
+ WAllocEv = 0x0008,
+ WInfoEv = 0x0080,
+ WIDropEv = 0x2000,
+ WTickEv = 0x8000,
+ WEvs = WRXEv|WTXEv|WAllocEv|WInfoEv|WIDropEv,
+
+ WR_EvAck = 0x34,
+ WR_Data0 = 0x36,
+ WR_Data1 = 0x38,
+
+ // Frame stuff
+
+ WF_Err = 0x0003,
+ WF_1042 = 0x2000,
+ WF_Tunnel = 0x4000,
+ WF_WMP = 0x6000,
+
+ WF_Data = 0x0008,
+
+ WSnapK1 = 0xaa,
+ WSnapK2 = 0x00,
+ WSnapCtlr = 0x03,
+ WSnap0 = (WSnapK1|(WSnapK1<<8)),
+ WSnap1 = (WSnapK2|(WSnapCtlr<<8)),
+ WSnapHdrLen = 6,
+
+ WF_802_11_Off = 0x44,
+ WF_802_3_Off = 0x2e,
+
+};
+
+#define csr_outs(ctlr,r,arg) outs((ctlr)->iob+(r),(arg))
+#define csr_ins(ctlr,r) ins((ctlr)->iob+(r))
+#define csr_ack(ctlr,ev) outs((ctlr)->iob+WR_EvAck,(ev))
+
+struct WKey
+{
+ ushort len;
+ char dat[WKeyLen];
+};
+
+struct Wltv
+{
+ ushort len;
+ ushort type;
+ union
+ {
+ struct {
+ ushort val;
+ ushort pad;
+ };
+ struct {
+ uchar addr[8];
+ };
+ struct {
+ ushort slen;
+ char s[WNameLen];
+ };
+ struct {
+ char name[WNameLen];
+ };
+ struct {
+ WKey keys[WNKeys];
+ };
+ };
+};
+
+// What the driver thinks. Not what the card thinks.
+struct Stats
+{
+ ulong nints;
+ ulong nrx;
+ ulong ntx;
+ ulong ntxrq;
+ ulong nrxerr;
+ ulong ntxerr;
+ ulong nalloc; // allocation (reclaim) events
+ ulong ninfo;
+ ulong nidrop;
+ ulong nwatchdogs; // transmit time outs, actually
+ int ticks;
+ int tickintr;
+ int signal;
+ int noise;
+};
+
+struct Ctlr
+{
+ Lock;
+ Rendez timer;
+
+ int attached;
+ int slot;
+ int iob;
+ int ptype;
+ int apdensity;
+ int rtsthres;
+ int txbusy;
+ int txrate;
+ int txdid;
+ int txmid;
+ int txtmout;
+ int maxlen;
+ int chan;
+ int pmena;
+ int pmwait;
+
+ char netname[WNameLen];
+ char wantname[WNameLen];
+ char nodename[WNameLen];
+ WFrame txf;
+ uchar txbuf[1536];
+
+ int hascrypt; // card has encryption
+ int crypt; // encryption off/on
+ int txkey; // transmit key
+ Wltv keys; // default keys
+ int xclear; // exclude clear packets off/on
+
+ Stats;
+ WStats;
+};
+
+// w_... routines do not ilock the Ctlr and should
+// be called locked.
+
+static void
+w_intdis(Ctlr* ctlr)
+{
+ csr_outs(ctlr, WR_IntEna, 0);
+ csr_ack(ctlr, 0xffff);
+}
+
+static void
+w_intena(Ctlr* ctlr)
+{
+ csr_outs(ctlr, WR_IntEna, WEvs);
+}
+
+static int
+w_cmd(Ctlr *ctlr, ushort cmd, ushort arg)
+{
+ int i, rc;
+
+ csr_outs(ctlr, WR_Parm0, arg);
+ csr_outs(ctlr, WR_Cmd, cmd);
+ for (i = 0; i<WTmOut; i++){
+ rc = csr_ins(ctlr, WR_EvSts);
+ if ( rc&WCmdEv ){
+ rc = csr_ins(ctlr, WR_Sts);
+ csr_ack(ctlr, WCmdEv);
+ if ((rc&WCmdMsk) != (cmd&WCmdMsk))
+ break;
+ if (rc&WResSts)
+ break;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int
+w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan)
+{
+ int i, rc;
+ static ushort sel[] = { WR_Sel0, WR_Sel1 };
+ static ushort off[] = { WR_Off0, WR_Off1 };
+
+ if (chan != 0 && chan != 1)
+ panic("wavelan: bad chan\n");
+ csr_outs(ctlr, sel[chan], id);
+ csr_outs(ctlr, off[chan], offset);
+ for (i=0; i<WTmOut; i++){
+ rc = csr_ins(ctlr, off[chan]);
+ if ((rc & (WBusyOff|WErrOff)) == 0)
+ return 0;
+ }
+ return -1;
+}
+
+static int
+w_inltv(Ctlr* ctlr, Wltv* ltv)
+{
+ int len;
+ ushort code;
+
+ if (w_cmd(ctlr, WCmdAccRd, ltv->type)){
+ DEBUG("wavelan: access read failed\n");
+ return -1;
+ }
+ if (w_seek(ctlr,ltv->type,0,1)){
+ DEBUG("wavelan: seek failed\n");
+ return -1;
+ }
+ len = csr_ins(ctlr, WR_Data1);
+ if (len > ltv->len)
+ return -1;
+ ltv->len = len;
+ if ((code=csr_ins(ctlr, WR_Data1)) != ltv->type){
+ DEBUG("wavelan: type %x != code %x\n",ltv->type,code);
+ return -1;
+ }
+ if(ltv->len > 0)
+ inss((ctlr)->iob+(WR_Data1), &ltv->val, ltv->len-1);
+
+ return 0;
+}
+
+static void
+w_outltv(Ctlr* ctlr, Wltv* ltv)
+{
+ if(w_seek(ctlr,ltv->type, 0, 1))
+ return;
+ outss((ctlr)->iob+(WR_Data1), ltv, ltv->len+1);
+ w_cmd(ctlr, WCmdAccWr, ltv->type);
+}
+
+static void
+ltv_outs(Ctlr* ctlr, int type, ushort val)
+{
+ Wltv ltv;
+
+ ltv.len = 2;
+ ltv.type = type;
+ ltv.val = val;
+ w_outltv(ctlr, &ltv);
+}
+
+static int
+ltv_ins(Ctlr* ctlr, int type)
+{
+ Wltv ltv;
+
+ ltv.len = 2;
+ ltv.type = type;
+ ltv.val = 0;
+ if(w_inltv(ctlr, &ltv))
+ return -1;
+ return ltv.val;
+}
+
+static void
+ltv_outstr(Ctlr* ctlr, int type, char* val)
+{
+ Wltv ltv;
+ int len;
+
+ len = strlen(val);
+ if(len > sizeof(ltv.s))
+ len = sizeof(ltv.s);
+ memset(&ltv, 0, sizeof(ltv));
+ ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2;
+ ltv.type = type;
+
+// This should be ltv.slen = len; according to Axel Belinfante
+ ltv.slen = len;
+
+ strncpy(ltv.s, val, len);
+ w_outltv(ctlr, &ltv);
+}
+
+static char Unkname[] = "who knows";
+static char Nilname[] = "card does not tell";
+
+static char*
+ltv_inname(Ctlr* ctlr, int type)
+{
+ static Wltv ltv;
+ int len;
+
+ memset(&ltv,0,sizeof(ltv));
+ ltv.len = WNameLen/2+2;
+ ltv.type = type;
+ if (w_inltv(ctlr, &ltv))
+ return Unkname;
+ len = ltv.slen;
+ if(len == 0 || ltv.s[0] == 0)
+ return Nilname;
+ if(len >= sizeof ltv.s)
+ len = sizeof ltv.s - 1;
+ ltv.s[len] = '\0';
+ return ltv.s;
+}
+
+static int
+w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len)
+{
+ if (w_seek(ctlr, type, off, 1)){
+ DEBUG("wavelan: w_read: seek failed");
+ return 0;
+ }
+ inss((ctlr)->iob+(WR_Data1), buf, len/2);
+
+ return len;
+}
+
+static int
+w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len)
+{
+ int tries;
+
+ for (tries=0; tries < WTmOut; tries++){
+ if (w_seek(ctlr, type, off, 0)){
+ DEBUG("wavelan: w_write: seek failed\n");
+ return 0;
+ }
+
+ outss((ctlr)->iob+(WR_Data0), buf, len/2);
+
+ csr_outs(ctlr, WR_Data0, 0xdead);
+ csr_outs(ctlr, WR_Data0, 0xbeef);
+ if (w_seek(ctlr, type, off + len, 0)){
+ DEBUG("wavelan: write seek failed\n");
+ return 0;
+ }
+ if (csr_ins(ctlr, WR_Data0) == 0xdead)
+ if (csr_ins(ctlr, WR_Data0) == 0xbeef)
+ return len;
+ DEBUG("wavelan: Hermes bug byte.\n");
+ return 0;
+ }
+ DEBUG("wavelan: tx timeout\n");
+ return 0;
+}
+
+static int
+w_alloc(Ctlr* ctlr, int len)
+{
+ int rc;
+ int i,j;
+
+ if (w_cmd(ctlr, WCmdMalloc, len)==0)
+ for (i = 0; i<WTmOut; i++)
+ if (csr_ins(ctlr, WR_EvSts) & WAllocEv){
+ csr_ack(ctlr, WAllocEv);
+ rc=csr_ins(ctlr, WR_Alloc);
+ if (w_seek(ctlr, rc, 0, 0))
+ return -1;
+ len = len/2;
+ for (j=0; j<len; j++)
+ csr_outs(ctlr, WR_Data0, 0);
+ return rc;
+ }
+ return -1;
+}
+
+static int
+w_enable(Ether* ether)
+{
+ Wltv ltv;
+ Ctlr* ctlr = (Ctlr*) ether->ctlr;
+
+ if (!ctlr)
+ return -1;
+
+ w_intdis(ctlr);
+ w_cmd(ctlr, WCmdDis, 0);
+ w_intdis(ctlr);
+ if(w_cmd(ctlr, WCmdIni, 0))
+ return -1;
+ w_intdis(ctlr);
+
+ ltv_outs(ctlr, WTyp_Tick, 8);
+ ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen);
+ ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype);
+ ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres);
+ ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate);
+ ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity);
+ ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait);
+ ltv_outs(ctlr, WTyp_PM, ctlr->pmena);
+ if (*ctlr->netname)
+ ltv_outstr(ctlr, WTyp_NetName, ctlr->netname);
+ if (*ctlr->wantname)
+ ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname);
+ ltv_outs(ctlr, WTyp_Chan, ctlr->chan);
+ if (*ctlr->nodename)
+ ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename);
+ ltv.len = 4;
+ ltv.type = WTyp_Mac;
+ memmove(ltv.addr, ether->ea, Eaddrlen);
+ w_outltv(ctlr, &ltv);
+
+ ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0));
+
+ if (ctlr->hascrypt){
+ ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt);
+ ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey);
+ w_outltv(ctlr, &ctlr->keys);
+ ltv_outs(ctlr, WTyp_XClear, ctlr->xclear);
+ }
+
+ // BUG: set multicast addresses
+
+ if (w_cmd(ctlr, WCmdEna, 0)){
+ DEBUG("wavelan: Enable failed");
+ return -1;
+ }
+ ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
+ ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
+ if (ctlr->txdid == -1 || ctlr->txmid == -1)
+ DEBUG("wavelan: alloc failed");
+ ctlr->txbusy = 0;
+ w_intena(ctlr);
+ return 0;
+}
+
+static void
+w_rxdone(Ether* ether)
+{
+ Ctlr* ctlr = (Ctlr*) ether->ctlr;
+ int len, sp;
+ WFrame f;
+ Block* bp=0;
+ Etherpkt* ep;
+
+ sp = csr_ins(ctlr, WR_RXId);
+ len = w_read(ctlr, sp, 0, &f, sizeof(f));
+ if (len == 0){
+ DEBUG("wavelan: read frame error\n");
+ goto rxerror;
+ }
+ if (f.sts&WF_Err){
+ goto rxerror;
+ }
+ switch(f.sts){
+ case WF_1042:
+ case WF_Tunnel:
+ case WF_WMP:
+ len = f.dlen + WSnapHdrLen;
+ bp = iallocb(ETHERHDRSIZE + len + 2);
+ if (!bp)
+ goto rxerror;
+ ep = (Etherpkt*) bp->wp;
+ memmove(ep->d, f.addr1, Eaddrlen);
+ memmove(ep->s, f.addr2, Eaddrlen);
+ memmove(ep->type,&f.type,2);
+ bp->wp += ETHERHDRSIZE;
+ if (w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){
+ DEBUG("wavelan: read 802.11 error\n");
+ goto rxerror;
+ }
+ bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen);
+ break;
+ default:
+ len = ETHERHDRSIZE + f.dlen + 2;
+ bp = iallocb(len);
+ if (!bp)
+ goto rxerror;
+ if (w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){
+ DEBUG("wavelan: read 800.3 error\n");
+ goto rxerror;
+ }
+ bp->wp += len;
+ }
+
+ ctlr->nrx++;
+ etheriq(ether,bp,1);
+ ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16;
+ ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16;
+ return;
+
+rxerror:
+ freeb(bp);
+ ctlr->nrxerr++;
+}
+
+static void
+w_txstart(Ether* ether)
+{
+ Etherpkt *pkt;
+ Ctlr *ctlr;
+ Block *bp;
+ int len, off;
+
+ if((ctlr = ether->ctlr) == nil || ctlr->attached == 0 || ctlr->txbusy)
+ return;
+
+ if((bp = qget(ether->oq)) == nil)
+ return;
+ pkt = (Etherpkt*)bp->rp;
+
+ //
+ // If the packet header type field is > 1500 it is an IP or
+ // ARP datagram, otherwise it is an 802.3 packet. See RFC1042.
+ //
+ memset(&ctlr->txf, 0, sizeof(ctlr->txf));
+ if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){
+ ctlr->txf.framectl = WF_Data;
+ memmove(ctlr->txf.addr1, pkt->d, Eaddrlen);
+ memmove(ctlr->txf.addr2, pkt->s, Eaddrlen);
+ memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen);
+ memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen);
+ memmove(&ctlr->txf.type, pkt->type, 2);
+ bp->rp += ETHERHDRSIZE;
+ len = BLEN(bp);
+ off = WF_802_11_Off;
+ ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen;
+ hnputs((uchar*)&ctlr->txf.dat[0], WSnap0);
+ hnputs((uchar*)&ctlr->txf.dat[1], WSnap1);
+ hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen);
+ }
+ else{
+ len = BLEN(bp);
+ off = WF_802_3_Off;
+ ctlr->txf.dlen = len;
+ }
+ w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf));
+ w_write(ctlr, ctlr->txdid, off, bp->rp, len+2);
+
+ if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){
+ DEBUG("wavelan: transmit failed\n");
+ ctlr->ntxerr++;
+ }
+ else{
+ ctlr->txbusy = 1;
+ ctlr->txtmout = 2;
+ }
+ freeb(bp);
+}
+
+static void
+w_txdone(Ctlr* ctlr, int sts)
+{
+ ctlr->txbusy = 0;
+ ctlr->txtmout = 0;
+ if (sts & WTxErrEv)
+ ctlr->ntxerr++;
+ else
+ ctlr->ntx++;
+}
+
+static int
+w_stats(Ctlr* ctlr)
+{
+ int i, rc, sp;
+ Wltv ltv;
+ ulong* p = (ulong*)&ctlr->WStats;
+ ulong* pend = (ulong*)&ctlr->end;
+
+ sp = csr_ins(ctlr, WR_InfoId);
+ ltv.len = ltv.type = 0;
+ w_read(ctlr, sp, 0, &ltv, 4);
+ if (ltv.type == WTyp_Stats){
+ ltv.len--;
+ for (i = 0; i < ltv.len && p < pend; i++){
+ rc = csr_ins(ctlr, WR_Data1);
+ if (rc > 0xf000)
+ rc = ~rc & 0xffff;
+ p[i] += rc;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+static void
+w_intr(Ether *ether)
+{
+ int rc, txid;
+ Ctlr* ctlr = (Ctlr*) ether->ctlr;
+
+ if (ctlr->attached == 0){
+ csr_ack(ctlr, 0xffff);
+ csr_outs(ctlr, WR_IntEna, 0);
+ return;
+ }
+
+ csr_outs(ctlr, WR_IntEna, 0);
+ rc = csr_ins(ctlr, WR_EvSts);
+ csr_ack(ctlr, ~WEvs); // Not interested on them
+
+ if (rc & WRXEv){
+ w_rxdone(ether);
+ csr_ack(ctlr, WRXEv);
+ }
+ if (rc & WTXEv){
+ w_txdone(ctlr, rc);
+ csr_ack(ctlr, WTXEv);
+ }
+ if (rc & WAllocEv){
+ ctlr->nalloc++;
+ txid = csr_ins(ctlr, WR_Alloc);
+ csr_ack(ctlr, WAllocEv);
+ if (txid == ctlr->txdid){
+ if ((rc & WTXEv) == 0)
+ w_txdone(ctlr, rc);
+ }
+ }
+ if (rc & WInfoEv){
+ ctlr->ninfo++;
+ w_stats(ctlr);
+ csr_ack(ctlr, WInfoEv);
+ }
+ if (rc & WTxErrEv){
+ w_txdone(ctlr, rc);
+ csr_ack(ctlr, WTxErrEv);
+ }
+ if (rc & WIDropEv){
+ ctlr->nidrop++;
+ csr_ack(ctlr, WIDropEv);
+ }
+
+ w_intena(ctlr);
+ w_txstart(ether);
+}
+
+// Watcher to ensure that the card still works properly and
+// to request WStats updates once a minute.
+// BUG: it runs much more often, see the comment below.
+
+static void
+w_timer(void* arg)
+{
+ Ether* ether = (Ether*) arg;
+ Ctlr* ctlr = (Ctlr*)ether->ctlr;
+
+ for(;;){
+ tsleep(&ctlr->timer, return0, 0, 50);
+ ctlr = (Ctlr*)ether->ctlr;
+ if (ctlr == 0)
+ break;
+ if (ctlr->attached == 0)
+ continue;
+ ctlr->ticks++;
+
+ ilock(ctlr);
+
+ // Seems that the card gets frames BUT does
+ // not send the interrupt; this is a problem because
+ // I suspect it runs out of receive buffers and
+ // stops receiving until a transmit watchdog
+ // reenables the card.
+ // The problem is serious because it leads to
+ // poor rtts.
+ // This can be seen clearly by commenting out
+ // the next if and doing a ping: it will stop
+ // receiving (although the icmp replies are being
+ // issued from the remote) after a few seconds.
+ // Of course this `bug' could be because I'm reading
+ // the card frames in the wrong way; due to the
+ // lack of documentation I cannot know.
+
+// if (csr_ins(ctlr, WR_EvSts)&WEvs){
+// ctlr->tickintr++;
+// w_intr(ether);
+// }
+
+ if ((ctlr->ticks % 10) == 0) {
+ if (ctlr->txtmout && --ctlr->txtmout == 0){
+ ctlr->nwatchdogs++;
+ w_txdone(ctlr, WTxErrEv);
+ if (w_enable(ether)){
+ DEBUG("wavelan: wdog enable failed\n");
+ }
+ w_txstart(ether);
+ }
+ if ((ctlr->ticks % 120) == 0)
+ if (ctlr->txbusy == 0)
+ w_cmd(ctlr, WCmdAskStats, WTyp_Stats);
+ }
+ iunlock(ctlr);
+ }
+ pexit("terminated",0);
+}
+
+static void
+multicast(void*, uchar*, int)
+{
+ // BUG: to be added.
+}
+
+static void
+attach(Ether* ether)
+{
+ Ctlr* ctlr;
+ char name[64];
+ int rc;
+
+ if (ether->ctlr == 0)
+ return;
+
+ snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
+ ctlr = (Ctlr*) ether->ctlr;
+ if (ctlr->attached == 0){
+ ilock(ctlr);
+ rc = w_enable(ether);
+ iunlock(ctlr);
+ if(rc == 0){
+ ctlr->attached = 1;
+ kproc(name, w_timer, ether, 0);
+ } else
+ print("#l%d: enable failed\n",ether->ctlrno);
+ }
+}
+
+#define PRINTSTAT(fmt,val) l += snprint(p+l, READSTR-l, (fmt), (val))
+#define PRINTSTR(fmt) l += snprint(p+l, READSTR-l, (fmt))
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+ Ctlr *ctlr = (Ctlr*) ether->ctlr;
+ char *k, *p;
+ int i, l, txid;
+
+ ether->oerrs = ctlr->ntxerr;
+ ether->crcs = ctlr->nrxfcserr;
+ ether->frames = 0;
+ ether->buffs = ctlr->nrxdropnobuf;
+ ether->overflows = 0;
+
+ //
+ // Offset must be zero or there's a possibility the
+ // new data won't match the previous read.
+ //
+ if(n == 0 || offset != 0)
+ return 0;
+
+ p = malloc(READSTR);
+ l = 0;
+
+ PRINTSTAT("Signal: %d\n", ctlr->signal-149);
+ PRINTSTAT("Noise: %d\n", ctlr->noise-149);
+ PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise);
+ PRINTSTAT("Interrupts: %lud\n", ctlr->nints);
+ PRINTSTAT("TxPackets: %lud\n", ctlr->ntx);
+ PRINTSTAT("RxPackets: %lud\n", ctlr->nrx);
+ PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr);
+ PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr);
+ PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq);
+ PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc);
+ PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo);
+ PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop);
+ PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs);
+ PRINTSTAT("Ticks: %ud\n", ctlr->ticks);
+ PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr);
+ k = ((ctlr->attached) ? "attached" : "not attached");
+ PRINTSTAT("Card %s", k);
+ k = ((ctlr->txbusy)? ", txbusy" : "");
+ PRINTSTAT("%s\n", k);
+
+ if (ctlr->hascrypt){
+ PRINTSTR("Keys: ");
+ for (i = 0; i < WNKeys; i++){
+ if (ctlr->keys.keys[i].len == 0)
+ PRINTSTR("none ");
+ else if (SEEKEYS == 0)
+ PRINTSTR("set ");
+ else
+ PRINTSTAT("%s ", ctlr->keys.keys[i].dat);
+ }
+ PRINTSTR("\n");
+ }
+
+ // real card stats
+ ilock(ctlr);
+ PRINTSTR("\nCard stats: \n");
+ PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts));
+ PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts));
+ i = ltv_ins(ctlr, WTyp_Ptype);
+ PRINTSTAT("Port type: %d\n", i);
+ PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate));
+ PRINTSTAT("Current Transmit rate: %d\n",
+ ltv_ins(ctlr, WTyp_CurTxRate));
+ PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan));
+ PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens));
+ PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom));
+ if(i == 3)
+ PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName));
+ else {
+ Wltv ltv;
+ PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName));
+ ltv.type = WTyp_BaseID;
+ ltv.len = 4;
+ if (w_inltv(ctlr, &ltv))
+ print("#l%d: unable to read base station mac addr\n", ether->ctlrno);
+ l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
+ ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]);
+ }
+ PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));
+ PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName));
+ if (ltv_ins(ctlr, WTyp_HasCrypt) == 0)
+ PRINTSTR("WEP: not supported\n");
+ else {
+ if (ltv_ins(ctlr, WTyp_Crypt) == 0)
+ PRINTSTR("WEP: disabled\n");
+ else{
+ PRINTSTR("WEP: enabled\n");
+ k = ((ctlr->xclear)? "excluded": "included");
+ PRINTSTAT("Clear packets: %s\n", k);
+ txid = ltv_ins(ctlr, WTyp_TxKey);
+ PRINTSTAT("Transmit key id: %d\n", txid);
+ }
+ }
+ iunlock(ctlr);
+
+ PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes);
+ PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes);
+ PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags);
+ PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes);
+ PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes);
+ PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred);
+ PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries);
+ PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries);
+ PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit);
+ PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards);
+ PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes);
+ PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes);
+ PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags);
+ PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes);
+ PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes);
+ PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr);
+ PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf);
+ PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa);
+ PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt);
+ PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag);
+ PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag);
+ USED(l);
+ n = readstr(offset, a, n, p);
+ free(p);
+ return n;
+}
+#undef PRINTSTR
+#undef PRINTSTAT
+
+static int
+w_option(Ctlr* ctlr, char* buf, long n)
+{
+ char *p;
+ int i, r;
+ WKey *key;
+ Cmdbuf *cb;
+
+ r = 0;
+
+ cb = parsecmd(buf, n);
+ if(cb->nf < 2)
+ r = -1;
+ else if(cistrcmp(cb->f[0], "essid") == 0){
+ if (cistrcmp(cb->f[1],"default") == 0)
+ p = "";
+ else
+ p = cb->f[1];
+ switch(ctlr->ptype){
+ case 0:
+ case 3:
+ memset(ctlr->netname, 0, sizeof(ctlr->netname));
+ strncpy(ctlr->netname, p, WNameLen);
+ if(ctlr->ptype == 3)
+ break;
+ /* fall through to set both for peer-to-peer */
+ default:
+ memset(ctlr->wantname, 0, sizeof(ctlr->wantname));
+ strncpy(ctlr->wantname, p, WNameLen);
+ break;
+ }
+ }
+ else if(cistrcmp(cb->f[0], "station") == 0){
+ memset(ctlr->nodename, 0, sizeof(ctlr->nodename));
+ strncpy(ctlr->nodename, cb->f[1], WNameLen);
+ }
+ else if(cistrcmp(cb->f[0], "channel") == 0){
+ if((i = atoi(cb->f[1])) >= 1 && i <= 16)
+ ctlr->chan = i;
+ else
+ r = -1;
+ }
+ else if(cistrcmp(cb->f[0], "mode") == 0){
+ if(cistrcmp(cb->f[1], "managed") == 0)
+ ctlr->ptype = WPTypeManaged;
+ else if(cistrcmp(cb->f[1], "wds") == 0)
+ ctlr->ptype = WPTypeWDS;
+ else if(cistrcmp(cb->f[1], "adhoc") == 0)
+ ctlr->ptype = WPTypeAdHoc;
+ else if(cistrcmp(cb->f[1], "peertopeer") == 0)
+ ctlr->ptype = WPTypePeerToPeer;
+ else if((i = atoi(cb->f[1])) >= 0 && i <= 3)
+ ctlr->ptype = i;
+ else
+ r = -1;
+ }
+ else if(cistrcmp(cb->f[0], "crypt") == 0){
+ if(cistrcmp(cb->f[1], "off") == 0)
+ ctlr->crypt = 0;
+ else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt)
+ ctlr->crypt = 1;
+ else
+ r = -1;
+ }
+ else if(cistrcmp(cb->f[0], "clear") == 0){
+ if(cistrcmp(cb->f[1], "on") == 0)
+ ctlr->xclear = 0;
+ else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt)
+ ctlr->xclear = 1;
+ else
+ r = -1;
+ }
+ else if(strncmp(cb->f[0], "key", 3) == 0){
+ if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){
+ ctlr->txkey = i-1;
+ key = &ctlr->keys.keys[ctlr->txkey];
+ key->len = strlen(cb->f[1]);
+ if (key->len > WKeyLen)
+ key->len = WKeyLen;
+ memset(key->dat, 0, sizeof(key->dat));
+ memmove(key->dat, cb->f[1], key->len);
+ }
+ else
+ r = -1;
+ }
+ else if(cistrcmp(cb->f[0], "txkey") == 0){
+ if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys)
+ ctlr->txkey = i-1;
+ else
+ r = -1;
+ }
+ else if(cistrcmp(cb->f[0], "pm") == 0){
+ if(cistrcmp(cb->f[1], "off") == 0)
+ ctlr->pmena = 0;
+ else if(cistrcmp(cb->f[1], "on") == 0){
+ ctlr->pmena = 1;
+ if(cb->nf == 3){
+ i = atoi(cb->f[2]);
+ // check range here? what are the units?
+ ctlr->pmwait = i;
+ }
+ }
+ else
+ r = -1;
+ }
+ else
+ r = -2;
+ free(cb);
+
+ return r;
+}
+
+static long
+ctl(Ether* ether, void* buf, long n)
+{
+ Ctlr *ctlr;
+
+ if((ctlr = ether->ctlr) == nil)
+ error(Enonexist);
+ if(ctlr->attached == 0)
+ error(Eshutdown);
+
+ ilock(ctlr);
+ if(w_option(ctlr, buf, n)){
+ iunlock(ctlr);
+ error(Ebadctl);
+ }
+ if(ctlr->txbusy)
+ w_txdone(ctlr, WTxErrEv);
+ w_enable(ether);
+ w_txstart(ether);
+ iunlock(ctlr);
+
+ return n;
+}
+
+static void
+transmit(Ether* ether)
+{
+ Ctlr* ctlr = ether->ctlr;
+
+ if (ctlr == 0)
+ return;
+
+ ilock(ctlr);
+ ctlr->ntxrq++;
+ w_txstart(ether);
+ iunlock(ctlr);
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+ Ether* ether = (Ether*)arg;
+ Ctlr* ctlr = ether->ctlr;
+
+ if (ctlr == nil)
+ error("card not found");
+ if (ctlr->attached == 0)
+ error("card not attached");
+ ilock(ctlr);
+ ltv_outs(ctlr, WTyp_Prom, (on?1:0));
+ iunlock(ctlr);
+}
+
+static void
+interrupt(Ureg* ,void* arg)
+{
+ Ether* ether = (Ether*) arg;
+ Ctlr* ctlr = (Ctlr*) ether->ctlr;
+
+ if (ctlr == 0)
+ return;
+ ilock(ctlr);
+ ctlr->nints++;
+ w_intr(ether);
+ iunlock(ctlr);
+}
+
+static int
+reset(Ether* ether)
+{
+ int i;
+ Wltv ltv;
+ Ctlr* ctlr;
+ int slot;
+ char *p;
+
+ if ((slot = pcmspecial("WaveLAN/IEEE", ether))<0){
+ DEBUG("no wavelan found\n");
+ return -1;
+ }
+
+ if((ctlr = malloc(sizeof(Ctlr))) == nil)
+ return -1;
+
+ ilock(ctlr);
+
+ if (ether->port==0)
+ ether->port = WDfltIOB;
+ ctlr->iob = ether->port;
+ ctlr->slot = slot;
+
+ if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){
+ print("#l%d: port 0x%lx in use\n",
+ ether->ctlrno, ether->port);
+ goto abort;
+ }
+ DEBUG("#l%d: port=0x%lx irq=%ld\n",
+ ether->ctlrno, ether->port, ether->irq);
+
+ w_intdis(ctlr);
+ if (w_cmd(ctlr,WCmdIni,0)){
+ print("#l%d: init failed\n", ether->ctlrno);
+ goto abort;
+ }
+ w_intdis(ctlr);
+ ltv_outs(ctlr, WTyp_Tick, 8);
+
+ ctlr->chan = 0;
+ ctlr->ptype = WDfltPType;
+ ctlr->txkey = 0;
+ ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1;
+ ctlr->keys.type = WTyp_Keys;
+ if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt))
+ ctlr->crypt = 1;
+ *ctlr->netname = *ctlr->wantname = 0;
+ strcpy(ctlr->nodename, "Plan 9 STA");
+
+ for(i = 0; i < ether->nopt; i++){
+ //
+ // The max. length of an 'opt' is ISAOPTLEN in dat.h.
+ // It should be > 16 to give reasonable name lengths.
+ //
+ if(p = strchr(ether->opt[i], '='))
+ *p = ' ';
+ w_option(ctlr, ether->opt[i], strlen(ether->opt[i]));
+ }
+
+ ctlr->netname[WNameLen-1] = 0;
+ ctlr->wantname[WNameLen-1] = 0;
+ ctlr->nodename[WNameLen-1] =0;
+
+ ltv.type = WTyp_Mac;
+ ltv.len = 4;
+ if (w_inltv(ctlr, &ltv)){
+ print("#l%d: unable to read mac addr\n",
+ ether->ctlrno);
+ goto abort;
+ }
+ memmove(ether->ea, ltv.addr, Eaddrlen);
+
+ if (ctlr->chan == 0)
+ ctlr->chan = ltv_ins(ctlr, WTyp_Chan);
+ ctlr->apdensity = WDfltApDens;
+ ctlr->rtsthres = WDfltRtsThres;
+ ctlr->txrate = WDfltTxRate;
+ ctlr->maxlen = WMaxLen;
+ ctlr->pmena = 0;
+ ctlr->pmwait = 100;
+ ctlr->signal = 1;
+ ctlr->noise = 1;
+
+ // link to ether
+ ether->ctlr = ctlr;
+ ether->mbps = 10;
+ ether->attach = attach;
+ ether->interrupt = interrupt;
+ ether->transmit = transmit;
+ ether->ifstat = ifstat;
+ ether->ctl = ctl;
+ ether->promiscuous = promiscuous;
+ ether->multicast = multicast;
+ ether->arg = ether;
+
+// DEBUG("#l%d: irq %ld port %lx type %s",
+// ether->ctlrno, ether->irq, ether->port, ether->type);
+// DEBUG(" %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX\n",
+// ether->ea[0], ether->ea[1], ether->ea[2],
+// ether->ea[3], ether->ea[4], ether->ea[5]);
+
+ iunlock(ctlr);
+ return 0;
+
+abort:
+ iunlock(ctlr);
+ free(ctlr);
+ ether->ctlr = nil;
+iprint("wave reset failed\n");
+ return -1;
+}
+
+void
+etherwavelanlink(void)
+{
+ addethercard("wavelan", reset);
+}
diff --git a/os/ipaq1110/fns.h b/os/ipaq1110/fns.h
new file mode 100644
index 00000000..2ad379e4
--- /dev/null
+++ b/os/ipaq1110/fns.h
@@ -0,0 +1,179 @@
+#include "../port/portfns.h"
+
+ulong aifinit(uchar *aifarr);
+void aamloop(int);
+int archaudiopower(int);
+void archaudiomute(int);
+void archaudioamp(int);
+int archaudiospeed(int, int);
+void archcodecreset(void);
+void archconfinit(void);
+void archconsole(void);
+int archflash12v(int);
+int archhooksw(int);
+long archkprofmicrosecondspertick(void);
+void archkprofenable(int);
+void archlcdenable(int);
+void archpowerdown(void);
+void archpowerup(void);
+void archreboot(void);
+void archreset(void);
+vlong archrdtsc(void);
+ulong archrdtsc32(void);
+void archuartpower(int, int);
+void blankscreen(int);
+ulong call_apcs(ulong addr, int nargs, ...);
+ulong call_apcs0(ulong addr);
+ulong call_apcs1(ulong addr, ulong a1);
+ulong call_apcs2(ulong addr, ulong a1, ulong a2);
+ulong call_apcs3(ulong addr, ulong a1, ulong a2, ulong a3);
+void cisread(int slotno, void (*f)(int, uchar *));
+void clockcheck(void);
+void clockinit(void);
+void clockpoll(void);
+#define coherence() /* nothing to do for cache coherence for uniprocessor */
+void cursorhide(void);
+void cursorunhide(void);
+void dcflush(void*, ulong);
+void dcflushall(void);
+void dcinval(void);
+int dmaidle(Dma*);
+Dma* dmasetup(int device, int direction, int bigend, void(*)(void*,ulong), void*);
+int dmastart(Dma*, void*, int);
+int dmacontinue(Dma*, void*, int);
+void dmastop(Dma*);
+int dmaerror(Dma*);
+void dmafree(Dma*);
+void dmareset(void);
+void dmawait(Dma*);
+void dumplongs(char *, ulong *, int);
+void dumpregs(Ureg* ureg);
+void dumpstack(void);
+int fpiarm(Ureg*);
+void fpinit(void);
+ulong getcallerpc(void*);
+ulong getcpsr(void);
+ulong getcpuid(void);
+ulong getspsr(void);
+void gotopc(ulong);
+
+void icflushall(void);
+void _idlemode(void);
+void (*idle)(void);
+void idlehands(void);
+int inb(ulong);
+int ins(ulong);
+ulong inl(ulong);
+void outb(ulong, int);
+void outs(ulong, int);
+void outl(ulong, ulong);
+void inss(ulong, void*, int);
+void outss(ulong, void*, int);
+void insb(ulong, void*, int);
+void outsb(ulong, void*, int);
+void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*);
+void intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
+void iofree(int);
+#define iofree(x)
+void ioinit(void);
+int iounused(int, int);
+int ioalloc(int, int, int, char*);
+#define ioalloc(a,b,c,d) 0
+int iprint(char*, ...);
+void installprof(void (*)(Ureg *, int));
+int isvalid_va(void*);
+void kbdinit(void);
+void L3init(void);
+int L3read(int, void*, int);
+int L3write(int, void*, int);
+void lcd_setbacklight(int);
+void lcd_sethz(int);
+void lights(ulong);
+void links(void);
+ulong mcpgettfreq(void);
+void mcpinit(void);
+void mcpsettfreq(ulong tfreq);
+void mcpspeaker(int, int);
+void mcptelecomsetup(ulong hz, int adm, int xint, int rint);
+ushort mcpadcread(int ts);
+void mcptouchsetup(int ts);
+void mcptouchintrenable(void);
+void mcptouchintrdisable(void);
+void mcpgpiowrite(ushort mask, ushort data);
+void mcpgpiosetdir(ushort mask, ushort dir);
+ushort mcpgpioread(void);
+void* minicached(void*);
+void minidcflush(void);
+void mmuenable(ulong);
+ulong mmugetctl(void);
+ulong mmugetdac(void);
+ulong mmugetfar(void);
+ulong mmugetfsr(void);
+void mmuinit(void);
+void* mmuphysmap(ulong, ulong);
+void mmuputctl(ulong);
+void mmuputdac(ulong);
+void mmuputfsr(ulong);
+void mmuputttb(ulong);
+void mmureset(void);
+void mouseinit(void);
+void nowriteSeg(void *, void *);
+void* pa2va(ulong);
+void pcmcisread(PCMslot*);
+int pcmcistuple(int, int, int, void*, int);
+PCMmap* pcmmap(int, ulong, int, int);
+void pcmunmap(int, PCMmap*);
+int pcmpin(int slot, int type);
+void pcmpower(int slotno, int on);
+int pcmpowered(int);
+void pcmreset(int);
+void pcmsetvcc(int, int);
+void pcmsetvpp(int, int);
+int pcmspecial(char *idstr, ISAConf *isa);
+void pcmspecialclose(int slotno);
+void pcmintrenable(int, void (*)(Ureg*, void*), void*);
+void powerenable(void (*)(int));
+void powerdisable(void (*)(int));
+void powerdown(void);
+void powerinit(void);
+void powersuspend(void);
+#define procsave(p)
+#define procrestore(p)
+long rtctime(void);
+void screeninit(void);
+void (*screenputs)(char*, int);
+int segflush(void*, ulong);
+void setpanic(void);
+void setr13(int, void*);
+int splfhi(void);
+int splflo(void);
+void _suspendcode(void);
+void tlbinvalidate(void);
+void tlbinvalidateaddr(void*);
+void trapinit(void);
+void trapstacks(void);
+void trapspecial(int (*)(Ureg *, uint));
+int uartprint(char*, ...);
+void uartspecial(int, int, char, Queue**, Queue**, int (*)(Queue*, int));
+ulong va2pa(void*);
+void vectors(void);
+void vtable(void);
+#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+int wasbusy(int);
+
+#define KADDR(p) ((void *) p)
+#define PADDR(v) va2pa((void*)(v))
+
+// #define timer_start() (*OSCR)
+// #define timer_ticks(t) (*OSCR - (ulong)(t))
+ulong timer_start(void);
+ulong timer_ticks(ulong);
+int timer_devwait(ulong *adr, ulong mask, ulong val, int ost);
+void timer_setwatchdog(int ost);
+void timer_delay(int ost);
+ulong ms2tmr(int ms);
+int tmr2ms(ulong t);
+void delay(int ms);
+ulong us2tmr(int us);
+int tmr2us(ulong t);
+void microdelay(int us);
diff --git a/os/ipaq1110/inflate b/os/ipaq1110/inflate
new file mode 100644
index 00000000..b5761204
--- /dev/null
+++ b/os/ipaq1110/inflate
Binary files differ
diff --git a/os/ipaq1110/io.h b/os/ipaq1110/io.h
new file mode 100644
index 00000000..29cba8cb
--- /dev/null
+++ b/os/ipaq1110/io.h
@@ -0,0 +1,60 @@
+/*
+ * iPAQ 36xx-specific definitions
+ */
+
+/*
+ * GPIO assignment on iPAQ (see H3600 hardware spec).
+ * Following Plan 9, _i is input signal, _o is output, _io is both.
+ */
+enum {
+ GPIO_PWR_ON_i = 1<<0, /* power on/off (active low)*/
+ GPIO_UP_IRQ_i = 1<<1, /* microcontroller interrupt (active low) */
+ /* 2-9 are LCD 8-15 */
+ GPIO_CARD_IND1_i = 1<<10, /* PCMCIA/CF socket 1 card inserted (active low) */
+ GPIO_CARD_IRQ1_i = 1<<11, /* socket 1 IRQ (active low) */
+ GPIO_CLK_SET0_o = 1<<12, /* codec clock select 0 */
+ GPIO_CLK_SET1_o = 1<<13, /* codec clock select 1 */
+ GPIO_L3_SDA_io = 1<<14, /* L3 data to/from UDA1341 */
+ GPIO_L3_MODE_o = 1<<15, /* L3 mode to UDA1341 */
+ GPIO_L3_SCLK_o = 1<<16, /* L3 SCLK to UDA1341 */
+ GPIO_CARD_IND0_i = 1<<17, /* PCMCIA/CF socket 0 card inserted (active low) */
+ GPIO_KEY_ACT_i = 1<<18, /* joypad centre button (active low) */
+ GPIO_SYS_CLK_i = 1<<19, /* codec external clock */
+ GPIO_BAT_FAULT_i = 1<<20, /* battery fault (active high) */
+ GPIO_CARD_IRQ0_i = 1<<21, /* socket 0 IRQ (active low) */
+ GPIO_LOCK_i = 1<<22, /* expansion pack lock/unlock signal (active low) */
+ GPIO_COM_DCD_i = 1<<23, /* UART3 DCD from cradle (active high) */
+ GPIO_OPT_IRQ_i = 1<<24, /* expansion pack shared IRQ (all but PCMCIA/CF, active high) */
+ GPIO_COM_CTS_i = 1<<25, /* UART3 CTS (active high) */
+ GPIO_COM_RTS_o = 1<<26, /* UART3 RTS (active high) */
+ GPIO_OPT_IND_i = 1<<27, /* expansion pack inserted (active low) */
+};
+
+/* special EGPIO register, write only*/
+enum {
+ EGPIO_VPEN = 1<<0, /* flash write enable */
+ EGPIO_CARD_RESET = 1<<1, /* CF/PCMCIA reset signal */
+ EGPIO_OPT_RESET = 1<<2, /* expansion pack reset for other than CF/PCMCIA */
+ EGPIO_CODEC_RESET = 1<<3, /* codec reset signal (active low) */
+ EGPIO_OPT_PWR_ON = 1<<4, /* enable power to NVRAM in expansion pack */
+ EGPIO_OPT_ON = 1<<5, /* enable full power to expansion pack */
+ EGPIO_LCD_ON = 1<<6, /* enable LCD 3.3v supply */
+ EGPIO_RS232_ON = 1<<7, /* enable RS232 transceiver */
+ EGPIO_LCD_PCI = 1<<8, /* enable power to LCD control IC */
+ EGPIO_IR_ON = 1<<9, /* enable power to IR module */
+ EGPIO_AUD_ON = 1<<10, /* enable power to audio amp */
+ EGPIO_AUD_PWR_ON = 1<<11, /* enable power to all other audio circuitry */
+ EGPIO_QMUTE = 1<<12, /* mute audio codec (nb: wastes power if set when audio not powered) */
+ EGPIO_IR_FSEL = 1<<13, /* FIR mode selection: 1=FIR, 0=SIR */
+ EGPIO_LCD_5V_ON = 1<<14, /* enable 5V to LCD module */
+ EGPIO_LVDD_ON = 1<<15, /* enable 9V and -6.5V to LCD module */
+};
+
+/* board-dependent GPIO pin assignment for l3gpio.c */
+enum {
+ L3Data = GPIO_L3_SDA_io,
+ L3Mode = GPIO_L3_MODE_o,
+ L3Clock = GPIO_L3_SCLK_o,
+};
+
+#include "../sa1110/sa1110io.h"
diff --git a/os/ipaq1110/ipaq b/os/ipaq1110/ipaq
new file mode 100644
index 00000000..19559326
--- /dev/null
+++ b/os/ipaq1110/ipaq
@@ -0,0 +1,172 @@
+dev
+ root
+ cons archipaq lcd l3gpio
+ env
+ gpio
+ mnt
+ pipe
+ prog
+ rtc
+ srv
+ dup
+ ssl
+ cap
+ sign
+ draw screen
+ pointer
+ uart
+ ip ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux
+ flash
+ ftl
+ pcmcia cis
+ ether netif netaux
+
+ audio
+ ipaq suspend
+ kprof
+
+ip
+ il
+ tcp
+ udp
+# rudp
+# igmp
+ ipifc
+ icmp
+ icmp6
+# ipmux
+
+link
+ flashcfi16
+ etherwavelan
+ ethermedium
+
+lib
+ interp
+ tk
+ draw
+ memlayer
+ memdraw
+ keyring
+ sec
+ mp
+ math
+ kern
+
+mod
+ math
+ sys
+ draw
+ tk
+ keyring
+
+port
+ alarm
+ alloc
+ allocb
+ chan
+ dev
+ dial
+ dis
+ discall
+ exception
+ exportfs
+ inferno
+ latin1
+ nocache
+ nodynld
+ parse
+ pgrp
+ print
+ proc
+ qio
+ qlock
+ random
+ sysfile
+ taslock
+ xalloc
+
+code
+ int kernel_pool_pcnt = 10;
+ int main_pool_pcnt = 33;
+ int heap_pool_pcnt = 34;
+ int image_pool_pcnt = 33;
+ int cflag = 0; /* for JIT */
+
+ int consoleprint = 1;
+ int redirectconsole = 1;
+ char debug_keys = 1;
+ int panicreset = 0;
+ char *tkfont = "/fonts/lucidasans/unicode.7.font";
+ int tkstylus = 1;
+
+init
+ ipaqinit
+
+root
+ /chan /
+ /dev /
+ /dis
+ /env /
+ /fd /
+ /net /
+ /net.alt /
+ /nvfs /
+ /prog /
+ /dis/lib
+ /dis/disk
+ /osinit.dis
+
+# initialisation
+ /dis/touchcal.dis
+
+# dos file system
+ /dis/dossrv.dis
+ /dis/disk/format.dis
+
+# kfs file system
+ /dis/disk/kfs.dis
+ /dis/disk/kfscmd.dis
+
+# used by file systems and commands
+ /dis/lib/arg.dis
+ /dis/lib/styx.dis
+ /dis/lib/string.dis
+ /dis/lib/daytime.dis
+
+# For development work:
+ /dis/sh.dis /dis/tiny/sh.dis
+ /dis/ls.dis
+ /dis/cat.dis
+ /dis/bind.dis
+ /dis/mount.dis
+ /dis/pwd.dis
+ /dis/echo.dis
+ /dis/cd.dis
+ /dis/xd.dis
+ /dis/cp.dis
+ /dis/mkdir.dis
+ /dis/rm.dis
+ /dis/p.dis
+ /dis/ps.dis
+ /dis/lib/readdir.dis
+ /dis/lib/workdir.dis
+ /dis/lib/daytime.dis
+ /dis/lib/auth.dis
+ /dis/lib/ssl.dis
+ /dis/lib/bufio.dis
+ /dis/lib/string.dis
+# /dis/pcmcia.dis /dis/auxi/pcmcia.dis
+# dhcp
+ /dis/lib/dhcpclient.dis /dis/lib/dhcpclient.dis
+ /dis/lib/ip.dis /dis/lib/ip.dis
+
+ /n/remote
+ /n/local
+ /n/client
+ /n/rdbg
+ /n/dump
+ /n/disk
+ /n/kfs
+# Authentication
+ /nvfs/default /usr/inferno/keyring/default
diff --git a/os/ipaq1110/lcd.c b/os/ipaq1110/lcd.c
new file mode 100644
index 00000000..11b9ef7d
--- /dev/null
+++ b/os/ipaq1110/lcd.c
@@ -0,0 +1,185 @@
+#include "u.h"
+#include "mem.h"
+#include "../port/lib.h"
+#include "dat.h"
+#include "draw.h"
+#include "fns.h"
+#include "io.h"
+#include <memdraw.h>
+#include "screen.h"
+
+#define DPRINT if(1)iprint
+
+enum {
+ /* lccr0 */
+ EnableCtlr = 1<<0, /* controller enable */
+ IsColour = 0<<1,
+ IsMono = 1<<1,
+ SinglePanel = 0<<2,
+ DualPanel = 1<<2,
+ DisableDone = 1<<3,
+ DisableBAU = 1<<4,
+ DisableErr = 1<<5,
+ PassivePanel = 0<<7,
+ ActivePanel = 1<<7,
+ BigEndian = 1<<8,
+ DoublePixel = 1<<9,
+ /* 19:12 is palette dma delay */
+
+ /* lcsr */
+ CtlrReady = 1<<0,
+
+ /* lccr3 */
+ VsyncLow = 1<<20,
+ HsyncLow = 1<<21,
+ PixelClockLow = 1<<22,
+ OELow = 1<<23,
+};
+
+typedef struct {
+ Vdisplay;
+ LCDparam;
+ ushort* palette;
+ uchar* upper;
+ uchar* lower;
+} LCDdisplay;
+
+static LCDdisplay *vd; // current active display
+
+void
+lcd_setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+ if(vd->pbs == 0 && p > 15 ||
+ vd->pbs == 1 && p > 255 ||
+ vd->pbs == 2)
+ return;
+ vd->palette[p] = (vd->pbs<<12) |
+ ((r>>(32-4))<<8) |
+ ((g>>(32-4))<<4) |
+ (b>>(32-4));
+}
+
+static void
+disablelcd(void)
+{
+ LcdReg *lcd = LCDREG;
+ int i;
+
+ /* if LCD enabled, turn off and wait for current frame to end */
+ if(lcd->lccr0 & EnableCtlr) {
+ lcd->lccr0 &= ~EnableCtlr;
+ for(i=0; i < 50 && !(lcd->lcsr & CtlrReady); i++)
+ delay(5);
+ }
+}
+
+static void
+setlcdmode(LCDdisplay *vd)
+{
+ LCDmode *p;
+ int ppf, pclk, clockdiv;
+ ulong v, c;
+ LcdReg *lcd = LCDREG;
+ GpioReg *gpio = GPIOREG;
+
+ p = (LCDmode*)&vd->Vmode;
+ ppf = ((((p->x+p->sol_wait+p->eol_wait) *
+ (p->mono ? 1 : 3)) >> (3-p->mono)) +
+ p->hsync_wid) *
+ (p->y/(p->dual+1)+p->vsync_hgt+
+ p->sof_wait+p->eof_wait);
+ pclk = ppf*p->hz;
+ clockdiv = ((m->cpuhz/pclk) >> 1)-2;
+ DPRINT(" oclockdiv=%d\n", clockdiv);
+clockdiv=0x10;
+ disablelcd();
+ lcd->lccr0 = 0; /* reset it */
+
+ DPRINT(" pclk=%d clockdiv=%d\n", pclk, clockdiv);
+ lcd->lccr3 = (clockdiv << 0) |
+ (p->acbias_lines << 8) |
+ (p->lines_per_int << 16) |
+ VsyncLow | HsyncLow; /* vsync active low, hsync active low */
+ lcd->lccr2 = (((p->y/(p->dual+1))-1) << 0) |
+ (p->vsync_hgt << 10) |
+ (p->eof_wait << 16) |
+ (p->sof_wait << 24);
+ lcd->lccr1 = ((p->x-16) << 0) |
+ (p->hsync_wid << 10) |
+ (p->eol_wait << 16) |
+ (p->sol_wait << 24);
+
+ // enable LCD controller, CODEC, and lower 4/8 data bits (for tft/dual)
+ v = p->obits < 12? 0: p->obits < 16? 0x3c: 0x3fc;
+ c = p->obits == 12? 0x3c0: 0;
+ gpio->gafr |= v;
+ gpio->gpdr |= v | c;
+ gpio->gpcr = c;
+
+ lcd->dbar1 = PADDR(vd->palette);
+ if(vd->dual)
+ lcd->dbar2 = PADDR(vd->lower);
+
+ // Enable LCD
+ lcd->lccr0 = EnableCtlr | (p->mono?IsMono:IsColour)
+ | (p->palette_delay << 12)
+ | (p->dual ? DualPanel : SinglePanel)
+ | (p->active? ActivePanel: PassivePanel)
+ | DisableDone | DisableBAU | DisableErr;
+
+ // recalculate actual HZ
+ pclk = (m->cpuhz/(clockdiv+2)) >> 1;
+ p->hz = pclk/ppf;
+
+ archlcdenable(1);
+iprint("lccr0=%8.8lux lccr1=%8.8lux lccr2=%8.8lux lccr3=%8.8lux\n", lcd->lccr0, lcd->lccr1, lcd->lccr2, lcd->lccr3);
+}
+static LCDdisplay main_display; /* TO DO: limits us to a single display */
+
+Vdisplay*
+lcd_init(LCDmode *p)
+{
+ int palsize;
+ int fbsize;
+
+ vd = &main_display;
+ vd->Vmode = *p;
+ vd->LCDparam = *p;
+ DPRINT("%dx%dx%d: hz=%d\n", vd->x, vd->y, vd->depth, vd->hz); /* */
+
+ palsize = vd->pbs==1? 256 : 16;
+ fbsize = palsize*2+(((vd->x*vd->y) * vd->depth) >> 3);
+ if((vd->palette = xspanalloc(fbsize+CACHELINESZ+512, CACHELINESZ, 0)) == nil) /* at least 16-byte alignment */
+ panic("no vidmem, no party...");
+ vd->palette[0] = (vd->pbs<<12);
+ vd->palette = minicached(vd->palette);
+ vd->upper = (uchar*)(vd->palette + palsize);
+ vd->bwid = (vd->x << vd->pbs) >> 1;
+ vd->lower = vd->upper+((vd->bwid*vd->y) >> 1);
+ vd->fb = vd->upper;
+ DPRINT(" fbsize=%d p=%p u=%p l=%p\n", fbsize, vd->palette, vd->upper, vd->lower); /* */
+
+ setlcdmode(vd);
+ return vd;
+}
+
+void
+lcd_flush(void)
+{
+ if(conf.useminicache)
+ minidcflush();
+ else
+ dcflushall(); /* need more precise addresses */
+}
+
+void
+blankscreen(int blank)
+{
+ if (blank) {
+ disablelcd();
+ archlcdenable(0);
+ } else {
+ archlcdenable(1);
+ setlcdmode(&main_display);
+ }
+}
diff --git a/os/ipaq1110/main.c b/os/ipaq1110/main.c
new file mode 100644
index 00000000..58bd792f
--- /dev/null
+++ b/os/ipaq1110/main.c
@@ -0,0 +1,255 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "version.h"
+
+Mach *m = (Mach*)MACHADDR;
+Proc *up = 0;
+Vectorpage *page0 = (Vectorpage*)KZERO; /* doubly-mapped to AIVECADDR */
+Conf conf;
+
+extern ulong kerndate;
+extern int cflag;
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+ulong cpuidlecount;
+
+static void
+poolsizeinit(void)
+{
+ ulong nb;
+
+ nb = conf.npage*BY2PG;
+ poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+ poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+ poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+void
+main(void)
+{
+ memset(edata, 0, end-edata); /* clear the BSS */
+ memset(m, 0, sizeof(Mach)); /* clear the mach struct */
+ conf.nmach = 1;
+ archreset();
+ dmareset();
+ quotefmtinstall();
+ confinit();
+ xinit();
+ mmuinit();
+ poolinit();
+ poolsizeinit();
+ trapinit();
+ clockinit();
+ printinit();
+ screeninit();
+ procinit();
+ links();
+ chandevreset();
+
+ eve = strdup("inferno");
+
+ archconsole();
+ kbdinit();
+
+ print("%ld MHz id %8.8lux\n", (m->cpuhz+500000)/1000000, getcpuid());
+ print("\nInferno %s\n", VERSION);
+ print("Vita Nuova\n");
+ print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag);
+
+ userinit();
+ schedinit();
+}
+
+void
+reboot(void)
+{
+ exit(0);
+}
+
+void
+halt(void)
+{
+ spllo();
+ print("cpu halted\n");
+ for(;;){
+ /* nothing to do */
+ }
+}
+
+void
+confinit(void)
+{
+ ulong base;
+
+ archconfinit();
+
+ base = PGROUND((ulong)end);
+ conf.base0 = base;
+
+ conf.base1 = 0;
+ conf.npage1 = 0;
+
+ conf.npage0 = (conf.topofmem - base)/BY2PG;
+
+ conf.npage = conf.npage0 + conf.npage1;
+ conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG;
+
+ conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+ conf.nmach = 1;
+
+}
+
+void
+init0(void)
+{
+ Osenv *o;
+ char buf[2*KNAMELEN];
+
+ up->nerrlab = 0;
+
+ spllo();
+
+ if(waserror())
+ panic("init0 %r");
+ /*
+ * These are o.k. because rootinit is null.
+ * Then early kproc's will have a root and dot.
+ */
+ o = up->env;
+ o->pgrp->slash = namec("#/", Atodir, 0, 0);
+ cnameclose(o->pgrp->slash->name);
+ o->pgrp->slash->name = newcname("/");
+ o->pgrp->dot = cclone(o->pgrp->slash);
+
+ chandevinit();
+
+ if(!waserror()){
+ ksetenv("cputype", "arm", 0);
+ snprint(buf, sizeof(buf), "arm %s", conffile);
+ ksetenv("terminal", buf, 0);
+ poperror();
+ }
+
+ poperror();
+
+ disinit("/osinit.dis");
+}
+
+void
+userinit(void)
+{
+ Proc *p;
+ Osenv *o;
+
+ p = newproc();
+ o = p->env;
+
+ o->fgrp = newfgrp(nil);
+ o->pgrp = newpgrp();
+ o->egrp = newegrp();
+ kstrdup(&o->user, eve);
+
+ strcpy(p->text, "interp");
+
+ p->fpstate = FPINIT;
+
+ /*
+ * Kernel Stack
+ *
+ * N.B. The -12 for the stack pointer is important.
+ * 4 bytes for gotolabel's return PC
+ */
+ p->sched.pc = (ulong)init0;
+ p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+ ready(p);
+}
+
+void
+exit(int inpanic)
+{
+ up = 0;
+
+ /* Shutdown running devices */
+ chandevshutdown();
+
+ if(inpanic && 0){
+ print("Hit the reset button\n");
+ for(;;)
+ clockpoll();
+ }
+ archreboot();
+}
+
+static void
+linkproc(void)
+{
+ spllo();
+ if (waserror())
+ print("error() underflow: %r\n");
+ else
+ (*up->kpfun)(up->arg);
+ pexit("end proc", 1);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+ p->sched.pc = (ulong)linkproc;
+ p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+ p->kpfun = func;
+ p->arg = arg;
+}
+
+void
+idlehands(void)
+{
+ cpuidlecount++;
+ INTRREG->iccr = 1; /* only unmasked interrupts will stop idle mode */
+ idle();
+}
+
+/* stubs */
+void
+setfsr(ulong)
+{
+}
+
+ulong
+getfsr()
+{
+ return 0;
+}
+
+void
+setfcr(ulong)
+{
+}
+
+ulong
+getfcr()
+{
+ return 0;
+}
+
+void
+fpinit(void)
+{
+}
+
+void
+FPsave(void*)
+{
+}
+
+void
+FPrestore(void*)
+{
+}
diff --git a/os/ipaq1110/mem.h b/os/ipaq1110/mem.h
new file mode 100644
index 00000000..19d55b07
--- /dev/null
+++ b/os/ipaq1110/mem.h
@@ -0,0 +1,200 @@
+/*
+ * Memory and machine-specific definitions. Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define BI2BY 8 /* bits per byte */
+#define BI2WD 32 /* bits per word */
+#define BY2WD 4 /* bytes per word */
+#define BY2V 8 /* bytes per double word */
+#define BY2PG 4096 /* bytes per page */
+#define WD2PG (BY2PG/BY2WD) /* words per page */
+#define PGSHIFT 12 /* log(BY2PG) */
+#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1))
+#define PGROUND(s) ROUND(s, BY2PG)
+#define BIT(n) (1<<n)
+#define BITS(a,b) ((1<<(b+1))-(1<<a))
+
+#define MAXMACH 1 /* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define HZ (100) /* clock frequency */
+#define MS2HZ (1000/HZ) /* millisec per clock tick */
+#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */
+#define MS2TK(t) ((t)/MS2HZ) /* milliseconds to ticks */
+
+/*
+ * More accurate time
+ */
+#define CLOCKFREQ 3686400
+#define MS2TMR(t) ((ulong)(((uvlong)(t)*CLOCKFREQ)/1000))
+#define US2TMR(t) ((ulong)(((uvlong)(t)*CLOCKFREQ)/1000000))
+
+/*
+ * Address spaces
+ * nearly everything maps 1-1 with physical addresses
+ * 0 to 1Mb is not mapped
+ * cache strategy varies as needed (see mmu.c)
+ */
+
+#define KZERO 0xC0000000
+#define MACHADDR (KZERO+0x00001000)
+#define KTTB (KZERO+0x00004000)
+#define KTZERO (KZERO+0x00008010)
+#define KSTACK 8192 /* Size of kernel stack */
+#define FLASHMEM 0x50000000 /* map flash at phys 0 to otherwise unused virtual space */
+#define FLUSHMEM 0xE0000000 /* internally decoded zero memory (for cache flushing) */
+#define DCFADDR FLUSHMEM /* cached and buffered for cache writeback */
+#define MCFADDR (FLUSHMEM+(1<<20)) /* cached and unbuffered for minicache writeback */
+#define UCDRAMZERO 0xC8000000 /* base of memory doubly-mapped as uncached */
+#define AIVECADDR 0xFFFF0000 /* alternative interrupt vector address (other is 0) */
+
+/*
+ * Physical addresses
+ */
+#define PHYSFLASH0 0x00000000 /* flash (chip select 0) */
+#define PHYSCS1 0x08000000 /* static chip select 1 */
+#define PHYSCS2 0x10000000 /* static chip select 2 */
+#define PHYSCS3 0x18000000 /* static chip select 3 */
+#define PHYSPCMCIA0 0x20000000 /* PCMCIA socket 0 space */
+#define PHYSPCMCIA1 0x30000000 /* PCMCIA socket 1 space */
+#define PCMCIASIZE 0x10000000 /* they're both huge */
+#define PHYSCS4 0x40000000 /* static chip select 4 */
+#define PHYSCS5 0x48000000 /* static chip select 5 */
+#define PHYSSERIAL(n) (0x80000000+0x10000*(n)) /* serial devices */
+#define PHYSUSB 0x80000000
+#define PHYSGPCLK 0x80020060
+#define PHYSMCP 0x80060000
+#define PHYSSSP 0x80070060
+#define PHYSOSTMR 0x90000000 /* timers */
+#define PHYSRTC 0x90010000 /* real time clock */
+#define PHYSPOWER 0x90020000 /* power management registers */
+#define PHYSRESET 0x90030000 /* reset controller */
+#define PHYSGPIO 0x90040000
+#define PHYSINTR 0x90050000 /* interrupt controller */
+#define PHYSPPC 0x90060000 /* peripheral pin controller */
+#define PHYSMEMCFG 0xA0000000 /* memory configuration */
+#define PHYSDMA 0xB0000000 /* DMA controller */
+#define PHYSLCD 0xB0100000 /* LCD controller */
+#define PHYSMEM0 0xC0000000
+#define PHYSFLUSH0 0xE0000000 /* internally decoded, for cache flushing */
+
+/*
+ * Memory Interface Control Registers
+ */
+#define MDCNFG (PHYSMEMCFG) /* memory controller configuration */
+#define MDCAS0 (PHYSMEMCFG+4)
+#define MDCAS1 (PHYSMEMCFG+8)
+#define MDCAS2 (PHYSMEMCFG+0xC)
+#define MSC0 (PHYSMEMCFG+0x10)
+#define MSC1 (PHYSMEMCFG+0x14)
+#define MSC2 (PHYSMEMCFG+0x2C) /* SA1110, but not SA1100 */
+
+#define MSCx(RRR, RDN, RDF, RBW, RT) ((((RRR)&0x7)<<13)|(((RDN)&0x1F)<<8)|(((RDF)&0x1F)<<3)|(((RBW)&1)<<2)|((RT)&3))
+
+#define CACHELINELOG 5
+#define CACHELINESZ (1<<CACHELINELOG)
+
+/*
+ * PSR
+ */
+#define PsrMusr 0x10 /* mode */
+#define PsrMfiq 0x11
+#define PsrMirq 0x12
+#define PsrMsvc 0x13
+#define PsrMabt 0x17
+#define PsrMund 0x1B
+#define PsrMsys 0x1F
+#define PsrMask 0x1F
+
+#define PsrDfiq 0x00000040 /* disable FIQ interrupts */
+#define PsrDirq 0x00000080 /* disable IRQ interrupts */
+
+#define PsrV 0x10000000 /* overflow */
+#define PsrC 0x20000000 /* carry/borrow/extend */
+#define PsrZ 0x40000000 /* zero */
+#define PsrN 0x80000000 /* negative/less than */
+
+/*
+ * Internal MMU coprocessor registers
+ */
+#define CpCPUID 0 /* R: */
+#define CpControl 1 /* R/W: */
+#define CpTTB 2 /* R/W: translation table base */
+#define CpDAC 3 /* R/W: domain access control */
+#define CpFSR 5 /* R/W: fault status */
+#define CpFAR 6 /* R/W: fault address */
+#define CpCacheCtl 7 /* W: */
+#define CpTLBops 8 /* W: TLB operations */
+#define CpRBops 9 /* W: Read Buffer operations */
+#define CpPID 13 /* R/W: Process ID Virtual Mapping */
+#define CpDebug 14 /* R/W: debug registers */
+#define CpTest 15 /* W: Test, Clock and Idle Control */
+
+/*
+ * Coprocessors
+ */
+#define CpMMU 15
+#define CpPWR 15
+
+/*
+ * CpControl bits
+ */
+#define CpCmmu 0x00000001 /* M: MMU enable */
+#define CpCalign 0x00000002 /* A: alignment fault enable */
+#define CpCDcache 0x00000004 /* C: instruction/data cache on */
+#define CpCwb 0x00000008 /* W: write buffer turned on */
+#define CpCi32 0x00000010 /* P: 32-bit programme space */
+#define CpCd32 0x00000020 /* D: 32-bit data space */
+#define CpCbe 0x00000080 /* B: big-endian operation */
+#define CpCsystem 0x00000100 /* S: system permission */
+#define CpCrom 0x00000200 /* R: ROM permission */
+#define CpCIcache 0x00001000 /* I: Instruction Cache on */
+#define CpCaltivec 0x00002000 /* X: alternative interrupt vectors */
+
+/*
+ * MMU
+ */
+/*
+ * Small pages:
+ * L1: 12-bit index -> 4096 descriptors -> 16Kb
+ * L2: 8-bit index -> 256 descriptors -> 1Kb
+ * Each L2 descriptor has access permissions for 4 1Kb sub-pages.
+ *
+ * TTB + L1Tx gives address of L1 descriptor
+ * L1 descriptor gives PTBA
+ * PTBA + L2Tx gives address of L2 descriptor
+ * L2 descriptor gives PBA
+ */
+#define MmuSection (1<<20)
+#define MmuLargePage (1<<16)
+#define MmuSmallPage (1<<12)
+#define MmuTTB(pa) ((pa) & ~0x3FFF) /* translation table base */
+#define MmuL1x(pa) (((pa)>>20) & 0xFFF) /* L1 table index */
+#define MmuPTBA(pa) ((pa) & ~0x3FF) /* page table base address */
+#define MmuL2x(pa) (((pa)>>12) & 0xFF) /* L2 table index */
+#define MmuPBA(pa) ((pa) & ~0xFFF) /* page base address */
+#define MmuSBA(pa) ((pa) & ~0xFFFFF) /* section base address */
+
+#define MmuL1type 0x03
+#define MmuL1page 0x01 /* descriptor is for L2 pages */
+#define MmuL1section 0x02 /* descriptor is for section */
+
+#define MmuL2invalid 0x000
+#define MmuL2large 0x001 /* large */
+#define MmuL2small 0x002 /* small */
+#define MmuWB 0x004 /* data goes through write buffer */
+#define MmuIDC 0x008 /* data placed in cache */
+
+#define MmuDAC(d) (((d) & 0xF)<<5) /* L1 domain */
+#define MmuAP(i, v) ((v)<<(((i)*2)+4)) /* access permissions */
+#define MmuL1AP(v) MmuAP(3, (v))
+#define MmuL2AP(v) MmuAP(3, (v))|MmuAP(2, (v))|MmuAP(1, (v))|MmuAP(0, (v))
+#define MmuAPsro 0 /* supervisor ro if S|R */
+#define MmuAPsrw 1 /* supervisor rw */
+#define MmuAPuro 2 /* supervisor rw + user ro */
+#define MmuAPurw 3 /* supervisor rw + user rw */
diff --git a/os/ipaq1110/mkfile b/os/ipaq1110/mkfile
new file mode 100644
index 00000000..d76a8110
--- /dev/null
+++ b/os/ipaq1110/mkfile
@@ -0,0 +1,103 @@
+<../../mkconfig
+TKSTYLE=std
+
+#Configurable parameters
+
+CONF=ipaq #default configuration
+CONFLIST=ipaq
+
+SYSTARG=$OSTARG
+OBJTYPE=arm
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin #path of directory where kernel is installed
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+KTZERO=0xC0008010
+
+OBJ=\
+ l.$O\
+ clock.$O\
+ dma.$O\
+ fpi.$O\
+ fpiarm.$O\
+ fpimem.$O\
+ defont.$O\
+ main.$O\
+ mmu.$O\
+ trap.$O\
+ $CONF.root.$O\
+ $IP\
+ $DEVS\
+ $ETHERS\
+ $LINKS\
+ $PORT\
+ $MISC\
+ $OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+LIBDIRS=$LIBS
+
+HFILES=\
+ mem.h\
+ dat.h\
+ fns.h\
+ io.h\
+ ../sa1110/sa1110io.h\
+ ../sa1110/fpi.h\
+
+CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -I../sa1110
+KERNDATE=`{$NDATE}
+
+default:V: i$CONF.gz i$CONF.p9 k.gz
+
+install:V: $INSTALLDIR/i$CONF $INSTALLDIR/i$CONF.gz $INSTALLDIR/i$CONF.p9.gz $INSTALLDIR/i$CONF.raw
+
+i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES i$CONF.p9
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD -s -o $target -H5 -T0xC0008010 -R4 -l $OBJ $CONF.$O $LIBFILES
+
+i$CONF.p9: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD -o $target -T0xC0008010 -R4 -l $OBJ $CONF.$O $LIBFILES
+
+i$CONF.gz: i$CONF
+ rm -f i$CONF.gz
+ gzip -9 <i$CONF >i$CONF.gz
+
+<../port/portmkfile
+CLEANEXTRA=k.gz
+
+../init/$INIT.dis: ../init/$INIT.b
+ cd ../init; mk $INIT.dis
+
+clock.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
+devether.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
+main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
+trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
+
+devether.$O $ETHERS: ../sa1110/etherif.h ../port/netif.h
+$IP devip.$O: ../ip/ip.h
+io.h:N: ../sa1110/sa1110io.h
+
+%.$O: ../sa1110/%.c
+ $CC $CFLAGS -I. ../sa1110/$stem.c
+
+%.$O: ../sa1110/%.s
+ $AS -I. -I../sa1110 ../sa1110/$stem.s
+
+dummy:V:
+
+k.gz: i$CONF.gz
+ cat inflate i$CONF.gz >k.gz
+ echo burble burble >>k.gz
+
+devaudio.$O: devaudio.c
+ $CC $CFLAGS devaudio.c
+
+arch$CONF.$O: ../sa1110/etherif.h
+
+devuart.$O: ../sa1110/devuart.c
+ $CC $CFLAGS ../sa1110/devuart.c
diff --git a/os/ipaq1110/screen.c b/os/ipaq1110/screen.c
new file mode 100644
index 00000000..2ed7bd32
--- /dev/null
+++ b/os/ipaq1110/screen.c
@@ -0,0 +1,917 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+
+#include "screen.h"
+
+enum {
+ Backgnd = 0xFF, /* white */
+ Foregnd = 0x00, /* black */
+};
+
+#define DPRINT if(1)iprint
+
+static Memdata xgdata;
+static Memimage xgscreen =
+{
+ {0, 0, 0, 0}, /* r */
+ {0, 0, 0, 0}, /* clipr */
+ 8, /* depth */
+ 1, /* nchan */
+ CMAP8, /* chan */
+ nil, /* cmap */
+ &xgdata, /* data */
+ 0, /* zero */
+ 0, /* width */
+ nil, /* layer */
+ 0, /* flags */
+};
+
+Memimage *gscreen;
+Memimage *conscol;
+Memimage *back;
+
+Memsubfont *memdefont;
+
+static Point curpos;
+static Rectangle window;
+
+typedef struct SWcursor SWcursor;
+
+static Vdisplay *vd;
+static SWcursor *swc = nil;
+
+SWcursor* swcurs_create(ulong *, int, int, Rectangle, int);
+void swcurs_destroy(SWcursor*);
+void swcurs_enable(SWcursor*);
+void swcurs_disable(SWcursor*);
+void swcurs_hide(SWcursor*);
+void swcurs_unhide(SWcursor*);
+void swcurs_load(SWcursor*, Cursor*);
+void swcursupdate(int, int, int, int);
+
+static char printbuf[1024];
+static int printbufpos = 0;
+static void lcdscreenputs(char*, int);
+static void screenpbuf(char*, int);
+void (*screenputs)(char*, int) = screenpbuf;
+
+static Cursor arrow = {
+ { -1, -1 },
+ { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
+ 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
+ 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
+ 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
+ },
+ { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
+ 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
+ 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
+ 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
+ },
+};
+
+static ushort palette16[256];
+static void (*flushpixels)(Rectangle, ulong*, int, ulong*, int);
+static void flush8to4(Rectangle, ulong*, int, ulong*, int);
+static void flush8to4r(Rectangle, ulong*, int, ulong*, int);
+static void flush8to16(Rectangle, ulong*, int, ulong*, int);
+static void flush8to16r(Rectangle, ulong*, int, ulong*, int);
+
+/*
+lccr0=000000b9 lccr1=0b100930 lccr2=0a0108ef lccr3=00300010
+ ---
+vd->wid=320 bwid=640 gscreen->width=60 fb=d0b7cb80 data=d0ba25c0
+ */
+
+int
+setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+ if(vd->depth >= 8)
+ p &= 0xff;
+ else
+ p &= 0xf;
+ vd->colormap[p][0] = r;
+ vd->colormap[p][1] = g;
+ vd->colormap[p][2] = b;
+ palette16[p] = ((r>>(32-4))<<12)|((g>>(32-4))<<7)|((b>>(32-4))<<1);
+ lcd_setcolor(p, r, g, b);
+ return ~0;
+}
+
+void
+getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
+{
+ if(vd->depth >= 8)
+ p = (p&0xff)^0xff;
+ else
+ p = (p&0xf)^0xf;
+ *pr = vd->colormap[p][0];
+ *pg = vd->colormap[p][1];
+ *pb = vd->colormap[p][2];
+}
+
+void
+graphicscmap(int invert)
+{
+ int num, den, i, j;
+ int r, g, b, cr, cg, cb, v, p;
+
+ for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){
+ for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){
+ den=r;
+ if(g>den) den=g;
+ if(b>den) den=b;
+ if(den==0) /* divide check -- pick grey shades */
+ cr=cg=cb=v*17;
+ else{
+ num=17*(4*den+v);
+ cr=r*num/den;
+ cg=g*num/den;
+ cb=b*num/den;
+ }
+ p = (i+(j&15));
+ if(invert)
+ p ^= 0xFF;
+ if(vd->depth == 4) {
+ if((p&0xf) != (p>>4))
+ continue;
+ p &= 0xf;
+ }
+ setcolor(p,
+ cr*0x01010101,
+ cg*0x01010101,
+ cb*0x01010101);
+ }
+ }
+ lcd_flush();
+}
+
+static uchar lum[256]={
+ 0, 7, 15, 23, 39, 47, 55, 63, 79, 87, 95, 103, 119, 127, 135, 143,
+154, 17, 9, 17, 25, 49, 59, 62, 68, 89, 98, 107, 111, 129, 138, 146,
+157, 166, 34, 11, 19, 27, 59, 71, 69, 73, 99, 109, 119, 119, 139, 148,
+159, 169, 178, 51, 13, 21, 29, 69, 83, 75, 78, 109, 120, 131, 128, 149,
+ 28, 35, 43, 60, 68, 75, 83, 100, 107, 115, 123, 140, 147, 155, 163, 20,
+ 25, 35, 40, 47, 75, 85, 84, 89, 112, 121, 129, 133, 151, 159, 168, 176,
+190, 30, 42, 44, 50, 90, 102, 94, 97, 125, 134, 144, 143, 163, 172, 181,
+194, 204, 35, 49, 49, 54, 105, 119, 103, 104, 137, 148, 158, 154, 175, 184,
+ 56, 63, 80, 88, 96, 103, 120, 128, 136, 143, 160, 168, 175, 183, 40, 48,
+ 54, 63, 69, 90, 99, 107, 111, 135, 144, 153, 155, 173, 182, 190, 198, 45,
+ 50, 60, 70, 74, 100, 110, 120, 120, 150, 160, 170, 167, 186, 195, 204, 214,
+229, 55, 66, 77, 79, 110, 121, 131, 129, 165, 176, 187, 179, 200, 210, 219,
+ 84, 100, 108, 116, 124, 140, 148, 156, 164, 180, 188, 196, 204, 60, 68, 76,
+ 82, 91, 108, 117, 125, 134, 152, 160, 169, 177, 195, 204, 212, 221, 66, 74,
+ 80, 89, 98, 117, 126, 135, 144, 163, 172, 181, 191, 210, 219, 228, 238, 71,
+ 76, 85, 95, 105, 126, 135, 145, 155, 176, 185, 195, 205, 225, 235, 245, 255,
+};
+
+void flushmemscreen(Rectangle r);
+
+void
+screenclear(void)
+{
+ memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD);
+ curpos = window.min;
+ flushmemscreen(gscreen->r);
+}
+
+static void
+setscreen(LCDmode *mode)
+{
+ int h;
+
+ if(swc != nil)
+ swcurs_destroy(swc);
+
+ vd = lcd_init(mode);
+ if(vd == nil)
+ panic("can't initialise LCD");
+
+ if(lum[255] == 255) {
+ int i;
+ for(i=0; i<256; i++)
+ lum[i] >>= 4; /* could support depths other than 4 */
+ }
+
+ gscreen = &xgscreen;
+ xgdata.ref = 1;
+
+ if(conf.portrait == 0)
+ gscreen->r = Rect(0, 0, vd->x, vd->y);
+ else
+ gscreen->r = Rect(0, 0, vd->y, vd->x);
+ gscreen->clipr = gscreen->r;
+ gscreen->depth = 8;
+ gscreen->width = wordsperline(gscreen->r, gscreen->depth);
+ if(vd->depth == 4 || vd->depth == 16 || conf.portrait) { /* use 8 to 4 bit fakeout for speed */
+ if((xgdata.bdata = xspanalloc(gscreen->width*gscreen->r.max.y*BY2WD+CACHELINESZ, CACHELINESZ, 0)) == nil)
+ panic("can't alloc vidmem");
+ xgdata.bdata = minicached(xgdata.bdata);
+ if(conf.portrait == 0)
+ flushpixels = vd->depth==4? flush8to4: flush8to16;
+ else
+ flushpixels = vd->depth==4? flush8to4r: flush8to16r;
+ } else{
+ xgdata.bdata = (uchar*)vd->fb;
+ flushpixels = nil;
+ }
+ memimageinit();
+ memdefont = getmemdefont();
+
+ memsetchan(gscreen, CMAP8); /* TO DO: could now use RGB16 */
+ back = memwhite;
+ conscol = memblack;
+ memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD);
+
+ DPRINT("vd->wid=%d bwid=%d gscreen->width=%ld fb=%p data=%p\n",
+ vd->x, vd->bwid, gscreen->width, vd->fb, xgdata.bdata);
+ graphicscmap(0);
+ h = memdefont->height;
+ window = insetrect(gscreen->r, 4);
+ window.max.y = window.min.y+(Dy(window)/h)*h;
+ screenclear();
+
+// swc = swcurs_create(gscreendata.data, gscreen.width, gscreen.ldepth, gscreen.r, 1);
+
+ drawcursor(nil);
+}
+
+void
+screeninit(void)
+{
+ LCDmode lcd;
+
+ memset(&lcd, 0, sizeof(lcd));
+ if(archlcdmode(&lcd) < 0)
+ return;
+ setscreen(&lcd);
+ screenputs = lcdscreenputs;
+ if(printbufpos)
+ screenputs("", 0);
+ blanktime = 3; /* minutes */
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen)
+{
+ *r = gscreen->r;
+ *d = gscreen->depth;
+ *chan = gscreen->chan;
+ *width = gscreen->width;
+ *softscreen = (gscreen->data->bdata != (uchar*)vd->fb);
+
+ return (uchar*)gscreen->data->bdata;
+}
+
+void
+detachscreen(void)
+{
+}
+
+static void
+flush8to4(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+ int i, h, w;
+
+/*
+ print("1) s=%ux sw=%d d=%ux dw=%d r=(%d,%d)(%d,%d)\n",
+ s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y);
+*/
+
+ r.min.x &= ~7;
+ r.max.x = (r.max.x + 7) & ~7;
+ s += (r.min.y*sw)+(r.min.x>>2);
+ d += (r.min.y*dw)+(r.min.x>>3);
+ h = Dy(r);
+ w = Dx(r) >> 3;
+ sw -= w*2;
+ dw -= w;
+
+ while(h--) {
+ for(i=w; i; i--) {
+ ulong v1 = *s++;
+ ulong v2 = *s++;
+ *d++ = (lum[v2>>24]<<28)
+ |(lum[(v2>>16)&0xff]<<24)
+ |(lum[(v2>>8)&0xff]<<20)
+ |(lum[v2&0xff]<<16)
+ |(lum[v1>>24]<<12)
+ |(lum[(v1>>16)&0xff]<<8)
+ |(lum[(v1>>8)&0xff]<<4)
+ |(lum[v1&0xff])
+ ;
+ }
+ s += sw;
+ d += dw;
+ }
+}
+
+static void
+flush8to16(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+ int i, h, w;
+ ushort *p;
+
+ if(0)
+ iprint("1) s=%p sw=%d d=%p dw=%d r=[%d,%d, %d,%d]\n",
+ s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y);
+
+ r.min.x &= ~3;
+ r.max.x = (r.max.x + 3) & ~3; /* nearest ulong */
+ s += (r.min.y*sw)+(r.min.x>>2);
+ d += (r.min.y*dw)+(r.min.x>>1);
+ h = Dy(r);
+ w = Dx(r) >> 2; /* also ulong */
+ sw -= w;
+ dw -= w*2;
+ if(0)
+ iprint("h=%d w=%d sw=%d dw=%d\n", h, w, sw, dw);
+
+ p = palette16;
+ while(--h >= 0){
+ for(i=w; --i>=0;){
+ ulong v = *s++;
+ *d++ = (p[(v>>8)&0xFF]<<16) | p[v & 0xFF];
+ *d++ = (p[v>>24]<<16) | p[(v>>16)&0xFF];
+ }
+ s += sw;
+ d += dw;
+ }
+}
+
+static void
+flush8to4r(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+ flush8to4(r, s, sw, d, dw); /* rotation not implemented */
+}
+
+static void
+flush8to16r(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+ int x, y, w, dws;
+ ushort *p;
+ ushort *ds;
+
+ if(0)
+ iprint("1) s=%p sw=%d d=%p dw=%d r=[%d,%d, %d,%d]\n",
+ s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y);
+
+ r.min.y &= ~3;
+ r.max.y = (r.max.y+3) & ~3;
+ r.min.x &= ~7;
+ r.max.x = (r.max.x + 7) & ~7;
+ s += (r.min.y*sw)+(r.min.x>>2);
+// d += (r.min.y*dw)+(r.min.x>>1);
+ w = Dx(r) >> 2; /* also ulong */
+ sw -= w;
+ dws = dw*2;
+ if(0)
+ iprint("h=%d w=%d sw=%d dw=%d x,y=%d,%d %d\n", Dy(r), w, sw, dw, r.min.x,r.min.y, dws);
+
+ p = palette16;
+ for(y=r.min.y; y<r.max.y; y++){
+ for(x=r.min.x; x<r.max.x; x+=4){
+ ulong v = *s++;
+ ds = (ushort*)(d + x*dw) + (gscreen->r.max.y-(y+1));
+ ds[0] = p[v & 0xFF];
+ ds[dws] = p[(v>>8)&0xFF];
+ ds[dws*2] = p[(v>>16)&0xFF];
+ ds[dws*3] = p[(v>>24)&0xFF];
+ }
+ s += sw;
+ }
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ if(rectclip(&r, gscreen->r) == 0)
+ return;
+ if(r.min.x >= r.max.x || r.min.y >= r.max.y)
+ return;
+ if(flushpixels != nil)
+ flushpixels(r, (ulong*)gscreen->data->bdata, gscreen->width, (ulong*)vd->fb, vd->bwid >> 2);
+ lcd_flush();
+}
+
+static void
+scroll(void)
+{
+ int o;
+ Point p;
+ Rectangle r;
+
+ o = 4*memdefont->height;
+ r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
+ p = Pt(window.min.x, window.min.y+o);
+ memimagedraw(gscreen, r, gscreen, p, nil, p, SoverD);
+ flushmemscreen(r);
+ r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
+ memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+ flushmemscreen(r);
+
+ curpos.y -= o;
+}
+
+static void
+clearline(void)
+{
+ Rectangle r;
+ int yloc = curpos.y;
+
+ r = Rpt(Pt(window.min.x, window.min.y + yloc),
+ Pt(window.max.x, window.min.y+yloc+memdefont->height));
+ memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+}
+
+static void
+screenputc(char *buf)
+{
+ Point p;
+ int h, w, pos;
+ Rectangle r;
+ static int *xp;
+ static int xbuf[256];
+
+ h = memdefont->height;
+ if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
+ xp = xbuf;
+
+ switch(buf[0]) {
+ case '\n':
+ if(curpos.y+h >= window.max.y)
+ scroll();
+ curpos.y += h;
+ /* fall through */
+ case '\r':
+ xp = xbuf;
+ curpos.x = window.min.x;
+ break;
+ case '\t':
+ if(curpos.x == window.min.x)
+ clearline();
+ p = memsubfontwidth(memdefont, " ");
+ w = p.x;
+ *xp++ = curpos.x;
+ pos = (curpos.x-window.min.x)/w;
+ pos = 8-(pos%8);
+ r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y+h);
+ memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+ flushmemscreen(r);
+ curpos.x += pos*w;
+ break;
+ case '\b':
+ if(xp <= xbuf)
+ break;
+ xp--;
+ r = Rpt(Pt(*xp, curpos.y), Pt(curpos.x, curpos.y + h));
+ memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+ flushmemscreen(r);
+ curpos.x = *xp;
+ break;
+ case '\0':
+ break;
+ default:
+ p = memsubfontwidth(memdefont, buf);
+ w = p.x;
+
+ if(curpos.x >= window.max.x-w)
+ screenputc("\n");
+
+ if(curpos.x == window.min.x)
+ clearline();
+ if(xp < xbuf+nelem(xbuf))
+ *xp++ = curpos.x;
+ r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h);
+ memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+ memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf);
+ flushmemscreen(r);
+ curpos.x += w;
+ }
+}
+
+static void
+screenpbuf(char *s, int n)
+{
+ if(printbufpos+n > sizeof(printbuf))
+ n = sizeof(printbuf)-printbufpos;
+ if(n > 0) {
+ memmove(&printbuf[printbufpos], s, n);
+ printbufpos += n;
+ }
+}
+
+static void
+screendoputs(char *s, int n)
+{
+ int i;
+ Rune r;
+ char buf[4];
+
+ while(n > 0) {
+ i = chartorune(&r, s);
+ if(i == 0){
+ s++;
+ --n;
+ continue;
+ }
+ memmove(buf, s, i);
+ buf[i] = 0;
+ n -= i;
+ s += i;
+ screenputc(buf);
+ }
+}
+
+void
+screenflush(void)
+{
+ int j = 0;
+ int k;
+
+ for (k = printbufpos; j < k; k = printbufpos) {
+ screendoputs(printbuf + j, k - j);
+ j = k;
+ }
+ printbufpos = 0;
+}
+
+static void
+lcdscreenputs(char *s, int n)
+{
+ static Proc *me;
+
+ if(!canlock(vd)) {
+ /* don't deadlock trying to print in interrupt */
+ /* don't deadlock trying to print while in print */
+ if(islo() == 0 || up != nil && up == me){
+ /* save it for later... */
+ /* In some cases this allows seeing a panic message
+ that would be locked out forever */
+ screenpbuf(s, n);
+ return;
+ }
+ lock(vd);
+ }
+
+ me = up;
+ if (printbufpos)
+ screenflush();
+ screendoputs(s, n);
+ if (printbufpos)
+ screenflush();
+ me = nil;
+
+ unlock(vd);
+}
+
+/*
+ * Software cursor code: done by hand, might be better to use memdraw
+ */
+
+typedef struct SWcursor {
+ ulong *fb; /* screen frame buffer */
+ Rectangle r;
+ int d; /* ldepth of screen */
+ int width; /* width of screen in ulongs */
+ int x;
+ int y;
+ int hotx;
+ int hoty;
+ uchar cbwid; /* cursor byte width */
+ uchar f; /* flags */
+ uchar cwid;
+ uchar chgt;
+ int hidecount;
+ uchar data[CURSWID*CURSHGT];
+ uchar mask[CURSWID*CURSHGT];
+ uchar save[CURSWID*CURSHGT];
+} SWcursor;
+
+enum {
+ CUR_ENA = 0x01, /* cursor is enabled */
+ CUR_DRW = 0x02, /* cursor is currently drawn */
+ CUR_SWP = 0x10, /* bit swap */
+};
+
+static Rectangle cursoroffrect;
+static int cursorisoff;
+
+static void swcursorflush(int, int);
+static void swcurs_draw_or_undraw(SWcursor *);
+
+static void
+cursorupdate0(void)
+{
+ int inrect, x, y;
+ Point m;
+
+ m = mousexy();
+ x = m.x - swc->hotx;
+ y = m.y - swc->hoty;
+ inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x
+ && y >= cursoroffrect.min.y && y < cursoroffrect.max.y);
+ if (cursorisoff == inrect)
+ return;
+ cursorisoff = inrect;
+ if (inrect)
+ swcurs_hide(swc);
+ else {
+ swc->hidecount = 0;
+ swcurs_draw_or_undraw(swc);
+ }
+ swcursorflush(m.x, m.y);
+}
+
+void
+cursorupdate(Rectangle r)
+{
+ lock(vd);
+ r.min.x -= 16;
+ r.min.y -= 16;
+ cursoroffrect = r;
+ if (swc != nil)
+ cursorupdate0();
+ unlock(vd);
+}
+
+void
+cursorenable(void)
+{
+ Point m;
+
+ lock(vd);
+ if(swc != nil) {
+ swcurs_enable(swc);
+ m = mousexy();
+ swcursorflush(m.x, m.y);
+ }
+ unlock(vd);
+}
+
+void
+cursordisable(void)
+{
+ Point m;
+
+ lock(vd);
+ if(swc != nil) {
+ swcurs_disable(swc);
+ m = mousexy();
+ swcursorflush(m.x, m.y);
+ }
+ unlock(vd);
+}
+
+void
+swcursupdate(int oldx, int oldy, int x, int y)
+{
+
+ if(!canlock(vd))
+ return; /* if can't lock, don't wake up stuff */
+
+ if(x < gscreen->r.min.x)
+ x = gscreen->r.min.x;
+ if(x >= gscreen->r.max.x)
+ x = gscreen->r.max.x;
+ if(y < gscreen->r.min.y)
+ y = gscreen->r.min.y;
+ if(y >= gscreen->r.max.y)
+ y = gscreen->r.max.y;
+ if(swc != nil) {
+ swcurs_hide(swc);
+ swc->x = x;
+ swc->y = y;
+ cursorupdate0();
+ swcurs_unhide(swc);
+ swcursorflush(oldx, oldy);
+ swcursorflush(x, y);
+ }
+
+ unlock(vd);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+ Point p, m;
+ Cursor curs, *cp;
+ int j, i, h, bpl;
+ uchar *bc, *bs, *cclr, *cset;
+
+ if(swc == nil)
+ return;
+
+ /* Set the default system cursor */
+ if(c == nil || c->data == nil){
+ swcurs_disable(swc);
+ return;
+ }
+ else {
+ cp = &curs;
+ p.x = c->hotx;
+ p.y = c->hoty;
+ cp->offset = p;
+ bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
+
+ h = (c->maxy-c->miny)/2;
+ if(h > 16)
+ h = 16;
+
+ bc = c->data;
+ bs = c->data + h*bpl;
+
+ cclr = cp->clr;
+ cset = cp->set;
+ for(i = 0; i < h; i++) {
+ for(j = 0; j < 2; j++) {
+ cclr[j] = bc[j];
+ cset[j] = bs[j];
+ }
+ bc += bpl;
+ bs += bpl;
+ cclr += 2;
+ cset += 2;
+ }
+ }
+ swcurs_load(swc, cp);
+ m = mousexy();
+ swcursorflush(m.x, m.y);
+ swcurs_enable(swc);
+}
+
+SWcursor*
+swcurs_create(ulong *fb, int width, int ldepth, Rectangle r, int bitswap)
+{
+ SWcursor *swc;
+
+ swc = (SWcursor*)malloc(sizeof(SWcursor));
+ swc->fb = fb;
+ swc->r = r;
+ swc->d = ldepth;
+ swc->width = width;
+ swc->f = bitswap ? CUR_SWP : 0;
+ swc->x = swc->y = 0;
+ swc->hotx = swc->hoty = 0;
+ swc->hidecount = 0;
+ return swc;
+}
+
+void
+swcurs_destroy(SWcursor *swc)
+{
+ swcurs_disable(swc);
+ free(swc);
+}
+
+static void
+swcursorflush(int x, int y)
+{
+ Rectangle r;
+
+ /* XXX a little too paranoid here */
+ r.min.x = x-16;
+ r.min.y = y-16;
+ r.max.x = x+17;
+ r.max.y = y+17;
+ flushmemscreen(r);
+}
+
+static void
+swcurs_draw_or_undraw(SWcursor *swc)
+{
+ uchar *p;
+ uchar *cs;
+ int w, vw;
+ int x1 = swc->r.min.x;
+ int y1 = swc->r.min.y;
+ int x2 = swc->r.max.x;
+ int y2 = swc->r.max.y;
+ int xp = swc->x - swc->hotx;
+ int yp = swc->y - swc->hoty;
+ int ofs;
+
+ if(((swc->f & CUR_ENA) && (swc->hidecount <= 0))
+ == ((swc->f & CUR_DRW) != 0))
+ return;
+ w = swc->cbwid*BI2BY/(1 << swc->d);
+ x1 = xp < x1 ? x1 : xp;
+ y1 = yp < y1 ? y1 : yp;
+ x2 = xp+w >= x2 ? x2 : xp+w;
+ y2 = yp+swc->chgt >= y2 ? y2 : yp+swc->chgt;
+ if(x2 <= x1 || y2 <= y1)
+ return;
+ p = (uchar*)(swc->fb + swc->width*y1)
+ + x1*(1 << swc->d)/BI2BY;
+ y2 -= y1;
+ x2 = (x2-x1)*(1 << swc->d)/BI2BY;
+ vw = swc->width*BY2WD - x2;
+ w = swc->cbwid - x2;
+ ofs = swc->cbwid*(y1-yp)+(x1-xp);
+ cs = swc->save + ofs;
+ if((swc->f ^= CUR_DRW) & CUR_DRW) {
+ uchar *cm = swc->mask + ofs;
+ uchar *cd = swc->data + ofs;
+ while(y2--) {
+ x1 = x2;
+ while(x1--) {
+ *p = ((*cs++ = *p) & *cm++) ^ *cd++;
+ p++;
+ }
+ cs += w;
+ cm += w;
+ cd += w;
+ p += vw;
+ }
+ } else {
+ while(y2--) {
+ x1 = x2;
+ while(x1--)
+ *p++ = *cs++;
+ cs += w;
+ p += vw;
+ }
+ }
+}
+
+void
+swcurs_hide(SWcursor *swc)
+{
+ ++swc->hidecount;
+ swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_unhide(SWcursor *swc)
+{
+ if (--swc->hidecount < 0)
+ swc->hidecount = 0;
+ swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_enable(SWcursor *swc)
+{
+ swc->f |= CUR_ENA;
+ swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_disable(SWcursor *swc)
+{
+ swc->f &= ~CUR_ENA;
+ swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_load(SWcursor *swc, Cursor *c)
+{
+ int i, k;
+ uchar *bc, *bs, *cd, *cm;
+ static uchar bdv[4] = {0,Backgnd,Foregnd,0xff};
+ static uchar bmv[4] = {0xff,0,0,0xff};
+ int bits = 1<<swc->d;
+ uchar mask = (1<<bits)-1;
+ int bswp = (swc->f&CUR_SWP) ? 8-bits : 0;
+
+ bc = c->clr;
+ bs = c->set;
+
+ swcurs_hide(swc);
+ cd = swc->data;
+ cm = swc->mask;
+ swc->hotx = c->offset.x;
+ swc->hoty = c->offset.y;
+ swc->chgt = CURSHGT;
+ swc->cwid = CURSWID;
+ swc->cbwid = CURSWID*(1<<swc->d)/BI2BY;
+ for(i = 0; i < CURSWID/BI2BY*CURSHGT; i++) {
+ uchar bcb = *bc++;
+ uchar bsb = *bs++;
+ for(k=0; k<BI2BY;) {
+ uchar cdv = 0;
+ uchar cmv = 0;
+ int z;
+ for(z=0; z<BI2BY; z += bits) {
+ int n = ((bsb&(0x80))|((bcb&(0x80))<<1))>>7;
+ int s = z^bswp;
+ cdv |= (bdv[n]&mask) << s;
+ cmv |= (bmv[n]&mask) << s;
+ bcb <<= 1;
+ bsb <<= 1;
+ k++;
+ }
+ *cd++ = cdv;
+ *cm++ = cmv;
+ }
+ }
+ swcurs_unhide(swc);
+}
diff --git a/os/ipaq1110/screen.h b/os/ipaq1110/screen.h
new file mode 100644
index 00000000..2d42e9e0
--- /dev/null
+++ b/os/ipaq1110/screen.h
@@ -0,0 +1,64 @@
+typedef struct Cursor Cursor;
+typedef struct LCDmode LCDmode;
+typedef struct LCDparam LCDparam;
+typedef struct Vdisplay Vdisplay;
+typedef struct Vmode Vmode;
+
+#define CURSWID 16
+#define CURSHGT 16
+
+struct Cursor {
+ Point offset;
+ uchar clr[CURSWID/BI2BY*CURSHGT];
+ uchar set[CURSWID/BI2BY*CURSHGT];
+};
+
+struct Vmode {
+ int x; /* 0 -> default or any match for all fields */
+ int y;
+ uchar depth;
+ uchar hz;
+};
+
+struct Vdisplay {
+ uchar* fb; /* frame buffer */
+ ulong colormap[256][3];
+ int bwid;
+ Lock;
+ Vmode;
+};
+
+struct LCDparam {
+ uchar pbs;
+ uchar dual;
+ uchar mono;
+ uchar active;
+ uchar hsync_wid;
+ uchar sol_wait;
+ uchar eol_wait;
+ uchar vsync_hgt;
+ uchar sof_wait;
+ uchar eof_wait;
+ uchar lines_per_int;
+ uchar palette_delay;
+ uchar acbias_lines;
+ uchar obits;
+ uchar vsynclow;
+ uchar hsynclow;
+};
+
+struct LCDmode {
+ Vmode;
+ LCDparam;
+};
+
+int archlcdmode(LCDmode*);
+
+Vdisplay *lcd_init(LCDmode*);
+void lcd_setcolor(ulong, ulong, ulong, ulong);
+void lcd_flush(void);
+
+extern void blankscreen(int);
+extern void drawblankscreen(int);
+extern ulong blanktime;
+extern Point mousexy(void);
diff --git a/os/ipaq1110/tstdraw.b b/os/ipaq1110/tstdraw.b
new file mode 100644
index 00000000..dff42a2f
--- /dev/null
+++ b/os/ipaq1110/tstdraw.b
@@ -0,0 +1,107 @@
+implement Test;
+
+include "sys.m";
+
+include "draw.m";
+
+Test: module
+{
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+init(ctxt: ref Draw->Context, nil: list of string)
+{
+ sys := load Sys Sys->PATH;
+ draw := load Draw Draw->PATH;
+ Display, Font, Rect, Point, Image, Screen: import draw;
+
+ #
+ # Set up connection to display, or use the existing one
+ # if provided.
+ #
+ display: ref Display;
+ disp: ref Image;
+ if (ctxt == nil) {
+ display = draw->Display.allocate(nil);
+ disp = display.image;
+ } else {
+ display = ctxt.display;
+ disp = ctxt.screen.newwindow(display.image.r, Draw->White);
+ }
+
+ #
+ # Initialize colours.
+ #
+ red := display.color(Draw->Red);
+ blue := display.color(Draw->Blue);
+ white := display.color(Draw->White);
+ yellow := display.color(Draw->Yellow);
+ ones := display.ones;
+
+ #
+ # Paint the screen red.
+ #
+ disp.draw(disp.r, red, ones, disp.r.min);
+ sys->sleep(5000);
+
+ #
+ # Texture a region with rectangular tiles.
+ #
+ texture := display.newimage(((0,0),(2,3)), disp.ldepth, 1, 0);
+ texture.clipr = ((-10000,-10000),(10000,10000));
+ # put something in the texture
+ texture.draw(((0,0),(1,3)), blue, ones, (0,0));
+ texture.draw(((0,0),(2, 1)), blue, ones, (0,0));
+ # use texture as both source and mask to let
+ # destination colour show through
+ disp.draw(((100,100),(200,200)), texture, texture, (0,0));
+ sys->sleep(5000);
+
+ #
+ # White-out a quarter of the pixels in a region,
+ # to make the region appear shaded.
+ #
+ stipple := display.newimage(((0,0),(2,2)), disp.ldepth, 1, 0);
+ stipple.draw(((0,0),(1,1)), ones, ones, (0,0));
+ disp.draw(((100,100),(300,200)), white, stipple, (0,0));
+ sys->sleep(5000);
+
+ #
+ # Draw textured characters.
+ #
+ font := Font.open(display, "*default*");
+ disp.text((100,210), texture, (0,0), font, "Hello world");
+ sys->sleep(5000);
+
+ #
+ # Draw picture in elliptical frame.
+ #
+ delight := display.open("/icons/delight.bit");
+ piccenter := delight.r.min.add(delight.r.max).div(2);
+ disp.fillellipse((200,100), 150, 50, delight, piccenter);
+ disp.ellipse((200,100), 150, 50, 3, yellow, (0,0));
+ sys->sleep(5000);
+
+ #
+ # Draw a parabolic brush stroke using an elliptical brush
+ # to reveal more of the picture, consistent with what's
+ # already visible.
+ #
+ dx : con 15;
+ dy : con 3;
+ brush := display.newimage(((0,0),(2*dx+1,2*dy+1)), disp.ldepth,
+ 0, 0);
+ brush.fillellipse((dx,dy), dx, dy, ones, (0,0));
+ for(x:=delight.r.min.x; x<delight.r.max.x; x++){
+ y := (x-piccenter.x)*(x-piccenter.x)/80;
+ y += 2*dy+1; # so whole brush is visible at top
+ xx := x+(200-piccenter.x)-dx;
+ yy := y+(100-piccenter.y)-dy;
+ disp.gendraw(((xx,yy),(xx+2*dx+1,yy+2*dy+1)),
+ delight, (x-dx, y-dy), brush, (0,0));
+ }
+ for (i := 0; i < 500; i++) {
+ disp.draw(disp.r, disp, ones, (0, 10));
+ sys->sleep(5);
+ }
+}
diff --git a/os/ipaq1110/upd b/os/ipaq1110/upd
new file mode 100755
index 00000000..c4d69032
--- /dev/null
+++ b/os/ipaq1110/upd
@@ -0,0 +1,4 @@
+#!/dis/sh
+bind -a '#F' /dev
+echo erase all >/dev/flash/kernelctl
+dd -conv sync </os/ipaq1110/k.gz >/dev/flash/kernel