 6b3f4dc157
			
		
	
	
		6b3f4dc157
		
	
	
	
	
		
			
			This commit separates the low-level keyboard driver from TTY, putting it in a separate driver (PCKBD). The commit also separates management of raw input devices from TTY, and puts it in a separate server (INPUT). All keyboard and mouse input from hardware is sent by drivers to the INPUT server, which either sends it to a process that has opened a raw input device, or otherwise forwards it to TTY for standard processing. Design by Dirk Vogt. Prototype by Uli Kastlunger. Additional changes made to the prototype: - the event communication is now based on USB HID codes; all input drivers have to use USB codes to describe events; - all TTY keymaps have been converted to USB format, with the effect that a single keymap covers all keys; there is no (static) escaped keymap anymore; - further keymap tweaks now allow remapping of literally all keys; - input device renumbering and protocol rewrite; - INPUT server rewrite, with added support for cancel and select; - PCKBD reimplementation, including PC/AT-to-USB translation; - support for manipulating keyboard LEDs has been added; - keyboard and mouse multiplexer devices have been added to INPUT, primarily so that an X server need only open two devices; - a new "libinputdriver" library abstracts away protocol details from input drivers, and should be used by all future input drivers; - both INPUT and PCKBD can be restarted; - TTY is now scheduled by KERNEL, so that it won't be punished for running a lot; without this, simply running "yes" on the console kills the system; - the KIOCBELL IOCTL has been moved to /dev/console; - support for the SCANCODES termios setting has been removed; - obsolete keymap compression has been removed; - the obsolete Olivetti M24 keymap has been removed. Change-Id: I3a672fb8c4fd566734e4b46d3994b4b7fc96d578
		
			
				
	
	
		
			528 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			528 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
 | |
| #define _SYSTEM 1
 | |
| 
 | |
| #include <minix/callnr.h>
 | |
| #include <minix/com.h>
 | |
| #include <minix/config.h>
 | |
| #include <minix/const.h>
 | |
| #include <minix/ds.h>
 | |
| #include <minix/endpoint.h>
 | |
| #include <minix/minlib.h>
 | |
| #include <minix/type.h>
 | |
| #include <minix/ipc.h>
 | |
| #include <minix/sysutil.h>
 | |
| #include <minix/syslib.h>
 | |
| #include <minix/bitmap.h>
 | |
| #include <minix/debug.h>
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <errno.h>
 | |
| #include <string.h>
 | |
| #include <env.h>
 | |
| 
 | |
| #include <sys/param.h>
 | |
| 
 | |
| #include "glo.h"
 | |
| #include "proto.h"
 | |
| #include "util.h"
 | |
| #include "sanitycheck.h"
 | |
| 
 | |
| #define SLABSIZES 200
 | |
| 
 | |
| #define ITEMSPERPAGE(bytes) (int)(DATABYTES / (bytes))
 | |
| 
 | |
| #define ELBITS		(sizeof(element_t)*8)
 | |
| #define BITPAT(b)	(1UL << ((b) %  ELBITS))
 | |
| #define BITEL(f, b)	(f)->sdh.usebits[(b)/ELBITS]
 | |
| 
 | |
| 
 | |
| #define OFF(f, b) assert(!GETBIT(f, b))
 | |
| #define ON(f, b)  assert(GETBIT(f, b))
 | |
| 
 | |
| #if MEMPROTECT
 | |
| #define SLABDATAWRITABLE(data, wr) do {			\
 | |
| 	assert(data->sdh.writable == WRITABLE_NONE);	\
 | |
| 	assert(wr != WRITABLE_NONE);			\
 | |
| 	vm_pagelock(data, 0);				\
 | |
| 	data->sdh.writable = wr;			\
 | |
| } while(0)
 | |
| 
 | |
| #define SLABDATAUNWRITABLE(data) do {			\
 | |
| 	assert(data->sdh.writable != WRITABLE_NONE);	\
 | |
| 	data->sdh.writable = WRITABLE_NONE;		\
 | |
