diff --git a/[refs] b/[refs] index 684c0ce6f581..30f905c01b6d 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 0a4ff8c2598b72f2fa9d50aae9e1809e684dbf41 +refs/heads/master: 853da0022023c046e0a5ccc51d427745f0c94de7 diff --git a/trunk/Documentation/SubmitChecklist b/trunk/Documentation/SubmitChecklist index 6491b2c45dd4..3af3e65cf43b 100644 --- a/trunk/Documentation/SubmitChecklist +++ b/trunk/Documentation/SubmitChecklist @@ -73,9 +73,9 @@ kernel patches. If the new code is substantial, addition of subsystem-specific fault injection might be appropriate. -22: Newly-added code has been compiled with `gcc -W'. This will generate - lots of noise, but is good for finding bugs like "warning: comparison - between signed and unsigned". +22: Newly-added code has been compiled with `gcc -W' (use "make + EXTRA_CFLAGS=-W"). This will generate lots of noise, but is good for + finding bugs like "warning: comparison between signed and unsigned". 23: Tested after it has been merged into the -mm patchset to make sure that it still works with all of the other queued patches and various diff --git a/trunk/Documentation/gpio.txt b/trunk/Documentation/gpio.txt index f8528db967fa..e8be0abb346c 100644 --- a/trunk/Documentation/gpio.txt +++ b/trunk/Documentation/gpio.txt @@ -66,7 +66,9 @@ registers; another might implement it by delegating through abstractions used for several very different kinds of GPIO controller. That said, if the convention is supported on their platform, drivers should -use it when possible: +use it when possible. Platforms should declare GENERIC_GPIO support in +Kconfig (boolean true), which multi-platform drivers can depend on when +using the include file: #include diff --git a/trunk/arch/frv/kernel/gdb-stub.c b/trunk/arch/frv/kernel/gdb-stub.c index 9550f37fb62c..1e7a101cbf4c 100644 --- a/trunk/arch/frv/kernel/gdb-stub.c +++ b/trunk/arch/frv/kernel/gdb-stub.c @@ -1195,7 +1195,7 @@ static void gdbstub_check_breakpoint(void) /* * */ -static void __attribute__((unused)) gdbstub_show_regs(void) +static void __maybe_unused gdbstub_show_regs(void) { unsigned long *reg; int loop; @@ -1223,7 +1223,7 @@ static void __attribute__((unused)) gdbstub_show_regs(void) /* * dump debugging regs */ -static void __attribute__((unused)) gdbstub_dump_debugregs(void) +static void __maybe_unused gdbstub_dump_debugregs(void) { gdbstub_printk("DCR %08lx ", __debug_status.dcr); gdbstub_printk("BRR %08lx\n", __debug_status.brr); @@ -2079,25 +2079,25 @@ void gdbstub_exit(int status) * GDB wants to call malloc() and free() to allocate memory for calling kernel * functions directly from its command line */ -static void *malloc(size_t size) __attribute__((unused)); +static void *malloc(size_t size) __maybe_unused; static void *malloc(size_t size) { return kmalloc(size, GFP_ATOMIC); } -static void free(void *p) __attribute__((unused)); +static void free(void *p) __maybe_unused; static void free(void *p) { kfree(p); } -static uint32_t ___get_HSR0(void) __attribute__((unused)); +static uint32_t ___get_HSR0(void) __maybe_unused; static uint32_t ___get_HSR0(void) { return __get_HSR(0); } -static uint32_t ___set_HSR0(uint32_t x) __attribute__((unused)); +static uint32_t ___set_HSR0(uint32_t x) __maybe_unused; static uint32_t ___set_HSR0(uint32_t x) { __set_HSR(0, x); diff --git a/trunk/arch/h8300/kernel/syscalls.S b/trunk/arch/h8300/kernel/syscalls.S index dab98fd99e63..54e21c3f2057 100644 --- a/trunk/arch/h8300/kernel/syscalls.S +++ b/trunk/arch/h8300/kernel/syscalls.S @@ -31,7 +31,7 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_mknod) .long SYMBOL_NAME(sys_chmod) /* 15 */ .long SYMBOL_NAME(sys_chown16) - .long SYMBOL_NAME(sys_ni_syscall) /* old break syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old break syscall holder */ .long SYMBOL_NAME(sys_stat) .long SYMBOL_NAME(sys_lseek) .long SYMBOL_NAME(sys_getpid) /* 20 */ @@ -45,11 +45,11 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_fstat) .long SYMBOL_NAME(sys_pause) .long SYMBOL_NAME(sys_utime) /* 30 */ - .long SYMBOL_NAME(sys_ni_syscall) /* old stty syscall holder */ - .long SYMBOL_NAME(sys_ni_syscall) /* old gtty syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old stty syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old gtty syscall holder */ .long SYMBOL_NAME(sys_access) .long SYMBOL_NAME(sys_nice) - .long SYMBOL_NAME(sys_ni_syscall) /* 35 */ /* old ftime syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* 35 old ftime syscall holder */ .long SYMBOL_NAME(sys_sync) .long SYMBOL_NAME(sys_kill) .long SYMBOL_NAME(sys_rename) @@ -58,7 +58,7 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_dup) .long SYMBOL_NAME(sys_pipe) .long SYMBOL_NAME(sys_times) - .long SYMBOL_NAME(sys_ni_syscall) /* old prof syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old prof syscall holder */ .long SYMBOL_NAME(sys_brk) /* 45 */ .long SYMBOL_NAME(sys_setgid16) .long SYMBOL_NAME(sys_getgid16) @@ -66,13 +66,13 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_geteuid16) .long SYMBOL_NAME(sys_getegid16) /* 50 */ .long SYMBOL_NAME(sys_acct) - .long SYMBOL_NAME(sys_umount) /* recycled never used phys() */ - .long SYMBOL_NAME(sys_ni_syscall) /* old lock syscall holder */ + .long SYMBOL_NAME(sys_umount) /* recycled never used phys() */ + .long SYMBOL_NAME(sys_ni_syscall) /* old lock syscall holder */ .long SYMBOL_NAME(sys_ioctl) .long SYMBOL_NAME(sys_fcntl) /* 55 */ - .long SYMBOL_NAME(sys_ni_syscall) /* old mpx syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old mpx syscall holder */ .long SYMBOL_NAME(sys_setpgid) - .long SYMBOL_NAME(sys_ni_syscall) /* old ulimit syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old ulimit syscall holder */ .long SYMBOL_NAME(sys_ni_syscall) .long SYMBOL_NAME(sys_umask) /* 60 */ .long SYMBOL_NAME(sys_chroot) @@ -112,7 +112,7 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_fchown16) /* 95 */ .long SYMBOL_NAME(sys_getpriority) .long SYMBOL_NAME(sys_setpriority) - .long SYMBOL_NAME(sys_ni_syscall) /* old profil syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old profil syscall holder */ .long SYMBOL_NAME(sys_statfs) .long SYMBOL_NAME(sys_fstatfs) /* 100 */ .long SYMBOL_NAME(sys_ni_syscall) /* ioperm for i386 */ @@ -202,8 +202,8 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_capset) /* 185 */ .long SYMBOL_NAME(sys_sigaltstack) .long SYMBOL_NAME(sys_sendfile) - .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ - .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ + .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ + .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ .long SYMBOL_NAME(sys_vfork) /* 190 */ .long SYMBOL_NAME(sys_getrlimit) .long SYMBOL_NAME(sys_mmap2) @@ -236,10 +236,10 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_ni_syscall) .long SYMBOL_NAME(sys_getdents64) /* 220 */ .long SYMBOL_NAME(sys_fcntl64) - .long SYMBOL_NAME(sys_ni_syscall) /* reserved for TUX */ - .long SYMBOL_NAME(sys_ni_syscall) + .long SYMBOL_NAME(sys_ni_syscall) /* reserved TUX */ + .long SYMBOL_NAME(sys_ni_syscall) /* reserved Security */ .long SYMBOL_NAME(sys_gettid) - .long SYMBOL_NAME(sys_ni_syscall) /* 225 */ /* sys_readahead */ + .long SYMBOL_NAME(sys_readahead) /* 225 */ .long SYMBOL_NAME(sys_setxattr) .long SYMBOL_NAME(sys_lsetxattr) .long SYMBOL_NAME(sys_fsetxattr) @@ -257,8 +257,8 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_futex) /* 240 */ .long SYMBOL_NAME(sys_sched_setaffinity) .long SYMBOL_NAME(sys_sched_getaffinity) - .long SYMBOL_NAME(sys_ni_syscall) /* sys_set_thread_area */ - .long SYMBOL_NAME(sys_ni_syscall) /* sys_get_thread_area */ + .long SYMBOL_NAME(sys_ni_syscall) + .long SYMBOL_NAME(sys_ni_syscall) .long SYMBOL_NAME(sys_io_setup) /* 245 */ .long SYMBOL_NAME(sys_io_destroy) .long SYMBOL_NAME(sys_io_getevents) @@ -288,8 +288,8 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_utimes) .long SYMBOL_NAME(sys_fadvise64_64) .long SYMBOL_NAME(sys_ni_syscall) /* sys_vserver */ - .long SYMBOL_NAME(sys_mbind) - .long SYMBOL_NAME(sys_get_mempolicy) + .long SYMBOL_NAME(sys_ni_syscall) + .long SYMBOL_NAME(sys_get_mempolicy) /* 275 */ .long SYMBOL_NAME(sys_set_mempolicy) .long SYMBOL_NAME(sys_mq_open) .long SYMBOL_NAME(sys_mq_unlink) @@ -297,16 +297,42 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_mq_timedreceive) /* 280 */ .long SYMBOL_NAME(sys_mq_notify) .long SYMBOL_NAME(sys_mq_getsetattr) - .long SYMBOL_NAME(sys_ni_syscall) /* reserved for kexec */ .long SYMBOL_NAME(sys_waitid) - .long SYMBOL_NAME(sys_ni_syscall) /* 285 */ /* available */ - .long SYMBOL_NAME(sys_add_key) + .long SYMBOL_NAME(sys_ni_syscall) /* sys_kexec_load */ + .long SYMBOL_NAME(sys_add_key) /* 285 */ .long SYMBOL_NAME(sys_request_key) .long SYMBOL_NAME(sys_keyctl) - - .rept NR_syscalls-(.-SYMBOL_NAME(sys_call_table))/4 - .long SYMBOL_NAME(sys_ni_syscall) - .endr + .long SYMBOL_NAME(sys_ioprio_set) + .long SYMBOL_NAME(sys_ioprio_get) /* 290 */ + .long SYMBOL_NAME(sys_inotify_init) + .long SYMBOL_NAME(sys_inotify_add_watch) + .long SYMBOL_NAME(sys_inotify_rm_watch) + .long SYMBOL_NAME(sys_migrate_pages) + .long SYMBOL_NAME(sys_openat) /* 295 */ + .long SYMBOL_NAME(sys_mkdirat) + .long SYMBOL_NAME(sys_mknodat) + .long SYMBOL_NAME(sys_fchownat) + .long SYMBOL_NAME(sys_futimesat) + .long SYMBOL_NAME(sys_fstatat64) /* 300 */ + .long SYMBOL_NAME(sys_unlinkat) + .long SYMBOL_NAME(sys_renameat) + .long SYMBOL_NAME(sys_linkat) + .long SYMBOL_NAME(sys_symlinkat) + .long SYMBOL_NAME(sys_readlinkat) /* 305 */ + .long SYMBOL_NAME(sys_fchmodat) + .long SYMBOL_NAME(sys_faccessat) + .long SYMBOL_NAME(sys_ni_syscall) /* sys_pselect6 */ + .long SYMBOL_NAME(sys_ni_syscall) /* sys_ppoll */ + .long SYMBOL_NAME(sys_unshare) /* 310 */ + .long SYMBOL_NAME(sys_set_robust_list) + .long SYMBOL_NAME(sys_get_robust_list) + .long SYMBOL_NAME(sys_splice) + .long SYMBOL_NAME(sys_sync_file_range) + .long SYMBOL_NAME(sys_tee) /* 315 */ + .long SYMBOL_NAME(sys_vmsplice) + .long SYMBOL_NAME(sys_ni_syscall) /* sys_move_pages */ + .long SYMBOL_NAME(sys_getcpu) + .long SYMBOL_NAME(sys_ni_syscall) /* sys_epoll_pwait */ .macro call_sp addr mov.l #SYMBOL_NAME(\addr),er6 diff --git a/trunk/arch/i386/kernel/syscall_table.S b/trunk/arch/i386/kernel/syscall_table.S index 0772678ceecf..bf6adce52267 100644 --- a/trunk/arch/i386/kernel/syscall_table.S +++ b/trunk/arch/i386/kernel/syscall_table.S @@ -320,3 +320,6 @@ ENTRY(sys_call_table) .long sys_getcpu .long sys_epoll_pwait .long sys_utimensat /* 320 */ + .long sys_signalfd + .long sys_timerfd + .long sys_eventfd diff --git a/trunk/arch/m32r/Kconfig b/trunk/arch/m32r/Kconfig index 9740d6b8ae11..c3bb8a755b00 100644 --- a/trunk/arch/m32r/Kconfig +++ b/trunk/arch/m32r/Kconfig @@ -241,6 +241,10 @@ config GENERIC_CALIBRATE_DELAY bool default y +config SCHED_NO_NO_OMIT_FRAME_POINTER + bool + default y + config PREEMPT bool "Preemptible Kernel" help diff --git a/trunk/arch/m32r/mm/mmu.S b/trunk/arch/m32r/mm/mmu.S index 8bb74b10dca7..49a6d16a3d58 100644 --- a/trunk/arch/m32r/mm/mmu.S +++ b/trunk/arch/m32r/mm/mmu.S @@ -163,7 +163,8 @@ ENTRY(tme_handler) ; pte_data = (unsigned long)pte_val(*pte); ld r2, @r3 ; r2: pte data - or3 r2, r2, #2 ; _PAGE_PRESENT(=2) + and3 r3, r2, #2 ; _PAGE_PRESENT(=2) check + beqz r3, 3f .fillinsn 5: @@ -264,11 +265,8 @@ ENTRY(tme_handler) ; and3 r1, r1, #0xeff ldi r4, #611 ; _KERNPG_TABLE(=611) - beq r1, r4, 4f ; !pmd_bad(*pmd) ? - .fillinsn -3: - ldi r1, #0 ; r1: pte_data = 0 - bra 5f + bne r1, r4, 3f ; !pmd_bad(*pmd) ? + .fillinsn 4: ; pte = pte_offset(pmd, address); @@ -282,8 +280,10 @@ ENTRY(tme_handler) add r4, r3 ; r4: pte ; pte_data = (unsigned long)pte_val(*pte); ld r1, @r4 ; r1: pte_data - .fillinsn + and3 r3, r1, #2 ; _PAGE_PRESENT(=2) check + beqz r3, 3f + .fillinsn ;; set tlb ; r0: address, r1: pte_data, r2: entry ; r3,r4: (free) @@ -295,8 +295,7 @@ ENTRY(tme_handler) and3 r4, r4, #(MMU_CONTEXT_ASID_MASK) or r3, r4 st r3, @r2 - or3 r4, r1, #2 ; _PAGE_PRESENT(=2) - st r4, @(4,r2) ; set_tlb_data(entry, pte_data); + st r1, @(4,r2) ; set_tlb_data(entry, pte_data); ld r4, @sp+ ld r3, @sp+ @@ -306,6 +305,11 @@ ENTRY(tme_handler) ld sp, @sp+ rte + .fillinsn +3: + ldi r1, #2 ; r1: pte_data = 0 | _PAGE_PRESENT(=2) + bra 5b + #else #error unknown isa configuration #endif diff --git a/trunk/arch/powerpc/kernel/irq.c b/trunk/arch/powerpc/kernel/irq.c index 9ed4931af164..068377a2a8dc 100644 --- a/trunk/arch/powerpc/kernel/irq.c +++ b/trunk/arch/powerpc/kernel/irq.c @@ -173,7 +173,7 @@ void local_irq_restore(unsigned long en) lv1_get_version_info(&tmp); } - hard_irq_enable(); + __hard_irq_enable(); } #endif /* CONFIG_PPC64 */ diff --git a/trunk/arch/powerpc/kernel/swsusp.c b/trunk/arch/powerpc/kernel/swsusp.c index 064a7ba4f02c..77b7b34b5955 100644 --- a/trunk/arch/powerpc/kernel/swsusp.c +++ b/trunk/arch/powerpc/kernel/swsusp.c @@ -36,8 +36,4 @@ void restore_processor_state(void) #ifdef CONFIG_PPC32 set_context(current->active_mm->context.id, current->active_mm->pgd); #endif - -#ifdef CONFIG_PPC64 - hard_irq_enable(); -#endif } diff --git a/trunk/arch/powerpc/platforms/cell/pervasive.c b/trunk/arch/powerpc/platforms/cell/pervasive.c index 8c20f0fb8651..812bf563ed65 100644 --- a/trunk/arch/powerpc/platforms/cell/pervasive.c +++ b/trunk/arch/powerpc/platforms/cell/pervasive.c @@ -43,12 +43,10 @@ static void cbe_power_save(void) unsigned long ctrl, thread_switch_control; /* - * We need to hard disable interrupts, but we also need to mark them - * hard disabled in the PACA so that the local_irq_enable() done by - * our caller upon return propertly hard enables. + * We need to hard disable interrupts, the local_irq_enable() done by + * our caller upon return will hard re-enable. */ hard_irq_disable(); - get_paca()->hard_enabled = 0; ctrl = mfspr(SPRN_CTRLF); diff --git a/trunk/arch/um/Kconfig b/trunk/arch/um/Kconfig index b9c0f307a8fa..c504312219b4 100644 --- a/trunk/arch/um/Kconfig +++ b/trunk/arch/um/Kconfig @@ -277,7 +277,8 @@ config HIGHMEM config KERNEL_STACK_ORDER int "Kernel stack size order" - default 2 + default 1 if 64BIT + default 0 if !64BIT help This option determines the size of UML kernel stacks. They will be 1 << order pages. The default is OK unless you're running Valgrind diff --git a/trunk/arch/um/defconfig b/trunk/arch/um/defconfig index f938fa822146..a54d0efecae1 100644 --- a/trunk/arch/um/defconfig +++ b/trunk/arch/um/defconfig @@ -86,7 +86,7 @@ CONFIG_MCONSOLE=y # CONFIG_MAGIC_SYSRQ is not set CONFIG_NEST_LEVEL=0 # CONFIG_HIGHMEM is not set -CONFIG_KERNEL_STACK_ORDER=2 +CONFIG_KERNEL_STACK_ORDER=0 CONFIG_UML_REAL_TIME_CLOCK=y # diff --git a/trunk/arch/um/include/common-offsets.h b/trunk/arch/um/include/common-offsets.h index 5593a8027083..541f4a8ca512 100644 --- a/trunk/arch/um/include/common-offsets.h +++ b/trunk/arch/um/include/common-offsets.h @@ -28,3 +28,5 @@ DEFINE(UM_NR_CPUS, NR_CPUS); /* For crypto assembler code. */ DEFINE(crypto_tfm_ctx_offset, offsetof(struct crypto_tfm, __crt_ctx)); + +DEFINE(UM_THREAD_SIZE, THREAD_SIZE); diff --git a/trunk/arch/um/include/kern_util.h b/trunk/arch/um/include/kern_util.h index 50a49691e0e6..8d7f7c1cb9c6 100644 --- a/trunk/arch/um/include/kern_util.h +++ b/trunk/arch/um/include/kern_util.h @@ -117,4 +117,7 @@ extern void sigio_handler(int sig, union uml_pt_regs *regs); extern void copy_sc(union uml_pt_regs *regs, void *from); +unsigned long to_irq_stack(int sig, unsigned long *mask_out); +unsigned long from_irq_stack(int nested); + #endif diff --git a/trunk/arch/um/include/os.h b/trunk/arch/um/include/os.h index 688d181b5f8a..4d9fb26387d5 100644 --- a/trunk/arch/um/include/os.h +++ b/trunk/arch/um/include/os.h @@ -272,7 +272,6 @@ extern void do_longjmp(void *p, int val); /* util.c */ extern void stack_protections(unsigned long address); -extern void task_protections(unsigned long address); extern int raw(int fd); extern void setup_machinename(char *machine_out); extern void setup_hostinfo(char *buf, int len); diff --git a/trunk/arch/um/kernel/dyn.lds.S b/trunk/arch/um/kernel/dyn.lds.S index e36f92b463ce..87a4e4427d8d 100644 --- a/trunk/arch/um/kernel/dyn.lds.S +++ b/trunk/arch/um/kernel/dyn.lds.S @@ -97,6 +97,8 @@ SECTIONS .data : { . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ *(.data.init_task) + . = ALIGN(KERNEL_STACK_SIZE); + *(.data.init_irqstack) *(.data .data.* .gnu.linkonce.d.*) SORT(CONSTRUCTORS) } diff --git a/trunk/arch/um/kernel/init_task.c b/trunk/arch/um/kernel/init_task.c index cda91aa8e703..d4f1d1ab252b 100644 --- a/trunk/arch/um/kernel/init_task.c +++ b/trunk/arch/um/kernel/init_task.c @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) +/* + * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,intel.linux}.com) * Licensed under the GPL */ @@ -33,28 +33,20 @@ EXPORT_SYMBOL(init_task); /* * Initial thread structure. * - * We need to make sure that this is 16384-byte aligned due to the + * We need to make sure that this is aligned due to the * way process stacks are handled. This is done by having a special * "init_task" linker map entry.. */ -union thread_union init_thread_union -__attribute__((__section__(".data.init_task"))) = -{ INIT_THREAD_INFO(init_task) }; +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task) }; + +union thread_union cpu0_irqstack + __attribute__((__section__(".data.init_irqstack"))) = + { INIT_THREAD_INFO(init_task) }; void unprotect_stack(unsigned long stack) { - os_protect_memory((void *) stack, (1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE, - 1, 1, 0); + os_protect_memory((void *) stack, THREAD_SIZE, 1, 1, 0); } - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/trunk/arch/um/kernel/irq.c b/trunk/arch/um/kernel/irq.c index 8f2ed3690315..dba04d88b432 100644 --- a/trunk/arch/um/kernel/irq.c +++ b/trunk/arch/um/kernel/irq.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) * Licensed under the GPL * Derived (i.e. mostly copied) from arch/i386/kernel/irq.c: @@ -32,6 +32,7 @@ #include "sigio.h" #include "um_malloc.h" #include "misc_constants.h" +#include "as-layout.h" /* * Generic, controller-independent functions: @@ -53,7 +54,7 @@ int show_interrupts(struct seq_file *p, void *v) if (i < NR_IRQS) { spin_lock_irqsave(&irq_desc[i].lock, flags); action = irq_desc[i].action; - if (!action) + if (!action) goto skip; seq_printf(p, "%3d: ",i); #ifndef CONFIG_SMP @@ -468,3 +469,113 @@ int init_aio_irq(int irq, char *name, irq_handler_t handler) out: return err; } + +/* + * IRQ stack entry and exit: + * + * Unlike i386, UML doesn't receive IRQs on the normal kernel stack + * and switch over to the IRQ stack after some preparation. We use + * sigaltstack to receive signals on a separate stack from the start. + * These two functions make sure the rest of the kernel won't be too + * upset by being on a different stack. The IRQ stack has a + * thread_info structure at the bottom so that current et al continue + * to work. + * + * to_irq_stack copies the current task's thread_info to the IRQ stack + * thread_info and sets the tasks's stack to point to the IRQ stack. + * + * from_irq_stack copies the thread_info struct back (flags may have + * been modified) and resets the task's stack pointer. + * + * Tricky bits - + * + * What happens when two signals race each other? UML doesn't block + * signals with sigprocmask, SA_DEFER, or sa_mask, so a second signal + * could arrive while a previous one is still setting up the + * thread_info. + * + * There are three cases - + * The first interrupt on the stack - sets up the thread_info and + * handles the interrupt + * A nested interrupt interrupting the copying of the thread_info - + * can't handle the interrupt, as the stack is in an unknown state + * A nested interrupt not interrupting the copying of the + * thread_info - doesn't do any setup, just handles the interrupt + * + * The first job is to figure out whether we interrupted stack setup. + * This is done by xchging the signal mask with thread_info->pending. + * If the value that comes back is zero, then there is no setup in + * progress, and the interrupt can be handled. If the value is + * non-zero, then there is stack setup in progress. In order to have + * the interrupt handled, we leave our signal in the mask, and it will + * be handled by the upper handler after it has set up the stack. + * + * Next is to figure out whether we are the outer handler or a nested + * one. As part of setting up the stack, thread_info->real_thread is + * set to non-NULL (and is reset to NULL on exit). This is the + * nesting indicator. If it is non-NULL, then the stack is already + * set up and the handler can run. + */ + +static unsigned long pending_mask; + +unsigned long to_irq_stack(int sig, unsigned long *mask_out) +{ + struct thread_info *ti; + unsigned long mask, old; + int nested; + + mask = xchg(&pending_mask, 1 << sig); + if(mask != 0){ + /* If any interrupts come in at this point, we want to + * make sure that their bits aren't lost by our + * putting our bit in. So, this loop accumulates bits + * until xchg returns the same value that we put in. + * When that happens, there were no new interrupts, + * and pending_mask contains a bit for each interrupt + * that came in. + */ + old = 1 << sig; + do { + old |= mask; + mask = xchg(&pending_mask, old); + } while(mask != old); + return 1; + } + + ti = current_thread_info(); + nested = (ti->real_thread != NULL); + if(!nested){ + struct task_struct *task; + struct thread_info *tti; + + task = cpu_tasks[ti->cpu].task; + tti = task_thread_info(task); + *ti = *tti; + ti->real_thread = tti; + task->stack = ti; + } + + mask = xchg(&pending_mask, 0); + *mask_out |= mask | nested; + return 0; +} + +unsigned long from_irq_stack(int nested) +{ + struct thread_info *ti, *to; + unsigned long mask; + + ti = current_thread_info(); + + pending_mask = 1; + + to = ti->real_thread; + current->stack = to; + ti->real_thread = NULL; + *to = *ti; + + mask = xchg(&pending_mask, 0); + return mask & ~1; +} + diff --git a/trunk/arch/um/kernel/skas/process.c b/trunk/arch/um/kernel/skas/process.c index a96ae1a0610e..2a69a7ce5792 100644 --- a/trunk/arch/um/kernel/skas/process.c +++ b/trunk/arch/um/kernel/skas/process.c @@ -163,8 +163,12 @@ static int start_kernel_proc(void *unused) extern int userspace_pid[]; +extern char cpu0_irqstack[]; + int start_uml_skas(void) { + stack_protections((unsigned long) &cpu0_irqstack); + set_sigstack(cpu0_irqstack, THREAD_SIZE); if(proc_mm) userspace_pid[0] = start_userspace(0); diff --git a/trunk/arch/um/kernel/tt/exec_kern.c b/trunk/arch/um/kernel/tt/exec_kern.c index 98e21743e604..40126cb51801 100644 --- a/trunk/arch/um/kernel/tt/exec_kern.c +++ b/trunk/arch/um/kernel/tt/exec_kern.c @@ -57,7 +57,7 @@ void flush_thread_tt(void) enable_timer(); free_page(stack); protect_memory(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1); - task_protections((unsigned long) current_thread); + stack_protections((unsigned long) current_thread); force_flush_all(); unblock_signals(); } diff --git a/trunk/arch/um/kernel/tt/process_kern.c b/trunk/arch/um/kernel/tt/process_kern.c index c631303cb800..74347adf81bf 100644 --- a/trunk/arch/um/kernel/tt/process_kern.c +++ b/trunk/arch/um/kernel/tt/process_kern.c @@ -209,7 +209,7 @@ void finish_fork_handler(int sig) if(current->mm != current->parent->mm) protect_memory(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1); - task_protections((unsigned long) current_thread); + stack_protections((unsigned long) current_thread); free_page(current->thread.temp_stack); local_irq_disable(); diff --git a/trunk/arch/um/kernel/um_arch.c b/trunk/arch/um/kernel/um_arch.c index 1cf954a47fd7..ecc458fe51b9 100644 --- a/trunk/arch/um/kernel/um_arch.c +++ b/trunk/arch/um/kernel/um_arch.c @@ -459,7 +459,7 @@ int __init linux_main(int argc, char **argv) uml_postsetup(); - task_protections((unsigned long) &init_thread_info); + stack_protections((unsigned long) &init_thread_info); os_flush_stdout(); return CHOOSE_MODE(start_uml_tt(), start_uml_skas()); diff --git a/trunk/arch/um/kernel/uml.lds.S b/trunk/arch/um/kernel/uml.lds.S index f6301274cf3c..bc59f97e34d0 100644 --- a/trunk/arch/um/kernel/uml.lds.S +++ b/trunk/arch/um/kernel/uml.lds.S @@ -59,6 +59,8 @@ SECTIONS { . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ *(.data.init_task) + . = ALIGN(KERNEL_STACK_SIZE); + *(.data.init_irqstack) *(.data) *(.gnu.linkonce.d*) CONSTRUCTORS diff --git a/trunk/arch/um/os-Linux/signal.c b/trunk/arch/um/os-Linux/signal.c index 48d493415301..18e5c8b67eb8 100644 --- a/trunk/arch/um/os-Linux/signal.c +++ b/trunk/arch/um/os-Linux/signal.c @@ -61,15 +61,19 @@ void sig_handler(int sig, struct sigcontext *sc) static void real_alarm_handler(int sig, struct sigcontext *sc) { + union uml_pt_regs regs; + if(sig == SIGALRM) switch_timers(0); - CHOOSE_MODE_PROC(sig_handler_common_tt, sig_handler_common_skas, - sig, sc); + if(sc != NULL) + copy_sc(®s, sc); + regs.skas.is_user = 0; + unblock_signals(); + timer_handler(sig, ®s); if(sig == SIGALRM) switch_timers(1); - } void alarm_handler(int sig, struct sigcontext *sc) @@ -113,6 +117,46 @@ void remove_sigstack(void) void (*handlers[_NSIG])(int sig, struct sigcontext *sc); +void handle_signal(int sig, struct sigcontext *sc) +{ + unsigned long pending = 0; + + do { + int nested, bail; + + /* + * pending comes back with one bit set for each + * interrupt that arrived while setting up the stack, + * plus a bit for this interrupt, plus the zero bit is + * set if this is a nested interrupt. + * If bail is true, then we interrupted another + * handler setting up the stack. In this case, we + * have to return, and the upper handler will deal + * with this interrupt. + */ + bail = to_irq_stack(sig, &pending); + if(bail) + return; + + nested = pending & 1; + pending &= ~1; + + while((sig = ffs(pending)) != 0){ + sig--; + pending &= ~(1 << sig); + (*handlers[sig])(sig, sc); + } + + /* Again, pending comes back with a mask of signals + * that arrived while tearing down the stack. If this + * is non-zero, we just go back, set up the stack + * again, and handle the new interrupts. + */ + if(!nested) + pending = from_irq_stack(nested); + } while(pending); +} + extern void hard_handler(int sig); void set_handler(int sig, void (*handler)(int), int flags, ...) diff --git a/trunk/arch/um/os-Linux/skas/process.c b/trunk/arch/um/os-Linux/skas/process.c index 6a0e466d01e3..f9d2f8545afe 100644 --- a/trunk/arch/um/os-Linux/skas/process.c +++ b/trunk/arch/um/os-Linux/skas/process.c @@ -288,7 +288,8 @@ int start_userspace(unsigned long stub_stack) void userspace(union uml_pt_regs *regs) { int err, status, op, pid = userspace_pid[0]; - int local_using_sysemu; /*To prevent races if using_sysemu changes under us.*/ + /* To prevent races if using_sysemu changes under us.*/ + int local_using_sysemu; while(1){ restore_registers(pid, regs); @@ -296,7 +297,8 @@ void userspace(union uml_pt_regs *regs) /* Now we set local_using_sysemu to be used for one loop */ local_using_sysemu = get_using_sysemu(); - op = SELECT_PTRACE_OPERATION(local_using_sysemu, singlestepping(NULL)); + op = SELECT_PTRACE_OPERATION(local_using_sysemu, + singlestepping(NULL)); err = ptrace(op, pid, 0, 0); if(err) @@ -490,8 +492,8 @@ void map_stub_pages(int fd, unsigned long code, void new_thread(void *stack, jmp_buf *buf, void (*handler)(void)) { (*buf)[0].JB_IP = (unsigned long) handler; - (*buf)[0].JB_SP = (unsigned long) stack + - (PAGE_SIZE << UML_CONFIG_KERNEL_STACK_ORDER) - sizeof(void *); + (*buf)[0].JB_SP = (unsigned long) stack + UM_THREAD_SIZE - + sizeof(void *); } #define INIT_JMP_NEW_THREAD 0 @@ -533,8 +535,7 @@ int start_idle_thread(void *stack, jmp_buf *switch_buf) case INIT_JMP_NEW_THREAD: (*switch_buf)[0].JB_IP = (unsigned long) new_thread_handler; (*switch_buf)[0].JB_SP = (unsigned long) stack + - (PAGE_SIZE << UML_CONFIG_KERNEL_STACK_ORDER) - - sizeof(void *); + UM_THREAD_SIZE - sizeof(void *); break; case INIT_JMP_CALLBACK: (*cb_proc)(cb_arg); diff --git a/trunk/arch/um/os-Linux/sys-i386/signal.c b/trunk/arch/um/os-Linux/sys-i386/signal.c index 0d3eae518352..f311609f93da 100644 --- a/trunk/arch/um/os-Linux/sys-i386/signal.c +++ b/trunk/arch/um/os-Linux/sys-i386/signal.c @@ -1,15 +1,13 @@ /* - * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com) + * Copyright (C) 2006 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ #include -extern void (*handlers[])(int sig, struct sigcontext *sc); +extern void handle_signal(int sig, struct sigcontext *sc); void hard_handler(int sig) { - struct sigcontext *sc = (struct sigcontext *) (&sig + 1); - - (*handlers[sig])(sig, sc); + handle_signal(sig, (struct sigcontext *) (&sig + 1)); } diff --git a/trunk/arch/um/os-Linux/sys-x86_64/signal.c b/trunk/arch/um/os-Linux/sys-x86_64/signal.c index 3f369e5f976b..82a388822cd3 100644 --- a/trunk/arch/um/os-Linux/sys-x86_64/signal.c +++ b/trunk/arch/um/os-Linux/sys-x86_64/signal.c @@ -1,16 +1,16 @@ /* - * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com) + * Copyright (C) 2006 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ #include -extern void (*handlers[])(int sig, struct sigcontext *sc); +extern void handle_signal(int sig, struct sigcontext *sc); void hard_handler(int sig) { struct ucontext *uc; asm("movq %%rdx, %0" : "=r" (uc)); - (*handlers[sig])(sig, (struct sigcontext *) &uc->uc_mcontext); + handle_signal(sig, (struct sigcontext *) &uc->uc_mcontext); } diff --git a/trunk/arch/um/os-Linux/util.c b/trunk/arch/um/os-Linux/util.c index c307a89ed259..7cbcf484e13d 100644 --- a/trunk/arch/um/os-Linux/util.c +++ b/trunk/arch/um/os-Linux/util.c @@ -33,25 +33,8 @@ void stack_protections(unsigned long address) { - int prot = PROT_READ | PROT_WRITE | PROT_EXEC; - - if(mprotect((void *) address, UM_KERN_PAGE_SIZE, prot) < 0) - panic("protecting stack failed, errno = %d", errno); -} - -void task_protections(unsigned long address) -{ - unsigned long guard = address + UM_KERN_PAGE_SIZE; - unsigned long stack = guard + UM_KERN_PAGE_SIZE; - int prot = 0, pages; - -#ifdef notdef - if(mprotect((void *) stack, UM_KERN_PAGE_SIZE, prot) < 0) - panic("protecting guard page failed, errno = %d", errno); -#endif - pages = (1 << UML_CONFIG_KERNEL_STACK_ORDER) - 2; - prot = PROT_READ | PROT_WRITE | PROT_EXEC; - if(mprotect((void *) stack, pages * UM_KERN_PAGE_SIZE, prot) < 0) + if(mprotect((void *) address, UM_THREAD_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC) < 0) panic("protecting stack failed, errno = %d", errno); } @@ -72,7 +55,7 @@ int raw(int fd) /* XXX tcsetattr could have applied only some changes * (and cfmakeraw() is a set of changes) */ - return(0); + return 0; } void setup_machinename(char *machine_out) diff --git a/trunk/arch/x86_64/ia32/ia32entry.S b/trunk/arch/x86_64/ia32/ia32entry.S index f21068378272..52be79beb306 100644 --- a/trunk/arch/x86_64/ia32/ia32entry.S +++ b/trunk/arch/x86_64/ia32/ia32entry.S @@ -716,4 +716,7 @@ ia32_sys_call_table: .quad sys_getcpu .quad sys_epoll_pwait .quad compat_sys_utimensat /* 320 */ + .quad sys_signalfd + .quad sys_timerfd + .quad sys_eventfd ia32_syscall_end: diff --git a/trunk/arch/x86_64/kernel/head64.c b/trunk/arch/x86_64/kernel/head64.c index 213d90e04755..6c34bdd22e26 100644 --- a/trunk/arch/x86_64/kernel/head64.c +++ b/trunk/arch/x86_64/kernel/head64.c @@ -62,13 +62,6 @@ void __init x86_64_start_kernel(char * real_mode_data) { int i; - /* - * Make sure kernel is aligned to 2MB address. Catching it at compile - * time is better. Change your config file and compile the kernel - * for a 2MB aligned address (CONFIG_PHYSICAL_START) - */ - BUILD_BUG_ON(CONFIG_PHYSICAL_START & (__KERNEL_ALIGN - 1)); - /* clear bss before set_intr_gate with early_idt_handler */ clear_bss(); diff --git a/trunk/drivers/bluetooth/hci_ldisc.c b/trunk/drivers/bluetooth/hci_ldisc.c index 0f4203b499af..6055b9c0ac0f 100644 --- a/trunk/drivers/bluetooth/hci_ldisc.c +++ b/trunk/drivers/bluetooth/hci_ldisc.c @@ -307,7 +307,9 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (hu) { struct hci_dev *hdev = hu->hdev; - hci_uart_close(hdev); + + if (hdev) + hci_uart_close(hdev); if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) { hu->proto->close(hu); @@ -473,12 +475,18 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, tty->low_latency = 1; } else return -EBUSY; + break; case HCIUARTGETPROTO: if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) return hu->proto->id; return -EUNATCH; + case HCIUARTGETDEVICE: + if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) + return hu->hdev->id; + return -EUNATCH; + default: err = n_tty_ioctl(tty, file, cmd, arg); break; diff --git a/trunk/drivers/bluetooth/hci_uart.h b/trunk/drivers/bluetooth/hci_uart.h index b250e6789dee..1097ce72393f 100644 --- a/trunk/drivers/bluetooth/hci_uart.h +++ b/trunk/drivers/bluetooth/hci_uart.h @@ -28,8 +28,9 @@ #endif /* Ioctls */ -#define HCIUARTSETPROTO _IOW('U', 200, int) -#define HCIUARTGETPROTO _IOR('U', 201, int) +#define HCIUARTSETPROTO _IOW('U', 200, int) +#define HCIUARTGETPROTO _IOR('U', 201, int) +#define HCIUARTGETDEVICE _IOR('U', 202, int) /* UART protocols */ #define HCI_UART_MAX_PROTO 4 diff --git a/trunk/drivers/char/n_tty.c b/trunk/drivers/char/n_tty.c index 6ac3ca4c723c..b3d4ccc33a47 100644 --- a/trunk/drivers/char/n_tty.c +++ b/trunk/drivers/char/n_tty.c @@ -1544,21 +1544,18 @@ static unsigned int normal_poll(struct tty_struct * tty, struct file * file, pol } struct tty_ldisc tty_ldisc_N_TTY = { - TTY_LDISC_MAGIC, /* magic */ - "n_tty", /* name */ - 0, /* num */ - 0, /* flags */ - n_tty_open, /* open */ - n_tty_close, /* close */ - n_tty_flush_buffer, /* flush_buffer */ - n_tty_chars_in_buffer, /* chars_in_buffer */ - read_chan, /* read */ - write_chan, /* write */ - n_tty_ioctl, /* ioctl */ - n_tty_set_termios, /* set_termios */ - normal_poll, /* poll */ - NULL, /* hangup */ - n_tty_receive_buf, /* receive_buf */ - n_tty_write_wakeup /* write_wakeup */ + .magic = TTY_LDISC_MAGIC, + .name = "n_tty", + .open = n_tty_open, + .close = n_tty_close, + .flush_buffer = n_tty_flush_buffer, + .chars_in_buffer = n_tty_chars_in_buffer, + .read = read_chan, + .write = write_chan, + .ioctl = n_tty_ioctl, + .set_termios = n_tty_set_termios, + .poll = normal_poll, + .receive_buf = n_tty_receive_buf, + .write_wakeup = n_tty_write_wakeup }; diff --git a/trunk/drivers/char/rio/riocmd.c b/trunk/drivers/char/rio/riocmd.c index 245f03195b7c..8cc60b693460 100644 --- a/trunk/drivers/char/rio/riocmd.c +++ b/trunk/drivers/char/rio/riocmd.c @@ -402,7 +402,7 @@ static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struc rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: Host number %Zd, name ``%s''\n", HostP - p->RIOHosts, HostP->Name); rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: Rup number 0x%x\n", rup); - if (Rup >= (unsigned short) MAX_RUP) { + if (Rup < (unsigned short) MAX_RUP) { rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: This is the RUP for RTA ``%s''\n", HostP->Mapping[Rup].Name); } else rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: This is the RUP for link ``%c'' of host ``%s''\n", ('A' + Rup - MAX_RUP), HostP->Name); diff --git a/trunk/drivers/char/rocket.c b/trunk/drivers/char/rocket.c index 61a63da420c2..a3fd7e7ba5a9 100644 --- a/trunk/drivers/char/rocket.c +++ b/trunk/drivers/char/rocket.c @@ -1014,9 +1014,6 @@ static int rp_open(struct tty_struct *tty, struct file *filp) /* * Info->count is now 1; so it's safe to sleep now. */ - info->session = process_session(current); - info->pgrp = process_group(current); - if ((info->flags & ROCKET_INITIALIZED) == 0) { cp = &info->channel; sSetRxTrigger(cp, TRIG_1); diff --git a/trunk/drivers/char/rocket_int.h b/trunk/drivers/char/rocket_int.h index 89b4d7b10d12..b4c53dfa7951 100644 --- a/trunk/drivers/char/rocket_int.h +++ b/trunk/drivers/char/rocket_int.h @@ -1158,8 +1158,6 @@ struct r_port { int xmit_head; int xmit_tail; int xmit_cnt; - int session; - int pgrp; int cd_status; int ignore_status_mask; int read_status_mask; diff --git a/trunk/drivers/char/synclink_gt.c b/trunk/drivers/char/synclink_gt.c index 2a7736b5f2f7..02b49bc00028 100644 --- a/trunk/drivers/char/synclink_gt.c +++ b/trunk/drivers/char/synclink_gt.c @@ -1170,6 +1170,112 @@ static int ioctl(struct tty_struct *tty, struct file *file, return 0; } +/* + * support for 32 bit ioctl calls on 64 bit systems + */ +#ifdef CONFIG_COMPAT +static long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params) +{ + struct MGSL_PARAMS32 tmp_params; + + DBGINFO(("%s get_params32\n", info->device_name)); + tmp_params.mode = (compat_ulong_t)info->params.mode; + tmp_params.loopback = info->params.loopback; + tmp_params.flags = info->params.flags; + tmp_params.encoding = info->params.encoding; + tmp_params.clock_speed = (compat_ulong_t)info->params.clock_speed; + tmp_params.addr_filter = info->params.addr_filter; + tmp_params.crc_type = info->params.crc_type; + tmp_params.preamble_length = info->params.preamble_length; + tmp_params.preamble = info->params.preamble; + tmp_params.data_rate = (compat_ulong_t)info->params.data_rate; + tmp_params.data_bits = info->params.data_bits; + tmp_params.stop_bits = info->params.stop_bits; + tmp_params.parity = info->params.parity; + if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32))) + return -EFAULT; + return 0; +} + +static long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params) +{ + struct MGSL_PARAMS32 tmp_params; + + DBGINFO(("%s set_params32\n", info->device_name)); + if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32))) + return -EFAULT; + + spin_lock(&info->lock); + info->params.mode = tmp_params.mode; + info->params.loopback = tmp_params.loopback; + info->params.flags = tmp_params.flags; + info->params.encoding = tmp_params.encoding; + info->params.clock_speed = tmp_params.clock_speed; + info->params.addr_filter = tmp_params.addr_filter; + info->params.crc_type = tmp_params.crc_type; + info->params.preamble_length = tmp_params.preamble_length; + info->params.preamble = tmp_params.preamble; + info->params.data_rate = tmp_params.data_rate; + info->params.data_bits = tmp_params.data_bits; + info->params.stop_bits = tmp_params.stop_bits; + info->params.parity = tmp_params.parity; + spin_unlock(&info->lock); + + change_params(info); + + return 0; +} + +static long slgt_compat_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct slgt_info *info = tty->driver_data; + int rc = -ENOIOCTLCMD; + + if (sanity_check(info, tty->name, "compat_ioctl")) + return -ENODEV; + DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd)); + + switch (cmd) { + + case MGSL_IOCSPARAMS32: + rc = set_params32(info, compat_ptr(arg)); + break; + + case MGSL_IOCGPARAMS32: + rc = get_params32(info, compat_ptr(arg)); + break; + + case MGSL_IOCGPARAMS: + case MGSL_IOCSPARAMS: + case MGSL_IOCGTXIDLE: + case MGSL_IOCGSTATS: + case MGSL_IOCWAITEVENT: + case MGSL_IOCGIF: + case MGSL_IOCSGPIO: + case MGSL_IOCGGPIO: + case MGSL_IOCWAITGPIO: + case TIOCGICOUNT: + rc = ioctl(tty, file, cmd, (unsigned long)(compat_ptr(arg))); + break; + + case MGSL_IOCSTXIDLE: + case MGSL_IOCTXENABLE: + case MGSL_IOCRXENABLE: + case MGSL_IOCTXABORT: + case TIOCMIWAIT: + case MGSL_IOCSIF: + rc = ioctl(tty, file, cmd, arg); + break; + } + + DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc)); + return rc; +} +#else +#define slgt_compat_ioctl NULL +#endif /* ifdef CONFIG_COMPAT */ + /* * proc fs support */ @@ -3446,6 +3552,7 @@ static const struct tty_operations ops = { .chars_in_buffer = chars_in_buffer, .flush_buffer = flush_buffer, .ioctl = ioctl, + .compat_ioctl = slgt_compat_ioctl, .throttle = throttle, .unthrottle = unthrottle, .send_xchar = send_xchar, diff --git a/trunk/drivers/char/tty_io.c b/trunk/drivers/char/tty_io.c index fc662e4ce58a..fe62c2170d01 100644 --- a/trunk/drivers/char/tty_io.c +++ b/trunk/drivers/char/tty_io.c @@ -151,6 +151,12 @@ static int tty_open(struct inode *, struct file *); static int tty_release(struct inode *, struct file *); int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +static long tty_compat_ioctl(struct file * file, unsigned int cmd, + unsigned long arg); +#else +#define tty_compat_ioctl NULL +#endif static int tty_fasync(int fd, struct file * filp, int on); static void release_tty(struct tty_struct *tty, int idx); static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); @@ -1143,8 +1149,8 @@ static unsigned int hung_up_tty_poll(struct file * filp, poll_table * wait) return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM; } -static int hung_up_tty_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg) +static long hung_up_tty_ioctl(struct file * file, + unsigned int cmd, unsigned long arg) { return cmd == TIOCSPGRP ? -ENOTTY : -EIO; } @@ -1155,6 +1161,7 @@ static const struct file_operations tty_fops = { .write = tty_write, .poll = tty_poll, .ioctl = tty_ioctl, + .compat_ioctl = tty_compat_ioctl, .open = tty_open, .release = tty_release, .fasync = tty_fasync, @@ -1167,6 +1174,7 @@ static const struct file_operations ptmx_fops = { .write = tty_write, .poll = tty_poll, .ioctl = tty_ioctl, + .compat_ioctl = tty_compat_ioctl, .open = ptmx_open, .release = tty_release, .fasync = tty_fasync, @@ -1179,6 +1187,7 @@ static const struct file_operations console_fops = { .write = redirected_tty_write, .poll = tty_poll, .ioctl = tty_ioctl, + .compat_ioctl = tty_compat_ioctl, .open = tty_open, .release = tty_release, .fasync = tty_fasync, @@ -1189,7 +1198,8 @@ static const struct file_operations hung_up_tty_fops = { .read = hung_up_tty_read, .write = hung_up_tty_write, .poll = hung_up_tty_poll, - .ioctl = hung_up_tty_ioctl, + .unlocked_ioctl = hung_up_tty_ioctl, + .compat_ioctl = hung_up_tty_ioctl, .release = tty_release, }; @@ -3357,6 +3367,32 @@ int tty_ioctl(struct inode * inode, struct file * file, return retval; } +#ifdef CONFIG_COMPAT +static long tty_compat_ioctl(struct file * file, unsigned int cmd, + unsigned long arg) +{ + struct inode *inode = file->f_dentry->d_inode; + struct tty_struct *tty = file->private_data; + struct tty_ldisc *ld; + int retval = -ENOIOCTLCMD; + + if (tty_paranoia_check(tty, inode, "tty_ioctl")) + return -EINVAL; + + if (tty->driver->compat_ioctl) { + retval = (tty->driver->compat_ioctl)(tty, file, cmd, arg); + if (retval != -ENOIOCTLCMD) + return retval; + } + + ld = tty_ldisc_ref_wait(tty); + if (ld->compat_ioctl) + retval = ld->compat_ioctl(tty, file, cmd, arg); + tty_ldisc_deref(ld); + + return retval; +} +#endif /* * This implements the "Secure Attention Key" --- the idea is to @@ -3689,6 +3725,7 @@ void tty_set_operations(struct tty_driver *driver, driver->write_room = op->write_room; driver->chars_in_buffer = op->chars_in_buffer; driver->ioctl = op->ioctl; + driver->compat_ioctl = op->compat_ioctl; driver->set_termios = op->set_termios; driver->throttle = op->throttle; driver->unthrottle = op->unthrottle; diff --git a/trunk/drivers/hid/hid-input.c b/trunk/drivers/hid/hid-input.c index a19b65ed3119..7f817897b178 100644 --- a/trunk/drivers/hid/hid-input.c +++ b/trunk/drivers/hid/hid-input.c @@ -240,11 +240,94 @@ static inline void hidinput_pb_setup(struct input_dev *input) } #endif +static inline int match_scancode(int code, int scancode) +{ + if (scancode == 0) + return 1; + return ((code & (HID_USAGE_PAGE | HID_USAGE)) == scancode); +} + +static inline int match_keycode(int code, int keycode) +{ + if (keycode == 0) + return 1; + return (code == keycode); +} + +static struct hid_usage *hidinput_find_key(struct hid_device *hid, + int scancode, int keycode) +{ + int i, j, k; + struct hid_report *report; + struct hid_usage *usage; + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { + list_for_each_entry(report, &hid->report_enum[k].report_list, list) { + for (i = 0; i < report->maxfield; i++) { + for ( j = 0; j < report->field[i]->maxusage; j++) { + usage = report->field[i]->usage + j; + if (usage->type == EV_KEY && + match_scancode(usage->hid, scancode) && + match_keycode(usage->code, keycode)) + return usage; + } + } + } + } + return NULL; +} + +static int hidinput_getkeycode(struct input_dev *dev, int scancode, + int *keycode) +{ + struct hid_device *hid = dev->private; + struct hid_usage *usage; + + usage = hidinput_find_key(hid, scancode, 0); + if (usage) { + *keycode = usage->code; + return 0; + } + return -EINVAL; +} + +static int hidinput_setkeycode(struct input_dev *dev, int scancode, + int keycode) +{ + struct hid_device *hid = dev->private; + struct hid_usage *usage; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + usage = hidinput_find_key(hid, scancode, 0); + if (usage) { + old_keycode = usage->code; + usage->code = keycode; + + clear_bit(old_keycode, dev->keybit); + set_bit(usage->code, dev->keybit); +#ifdef CONFIG_HID_DEBUG + printk (KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode); +#endif + /* Set the keybit for the old keycode if the old keycode is used + * by another key */ + if (hidinput_find_key (hid, 0, old_keycode)) + set_bit(old_keycode, dev->keybit); + + return 0; + } + + return -EINVAL; +} + + static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage) { struct input_dev *input = hidinput->input; - struct hid_device *device = input->private; + struct hid_device *device = input_get_drvdata(input); int max = 0, code; unsigned long *bit = NULL; @@ -553,6 +636,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x1015: map_key_clear(KEY_RECORD); break; case 0x1016: map_key_clear(KEY_PLAYER); break; case 0x1017: map_key_clear(KEY_EJECTCD); break; + case 0x1018: map_key_clear(KEY_MEDIA); break; case 0x1019: map_key_clear(KEY_PROG1); break; case 0x101a: map_key_clear(KEY_PROG2); break; case 0x101b: map_key_clear(KEY_PROG3); break; @@ -560,9 +644,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x1020: map_key_clear(KEY_ZOOMOUT); break; case 0x1021: map_key_clear(KEY_ZOOMRESET); break; case 0x1023: map_key_clear(KEY_CLOSE); break; + case 0x1027: map_key_clear(KEY_MENU); break; /* this one is marked as 'Rotate' */ case 0x1028: map_key_clear(KEY_ANGLE); break; case 0x1029: map_key_clear(KEY_SHUFFLE); break; + case 0x102a: map_key_clear(KEY_BACK); break; + case 0x102b: map_key_clear(KEY_CYCLEWINDOWS); break; case 0x1041: map_key_clear(KEY_BATTERY); break; case 0x1042: map_key_clear(KEY_WORDPROCESSOR); break; case 0x1043: map_key_clear(KEY_SPREADSHEET); break; @@ -855,13 +942,15 @@ EXPORT_SYMBOL_GPL(hidinput_find_field); static int hidinput_open(struct input_dev *dev) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); + return hid->hid_open(hid); } static void hidinput_close(struct input_dev *dev) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); + hid->hid_close(hid); } @@ -909,10 +998,12 @@ int hidinput_connect(struct hid_device *hid) return -1; } - input_dev->private = hid; + input_set_drvdata(input_dev, hid); input_dev->event = hid->hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; + input_dev->setkeycode = hidinput_setkeycode; + input_dev->getkeycode = hidinput_getkeycode; input_dev->name = hid->name; input_dev->phys = hid->phys; @@ -921,7 +1012,7 @@ int hidinput_connect(struct hid_device *hid) input_dev->id.vendor = hid->vendor; input_dev->id.product = hid->product; input_dev->id.version = hid->version; - input_dev->cdev.dev = hid->dev; + input_dev->dev.parent = hid->dev; hidinput->input = input_dev; list_add_tail(&hidinput->list, &hid->inputs); } diff --git a/trunk/drivers/hid/usbhid/Kconfig b/trunk/drivers/hid/usbhid/Kconfig index 7c87bdc538bc..1b4b572f899b 100644 --- a/trunk/drivers/hid/usbhid/Kconfig +++ b/trunk/drivers/hid/usbhid/Kconfig @@ -25,12 +25,12 @@ comment "Input core support is needed for USB HID input layer or HIDBP support" depends on USB_HID && INPUT=n config USB_HIDINPUT_POWERBOOK - bool "Enable support for iBook/PowerBook special keys" + bool "Enable support for iBook/PowerBook/MacBook/MacBookPro special keys" default n depends on USB_HID help Say Y here if you want support for the special keys (Fn, Numlock) on - Apple iBooks and PowerBooks. + Apple iBooks, PowerBooks, MacBooks and MacBook Pros. If unsure, say N. diff --git a/trunk/drivers/hid/usbhid/hid-core.c b/trunk/drivers/hid/usbhid/hid-core.c index 91d610358d57..d91b9dac6dff 100644 --- a/trunk/drivers/hid/usbhid/hid-core.c +++ b/trunk/drivers/hid/usbhid/hid-core.c @@ -446,7 +446,7 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct hid_field *field; int offset; @@ -626,14 +626,10 @@ static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; - if (usbhid->inbuf) - usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma); - if (usbhid->outbuf) - usb_buffer_free(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma); - if (usbhid->cr) - usb_buffer_free(dev, sizeof(*(usbhid->cr)), usbhid->cr, usbhid->cr_dma); - if (usbhid->ctrlbuf) - usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma); + usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma); + usb_buffer_free(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma); + usb_buffer_free(dev, sizeof(*(usbhid->cr)), usbhid->cr, usbhid->cr_dma); + usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma); } /* @@ -692,6 +688,30 @@ static void hid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize) } } +/* + * Some USB barcode readers from cypress have usage min and usage max in + * the wrong order + */ +static void hid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize) +{ + short fixed = 0; + int i; + + for (i = 0; i < rsize - 4; i++) { + if (rdesc[i] == 0x29 && rdesc [i+2] == 0x19) { + unsigned char tmp; + + rdesc[i] = 0x19; rdesc[i+2] = 0x29; + tmp = rdesc[i+3]; + rdesc[i+3] = rdesc[i+1]; + rdesc[i+1] = tmp; + } + } + + if (fixed) + info("Fixing up Cypress report descriptor"); +} + static struct hid_device *usb_hid_configure(struct usb_interface *intf) { struct usb_host_interface *interface = intf->cur_altsetting; @@ -758,6 +778,9 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if (quirks & HID_QUIRK_LOGITECH_DESCRIPTOR) hid_fixup_logitech_descriptor(rdesc, rsize); + if (quirks & HID_QUIRK_SWAPPED_MIN_MAX) + hid_fixup_cypress_descriptor(rdesc, rsize); + #ifdef CONFIG_HID_DEBUG printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); for (n = 0; n < rsize; n++) diff --git a/trunk/drivers/hid/usbhid/hid-lgff.c b/trunk/drivers/hid/usbhid/hid-lgff.c index 92d2553f17b6..c5cd4107d6af 100644 --- a/trunk/drivers/hid/usbhid/hid-lgff.c +++ b/trunk/drivers/hid/usbhid/hid-lgff.c @@ -60,7 +60,7 @@ static const struct dev_type devices[] = { static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct hid_report *report = list_entry(report_list->next, struct hid_report, list); int x, y; diff --git a/trunk/drivers/hid/usbhid/hid-plff.c b/trunk/drivers/hid/usbhid/hid-plff.c index 76d2e6e14db4..d6a8f2b49bd2 100644 --- a/trunk/drivers/hid/usbhid/hid-plff.c +++ b/trunk/drivers/hid/usbhid/hid-plff.c @@ -37,7 +37,7 @@ struct plff_device { static int hid_plff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct plff_device *plff = data; int left, right; diff --git a/trunk/drivers/hid/usbhid/hid-quirks.c b/trunk/drivers/hid/usbhid/hid-quirks.c index 17a87555e32f..f6c4145dc202 100644 --- a/trunk/drivers/hid/usbhid/hid-quirks.c +++ b/trunk/drivers/hid/usbhid/hid-quirks.c @@ -92,6 +92,8 @@ #define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 #define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 #define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 +#define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61 +#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64 #define USB_VENDOR_ID_DELL 0x413c #define USB_DEVICE_ID_DELL_W7658 0x2005 @@ -193,6 +195,7 @@ #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 +#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_S510_RECEIVER 0xc50c #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 #define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 @@ -422,6 +425,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, @@ -445,6 +449,9 @@ static const struct hid_blacklist { { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_SWAPPED_MIN_MAX }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_SWAPPED_MIN_MAX }, + { 0, 0 } }; diff --git a/trunk/drivers/hid/usbhid/hid-tmff.c b/trunk/drivers/hid/usbhid/hid-tmff.c index ab67331620d0..ab5ba6ef891c 100644 --- a/trunk/drivers/hid/usbhid/hid-tmff.c +++ b/trunk/drivers/hid/usbhid/hid-tmff.c @@ -59,7 +59,7 @@ static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct tmff_device *tmff = data; int left, right; /* Rumbling */ diff --git a/trunk/drivers/hid/usbhid/hid-zpff.c b/trunk/drivers/hid/usbhid/hid-zpff.c index 7bd8238ca212..a7fbffcdaf36 100644 --- a/trunk/drivers/hid/usbhid/hid-zpff.c +++ b/trunk/drivers/hid/usbhid/hid-zpff.c @@ -37,7 +37,7 @@ struct zpff_device { static int hid_zpff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct zpff_device *zpff = data; int left, right; diff --git a/trunk/drivers/hid/usbhid/hiddev.c b/trunk/drivers/hid/usbhid/hiddev.c index a8b3d66cd498..488d61bdbf2c 100644 --- a/trunk/drivers/hid/usbhid/hiddev.c +++ b/trunk/drivers/hid/usbhid/hiddev.c @@ -51,6 +51,7 @@ struct hiddev { wait_queue_head_t wait; struct hid_device *hid; struct list_head list; + spinlock_t list_lock; }; struct hiddev_list { @@ -161,7 +162,9 @@ static void hiddev_send_event(struct hid_device *hid, { struct hiddev *hiddev = hid->hiddev; struct hiddev_list *list; + unsigned long flags; + spin_lock_irqsave(&hiddev->list_lock, flags); list_for_each_entry(list, &hiddev->list, node) { if (uref->field_index != HID_FIELD_INDEX_NONE || (list->flags & HIDDEV_FLAG_REPORT) != 0) { @@ -171,6 +174,7 @@ static void hiddev_send_event(struct hid_device *hid, kill_fasync(&list->fasync, SIGIO, POLL_IN); } } + spin_unlock_irqrestore(&hiddev->list_lock, flags); wake_up_interruptible(&hiddev->wait); } @@ -235,9 +239,13 @@ static int hiddev_fasync(int fd, struct file *file, int on) static int hiddev_release(struct inode * inode, struct file * file) { struct hiddev_list *list = file->private_data; + unsigned long flags; hiddev_fasync(-1, file, 0); + + spin_lock_irqsave(&list->hiddev->list_lock, flags); list_del(&list->node); + spin_unlock_irqrestore(&list->hiddev->list_lock, flags); if (!--list->hiddev->open) { if (list->hiddev->exist) @@ -257,6 +265,7 @@ static int hiddev_release(struct inode * inode, struct file * file) static int hiddev_open(struct inode *inode, struct file *file) { struct hiddev_list *list; + unsigned long flags; int i = iminor(inode) - HIDDEV_MINOR_BASE; @@ -267,7 +276,11 @@ static int hiddev_open(struct inode *inode, struct file *file) return -ENOMEM; list->hiddev = hiddev_table[i]; + + spin_lock_irqsave(&list->hiddev->list_lock, flags); list_add_tail(&list->node, &hiddev_table[i]->list); + spin_unlock_irqrestore(&list->hiddev->list_lock, flags); + file->private_data = list; if (!list->hiddev->open++) @@ -773,6 +786,7 @@ int hiddev_connect(struct hid_device *hid) init_waitqueue_head(&hiddev->wait); INIT_LIST_HEAD(&hiddev->list); + spin_lock_init(&hiddev->list_lock); hiddev->hid = hid; hiddev->exist = 1; diff --git a/trunk/drivers/hid/usbhid/usbkbd.c b/trunk/drivers/hid/usbhid/usbkbd.c index 65aa12e8d7b3..130978780713 100644 --- a/trunk/drivers/hid/usbhid/usbkbd.c +++ b/trunk/drivers/hid/usbhid/usbkbd.c @@ -133,12 +133,11 @@ static void usb_kbd_irq(struct urb *urb) static int usb_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct usb_kbd *kbd = dev->private; + struct usb_kbd *kbd = input_get_drvdata(dev); if (type != EV_LED) return -1; - kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | (!!test_bit(LED_NUML, dev->led)); @@ -175,7 +174,7 @@ static void usb_kbd_led(struct urb *urb) static int usb_kbd_open(struct input_dev *dev) { - struct usb_kbd *kbd = dev->private; + struct usb_kbd *kbd = input_get_drvdata(dev); kbd->irq->dev = kbd->usbdev; if (usb_submit_urb(kbd->irq, GFP_KERNEL)) @@ -186,7 +185,7 @@ static int usb_kbd_open(struct input_dev *dev) static void usb_kbd_close(struct input_dev *dev) { - struct usb_kbd *kbd = dev->private; + struct usb_kbd *kbd = input_get_drvdata(dev); usb_kill_urb(kbd->irq); } @@ -211,12 +210,9 @@ static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd) { usb_free_urb(kbd->irq); usb_free_urb(kbd->led); - if (kbd->new) - usb_buffer_free(dev, 8, kbd->new, kbd->new_dma); - if (kbd->cr) - usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma); - if (kbd->leds) - usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma); + usb_buffer_free(dev, 8, kbd->new, kbd->new_dma); + usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma); + usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma); } static int usb_kbd_probe(struct usb_interface *iface, @@ -274,8 +270,9 @@ static int usb_kbd_probe(struct usb_interface *iface, input_dev->name = kbd->name; input_dev->phys = kbd->phys; usb_to_input_id(dev, &input_dev->id); - input_dev->cdev.dev = &iface->dev; - input_dev->private = kbd; + input_dev->dev.parent = &iface->dev; + + input_set_drvdata(input_dev, kbd); input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA); diff --git a/trunk/drivers/hid/usbhid/usbmouse.c b/trunk/drivers/hid/usbhid/usbmouse.c index 573776d865e1..5345c73bcf62 100644 --- a/trunk/drivers/hid/usbhid/usbmouse.c +++ b/trunk/drivers/hid/usbhid/usbmouse.c @@ -96,7 +96,7 @@ static void usb_mouse_irq(struct urb *urb) static int usb_mouse_open(struct input_dev *dev) { - struct usb_mouse *mouse = dev->private; + struct usb_mouse *mouse = input_get_drvdata(dev); mouse->irq->dev = mouse->usbdev; if (usb_submit_urb(mouse->irq, GFP_KERNEL)) @@ -107,7 +107,7 @@ static int usb_mouse_open(struct input_dev *dev) static void usb_mouse_close(struct input_dev *dev) { - struct usb_mouse *mouse = dev->private; + struct usb_mouse *mouse = input_get_drvdata(dev); usb_kill_urb(mouse->irq); } @@ -171,7 +171,7 @@ static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_i input_dev->name = mouse->name; input_dev->phys = mouse->phys; usb_to_input_id(dev, &input_dev->id); - input_dev->cdev.dev = &intf->dev; + input_dev->dev.parent = &intf->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); @@ -179,7 +179,8 @@ static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_i input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA); input_dev->relbit[0] |= BIT(REL_WHEEL); - input_dev->private = mouse; + input_set_drvdata(input_dev, mouse); + input_dev->open = usb_mouse_open; input_dev->close = usb_mouse_close; diff --git a/trunk/drivers/input/evdev.c b/trunk/drivers/input/evdev.c index 55a72592704c..b234729706be 100644 --- a/trunk/drivers/input/evdev.c +++ b/trunk/drivers/input/evdev.c @@ -336,7 +336,7 @@ static int bits_to_user(unsigned long *bits, unsigned int maxbit, if (compat) { len = NBITS_COMPAT(maxbit) * sizeof(compat_long_t); - if (len < maxlen) + if (len > maxlen) len = maxlen; for (i = 0; i < len / sizeof(compat_long_t); i++) diff --git a/trunk/drivers/md/md.c b/trunk/drivers/md/md.c index 65814b0340cb..c10ce91b64e9 100644 --- a/trunk/drivers/md/md.c +++ b/trunk/drivers/md/md.c @@ -5103,7 +5103,7 @@ static int is_mddev_idle(mddev_t *mddev) * * Note: the following is an unsigned comparison. */ - if ((curr_events - rdev->last_events + 4096) > 8192) { + if ((long)curr_events - (long)rdev->last_events > 4096) { rdev->last_events = curr_events; idle = 0; } diff --git a/trunk/drivers/net/irda/Kconfig b/trunk/drivers/net/irda/Kconfig index 7c8ccc09b601..829da9a1d113 100644 --- a/trunk/drivers/net/irda/Kconfig +++ b/trunk/drivers/net/irda/Kconfig @@ -141,6 +141,20 @@ config ACT200L_DONGLE To activate support for ACTiSYS IR-200L dongle you will have to start irattach like this: "irattach -d act200l". +config KINGSUN_DONGLE + tristate "KingSun/DonShine DS-620 IrDA-USB dongle" + depends on IRDA && USB && EXPERIMENTAL + help + Say Y or M here if you want to build support for the KingSun/DonShine + DS-620 IrDA-USB bridge device driver. + + This USB bridge does not conform to the IrDA-USB device class + specification, and therefore needs its own specific driver. This + dongle supports SIR speed only (9600 bps). + + To compile it as a module, choose M here: the module will be called + kingsun-sir. + comment "Old SIR device drivers" config IRPORT_SIR diff --git a/trunk/drivers/net/irda/Makefile b/trunk/drivers/net/irda/Makefile index 5be09f1b9ee2..233a2f923730 100644 --- a/trunk/drivers/net/irda/Makefile +++ b/trunk/drivers/net/irda/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_MCP2120_DONGLE) += mcp2120-sir.o obj-$(CONFIG_ACT200L_DONGLE) += act200l-sir.o obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o obj-$(CONFIG_TOIM3232_DONGLE) += toim3232-sir.o +obj-$(CONFIG_KINGSUN_DONGLE) += kingsun-sir.o # The SIR helper module sir-dev-objs := sir_dev.o sir_dongle.o diff --git a/trunk/drivers/net/irda/kingsun-sir.c b/trunk/drivers/net/irda/kingsun-sir.c new file mode 100644 index 000000000000..217429122e79 --- /dev/null +++ b/trunk/drivers/net/irda/kingsun-sir.c @@ -0,0 +1,657 @@ +/***************************************************************************** +* +* Filename: kingsun-sir.c +* Version: 0.1.1 +* Description: Irda KingSun/DonShine USB Dongle +* Status: Experimental +* Author: Alex Villac�s Lasso +* +* Based on stir4200 and mcs7780 drivers, with (strange?) differences +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +/* + * This is my current (2007-04-25) understanding of how this dongle is supposed + * to work. This is based on reverse-engineering and examination of the packet + * data sent and received by the WinXP driver using USBSnoopy. Feel free to + * update here as more of this dongle is known: + * + * General: Unlike the other USB IrDA dongles, this particular dongle exposes, + * not two bulk (in and out) endpoints, but two *interrupt* ones. This dongle, + * like the bulk based ones (stir4200.c and mcs7780.c), requires polling in + * order to receive data. + * Transmission: Just like stir4200, this dongle uses a raw stream of data, + * which needs to be wrapped and escaped in a similar way as in stir4200.c. + * Reception: Poll-based, as in stir4200. Each read returns the contents of a + * 8-byte buffer, of which the first byte (LSB) indicates the number of bytes + * (1-7) of valid data contained within the remaining 7 bytes. For example, if + * the buffer had the following contents: + * 06 ff ff ff c0 01 04 aa + * This means that (06) there are 6 bytes of valid data. The byte 0xaa at the + * end is garbage (left over from a previous reception) and is discarded. + * If a read returns an "impossible" value as the length of valid data (such as + * 0x36) in the first byte, then the buffer is uninitialized (as is the case of + * first plug-in) and its contents should be discarded. There is currently no + * evidence that the top 5 bits of the 1st byte of the buffer can have values + * other than 0 once reception begins. + * Once valid bytes are collected, the assembled stream is a sequence of + * wrapped IrDA frames that is unwrapped and unescaped as in stir4200.c. + * BIG FAT WARNING: the dongle does *not* reset the RX buffer in any way after + * a successful read from the host, which means that in absence of further + * reception, repeated reads from the dongle will return the exact same + * contents repeatedly. Attempts to be smart and cache a previous read seem + * to result in corrupted packets, so this driver depends on the unwrap logic + * to sort out any repeated reads. + * Speed change: no commands observed so far to change speed, assumed fixed + * 9600bps (SIR). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* + * According to lsusb, 0x07c0 is assigned to + * "Code Mercenaries Hard- und Software GmbH" + */ +#define KING_VENDOR_ID 0x07c0 +#define KING_PRODUCT_ID 0x4200 + +/* These are the currently known USB ids */ +static struct usb_device_id dongles[] = { + /* KingSun Co,Ltd IrDA/USB Bridge */ + { USB_DEVICE(KING_VENDOR_ID, KING_PRODUCT_ID) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, dongles); + +#define KINGSUN_MTT 0x07 + +#define KINGSUN_FIFO_SIZE 4096 +#define KINGSUN_EP_IN 0 +#define KINGSUN_EP_OUT 1 + +struct kingsun_cb { + struct usb_device *usbdev; /* init: probe_irda */ + struct net_device *netdev; /* network layer */ + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct net_device_stats stats; /* network statistics */ + struct qos_info qos; + + __u8 *in_buf; /* receive buffer */ + __u8 *out_buf; /* transmit buffer */ + __u8 max_rx; /* max. atomic read from dongle + (usually 8), also size of in_buf */ + __u8 max_tx; /* max. atomic write to dongle + (usually 8) */ + + iobuff_t rx_buff; /* receive unwrap state machine */ + struct timeval rx_time; + spinlock_t lock; + int receiving; + + __u8 ep_in; + __u8 ep_out; + + struct urb *tx_urb; + struct urb *rx_urb; +}; + +/* Callback transmission routine */ +static void kingsun_send_irq(struct urb *urb) +{ + struct kingsun_cb *kingsun = urb->context; + struct net_device *netdev = kingsun->netdev; + + /* in process of stopping, just drop data */ + if (!netif_running(kingsun->netdev)) { + err("kingsun_send_irq: Network not running!"); + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + err("kingsun_send_irq: urb asynchronously failed - %d", + urb->status); + } + netif_wake_queue(netdev); +} + +/* + * Called from net/core when new frame is available. + */ +static int kingsun_hard_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct kingsun_cb *kingsun; + int wraplen; + int ret = 0; + + if (skb == NULL || netdev == NULL) + return -EINVAL; + + netif_stop_queue(netdev); + + /* the IRDA wrapping routines don't deal with non linear skb */ + SKB_LINEAR_ASSERT(skb); + + kingsun = netdev_priv(netdev); + + spin_lock(&kingsun->lock); + + /* Append data to the end of whatever data remains to be transmitted */ + wraplen = async_wrap_skb(skb, + kingsun->out_buf, + KINGSUN_FIFO_SIZE); + + /* Calculate how much data can be transmitted in this urb */ + usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev, + usb_sndintpipe(kingsun->usbdev, kingsun->ep_out), + kingsun->out_buf, wraplen, kingsun_send_irq, + kingsun, 1); + + if ((ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC))) { + err("kingsun_hard_xmit: failed tx_urb submit: %d", ret); + switch (ret) { + case -ENODEV: + case -EPIPE: + break; + default: + kingsun->stats.tx_errors++; + netif_start_queue(netdev); + } + } else { + kingsun->stats.tx_packets++; + kingsun->stats.tx_bytes += skb->len; + } + + dev_kfree_skb(skb); + spin_unlock(&kingsun->lock); + + return ret; +} + +/* Receive callback function */ +static void kingsun_rcv_irq(struct urb *urb) +{ + struct kingsun_cb *kingsun = urb->context; + int ret; + + /* in process of stopping, just drop data */ + if (!netif_running(kingsun->netdev)) { + kingsun->receiving = 0; + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + err("kingsun_rcv_irq: urb asynchronously failed - %d", + urb->status); + kingsun->receiving = 0; + return; + } + + if (urb->actual_length == kingsun->max_rx) { + __u8 *bytes = urb->transfer_buffer; + int i; + + /* The very first byte in the buffer indicates the length of + valid data in the read. This byte must be in the range + 1..kingsun->max_rx -1 . Values outside this range indicate + an uninitialized Rx buffer when the dongle has just been + plugged in. */ + if (bytes[0] >= 1 && bytes[0] < kingsun->max_rx) { + for (i = 1; i <= bytes[0]; i++) { + async_unwrap_char(kingsun->netdev, + &kingsun->stats, + &kingsun->rx_buff, bytes[i]); + } + kingsun->netdev->last_rx = jiffies; + do_gettimeofday(&kingsun->rx_time); + kingsun->receiving = + (kingsun->rx_buff.state != OUTSIDE_FRAME) + ? 1 : 0; + } + } else if (urb->actual_length > 0) { + err("%s(): Unexpected response length, expected %d got %d", + __FUNCTION__, kingsun->max_rx, urb->actual_length); + } + /* This urb has already been filled in kingsun_net_open */ + ret = usb_submit_urb(urb, GFP_ATOMIC); +} + +/* + * Function kingsun_net_open (dev) + * + * Network device is taken up. Usually this is done by "ifconfig irda0 up" + */ +static int kingsun_net_open(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + int err = -ENOMEM; + char hwname[16]; + + /* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */ + kingsun->receiving = 0; + + /* Initialize for SIR to copy data directly into skb. */ + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->rx_buff.truesize = IRDA_SKB_MAX_MTU; + kingsun->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); + if (!kingsun->rx_buff.skb) + goto free_mem; + + skb_reserve(kingsun->rx_buff.skb, 1); + kingsun->rx_buff.head = kingsun->rx_buff.skb->data; + do_gettimeofday(&kingsun->rx_time); + + kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->rx_urb) + goto free_mem; + + kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->tx_urb) + goto free_mem; + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + */ + sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); + kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); + if (!kingsun->irlap) { + err("kingsun-sir: irlap_open failed"); + goto free_mem; + } + + /* Start first reception */ + usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev, + usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in), + kingsun->in_buf, kingsun->max_rx, + kingsun_rcv_irq, kingsun, 1); + kingsun->rx_urb->status = 0; + err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + if (err) { + err("kingsun-sir: first urb-submit failed: %d", err); + goto close_irlap; + } + + netif_start_queue(netdev); + + /* Situation at this point: + - all work buffers allocated + - urbs allocated and ready to fill + - max rx packet known (in max_rx) + - unwrap state machine initialized, in state outside of any frame + - receive request in progress + - IrLAP layer started, about to hand over packets to send + */ + + return 0; + + close_irlap: + irlap_close(kingsun->irlap); + free_mem: + if (kingsun->tx_urb) { + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + } + if (kingsun->rx_urb) { + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + } + if (kingsun->rx_buff.skb) { + kfree_skb(kingsun->rx_buff.skb); + kingsun->rx_buff.skb = NULL; + kingsun->rx_buff.head = NULL; + } + return err; +} + +/* + * Function kingsun_net_close (kingsun) + * + * Network device is taken down. Usually this is done by + * "ifconfig irda0 down" + */ +static int kingsun_net_close(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + + /* Stop transmit processing */ + netif_stop_queue(netdev); + + /* Mop up receive && transmit urb's */ + usb_kill_urb(kingsun->tx_urb); + usb_kill_urb(kingsun->rx_urb); + + usb_free_urb(kingsun->tx_urb); + usb_free_urb(kingsun->rx_urb); + + kingsun->tx_urb = NULL; + kingsun->rx_urb = NULL; + + kfree_skb(kingsun->rx_buff.skb); + kingsun->rx_buff.skb = NULL; + kingsun->rx_buff.head = NULL; + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->receiving = 0; + + /* Stop and remove instance of IrLAP */ + if (kingsun->irlap) + irlap_close(kingsun->irlap); + + kingsun->irlap = NULL; + + return 0; +} + +/* + * IOCTLs : Extra out-of-band network commands... + */ +static int kingsun_net_ioctl(struct net_device *netdev, struct ifreq *rq, + int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct kingsun_cb *kingsun = netdev_priv(netdev); + int ret = 0; + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the device is still there */ + if (netif_device_present(kingsun->netdev)) + /* No observed commands for speed change */ + ret = -EOPNOTSUPP; + break; + + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the IrDA stack is still there */ + if (netif_running(kingsun->netdev)) + irda_device_set_media_busy(kingsun->netdev, TRUE); + break; + + case SIOCGRECEIVING: + /* Only approximately true */ + irq->ifr_receiving = kingsun->receiving; + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/* + * Get device stats (for /proc/net/dev and ifconfig) + */ +static struct net_device_stats * +kingsun_net_get_stats(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + return &kingsun->stats; +} + +/* + * This routine is called by the USB subsystem for each new device + * in the system. We need to check if the device is ours, and in + * this case start handling it. + */ +static int kingsun_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + + struct usb_device *dev = interface_to_usbdev(intf); + struct kingsun_cb *kingsun = NULL; + struct net_device *net = NULL; + int ret = -ENOMEM; + int pipe, maxp_in, maxp_out; + __u8 ep_in; + __u8 ep_out; + + /* Check that there really are two interrupt endpoints. + Check based on the one in drivers/usb/input/usbmouse.c + */ + interface = intf->cur_altsetting; + if (interface->desc.bNumEndpoints != 2) { + err("kingsun-sir: expected 2 endpoints, found %d", + interface->desc.bNumEndpoints); + return -ENODEV; + } + endpoint = &interface->endpoint[KINGSUN_EP_IN].desc; + if (!usb_endpoint_is_int_in(endpoint)) { + err("kingsun-sir: endpoint 0 is not interrupt IN"); + return -ENODEV; + } + + ep_in = endpoint->bEndpointAddress; + pipe = usb_rcvintpipe(dev, ep_in); + maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + if (maxp_in > 255 || maxp_in <= 1) { + err("%s: endpoint 0 has max packet size %d not in range", + __FILE__, maxp_in); + return -ENODEV; + } + + endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc; + if (!usb_endpoint_is_int_out(endpoint)) { + err("kingsun-sir: endpoint 1 is not interrupt OUT"); + return -ENODEV; + } + + ep_out = endpoint->bEndpointAddress; + pipe = usb_sndintpipe(dev, ep_out); + maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + /* Allocate network device container. */ + net = alloc_irdadev(sizeof(*kingsun)); + if(!net) + goto err_out1; + + SET_MODULE_OWNER(net); + SET_NETDEV_DEV(net, &intf->dev); + kingsun = netdev_priv(net); + kingsun->irlap = NULL; + kingsun->tx_urb = NULL; + kingsun->rx_urb = NULL; + kingsun->ep_in = ep_in; + kingsun->ep_out = ep_out; + kingsun->in_buf = NULL; + kingsun->out_buf = NULL; + kingsun->max_rx = (__u8)maxp_in; + kingsun->max_tx = (__u8)maxp_out; + kingsun->netdev = net; + kingsun->usbdev = dev; + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->rx_buff.skb = NULL; + kingsun->receiving = 0; + spin_lock_init(&kingsun->lock); + + /* Allocate input buffer */ + kingsun->in_buf = (__u8 *)kmalloc(kingsun->max_rx, GFP_KERNEL); + if (!kingsun->in_buf) + goto free_mem; + + /* Allocate output buffer */ + kingsun->out_buf = (__u8 *)kmalloc(KINGSUN_FIFO_SIZE, GFP_KERNEL); + if (!kingsun->out_buf) + goto free_mem; + + printk(KERN_INFO "KingSun/DonShine IRDA/USB found at address %d, " + "Vendor: %x, Product: %x\n", + dev->devnum, le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&kingsun->qos); + + /* That's the Rx capability. */ + kingsun->qos.baud_rate.bits &= IR_9600; + kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; + irda_qos_bits_to_value(&kingsun->qos); + + /* Override the network functions we need to use */ + net->hard_start_xmit = kingsun_hard_xmit; + net->open = kingsun_net_open; + net->stop = kingsun_net_close; + net->get_stats = kingsun_net_get_stats; + net->do_ioctl = kingsun_net_ioctl; + + ret = register_netdev(net); + if (ret != 0) + goto free_mem; + + info("IrDA: Registered KingSun/DonShine device %s", net->name); + + usb_set_intfdata(intf, kingsun); + + /* Situation at this point: + - all work buffers allocated + - urbs not allocated, set to NULL + - max rx packet known (in max_rx) + - unwrap state machine (partially) initialized, but skb == NULL + */ + + return 0; + +free_mem: + if (kingsun->out_buf) kfree(kingsun->out_buf); + if (kingsun->in_buf) kfree(kingsun->in_buf); + free_netdev(net); +err_out1: + return ret; +} + +/* + * The current device is removed, the USB layer tell us to shut it down... + */ +static void kingsun_disconnect(struct usb_interface *intf) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + if (!kingsun) + return; + + unregister_netdev(kingsun->netdev); + + /* Mop up receive && transmit urb's */ + if (kingsun->tx_urb != NULL) { + usb_kill_urb(kingsun->tx_urb); + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + } + if (kingsun->rx_urb != NULL) { + usb_kill_urb(kingsun->rx_urb); + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + } + + kfree(kingsun->out_buf); + kfree(kingsun->in_buf); + free_netdev(kingsun->netdev); + + usb_set_intfdata(intf, NULL); +} + +#ifdef CONFIG_PM +/* USB suspend, so power off the transmitter/receiver */ +static int kingsun_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + netif_device_detach(kingsun->netdev); + if (kingsun->tx_urb != NULL) usb_kill_urb(kingsun->tx_urb); + if (kingsun->rx_urb != NULL) usb_kill_urb(kingsun->rx_urb); + return 0; +} + +/* Coming out of suspend, so reset hardware */ +static int kingsun_resume(struct usb_interface *intf) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + if (kingsun->rx_urb != NULL) + usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + netif_device_attach(kingsun->netdev); + + return 0; +} +#endif + +/* + * USB device callbacks + */ +static struct usb_driver irda_driver = { + .name = "kingsun-sir", + .probe = kingsun_probe, + .disconnect = kingsun_disconnect, + .id_table = dongles, +#ifdef CONFIG_PM + .suspend = kingsun_suspend, + .resume = kingsun_resume, +#endif +}; + +/* + * Module insertion + */ +static int __init kingsun_init(void) +{ + return usb_register(&irda_driver); +} +module_init(kingsun_init); + +/* + * Module removal + */ +static void __exit kingsun_cleanup(void) +{ + /* Deregister the driver and remove all pending instances */ + usb_deregister(&irda_driver); +} +module_exit(kingsun_cleanup); + +MODULE_AUTHOR("Alex Villac�s Lasso "); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun/DonShine"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/rtc/Kconfig b/trunk/drivers/rtc/Kconfig index 1759baad439c..ad445d5e58c7 100644 --- a/trunk/drivers/rtc/Kconfig +++ b/trunk/drivers/rtc/Kconfig @@ -397,7 +397,7 @@ config RTC_DRV_BFIN config RTC_DRV_RS5C313 tristate "Ricoh RS5C313" - depends on RTC_CLASS && BROKEN + depends on RTC_CLASS && SH_LANDISK help If you say yes here you get support for the Ricoh RS5C313 RTC chips. diff --git a/trunk/drivers/rtc/rtc-rs5c313.c b/trunk/drivers/rtc/rtc-rs5c313.c index 9d6de371495b..66eb133bf5fd 100644 --- a/trunk/drivers/rtc/rtc-rs5c313.c +++ b/trunk/drivers/rtc/rtc-rs5c313.c @@ -126,7 +126,7 @@ static void rs5c313_write_data(unsigned char data) static unsigned char rs5c313_read_data(void) { int i; - unsigned char data; + unsigned char data = 0; for (i = 0; i < 8; i++) { ndelay(700); @@ -194,7 +194,7 @@ static void rs5c313_write_reg(unsigned char addr, unsigned char data) return; } -static inline unsigned char rs5c313_read_cntreg(unsigned char addr) +static inline unsigned char rs5c313_read_cntreg(void) { return rs5c313_read_reg(RS5C313_ADDR_CNTREG); } @@ -212,7 +212,9 @@ static inline void rs5c313_write_intintvreg(unsigned char data) static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm) { int data; + int cnt; + cnt = 0; while (1) { RS5C313_CEENABLE; /* CE:H */ @@ -225,6 +227,10 @@ static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm) RS5C313_CEDISABLE; ndelay(700); /* CE:L */ + if (cnt++ > 100) { + dev_err(dev, "%s: timeout error\n", __FUNCTION__); + return -EIO; + } } data = rs5c313_read_reg(RS5C313_ADDR_SEC); @@ -266,7 +272,9 @@ static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm) static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) { int data; + int cnt; + cnt = 0; /* busy check. */ while (1) { RS5C313_CEENABLE; /* CE:H */ @@ -279,6 +287,11 @@ static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) RS5C313_MISCOP; RS5C313_CEDISABLE; ndelay(700); /* CE:L */ + + if (cnt++ > 100) { + dev_err(dev, "%s: timeout error\n", __FUNCTION__); + return -EIO; + } } data = BIN2BCD(tm->tm_sec); @@ -317,6 +330,7 @@ static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) static void rs5c313_check_xstp_bit(void) { struct rtc_time tm; + int cnt; RS5C313_CEENABLE; /* CE:H */ if (rs5c313_read_cntreg() & RS5C313_CNTREG_WTEN_XSTP) { @@ -326,12 +340,16 @@ static void rs5c313_check_xstp_bit(void) rs5c313_write_cntreg(0x07); /* busy check. */ - while (rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY) + for (cnt = 0; cnt < 100; cnt++) { + if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY)) + break; RS5C313_MISCOP; + } memset(&tm, 0, sizeof(struct rtc_time)); tm.tm_mday = 1; - tm.tm_mon = 1; + tm.tm_mon = 1 - 1; + tm.tm_year = 2000 - 1900; rs5c313_rtc_set_time(NULL, &tm); printk(KERN_ERR "RICHO RS5C313: invalid value, resetting to " @@ -356,7 +374,7 @@ static int rs5c313_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rtc); - return err; + return 0; } static int __devexit rs5c313_rtc_remove(struct platform_device *pdev) diff --git a/trunk/drivers/spi/Kconfig b/trunk/drivers/spi/Kconfig index 7c9d37f651e3..5e3f748f2693 100644 --- a/trunk/drivers/spi/Kconfig +++ b/trunk/drivers/spi/Kconfig @@ -107,6 +107,13 @@ config SPI_IMX This enables using the Freescale iMX SPI controller in master mode. +config SPI_MPC52xx_PSC + tristate "Freescale MPC52xx PSC SPI controller" + depends on SPI_MASTER && PPC_MPC52xx && EXPERIMENTAL + help + This enables using the Freescale MPC52xx Programmable Serial + Controller in master SPI mode. + config SPI_MPC83xx tristate "Freescale MPC83xx SPI controller" depends on SPI_MASTER && PPC_83xx && EXPERIMENTAL diff --git a/trunk/drivers/spi/Makefile b/trunk/drivers/spi/Makefile index 624b6363f490..5788d867de84 100644 --- a/trunk/drivers/spi/Makefile +++ b/trunk/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o +obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o diff --git a/trunk/drivers/spi/mpc52xx_psc_spi.c b/trunk/drivers/spi/mpc52xx_psc_spi.c new file mode 100644 index 000000000000..052359fc41ee --- /dev/null +++ b/trunk/drivers/spi/mpc52xx_psc_spi.c @@ -0,0 +1,654 @@ +/* + * MPC52xx SPC in SPI mode driver. + * + * Maintainer: Dragos Carp + * + * Copyright (C) 2006 TOPTICA Photonics AG. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +#if defined(CONFIG_PPC_MERGE) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MCLK 20000000 /* PSC port MClk in hz */ + +struct mpc52xx_psc_spi { + /* fsl_spi_platform data */ + void (*activate_cs)(u8, u8); + void (*deactivate_cs)(u8, u8); + u32 sysclk; + + /* driver internal data */ + struct mpc52xx_psc __iomem *psc; + unsigned int irq; + u8 bits_per_word; + u8 busy; + + struct workqueue_struct *workqueue; + struct work_struct work; + + struct list_head queue; + spinlock_t lock; + + struct completion done; +}; + +/* controller state */ +struct mpc52xx_psc_spi_cs { + int bits_per_word; + int speed_hz; +}; + +/* set clock freq, clock ramp, bits per work + * if t is NULL then reset the values to the default values + */ +static int mpc52xx_psc_spi_transfer_setup(struct spi_device *spi, + struct spi_transfer *t) +{ + struct mpc52xx_psc_spi_cs *cs = spi->controller_state; + + cs->speed_hz = (t && t->speed_hz) + ? t->speed_hz : spi->max_speed_hz; + cs->bits_per_word = (t && t->bits_per_word) + ? t->bits_per_word : spi->bits_per_word; + cs->bits_per_word = ((cs->bits_per_word + 7) / 8) * 8; + return 0; +} + +static void mpc52xx_psc_spi_activate_cs(struct spi_device *spi) +{ + struct mpc52xx_psc_spi_cs *cs = spi->controller_state; + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + struct mpc52xx_psc __iomem *psc = mps->psc; + u32 sicr; + u16 ccr; + + sicr = in_be32(&psc->sicr); + + /* Set clock phase and polarity */ + if (spi->mode & SPI_CPHA) + sicr |= 0x00001000; + else + sicr &= ~0x00001000; + if (spi->mode & SPI_CPOL) + sicr |= 0x00002000; + else + sicr &= ~0x00002000; + + if (spi->mode & SPI_LSB_FIRST) + sicr |= 0x10000000; + else + sicr &= ~0x10000000; + out_be32(&psc->sicr, sicr); + + /* Set clock frequency and bits per word + * Because psc->ccr is defined as 16bit register instead of 32bit + * just set the lower byte of BitClkDiv + */ + ccr = in_be16(&psc->ccr); + ccr &= 0xFF00; + if (cs->speed_hz) + ccr |= (MCLK / cs->speed_hz - 1) & 0xFF; + else /* by default SPI Clk 1MHz */ + ccr |= (MCLK / 1000000 - 1) & 0xFF; + out_be16(&psc->ccr, ccr); + mps->bits_per_word = cs->bits_per_word; + + if (mps->activate_cs) + mps->activate_cs(spi->chip_select, + (spi->mode & SPI_CS_HIGH) ? 1 : 0); +} + +static void mpc52xx_psc_spi_deactivate_cs(struct spi_device *spi) +{ + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + + if (mps->deactivate_cs) + mps->deactivate_cs(spi->chip_select, + (spi->mode & SPI_CS_HIGH) ? 1 : 0); +} + +#define MPC52xx_PSC_BUFSIZE (MPC52xx_PSC_RFNUM_MASK + 1) +/* wake up when 80% fifo full */ +#define MPC52xx_PSC_RFALARM (MPC52xx_PSC_BUFSIZE * 20 / 100) + +static int mpc52xx_psc_spi_transfer_rxtx(struct spi_device *spi, + struct spi_transfer *t) +{ + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + struct mpc52xx_psc __iomem *psc = mps->psc; + unsigned rb = 0; /* number of bytes receieved */ + unsigned sb = 0; /* number of bytes sent */ + unsigned char *rx_buf = (unsigned char *)t->rx_buf; + unsigned char *tx_buf = (unsigned char *)t->tx_buf; + unsigned rfalarm; + unsigned send_at_once = MPC52xx_PSC_BUFSIZE; + unsigned recv_at_once; + unsigned bpw = mps->bits_per_word / 8; + + if (!t->tx_buf && !t->rx_buf && t->len) + return -EINVAL; + + /* enable transmiter/receiver */ + out_8(&psc->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); + while (rb < t->len) { + if (t->len - rb > MPC52xx_PSC_BUFSIZE) { + rfalarm = MPC52xx_PSC_RFALARM; + } else { + send_at_once = t->len - sb; + rfalarm = MPC52xx_PSC_BUFSIZE - (t->len - rb); + } + + dev_dbg(&spi->dev, "send %d bytes...\n", send_at_once); + if (tx_buf) { + for (; send_at_once; sb++, send_at_once--) { + /* set EOF flag */ + if (mps->bits_per_word + && (sb + 1) % bpw == 0) + out_8(&psc->ircr2, 0x01); + out_8(&psc->mpc52xx_psc_buffer_8, tx_buf[sb]); + } + } else { + for (; send_at_once; sb++, send_at_once--) { + /* set EOF flag */ + if (mps->bits_per_word + && ((sb + 1) % bpw) == 0) + out_8(&psc->ircr2, 0x01); + out_8(&psc->mpc52xx_psc_buffer_8, 0); + } + } + + + /* enable interupts and wait for wake up + * if just one byte is expected the Rx FIFO genererates no + * FFULL interrupt, so activate the RxRDY interrupt + */ + out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); + if (t->len - rb == 1) { + out_8(&psc->mode, 0); + } else { + out_8(&psc->mode, MPC52xx_PSC_MODE_FFULL); + out_be16(&psc->rfalarm, rfalarm); + } + out_be16(&psc->mpc52xx_psc_imr, MPC52xx_PSC_IMR_RXRDY); + wait_for_completion(&mps->done); + recv_at_once = in_be16(&psc->rfnum); + dev_dbg(&spi->dev, "%d bytes received\n", recv_at_once); + + send_at_once = recv_at_once; + if (rx_buf) { + for (; recv_at_once; rb++, recv_at_once--) + rx_buf[rb] = in_8(&psc->mpc52xx_psc_buffer_8); + } else { + for (; recv_at_once; rb++, recv_at_once--) + in_8(&psc->mpc52xx_psc_buffer_8); + } + } + /* disable transmiter/receiver */ + out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); + + return 0; +} + +static void mpc52xx_psc_spi_work(struct work_struct *work) +{ + struct mpc52xx_psc_spi *mps = + container_of(work, struct mpc52xx_psc_spi, work); + + spin_lock_irq(&mps->lock); + mps->busy = 1; + while (!list_empty(&mps->queue)) { + struct spi_message *m; + struct spi_device *spi; + struct spi_transfer *t = NULL; + unsigned cs_change; + int status; + + m = container_of(mps->queue.next, struct spi_message, queue); + list_del_init(&m->queue); + spin_unlock_irq(&mps->lock); + + spi = m->spi; + cs_change = 1; + status = 0; + list_for_each_entry (t, &m->transfers, transfer_list) { + if (t->bits_per_word || t->speed_hz) { + status = mpc52xx_psc_spi_transfer_setup(spi, t); + if (status < 0) + break; + } + + if (cs_change) + mpc52xx_psc_spi_activate_cs(spi); + cs_change = t->cs_change; + + status = mpc52xx_psc_spi_transfer_rxtx(spi, t); + if (status) + break; + m->actual_length += t->len; + + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (cs_change) + mpc52xx_psc_spi_deactivate_cs(spi); + } + + m->status = status; + m->complete(m->context); + + if (status || !cs_change) + mpc52xx_psc_spi_deactivate_cs(spi); + + mpc52xx_psc_spi_transfer_setup(spi, NULL); + + spin_lock_irq(&mps->lock); + } + mps->busy = 0; + spin_unlock_irq(&mps->lock); +} + +static int mpc52xx_psc_spi_setup(struct spi_device *spi) +{ + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + struct mpc52xx_psc_spi_cs *cs = spi->controller_state; + unsigned long flags; + + if (spi->bits_per_word%8) + return -EINVAL; + + if (!cs) { + cs = kzalloc(sizeof *cs, GFP_KERNEL); + if (!cs) + return -ENOMEM; + spi->controller_state = cs; + } + + cs->bits_per_word = spi->bits_per_word; + cs->speed_hz = spi->max_speed_hz; + + spin_lock_irqsave(&mps->lock, flags); + if (!mps->busy) + mpc52xx_psc_spi_deactivate_cs(spi); + spin_unlock_irqrestore(&mps->lock, flags); + + return 0; +} + +static int mpc52xx_psc_spi_transfer(struct spi_device *spi, + struct spi_message *m) +{ + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + unsigned long flags; + + m->actual_length = 0; + m->status = -EINPROGRESS; + + spin_lock_irqsave(&mps->lock, flags); + list_add_tail(&m->queue, &mps->queue); + queue_work(mps->workqueue, &mps->work); + spin_unlock_irqrestore(&mps->lock, flags); + + return 0; +} + +static void mpc52xx_psc_spi_cleanup(struct spi_device *spi) +{ + kfree(spi->controller_state); +} + +static int mpc52xx_psc_spi_port_config(int psc_id, struct mpc52xx_psc_spi *mps) +{ + struct mpc52xx_cdm __iomem *cdm; + struct mpc52xx_gpio __iomem *gpio; + struct mpc52xx_psc __iomem *psc = mps->psc; + u32 ul; + u32 mclken_div; + int ret = 0; + +#if defined(CONFIG_PPC_MERGE) + cdm = mpc52xx_find_and_map("mpc52xx-cdm"); + gpio = mpc52xx_find_and_map("mpc52xx-gpio"); +#else + cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE); + gpio = ioremap(MPC52xx_PA(MPC52xx_GPIO_OFFSET), MPC52xx_GPIO_SIZE); +#endif + if (!cdm || !gpio) { + printk(KERN_ERR "Error mapping CDM/GPIO\n"); + ret = -EFAULT; + goto unmap_regs; + } + + /* default sysclk is 512MHz */ + mclken_div = 0x8000 | + (((mps->sysclk ? mps->sysclk : 512000000) / MCLK) & 0x1FF); + + switch (psc_id) { + case 1: + ul = in_be32(&gpio->port_config); + ul &= 0xFFFFFFF8; + ul |= 0x00000006; + out_be32(&gpio->port_config, ul); + out_be16(&cdm->mclken_div_psc1, mclken_div); + ul = in_be32(&cdm->clk_enables); + ul |= 0x00000020; + out_be32(&cdm->clk_enables, ul); + break; + case 2: + ul = in_be32(&gpio->port_config); + ul &= 0xFFFFFF8F; + ul |= 0x00000060; + out_be32(&gpio->port_config, ul); + out_be16(&cdm->mclken_div_psc2, mclken_div); + ul = in_be32(&cdm->clk_enables); + ul |= 0x00000040; + out_be32(&cdm->clk_enables, ul); + break; + case 3: + ul = in_be32(&gpio->port_config); + ul &= 0xFFFFF0FF; + ul |= 0x00000600; + out_be32(&gpio->port_config, ul); + out_be16(&cdm->mclken_div_psc3, mclken_div); + ul = in_be32(&cdm->clk_enables); + ul |= 0x00000080; + out_be32(&cdm->clk_enables, ul); + break; + case 6: + ul = in_be32(&gpio->port_config); + ul &= 0xFF8FFFFF; + ul |= 0x00700000; + out_be32(&gpio->port_config, ul); + out_be16(&cdm->mclken_div_psc6, mclken_div); + ul = in_be32(&cdm->clk_enables); + ul |= 0x00000010; + out_be32(&cdm->clk_enables, ul); + break; + default: + ret = -EINVAL; + goto unmap_regs; + } + + /* Reset the PSC into a known state */ + out_8(&psc->command, MPC52xx_PSC_RST_RX); + out_8(&psc->command, MPC52xx_PSC_RST_TX); + out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); + + /* Disable interrupts, interrupts are based on alarm level */ + out_be16(&psc->mpc52xx_psc_imr, 0); + out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); + out_8(&psc->rfcntl, 0); + out_8(&psc->mode, MPC52xx_PSC_MODE_FFULL); + + /* Configure 8bit codec mode as a SPI master and use EOF flags */ + /* SICR_SIM_CODEC8|SICR_GENCLK|SICR_SPI|SICR_MSTR|SICR_USEEOF */ + out_be32(&psc->sicr, 0x0180C800); + out_be16(&psc->ccr, 0x070F); /* by default SPI Clk 1MHz */ + + /* Set 2ms DTL delay */ + out_8(&psc->ctur, 0x00); + out_8(&psc->ctlr, 0x84); + + mps->bits_per_word = 8; + +unmap_regs: + if (cdm) + iounmap(cdm); + if (gpio) + iounmap(gpio); + + return ret; +} + +static irqreturn_t mpc52xx_psc_spi_isr(int irq, void *dev_id) +{ + struct mpc52xx_psc_spi *mps = (struct mpc52xx_psc_spi *)dev_id; + struct mpc52xx_psc __iomem *psc = mps->psc; + + /* disable interrupt and wake up the work queue */ + if (in_be16(&psc->mpc52xx_psc_isr) & MPC52xx_PSC_IMR_RXRDY) { + out_be16(&psc->mpc52xx_psc_imr, 0); + complete(&mps->done); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +/* bus_num is used only for the case dev->platform_data == NULL */ +static int __init mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr, + u32 size, unsigned int irq, s16 bus_num) +{ + struct fsl_spi_platform_data *pdata = dev->platform_data; + struct mpc52xx_psc_spi *mps; + struct spi_master *master; + int ret; + + if (pdata == NULL) + return -ENODEV; + + master = spi_alloc_master(dev, sizeof *mps); + if (master == NULL) + return -ENOMEM; + + dev_set_drvdata(dev, master); + mps = spi_master_get_devdata(master); + + mps->irq = irq; + if (pdata == NULL) { + dev_warn(dev, "probe called without platform data, no " + "(de)activate_cs function will be called\n"); + mps->activate_cs = NULL; + mps->deactivate_cs = NULL; + mps->sysclk = 0; + master->bus_num = bus_num; + master->num_chipselect = 255; + } else { + mps->activate_cs = pdata->activate_cs; + mps->deactivate_cs = pdata->deactivate_cs; + mps->sysclk = pdata->sysclk; + master->bus_num = pdata->bus_num; + master->num_chipselect = pdata->max_chipselect; + } + master->setup = mpc52xx_psc_spi_setup; + master->transfer = mpc52xx_psc_spi_transfer; + master->cleanup = mpc52xx_psc_spi_cleanup; + + mps->psc = ioremap(regaddr, size); + if (!mps->psc) { + dev_err(dev, "could not ioremap I/O port range\n"); + ret = -EFAULT; + goto free_master; + } + + ret = request_irq(mps->irq, mpc52xx_psc_spi_isr, 0, "mpc52xx-psc-spi", + mps); + if (ret) + goto free_master; + + ret = mpc52xx_psc_spi_port_config(master->bus_num, mps); + if (ret < 0) + goto free_irq; + + spin_lock_init(&mps->lock); + init_completion(&mps->done); + INIT_WORK(&mps->work, mpc52xx_psc_spi_work); + INIT_LIST_HEAD(&mps->queue); + + mps->workqueue = create_singlethread_workqueue( + master->cdev.dev->bus_id); + if (mps->workqueue == NULL) { + ret = -EBUSY; + goto free_irq; + } + + ret = spi_register_master(master); + if (ret < 0) + goto unreg_master; + + return ret; + +unreg_master: + destroy_workqueue(mps->workqueue); +free_irq: + free_irq(mps->irq, mps); +free_master: + if (mps->psc) + iounmap(mps->psc); + spi_master_put(master); + + return ret; +} + +static int __exit mpc52xx_psc_spi_do_remove(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master); + + flush_workqueue(mps->workqueue); + destroy_workqueue(mps->workqueue); + spi_unregister_master(master); + free_irq(mps->irq, mps); + if (mps->psc) + iounmap(mps->psc); + + return 0; +} + +#if !defined(CONFIG_PPC_MERGE) +static int __init mpc52xx_psc_spi_probe(struct platform_device *dev) +{ + switch(dev->id) { + case 1: + case 2: + case 3: + case 6: + return mpc52xx_psc_spi_do_probe(&dev->dev, + MPC52xx_PA(MPC52xx_PSCx_OFFSET(dev->id)), + MPC52xx_PSC_SIZE, platform_get_irq(dev, 0), dev->id); + default: + return -EINVAL; + } +} + +static int __exit mpc52xx_psc_spi_remove(struct platform_device *dev) +{ + return mpc52xx_psc_spi_do_remove(&dev->dev); +} + +static struct platform_driver mpc52xx_psc_spi_platform_driver = { + .remove = __exit_p(mpc52xx_psc_spi_remove), + .driver = { + .name = "mpc52xx-psc-spi", + .owner = THIS_MODULE, + }, +}; + +static int __init mpc52xx_psc_spi_init(void) +{ + return platform_driver_probe(&mpc52xx_psc_spi_platform_driver, + mpc52xx_psc_spi_probe); +} +module_init(mpc52xx_psc_spi_init); + +static void __exit mpc52xx_psc_spi_exit(void) +{ + platform_driver_unregister(&mpc52xx_psc_spi_platform_driver); +} +module_exit(mpc52xx_psc_spi_exit); + +#else /* defined(CONFIG_PPC_MERGE) */ + +static int __init mpc52xx_psc_spi_of_probe(struct of_device *op, + const struct of_device_id *match) +{ + const u32 *regaddr_p; + u64 regaddr64, size64; + s16 id = -1; + + regaddr_p = of_get_address(op->node, 0, &size64, NULL); + if (!regaddr_p) { + printk(KERN_ERR "Invalid PSC address\n"); + return -EINVAL; + } + regaddr64 = of_translate_address(op->node, regaddr_p); + + if (op->dev.platform_data == NULL) { + struct device_node *np; + int i = 0; + + for_each_node_by_type(np, "spi") { + if (of_find_device_by_node(np) == op) { + id = i; + break; + } + i++; + } + } + + return mpc52xx_psc_spi_do_probe(&op->dev, (u32)regaddr64, (u32)size64, + irq_of_parse_and_map(op->node, 0), id); +} + +static int __exit mpc52xx_psc_spi_of_remove(struct of_device *op) +{ + return mpc52xx_psc_spi_do_remove(&op->dev); +} + +static struct of_device_id mpc52xx_psc_spi_of_match[] = { + { .type = "spi", .compatible = "mpc52xx-psc-spi", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, mpc52xx_psc_spi_of_match); + +static struct of_platform_driver mpc52xx_psc_spi_of_driver = { + .owner = THIS_MODULE, + .name = "mpc52xx-psc-spi", + .match_table = mpc52xx_psc_spi_of_match, + .probe = mpc52xx_psc_spi_of_probe, + .remove = __exit_p(mpc52xx_psc_spi_of_remove), + .driver = { + .name = "mpc52xx-psc-spi", + .owner = THIS_MODULE, + }, +}; + +static int __init mpc52xx_psc_spi_init(void) +{ + return of_register_platform_driver(&mpc52xx_psc_spi_of_driver); +} +module_init(mpc52xx_psc_spi_init); + +static void __exit mpc52xx_psc_spi_exit(void) +{ + of_unregister_platform_driver(&mpc52xx_psc_spi_of_driver); +} +module_exit(mpc52xx_psc_spi_exit); + +#endif /* defined(CONFIG_PPC_MERGE) */ + +MODULE_AUTHOR("Dragos Carp"); +MODULE_DESCRIPTION("MPC52xx PSC SPI Driver"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/video/Kconfig b/trunk/drivers/video/Kconfig index f54438828cb9..eebcb708cff1 100644 --- a/trunk/drivers/video/Kconfig +++ b/trunk/drivers/video/Kconfig @@ -748,6 +748,22 @@ config FB_S1D13XXX working with S1D13806). Product specs at +config FB_ATMEL + tristate "AT91/AT32 LCD Controller support" + depends on FB && (ARCH_AT91SAM9261 || ARCH_AT91SAM9263 || AVR32) + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This enables support for the AT91/AT32 LCD Controller. + +config FB_INTSRAM + bool "Frame Buffer in internal SRAM" + depends on FB_ATMEL && ARCH_AT91SAM9261 + help + Say Y if you want to map Frame Buffer in internal SRAM. Say N if you want + to let frame buffer in external SDRAM. + config FB_NVIDIA tristate "nVidia Framebuffer Support" depends on FB && PCI @@ -780,6 +796,15 @@ config FB_NVIDIA_I2C independently validate video mode parameters, you should say Y here. +config FB_NVIDIA_DEBUG + bool "Lots of debug output" + depends on FB_NVIDIA + default n + help + Say Y here if you want the nVidia driver to output all sorts + of debugging information to provide to the maintainer when + something goes wrong. + config FB_NVIDIA_BACKLIGHT bool "Support for backlight control" depends on FB_NVIDIA @@ -819,7 +844,7 @@ config FB_RIVA_I2C here. config FB_RIVA_DEBUG - bool "Lots of debug output from Riva(nVidia) driver" + bool "Lots of debug output" depends on FB_RIVA default n help @@ -1431,8 +1456,11 @@ config FB_ARK and ICS 5342 RAMDAC. config FB_PM3 - tristate "Permedia3 support" - depends on FB && PCI && BROKEN + tristate "Permedia3 support (EXPERIMENTAL)" + depends on FB && PCI && EXPERIMENTAL + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT help This is the frame buffer device driver for the 3DLabs Permedia3 chipset, used in Formac ProFormance III, 3DLabs Oxygen VX1 & diff --git a/trunk/drivers/video/Makefile b/trunk/drivers/video/Makefile index 0b70567458fb..bd8b05229500 100644 --- a/trunk/drivers/video/Makefile +++ b/trunk/drivers/video/Makefile @@ -87,6 +87,7 @@ obj-$(CONFIG_FB_G364) += g364fb.o obj-$(CONFIG_FB_SA1100) += sa1100fb.o obj-$(CONFIG_FB_HIT) += hitfb.o obj-$(CONFIG_FB_EPSON1355) += epson1355fb.o +obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o diff --git a/trunk/drivers/video/atmel_lcdfb.c b/trunk/drivers/video/atmel_lcdfb.c new file mode 100644 index 000000000000..e1d5bd0c98c4 --- /dev/null +++ b/trunk/drivers/video/atmel_lcdfb.c @@ -0,0 +1,752 @@ +/* + * Driver for AT91/AT32 LCD Controller + * + * Copyright (C) 2007 Atmel Corporation + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include