diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
| commit | 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch) | |
| tree | c6e220ba61db3a6ea4052e6841296d829654e664 /os/ipaq1110 | |
| parent | 46439007cf417cbd9ac8049bb4122c890097a0fa (diff) | |
20060303
Diffstat (limited to 'os/ipaq1110')
| -rwxr-xr-x | os/ipaq1110/Mk | 8 | ||||
| -rw-r--r-- | os/ipaq1110/NOTICE | 6 | ||||
| -rw-r--r-- | os/ipaq1110/README | 65 | ||||
| -rw-r--r-- | os/ipaq1110/archipaq.c | 376 | ||||
| -rw-r--r-- | os/ipaq1110/dat.h | 135 | ||||
| -rw-r--r-- | os/ipaq1110/defont.c | 290 | ||||
| -rw-r--r-- | os/ipaq1110/devaudio.c | 1056 | ||||
| -rw-r--r-- | os/ipaq1110/devipaq.c | 762 | ||||
| -rw-r--r-- | os/ipaq1110/etherwavelan.c | 1307 | ||||
| -rw-r--r-- | os/ipaq1110/fns.h | 179 | ||||
| -rw-r--r-- | os/ipaq1110/inflate | bin | 0 -> 20480 bytes | |||
| -rw-r--r-- | os/ipaq1110/io.h | 60 | ||||
| -rw-r--r-- | os/ipaq1110/ipaq | 172 | ||||
| -rw-r--r-- | os/ipaq1110/lcd.c | 185 | ||||
| -rw-r--r-- | os/ipaq1110/main.c | 255 | ||||
| -rw-r--r-- | os/ipaq1110/mem.h | 200 | ||||
| -rw-r--r-- | os/ipaq1110/mkfile | 103 | ||||
| -rw-r--r-- | os/ipaq1110/screen.c | 917 | ||||
| -rw-r--r-- | os/ipaq1110/screen.h | 64 | ||||
| -rw-r--r-- | os/ipaq1110/tstdraw.b | 107 | ||||
| -rwxr-xr-x | os/ipaq1110/upd | 4 |
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), <v->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, <v); +} + +static int +ltv_ins(Ctlr* ctlr, int type) +{ + Wltv ltv; + + ltv.len = 2; + ltv.type = type; + ltv.val = 0; + if(w_inltv(ctlr, <v)) + 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(<v, 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, <v); +} + +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(<v,0,sizeof(ltv)); + ltv.len = WNameLen/2+2; + ltv.type = type; + if (w_inltv(ctlr, <v)) + 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, <v); + + 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, <v, 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, <v)) + 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, <v)){ + 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 Binary files differnew file mode 100644 index 00000000..b5761204 --- /dev/null +++ b/os/ipaq1110/inflate 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 |