| 	vm_pagelock(data, 1);				\
 | |
| } while(0)
 | |
| 
 | |
| #define SLABDATAUSE(data, code) do {			\
 | |
| 	SLABDATAWRITABLE(data, WRITABLE_HEADER);	\
 | |
| 	code						\
 | |
| 	SLABDATAUNWRITABLE(data);			\
 | |
| } while(0)
 | |
| 
 | |
| #else
 | |
| 
 | |
| #define SLABDATAWRITABLE(data, wr)
 | |
| #define SLABDATAUNWRITABLE(data)
 | |
| #define SLABDATAUSE(data, code) do { code } while(0)
 | |
| 
 | |
| #endif
 | |
| 
 | |
| #define GETBIT(f, b)	  (BITEL(f,b) &   BITPAT(b))
 | |
| #define SETBIT(f, b)   {OFF(f,b); SLABDATAUSE(f, BITEL(f,b)|= BITPAT(b); (f)->sdh.nused++;); }
 | |
| #define CLEARBIT(f, b) {ON(f, b); SLABDATAUSE(f, BITEL(f,b)&=~BITPAT(b); (f)->sdh.nused--; (f)->sdh.freeguess = (b);); }
 | |
| 
 | |
| #define OBJALIGN	8
 | |
| 
 | |
| #define MINSIZE 8
 | |
| #define MAXSIZE (SLABSIZES-1+MINSIZE)
 | |
| #define USEELEMENTS (1+(VM_PAGE_SIZE/MINSIZE/8))
 | |
| 
 | |
| static int pages = 0;
 | |
| 
 | |
| typedef u8_t element_t;
 | |
| #define BITS_FULL (~(element_t)0)
 | |
| typedef element_t elements_t[USEELEMENTS];
 | |
| 
 | |
| /* This file is too low-level to have global SANITYCHECKs everywhere,
 | |
|  * as the (other) data structures are often necessarily in an
 | |
|  * inconsistent state during a slaballoc() / slabfree(). So only do
 | |
|  * our own sanity checks here, with SLABSANITYCHECK.
 | |
|  */
 | |
| 
 | |
| 
 | |
| /* Special writable values. */
 | |
| #define WRITABLE_NONE	-2
 | |
| #define WRITABLE_HEADER	-1
 | |
| 
 | |
| struct sdh {
 | |
| #if SANITYCHECKS
 | |
| 	u32_t magic1;
 | |
| #endif
 | |
| 	int freeguess;
 | |
| 	struct slabdata *next, *prev;
 | |
| 	elements_t usebits;
 | |
| 	phys_bytes phys;
 | |
| #if SANITYCHECKS
 | |
| 	int writable;	/* data item number or WRITABLE_* */
 | |
| 	u32_t magic2;
 | |
| #endif
 | |
| 	u16_t nused;	/* Number of data items used in this slab. */
 | |
| };
 | |
| 
 | |
| #define DATABYTES	(VM_PAGE_SIZE-sizeof(struct sdh))
 | |
| 
 | |
| #define MAGIC1 0x1f5b842f
 | |
| #define MAGIC2 0x8bb5a420
 | |
| #define JUNK  0xdeadbeef
 | |
| #define NOJUNK 0xc0ffee
 | |
| 
 | |
| static struct slabheader {
 | |
| 	struct slabdata {
 | |
| 		u8_t 	data[DATABYTES];
 | |
| 		struct	sdh sdh;
 | |
| 	} *list_head;
 | |
| } slabs[SLABSIZES];
 | |
| 
 | |
| static int objstats(void *, int, struct slabheader **, struct slabdata
 | |
| 	**, int *);
 | |
| 
 | |
| #define GETSLAB(b, s) {			\
 | |
| 	int _gsi;				\
 | |
| 	assert((b) >= MINSIZE);	\
 | |
| 	_gsi = (b) - MINSIZE;		\
 | |
| 	assert((_gsi) < SLABSIZES);	\
 | |
| 	assert((_gsi) >= 0);		\
 | |
| 	s = &slabs[_gsi];			\
 | |
