# # Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher # --- linux-2.4.28/CREDITS~colinux-0.6.1 2004-11-17 06:54:20.000000000 -0500 +++ linux-2.4.28/CREDITS 2004-11-27 15:59:21.283010512 -0500 @@ -54,6 +54,12 @@ S: CH-1015 Lausanne S: Switzerland +A: Dan Aloni +E: da-x@gmx.net +D: Cooperative Linux +D: Various kernel patches +S: Israel + N: Tim Alpaerts E: tim_alpaerts@toyota-motor-europe.com D: 802.2 class II logical link control layer, --- linux-2.4.28/Documentation/devices.txt~colinux-0.6.1 2004-02-18 08:36:30.000000000 -0500 +++ linux-2.4.28/Documentation/devices.txt 2004-11-27 15:59:21.285010208 -0500 @@ -1926,6 +1926,13 @@ 17 = /dev/cosa1c1 2nd board, 2nd channel ... + block coLinux Block devices + 0 = /dev/cobd0 First coLinux device + 1 = /dev/cobd1 Second coLinux device + ... + + These block devices map to files or devices in the host FS. + 118 char Solidum ??? 0 = /dev/solnp0 1 = /dev/solnp1 --- linux-2.4.28/Makefile~colinux-0.6.1 2004-11-17 06:54:22.000000000 -0500 +++ linux-2.4.28/Makefile 2004-11-27 15:59:30.235649504 -0500 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 28 -EXTRAVERSION = +EXTRAVERSION = -co-0.6.1 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -27,7 +27,11 @@ AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld -CC = $(CROSS_COMPILE)gcc +ifeq ($(GCCTRACE),Y) +CC = $(CROSS_COMPILE)$(COLINUX_ROOT)/bin/tracewrapper.py gcc +else +CC = $(CROSS_COMPILE)gcc +endif CPP = $(CC) -E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm --- linux-2.4.28/arch/i386/config.in~colinux-0.6.1 2004-11-17 06:54:21.000000000 -0500 +++ linux-2.4.28/arch/i386/config.in 2004-11-27 15:59:21.286010056 -0500 @@ -226,13 +226,20 @@ bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR bool 'Symmetric multi-processing support' CONFIG_SMP if [ "$CONFIG_SMP" != "y" ]; then - bool 'Local APIC support on uniprocessors' CONFIG_X86_UP_APIC - dep_bool 'IO-APIC support on uniprocessors' CONFIG_X86_UP_IOAPIC $CONFIG_X86_UP_APIC - if [ "$CONFIG_X86_UP_APIC" = "y" ]; then - define_bool CONFIG_X86_LOCAL_APIC y - fi - if [ "$CONFIG_X86_UP_IOAPIC" = "y" ]; then - define_bool CONFIG_X86_IO_APIC y + if [ "$CONFIG_COOPERATIVE" = "y" ]; then + bool 'Cooperative PIC (COPIC) support' CONFIG_X86_UP_COPIC + if [ "$CONFIG_X86_UP_COPIC" = "y" ]; then + define_bool CONFIG_X86_COPIC y + fi + else + bool 'Local APIC support on uniprocessors' CONFIG_X86_UP_APIC + dep_bool 'IO-APIC support on uniprocessors' CONFIG_X86_UP_IOAPIC $CONFIG_X86_UP_APIC + if [ "$CONFIG_X86_UP_APIC" = "y" ]; then + define_bool CONFIG_X86_LOCAL_APIC y + fi + if [ "$CONFIG_X86_UP_IOAPIC" = "y" ]; then + define_bool CONFIG_X86_IO_APIC y + fi fi else int 'Maximum number of CPUs (2-32)' CONFIG_NR_CPUS 32 @@ -343,12 +350,13 @@ bool ' Use real mode APM BIOS call to power off' CONFIG_APM_REAL_MODE_POWER_OFF fi +bool 'Cooperative Mode' CONFIG_COOPERATIVE + source drivers/acpi/Config.in endmenu source drivers/mtd/Config.in - source drivers/parport/Config.in source drivers/pnp/Config.in @@ -445,7 +453,11 @@ if [ "$CONFIG_VT" = "y" ]; then mainmenu_option next_comment comment 'Console drivers' - bool 'VGA text console' CONFIG_VGA_CONSOLE + if [ "$CONFIG_COOPERATIVE" = "n" ]; then + bool 'VGA text console' CONFIG_VGA_CONSOLE + else + bool 'coLinux Pseudo-VGA text console' CONFIG_COOPERATIVE_CONSOLE + fi bool 'Video mode selection support' CONFIG_VIDEO_SELECT if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'MDA text console (dual-headed) (EXPERIMENTAL)' CONFIG_MDA_CONSOLE --- linux-2.4.28/arch/i386/kernel/Makefile~colinux-0.6.1 2003-11-28 13:26:19.000000000 -0500 +++ linux-2.4.28/arch/i386/kernel/Makefile 2004-11-27 15:59:21.287009904 -0500 @@ -18,7 +18,7 @@ obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \ - pci-dma.o i386_ksyms.o i387.o bluesmoke.o dmi_scan.o + pci-dma.o i386_ksyms.o i387.o bluesmoke.o dmi_scan.o cooperative.o \ ifdef CONFIG_PCI --- /dev/null 2004-04-06 13:56:48.000000000 -0400 +++ linux-2.4.28/arch/i386/kernel/cooperative.c 2004-11-27 15:59:21.287009904 -0500 @@ -0,0 +1,160 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +CO_TRACE_STOP; + + +/* + * The next asm code is the first Linux code that runs in the + * coLinux kernel context. It receives %ecx which contains the + * address of the passage page. The passage page code sets %ecx + * to this value in its context restore part. + */ + +asm( + "" + ".section .text\n" + ".globl colinux_start\n" + "colinux_start:\n" + " call colinux_start_arch\n" + ".previous\n" + ""); + +static void colinux_early_cpu_init(void) +{ + /* + * On the first switch to Linux we must set up a valid TR because + * the passage page code assumes such one exists. This is basically + * copied code from cpu_init(). + * + * P.S this is protected by CO_TRACE_STOP so that we don't + * have a monitor context switch. + */ + + int nr = smp_processor_id(); + struct tss_struct * t = &init_tss[nr]; + + set_tss_desc(nr,t); + gdt_table[__TSS(nr)].b &= 0xfffffdff; + load_TR(nr); + gdt_table[__TSS(nr)].b &= 0xfffffdff; + + __asm__ __volatile__("movl %%cr4, %0" : "=r" (mmu_cr4_features)); +} + +void colinux_start_arch() +{ + colinux_early_cpu_init(); + colinux_start_c(); +} + +extern void ctrl_alt_del(void); + +void co_handle_device_interrupt(co_linux_message_t *message) +{ + unsigned long flags; + + local_irq_save(flags); + + switch (message->device) { + case CO_DEVICE_TIMER: { + co_linux_message_idle_t data = *(co_linux_message_idle_t *)message->data; + + if (data.tick_count > HZ) { + xtime.tv_sec += data.tick_count / HZ; + data.tick_count -= ((data.tick_count / HZ) * HZ); + } + + + while (data.tick_count > 0) { + struct pt_regs regs = {0, }; + regs.orig_eax = TIMER_IRQ; + do_IRQ(regs); + + data.tick_count--; + } + break; + } + case CO_DEVICE_POWER: { + co_linux_message_power_t *type = (co_scan_code_t *)message->data; + switch (type->type) { + case CO_LINUX_MESSAGE_POWER_ALT_CTRL_DEL: { + ctrl_alt_del(); + break; + } + } + break; + } + + case CO_DEVICE_KEYBOARD: { + struct pt_regs regs; + co_received_message(message); + regs.orig_eax = KEYBOARD_IRQ; + do_IRQ(regs); + break; + } + + case CO_DEVICE_NETWORK: { + struct pt_regs regs; + if (message->size > 1600) { + printk("co_handle_device_interrupt: warning, packet len: %d\n", message->size); + break; + } + co_received_message(message); + regs.orig_eax = NETWORK_IRQ; + do_IRQ(regs); + break; + } + default: + break; + } + + local_irq_restore(flags); +} + +void co_switch_wrapper_protected(void) +{ + /* + * We don't trust the passage page code to safely restore %gs and %fs. + * + * This wrapper ensures that if %fs or %gs are invalid, the processes + * exits with a segmentation fault rather than bringing down the + * machine. + **/ + unsigned long fs = 0; + unsigned long gs = 0; + + asm volatile("movl %%fs,%0": "=m" (fs)); + asm volatile("movl %%gs,%0": "=m" (gs)); + + /* + * Nullify the registers so the passage page code restores to + * null segment values on return. + */ + asm volatile("movl %0, %%fs; movl %0, %%gs" : : "r" (0)); + + /* And switch... */ + co_switch(); + + /* + * Safely restore the registers. + */ + loadsegment(fs, fs); + loadsegment(gs, gs); +} + +void co_switch_wrapper(void) +{ + co_switch_wrapper_protected(); +} + +CO_TRACE_CONTINUE; + --- linux-2.4.28/arch/i386/kernel/head.S~colinux-0.6.1 2003-11-28 13:26:19.000000000 -0500 +++ linux-2.4.28/arch/i386/kernel/head.S 2004-11-27 15:59:21.287009904 -0500 @@ -320,7 +320,7 @@ ret ENTRY(stack_start) - .long SYMBOL_NAME(init_task_union)+8192 + .long SYMBOL_NAME(init_task_union)+8192-100 .long __KERNEL_DS /* This is the default interrupt "handler" :-) */ @@ -361,12 +361,14 @@ ALIGN .word 0 +.globl idt_descr idt_descr: .word IDT_ENTRIES*8-1 # idt contains 256 entries SYMBOL_NAME(idt): .long SYMBOL_NAME(idt_table) .word 0 +.globl gdt_descr gdt_descr: .word GDT_ENTRIES*8-1 SYMBOL_NAME(gdt): --- linux-2.4.28/arch/i386/kernel/i387.c~colinux-0.6.1 2003-08-25 07:44:39.000000000 -0400 +++ linux-2.4.28/arch/i386/kernel/i387.c 2004-11-27 15:59:21.288009752 -0500 @@ -342,6 +342,8 @@ return 1; } +CO_TRACE_STOP; + int save_i387( struct _fpstate *buf ) { if ( !current->used_math ) @@ -363,6 +365,8 @@ } } +CO_TRACE_CONTINUE; + static inline int restore_i387_fsave( struct _fpstate *buf ) { struct task_struct *tsk = current; @@ -383,6 +387,8 @@ return err ? 1 : convert_fxsr_from_user( &tsk->thread.i387.fxsave, buf ); } +CO_TRACE_STOP; + int restore_i387( struct _fpstate *buf ) { int err; @@ -400,6 +406,8 @@ return err; } +CO_TRACE_CONTINUE; + /* * ptrace request handlers. */ --- linux-2.4.28/arch/i386/kernel/i8259.c~colinux-0.6.1 2004-08-07 19:26:04.000000000 -0400 +++ linux-2.4.28/arch/i386/kernel/i8259.c 2004-11-27 15:59:21.288009752 -0500 @@ -24,6 +24,7 @@ #include #include +#include /* * Common place to define all x86 IRQ vectors @@ -121,6 +122,81 @@ #undef IRQ #undef IRQLIST_16 +#ifdef CONFIG_COOPERATIVE + +CO_TRACE_STOP; + +void proxy_interrupt_handler(unsigned long interrupt, struct pt_regs regs) +{ + co_passage_page->operation = CO_OPERATION_FORWARD_INTERRUPT; + co_passage_page->params[0] = interrupt + 0x20; + co_passage_page->params[1] = regs.eip; + co_passage_page->params[2] = (unsigned long)(&((&interrupt)[10])); + co_passage_page->host_state.flags &= ~(1 << 9); /* Turn IF off */ + + co_switch_wrapper(); + co_callback(); +} + +CO_TRACE_CONTINUE; + +#define IRQLIST_16(x) \ + IRQ(x,0) IRQ(x,1) IRQ(x,2) IRQ(x,3) \ + IRQ(x,4) IRQ(x,5) IRQ(x,6) IRQ(x,7) \ + IRQ(x,8) IRQ(x,9) IRQ(x,a) IRQ(x,b) \ + IRQ(x,c) IRQ(x,d) IRQ(x,e) IRQ(x,f) + +#define IRQLIST_224 \ + IRQLIST_16(0x0) IRQLIST_16(0x1) IRQLIST_16(0x2) IRQLIST_16(0x3) \ + IRQLIST_16(0x4) IRQLIST_16(0x5) IRQLIST_16(0x6) IRQLIST_16(0x7) \ + IRQLIST_16(0x8) IRQLIST_16(0x9) IRQLIST_16(0xa) IRQLIST_16(0xb) \ + IRQLIST_16(0xc) IRQLIST_16(0xd) + +#define IRQ(x,y) \ + extern asmlinkage void IRQ_proxy_##x##y##_interrupt(void); +IRQLIST_224; +#undef IRQ + +struct _fpstate co_interrupt_register_state; + +#define BIRQ(id) \ +asm( \ + "\n"__ALIGN_STR"\n" \ + ".section .text\n" \ + ".globl IRQ_proxy_" #id "_interrupt\n" \ + "IRQ_proxy_" #id "_interrupt:\n" \ + "push %eax\n\t" \ + SAVE_ALL \ + \ + "pushl $co_interrupt_register_state\n\t" \ + "call save_i387\n\t" \ + "popl %ebx\n\t" \ + "pushl $" #id "\n\t" \ + "call proxy_interrupt_handler\n\t" \ + "popl %ebx\n\t" \ + "pushl $co_interrupt_register_state\n\t" \ + "call restore_i387\n\t" \ + "popl %ebx\n\t" \ + \ + "jmp ret_from_intr\n" \ + ".previous\n" \ + ); \ + +#define IRQ(x,y) BIRQ(x##y) +IRQLIST_224; +#undef IRQ + +#define IRQ(x,y) &IRQ_proxy_##x##y##_interrupt, +void (*proxy_interrupt[NR_IRQS])(void) = { + IRQLIST_224 +}; +#undef IRQ + +#undef IRQLIST_16 +#undef IRQLIST_224 + +#endif + /* * This is the 'legacy' 8259A Programmable Interrupt Controller, * present in the majority of PC/AT boxes. @@ -441,15 +517,74 @@ } } +#ifdef CONFIG_X86_COPIC + +/* + * Not like you have any other choice other than using + * COPIC in Cooperative mode. + */ + +static void end_COPIC_irq(unsigned int irq) +{ +} + +#define shutdown_COPIC_irq disable_COPIC_irq + +static void mask_and_ack_COPIC(unsigned int irq) +{ +} + +static unsigned int startup_COPIC_irq(unsigned int irq) +{ + return 0; +} + +void disable_COPIC_irq(unsigned int irq) +{ +} + +void enable_COPIC_irq(unsigned int irq) +{ +} + +static struct hw_interrupt_type co_pic_irq_type = { + "CO-PIC", + startup_COPIC_irq, + shutdown_COPIC_irq, + enable_COPIC_irq, + disable_COPIC_irq, + mask_and_ack_COPIC, + end_COPIC_irq, + NULL +}; + +void __init init_COPIC_irqs(void) +{ + int i; + + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = 0; + irq_desc[i].depth = 1; + + irq_desc[i].handler = &co_pic_irq_type; + } + +} + +#endif + void __init init_IRQ(void) { int i; +#ifndef CONFIG_COOPERATIVE #ifndef CONFIG_X86_VISWS_APIC init_ISA_irqs(); #else init_VISWS_APIC_irqs(); #endif + /* * Cover the whole vector space, no vector can escape * us. (some of these will be overridden and become @@ -490,6 +625,8 @@ set_intr_gate(ERROR_APIC_VECTOR, error_interrupt); #endif + +#ifndef CONFIG_COOPERATIVE /* * Set the clock to HZ Hz, we already have a valid * vector now: @@ -509,4 +646,20 @@ */ if (boot_cpu_data.hard_math && !cpu_has_fpu) setup_irq(13, &irq13); +#endif + +#else + printk("Setting proxy interrupt vectors\n"); + +#ifdef CONFIG_X86_COPIC + init_COPIC_irqs(); +#endif + + for (i = 0; i < NR_IRQS; i++) { + int vector = FIRST_EXTERNAL_VECTOR + i; + if (vector != SYSCALL_VECTOR) { + set_intr_gate(vector, proxy_interrupt[i]); + } + } +#endif } --- linux-2.4.28/arch/i386/kernel/ioport.c~colinux-0.6.1 2003-06-13 10:51:29.000000000 -0400 +++ linux-2.4.28/arch/i386/kernel/ioport.c 2004-11-27 15:59:21.289009600 -0500 @@ -54,6 +54,7 @@ */ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) { +#ifndef CONFIG_COOPERATIVE struct thread_struct * t = ¤t->thread; struct tss_struct * tss = init_tss + smp_processor_id(); @@ -86,6 +87,10 @@ } return 0; +#else + /* No ports for yer VM */ + return -EPERM; +#endif } /* @@ -101,6 +106,7 @@ asmlinkage int sys_iopl(unsigned long unused) { +#ifndef CONFIG_COOPERATIVE struct pt_regs * regs = (struct pt_regs *) &unused; unsigned int level = regs->ebx; unsigned int old = (regs->eflags >> 12) & 3; @@ -114,4 +120,8 @@ } regs->eflags = (regs->eflags & 0xffffcfff) | (level << 12); return 0; +#else + /* No IPL for yer VM */ + return -EPERM; +#endif } --- linux-2.4.28/arch/i386/kernel/process.c~colinux-0.6.1 2004-11-17 06:54:21.000000000 -0500 +++ linux-2.4.28/arch/i386/kernel/process.c 2004-11-27 15:59:21.289009600 -0500 @@ -51,6 +51,7 @@ #include #include +#include asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); @@ -131,11 +132,15 @@ current->counter = -100; while (1) { +#ifdef CONFIG_COOPERATIVE + co_idle_processor(); +#else void (*idle)(void) = pm_idle; if (!idle) idle = default_idle; while (!current->need_resched) idle(); +#endif schedule(); check_pgt_cache(); } @@ -280,6 +285,7 @@ */ void machine_real_restart(unsigned char *code, int length) { +#ifndef CONFIG_COOPERATIVE unsigned long flags; cli(); @@ -363,10 +369,14 @@ __asm__ __volatile__ ("ljmp $0x0008,%0" : : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100))); +#else + co_terminate(CO_TERMINATE_REBOOT); +#endif } void machine_restart(char * __unused) { +#ifndef CONFIG_COOPERATIVE #if CONFIG_SMP int cpuid; @@ -430,16 +440,26 @@ } machine_real_restart(jump_to_bios, sizeof(jump_to_bios)); +#else + co_terminate(CO_TERMINATE_REBOOT); +#endif } void machine_halt(void) { +#ifdef CONFIG_COOPERATIVE + co_terminate(CO_TERMINATE_HALT); +#endif } void machine_power_off(void) { +#ifndef CONFIG_COOPERATIVE if (pm_power_off) pm_power_off(); +#else + co_terminate(CO_TERMINATE_POWEROFF); +#endif } extern void show_trace(unsigned long* esp); @@ -657,12 +677,20 @@ */ tss->esp0 = next->esp0; +#ifdef CONFIG_COOPERATIVE + /* + * We would save %fs and %gs using an atomic operation in the + * just before the LDT of the next process is loaded. It is + * not here, it's in... + */ +#else /* * Save away %fs and %gs. No need to save %es and %ds, as * those are always kernel segments while inside the kernel. */ asm volatile("movl %%fs,%0":"=m" (*(int *)&prev->fs)); asm volatile("movl %%gs,%0":"=m" (*(int *)&prev->gs)); +#endif /* * Restore %fs and %gs. --- linux-2.4.28/arch/i386/kernel/setup.c~colinux-0.6.1 2004-08-07 19:26:04.000000000 -0400 +++ linux-2.4.28/arch/i386/kernel/setup.c 2004-11-27 15:59:21.291009296 -0500 @@ -108,6 +108,7 @@ #include #include #include +#include #include #include #include @@ -782,9 +783,18 @@ int len = 0; int userdef = 0; +#ifdef CONFIG_COOPERATIVE + /* + * Better to have 'root=/dev/cobd0' here. + */ + from = co_boot_parameters; + snprintf(saved_command_line, COMMAND_LINE_SIZE, "%s", + co_boot_parameters); +#else /* Save unparsed command line copy for /proc/cmdline */ memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; +#endif for (;;) { if (c != ' ') @@ -1044,16 +1054,23 @@ static unsigned long __init setup_memory(void) { unsigned long bootmap_size, start_pfn, max_low_pfn; - +#ifdef CONFIG_COOPERATIVE + unsigned long bootmem_size; + unsigned long bootmem_start; +#endif /* * partially used pages are not usable - thus * we are rounding upwards: */ start_pfn = PFN_UP(__pa(&_end)); +#ifdef CONFIG_COOPERATIVE + max_low_pfn = max_pfn = co_memory_size / PAGE_SIZE; +#else find_max_pfn(); max_low_pfn = find_max_low_pfn(); +#endif #ifdef CONFIG_HIGHMEM highstart_pfn = highend_pfn = max_pfn; @@ -1065,12 +1082,19 @@ #endif printk(KERN_NOTICE "%ldMB LOWMEM available.\n", pages_to_mb(max_low_pfn)); + /* * Initialize the boot-time allocator (with low memory only): */ bootmap_size = init_bootmem(start_pfn, max_low_pfn); - register_bootmem_low_pages(max_low_pfn); +#ifdef CONFIG_COOPERATIVE + bootmap_size = (bootmap_size + PAGE_SIZE - 1) & PAGE_MASK; + bootmem_size = (0x800 << PAGE_SHIFT) - bootmap_size; + bootmem_start = co_core_end + bootmap_size; + + free_bootmem(__pa(bootmem_start), bootmem_size); +#else /* * Reserve the bootmem bitmap itself as well. We do this in two @@ -1124,7 +1148,7 @@ } } #endif - +#endif return max_low_pfn; } @@ -1176,7 +1200,9 @@ void __init setup_arch(char **cmdline_p) { +#ifndef CONFIG_COOPERATIVE unsigned long max_low_pfn; +#endif #ifdef CONFIG_VISWS visws_get_board_type_and_rev(); @@ -1186,6 +1212,7 @@ blk_nohighio = 1; #endif + boot_cpu_data.hard_math = 1; ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); drive_info = DRIVE_INFO; screen_info = SCREEN_INFO; @@ -1203,7 +1230,10 @@ rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); #endif + +#ifndef CONFIG_COOPERATIVE setup_memory_region(); +#endif copy_edd(); if (!MOUNT_ROOT_RDONLY) @@ -1230,8 +1260,10 @@ #ifdef CONFIG_SMP smp_alloc_memory(); /* AP processor realmode stacks in low memory*/ #endif + paging_init(); +#ifndef CONFIG_COOPERATIVE dmi_scan_machine(); /* @@ -1248,14 +1280,19 @@ #endif register_memory(max_low_pfn); +#endif #ifdef CONFIG_VT +#ifdef CONFIG_COOPERATIVE_CONSOLE + conswitchp = &colinux_con; +#else #if defined(CONFIG_VGA_CONSOLE) conswitchp = &vga_con; #elif defined(CONFIG_DUMMY_CONSOLE) conswitchp = &dummy_con; #endif #endif +#endif } static int cachesize_override __initdata = -1; @@ -3216,29 +3253,41 @@ if(current->mm) BUG(); enter_lazy_tlb(&init_mm, current, nr); - t->esp0 = current->thread.esp0; + set_tss_desc(nr,t); gdt_table[__TSS(nr)].b &= 0xfffffdff; + load_TR(nr); load_LDT(&init_mm.context); /* * Clear all 6 debug registers: */ - +#ifndef CONFIG_COOPERATIVE #define CD(register) __asm__("movl %0,%%db" #register ::"r"(0) ); CD(0); CD(1); CD(2); CD(3); /* no db4 and db5 */; CD(6); CD(7); #undef CD - +#endif /* * Force FPU initialization: */ current->flags &= ~PF_USEDFPU; current->used_math = 0; +#ifndef CONFIG_COOPERATIVE stts(); +#else +#if (0) + clear_in_cr4(X86_CR4_PVI); + clear_in_cr4(X86_CR4_DE); + clear_in_cr4(X86_CR4_MCE); + clear_in_cr4(X86_CR4_PGE); + flush_tlb_all(); + clear_in_cr4(X86_CR4_OSXMMEXCPT); +#endif +#endif } /* --- linux-2.4.28/arch/i386/kernel/time.c~colinux-0.6.1 2004-02-18 08:36:30.000000000 -0500 +++ linux-2.4.28/arch/i386/kernel/time.c 2004-11-27 15:59:21.292009144 -0500 @@ -84,8 +84,24 @@ spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; -static inline unsigned long do_fast_gettimeoffset(void) +static inline unsigned long long rdtsc_u64(void) +{ + register unsigned long eax, edx; + + rdtsc(eax,edx); + + return ((((unsigned long long)edx) << 32) | eax); +} + +#ifdef CONFIG_COOPERATIVE +static unsigned long long co_last_interrupt_time; +static unsigned long co_tsc_per_5_ticks; +static long co_last_time_offset; +#endif + +static inline long do_fast_gettimeoffset(void) { +#ifndef CONFIG_COOPERATIVE register unsigned long eax, edx; /* Read the Time Stamp Counter */ @@ -111,6 +127,32 @@ /* our adjusted time offset in microseconds */ return delay_at_last_interrupt + edx; +#else + union { + unsigned long long ll_var; + struct { unsigned long low, high; }; + } differential; + unsigned long edx; + unsigned long ret = 0; + + differential.ll_var = rdtsc_u64() - co_last_interrupt_time; + if (differential.high) + return 0; + + if (co_tsc_per_5_ticks == 0) + return 0; + + differential.ll_var *= 50000; + + __asm__("divl %2" + :"=a" (ret), "=d" (edx) + :"r" (co_tsc_per_5_ticks), + "0" (differential.low), "1" (differential.high)); + + ret += co_last_time_offset; + + return ret; +#endif } #define TICK_SIZE tick @@ -121,6 +163,8 @@ extern spinlock_t i8259A_lock; +#ifndef CONFIG_COOPERATIVE + #ifndef CONFIG_X86_TSC /* This function must be called with interrupts disabled @@ -254,7 +298,16 @@ return count; } -static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; +#else + +static unsigned long do_slow_gettimeoffset(void) +{ + return 0; +} + +#endif + +static long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; /* IBM Summit (EXA) Cyclone Timer code*/ @@ -305,7 +358,7 @@ delay_at_last_interrupt = (count + LATCH/2) / LATCH; } -static unsigned long do_gettimeoffset_cyclone(void) +static long do_gettimeoffset_cyclone(void) { u32 offset; @@ -443,7 +496,8 @@ void do_gettimeofday(struct timeval *tv) { unsigned long flags; - unsigned long usec, sec; + unsigned long sec; + long usec; read_lock_irqsave(&xtime_lock, flags); usec = do_gettimeoffset(); @@ -461,6 +515,11 @@ sec++; } + if (usec < 0) { + usec = usec + 1000000; + sec -= 1; + } + tv->tv_sec = sec; tv->tv_usec = usec; } @@ -502,6 +561,7 @@ */ static int set_rtc_mmss(unsigned long nowtime) { +#ifndef CONFIG_COOPERATIVE int retval = 0; int real_seconds, real_minutes, cmos_minutes; unsigned char save_control, save_freq_select; @@ -554,8 +614,13 @@ CMOS_WRITE(save_control, RTC_CONTROL); CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); spin_unlock(&rtc_lock); - return retval; +#else + /* + * Don't let Linux change the system time of the host machine. + */ + return -1; +#endif } /* last time the cmos clock got updated */ @@ -603,6 +668,7 @@ smp_local_timer_interrupt(regs); #endif +#ifndef CONFIG_COOPERATIVE /* * If we have an externally synchronized Linux clock, then update * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be @@ -617,6 +683,7 @@ else last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ } +#endif #ifdef CONFIG_MCA if( MCA_bus ) { @@ -655,6 +722,7 @@ */ write_lock(&xtime_lock); +#ifndef CONFIG_COOPERATIVE if(use_cyclone) mark_timeoffset_cyclone(); else if (use_tsc) { @@ -706,7 +774,15 @@ count = ((LATCH-1) - count) * TICK_SIZE; delay_at_last_interrupt = (count + LATCH/2) / LATCH; } - +#else + if (use_tsc) { + if (co_last_interrupt_time == 0) + co_last_time_offset = 0; + else + co_last_time_offset = ((long)do_fast_gettimeoffset()) - 10000; + co_last_interrupt_time = rdtsc_u64(); + } +#endif do_timer_interrupt(irq, NULL, regs); write_unlock(&xtime_lock); @@ -716,6 +792,7 @@ /* not static: needed by APM */ unsigned long get_cmos_time(void) { +#ifndef CONFIG_COOPERATIVE unsigned int year, mon, day, hour, min, sec; int i; @@ -753,6 +830,9 @@ if ((year += 1900) < 1970) year += 100; return mktime(year, mon, day, hour, min, sec); +#else + return co_get_host_time(); +#endif } static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL}; @@ -818,6 +898,10 @@ if (endlow <= CALIBRATE_TIME) goto bad_ctc; +#ifdef CONFIG_COOPERATIVE + co_tsc_per_5_ticks = endlow; +#endif + __asm__("divl %2" :"=a" (endlow), "=d" (endhigh) :"r" (endlow), "0" (0), "1" (CALIBRATE_TIME)); @@ -870,7 +954,13 @@ if(use_cyclone) init_cyclone_clock(); - + +#ifdef CONFIG_COOPERATIVE + /* + * We pretty much depend on this at the moment... + */ + set_bit(X86_FEATURE_TSC, &boot_cpu_data.x86_capability); +#endif if (cpu_has_tsc) { unsigned long tsc_quotient = calibrate_tsc(); if (tsc_quotient) { --- linux-2.4.28/arch/i386/kernel/traps.c~colinux-0.6.1 2002-11-28 18:53:09.000000000 -0500 +++ linux-2.4.28/arch/i386/kernel/traps.c 2004-11-27 15:59:21.293008992 -0500 @@ -852,11 +852,20 @@ "rorl $16,%%eax" \ : "=m"(*(n)) : "a" (addr), "r"(n), "ir"(limit), "i"(type)) +CO_TRACE_STOP; + +/* + * We use this in colinux_early_cpu_init(), so it must be protected + * from traces. + */ + void set_tss_desc(unsigned int n, void *addr) { _set_tssldt_desc(gdt_table+__TSS(n), (int)addr, 235, 0x89); } +CO_TRACE_CONTINUE; + void set_ldt_desc(unsigned int n, void *addr, unsigned int size) { _set_tssldt_desc(gdt_table+__LDT(n), (int)addr, ((size << 3)-1), 0x82); --- linux-2.4.28/arch/i386/mm/fault.c~colinux-0.6.1 2004-08-07 19:26:04.000000000 -0400 +++ linux-2.4.28/arch/i386/mm/fault.c 2004-11-27 15:59:21.293008992 -0500 @@ -380,7 +380,11 @@ pte_t *pte_k; asm("movl %%cr3,%0":"=r" (pgd)); +#ifndef CONFIG_COOPERATIVE pgd = offset + (pgd_t *)__va(pgd); +#else + pgd = offset + (pgd_t *)CO_VA(((unsigned long)pgd)); +#endif pgd_k = init_mm.pgd + offset; if (!pgd_present(*pgd_k)) --- linux-2.4.28/arch/i386/mm/init.c~colinux-0.6.1 2004-04-14 09:05:25.000000000 -0400 +++ linux-2.4.28/arch/i386/mm/init.c 2004-11-27 15:59:21.294008840 -0500 @@ -38,6 +38,8 @@ #include #include +#include + mmu_gather_t mmu_gathers[NR_CPUS]; unsigned long highstart_pfn, highend_pfn; static unsigned long totalram_pages; @@ -122,7 +124,7 @@ /* References to section boundaries */ -extern char _text, _etext, _edata, __bss_start, _end; +extern char _text, _etext, _edata, _sdata, __bss_start, _end; extern char __init_begin, __init_end; static inline void set_pte_phys (unsigned long vaddr, @@ -204,6 +206,7 @@ static void __init pagetable_init (void) { +#ifndef CONFIG_COOPERATIVE unsigned long vaddr, end; pgd_t *pgd, *pgd_base; int i, j, k; @@ -300,6 +303,8 @@ */ pgd_base[0] = pgd_base[USER_PTRS_PER_PGD]; #endif +#else +#endif } void __init zap_low_mappings (void) @@ -325,6 +330,7 @@ unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; unsigned int max_dma, high, low; +#ifndef CONFIG_COOPERATIVE max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; low = max_low_pfn; high = highend_pfn; @@ -338,6 +344,11 @@ zones_size[ZONE_HIGHMEM] = high - low; #endif } +#else + zones_size[ZONE_DMA] = 0; + zones_size[ZONE_NORMAL] = max_low_pfn; + zones_size[ZONE_HIGHMEM] = 0; +#endif free_area_init(zones_size); } @@ -485,6 +496,22 @@ bad_ppro = ppro_with_ram_bug(); +#ifdef CONFIG_COOPERATIVE + /* + * Only at this stage, after the buddy allocator's maps + * have been allocated, we can let the kernel use its + * other pseudo physical space. + */ + { + unsigned long bootmem_end = co_core_end + + (0x800 << PAGE_SHIFT); + unsigned long physical_end = __PAGE_OFFSET + + (max_low_pfn << PAGE_SHIFT); + + free_bootmem(__pa(bootmem_end), physical_end - bootmem_end); + } + +#endif /* this will put all low memory onto the freelists */ totalram_pages += free_all_bootmem(); @@ -529,7 +556,7 @@ reservedpages = free_pages_init(); codesize = (unsigned long) &_etext - (unsigned long) &_text; - datasize = (unsigned long) &_edata - (unsigned long) &_etext; + datasize = (unsigned long) &_edata - (unsigned long) &_sdata; initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %ldk highmem)\n", @@ -546,19 +573,21 @@ if (!cpu_has_pae) panic("cannot execute a PAE-enabled kernel on a PAE-less CPU!"); #endif +#ifndef CONFIG_COOPERATIVE if (boot_cpu_data.wp_works_ok < 0) test_wp_bit(); - +#endif /* * Subtle. SMP is doing it's boot stuff late (because it has to * fork idle threads) - but it also needs low mappings for the * protected-mode entry to work. We zap these entries only after * the WP-bit has been tested. */ +#ifndef CONFIG_COOPERATIVE #ifndef CONFIG_SMP zap_low_mappings(); #endif - +#endif } /* Put this after the callers, so that it cannot be inlined */ --- linux-2.4.28/arch/i386/vmlinux.lds~colinux-0.6.1 2002-02-25 14:37:53.000000000 -0500 +++ linux-2.4.28/arch/i386/vmlinux.lds 2004-11-27 15:59:21.294008840 -0500 @@ -7,50 +7,72 @@ SECTIONS { . = 0xC0000000 + 0x100000; - _text = .; /* Text and read-only data */ + _kernel_start = .; .text : { + _text = .; /* Text and read-only data */ *(.text) *(.fixup) *(.gnu.warning) - } = 0x9090 - - _etext = .; /* End of text section */ + _etext = .; /* End of text section */ + } .rodata : { *(.rodata) *(.rodata.*) } .kstrtab : { *(.kstrtab) } . = ALIGN(16); /* Exception table */ - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; + __ex_table : { + __start___ex_table = .; + *(__ex_table) + __stop___ex_table = .; + } - __start___ksymtab = .; /* Kernel symbol table */ - __ksymtab : { *(__ksymtab) } - __stop___ksymtab = .; + . = ALIGN(4); + __import_table_address : { + __start_import_address = .; + *(__im_table_address) + __stop_import_address = .; + } + __ksymtab : { + __start___ksymtab = .; /* Kernel symbol table */ + *(__ksymtab) + __stop___ksymtab = .; + } .data : { /* Data */ + _sdata = .; /* Start of data section */ *(.data) CONSTRUCTORS - } + _edata = .; /* End of data section */ + } - _edata = .; /* End of data section */ + . = ALIGN(4096); /* gdt */ + .data.gdt : { + *(.data.gdt) + } + .data.idt : { + *(.data.idt) + } . = ALIGN(8192); /* init_task */ - .data.init_task : { *(.data.init_task) } + .data.init_task : { + *(.data.init_task) + } . = ALIGN(4096); /* Init code and data */ - __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } - . = ALIGN(16); - __setup_start = .; - .setup.init : { *(.setup.init) } - __setup_end = .; - __initcall_start = .; - .initcall.init : { *(.initcall.init) } - __initcall_end = .; - . = ALIGN(4096); - __init_end = .; + .init : { + __init_begin = .; + *(.text.init) + *(.data.init) + . = ALIGN(16); + __setup_start = .; + *(.setup.init) + __setup_end = .; + __initcall_start = .; + *(.initcall.init) + __initcall_end = .; + . = ALIGN(4096); + __init_end = .; + } . = ALIGN(4096); .data.page_aligned : { *(.data.idt) } @@ -58,11 +80,11 @@ . = ALIGN(32); .data.cacheline_aligned : { *(.data.cacheline_aligned) } - __bss_start = .; /* BSS */ .bss : { + __bss_start = .; /* BSS */ *(.bss) - } - _end = . ; + _end = . ; + } /* Sections to be discarded */ /DISCARD/ : { --- linux-2.4.28/drivers/block/Config.in~colinux-0.6.1 2004-08-07 19:26:04.000000000 -0400 +++ linux-2.4.28/drivers/block/Config.in 2004-11-27 15:59:21.294008840 -0500 @@ -50,6 +50,10 @@ fi dep_bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM +if [ "$CONFIG_COOPERATIVE" = "y" ]; then + tristate 'coLinux block device support' CONFIG_BLK_DEV_PBD +fi + bool 'Per partition statistics in /proc/partitions' CONFIG_BLK_STATS endmenu --- linux-2.4.28/drivers/block/Makefile~colinux-0.6.1 2004-08-07 19:26:04.000000000 -0400 +++ linux-2.4.28/drivers/block/Makefile 2004-11-27 15:59:21.295008688 -0500 @@ -31,6 +31,7 @@ obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o obj-$(CONFIG_BLK_DEV_UMEM) += umem.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o +obj-$(CONFIG_BLK_DEV_PBD) += cobd.o obj-$(CONFIG_BLK_DEV_SX8) += sx8.o subdir-$(CONFIG_PARIDE) += paride --- /dev/null 2004-04-06 13:56:48.000000000 -0400 +++ linux-2.4.28/drivers/block/cobd.c 2004-11-27 15:59:21.295008688 -0500 @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2003 Dan Aloni + * + * Cooperative Linux Block Device implementation + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAJOR_NR COLINUX_MAJOR +#define DEVICE_NR(device) MINOR(device) /* has no partition bits */ +#define DEVICE_NAME "cobd" /* name for messaging */ +#define DEVICE_NO_RANDOM /* no entropy to contribute */ +#define DEVICE_REQUEST cobd_request +#define DEVICE_OFF(d) /* do-nothing */ + +#include +#include +#include +#include + +#include + +#define PBD_BLOCK_SIZE 512 + +static int *cobd_sizes; +static int *cobd_blksizes; +static int *cobd_hardsects; +static int cobd_devs; +static int cobd_rahead = 1; + +struct cobd_device_aliases_minor { + int cobd_minor; +}; + +struct cobd_device_aliases_major { + int sizes[0x100]; + int blksizes[0x100]; + int cobd_hardsects[0x100]; + struct cobd_device_aliases_minor *minors[0x100]; +}; + +static struct cobd_device_aliases_major *alias_majors; + +static int cobd_stat(int dev, co_block_request_t *out_request) +{ + co_block_request_t *request; + unsigned long flags; + long rc = 0; + + local_irq_save(flags); + co_passage_page->operation = CO_OPERATION_DEVICE; + co_passage_page->params[0] = CO_DEVICE_BLOCK; + co_passage_page->params[1] = dev; + request = (co_block_request_t *)&co_passage_page->params[2]; + request->type = CO_BLOCK_STAT; + co_switch_wrapper(); + rc = request->rc; + *out_request = *request; + local_irq_restore(flags); + + return rc; +} + +static int cobd_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int err; + int dev; + + dev = MINOR(inode->i_rdev); + + switch(cmd) { + + case BLKGETSIZE: { + unsigned long size; + + /* Return the device size, expressed in sectors */ + if (!arg) + return -EINVAL; /* NULL pointer: not valid */ + + err = !access_ok (VERIFY_WRITE, arg, sizeof(long)); + if (err) + return -EFAULT; + + size = (cobd_sizes[dev]) * (1024 / cobd_hardsects[dev]); + if (copy_to_user((unsigned long *) arg, &size, sizeof (unsigned long))) + return -EFAULT; + + return 0; + } + + case BLKGETSIZE64: { + unsigned long long size; + + /* Return the device size, expressed in sectors */ + if (!arg) + return -EINVAL; /* NULL pointer: not valid */ + + err = !access_ok (VERIFY_WRITE, arg, sizeof(unsigned long long)); + if (err) + return -EFAULT; + + size = cobd_sizes[dev]; + size = size * 1024; + if (copy_to_user((unsigned long long *) arg, &size, sizeof (unsigned long long))) + return -EFAULT; + + return 0; + } + + case BLKRRPART: /* reread partition table: can't do it */ + return -ENOTTY; + + default: + /* + * For ioctls we don't understand, let the block layer + * handle them. + */ + return blk_ioctl(inode->i_rdev, cmd, arg); + } + + return -ENOTTY; /* unknown command */ +} + +static int cobd_open(struct inode *inode, struct file *file) +{ + co_block_request_t *co_request; + co_block_request_t stat_request; + unsigned long flags; + int ret = 0; + int dev = MINOR(inode->i_rdev); + + if (dev >= cobd_devs) + return -EIO; + + if (!cobd_stat(dev, &stat_request)) { + unsigned long long size; + + /* Request succeeded */ + size = stat_request.disk_size; + cobd_sizes[dev] = size / 1024; + } else { + cobd_sizes[dev] = 0; + } + + local_irq_save(flags); + co_passage_page->operation = CO_OPERATION_DEVICE; + co_passage_page->params[0] = CO_DEVICE_BLOCK; + co_passage_page->params[1] = DEVICE_NR(inode->i_rdev); + co_request = (co_block_request_t *)&co_passage_page->params[2]; + co_request->type = CO_BLOCK_OPEN; + co_switch_wrapper(); + if (co_request->rc) + ret = -EIO; + local_irq_restore(flags); + + return ret; +} + +static int cobd_release(struct inode *inode, struct file *file) +{ + co_block_request_t *co_request; + unsigned long flags; + int ret = 0; + local_irq_save(flags); + + co_passage_page->operation = CO_OPERATION_DEVICE; + co_passage_page->params[0] = CO_DEVICE_BLOCK; + co_passage_page->params[1] = DEVICE_NR(inode->i_rdev); + co_request = (co_block_request_t *)&co_passage_page->params[2]; + co_request->type = CO_BLOCK_CLOSE; + co_switch_wrapper(); + if (co_request->rc) + ret = -EIO; + local_irq_restore(flags); + + return ret; +} + +static int cobd_transfer(int dev, const struct request *req) +{ + co_block_request_t *co_request; + unsigned long flags; + int ret = 0; + + local_irq_save(flags); + + co_passage_page->operation = CO_OPERATION_DEVICE; + co_passage_page->params[0] = CO_DEVICE_BLOCK; + co_passage_page->params[1] = dev; + co_request = (co_block_request_t *)&co_passage_page->params[2]; + if (req->cmd == READ) + co_request->type = CO_BLOCK_READ; + else + co_request->type = CO_BLOCK_WRITE; + co_request->offset = ((unsigned long long)req->sector) * PBD_BLOCK_SIZE; + co_request->size = req->current_nr_sectors * PBD_BLOCK_SIZE; + co_request->address = req->buffer; + co_request->rc = 0; + + co_switch_wrapper(); + + if (!co_request->rc) + ret = 1; + + local_irq_restore(flags); + + return ret; +} + +static void cobd_request(request_queue_t *q) +{ + int status; + int devno; + + while (1) { + INIT_REQUEST; /* returns when queue is empty */ + + devno = DEVICE_NR(CURRENT->rq_dev); + + status = cobd_transfer(devno, CURRENT); + + end_request(status); + } +} + +static int cobd_check_change(kdev_t dev) +{ + return 1; +} + +static struct block_device_operations cobd_fops = { + owner: THIS_MODULE, + open: cobd_open, + release: cobd_release, + ioctl: cobd_ioctl, + check_media_change: cobd_check_change, +}; + +int __init cobd_init(void) +{ + int result; + int i; + + if (devfs_register_blkdev(MAJOR_NR, "cobd", &cobd_fops)) { + printk(KERN_WARNING "Unable to get major number %d for cobd device\n", MAJOR_NR); + return -EIO; + } + + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), cobd_request); + + read_ahead[MAJOR_NR] = cobd_rahead; + result = -ENOMEM; /* for the possible errors */ + + cobd_devs = CO_MODULE_MAX_COBD; + + cobd_sizes = kmalloc(cobd_devs * sizeof(int), GFP_KERNEL); + if (!cobd_sizes) + goto fail_malloc; + + cobd_blksizes = kmalloc(cobd_devs * sizeof(int), GFP_KERNEL); + if (!cobd_blksizes) + goto fail_malloc_1; + + cobd_hardsects = kmalloc(cobd_devs * sizeof(int), GFP_KERNEL); + if (!cobd_hardsects) + goto fail_malloc_2; + + for (i=0; i < cobd_devs; i++) { + co_block_request_t request; + unsigned long long size = 0; + + if (!cobd_stat(i, &request)) { + /* Request successed */ + size = request.disk_size; + cobd_sizes[i] = size >> 10; + } else { + cobd_sizes[i] = 0; + } + + if (cobd_sizes[i] != 0) + printk(KERN_DEBUG "cobd%d size: %d kb\n", i, cobd_sizes[i]); + + cobd_blksizes[i] = PAGE_SIZE; + cobd_hardsects[i] = PBD_BLOCK_SIZE; + } + + blk_size[MAJOR_NR] = cobd_sizes; + blksize_size[MAJOR_NR] = cobd_blksizes; + hardsect_size[MAJOR_NR] = cobd_hardsects; + + devfs_register_series(NULL, "cobd%u", cobd_devs, DEVFS_FL_DEFAULT, + MAJOR_NR, 0, + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, + &cobd_fops, NULL); + + printk(KERN_DEBUG "cobd: loaded (max %d devices)\n", cobd_devs); + + return 0; + +fail_malloc_2: + kfree(cobd_blksizes); +fail_malloc_1: + kfree(cobd_sizes); +fail_malloc: + + if (devfs_unregister_blkdev(MAJOR_NR, "cobd")) + printk(KERN_WARNING "loop: cannot unregister blkdev\n"); + + return result; +} + +void cobd_exit(void) +{ + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + + if (devfs_unregister_blkdev(MAJOR_NR, "cobd")) + printk(KERN_WARNING "cobd: cannot unregister blkdev\n"); + + kfree(blk_size[MAJOR_NR]); + kfree(blksize_size[MAJOR_NR]); + kfree(hardsect_size[MAJOR_NR]); +} + +module_init(cobd_init); +module_exit(cobd_exit); + + --- linux-2.4.28/drivers/block/ll_rw_blk.c~colinux-0.6.1 2004-11-17 06:54:21.000000000 -0500 +++ linux-2.4.28/drivers/block/ll_rw_blk.c 2004-11-27 15:59:21.298008232 -0500 @@ -1576,7 +1576,7 @@ #ifdef CONFIG_BLK_DEV_FD floppy_init(); #else -#if defined(__i386__) /* Do we even need this? */ +#if defined(__i386__) && !defined(CONFIG_COOPERATIVE) /* Do we even need this? */ outb_p(0xc, 0x3f2); #endif #endif --- linux-2.4.28/drivers/char/Makefile~colinux-0.6.1 2004-08-07 19:26:04.000000000 -0400 +++ linux-2.4.28/drivers/char/Makefile 2004-11-27 15:59:21.298008232 -0500 @@ -169,6 +169,10 @@ KEYBD = dummy_keyb.o endif +ifeq ($(CONFIG_COOPERATIVE),y) + KEYBD = colx_keyb.o +endif + obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o obj-$(CONFIG_SERIAL) += $(SERIAL) obj-$(CONFIG_PARPORT_SERIAL) += parport_serial.o --- /dev/null 2004-04-06 13:56:48.000000000 -0400 +++ linux-2.4.28/drivers/char/colx_keyb.c 2004-11-27 15:59:21.299008080 -0500 @@ -0,0 +1,1228 @@ +/* + * linux/drivers/char/pc_keyb.c + * + * Separation of the PC low-level part by Geert Uytterhoeven, May 1997 + * See keyboard.c for the whole history. + * + * Major cleanup by Martin Mares, May 1997 + * + * Combined the keyboard and PS/2 mouse handling into one file, + * because they share the same hardware. + * Johan Myreen 1998-10-08. + * + * Code fixes to handle mouse ACKs properly. + * C. Scott Ananian 1999-01-29. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +/* Some configuration switches are present in the include file... */ + +#include + +/* Simple translation table for the SysRq keys */ + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char pckbd_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif + +static void kbd_write_command_w(int data); +static void kbd_write_output_w(int data); +#ifdef CONFIG_PSMOUSE +static void aux_write_ack(int val); +static void __aux_write_ack(int val); +static int aux_reconnect = 0; +#endif + +#ifndef kbd_controller_present +#define kbd_controller_present() 1 +#endif +static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; +static unsigned char handle_kbd_event(void); + +/* used only by send_data - set by keyboard_interrupt */ +static volatile unsigned char reply_expected; +static volatile unsigned char acknowledge; +static volatile unsigned char resend; + + +#if defined CONFIG_PSMOUSE +/* + * PS/2 Auxiliary Device + */ + +static int __init psaux_init(void); + +#define AUX_RECONNECT1 0xaa /* scancode1 when ps2 device is plugged (back) in */ +#define AUX_RECONNECT2 0x00 /* scancode2 when ps2 device is plugged (back) in */ + +static struct aux_queue *queue; /* Mouse data buffer. */ +static int aux_count; +/* used when we send commands to the mouse that expect an ACK. */ +static unsigned char mouse_reply_expected; + +#define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT) +#define AUX_INTS_ON (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT) + +#define MAX_RETRIES 60 /* some aux operations take long time*/ +#endif /* CONFIG_PSMOUSE */ + +/* + * Wait for keyboard controller input buffer to drain. + * + * Don't use 'jiffies' so that we don't depend on + * interrupts.. + * + * Quote from PS/2 System Reference Manual: + * + * "Address hex 0060 and address hex 0064 should be written only when + * the input-buffer-full bit and output-buffer-full bit in the + * Controller Status register are set 0." + */ + +static void kb_wait(void) +{ + unsigned long timeout = KBC_TIMEOUT; + + do { + /* + * "handle_kbd_event()" will handle any incoming events + * while we wait - keypresses or mouse movement. + */ + unsigned char status = handle_kbd_event(); + + if (! (status & KBD_STAT_IBF)) + return; + mdelay(1); + timeout--; + } while (timeout); +#ifdef KBD_REPORT_TIMEOUTS + printk(KERN_WARNING "Keyboard timed out[1]\n"); +#endif +} + +/* + * Translation of escaped scancodes to keycodes. + * This is now user-settable. + * The keycodes 1-88,96-111,119 are fairly standard, and + * should probably not be changed - changing might confuse X. + * X also interprets scancode 0x5d (KEY_Begin). + * + * For 1-88 keycode equals scancode. + */ + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 + +#define E1_PAUSE 119 + +/* + * The keycodes below are randomly located in 89-95,112-118,120-127. + * They could be thrown away (and all occurrences below replaced by 0), + * but that would force many users to use the `setkeycodes' utility, where + * they needed not before. It does not matter that there are duplicates, as + * long as no duplication occurs for any single keyboard. + */ +#define SC_LIM 89 + +#define FOCUS_PF1 85 /* actual code! */ +#define FOCUS_PF2 89 +#define FOCUS_PF3 90 +#define FOCUS_PF4 91 +#define FOCUS_PF5 92 +#define FOCUS_PF6 93 +#define FOCUS_PF7 94 +#define FOCUS_PF8 95 +#define FOCUS_PF9 120 +#define FOCUS_PF10 121 +#define FOCUS_PF11 122 +#define FOCUS_PF12 123 + +#define JAP_86 124 +/* tfj@olivia.ping.dk: + * The four keys are located over the numeric keypad, and are + * labelled A1-A4. It's an rc930 keyboard, from + * Regnecentralen/RC International, Now ICL. + * Scancodes: 59, 5a, 5b, 5c. + */ +#define RGN1 124 +#define RGN2 125 +#define RGN3 126 +#define RGN4 127 + +static unsigned char high_keys[128 - SC_LIM] = { + RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ + 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ + FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ + FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ +}; + +/* BTC */ +#define E0_MACRO 112 +/* LK450 */ +#define E0_F13 113 +#define E0_F14 114 +#define E0_HELP 115 +#define E0_DO 116 +#define E0_F17 117 +#define E0_KPMINPLUS 118 +/* + * My OmniKey generates e0 4c for the "OMNI" key and the + * right alt key does nada. [kkoller@nyx10.cs.du.edu] + */ +#define E0_OK 124 +/* + * New microsoft keyboard is rumoured to have + * e0 5b (left window button), e0 5c (right window button), + * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] + * [or: Windows_L, Windows_R, TaskMan] + */ +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ + E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + +int pckbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + if (scancode < SC_LIM || scancode > 255 || keycode > 127) + return -EINVAL; + if (scancode < 128) + high_keys[scancode - SC_LIM] = keycode; + else + e0_keys[scancode - 128] = keycode; + return 0; +} + +int pckbd_getkeycode(unsigned int scancode) +{ + return + (scancode < SC_LIM || scancode > 255) ? -EINVAL : + (scancode < 128) ? high_keys[scancode - SC_LIM] : + e0_keys[scancode - 128]; +} + +static int do_acknowledge(unsigned char scancode) +{ + if (reply_expected) { + /* Unfortunately, we must recognise these codes only if we know they + * are known to be valid (i.e., after sending a command), because there + * are some brain-damaged keyboards (yes, FOCUS 9000 again) which have + * keys with such codes :( + */ + if (scancode == KBD_REPLY_ACK) { + acknowledge = 1; + reply_expected = 0; + return 0; + } else if (scancode == KBD_REPLY_RESEND) { + resend = 1; + reply_expected = 0; + return 0; + } + /* Should not happen... */ +#if 0 + printk(KERN_DEBUG "keyboard reply expected - got %02x\n", + scancode); +#endif + } + return 1; +} + +int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + static int prev_scancode; + + /* special prefix scancodes.. */ + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + + /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */ + if (scancode == 0x00 || scancode == 0xff) { + prev_scancode = 0; + return 0; + } + + scancode &= 0x7f; + + if (prev_scancode) { + /* + * usually it will be 0xe0, but a Pause key generates + * e1 1d 45 e1 9d c5 when pressed, and nothing when released + */ + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + return 0; + } else if (prev_scancode == 0x100 && scancode == 0x45) { + *keycode = E1_PAUSE; + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + /* + * The keyboard maintains its own internal caps lock and + * num lock statuses. In caps lock mode E0 AA precedes make + * code and E0 2A follows break code. In num lock mode, + * E0 2A precedes make code and E0 AA follows break code. + * We do our own book-keeping, so we will just ignore these. + */ + /* + * For my keyboard there is no caps lock mode, but there are + * both Shift-L and Shift-R modes. The former mode generates + * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. + * So, we should also ignore the latter. - aeb@cwi.nl + */ + if (scancode == 0x2a || scancode == 0x36) + return 0; + + if (e0_keys[scancode]) + *keycode = e0_keys[scancode]; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } else if (scancode >= SC_LIM) { + /* This happens with the FOCUS 9000 keyboard + Its keys PF1..PF12 are reported to generate + 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f + Moreover, unless repeated, they do not generate + key-down events, so we have to zero up_flag below */ + /* Also, Japanese 86/106 keyboards are reported to + generate 0x73 and 0x7d for \ - and \ | respectively. */ + /* Also, some Brazilian keyboard is reported to produce + 0x73 and 0x7e for \ ? and KP-dot, respectively. */ + + *keycode = high_keys[scancode - SC_LIM]; + + if (!*keycode) { + if (!raw_mode) { +#ifdef KBD_REPORT_UNKN + printk(KERN_INFO "keyboard: unrecognized scancode (%02x)" + " - ignored\n", scancode); +#endif + } + return 0; + } + } else + *keycode = scancode; + return 1; +} + +char pckbd_unexpected_up(unsigned char keycode) +{ + /* unexpected, but this can happen: maybe this was a key release for a + FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */ + if (keycode >= SC_LIM || keycode == 85) + return 0; + else + return 0200; +} + +int pckbd_pm_resume(struct pm_dev *dev, pm_request_t rqst, void *data) +{ +#if defined CONFIG_PSMOUSE + unsigned long flags; + + if (rqst == PM_RESUME) { + if (queue) { /* Aux port detected */ + if (aux_count == 0) { /* Mouse not in use */ + spin_lock_irqsave(&kbd_controller_lock, flags); + /* + * Dell Lat. C600 A06 enables mouse after resume. + * When user touches the pad, it posts IRQ 12 + * (which we do not process), thus holding keyboard. + */ + kbd_write_command(KBD_CCMD_MOUSE_DISABLE); + /* kbd_write_cmd(AUX_INTS_OFF); */ /* Config & lock */ + kb_wait(); + kbd_write_command(KBD_CCMD_WRITE_MODE); + kb_wait(); + kbd_write_output(AUX_INTS_OFF); + spin_unlock_irqrestore(&kbd_controller_lock, flags); + } + } + } +#endif + return 0; +} + + +static inline void handle_mouse_event(unsigned char scancode) +{ +#ifdef CONFIG_PSMOUSE + static unsigned char prev_code; + if (mouse_reply_expected) { + if (scancode == AUX_ACK) { + mouse_reply_expected--; + return; + } + mouse_reply_expected = 0; + } + else if(scancode == AUX_RECONNECT2 && prev_code == AUX_RECONNECT1 + && aux_reconnect) { + printk (KERN_INFO "PS/2 mouse reconnect detected\n"); + queue->head = queue->tail = 0; /* Flush input queue */ + __aux_write_ack(AUX_ENABLE_DEV); /* ping the mouse :) */ + return; + } + + prev_code = scancode; + add_mouse_randomness(scancode); + if (aux_count) { + int head = queue->head; + + queue->buf[head] = scancode; + head = (head + 1) & (AUX_BUF_SIZE-1); + if (head != queue->tail) { + queue->head = head; + kill_fasync(&queue->fasync, SIGIO, POLL_IN); + wake_up_interruptible(&queue->proc_list); + } + } +#endif +} + +static unsigned char kbd_exists = 1; + +void handle_keyboard_event(unsigned char scancode) +{ +#ifdef CONFIG_VT + kbd_exists = 1; + if (do_acknowledge(scancode)) + handle_scancode(scancode, !(scancode & 0x80)); +#endif + tasklet_schedule(&keyboard_tasklet); +} + +/* + * This reads the keyboard status port, and does the + * appropriate action. + * + * It requires that we hold the keyboard controller + * spinlock. + */ +static unsigned char handle_kbd_event(void) +{ + unsigned char scancode; + co_linux_message_t *message; + + while (co_get_message(&message, CO_DEVICE_KEYBOARD)) { + co_scan_code_t *sc = (co_scan_code_t *)message->data; + unsigned long scancode = sc->code; + + if (!sc->down) + scancode |= 0x80; + + handle_keyboard_event(scancode); + + co_free_message(message); + } + + return 0; +} + + +static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +#ifdef CONFIG_VT + kbd_pt_regs = regs; +#endif + + spin_lock_irq(&kbd_controller_lock); + handle_kbd_event(); + spin_unlock_irq(&kbd_controller_lock); +} + +/* + * send_data sends a character to the keyboard and waits + * for an acknowledge, possibly retrying if asked to. Returns + * the success status. + * + * Don't use 'jiffies', so that we don't depend on interrupts + */ +static int send_data(unsigned char data) +{ + return 0; +} + +void pckbd_leds(unsigned char leds) +{ + if (kbd_exists && (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))) { + send_data(KBD_CMD_ENABLE); /* re-enable kbd if any errors */ + kbd_exists = 0; + } +} + +#define DEFAULT_KEYB_REP_DELAY 250 +#define DEFAULT_KEYB_REP_RATE 30 /* cps */ + +static struct kbd_repeat kbdrate={ + DEFAULT_KEYB_REP_DELAY, + DEFAULT_KEYB_REP_RATE +}; + +static unsigned char parse_kbd_rate(struct kbd_repeat *r) +{ + static struct r2v{ + int rate; + unsigned char val; + } kbd_rates[]={ {5,0x14}, + {7,0x10}, + {10,0x0c}, + {15,0x08}, + {20,0x04}, + {25,0x02}, + {30,0x00} + }; + static struct d2v{ + int delay; + unsigned char val; + } kbd_delays[]={{250,0}, + {500,1}, + {750,2}, + {1000,3} + }; + int rate=0,delay=0; + if (r != NULL){ + int i,new_rate=30,new_delay=250; + if (r->rate <= 0) + r->rate=kbdrate.rate; + if (r->delay <= 0) + r->delay=kbdrate.delay; + for (i=0; i < sizeof(kbd_rates)/sizeof(struct r2v); i++) + if (kbd_rates[i].rate == r->rate){ + new_rate=kbd_rates[i].rate; + rate=kbd_rates[i].val; + break; + } + for (i=0; i < sizeof(kbd_delays)/sizeof(struct d2v); i++) + if (kbd_delays[i].delay == r->delay){ + new_delay=kbd_delays[i].delay; + delay=kbd_delays[i].val; + break; + } + r->rate=new_rate; + r->delay=new_delay; + } + return (delay << 5) | rate; +} + +static int write_kbd_rate(unsigned char r) +{ + if (!send_data(KBD_CMD_SET_RATE) || !send_data(r)){ + send_data(KBD_CMD_ENABLE); /* re-enable kbd if any errors */ + return 0; + }else + return 1; +} + +static int pckbd_rate(struct kbd_repeat *rep) +{ + if (rep == NULL) + return -EINVAL; + else{ + unsigned char r=parse_kbd_rate(rep); + struct kbd_repeat old_rep; + memcpy(&old_rep,&kbdrate,sizeof(struct kbd_repeat)); + if (write_kbd_rate(r)){ + memcpy(&kbdrate,rep,sizeof(struct kbd_repeat)); + memcpy(rep,&old_rep,sizeof(struct kbd_repeat)); + return 0; + } + } + return -EIO; +} + +/* + * In case we run on a non-x86 hardware we need to initialize both the + * keyboard controller and the keyboard. On a x86, the BIOS will + * already have initialized them. + * + * Some x86 BIOSes do not correctly initialize the keyboard, so the + * "kbd-reset" command line options can be given to force a reset. + * [Ranger] + */ +#ifdef __i386__ + int kbd_startup_reset __initdata = 0; +#else + int kbd_startup_reset __initdata = 1; +#endif + +/* for "kbd-reset" cmdline param */ +static int __init kbd_reset_setup(char *str) +{ + kbd_startup_reset = 1; + return 1; +} + +__setup("kbd-reset", kbd_reset_setup); + +#define KBD_NO_DATA (-1) /* No data */ +#define KBD_BAD_DATA (-2) /* Parity or other error */ + +static int __init kbd_read_data(void) +{ + int retval = KBD_NO_DATA; + +#if (0) + status = kbd_read_status(); + if (status & KBD_STAT_OBF) { + unsigned char data = kbd_read_input(); + + retval = data; + if (status & (KBD_STAT_GTO | KBD_STAT_PERR)) + retval = KBD_BAD_DATA; + } +#endif + return retval; +} + +static void __init kbd_clear_input(void) +{ + int maxread = 100; /* Random number */ + + do { + if (kbd_read_data() == KBD_NO_DATA) + break; + } while (--maxread); +} + +static int __init kbd_wait_for_input(void) +{ + long timeout = KBD_INIT_TIMEOUT; + + do { + int retval = kbd_read_data(); + if (retval >= 0) + return retval; + mdelay(1); + } while (--timeout); + return -1; +} + +static void kbd_write_command_w(int data) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_command(data); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +static void kbd_write_output_w(int data) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_output(data); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +#if defined(__alpha__) +/* + * Some Alphas cannot mask some/all interrupts, so we have to + * make sure not to allow interrupts AT ALL when polling for + * specific return values from the keyboard. + * + * I think this should work on any architecture, but for now, only Alpha. + */ +static int kbd_write_command_w_and_wait(int data) +{ + unsigned long flags; + int input; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_command(data); + input = kbd_wait_for_input(); + spin_unlock_irqrestore(&kbd_controller_lock, flags); + return input; +} + +static int kbd_write_output_w_and_wait(int data) +{ + unsigned long flags; + int input; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_output(data); + input = kbd_wait_for_input(); + spin_unlock_irqrestore(&kbd_controller_lock, flags); + return input; +} +#else +static int kbd_write_command_w_and_wait(int data) +{ + kbd_write_command_w(data); + return kbd_wait_for_input(); +} + +static int kbd_write_output_w_and_wait(int data) +{ + kbd_write_output_w(data); + return kbd_wait_for_input(); +} +#endif /* __alpha__ */ + +#if defined CONFIG_PSMOUSE +static void kbd_write_cmd(int cmd) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_command(KBD_CCMD_WRITE_MODE); + kb_wait(); + kbd_write_output(cmd); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} +#endif /* CONFIG_PSMOUSE */ + +static char * __init initialize_kbd(void) +{ + int status; + + /* + * Test the keyboard interface. + * This seems to be the only way to get it going. + * If the test is successful a x55 is placed in the input buffer. + */ + kbd_write_command_w(KBD_CCMD_SELF_TEST); + if (kbd_wait_for_input() != 0x55) + return "Keyboard failed self test"; + + /* + * Perform a keyboard interface test. This causes the controller + * to test the keyboard clock and data lines. The results of the + * test are placed in the input buffer. + */ + kbd_write_command_w(KBD_CCMD_KBD_TEST); + if (kbd_wait_for_input() != 0x00) + return "Keyboard interface failed self test"; + + /* + * Enable the keyboard by allowing the keyboard clock to run. + */ + kbd_write_command_w(KBD_CCMD_KBD_ENABLE); + + /* + * Reset keyboard. If the read times out + * then the assumption is that no keyboard is + * plugged into the machine. + * This defaults the keyboard to scan-code set 2. + * + * Set up to try again if the keyboard asks for RESEND. + */ + do { + kbd_write_output_w(KBD_CMD_RESET); + status = kbd_wait_for_input(); + if (status == KBD_REPLY_ACK) + break; + if (status != KBD_REPLY_RESEND) + return "Keyboard reset failed, no ACK"; + } while (1); + + if (kbd_wait_for_input() != KBD_REPLY_POR) + return "Keyboard reset failed, no POR"; + + /* + * Set keyboard controller mode. During this, the keyboard should be + * in the disabled state. + * + * Set up to try again if the keyboard asks for RESEND. + */ + do { + kbd_write_output_w(KBD_CMD_DISABLE); + status = kbd_wait_for_input(); + if (status == KBD_REPLY_ACK) + break; + if (status != KBD_REPLY_RESEND) + return "Disable keyboard: no ACK"; + } while (1); + + kbd_write_command_w(KBD_CCMD_WRITE_MODE); + kbd_write_output_w(KBD_MODE_KBD_INT + | KBD_MODE_SYS + | KBD_MODE_DISABLE_MOUSE + | KBD_MODE_KCC); + + /* ibm powerpc portables need this to use scan-code set 1 -- Cort */ + if (!(kbd_writ