xref: /plan9/sys/src/cmd/aux/vga/io.c (revision 8cf6001e50e647a07ccf484b8e2f9940411befb9)
1219b2ee8SDavid du Colombier #include <u.h>
2219b2ee8SDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
4219b2ee8SDavid du Colombier 
59a747e4fSDavid du Colombier #include "pci.h"
6219b2ee8SDavid du Colombier #include "vga.h"
7219b2ee8SDavid du Colombier 
87dd7cddfSDavid du Colombier int curprintindex;
97dd7cddfSDavid du Colombier 
10219b2ee8SDavid du Colombier static int iobfd = -1;
11219b2ee8SDavid du Colombier static int iowfd = -1;
12219b2ee8SDavid du Colombier static int iolfd = -1;
13219b2ee8SDavid du Colombier static int biosfd = -1;
1418027f8cSDavid du Colombier static ulong biosoffset = 0;
15219b2ee8SDavid du Colombier 
16219b2ee8SDavid du Colombier enum {
17219b2ee8SDavid du Colombier 	Nctlchar	= 256,
18219b2ee8SDavid du Colombier 	Nattr		= 16,
19219b2ee8SDavid du Colombier };
20219b2ee8SDavid du Colombier 
21219b2ee8SDavid du Colombier static int ctlfd = -1;
22219b2ee8SDavid du Colombier static char ctlbuf[Nctlchar];
237dd7cddfSDavid du Colombier static int ctlclean;
24219b2ee8SDavid du Colombier 
25219b2ee8SDavid du Colombier static struct {
26219b2ee8SDavid du Colombier 	char*	attr;
27219b2ee8SDavid du Colombier 	char*	val;
28219b2ee8SDavid du Colombier } attr[Nattr];
29219b2ee8SDavid du Colombier 
30219b2ee8SDavid du Colombier static int
devopen(char * device,int mode)31219b2ee8SDavid du Colombier devopen(char* device, int mode)
32219b2ee8SDavid du Colombier {
33219b2ee8SDavid du Colombier 	int fd;
34219b2ee8SDavid du Colombier 
35219b2ee8SDavid du Colombier 	if((fd = open(device, mode)) < 0)
36219b2ee8SDavid du Colombier 		error("devopen(%s, %d): %r\n", device, mode);
37219b2ee8SDavid du Colombier 	return fd;
38219b2ee8SDavid du Colombier }
39219b2ee8SDavid du Colombier 
40219b2ee8SDavid du Colombier uchar
inportb(long port)41219b2ee8SDavid du Colombier inportb(long port)
42219b2ee8SDavid du Colombier {
43219b2ee8SDavid du Colombier 	uchar data;
44219b2ee8SDavid du Colombier 
45219b2ee8SDavid du Colombier 	if(iobfd == -1)
467dd7cddfSDavid du Colombier 		iobfd = devopen("#P/iob", ORDWR);
47219b2ee8SDavid du Colombier 
489a747e4fSDavid du Colombier 	if(pread(iobfd, &data, sizeof(data), port) != sizeof(data))
497dd7cddfSDavid du Colombier 		error("inportb(0x%4.4lx): %r\n", port);
50219b2ee8SDavid du Colombier 	return data;
51219b2ee8SDavid du Colombier }
52219b2ee8SDavid du Colombier 
53219b2ee8SDavid du Colombier void
outportb(long port,uchar data)54219b2ee8SDavid du Colombier outportb(long port, uchar data)
55219b2ee8SDavid du Colombier {
56219b2ee8SDavid du Colombier 	if(iobfd == -1)
577dd7cddfSDavid du Colombier 		iobfd = devopen("#P/iob", ORDWR);
58219b2ee8SDavid du Colombier 
599a747e4fSDavid du Colombier 	if(pwrite(iobfd, &data, sizeof(data), port) != sizeof(data))
607dd7cddfSDavid du Colombier 		error("outportb(0x%4.4lx, 0x%2.2uX): %r\n", port, data);
61219b2ee8SDavid du Colombier }
62219b2ee8SDavid du Colombier 
63219b2ee8SDavid du Colombier ushort
inportw(long port)64219b2ee8SDavid du Colombier inportw(long port)
65219b2ee8SDavid du Colombier {
66219b2ee8SDavid du Colombier 	ushort data;
67219b2ee8SDavid du Colombier 
68219b2ee8SDavid du Colombier 	if(iowfd == -1)
697dd7cddfSDavid du Colombier 		iowfd = devopen("#P/iow", ORDWR);
70219b2ee8SDavid du Colombier 
719a747e4fSDavid du Colombier 	if(pread(iowfd, &data, sizeof(data), port) != sizeof(data))
727dd7cddfSDavid du Colombier 		error("inportw(0x%4.4lx): %r\n", port);
73219b2ee8SDavid du Colombier 	return data;
74219b2ee8SDavid du Colombier }
75219b2ee8SDavid du Colombier 
76219b2ee8SDavid du Colombier void
outportw(long port,ushort data)77219b2ee8SDavid du Colombier outportw(long port, ushort data)
78219b2ee8SDavid du Colombier {
79219b2ee8SDavid du Colombier 	if(iowfd == -1)
807dd7cddfSDavid du Colombier 		iowfd = devopen("#P/iow", ORDWR);
81219b2ee8SDavid du Colombier 
829a747e4fSDavid du Colombier 	if(pwrite(iowfd, &data, sizeof(data), port) != sizeof(data))
837dd7cddfSDavid du Colombier 		error("outportw(0x%4.4lx, 0x%2.2uX): %r\n", port, data);
84219b2ee8SDavid du Colombier }
85219b2ee8SDavid du Colombier 
86219b2ee8SDavid du Colombier ulong
inportl(long port)87219b2ee8SDavid du Colombier inportl(long port)
88219b2ee8SDavid du Colombier {
89219b2ee8SDavid du Colombier 	ulong data;
90219b2ee8SDavid du Colombier 
91219b2ee8SDavid du Colombier 	if(iolfd == -1)
927dd7cddfSDavid du Colombier 		iolfd = devopen("#P/iol", ORDWR);
93219b2ee8SDavid du Colombier 
949a747e4fSDavid du Colombier 	if(pread(iolfd, &data, sizeof(data), port) != sizeof(data))
957dd7cddfSDavid du Colombier 		error("inportl(0x%4.4lx): %r\n", port);
96219b2ee8SDavid du Colombier 	return data;
97219b2ee8SDavid du Colombier }
98219b2ee8SDavid du Colombier 
99219b2ee8SDavid du Colombier void
outportl(long port,ulong data)100219b2ee8SDavid du Colombier outportl(long port, ulong data)
101219b2ee8SDavid du Colombier {
102219b2ee8SDavid du Colombier 	if(iolfd == -1)
1037dd7cddfSDavid du Colombier 		iolfd = devopen("#P/iol", ORDWR);
104219b2ee8SDavid du Colombier 
1059a747e4fSDavid du Colombier 	if(pwrite(iolfd, &data, sizeof(data), port) != sizeof(data))
1067dd7cddfSDavid du Colombier 		error("outportl(0x%4.4lx, 0x%2.2luX): %r\n", port, data);
107219b2ee8SDavid du Colombier }
108219b2ee8SDavid du Colombier 
109219b2ee8SDavid du Colombier static void
vgactlinit(void)110219b2ee8SDavid du Colombier vgactlinit(void)
111219b2ee8SDavid du Colombier {
112219b2ee8SDavid du Colombier 	int nattr;
113219b2ee8SDavid du Colombier 	char *nl, *p, *vp;
114219b2ee8SDavid du Colombier 
1157dd7cddfSDavid du Colombier 	if(ctlclean)
1167dd7cddfSDavid du Colombier 		return;
1177dd7cddfSDavid du Colombier 
118219b2ee8SDavid du Colombier 	if(ctlfd == -1){
119219b2ee8SDavid du Colombier 		ctlfd = devopen("#v/vgactl", ORDWR);
120219b2ee8SDavid du Colombier 		memset(attr, 0, sizeof(attr));
121219b2ee8SDavid du Colombier 	}
122219b2ee8SDavid du Colombier 
123219b2ee8SDavid du Colombier 	seek(ctlfd, 0, 0);
1247dd7cddfSDavid du Colombier 	nattr = read(ctlfd, ctlbuf, Nctlchar-1);
1257dd7cddfSDavid du Colombier 	if(nattr < 0)
126219b2ee8SDavid du Colombier 		error("vgactlr: read: %r\n");
1277dd7cddfSDavid du Colombier 	ctlbuf[nattr] = 0;
128219b2ee8SDavid du Colombier 
129219b2ee8SDavid du Colombier 	nattr = 0;
130219b2ee8SDavid du Colombier 	vp = ctlbuf;
131219b2ee8SDavid du Colombier 	for(nl = strchr(ctlbuf, '\n'); nl; nl = strchr(nl, '\n')){
132219b2ee8SDavid du Colombier 
133219b2ee8SDavid du Colombier 		*nl = '\0';
1349a747e4fSDavid du Colombier 		if(p = strchr(vp, ' ')){
1359a747e4fSDavid du Colombier 			*p++ = '\0';
1369a747e4fSDavid du Colombier 			attr[nattr].attr = vp;
1379a747e4fSDavid du Colombier 			if(*p == '\0')
138219b2ee8SDavid du Colombier 				error("vgactlr: bad format: <%s>\n", vp);
139219b2ee8SDavid du Colombier 			attr[nattr].val = p;
140219b2ee8SDavid du Colombier 		}
141219b2ee8SDavid du Colombier 		else
142219b2ee8SDavid du Colombier 			error("vgactlr: bad format: <%s>\n", vp);
143219b2ee8SDavid du Colombier 
144219b2ee8SDavid du Colombier 		if(++nattr >= Nattr-2)
145219b2ee8SDavid du Colombier 			error("vgactlr: too many attributes: %d\n", nattr);
146219b2ee8SDavid du Colombier 		attr[nattr].attr = 0;
147219b2ee8SDavid du Colombier 
148219b2ee8SDavid du Colombier 		vp = ++nl;
149219b2ee8SDavid du Colombier 	}
1507dd7cddfSDavid du Colombier 
1517dd7cddfSDavid du Colombier 	ctlclean = 1;
152219b2ee8SDavid du Colombier }
153219b2ee8SDavid du Colombier 
154219b2ee8SDavid du Colombier char*
vgactlr(char * a,char * v)155219b2ee8SDavid du Colombier vgactlr(char* a, char* v)
156219b2ee8SDavid du Colombier {
157219b2ee8SDavid du Colombier 	int i;
158219b2ee8SDavid du Colombier 
1597dd7cddfSDavid du Colombier 	trace("vgactlr: look for %s\n", a);
160219b2ee8SDavid du Colombier 	vgactlinit();
161219b2ee8SDavid du Colombier 	for(i = 0; attr[i].attr; i++){
162219b2ee8SDavid du Colombier 		if(strcmp(attr[i].attr, a) == 0){
163219b2ee8SDavid du Colombier 			strcpy(v, attr[i].val);
1647dd7cddfSDavid du Colombier 			trace("vgactlr: value %s\n", v);
165219b2ee8SDavid du Colombier 			return v;
166219b2ee8SDavid du Colombier 		}
167219b2ee8SDavid du Colombier 	}
1687dd7cddfSDavid du Colombier 	trace("vgactlr: %s not found\n", a);
169219b2ee8SDavid du Colombier 
170219b2ee8SDavid du Colombier 	return 0;
171219b2ee8SDavid du Colombier }
172219b2ee8SDavid du Colombier 
173219b2ee8SDavid du Colombier void
vgactlw(char * attr,char * val)174219b2ee8SDavid du Colombier vgactlw(char* attr, char* val)
175219b2ee8SDavid du Colombier {
176219b2ee8SDavid du Colombier 	int len;
177219b2ee8SDavid du Colombier 	char buf[128];
178219b2ee8SDavid du Colombier 
179219b2ee8SDavid du Colombier 	if(ctlfd == -1)
180219b2ee8SDavid du Colombier 		ctlfd = devopen("#v/vgactl", ORDWR);
181219b2ee8SDavid du Colombier 
182219b2ee8SDavid du Colombier 	seek(ctlfd, 0, 0);
183219b2ee8SDavid du Colombier 	len = sprint(buf, "%s %s", attr, val);
1847dd7cddfSDavid du Colombier 	trace("+vgactlw %s\n", buf);
185219b2ee8SDavid du Colombier 	if(write(ctlfd, buf, len) != len)
186219b2ee8SDavid du Colombier 		error("vgactlw: <%s>: %r\n",  buf);
1877dd7cddfSDavid du Colombier 	trace("-vgactlw %s\n", buf);
1887dd7cddfSDavid du Colombier 
1897dd7cddfSDavid du Colombier 	ctlclean = 0;
190219b2ee8SDavid du Colombier }
191219b2ee8SDavid du Colombier 
1927dd7cddfSDavid du Colombier void
setpalette(int p,int r,int g,int b)1937dd7cddfSDavid du Colombier setpalette(int p, int r, int g, int b)
194219b2ee8SDavid du Colombier {
1957dd7cddfSDavid du Colombier 	vgao(PaddrW, p);
1967dd7cddfSDavid du Colombier 	vgao(Pdata, r);
1977dd7cddfSDavid du Colombier 	vgao(Pdata, g);
1987dd7cddfSDavid du Colombier 	vgao(Pdata, b);
1997dd7cddfSDavid du Colombier }
2007dd7cddfSDavid du Colombier 
2017dd7cddfSDavid du Colombier static long
doreadbios(char * buf,long len,long offset)2027dd7cddfSDavid du Colombier doreadbios(char* buf, long len, long offset)
2037dd7cddfSDavid du Colombier {
2049a747e4fSDavid du Colombier 	char file[64];
20559cc4ca5SDavid du Colombier 
20618027f8cSDavid du Colombier 	if(biosfd == -1){
2077dd7cddfSDavid du Colombier 		biosfd = open("#v/vgabios", OREAD);
20818027f8cSDavid du Colombier 		biosoffset = 0;
20918027f8cSDavid du Colombier 	}
21059cc4ca5SDavid du Colombier 	if(biosfd == -1){
2119a747e4fSDavid du Colombier 		snprint(file, sizeof file, "#p/%d/mem", getpid());
21259cc4ca5SDavid du Colombier 		biosfd = devopen(file, OREAD);
21318027f8cSDavid du Colombier 		biosoffset = 0x80000000;
21459cc4ca5SDavid du Colombier 	}
21518027f8cSDavid du Colombier 	if(biosfd == -1)
21618027f8cSDavid du Colombier 		return -1;
21718027f8cSDavid du Colombier 	seek(biosfd, biosoffset+offset, 0);
2187dd7cddfSDavid du Colombier 	return read(biosfd, buf, len);
2197dd7cddfSDavid du Colombier }
2207dd7cddfSDavid du Colombier 
2217dd7cddfSDavid du Colombier char*
readbios(long len,long offset)2227dd7cddfSDavid du Colombier readbios(long len, long offset)
2237dd7cddfSDavid du Colombier {
22459cc4ca5SDavid du Colombier 	static char bios[0x10000];
2257dd7cddfSDavid du Colombier 	static long biosoffset;
2267dd7cddfSDavid du Colombier 	static long bioslen;
2277dd7cddfSDavid du Colombier 	int n;
2287dd7cddfSDavid du Colombier 
2297dd7cddfSDavid du Colombier 	if(biosoffset <= offset && offset+len <= biosoffset+bioslen)
2307dd7cddfSDavid du Colombier 		return bios+(offset - biosoffset);
2317dd7cddfSDavid du Colombier 
2327dd7cddfSDavid du Colombier 	if(len > sizeof(bios))
2337dd7cddfSDavid du Colombier 		error("enormous bios len %ld at %lux\n", len, offset);
2347dd7cddfSDavid du Colombier 
2357dd7cddfSDavid du Colombier 	n = doreadbios(bios, sizeof(bios), offset);
2367dd7cddfSDavid du Colombier 	if(n < len)
2377dd7cddfSDavid du Colombier 		error("short bios read %ld at %lux got %d\n", len,offset, n);
2387dd7cddfSDavid du Colombier 
2397dd7cddfSDavid du Colombier 	biosoffset = offset;
2407dd7cddfSDavid du Colombier 	bioslen = n;
2417dd7cddfSDavid du Colombier 	return bios;
242219b2ee8SDavid du Colombier }
243219b2ee8SDavid du Colombier 
244219b2ee8SDavid du Colombier void
dumpbios(long size)2457dd7cddfSDavid du Colombier dumpbios(long size)
246219b2ee8SDavid du Colombier {
2477dd7cddfSDavid du Colombier 	uchar *buf;
248219b2ee8SDavid du Colombier 	long offset;
249219b2ee8SDavid du Colombier 	int i, n;
250219b2ee8SDavid du Colombier 	char c;
251219b2ee8SDavid du Colombier 
2527dd7cddfSDavid du Colombier 	buf = alloc(size);
253219b2ee8SDavid du Colombier 	offset = 0xC0000;
2547dd7cddfSDavid du Colombier 	if(doreadbios((char*)buf, size, offset) != size)
255*8cf6001eSDavid du Colombier 		error("short bios read in dumpbios\n");
2567dd7cddfSDavid du Colombier 
257219b2ee8SDavid du Colombier 	if(buf[0] != 0x55 || buf[1] != 0xAA){
258219b2ee8SDavid du Colombier 		offset = 0xE0000;
2597dd7cddfSDavid du Colombier 		if(doreadbios((char*)buf, size, offset) != size)
260*8cf6001eSDavid du Colombier 			error("short bios read in dumpbios\n");
2617dd7cddfSDavid du Colombier 		if(buf[0] != 0x55 || buf[1] != 0xAA){
2627dd7cddfSDavid du Colombier 			free(buf);
263219b2ee8SDavid du Colombier 			return;
264219b2ee8SDavid du Colombier 		}
2657dd7cddfSDavid du Colombier 	}
266219b2ee8SDavid du Colombier 
2677dd7cddfSDavid du Colombier 	for(i = 0; i < size; i += 16){
2687dd7cddfSDavid du Colombier 		Bprint(&stdout, "0x%luX", offset+i);
269219b2ee8SDavid du Colombier 		for(n = 0; n < 16; n++)
2707dd7cddfSDavid du Colombier 			Bprint(&stdout, " %2.2uX", buf[i+n]);
2717dd7cddfSDavid du Colombier 		Bprint(&stdout, "  ");
272219b2ee8SDavid du Colombier 		for(n = 0; n < 16; n++){
273219b2ee8SDavid du Colombier 			c = buf[i+n];
274219b2ee8SDavid du Colombier 			if(c < 0x20 || c >= 0x7F)
275219b2ee8SDavid du Colombier 				c = '.';
2767dd7cddfSDavid du Colombier 			Bprint(&stdout, "%c", c);
277219b2ee8SDavid du Colombier 		}
2787dd7cddfSDavid du Colombier 		Bprint(&stdout, "\n");
279219b2ee8SDavid du Colombier 	}
2807dd7cddfSDavid du Colombier 	free(buf);
281219b2ee8SDavid du Colombier }
282219b2ee8SDavid du Colombier 
283219b2ee8SDavid du Colombier void*
alloc(ulong nbytes)284219b2ee8SDavid du Colombier alloc(ulong nbytes)
285219b2ee8SDavid du Colombier {
286219b2ee8SDavid du Colombier 	void *v;
287219b2ee8SDavid du Colombier 
288219b2ee8SDavid du Colombier 	if((v = malloc(nbytes)) == 0)
2897dd7cddfSDavid du Colombier 		error("alloc: %lud bytes - %r\n", nbytes);
290219b2ee8SDavid du Colombier 
291219b2ee8SDavid du Colombier 	return memset(v, 0, nbytes);
292219b2ee8SDavid du Colombier }
293219b2ee8SDavid du Colombier 
294219b2ee8SDavid du Colombier void
printitem(char * ctlr,char * item)295219b2ee8SDavid du Colombier printitem(char* ctlr, char* item)
296219b2ee8SDavid du Colombier {
297219b2ee8SDavid du Colombier 	int n;
298219b2ee8SDavid du Colombier 
2997dd7cddfSDavid du Colombier 	if(curprintindex){
3007dd7cddfSDavid du Colombier 		curprintindex = 0;
3017dd7cddfSDavid du Colombier 		Bprint(&stdout, "\n");
302219b2ee8SDavid du Colombier 	}
303219b2ee8SDavid du Colombier 
304219b2ee8SDavid du Colombier 	n = 0;
305219b2ee8SDavid du Colombier 	if(ctlr && *ctlr)
3067dd7cddfSDavid du Colombier 		n = Bprint(&stdout, "%s ", ctlr);
3077dd7cddfSDavid du Colombier 	Bprint(&stdout, "%-*s", 20-n, item);
308219b2ee8SDavid du Colombier }
309219b2ee8SDavid du Colombier 
310219b2ee8SDavid du Colombier void
printreg(ulong data)311219b2ee8SDavid du Colombier printreg(ulong data)
312219b2ee8SDavid du Colombier {
313219b2ee8SDavid du Colombier 	int width;
314219b2ee8SDavid du Colombier 
315219b2ee8SDavid du Colombier 	width = 3;
3167dd7cddfSDavid du Colombier 	if((curprintindex % 16) == 0 && curprintindex){
3177dd7cddfSDavid du Colombier 		Bprint(&stdout, "\n");
3187dd7cddfSDavid du Colombier 		curprintindex = 0;
319219b2ee8SDavid du Colombier 		width = 23;
320219b2ee8SDavid du Colombier 	}
3217dd7cddfSDavid du Colombier 	if(curprintindex == 8)
3227dd7cddfSDavid du Colombier 		Bprint(&stdout, " -");
3237dd7cddfSDavid du Colombier 	Bprint(&stdout, "%*.2luX", width, data);
3247dd7cddfSDavid du Colombier 	curprintindex++;
325219b2ee8SDavid du Colombier }
326219b2ee8SDavid du Colombier 
3277dd7cddfSDavid du Colombier static char *flagname[32] = {
3287dd7cddfSDavid du Colombier 	[0x00]	"Fsnarf",
3297dd7cddfSDavid du Colombier 	[0x01]	"Foptions",
3307dd7cddfSDavid du Colombier 	[0x02]	"Finit",
3317dd7cddfSDavid du Colombier 	[0x03]	"Fload",
3327dd7cddfSDavid du Colombier 	[0x04]	"Fdump",
3337dd7cddfSDavid du Colombier 
3347dd7cddfSDavid du Colombier 	[0x08]	"Hpclk2x8",
3357dd7cddfSDavid du Colombier 	[0x09]	"Upclk2x8",
3367dd7cddfSDavid du Colombier 	[0x0A]	"Henhanced",
3377dd7cddfSDavid du Colombier 	[0x0B]	"Uenhanced",
3387dd7cddfSDavid du Colombier 	[0x0C]	"Hpvram",
3397dd7cddfSDavid du Colombier 	[0x0D]	"Upvram",
3407dd7cddfSDavid du Colombier 	[0x0E]	"Hextsid",
3417dd7cddfSDavid du Colombier 	[0x0F]	"Uextsid",
3427dd7cddfSDavid du Colombier 	[0x10]	"Hclk2",
3437dd7cddfSDavid du Colombier 	[0x11]	"Uclk2",
3447dd7cddfSDavid du Colombier 	[0x12]	"Hlinear",
3457dd7cddfSDavid du Colombier 	[0x13]	"Ulinear",
3467dd7cddfSDavid du Colombier 	[0x14]	"Hclkdiv",
3477dd7cddfSDavid du Colombier 	[0x15]	"Uclkdiv",
3487dd7cddfSDavid du Colombier 	[0x16]	"Hsid32",
3497dd7cddfSDavid du Colombier };
3507dd7cddfSDavid du Colombier 
351219b2ee8SDavid du Colombier void
printflag(ulong flag)3527dd7cddfSDavid du Colombier printflag(ulong flag)
353219b2ee8SDavid du Colombier {
3547dd7cddfSDavid du Colombier 	int i;
3557dd7cddfSDavid du Colombier 	char first;
356219b2ee8SDavid du Colombier 
3577dd7cddfSDavid du Colombier 	first = ' ';
3587dd7cddfSDavid du Colombier 	for(i = 31; i >= 0; i--){
3597dd7cddfSDavid du Colombier 		if((flag & (1<<i)) == 0)
3607dd7cddfSDavid du Colombier 			continue;
3617dd7cddfSDavid du Colombier 		if(flagname[i])
3627dd7cddfSDavid du Colombier 			Bprint(&stdout, "%c%s", first, flagname[i]);
3637dd7cddfSDavid du Colombier 		else
3647dd7cddfSDavid du Colombier 			Bprint(&stdout, "%c0x%x", first, 1<<i);
3657dd7cddfSDavid du Colombier 		first = '|';
366219b2ee8SDavid du Colombier 	}
367219b2ee8SDavid du Colombier }
368