| }
 | |
| 
 | |
| /* move slabdata nw to slabheader sl under list number l. */
 | |
| #define ADDHEAD(nw, sl) {			\
 | |
| 	SLABDATAUSE(nw,				\
 | |
| 		(nw)->sdh.next = sl->list_head;	\
 | |
| 		(nw)->sdh.prev = NULL;);	\
 | |
| 	sl->list_head = nw;			\
 | |
| 	if((nw)->sdh.next) {			\
 | |
| 		SLABDATAUSE((nw)->sdh.next, \
 | |
| 			(nw)->sdh.next->sdh.prev = (nw););	\
 | |
| 	} \
 | |
| }
 | |
| 
 | |
| #define UNLINKNODE(node)	{				\
 | |
| 	struct slabdata *next, *prev;				\
 | |
| 	prev = (node)->sdh.prev;				\
 | |
| 	next = (node)->sdh.next;				\
 | |
| 	if(prev) { SLABDATAUSE(prev, prev->sdh.next = next;); }	\
 | |
| 	if(next) { SLABDATAUSE(next, next->sdh.prev = prev;); }	\
 | |
| }
 | |
| 
 | |
| static struct slabdata *newslabdata(void)
 | |
| {
 | |
| 	struct slabdata *n;
 | |
| 	phys_bytes p;
 | |
| 
 | |
| 	assert(sizeof(*n) == VM_PAGE_SIZE);
 | |
| 
 | |
| 	if(!(n = vm_allocpage(&p, VMP_SLAB))) {
 | |
| 		printf("newslabdata: vm_allocpage failed\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	memset(n->sdh.usebits, 0, sizeof(n->sdh.usebits));
 | |
| 	pages++;
 | |
| 
 | |
| 	n->sdh.phys = p;
 | |
| #if SANITYCHECKS
 | |
| 	n->sdh.magic1 = MAGIC1;
 | |
| 	n->sdh.magic2 = MAGIC2;
 | |
| #endif
 | |
| 	n->sdh.nused = 0;
 | |
| 	n->sdh.freeguess = 0;
 | |
| 
 | |
| #if SANITYCHECKS
 | |
| 	n->sdh.writable = WRITABLE_HEADER;
 | |
| 	SLABDATAUNWRITABLE(n);
 | |
| #endif
 | |
| 
 | |
| 	return n;
 | |
| }
 | |
| 
 | |
| #if SANITYCHECKS
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				checklist				     *
 | |
|  *===========================================================================*/
 | |
| static int checklist(char *file, int line,
 | |
| 	struct slabheader *s, int bytes)
 | |
| {
 | |
| 	struct slabdata *n = s->list_head;
 | |
| 	int ch = 0;
 | |
| 
 | |
| 	while(n) {
 | |
| 		int count = 0, i;
 | |
| #if SANITYCHECKS
 | |
| 		MYASSERT(n->sdh.magic1 == MAGIC1);
 | |
| 		MYASSERT(n->sdh.magic2 == MAGIC2);
 | |
| #endif
 | |
| 		MYASSERT(usedpages_add(n->sdh.phys, VM_PAGE_SIZE) == OK);
 | |
| 		if(n->sdh.prev)
 | |
| 			MYASSERT(n->sdh.prev->sdh.next == n);
 | |
| 		else
 | |
| 			MYASSERT(s->list_head == n);
 | |
| 		if(n->sdh.next) MYASSERT(n->sdh.next->sdh.prev == n);
 | |
| 		for(i = 0; i < USEELEMENTS*8; i++)
 | |
| 			if(i >= ITEMSPERPAGE(bytes))
 | |
| 				MYASSERT(!GETBIT(n, i));
 | |
| 			else
 | |
| 				if(GETBIT(n,i))
 | |
| 					count++;
 | |
| 		MYASSERT(count == n->sdh.nused);
 | |
| 		ch += count;
 | |
| 		n = n->sdh.next;
 | |
| 	}
 | |
| 
 | |
| 	return ch;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				void slab_sanitycheck			     *
 | |
|  *===========================================================================*/
 | |
| void slab_sanitycheck(char *file, int line)
 | |
| {
 | |
| 	int s;
 | |
| 	for(s = 0; s < SLABSIZES; s++) {
 | |
| 		checklist(file, line, &slabs[s], s + MINSIZE);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				int slabsane				     *
 | |
|  *===========================================================================*/
 | |
| int slabsane_f(char *file, int line, void *mem, int bytes)
 | |
| {
 | |
| 	struct slabheader *s;
 | |
| 	struct slabdata *f;
 | |
| 	int i;
 | |
| 
 | |
| 	bytes = roundup(bytes, OBJALIGN);
 | |
| 
 | |
| 	return (objstats(mem, bytes, &s, &f, &i) == OK);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #if SANITYCHECKS
 | |
| static int nojunkwarning = 0;
 | |
| #endif
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				void *slaballoc				     *
 | |
|  *===========================================================================*/
 | |
| void *slaballoc(int bytes)
 | |
| {
 | |
| 	int i;
 | |
| 	int count = 0;
 | |
| 	struct slabheader *s;
 | |
| 	struct slabdata *newslab;
 | |
| 	char *ret;
 | |
| 
 | |
| 	bytes = roundup(bytes, OBJALIGN);
 | |
| 
 | |
| 	SLABSANITYCHECK(SCL_FUNCTIONS);
 | |
| 
 | |
| 	/* Retrieve entry in slabs[]. */
 | |
| 	GETSLAB(bytes, s);
 | |
| 	assert(s);
 | |
| 
 | |
| 	if(!(newslab = s->list_head)) {
 | |
| 		/* Make sure there is something on the freelist. */
 | |
| 		newslab = newslabdata();
 | |
| 		if(!newslab) return NULL;
 | |
| 		ADDHEAD(newslab, s);
 | |
| 		assert(newslab->sdh.nused == 0);
 | |
| 	} else	assert(newslab->sdh.nused > 0);
 | |
| 	assert(newslab->sdh.nused < ITEMSPERPAGE(bytes));
 | |
| 
 | |
| 	SLABSANITYCHECK(SCL_DETAIL);
 | |
| 
 | |
| #if SANITYCHECKS
 | |
| 	assert(newslab->sdh.magic1 == MAGIC1);
 | |
| 	assert(newslab->sdh.magic2 == MAGIC2);
 | |
| #endif
 | |
| 
 | |
| 	for(i = newslab->sdh.freeguess;
 | |
| 		count < ITEMSPERPAGE(bytes); count++, i++) {
 | |
| 		i = i % ITEMSPERPAGE(bytes);
 | |
| 
 | |
| 		if(!GETBIT(newslab, i)) 
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	SLABSANITYCHECK(SCL_FUNCTIONS);
 | |
| 
 | |
| 	assert(count < ITEMSPERPAGE(bytes));
 | |
| 	assert(i >= 0 && i < ITEMSPERPAGE(bytes));
 | |
| 
 | |
| 	SETBIT(newslab, i);
 | |
| 	if(newslab->sdh.nused == ITEMSPERPAGE(bytes)) {
 | |
| 		UNLINKNODE(newslab);
 | |
| 		s->list_head = newslab->sdh.next;
 | |
| 	}
 | |
| 
 | |
| 	ret = ((char *) newslab) + i*bytes;
 | |
| 
 | |
| #if SANITYCHECKS
 | |
| #if MEMPROTECT
 | |
| 	nojunkwarning++;
 | |
| 	slabunlock(ret, bytes);
 | |
| 	nojunkwarning--;
 | |
| 	assert(!nojunkwarning);
 | |
| #endif
 | |
| 	*(u32_t *) ret = NOJUNK;
 | |
| #if MEMPROTECT
 | |
| 	slablock(ret, bytes);
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| 	SLABDATAUSE(newslab, newslab->sdh.freeguess = i+1;);
 | |
| 
 | |
| #if SANITYCHECKS
 | |
| 	if(bytes >= SLABSIZES+MINSIZE) {
 | |
| 		printf("slaballoc: odd, bytes %d?\n", bytes);
 | |
| 	}
 | |
| 
 | |
| 	if(!slabsane_f(__FILE__, __LINE__, ret, bytes))
 | |
| 		panic("slaballoc: slabsane failed");
 | |
| #endif
 | |
| 
 | |
| 	assert(!((vir_bytes) ret % OBJALIGN));
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				int objstats				     *
 | |
|  *===========================================================================*/
 | |
| static inline int objstats(void *mem, int bytes,
 | |
| 	struct slabheader **sp, struct slabdata **fp, int *ip)
 | |
| {
 | |
| #if SANITYCHECKS
 | |
| #define OBJSTATSCHECK(cond) \
 | |
| 	if(!(cond)) { \
 | |
| 		printf("VM: objstats: %s failed for ptr %p, %d bytes\n", \
 | |
| 			#cond, mem, bytes); \
 | |
| 		return EINVAL; \
 | |
| 	}
 | |
| #else
 | |
| #define OBJSTATSCHECK(cond)
 | |
| #endif
 | |
| 
 | |
| 	struct slabheader *s;
 | |
| 	struct slabdata *f;
 | |
| 	int i;
 | |
| 
 | |
| 	assert(!(bytes % OBJALIGN));
 | |
| 
 | |
| 	OBJSTATSCHECK((char *) mem >= (char *) VM_PAGE_SIZE);
 | |
| 
 | |
| #if SANITYCHECKS
 | |
| 	if(*(u32_t *) mem == JUNK && !nojunkwarning) {
 | |
| 		util_stacktrace();
 | |
| 		printf("VM: WARNING: JUNK seen in slab object, likely freed\n");
 | |
| 	}
 | |
| #endif
 | |
| 	/* Retrieve entry in slabs[]. */
 | |
| 	GETSLAB(bytes, s);
 | |
| 
 | |
| 	/* Round address down to VM_PAGE_SIZE boundary to get header. */
 | |
| 	f = (struct slabdata *) ((char *) mem - (vir_bytes) mem % VM_PAGE_SIZE);
 | |
| 
 | |
| #if SANITYCHECKS
 | |
| 	OBJSTATSCHECK(f->sdh.magic1 == MAGIC1);
 | |
| 	OBJSTATSCHECK(f->sdh.magic2 == MAGIC2);
 | |
| #endif
 | |
| 
 | |
| 	/* Make sure it's in range. */
 | |
| 	OBJSTATSCHECK((char *) mem >= (char *) f->data);
 | |
| 	OBJSTATSCHECK((char *) mem < (char *) f->data + sizeof(f->data));
 | |
| 
 | |
| 	/* Get position. */
 | |
| 	i = (char *) mem - (char *) f->data;
 | |
| 	OBJSTATSCHECK(!(i % bytes));
 | |
| 	i = i / bytes;
 | |
| 
 | |
| 	/* Make sure it is marked as allocated. */
 | |
| 	OBJSTATSCHECK(GETBIT(f, i));
 | |
| 
 | |
| 	/* return values */
 | |
| 	*ip = i;
 | |
| 	*fp = f;
 | |
| 	*sp = s;
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				void *slabfree				     *
 | |
|  *===========================================================================*/
 | |
| void slabfree(void *mem, int bytes)
 | |
| {
 | |
| 	int i;
 | |
| 	struct slabheader *s;
 | |
| 	struct slabdata *f;
 | |
| 
 | |
| 	bytes = roundup(bytes, OBJALIGN);
 | |
| 
 | |
| 	SLABSANITYCHECK(SCL_FUNCTIONS);
 | |
| 
 | |
| 	if(objstats(mem, bytes, &s, &f, &i) != OK) {
 | |
| 		panic("slabfree objstats failed");
 | |
| 	}
 | |
| 
 | |
| #if SANITYCHECKS
 | |
| 	if(*(u32_t *) mem == JUNK) {
 | |
| 		printf("VM: WARNING: likely double free, JUNK seen\n");
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| #if SANITYCHECKS
 | |
| #if MEMPROTECT
 | |
| 	slabunlock(mem, bytes);
 | |
| #endif
 | |
| #if JUNKFREE
 | |
| 	memset(mem, 0xa6, bytes);
 | |
| #endif
 | |
| 	*(u32_t *) mem = JUNK;
 | |
| 	nojunkwarning++;
 | |
| #if MEMPROTECT
 | |
| 	slablock(mem, bytes);
 | |
| #endif
 | |
| 	nojunkwarning--;
 | |
| 	assert(!nojunkwarning);
 | |
| #endif
 | |
| 
 | |
| 	/* Free this data. */
 | |
| 	CLEARBIT(f, i);
 | |
| 
 | |
| 	/* Check if this slab changes lists. */
 | |
| 	if(f->sdh.nused == 0) {
 | |
| 		UNLINKNODE(f);
 | |
| 		if(f == s->list_head) s->list_head = f->sdh.next;
 | |
| 		vm_freepages((vir_bytes) f, 1);
 | |
| 		SLABSANITYCHECK(SCL_DETAIL);
 | |
| 	} else if(f->sdh.nused == ITEMSPERPAGE(bytes)-1) {
 | |
| 		ADDHEAD(f, s);
 | |
| 	}
 | |
| 
 | |
| 	SLABSANITYCHECK(SCL_FUNCTIONS);
 | |
| 
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| #if MEMPROTECT
 | |
| /*===========================================================================*
 | |
|  *				void *slablock				     *
 | |
|  *===========================================================================*/
 | |
| void slablock(void *mem, int bytes)
 | |
| {
 | |
| 	int i;
 | |
| 	struct slabheader *s;
 | |
| 	struct slabdata *f;
 | |
| 
 | |
| 	bytes = roundup(bytes, OBJALIGN);
 | |
| 
 | |
| 	if(objstats(mem, bytes, &s, &f, &i) != OK)
 | |
| 		panic("slablock objstats failed");
 | |
| 
 | |
| 	SLABDATAUNWRITABLE(f);
 | |
| 
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				void *slabunlock			     *
 | |
|  *===========================================================================*/
 | |
| void slabunlock(void *mem, int bytes)
 | |
| {
 | |
| 	int i;
 | |
| 	struct slabheader *s;
 | |
| 	struct slabdata *f;
 | |
| 
 | |
| 	bytes = roundup(bytes, OBJALIGN);
 | |
| 
 | |
| 	if(objstats(mem, bytes, &s, &f, &i) != OK)
 | |
| 		panic("slabunlock objstats failed");
 | |
| 
 | |
| 	SLABDATAWRITABLE(f, i);
 | |
| 
 | |
| 	return;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #if SANITYCHECKS
 | |
| /*===========================================================================*
 | |
|  *				void slabstats				     *
 | |
|  *===========================================================================*/
 | |
| void slabstats(void)
 | |
| {
 | |
| 	int s, totalbytes = 0;
 | |
| 	static int n;
 | |
| 	n++;
 | |
| 	if(n%1000) return;
 | |
| 	for(s = 0; s < SLABSIZES; s++) {
 | |
| 		int b, t;
 | |
| 		b = s + MINSIZE;
 | |
| 		t = checklist(__FILE__, __LINE__, &slabs[s], b);
 | |
| 
 | |
| 		if(t > 0) {
 | |
| 			int bytes = t * b;
 | |
| 			printf("VMSTATS: %2d slabs: %d (%dkB)\n", b, t, bytes/1024);
 | |
| 			totalbytes += bytes;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if(pages > 0) {
 | |
| 		printf("VMSTATS: %dK net used in slab objects in %d pages (%dkB): %d%% utilization\n",
 | |
| 			totalbytes/1024, pages, pages*VM_PAGE_SIZE/1024,
 | |
| 				100 * totalbytes / (pages*VM_PAGE_SIZE));
 | |
| 	}
 | |
| }
 | |
| #endif
 |