From 478add0ea718dee38702c066907a510525964b48 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Tue, 7 Mar 2000 16:02:18 +0000 Subject: Rest of the merge. --- Documentation/usb/rio.txt | 138 ++ arch/alpha/kernel/irq_alpha.c | 248 +++ arch/alpha/kernel/irq_i8259.c | 187 +++ arch/alpha/kernel/irq_smp.c | 250 +++ arch/alpha/kernel/irq_srm.c | 82 + arch/arm/mm/mm-clps7500.c | 28 + drivers/block/ide-cs.c | 481 ++++++ drivers/char/digi1.h | 100 ++ drivers/char/digiFep1.h | 136 ++ drivers/char/digiPCI.h | 42 + drivers/char/epca.h | 170 +++ drivers/char/epcaconfig.h | 7 + drivers/char/tuner-3036.c | 227 +++ drivers/net/bonding.c | 350 +++++ drivers/usb/pegasus.c | 579 +++++++ drivers/usb/rio500.c | 455 ++++++ drivers/usb/rio500_usb.h | 37 + drivers/usb/wacom.c | 286 ++++ drivers/video/riva/Makefile | 14 + drivers/video/riva/fbdev.c | 1705 +++++++++++++++++++++ drivers/video/riva/nv4ref.h | 2445 ++++++++++++++++++++++++++++++ drivers/video/riva/nvreg.h | 188 +++ drivers/video/riva/riva_hw.c | 1426 +++++++++++++++++ drivers/video/riva/riva_hw.h | 343 +++++ drivers/video/riva/riva_tbl.h | 402 +++++ include/asm-arm/arch-nexuspci/ide.h | 36 + include/asm-arm/arch-nexuspci/keyboard.h | 29 + include/linux/brlock.h | 207 +++ include/linux/if_bonding.h | 33 + include/linux/netfilter_ipv6.h | 57 + lib/brlock.c | 65 + 31 files changed, 10753 insertions(+) create mode 100644 Documentation/usb/rio.txt create mode 100644 arch/alpha/kernel/irq_alpha.c create mode 100644 arch/alpha/kernel/irq_i8259.c create mode 100644 arch/alpha/kernel/irq_smp.c create mode 100644 arch/alpha/kernel/irq_srm.c create mode 100644 arch/arm/mm/mm-clps7500.c create mode 100644 drivers/block/ide-cs.c create mode 100644 drivers/char/digi1.h create mode 100644 drivers/char/digiFep1.h create mode 100644 drivers/char/digiPCI.h create mode 100644 drivers/char/epca.h create mode 100644 drivers/char/epcaconfig.h create mode 100644 drivers/char/tuner-3036.c create mode 100644 drivers/net/bonding.c create mode 100644 drivers/usb/pegasus.c create mode 100644 drivers/usb/rio500.c create mode 100644 drivers/usb/rio500_usb.h create mode 100644 drivers/usb/wacom.c create mode 100644 drivers/video/riva/Makefile create mode 100644 drivers/video/riva/fbdev.c create mode 100644 drivers/video/riva/nv4ref.h create mode 100644 drivers/video/riva/nvreg.h create mode 100644 drivers/video/riva/riva_hw.c create mode 100644 drivers/video/riva/riva_hw.h create mode 100644 drivers/video/riva/riva_tbl.h create mode 100644 include/asm-arm/arch-nexuspci/ide.h create mode 100644 include/asm-arm/arch-nexuspci/keyboard.h create mode 100644 include/linux/brlock.h create mode 100644 include/linux/if_bonding.h create mode 100644 include/linux/netfilter_ipv6.h create mode 100644 lib/brlock.c diff --git a/Documentation/usb/rio.txt b/Documentation/usb/rio.txt new file mode 100644 index 000000000..0aa79ab00 --- /dev/null +++ b/Documentation/usb/rio.txt @@ -0,0 +1,138 @@ +Copyright (C) 1999, 2000 Bruce Tenison +Portions Copyright (C) 1999, 2000 David Nelson +Thanks to David Nelson for guidance and the usage of the scanner.txt +and scanner.c files to model our driver and this informative file. + +Mar. 2, 2000 + +CHANGES + +- Initial Revision + + +OVERVIEW + +This README will address issues regarding how to configure the kernel +to access a RIO 500 mp3 player. +Before I explain how to use this to access the Rio500 please be warned: + +W A R N I N G: +-------------- + +Please note that this software is still under development. The authors +are in no way responsible for any damage that may occur, no matter how +inconsequential. + +It seems that the Rio has a problem when sending .mp3 with low batteries. +I suggest when the batteries are low and want to transfer stuff that you +replace it with a fresh one. In my case, what happened is I lost two 16kb +blocks (they are no longer usable to store information to it). But I don't +know if thats normal or not. It could simply be a problem with the flash +memory. + +In an extreme case, I left my Rio playing overnight and the batteries wore +down to nothing and appear to have corrupted the flash memory. My RIO +needed to be replaced as a result. Diamond tech support is aware of the +problem. Do NOT allow your batteries to wear down to nothing before +changing them. It appears RIO 500 firmware does not handle low battery +power well at all. + +On systems with OHCI controllers, the kernel OHCI code appears to have +power on problems with some chipsets. If you are having problems +connecting to your RIO 500, try turning it on first and then plugging it +into the USB cable. + +Contact information: +-------------------- + + The main page for the project is hosted at sourceforge.net in the following + address: http://rio500.sourceforge.net You can also go to the sourceforge + project page at: http://sourceforge.net/project/?group_id=1944 There is + also a mailing list: rio500-users@lists.sourceforge.net + +Authors: +------- + +Most of the code was written by Cesar Miquel . Keith +Clayton is incharge of the PPC port and making sure +things work there. Bruce Tenison is adding support +for .fon files and also does testing. The program will mostly sure be +re-written and Pete Ikusz along with the rest will re-design it. I would +also like to thank Tri Nguyen who provided use +with some important information regarding the communication with the Rio. + +ADDITIONAL INFORMATION and Userspace tools + +http://rio500.sourceforge.net/ + + +REQUIREMENTS + +A host with a USB port. Ideally, either a UHCI (Intel) or OHCI +(Compaq and others) hardware port should work. + +A Linux development kernel (2.3.x) with USB support enabled or a +backported version to linux-2.2.x. See http://www.linux-usb.org for +more information on accomplishing this. + +A Linux kernel with RIO 500 support enabled. + +'lspci' which is only needed to determine the type of USB hardware +available in your machine. + +CONFIGURATION + +Using `lspci -v`, determine the type of USB hardware available. + + If you see something like: + + USB Controller: ...... + Flags: ..... + I/O ports at .... + + Then you have a UHCI based controller. + + If you see something like: + + USB Controller: ..... + Flags: .... + Memory at ..... + + Then you have a OHCI based controller. + +Using `make menuconfig` or your preferred method for configuring the +kernel, select 'Support for USB', 'OHCI/UHCI' depending on your +hardware (determined from the steps above), 'USB Diamond Rio500 support', and +'Preliminary USB device filesystem'. Compile and install the modules +(you may need to execute `depmod -a` to update the module +dependencies). + +Add a device for the USB rio500: + `mknod /dev/usb/rio500 c 180 64` + +Set appropriate permissions for /dev/usb/rio500 (don't forget about +group and world permissions). Both read and write permissions are +required for proper operation. + +Load the appropriate modules (if compiled as modules): + + OHCI: + modprobe usbcore + modprobe usb-ohci + modprobe rio500 + + UHCI: + modprobe usbcore + modprobe usb-uhci (or uhci) + modprobe rio500 + +That's it. The Rio500 Utils at: http://rio500.sourceforge.net should +be able to access the rio500. + +BUGS + +If you encounter any problems feel free to drop me an email. + +Bruce Tenison +btenison@dibbs.net + diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c new file mode 100644 index 000000000..62ba2362f --- /dev/null +++ b/arch/alpha/kernel/irq_alpha.c @@ -0,0 +1,248 @@ +/* + * Alpha specific irq code. + */ + +#include +#include +#include +#include + +#include +#include + +#include "proto.h" +#include "irq_impl.h" + +/* Only uniprocessor needs this IRQ/BH locking depth, on SMP it lives + in the per-cpu structure for cache reasons. */ +#ifndef CONFIG_SMP +int __local_irq_count; +int __local_bh_count; +unsigned long __irq_attempt[NR_IRQS]; +#endif + +/* Hack minimum IPL during interupt processing for broken hardware. */ +#ifdef CONFIG_ALPHA_BROKEN_IRQ_MASK +int __min_ipl; +#endif + +/* + * Performance counter hook. A module can override this to + * do something useful. + */ +static void +dummy_perf(unsigned long vector, struct pt_regs *regs) +{ + irq_err_count++; + printk(KERN_CRIT "Performance counter interrupt!\n"); +} + +void (*perf_irq)(unsigned long, struct pt_regs *) = dummy_perf; + +/* + * The main interrupt entry point. + */ + +asmlinkage void +do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + switch (type) { + case 0: +#ifdef CONFIG_SMP + handle_ipi(®s); + return; +#else + irq_err_count++; + printk(KERN_CRIT "Interprocessor interrupt? " + "You must be kidding!\n"); +#endif + break; + case 1: +#ifdef CONFIG_SMP + cpu_data[smp_processor_id()].smp_local_irq_count++; + smp_percpu_timer_interrupt(®s); + if (smp_processor_id() == boot_cpuid) +#endif + handle_irq(RTC_IRQ, ®s); + return; + case 2: + irq_err_count++; + alpha_mv.machine_check(vector, la_ptr, ®s); + return; + case 3: + alpha_mv.device_interrupt(vector, ®s); + return; + case 4: + perf_irq(vector, ®s); + return; + default: + printk(KERN_CRIT "Hardware intr %ld %lx? Huh?\n", + type, vector); + } + printk("PC = %016lx PS=%04lx\n", regs.pc, regs.ps); +} + +void __init +common_init_isa_dma(void) +{ + outb(0, DMA1_RESET_REG); + outb(0, DMA2_RESET_REG); + outb(0, DMA1_CLR_MASK_REG); + outb(0, DMA2_CLR_MASK_REG); +} + +void __init +init_IRQ(void) +{ + alpha_mv.init_irq(); + wrent(entInt, 0); +} + +/* + * machine error checks + */ +#define MCHK_K_TPERR 0x0080 +#define MCHK_K_TCPERR 0x0082 +#define MCHK_K_HERR 0x0084 +#define MCHK_K_ECC_C 0x0086 +#define MCHK_K_ECC_NC 0x0088 +#define MCHK_K_OS_BUGCHECK 0x008A +#define MCHK_K_PAL_BUGCHECK 0x0090 + +#ifndef CONFIG_SMP +struct mcheck_info __mcheck_info; +#endif + +void +process_mcheck_info(unsigned long vector, unsigned long la_ptr, + struct pt_regs *regs, const char *machine, + int expected) +{ + struct el_common *mchk_header; + const char *reason; + + /* + * See if the machine check is due to a badaddr() and if so, + * ignore it. + */ + +#if DEBUG_MCHECK > 0 + printk(KERN_CRIT "%s machine check %s\n", machine, + expected ? "expected." : "NOT expected!!!"); +#endif + + if (expected) { + int cpu = smp_processor_id(); + mcheck_expected(cpu) = 0; + mcheck_taken(cpu) = 1; + return; + } + + mchk_header = (struct el_common *)la_ptr; + + printk(KERN_CRIT "%s machine check: vector=0x%lx pc=0x%lx code=0x%lx\n", + machine, vector, regs->pc, mchk_header->code); + + switch ((unsigned int) mchk_header->code) { + /* Machine check reasons. Defined according to PALcode sources. */ + case 0x80: reason = "tag parity error"; break; + case 0x82: reason = "tag control parity error"; break; + case 0x84: reason = "generic hard error"; break; + case 0x86: reason = "correctable ECC error"; break; + case 0x88: reason = "uncorrectable ECC error"; break; + case 0x8A: reason = "OS-specific PAL bugcheck"; break; + case 0x90: reason = "callsys in kernel mode"; break; + case 0x96: reason = "i-cache read retryable error"; break; + case 0x98: reason = "processor detected hard error"; break; + + /* System specific (these are for Alcor, at least): */ + case 0x202: reason = "system detected hard error"; break; + case 0x203: reason = "system detected uncorrectable ECC error"; break; + case 0x204: reason = "SIO SERR occurred on PCI bus"; break; + case 0x205: reason = "parity error detected by CIA"; break; + case 0x206: reason = "SIO IOCHK occurred on ISA bus"; break; + case 0x207: reason = "non-existent memory error"; break; + case 0x208: reason = "MCHK_K_DCSR"; break; + case 0x209: reason = "PCI SERR detected"; break; + case 0x20b: reason = "PCI data parity error detected"; break; + case 0x20d: reason = "PCI address parity error detected"; break; + case 0x20f: reason = "PCI master abort error"; break; + case 0x211: reason = "PCI target abort error"; break; + case 0x213: reason = "scatter/gather PTE invalid error"; break; + case 0x215: reason = "flash ROM write error"; break; + case 0x217: reason = "IOA timeout detected"; break; + case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break; + case 0x21b: reason = "EISA fail-safe timer timeout"; break; + case 0x21d: reason = "EISA bus time-out"; break; + case 0x21f: reason = "EISA software generated NMI"; break; + case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break; + default: reason = "unknown"; break; + } + + printk(KERN_CRIT "machine check type: %s%s\n", + reason, mchk_header->retry ? " (retryable)" : ""); + + dik_show_regs(regs, NULL); + +#if DEBUG_MCHECK > 1 + { + /* Dump the logout area to give all info. */ + unsigned long *ptr = (unsigned long *)la_ptr; + long i; + for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { + printk(KERN_CRIT " +%8lx %016lx %016lx\n", + i*sizeof(long), ptr[i], ptr[i+1]); + } + } +#endif +} + +/* + * The special RTC interrupt type. The interrupt itself was + * processed by PALcode, and comes in via entInt vector 1. + */ + +static void rtc_enable_disable(unsigned int irq) { } +static unsigned int rtc_startup(unsigned int irq) { return 0; } + +struct irqaction timer_irqaction = { + handler: timer_interrupt, + flags: SA_INTERRUPT, + name: "timer", +}; + +static struct hw_interrupt_type rtc_irq_type = { + typename: "RTC", + startup: rtc_startup, + shutdown: rtc_enable_disable, + enable: rtc_enable_disable, + disable: rtc_enable_disable, + ack: rtc_enable_disable, + end: rtc_enable_disable, +}; + +void __init +init_rtc_irq(void) +{ + irq_desc[RTC_IRQ].status = IRQ_DISABLED; + irq_desc[RTC_IRQ].handler = &rtc_irq_type; + setup_irq(RTC_IRQ, &timer_irqaction); +} + +/* Dummy irqactions. */ +struct irqaction isa_cascade_irqaction = { + handler: no_action, + name: "isa-cascade" +}; + +struct irqaction timer_cascade_irqaction = { + handler: no_action, + name: "timer-cascade" +}; + +struct irqaction halt_switch_irqaction = { + handler: no_action, + name: "halt-switch" +}; diff --git a/arch/alpha/kernel/irq_i8259.c b/arch/alpha/kernel/irq_i8259.c new file mode 100644 index 000000000..a47178050 --- /dev/null +++ b/arch/alpha/kernel/irq_i8259.c @@ -0,0 +1,187 @@ +/* + * linux/arch/alpha/kernel/irq_i8259.c + * + * This is the 'legacy' 8259A Programmable Interrupt Controller, + * present in the majority of PC/AT boxes. + * + * Started hacking from linux-2.3.30pre6/arch/i386/kernel/i8259.c. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "proto.h" +#include "irq_impl.h" + + +/* Note mask bit is true for DISABLED irqs. */ +static unsigned int cached_irq_mask = 0xffff; +spinlock_t i8259_irq_lock = SPIN_LOCK_UNLOCKED; + +static inline void +i8259_update_irq_hw(unsigned int irq, unsigned long mask) +{ + int port = 0x21; + if (irq & 8) mask >>= 8; + if (irq & 8) port = 0xA1; + outb(mask, port); +} + +inline void +i8259a_enable_irq(unsigned int irq) +{ + spin_lock(&i8259_irq_lock); + i8259_update_irq_hw(irq, cached_irq_mask &= ~(1 << irq)); + spin_unlock(&i8259_irq_lock); +} + +static inline void +__i8259a_disable_irq(unsigned int irq) +{ + i8259_update_irq_hw(irq, cached_irq_mask |= 1 << irq); +} + +void +i8259a_disable_irq(unsigned int irq) +{ + spin_lock(&i8259_irq_lock); + __i8259a_disable_irq(irq); + spin_unlock(&i8259_irq_lock); +} + +void +i8259a_mask_and_ack_irq(unsigned int irq) +{ + spin_lock(&i8259_irq_lock); + __i8259a_disable_irq(irq); + + /* Ack the interrupt making it the lowest priority. */ + if (irq >= 8) { + outb(0xE0 | (irq - 8), 0xa0); /* ack the slave */ + irq = 2; + } + outb(0xE0 | irq, 0x20); /* ack the master */ + spin_unlock(&i8259_irq_lock); +} + +unsigned int +i8259a_startup_irq(unsigned int irq) +{ + i8259a_enable_irq(irq); + return 0; /* never anything pending */ +} + +void +i8259a_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + i8259a_enable_irq(irq); +} + +struct hw_interrupt_type i8259a_irq_type = { + typename: "XT-PIC", + startup: i8259a_startup_irq, + shutdown: i8259a_disable_irq, + enable: i8259a_enable_irq, + disable: i8259a_disable_irq, + ack: i8259a_mask_and_ack_irq, + end: i8259a_end_irq, +}; + +void __init +init_i8259a_irqs(void) +{ + static struct irqaction cascade = { + handler: no_action, + name: "cascade", + }; + + long i; + + outb(0xff, 0x21); /* mask all of 8259A-1 */ + outb(0xff, 0xA1); /* mask all of 8259A-2 */ + + for (i = 0; i < 16; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].handler = &i8259a_irq_type; + } + + setup_irq(2, &cascade); +} + + +#if defined(CONFIG_ALPHA_GENERIC) +# define IACK_SC alpha_mv.iack_sc +#elif defined(CONFIG_ALPHA_APECS) +# define IACK_SC APECS_IACK_SC +#elif defined(CONFIG_ALPHA_LCA) +# define IACK_SC LCA_IACK_SC +#elif defined(CONFIG_ALPHA_CIA) +# define IACK_SC CIA_IACK_SC +#elif defined(CONFIG_ALPHA_PYXIS) +# define IACK_SC PYXIS_IACK_SC +#elif defined(CONFIG_ALPHA_TSUNAMI) +# define IACK_SC TSUNAMI_IACK_SC +#elif defined(CONFIG_ALPHA_POLARIS) +# define IACK_SC POLARIS_IACK_SC +#elif defined(CONFIG_ALPHA_IRONGATE) +# define IACK_SC IRONGATE_IACK_SC +#endif + +#if defined(IACK_SC) +void +isa_device_interrupt(unsigned long vector, struct pt_regs *regs) +{ + /* + * Generate a PCI interrupt acknowledge cycle. The PIC will + * respond with the interrupt vector of the highest priority + * interrupt that is pending. The PALcode sets up the + * interrupts vectors such that irq level L generates vector L. + */ + int j = *(vuip) IACK_SC; + j &= 0xff; + if (j == 7) { + if (!(inb(0x20) & 0x80)) { + /* It's only a passive release... */ + return; + } + } + handle_irq(j, regs); +} +#endif + +#if defined(CONFIG_ALPHA_GENERIC) || !defined(IACK_SC) +void +isa_no_iack_sc_device_interrupt(unsigned long vector, struct pt_regs *regs) +{ + unsigned long pic; + + /* + * It seems to me that the probability of two or more *device* + * interrupts occurring at almost exactly the same time is + * pretty low. So why pay the price of checking for + * additional interrupts here if the common case can be + * handled so much easier? + */ + /* + * The first read of gives you *all* interrupting lines. + * Therefore, read the mask register and and out those lines + * not enabled. Note that some documentation has 21 and a1 + * write only. This is not true. + */ + pic = inb(0x20) | (inb(0xA0) << 8); /* read isr */ + pic &= 0xFFFB; /* mask out cascade & hibits */ + + while (pic) { + int j = ffz(~pic); + pic &= pic - 1; + handle_irq(j, regs); + } +} +#endif diff --git a/arch/alpha/kernel/irq_smp.c b/arch/alpha/kernel/irq_smp.c new file mode 100644 index 000000000..90a3c30b2 --- /dev/null +++ b/arch/alpha/kernel/irq_smp.c @@ -0,0 +1,250 @@ +/* + * linux/arch/alpha/kernel/irq_smp.c + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* Who has global_irq_lock. */ +int global_irq_holder = NO_PROC_ID; + +/* This protects IRQ's. */ +spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED; + +/* Global IRQ locking depth. */ +static void *previous_irqholder = NULL; + +#define MAXCOUNT 100000000 + + +static void +show(char * str, void *where) +{ +#if 0 + int i; + unsigned long *stack; +#endif + int cpu = smp_processor_id(); + + printk("\n%s, CPU %d: %p\n", str, cpu, where); + printk("irq: %d [%d %d]\n", + irqs_running(), + cpu_data[0].irq_count, + cpu_data[1].irq_count); + + printk("bh: %d [%d %d]\n", + spin_is_locked(&global_bh_lock) ? 1 : 0, + cpu_data[0].bh_count, + cpu_data[1].bh_count); +#if 0 + stack = (unsigned long *) &str; + for (i = 40; i ; i--) { + unsigned long x = *++stack; + if (x > (unsigned long) &init_task_union && + x < (unsigned long) &vsprintf) { + printk("<[%08lx]> ", x); + } + } +#endif +} + +static inline void +wait_on_irq(int cpu, void *where) +{ + int count = MAXCOUNT; + + for (;;) { + + /* + * Wait until all interrupts are gone. Wait + * for bottom half handlers unless we're + * already executing in one.. + */ + if (!irqs_running()) { + if (local_bh_count(cpu) + || !spin_is_locked(&global_bh_lock)) + break; + } + + /* Duh, we have to loop. Release the lock to avoid deadlocks */ + spin_unlock(&global_irq_lock); + + for (;;) { + if (!--count) { + show("wait_on_irq", where); + count = MAXCOUNT; + } + __sti(); + udelay(1); /* make sure to run pending irqs */ + __cli(); + + if (irqs_running()) + continue; + if (spin_is_locked(&global_irq_lock)) + continue; + if (!local_bh_count(cpu) + && spin_is_locked(&global_bh_lock)) + continue; + if (spin_trylock(&global_irq_lock)) + break; + } + } +} + +static inline void +get_irqlock(int cpu, void* where) +{ + if (!spin_trylock(&global_irq_lock)) { + /* Do we already hold the lock? */ + if (cpu == global_irq_holder) + return; + /* Uhhuh.. Somebody else got it. Wait. */ + spin_lock(&global_irq_lock); + } + + /* + * Ok, we got the lock bit. + * But that's actually just the easy part.. Now + * we need to make sure that nobody else is running + * in an interrupt context. + */ + wait_on_irq(cpu, where); + + /* + * Finally. + */ +#if DEBUG_SPINLOCK + global_irq_lock.task = current; + global_irq_lock.previous = where; +#endif + global_irq_holder = cpu; + previous_irqholder = where; +} + +void +__global_cli(void) +{ + int cpu = smp_processor_id(); + void *where = __builtin_return_address(0); + + /* + * Maximize ipl. If ipl was previously 0 and if this thread + * is not in an irq, then take global_irq_lock. + */ + if (swpipl(IPL_MAX) == IPL_MIN && !local_irq_count(cpu)) + get_irqlock(cpu, where); +} + +void +__global_sti(void) +{ + int cpu = smp_processor_id(); + + if (!local_irq_count(cpu)) + release_irqlock(cpu); + __sti(); +} + +/* + * SMP flags value to restore to: + * 0 - global cli + * 1 - global sti + * 2 - local cli + * 3 - local sti + */ +unsigned long +__global_save_flags(void) +{ + int retval; + int local_enabled; + unsigned long flags; + int cpu = smp_processor_id(); + + __save_flags(flags); + local_enabled = (!(flags & 7)); + /* default to local */ + retval = 2 + local_enabled; + + /* Check for global flags if we're not in an interrupt. */ + if (!local_irq_count(cpu)) { + if (local_enabled) + retval = 1; + if (global_irq_holder == cpu) + retval = 0; + } + return retval; +} + +void +__global_restore_flags(unsigned long flags) +{ + switch (flags) { + case 0: + __global_cli(); + break; + case 1: + __global_sti(); + break; + case 2: + __cli(); + break; + case 3: + __sti(); + break; + default: + printk(KERN_ERR "global_restore_flags: %08lx (%p)\n", + flags, __builtin_return_address(0)); + } +} + +/* + * From its use, I infer that synchronize_irq() stalls a thread until + * the effects of a command to an external device are known to have + * taken hold. Typically, the command is to stop sending interrupts. + * The strategy here is wait until there is at most one processor + * (this one) in an irq. The memory barrier serializes the write to + * the device and the subsequent accesses of global_irq_count. + * --jmartin + */ +#define DEBUG_SYNCHRONIZE_IRQ 0 + +void +synchronize_irq(void) +{ +#if 0 + /* Joe's version. */ + int cpu = smp_processor_id(); + int local_count; + int global_count; + int countdown = 1<<24; + void *where = __builtin_return_address(0); + + mb(); + do { + local_count = local_irq_count(cpu); + global_count = atomic_read(&global_irq_count); + if (DEBUG_SYNCHRONIZE_IRQ && (--countdown == 0)) { + printk("%d:%d/%d\n", cpu, local_count, global_count); + show("synchronize_irq", where); + break; + } + } while (global_count != local_count); +#else + /* Jay's version. */ + if (irqs_running()) { + cli(); + sti(); + } +#endif +} diff --git a/arch/alpha/kernel/irq_srm.c b/arch/alpha/kernel/irq_srm.c new file mode 100644 index 000000000..8b14a59fe --- /dev/null +++ b/arch/alpha/kernel/irq_srm.c @@ -0,0 +1,82 @@ +/* + * Handle interrupts from the SRM, assuming no additional weirdness. + */ + +#include +#include +#include + +#include +#include + +#include "proto.h" +#include "irq_impl.h" + + +/* + * Is the palcode SMP safe? In other words: can we call cserve_ena/dis + * at the same time in multiple CPUs? To be safe I added a spinlock + * but it can be removed trivially if the palcode is robust against smp. + */ +spinlock_t srm_irq_lock = SPIN_LOCK_UNLOCKED; + +static inline void +srm_enable_irq(unsigned int irq) +{ + spin_lock(&srm_irq_lock); + cserve_ena(irq - 16); + spin_unlock(&srm_irq_lock); +} + +static void +srm_disable_irq(unsigned int irq) +{ + spin_lock(&srm_irq_lock); + cserve_dis(irq - 16); + spin_unlock(&srm_irq_lock); +} + +static unsigned int +srm_startup_irq(unsigned int irq) +{ + srm_enable_irq(irq); + return 0; +} + +static void +srm_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + srm_enable_irq(irq); +} + +/* Handle interrupts from the SRM, assuming no additional weirdness. */ +static struct hw_interrupt_type srm_irq_type = { + typename: "SRM", + startup: srm_startup_irq, + shutdown: srm_disable_irq, + enable: srm_enable_irq, + disable: srm_disable_irq, + ack: srm_disable_irq, + end: srm_end_irq, +}; + +void __init +init_srm_irqs(long max, unsigned long ignore_mask) +{ + long i; + + for (i = 16; i < max; ++i) { + if (i < 64 && ((ignore_mask >> i) & 1)) + continue; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; + irq_desc[i].handler = &srm_irq_type; + } +} + +void +srm_device_interrupt(unsigned long vector, struct pt_regs * regs) +{ + int irq = (vector - 0x800) >> 4; + handle_irq(irq, regs); +} diff --git a/arch/arm/mm/mm-clps7500.c b/arch/arm/mm/mm-clps7500.c new file mode 100644 index 000000000..b9199de80 --- /dev/null +++ b/arch/arm/mm/mm-clps7500.c @@ -0,0 +1,28 @@ +/* + * arch/arm/mm/mm-cl7500.c + * + * Extra MM routines for CL7500 architecture + * + * Copyright (C) 1998 Russell King + * Copyright (C) 1999 Nexus Electronics Ltd + */ + +#include + +#include +#include +#include +#include + +#include "map.h" + +#define SIZE(x) (sizeof(x) / sizeof(x[0])) + +struct map_desc io_desc[] __initdata = { + { IO_BASE, IO_START, IO_SIZE , DOMAIN_IO, 0, 1 }, /* IO space */ + { ISA_BASE, ISA_START, ISA_SIZE , DOMAIN_IO, 0, 1 }, /* ISA space */ + { FLASH_BASE, FLASH_START, FLASH_SIZE, DOMAIN_IO, 0, 1 }, /* Flash */ + { LED_BASE, LED_START, LED_SIZE , DOMAIN_IO, 0, 1 } /* LED */ +}; + +unsigned int __initdata io_desc_size = SIZE(io_desc); diff --git a/drivers/block/ide-cs.c b/drivers/block/ide-cs.c new file mode 100644 index 000000000..73d285cb1 --- /dev/null +++ b/drivers/block/ide-cs.c @@ -0,0 +1,481 @@ +/*====================================================================== + + A driver for PCMCIA IDE/ATA disk cards + + ide_cs.c 1.26 1999/11/16 02:10:49 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + . Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"ide_cs.c 1.26 1999/11/16 02:10:49 (David Hinds)"; +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Bit map of interrupts to choose from */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); + +/*====================================================================*/ + +static const char ide_major[] = { + IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, +#ifdef IDE4_MAJOR + IDE4_MAJOR, IDE5_MAJOR +#endif +}; + +typedef struct ide_info_t { + dev_link_t link; + int ndev; + dev_node_t node; + int hd; +} ide_info_t; + +static void ide_config(dev_link_t *link); +static void ide_release(u_long arg); +static int ide_event(event_t event, int priority, + event_callback_args_t *args); + +static dev_info_t dev_info = "ide_cs"; + +static dev_link_t *ide_attach(void); +static void ide_detach(dev_link_t *); + +static dev_link_t *dev_list = NULL; + +/*====================================================================*/ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================== + + ide_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. + +======================================================================*/ + +static dev_link_t *ide_attach(void) +{ + ide_info_t *info; + dev_link_t *link; + client_reg_t client_reg; + int i, ret; + + DEBUG(0, "ide_attach()\n"); + + /* Create new ide device */ + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) return NULL; + memset(info, 0, sizeof(*info)); + link = &info->link; link->priv = info; + + link->release.function = &ide_release; + link->release.data = (u_long)link; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; + link->io.IOAddrLines = 3; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &ide_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + ide_detach(link); + return NULL; + } + + return link; +} /* ide_attach */ + +/*====================================================================== + + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. + +======================================================================*/ + +static void ide_detach(dev_link_t *link) +{ + dev_link_t **linkp; + long flags; + int ret; + + DEBUG(0, "ide_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + save_flags(flags); + cli(); + if (link->state & DEV_RELEASE_PENDING) { + del_timer(&link->release); + link->state &= ~DEV_RELEASE_PENDING; + } + restore_flags(flags); + + if (link->state & DEV_CONFIG) + ide_release((u_long)link); + + if (link->handle) { + ret = CardServices(DeregisterClient, link->handle); + if (ret != CS_SUCCESS) + cs_error(link->handle, DeregisterClient, ret); + } + + /* Unlink, free device structure */ + *linkp = link->next; + kfree(link->priv); + +} /* ide_detach */ + +/*====================================================================== + + ide_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + ide device available to the system. + +======================================================================*/ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +#define CFG_CHECK(fn, args...) \ +if (CardServices(fn, args) != 0) goto next_entry + +void ide_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + ide_info_t *info = link->priv; + tuple_t tuple; + u_short buf[128]; + cisparse_t parse; + config_info_t conf; + cistpl_cftable_entry_t *cfg = &parse.cftable_entry; + cistpl_cftable_entry_t dflt = { 0 }; + int i, pass, last_ret, last_fn, hd, io_base, ctl_base; + + DEBUG(0, "ide_config(0x%p)\n", link); + + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleOffset = 0; tuple.TupleDataMax = 255; + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Configure card */ + link->state |= DEV_CONFIG; + + /* Not sure if this is right... look up the current Vcc */ + CS_CHECK(GetConfigurationInfo, handle, &conf); + link->conf.Vcc = conf.Vcc; + + pass = io_base = ctl_base = 0; + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + tuple.Attributes = 0; + CS_CHECK(GetFirstTuple, handle, &tuple); + while (1) { + CFG_CHECK(GetTupleData, handle, &tuple); + CFG_CHECK(ParseTuple, handle, &tuple, &parse); + + /* Check for matching Vcc, unless we're desperate */ + if (!pass) { + if (cfg->vcc.present & (1<vcc.param[CISTPL_POWER_VNOM]/10000) + goto next_entry; + } else if (dflt.vcc.present & (1<vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = + cfg->vpp1.param[CISTPL_POWER_VNOM]/10000; + else if (dflt.vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = + dflt.vpp1.param[CISTPL_POWER_VNOM]/10000; + + if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; + link->conf.ConfigIndex = cfg->index; + link->io.BasePort1 = io->win[0].base; + link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; + if (!(io->flags & CISTPL_IO_16BIT)) + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + if (io->nwin == 2) { + link->io.NumPorts1 = 8; + link->io.BasePort2 = io->win[1].base; + link->io.NumPorts2 = 1; + CFG_CHECK(RequestIO, link->handle, &link->io); + io_base = link->io.BasePort1; + ctl_base = link->io.BasePort2; + } else if ((io->nwin == 1) && (io->win[0].len >= 16)) { + link->io.NumPorts1 = io->win[0].len; + link->io.NumPorts2 = 0; + CFG_CHECK(RequestIO, link->handle, &link->io); + io_base = link->io.BasePort1; + ctl_base = link->io.BasePort1+0x0e; + } else goto next_entry; + /* If we've got this far, we're done */ + break; + } + + next_entry: + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; + if (pass) { + CS_CHECK(GetNextTuple, handle, &tuple); + } else if (CardServices(GetNextTuple, handle, &tuple) != 0) { + CS_CHECK(GetFirstTuple, handle, &tuple); + memset(&dflt, 0, sizeof(dflt)); + pass++; + } + } + + CS_CHECK(RequestIRQ, handle, &link->irq); + CS_CHECK(RequestConfiguration, handle, &link->conf); + + /* deal with brain dead IDE resource management */ + release_region(link->io.BasePort1, link->io.NumPorts1); + if (link->io.NumPorts2) + release_region(link->io.BasePort2, link->io.NumPorts2); + + /* retry registration in case device is still spinning up */ + for (i = 0; i < 10; i++) { + hd = ide_register(io_base, ctl_base, link->irq.AssignedIRQ); + if (hd >= 0) break; + if (link->io.NumPorts1 == 0x20) { + hd = ide_register(io_base+0x10, ctl_base+0x10, + link->irq.AssignedIRQ); + if (hd >= 0) { + io_base += 0x10; ctl_base += 0x10; + break; + } + } + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/10); + } + + if (hd < 0) { + printk(KERN_NOTICE "ide_cs: ide_register() at 0x%3x & 0x%3x" + ", irq %u failed\n", io_base, ctl_base, + link->irq.AssignedIRQ); + goto failed; + } + + MOD_INC_USE_COUNT; + info->ndev = 1; + sprintf(info->node.dev_name, "hd%c", 'a'+(hd*2)); + info->node.major = ide_major[hd]; + info->node.minor = 0; + info->hd = hd; + link->dev = &info->node; + printk(KERN_INFO "ide_cs: %s: Vcc = %d.%d, Vpp = %d.%d\n", + info->node.dev_name, link->conf.Vcc/10, link->conf.Vcc%10, + link->conf.Vpp1/10, link->conf.Vpp1%10); + + link->state &= ~DEV_CONFIG_PENDING; + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + ide_release((u_long)link); + +} /* ide_config */ + +/*====================================================================== + + After a card is removed, ide_release() will unregister the net + device, and release the PCMCIA configuration. If the device is + still open, this will be postponed until it is closed. + +======================================================================*/ + +void ide_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + ide_info_t *info = link->priv; + + DEBUG(0, "ide_release(0x%p)\n", link); + + if (info->ndev) { + ide_unregister(info->hd); + MOD_DEC_USE_COUNT; + } + info->ndev = 0; + link->dev = NULL; + + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~DEV_CONFIG; + +} /* ide_release */ + +/*====================================================================== + + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the ide drivers from + talking to the ports. + +======================================================================*/ + +int ide_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + + DEBUG(1, "ide_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + link->release.expires = jiffies + HZ/20; + link->state |= DEV_RELEASE_PENDING; + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + ide_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) + CardServices(ReleaseConfiguration, link->handle); + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (DEV_OK(link)) + CardServices(RequestConfiguration, link->handle, &link->conf); + break; + } + return 0; +} /* ide_event */ + +/*====================================================================*/ + +static int __init init_ide_cs(void) +{ + servinfo_t serv; + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "ide_cs: Card Services release " + "does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &ide_attach, &ide_detach); + return 0; +} + +static void __exit exit_ide_cs(void) +{ + DEBUG(0, "ide_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + ide_detach(dev_list); +} + +module_init(init_ide_cs); +module_exit(exit_ide_cs); diff --git a/drivers/char/digi1.h b/drivers/char/digi1.h new file mode 100644 index 000000000..184378d23 --- /dev/null +++ b/drivers/char/digi1.h @@ -0,0 +1,100 @@ +/* Definitions for DigiBoard ditty(1) command. */ + +#if !defined(TIOCMODG) +#define TIOCMODG ('d'<<8) | 250 /* get modem ctrl state */ +#define TIOCMODS ('d'<<8) | 251 /* set modem ctrl state */ +#endif + +#if !defined(TIOCMSET) +#define TIOCMSET ('d'<<8) | 252 /* set modem ctrl state */ +#define TIOCMGET ('d'<<8) | 253 /* set modem ctrl state */ +#endif + +#if !defined(TIOCMBIC) +#define TIOCMBIC ('d'<<8) | 254 /* set modem ctrl state */ +#define TIOCMBIS ('d'<<8) | 255 /* set modem ctrl state */ +#endif + +#if !defined(TIOCSDTR) +#define TIOCSDTR ('e'<<8) | 0 /* set DTR */ +#define TIOCCDTR ('e'<<8) | 1 /* clear DTR */ +#endif + +/************************************************************************ + * Ioctl command arguments for DIGI parameters. + ************************************************************************/ +#define DIGI_GETA ('e'<<8) | 94 /* Read params */ + +#define DIGI_SETA ('e'<<8) | 95 /* Set params */ +#define DIGI_SETAW ('e'<<8) | 96 /* Drain & set params */ +#define DIGI_SETAF ('e'<<8) | 97 /* Drain, flush & set params */ + +#define DIGI_GETFLOW ('e'<<8) | 99 /* Get startc/stopc flow */ + /* control characters */ +#define DIGI_SETFLOW ('e'<<8) | 100 /* Set startc/stopc flow */ + /* control characters */ +#define DIGI_GETAFLOW ('e'<<8) | 101 /* Get Aux. startc/stopc */ + /* flow control chars */ +#define DIGI_SETAFLOW ('e'<<8) | 102 /* Set Aux. startc/stopc */ + /* flow control chars */ + +#define DIGI_GETINFO ('e'<<8) | 103 /* Fill in digi_info */ +#define DIGI_POLLER ('e'<<8) | 104 /* Turn on/off poller */ +#define DIGI_INIT ('e'<<8) | 105 /* Allow things to run. */ + +struct digiflow_struct +{ + unsigned char startc; /* flow cntl start char */ + unsigned char stopc; /* flow cntl stop char */ +}; + +typedef struct digiflow_struct digiflow_t; + + +/************************************************************************ + * Values for digi_flags + ************************************************************************/ +#define DIGI_IXON 0x0001 /* Handle IXON in the FEP */ +#define DIGI_FAST 0x0002 /* Fast baud rates */ +#define RTSPACE 0x0004 /* RTS input flow control */ +#define CTSPACE 0x0008 /* CTS output flow control */ +#define DSRPACE 0x0010 /* DSR output flow control */ +#define DCDPACE 0x0020 /* DCD output flow control */ +#define DTRPACE 0x0040 /* DTR input flow control */ +#define DIGI_FORCEDCD 0x0100 /* Force carrier */ +#define DIGI_ALTPIN 0x0200 /* Alternate RJ-45 pin config */ +#define DIGI_AIXON 0x0400 /* Aux flow control in fep */ + + +/************************************************************************ + * Values for digiDload + ************************************************************************/ +#define NORMAL 0 +#define PCI_CTL 1 + +#define SIZE8 0 +#define SIZE16 1 +#define SIZE32 2 + +/************************************************************************ + * Structure used with ioctl commands for DIGI parameters. + ************************************************************************/ +struct digi_struct +{ + unsigned short digi_flags; /* Flags (see above) */ +}; + +typedef struct digi_struct digi_t; + +struct digi_info +{ + unsigned long board; /* Which board is this ? */ + unsigned char status; /* Alive or dead */ + unsigned char type; /* see epca.h */ + unsigned char subtype; /* For future XEM, XR, etc ... */ + unsigned short numports; /* Number of ports configured */ + unsigned char *port; /* I/O Address */ + unsigned char *membase; /* DPR Address */ + unsigned char *version; /* For future ... */ + unsigned short windowData; /* For future ... */ +} ; diff --git a/drivers/char/digiFep1.h b/drivers/char/digiFep1.h new file mode 100644 index 000000000..c47d7fcb8 --- /dev/null +++ b/drivers/char/digiFep1.h @@ -0,0 +1,136 @@ + +#define CSTART 0x400L +#define CMAX 0x800L +#define ISTART 0x800L +#define IMAX 0xC00L +#define CIN 0xD10L +#define GLOBAL 0xD10L +#define EIN 0xD18L +#define FEPSTAT 0xD20L +#define CHANSTRUCT 0x1000L +#define RXTXBUF 0x4000L + + +struct global_data +{ + volatile ushort cin; + volatile ushort cout; + volatile ushort cstart; + volatile ushort cmax; + volatile ushort ein; + volatile ushort eout; + volatile ushort istart; + volatile ushort imax; +}; + + +struct board_chan +{ + int filler1; + int filler2; + volatile ushort tseg; + volatile ushort tin; + volatile ushort tout; + volatile ushort tmax; + + volatile ushort rseg; + volatile ushort rin; + volatile ushort rout; + volatile ushort rmax; + + volatile ushort tlow; + volatile ushort rlow; + volatile ushort rhigh; + volatile ushort incr; + + volatile ushort etime; + volatile ushort edelay; + volatile unchar *dev; + + volatile ushort iflag; + volatile ushort oflag; + volatile ushort cflag; + volatile ushort gmask; + + volatile ushort col; + volatile ushort delay; + volatile ushort imask; + volatile ushort tflush; + + int filler3; + int filler4; + int filler5; + int filler6; + + volatile unchar num; + volatile unchar ract; + volatile unchar bstat; + volatile unchar tbusy; + volatile unchar iempty; + volatile unchar ilow; + volatile unchar idata; + volatile unchar eflag; + + volatile unchar tflag; + volatile unchar rflag; + volatile unchar xmask; + volatile unchar xval; + volatile unchar mstat; + volatile unchar mchange; + volatile unchar mint; + volatile unchar lstat; + + volatile unchar mtran; + volatile unchar orun; + volatile unchar startca; + volatile unchar stopca; + volatile unchar startc; + volatile unchar stopc; + volatile unchar vnext; + volatile unchar hflow; + + volatile unchar fillc; + volatile unchar ochar; + volatile unchar omask; + + unchar filler7; + unchar filler8[28]; +}; + + +#define SRXLWATER 0xE0 +#define SRXHWATER 0xE1 +#define STOUT 0xE2 +#define PAUSETX 0xE3 +#define RESUMETX 0xE4 +#define SAUXONOFFC 0xE6 +#define SENDBREAK 0xE8 +#define SETMODEM 0xE9 +#define SETIFLAGS 0xEA +#define SONOFFC 0xEB +#define STXLWATER 0xEC +#define PAUSERX 0xEE +#define RESUMERX 0xEF +#define SETBUFFER 0xF2 +#define SETCOOKED 0xF3 +#define SETHFLOW 0xF4 +#define SETCTRLFLAGS 0xF5 +#define SETVNEXT 0xF6 + + + +#define BREAK_IND 0x01 +#define LOWTX_IND 0x02 +#define EMPTYTX_IND 0x04 +#define DATA_IND 0x08 +#define MODEMCHG_IND 0x20 + +#define FEP_HUPCL 0002000 +#if 0 +#define RTS 0x02 +#define CD 0x08 +#define DSR 0x10 +#define CTS 0x20 +#define RI 0x40 +#define DTR 0x80 +#endif diff --git a/drivers/char/digiPCI.h b/drivers/char/digiPCI.h new file mode 100644 index 000000000..6ca7819e5 --- /dev/null +++ b/drivers/char/digiPCI.h @@ -0,0 +1,42 @@ +/************************************************************************* + * Defines and structure definitions for PCI BIOS Interface + *************************************************************************/ +#define PCIMAX 32 /* maximum number of PCI boards */ + + +#define PCI_VENDOR_DIGI 0x114F +#define PCI_DEVICE_EPC 0x0002 +#define PCI_DEVICE_RIGHTSWITCH 0x0003 /* For testing */ +#define PCI_DEVICE_XEM 0x0004 +#define PCI_DEVICE_XR 0x0005 +#define PCI_DEVICE_CX 0x0006 +#define PCI_DEVICE_XRJ 0x0009 /* Jupiter boards with */ +#define PCI_DEVICE_EPCJ 0x000a /* PLX 9060 chip for PCI */ + + +/* + * On the PCI boards, there is no IO space allocated + * The I/O registers will be in the first 3 bytes of the + * upper 2MB of the 4MB memory space. The board memory + * will be mapped into the low 2MB of the 4MB memory space + */ + +/* Potential location of PCI Bios from E0000 to FFFFF*/ +#define PCI_BIOS_SIZE 0x00020000 + +/* Size of Memory and I/O for PCI (4MB) */ +#define PCI_RAM_SIZE 0x00400000 + +/* Size of Memory (2MB) */ +#define PCI_MEM_SIZE 0x00200000 + +/* Offset of I/0 in Memory (2MB) */ +#define PCI_IO_OFFSET 0x00200000 + +#define MEMOUTB(basemem, pnum, setmemval) *(caddr_t)((basemem) + ( PCI_IO_OFFSET | pnum << 4 | pnum )) = (setmemval) +#define MEMINB(basemem, pnum) *(caddr_t)((basemem) + (PCI_IO_OFFSET | pnum << 4 | pnum )) /* for PCI I/O */ + + + + + diff --git a/drivers/char/epca.h b/drivers/char/epca.h new file mode 100644 index 000000000..18cedf37c --- /dev/null +++ b/drivers/char/epca.h @@ -0,0 +1,170 @@ +#define XEMPORTS 0xC02 +#define XEPORTS 0xC22 + +#define MAX_ALLOC 0x100 + +#define MAXBOARDS 12 +#define FEPCODESEG 0x0200L +#define FEPCODE 0x2000L +#define BIOSCODE 0xf800L + +#define MISCGLOBAL 0x0C00L +#define NPORT 0x0C22L +#define MBOX 0x0C40L +#define PORTBASE 0x0C90L + +/* Begin code defines used for epca_setup */ + +#define INVALID_BOARD_TYPE 0x1 +#define INVALID_NUM_PORTS 0x2 +#define INVALID_MEM_BASE 0x4 +#define INVALID_PORT_BASE 0x8 +#define INVALID_BOARD_STATUS 0x10 +#define INVALID_ALTPIN 0x20 + +/* End code defines used for epca_setup */ + + +#define FEPCLR 0x00 +#define FEPMEM 0x02 +#define FEPRST 0x04 +#define FEPINT 0x08 +#define FEPMASK 0x0e +#define FEPWIN 0x80 + +#define PCXE 0 +#define PCXEVE 1 +#define PCXEM 2 +#define EISAXEM 3 +#define PC64XE 4 +#define PCXI 5 +#define PCIXEM 7 +#define PCICX 8 +#define PCIXR 9 +#define PCIXRJ 10 +#define EPCA_NUM_TYPES 6 + + +static char *board_desc[] = +{ + "PC/Xe", + "PC/Xeve", + "PC/Xem", + "EISA/Xem", + "PC/64Xe", + "PC/Xi", + "unknown", + "PCI/Xem", + "PCI/CX", + "PCI/Xr", + "PCI/Xrj", +}; + +#define STARTC 021 +#define STOPC 023 +#define IAIXON 0x2000 + + +#define TXSTOPPED 0x1 +#define LOWWAIT 0x2 +#define EMPTYWAIT 0x4 +#define RXSTOPPED 0x8 +#define TXBUSY 0x10 + +#define DISABLED 0 +#define ENABLED 1 +#define OFF 0 +#define ON 1 + +#define FEPTIMEOUT 200000 +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +#define SERIAL_TYPE_INFO 3 +#define EPCA_EVENT_HANGUP 1 +#define EPCA_MAGIC 0x5c6df104L + +struct channel +{ + long magic; + unchar boardnum; + unchar channelnum; + unchar omodem; /* FEP output modem status */ + unchar imodem; /* FEP input modem status */ + unchar modemfake; /* Modem values to be forced */ + unchar modem; /* Force values */ + unchar hflow; + unchar dsr; + unchar dcd; + unchar m_rts ; /* The bits used in whatever FEP */ + unchar m_dcd ; /* is indiginous to this board to */ + unchar m_dsr ; /* represent each of the physical */ + unchar m_cts ; /* handshake lines */ + unchar m_ri ; + unchar m_dtr ; + unchar stopc; + unchar startc; + unchar stopca; + unchar startca; + unchar fepstopc; + unchar fepstartc; + unchar fepstopca; + unchar fepstartca; + unchar txwin; + unchar rxwin; + ushort fepiflag; + ushort fepcflag; + ushort fepoflag; + ushort txbufhead; + ushort txbufsize; + ushort rxbufhead; + ushort rxbufsize; + int close_delay; + int count; + int blocked_open; + int event; + int asyncflags; + uint dev; + long session; + long pgrp; + ulong statusflags; + ulong c_iflag; + ulong c_cflag; + ulong c_lflag; + ulong c_oflag; + unchar *txptr; + unchar *rxptr; + unchar *tmp_buf; + struct board_info *board; + volatile struct board_chan *brdchan; + struct digi_struct digiext; + struct tty_struct *tty; + struct termios normal_termios; + struct termios callout_termios; + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; + struct tq_struct tqueue; + volatile struct global_data *mailbox; +}; + +struct board_info +{ + unchar status; + unchar type; + unchar altpin; + ushort numports; + unchar *port; + unchar *membase; + unchar *re_map_port; + unchar *re_map_membase; + ulong memory_seg; + void ( * memwinon ) (struct board_info *, unsigned int) ; + void ( * memwinoff ) (struct board_info *, unsigned int) ; + void ( * globalwinon ) (struct channel *) ; + void ( * txwinon ) (struct channel *) ; + void ( * rxwinon ) (struct channel *) ; + void ( * memoff ) (struct channel *) ; + void ( * assertgwinon ) (struct channel *) ; + void ( * assertmemoff ) (struct channel *) ; + unchar poller_inhibited ; +}; + diff --git a/drivers/char/epcaconfig.h b/drivers/char/epcaconfig.h new file mode 100644 index 000000000..55dec0670 --- /dev/null +++ b/drivers/char/epcaconfig.h @@ -0,0 +1,7 @@ +#define NUMCARDS 0 +#define NBDEVS 0 + +struct board_info static_boards[NUMCARDS]={ +}; + +/* DO NOT HAND EDIT THIS FILE! */ diff --git a/drivers/char/tuner-3036.c b/drivers/char/tuner-3036.c new file mode 100644 index 000000000..807ae339b --- /dev/null +++ b/drivers/char/tuner-3036.c @@ -0,0 +1,227 @@ +/* + * Driver for Philips SAB3036 "CITAC" tuner control chip. + * + * Author: Phil Blundell + * + * The SAB3036 is just about different enough from the chips that + * tuner.c copes with to make it not worth the effort to crowbar + * the support into that file. So instead we have a separate driver. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tuner.h" + +static int debug; /* insmod parameter */ +static int this_adap; + +static struct i2c_client client_template; + +/* Addresses to scan */ +static unsigned short normal_i2c[] = {I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {0x60, 0x61, I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; + +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + +/* ---------------------------------------------------------------------- */ + +static unsigned char +tuner_getstatus (struct i2c_client *c) +{ + unsigned char byte; + if (i2c_master_recv(c, &byte, 1) != 1) + printk(KERN_ERR "tuner-3036: I/O error.\n"); + return byte; +} + +#define TUNER_FL 0x80 + +static int +tuner_islocked (struct i2c_client *c) +{ + return (tuner_getstatus(c) & TUNER_FL); +} + +/* ---------------------------------------------------------------------- */ + +static void +set_tv_freq(struct i2c_client *c, int freq) +{ + u16 div = ((freq * 20) / 16); + unsigned long give_up = jiffies + HZ; + unsigned char buffer[2]; + + if (debug) + printk(KERN_DEBUG "tuner: setting frequency %dMHz, divisor %x\n", freq / 16, div); + + /* Select high tuning current */ + buffer[0] = 0x29; + buffer[1] = 0x3e; + + if (i2c_master_send(c, buffer, 2) != 2) + printk("tuner: i2c i/o error 1\n"); + + buffer[0] = 0x80 | ((div>>8) & 0x7f); + buffer[1] = div & 0xff; + + if (i2c_master_send(c, buffer, 2) != 2) + printk("tuner: i2c i/o error 2\n"); + + while (!tuner_islocked(c) && time_before(jiffies, give_up)) + schedule(); + + if (!tuner_islocked(c)) + printk(KERN_WARNING "tuner: failed to achieve PLL lock\n"); + + /* Select low tuning current and engage AFC */ + buffer[0] = 0x29; + buffer[1] = 0xb2; + + if (i2c_master_send(c, buffer, 2) != 2) + printk("tuner: i2c i/o error 3\n"); + + if (debug) + printk(KERN_DEBUG "tuner: status %02x\n", tuner_getstatus(c)); +} + +/* ---------------------------------------------------------------------- */ + +static int +tuner_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + static unsigned char buffer[] = { 0x29, 0x32, 0x2a, 0, 0x2b, 0 }; + + struct i2c_client *client; + + if (this_adap > 0) + return -1; + this_adap++; + + client_template.adapter = adap; + client_template.addr = addr; + + client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == NULL) + return -ENOMEM; + memcpy(client, &client_template, sizeof(struct i2c_client)); + + printk("tuner: SAB3036 found, status %02x\n", tuner_getstatus(client)); + + i2c_attach_client(client); + MOD_INC_USE_COUNT; + + if (i2c_master_send(client, buffer, 2) != 2) + printk("tuner: i2c i/o error 1\n"); + if (i2c_master_send(client, buffer+2, 2) != 2) + printk("tuner: i2c i/o error 2\n"); + if (i2c_master_send(client, buffer+4, 2) != 2) + printk("tuner: i2c i/o error 3\n"); + return 0; +} + +static int +tuner_detach(struct i2c_client *c) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static int +tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + int *iarg = (int*)arg; + + switch (cmd) + { + case TUNER_SET_TVFREQ: + set_tv_freq(client, *iarg); + break; + + default: + return -EINVAL; + } + return 0; +} + +static int +tuner_probe(struct i2c_adapter *adap) +{ + this_adap = 0; + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_LP)) + return i2c_probe(adap, &addr_data, tuner_attach); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver +i2c_driver_tuner = +{ + "sab3036", /* name */ + I2C_DRIVERID_SAB3036, /* ID */ + I2C_DF_NOTIFY, + tuner_probe, + tuner_detach, + tuner_command +}; + +static struct i2c_client client_template = +{ + "SAB3036", /* name */ + -1, + 0, + 0, + NULL, + &i2c_driver_tuner +}; + +EXPORT_NO_SYMBOLS; + +int __init +tuner3036_init(void) +{ + i2c_add_driver(&i2c_driver_tuner); + return 0; +} + +void __exit +tuner3036_exit(void) +{ + i2c_del_driver(&i2c_driver_tuner); +} + +MODULE_DESCRIPTION("SAB3036 tuner driver"); +MODULE_AUTHOR("Philip Blundell "); +MODULE_PARM(debug,"i"); +MODULE_PARM_DESC(debug,"Enable debugging output"); + +module_init(tuner3036_init); +module_exit(tuner3036_exit); diff --git a/drivers/net/bonding.c b/drivers/net/bonding.c new file mode 100644 index 000000000..f1a4526d4 --- /dev/null +++ b/drivers/net/bonding.c @@ -0,0 +1,350 @@ +/* + * originally based on the dummy device. + * + * Copyright 1999, Thomas Davis, tadavis@lbl.gov. + * Licensed under the GPL. Based on dummy.c, and eql.c devices. + * + * bond.c: a bonding/etherchannel/sun trunking net driver + * + * This is useful to talk to a Cisco 5500, running Etherchannel, aka: + * Linux Channel Bonding + * Sun Trunking (Solaris) + * + * How it works: + * ifconfig bond0 ipaddress netmask up + * will setup a network device, with an ip address. No mac address + * will be assigned at this time. The hw mac address will come from + * the first slave bonded to the channel. All slaves will then use + * this hw mac address. + * + * ifconfig bond0 down + * will release all slaves, marking them as down. + * + * ifenslave bond0 eth0 + * will attache eth0 to bond0 as a slave. eth0 hw mac address will either + * a: be used as initial mac address + * b: if a hw mac address already is there, eth0's hw mac address + * will then be set from bond0. + * + * v0.1 - first working version. + * v0.2 - changed stats to be calculated by summing slaves stats. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +typedef struct slave +{ + struct slave *next; + struct slave *prev; + struct net_device *dev; +} slave_t; + +typedef struct bonding +{ + slave_t *next; + slave_t *prev; + struct net_device *master; + + slave_t *current_slave; + struct net_device_stats stats; +} bonding_t; + + +static int bond_xmit(struct sk_buff *skb, struct net_device *dev); +static struct net_device_stats *bond_get_stats(struct net_device *dev); + +static struct net_device *this_bond; + +static int bond_open(struct net_device *dev) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static int bond_close(struct net_device *master) +{ + bonding_t *bond = master->priv; + slave_t *slave; + + while ((slave = bond->next) != (slave_t*)bond) { + + spin_lock_bh(&master->xmit_lock); + slave->next->prev = slave->prev; + slave->prev->next = slave->next; + bond->current_slave = (slave_t*)bond; + spin_unlock_bh(&master->xmit_lock); + + netdev_set_master(slave->dev, NULL); + + kfree(slave); + } + + MOD_DEC_USE_COUNT; + return 0; +} + +/* Fake multicast ability. + + NB. It is possible and necessary to make it true one, otherwise + the device is not functional. + */ +static void bond_set_multicast_list(struct net_device *dev) +{ +} + +static int bond_enslave(struct net_device *master, struct net_device *dev) +{ + int err; + bonding_t *bond = master->priv; + slave_t *slave; + + if (dev->type != master->type) + return -ENODEV; + + if ((slave = kmalloc(sizeof(slave_t), GFP_KERNEL)) == NULL) + return -ENOMEM; + + memset(slave, 0, sizeof(slave_t)); + + err = netdev_set_master(dev, master); + if (err) { + kfree(slave); + return err; + } + + slave->dev = dev; + + spin_lock_bh(&master->xmit_lock); + + dev_hold(dev); + + slave->prev = bond->prev; + slave->next = (slave_t*)bond; + slave->prev->next = slave; + slave->next->prev = slave; + + spin_unlock_bh(&master->xmit_lock); + + MOD_INC_USE_COUNT; + return 0; +} + +static int bond_release(struct net_device *master, struct net_device *dev) +{ + bonding_t *bond = master->priv; + slave_t *slave; + + if (dev->master != master) + return -EINVAL; + + netdev_set_master(dev, NULL); + + for (slave = bond->next; slave != (slave_t*)bond; slave = slave->next) { + if (slave->dev == dev) { + spin_lock_bh(&master->xmit_lock); + if (bond->current_slave == slave) + bond->current_slave = slave->next; + slave->next->prev = slave->prev; + slave->prev->next = slave->next; + spin_unlock_bh(&master->xmit_lock); + + kfree(slave); + dev_put(dev); + MOD_DEC_USE_COUNT; + break; + } + } + + return 0; +} + +/* It is pretty silly, SIOCSIFHWADDR exists to make this. */ + +static int bond_sethwaddr(struct net_device *master, struct net_device *slave) +{ + memcpy(master->dev_addr, slave->dev_addr, slave->addr_len); + return 0; +} + +static int bond_ioctl(struct net_device *master, struct ifreq *ifr, int cmd) +{ + struct net_device *slave = __dev_get_by_name(ifr->ifr_slave); + + if (slave == NULL) + return -ENODEV; + + switch (cmd) { + case BOND_ENSLAVE: + return bond_enslave(master, slave); + case BOND_RELEASE: + return bond_release(master, slave); + case BOND_SETHWADDR: + return bond_sethwaddr(master, slave); + default: + return -EOPNOTSUPP; + } +} + +static int bond_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + struct net_device *slave = ptr; + + if (this_bond == NULL || + this_bond == slave || + this_bond != slave->master) + return NOTIFY_DONE; + + switch (event) { + case NETDEV_UNREGISTER: + bond_release(this_bond, slave); + break; + } + + return NOTIFY_DONE; +} + +struct notifier_block bond_netdev_notifier={ + bond_event, + NULL, + 0 +}; + +int __init bond_init(struct net_device *dev) +{ + bonding_t *bond; + + bond = kmalloc(sizeof(struct bonding), GFP_KERNEL); + if (bond == NULL) + return -ENOMEM; + + memset(bond, 0, sizeof(struct bonding)); + bond->next = (slave_t*)bond; + bond->prev = (slave_t*)bond; + bond->master = dev; + bond->current_slave = (slave_t*)bond; + dev->priv = bond; + + /* Initialize the device structure. */ + dev->hard_start_xmit = bond_xmit; + dev->get_stats = bond_get_stats; + dev->open = bond_open; + dev->stop = bond_close; + dev->set_multicast_list = bond_set_multicast_list; + dev->do_ioctl = bond_ioctl; + + /* Fill in the fields of the device structure with ethernet-generic + values. */ + ether_setup(dev); + dev->tx_queue_len = 0; + dev->flags |= IFF_MASTER; + + this_bond = dev; + + register_netdevice_notifier(&bond_netdev_notifier); + + return 0; +} + +static int bond_xmit(struct sk_buff *skb, struct net_device *dev) +{ + bonding_t *bond = dev->priv; + slave_t *slave, *start_at; + int pkt_len = skb->len; + + slave = start_at = bond->current_slave; + + do { + if (slave == (slave_t*)bond) + continue; + + if (netif_running(slave->dev) && netif_carrier_ok(dev)) { + bond->current_slave = slave->next; + skb->dev = slave->dev; + + if (dev_queue_xmit(skb)) { + bond->stats.tx_dropped++; + } else { + bond->stats.tx_packets++; + bond->stats.tx_bytes += pkt_len; + } + return 0; + } + } while ((slave = slave->next) != start_at); + + bond->stats.tx_dropped++; + kfree_skb(skb); + return 0; +} + +static struct net_device_stats *bond_get_stats(struct net_device *dev) +{ + bonding_t *bond = dev->priv; + + return &bond->stats; +} + +#ifdef MODULE + +static char bond_name[16]; + +static struct net_device dev_bond = { + bond_name, /* Needs to be writeable */ + 0, 0, 0, 0, + 0x0, 0, + 0, 0, 0, NULL, bond_init }; + +int init_module(void) +{ + /* Find a name for this unit */ + int err=dev_alloc_name(&dev_bond,"bond%d"); + + if (err<0) + return err; + + if (register_netdev(&dev_bond) != 0) + return -EIO; + + return 0; +} + +void cleanup_module(void) +{ + unregister_netdevice_notifier(&bond_netdev_notifier); + + unregister_netdev(&dev_bond); + + kfree(dev_bond.priv); +} +#endif /* MODULE */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c new file mode 100644 index 000000000..1e752dc75 --- /dev/null +++ b/drivers/usb/pegasus.c @@ -0,0 +1,579 @@ +/* +** +** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller +** +** Copyleft (L) 1999 Petko Manolov - Petkan (petkan@spct.net) +** +** Distribute under GPL version 2 or later. +*/ + + +#include +#include +#include +#include +#include + +#include +#include + +#include "usb.h" + +#if LINUX_VERSION_CODE<0x2032d || !defined(__KERNEL__) || !defined(__OPTIMIZE__) +#error You can not compile this driver on this kernel with this C options! +#endif + + +#define ADMTEK_VENDOR_ID 0x07a6 +#define ADMTEK_HPNA_PEGASUS 0x0986 + +#define HPNA_MTU 1500 +#define MAX_MTU 1536 + +#define TX_TIMEOUT (HZ*5) +#define SOMETHING (jiffies + TX_TIMEOUT) + + +static const char version[] = "pegasus.c: v0.2.27 2000/02/29 Written by Petko Manolov (petkan@spct.net)\n"; + + +typedef struct usb_hpna +{ + struct usb_device *usb_dev; + struct net_device *net_dev; + int present; + int active; + void *irq_handler; + struct list_head list; + struct net_device_stats stats; + spinlock_t hpna_lock; + struct timer_list timer; + + unsigned int rx_pipe; + unsigned char * rx_buff; + urb_t rx_urb; + + unsigned int tx_pipe; + unsigned char * tx_buff; + urb_t tx_urb; + struct sk_buff * tx_skbuff; + + __u8 intr_ival; + unsigned int intr_pipe; + unsigned char intr_buff[8]; + urb_t intr_urb; +} usb_hpna_t; + + +usb_hpna_t usb_dev_hpna; +static int loopback = 0; +int multicast_filter_limit = 32; +static LIST_HEAD(hpna_list); + + +MODULE_AUTHOR("Petko Manolov "); +MODULE_DESCRIPTION("ADMtek \"Pegasus\" USB Ethernet driver"); +MODULE_PARM(loopback, "i"); + + + +/*** vendor specific commands ***/ +static __inline__ int hpna_get_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data ) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, + indx, data, size, HZ); +} + + +static __inline__ int hpna_set_register( struct usb_device *dev, __u16 indx, __u8 value ) +{ + __u8 data = value; + return usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, + data, indx, &data, 1, HZ); +} + + +static __inline__ int hpna_set_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data ) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, + indx, data, size, HZ); +} + + +static int read_phy_word( struct usb_device *dev, __u8 index, __u16 *regdata ) +{ + int i; + __u8 data[4]; + + data[0] = 1; + data[1] = 0; + data[2] = 0; + data[3] = 0x40 + index; + hpna_set_registers( dev, 0x25, 4, data ); + for ( i=0; i<100; i++ ) { + hpna_get_registers( dev, 0x25, 4, data ); + if ( data[3] & 0x80 ) { + *regdata = *(__u16 *)(data+1); + return 0; + } + udelay(100); + } + warn("read_phy_word() failed"); + return 1; +} + + +static int write_phy_word( struct usb_device *dev, __u8 index, __u16 regdata ) +{ + int i; + __u8 data[4]; + + data[0] = 1; + data[1] = regdata; + data[2] = regdata >> 8; + data[3] = 0x20 + index; + hpna_set_registers( dev, 0x25, 4, data ); + for ( i=0; i<100; i++ ) { + hpna_get_registers( dev, 0x28, 1, data ); + if ( data[0] & 0x80 ) { + return 0; + } + udelay(100); + } + warn("write_phy_word() failed"); + return 1; +} + + +int read_srom_word( struct usb_device *dev, __u8 index, __u16 *retdata) +{ + int i; + __u8 data[4]; + + data[0] = index; + data[1] = data[2] = 0; + data[3] = 0x02; + hpna_set_registers(dev, 0x20, 4, data); + for ( i=0; i<100; i++ ) { + hpna_get_registers(dev, 0x23, 1, data); + if ( data[0] & 4 ) { + hpna_get_registers(dev, 0x21, 2, data); + *retdata = *(__u16 *)data; + return 0; + } + } + warn("read_srom_word() failed"); + return 1; +} +/*** end ***/ + + + + +int get_node_id( struct usb_device *dev, __u8 *id ) +{ + int i; + + for ( i=0; i<3; i++ ) { + if ( read_srom_word(dev, i, (__u16 *)&id[i*2] ) ) + return 1; + } + return 0; +} + + +static int reset_mac( struct usb_device *dev ) +{ + __u8 data = 0x8; + int i; + + hpna_set_register( dev, 1, 0x08 ); + for ( i=0; i<100; i++ ) { + hpna_get_registers( dev, 1, 1, &data); + if ( !(data & 0x08) ) { + if ( loopback & 1 ) + return 0; + else if ( loopback & 2 ) { + write_phy_word( dev, 0, 0x4000 ); + /*return 0;*/ + } + hpna_set_register( dev, 0x7e, 0x24 ); + hpna_set_register( dev, 0x7e, 0x27 ); + return 0; + } + } + return 1; +} + + +int start_net( struct net_device *dev, struct usb_device *usb_dev ) +{ + __u16 partmedia, temp; + __u8 node_id[6]; + __u8 data[4]; + + if ( get_node_id(usb_dev, node_id) ) + return 1; + hpna_set_registers(usb_dev, 0x10, 6, node_id); + memcpy(dev->dev_addr, node_id, 6); + if ( read_phy_word(usb_dev, 1, &temp) ) + return 2; + if ( !(temp & 4) ) { + if ( loopback ) + goto ok; + err("link NOT established - %x", temp); + return 3; + } +ok: + if ( read_phy_word(usb_dev, 5, &partmedia) ) + return 4; + temp = partmedia; + partmedia &= 0x1f; + if ( partmedia != 1 ) { + err("party FAIL %x", temp); + return 5; + } + partmedia = temp; + if ( partmedia & 0x100 ) + data[1] = 0x30; + else { + if ( partmedia & 0x80 ) + data[1] = 0x10; + else + data[1] = 0; + } + + data[0] = 0xc9; + data[2] = (loopback & 1) ? 0x08 : 0x00; + + hpna_set_registers(usb_dev, 0, 3, data); + + return 0; +} + + +static void hpna_read_irq( purb_t urb ) +{ + struct net_device *net_dev = urb->context; + usb_hpna_t *hpna = net_dev->priv; + int count = urb->actual_length, res; + int rx_status = *(int *)(hpna->rx_buff + count - 4); + + + if ( urb->status ) { + info( "%s: RX status %d\n", net_dev->name, urb->status ); + goto goon; + } + + if ( !count ) + goto goon; +/* if ( rx_status & 0x00010000 ) + goto goon; +*/ + if ( rx_status & 0x000e0000 ) { + dbg("%s: error receiving packet %x", + net_dev->name, rx_status & 0xe0000); + hpna->stats.rx_errors++; + if(rx_status & 0x060000) hpna->stats.rx_length_errors++; + if(rx_status & 0x080000) hpna->stats.rx_crc_errors++; + if(rx_status & 0x100000) hpna->stats.rx_frame_errors++; + } else { + struct sk_buff *skb; + __u16 pkt_len = (rx_status & 0xfff) - 8; + + + if((skb = dev_alloc_skb(pkt_len+2)) != NULL ) { + skb->dev = net_dev; + skb_reserve(skb, 2); + eth_copy_and_sum(skb, hpna->rx_buff, pkt_len, 0); + skb_put(skb, pkt_len); + } else + goto goon; + skb->protocol = eth_type_trans(skb, net_dev); + netif_rx(skb); + hpna->stats.rx_packets++; + hpna->stats.rx_bytes += pkt_len; + } +goon: + if ( (res = usb_submit_urb( &hpna->rx_urb )) ) + warn("failed rx_urb %d", res); +} + + +static void hpna_irq( urb_t *urb) +{ + if( urb->status ) { + __u8 *d = urb->transfer_buffer; + printk("txst0 %x, txst1 %x, rxst %x, rxlst0 %x, rxlst1 %x, wakest %x", + d[0], d[1], d[2], d[3], d[4], d[5] ); + } +} + + +static void hpna_write_irq( purb_t urb ) +{ + struct net_device *net_dev = urb->context; + usb_hpna_t *hpna = net_dev->priv; + + + spin_lock( &hpna->hpna_lock ); + + if ( urb->status ) + info("%s: TX status %d\n", net_dev->name, urb->status); + netif_wake_queue( net_dev ); + + spin_unlock( &hpna->hpna_lock ); +} + + +static void tx_timeout( struct net_device *dev ) +{ + usb_hpna_t *hpna = dev->priv; + + warn( "%s: Tx timed out. Reseting...", dev->name ); + hpna->stats.tx_errors++; + dev->trans_start = jiffies; + netif_wake_queue( dev ); +} + + +static int hpna_start_xmit( struct sk_buff *skb, struct net_device *net_dev ) +{ + usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv; + int count = skb->len+2 % 64 ? skb->len+2 : skb->len+3; + int res; + + spin_lock( &hpna->hpna_lock ); + + netif_stop_queue( net_dev ); + ((__u16 *)hpna->tx_buff)[0] = skb->len; + memcpy(hpna->tx_buff+2, skb->data, skb->len); + (&hpna->tx_urb)->transfer_buffer_length = count; + if ( (res = usb_submit_urb( &hpna->tx_urb )) ) { + warn("failed tx_urb %d", res); + hpna->stats.tx_errors++; + netif_start_queue( net_dev ); + } else { + hpna->stats.tx_packets++; + hpna->stats.tx_bytes += skb->len; + net_dev->trans_start = jiffies; + } + dev_kfree_skb( skb ); + spin_unlock( &hpna->hpna_lock ); + return 0; +} + + +static struct net_device_stats *hpna_netdev_stats( struct net_device *dev ) +{ + return &((usb_hpna_t *)dev->priv)->stats; +} + +static int hpna_open( struct net_device *net_dev ) +{ + usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv; + int res; + + if ( hpna->active ) + return -EBUSY; + else + hpna->active = 1; + + if ( start_net(net_dev, hpna->usb_dev) ) { + err("can't start_net()"); + return -EIO; + } + + if ( (res = usb_submit_urb( &hpna->rx_urb )) ) + warn("failed rx_urb %d", res); + +/* usb_submit_urb( &hpna->intr_urb );*/ + netif_start_queue( net_dev ); + + MOD_INC_USE_COUNT; + + return 0; +} + + +static int hpna_close( struct net_device *net_dev ) +{ + usb_hpna_t *hpna = net_dev->priv; + + + netif_stop_queue( net_dev ); + + usb_unlink_urb( &hpna->rx_urb ); + usb_unlink_urb( &hpna->tx_urb ); +/* usb_unlink_urb( hpna->intr_urb );*/ + + hpna->active = 0; + + MOD_DEC_USE_COUNT; + + return 0; +} + + +static int hpna_ioctl( struct net_device *dev, struct ifreq *rq, int cmd ) +{ + __u16 *data = (__u16 *)&rq->ifr_data; + usb_hpna_t *hpna = dev->priv; + + switch( cmd ) { + case SIOCDEVPRIVATE: + data[0] = 1; + case SIOCDEVPRIVATE+1: + read_phy_word(hpna->usb_dev, data[1] & 0x1f, &data[3]); + return 0; + case SIOCDEVPRIVATE+2: + if ( !capable(CAP_NET_ADMIN) ) + return -EPERM; + write_phy_word(hpna->usb_dev, data[1] & 0x1f, data[2]); + return 0; + default: + return -EOPNOTSUPP; + } +} + + +static void set_rx_mode( struct net_device *net_dev ) +{ + usb_hpna_t *hpna=net_dev->priv; + + netif_stop_queue( net_dev ); + + if ( net_dev->flags & IFF_PROMISC ) { + info("%s: Promiscuous mode enabled", net_dev->name); + hpna_set_register( hpna->usb_dev, 2, 0x04 ); + } else if ((net_dev->mc_count > multicast_filter_limit) || + (net_dev->flags & IFF_ALLMULTI)) { + hpna_set_register(hpna->usb_dev, 0, 0xfa); + hpna_set_register(hpna->usb_dev, 2, 0); + } else { + dbg("%s: set Rx mode", net_dev->name); + } + + netif_wake_queue( net_dev ); +} + + +static void * usb_hpna_probe( struct usb_device *dev, unsigned int ifnum ) +{ + struct net_device *net_dev; + usb_hpna_t *hpna = &usb_dev_hpna; + + + + if ( dev->descriptor.idVendor != ADMTEK_VENDOR_ID || + dev->descriptor.idProduct != ADMTEK_HPNA_PEGASUS ) { + return NULL; + } + + printk("USB HPNA Pegasus found\n"); + + if ( usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { + err("usb_set_configuration() failed"); + return NULL; + } + + hpna->usb_dev = dev; + + hpna->rx_pipe = usb_rcvbulkpipe(hpna->usb_dev, 1); + hpna->tx_pipe = usb_sndbulkpipe(hpna->usb_dev, 2); + hpna->intr_pipe = usb_rcvintpipe(hpna->usb_dev, 0); + + if ( reset_mac(dev) ) { + err("can't reset MAC"); + } + + hpna->present = 1; + + if(!(hpna->rx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) { + err("not enough mem for out buff"); + return NULL; + } + if(!(hpna->tx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) { + kfree_s(hpna->rx_buff, MAX_MTU); + err("not enough mem for out buff"); + return NULL; + } + + net_dev = init_etherdev( 0, 0 ); + hpna->net_dev = net_dev; + net_dev->priv = hpna; + net_dev->open = hpna_open; + net_dev->stop = hpna_close; + net_dev->watchdog_timeo = TX_TIMEOUT; + net_dev->tx_timeout = tx_timeout; + net_dev->do_ioctl = hpna_ioctl; + net_dev->hard_start_xmit = hpna_start_xmit; + net_dev->set_multicast_list = set_rx_mode; + net_dev->get_stats = hpna_netdev_stats; + net_dev->mtu = HPNA_MTU; + hpna->hpna_lock = SPIN_LOCK_UNLOCKED; + + FILL_BULK_URB( &hpna->rx_urb, hpna->usb_dev, hpna->rx_pipe, + hpna->rx_buff, MAX_MTU, hpna_read_irq, net_dev ); + FILL_BULK_URB( &hpna->tx_urb, hpna->usb_dev, hpna->tx_pipe, + hpna->tx_buff, MAX_MTU, hpna_write_irq, net_dev ); + FILL_INT_URB( &hpna->intr_urb, hpna->usb_dev, hpna->intr_pipe, + hpna->intr_buff, 8, hpna_irq, net_dev, 250 ); + +/* list_add( &hpna->list, &hpna_list );*/ + + return net_dev; +} + + +static void usb_hpna_disconnect( struct usb_device *dev, void *ptr ) +{ + struct net_device *net_dev = ptr; + struct usb_hpna *hpna = net_dev->priv; + + + if ( net_dev->flags & IFF_UP ) + dev_close(net_dev); + + unregister_netdev( net_dev ); + + if ( !hpna ) /* should never happen */ + return; + + usb_unlink_urb( &hpna->rx_urb ); + usb_unlink_urb( &hpna->tx_urb ); +/* usb_unlink_urb( &hpna->intr_urb );*/ + kfree_s(hpna->rx_buff, MAX_MTU); + kfree_s(hpna->tx_buff, MAX_MTU); + + hpna->usb_dev = NULL; + hpna->present = 0; + + printk("USB HPNA disconnected\n"); +} + + +static struct usb_driver usb_hpna_driver = { + "ADMtek \"Pegasus\" USB Ethernet", + usb_hpna_probe, + usb_hpna_disconnect, + {NULL, NULL} +}; + + + +static int __init start_hpna( void ) +{ + printk( version ); + return usb_register( &usb_hpna_driver ); +} + + +static void __exit stop_hpna( void ) +{ + usb_deregister( &usb_hpna_driver ); +} + + +module_init( start_hpna ); +module_exit( stop_hpna ); diff --git a/drivers/usb/rio500.c b/drivers/usb/rio500.c new file mode 100644 index 000000000..e25ba03d3 --- /dev/null +++ b/drivers/usb/rio500.c @@ -0,0 +1,455 @@ +/* -*- linux-c -*- */ + +/* + * Driver for USB Rio 500 + * + * Cesar Miquel (miquel@df.uba.ar) + * + * based on hp_scanner.c by David E. Nelson (dnelson@jump.net) + * + * 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. + * + * 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. + * + * Based upon mouse.c (Brad Keryan) and printer.c (Michael Gee). + * + * */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usb.h" + +#include "rio500_usb.h" + +#define RIO_MINOR 64 + +/* stall/wait timeout for rio */ +#define NAK_TIMEOUT (HZ) + +#define IBUF_SIZE 128 + +/* Size of the rio buffer */ +#define OBUF_SIZE 0x10000 + +struct rio_usb_data { + struct usb_device *rio_dev; /* init: probe_rio */ + unsigned int ifnum; /* Interface number of the USB device */ + int isopen; /* nz if open */ + int present; /* Device is present on the bus */ + char *obuf, *ibuf; /* transfer buffers */ + char bulk_in_ep, bulk_out_ep; /* Endpoint assignments */ + wait_queue_head_t wait_q; /* for timeouts */ +}; + +static struct rio_usb_data rio_instance; + +static int open_rio(struct inode *inode, struct file *file) +{ + struct rio_usb_data *rio = &rio_instance; + + if (rio->isopen || !rio->present) { + return -EBUSY; + } + rio->isopen = 1; + + init_waitqueue_head(&rio->wait_q); + + MOD_INC_USE_COUNT; + + info("Rio opened."); + + return 0; +} + +static int close_rio(struct inode *inode, struct file *file) +{ + struct rio_usb_data *rio = &rio_instance; + + rio->isopen = 0; + + MOD_DEC_USE_COUNT; + + info("Rio closed."); + return 0; +} + +static int +ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct RioCommand rio_cmd; + struct rio_usb_data *rio = &rio_instance; + void *data; + unsigned char *buffer; + int result, requesttype; + int retries; + + /* Sanity check to make sure rio is connected, powered, etc */ + if ( rio == NULL || + rio->present == 0 || + rio->rio_dev == NULL ) + return -1; + + switch (cmd) { + case RIO_RECV_COMMAND: + data = (void *) arg; + if (data == NULL) + break; + copy_from_user_ret(&rio_cmd, data, sizeof(struct RioCommand), + -EFAULT); + if (rio_cmd.length > PAGE_SIZE) + return -EINVAL; + buffer = (unsigned char *) __get_free_page(GFP_KERNEL); + if (buffer == NULL) + return -ENOMEM; + copy_from_user_ret(buffer, rio_cmd.buffer, rio_cmd.length, + -EFAULT); + + requesttype = rio_cmd.requesttype | USB_DIR_IN | + USB_TYPE_VENDOR | USB_RECIP_DEVICE; + dbg + ("sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x", + requesttype, rio_cmd.request, rio_cmd.value, + rio_cmd.index, rio_cmd.length); + /* Send rio control message */ + retries = 3; + while (retries) { + result = usb_control_msg(rio->rio_dev, + usb_rcvctrlpipe(rio-> rio_dev, 0), + rio_cmd.request, + requesttype, + rio_cmd.value, + rio_cmd.index, buffer, + rio_cmd.length, + rio_cmd.timeout); + if (result == -ETIMEDOUT) + retries--; + else if (result < 0) { + err("Error executing ioctrl. code = %d", + le32_to_cpu(result)); + retries = 0; + } else { + dbg("Executed ioctl. Result = %d (data=%04x)", + le32_to_cpu(result), + le32_to_cpu(*((long *) buffer))); + copy_to_user_ret(rio_cmd.buffer, buffer, + rio_cmd.length, -EFAULT); + retries = 0; + } + + /* rio_cmd.buffer contains a raw stream of single byte + data which has been returned from rio. Data is + interpreted at application level. For data that + will be cast to data types longer than 1 byte, data + will be little_endian and will potentially need to + be swapped at the app level */ + + } + free_page((unsigned long) buffer); + break; + + case RIO_SEND_COMMAND: + data = (void *) arg; + if (data == NULL) + break; + copy_from_user_ret(&rio_cmd, data, sizeof(struct RioCommand), + -EFAULT); + if (rio_cmd.length > PAGE_SIZE) + return -EINVAL; + buffer = (unsigned char *) __get_free_page(GFP_KERNEL); + if (buffer == NULL) + return -ENOMEM; + copy_from_user_ret(buffer, rio_cmd.buffer, rio_cmd.length, + -EFAULT); + + requesttype = rio_cmd.requesttype | USB_DIR_OUT | + USB_TYPE_VENDOR | USB_RECIP_DEVICE; + dbg("sending command: reqtype=%0x req=%0x value=%0x index=%0x len=%0x", + requesttype, rio_cmd.request, rio_cmd.value, + rio_cmd.index, rio_cmd.length); + /* Send rio control message */ + retries = 3; + while (retries) { + result = usb_control_msg(rio->rio_dev, + usb_sndctrlpipe(rio-> rio_dev, 0), + rio_cmd.request, + requesttype, + rio_cmd.value, + rio_cmd.index, buffer, + rio_cmd.length, + rio_cmd.timeout); + if (result == -ETIMEDOUT) + retries--; + else if (result < 0) { + err("Error executing ioctrl. code = %d", + le32_to_cpu(result)); + retries = 0; + } else { + dbg("Executed ioctl. Result = %d", + le32_to_cpu(result)); + retries = 0; + + } + + } + free_page((unsigned long) buffer); + break; + + default: + return -ENOIOCTLCMD; + break; + } + + return 0; +} + +static ssize_t +write_rio(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + struct rio_usb_data *rio = &rio_instance; + + unsigned long copy_size; + unsigned long bytes_written = 0; + unsigned int partial; + + int result = 0; + int maxretry; + + /* Sanity check to make sure rio is connected, powered, etc */ + if ( rio == NULL || + rio->present == 0 || + rio->rio_dev == NULL ) + return -1; + + do { + unsigned long thistime; + char *obuf = rio->obuf; + + thistime = copy_size = + (count >= OBUF_SIZE) ? OBUF_SIZE : count; + if (copy_from_user(rio->obuf, buffer, copy_size)) + return -EFAULT; + maxretry = 5; + while (thistime) { + if (!rio->rio_dev) + return -ENODEV; + if (signal_pending(current)) { + return bytes_written ? bytes_written : -EINTR; + } + + result = usb_bulk_msg(rio->rio_dev, + usb_sndbulkpipe(rio->rio_dev, 2), + obuf, thistime, &partial, 5 * HZ); + + dbg("write stats: result:%d thistime:%lu partial:%u", + result, thistime, partial); + + if (result == USB_ST_TIMEOUT) { /* NAK - so hold for a while */ + if (!maxretry--) { + return -ETIME; + } + interruptible_sleep_on_timeout(&rio-> wait_q, NAK_TIMEOUT); + continue; + } else if (!result & partial) { + obuf += partial; + thistime -= partial; + } else + break; + }; + if (result) { + err("Write Whoops - %x", result); + return -EIO; + } + bytes_written += copy_size; + count -= copy_size; + buffer += copy_size; + } while (count > 0); + + return bytes_written ? bytes_written : -EIO; +} + +static ssize_t +read_rio(struct file *file, char *buffer, size_t count, loff_t * ppos) +{ + struct rio_usb_data *rio = &rio_instance; + ssize_t read_count; + unsigned int partial; + int this_read; + int result; + int maxretry = 10; + char *ibuf = rio->ibuf; + + /* Sanity check to make sure rio is connected, powered, etc */ + if ( rio == NULL || + rio->present == 0 || + rio->rio_dev == NULL ) + return -1; + + read_count = 0; + + while (count > 0) { + if (signal_pending(current)) { + return read_count ? read_count : -EINTR; + } + if (!rio->rio_dev) + return -ENODEV; + this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count; + + result = usb_bulk_msg(rio->rio_dev, + usb_rcvbulkpipe(rio->rio_dev, 1), + ibuf, this_read, &partial, + (int) (HZ * .1)); + + dbg(KERN_DEBUG "read stats: result:%d this_read:%u partial:%u", + result, this_read, partial); + + if (partial) { + count = this_read = partial; + } else if (result == USB_ST_TIMEOUT || result == 15) { /* FIXME: 15 ??? */ + if (!maxretry--) { + err("read_rio: maxretry timeout"); + return -ETIME; + } + interruptible_sleep_on_timeout(&rio->wait_q, + NAK_TIMEOUT); + continue; + } else if (result != USB_ST_DATAUNDERRUN) { + err("Read Whoops - result:%u partial:%u this_read:%u", + result, partial, this_read); + return -EIO; + } else { + return (0); + } + + if (this_read) { + if (copy_to_user(buffer, ibuf, this_read)) + return -EFAULT; + count -= this_read; + read_count += this_read; + buffer += this_read; + } + } + return read_count; +} + +static void *probe_rio(struct usb_device *dev, unsigned int ifnum) +{ + struct rio_usb_data *rio = &rio_instance; + + if (dev->descriptor.idVendor != 0x841) { + return NULL; + } + + if (dev->descriptor.idProduct != 0x1 /* RIO 500 */ ) { + warn(KERN_INFO "Rio player model not supported/tested."); + return NULL; + } + + info("USB Rio found at address %d", dev->devnum); + + rio->present = 1; + rio->rio_dev = dev; + + if (!(rio->obuf = (char *) kmalloc(OBUF_SIZE, GFP_KERNEL))) { + err("probe_rio: Not enough memory for the output buffer"); + return NULL; + } + dbg("probe_rio: obuf address:%p", rio->obuf); + + if (!(rio->ibuf = (char *) kmalloc(IBUF_SIZE, GFP_KERNEL))) { + err("probe_rio: Not enough memory for the input buffer"); + kfree(rio->obuf); + return NULL; + } + dbg("probe_rio: ibuf address:%p", rio->ibuf); + + return rio; +} + +static void disconnect_rio(struct usb_device *dev, void *ptr) +{ + struct rio_usb_data *rio = (struct rio_usb_data *) ptr; + + if (rio->isopen) { + rio->isopen = 0; + /* better let it finish - the release will do whats needed */ + rio->rio_dev = NULL; + return; + } + kfree(rio->ibuf); + kfree(rio->obuf); + + info("USB Rio disconnected."); + + rio->present = 0; +} + +static struct +file_operations usb_rio_fops = { + NULL, /* seek */ + read_rio, + write_rio, + NULL, /* readdir */ + NULL, /* poll */ + ioctl_rio, /* ioctl */ + NULL, /* mmap */ + open_rio, + NULL, /* flush */ + close_rio, + NULL, + NULL, /* fasync */ +}; + +static struct +usb_driver rio_driver = { + "rio500", + probe_rio, + disconnect_rio, + {NULL, NULL}, + &usb_rio_fops, + RIO_MINOR +}; + +int usb_rio_init(void) +{ + if (usb_register(&rio_driver) < 0) + return -1; + + info("USB Rio support registered."); + return 0; +} + + +void usb_rio_cleanup(void) +{ + struct rio_usb_data *rio = &rio_instance; + + rio->present = 0; + usb_deregister(&rio_driver); + + +} + +module_init(usb_rio_init); +module_exit(usb_rio_cleanup); + diff --git a/drivers/usb/rio500_usb.h b/drivers/usb/rio500_usb.h new file mode 100644 index 000000000..9fc712f3d --- /dev/null +++ b/drivers/usb/rio500_usb.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + + Copyright (C) 2000 Cesar Miquel (miquel@df.uba.ar) + + 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. + + 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. + + ---------------------------------------------------------------------- */ + + + +#define RIO_SEND_COMMAND 0x1 +#define RIO_RECV_COMMAND 0x2 + +#define RIO_DIR_OUT 0x0 +#define RIO_DIR_IN 0x1 + +struct RioCommand { + short length; + int request; + int requesttype; + int value; + int index; + void *buffer; + int timeout; +}; diff --git a/drivers/usb/wacom.c b/drivers/usb/wacom.c new file mode 100644 index 000000000..c67d30a60 --- /dev/null +++ b/drivers/usb/wacom.c @@ -0,0 +1,286 @@ +/* + * wacom.c Version 0.3 + * + * Copyright (c) 2000 Vojtech Pavlik + * Copyright (c) 2000 Andreas Bach Aaen + * Copyright (c) 2000 Clifford Wolf + * + * USB Wacom Graphire and Wacom Intuos tablet support + * + * Sponsored by SuSE + * + * ChangeLog: + * v0.1 (vp) - Initial release + * v0.2 (aba) - Support for all buttons / combinations + * v0.3 (vp) - Support for Intuos added + */ + +/* + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include "usb.h" + +MODULE_AUTHOR("Vojtech Pavlik "); + +/* + * Wacom Graphire packet: + * + * The input report: + * + * byte 0: report ID (2) + * byte 1: bit7 mouse/pen/rubber near + * bit5-6 0 - pen, 1 - rubber, 2 - mouse + * bit4 1 ? + * bit3 0 ? + * bit2 mouse middle button / pen button2 + * bit1 mouse right button / pen button1 + * bit0 mouse left button / pen tip / rubber + * byte 2: X low bits + * byte 3: X high bits + * byte 4: Y low bits + * byte 5: Y high bits + * byte 6: pen pressure low bits / mouse wheel + * byte 7: pen presure high bits / mouse distance + * + * There are also two single-byte feature reports (2 and 3). + * + * Resolution: + * X: 0 - 10206 + * Y: 0 - 7422 + * + * (0,0) is upper left corner + * + * Wacom Intuos packet: + * + * byte 0: report ID (2) + * byte 1: bit7 1 ? + * bit6 tilt (pressure?) data valid + * bit5 near + * bit4 0 ? + * bit3 0 ? + * bit2 pen button + * bit1 first packet (contains other infos) + * bit0 0 ? + * byte 2: X high bits + * byte 3: X low bits + * byte 4: Y high bits + * byte 5: Y low bits + * byte 6: bits 0-7: pressure (bits 2-9) + * byte 7: bits 6-7: pressure (bits 0-1) + * byte 7: bits 0-5: X tilt (bits 1-6) + * byte 8: bit 7: X tilt (bit 0) + * byte 8: bits 0-6: Y tilt (bits 0-6) + * byte 9: ? + */ + +#define USB_VENDOR_ID_WACOM 0x056a +#define USB_DEVICE_ID_WACOM_GRAPHIRE 0x0010 +#define USB_DEVICE_ID_WACOM_INTUOS 0x0021 + +struct wacom { + signed char data[12]; + struct input_dev dev; + struct urb irq; +}; + +static void wacom_graphire_irq(struct urb *urb) +{ + struct wacom *wacom = urb->context; + unsigned char *data = wacom->data; + struct input_dev *dev = &wacom->dev; + + if (urb->status) return; + + if (data[0] != 2) + dbg("received unknown report #%d", data[0]); + + if ( data[1] & 0x80 ) { + input_report_abs(dev, ABS_X, data[2] | ((__u32)data[3] << 8)); + input_report_abs(dev, ABS_Y, 7422 - (data[4] | ((__u32)data[5] << 8))); + } + + switch ((data[1] >> 5) & 3) { + + case 0: /* Pen */ + input_report_btn(dev, BTN_TOOL_PEN, data[1] & 0x80); + input_report_btn(dev, BTN_TOUCH, data[1] & 0x01); + input_report_btn(dev, BTN_STYLUS, data[1] & 0x02); + input_report_btn(dev, BTN_STYLUS2, data[1] & 0x04); + input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8)); + break; + + case 1: /* Rubber */ + input_report_btn(dev, BTN_TOOL_RUBBER, data[1] & 0x80); + input_report_btn(dev, BTN_TOUCH, data[1] & 0x01); + input_report_btn(dev, BTN_STYLUS, data[1] & 0x02); + input_report_btn(dev, BTN_STYLUS2, data[1] & 0x04); + input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8)); + break; + + case 2: /* Mouse */ + input_report_btn(dev, BTN_TOOL_MOUSE, data[7] > 24); + input_report_btn(dev, BTN_LEFT, data[1] & 0x01); + input_report_btn(dev, BTN_RIGHT, data[1] & 0x02); + input_report_btn(dev, BTN_MIDDLE, data[1] & 0x04); + input_report_abs(dev, ABS_DISTANCE, data[7]); + input_report_rel(dev, REL_WHEEL, (signed char) data[6]); + break; + } +} + +static void wacom_intuos_irq(struct urb *urb) +{ + struct wacom *wacom = urb->context; + unsigned char *data = wacom->data; + struct input_dev *dev = &wacom->dev; + unsigned int t; + + if (urb->status) return; + + if (data[0] != 2) + dbg("received unknown report #%d", data[0]); + + if (data[1] & 0x02) /* First record, weird data */ + return; + + if (data[1] & 0x20) { /* Near */ + input_report_abs(dev, ABS_X, (((unsigned int) data[2]) << 8) | data[3]); + input_report_abs(dev, ABS_Y, 16240 - ((((unsigned int) data[4]) << 8) | data[5])); + } + + input_report_btn(dev, BTN_TOOL_PEN, data[1] & 0x20); + input_report_btn(dev, BTN_STYLUS, data[1] & 0x04); + + t = (((unsigned int) data[6]) << 2) | ((data[7] & 0xC0) >> 6); + + input_report_btn(dev, BTN_TOUCH, t > 10); + input_report_abs(dev, ABS_PRESSURE, t); + + if (data[1] & 0x40) { /* Tilt data */ + input_report_abs(dev, ABS_TILT_X, ((((unsigned int) data[7]) & 0x3f) << 1) | ((data[8] & 0x80) >> 7)); + input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f); + } +} + +static void *wacom_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_endpoint_descriptor *endpoint; + struct wacom *wacom; + char *name; + + if (dev->descriptor.idVendor != USB_VENDOR_ID_WACOM || + (dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_GRAPHIRE && + dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_INTUOS)) + return NULL; + + endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; + + if (!(wacom = kmalloc(sizeof(struct wacom), GFP_KERNEL))) return NULL; + memset(wacom, 0, sizeof(struct wacom)); + + switch (dev->descriptor.idProduct) { + + case USB_DEVICE_ID_WACOM_GRAPHIRE: + + wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); + wacom->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE); + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2); + wacom->dev.relbit[0] |= BIT(REL_WHEEL); + wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE); + + wacom->dev.absmax[ABS_X] = 10206; + wacom->dev.absmax[ABS_Y] = 7422; + wacom->dev.absmax[ABS_PRESSURE] = 511; + wacom->dev.absmax[ABS_DISTANCE] = 32; + + FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), + wacom->data, 8, wacom_graphire_irq, wacom, endpoint->bInterval); + + name = "Graphire"; + break; + + case USB_DEVICE_ID_WACOM_INTUOS: + + wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS); + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS); + wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); + wacom->dev.absbit[0] |= BIT(ABS_TILT_X) | BIT(ABS_TILT_Y); + + wacom->dev.absmax[ABS_X] = 20320; + wacom->dev.absmax[ABS_Y] = 16240; + wacom->dev.absmax[ABS_PRESSURE] = 1024; + wacom->dev.absmax[ABS_TILT_X] = 127; + wacom->dev.absmax[ABS_TILT_Y] = 127; + + FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), + wacom->data, 8, wacom_intuos_irq, wacom, endpoint->bInterval); + + name = "Intuos"; + break; + + default: + return NULL; + } + + if (usb_submit_urb(&wacom->irq)) { + kfree(wacom); + return NULL; + } + + input_register_device(&wacom->dev); + + printk(KERN_INFO "input%d: Wacom %s\n", wacom->dev.number, name); + + return wacom; +} + +static void wacom_disconnect(struct usb_device *dev, void *ptr) +{ + struct wacom *wacom = ptr; + usb_unlink_urb(&wacom->irq); + input_unregister_device(&wacom->dev); + kfree(wacom); +} + +static struct usb_driver wacom_driver = { + name: "wacom", + probe: wacom_probe, + disconnect: wacom_disconnect, +}; + +static int __init wacom_init(void) +{ + usb_register(&wacom_driver); + return 0; +} + +static void __exit wacom_exit(void) +{ + usb_deregister(&wacom_driver); +} + +module_init(wacom_init); +module_exit(wacom_exit); diff --git a/drivers/video/riva/Makefile b/drivers/video/riva/Makefile new file mode 100644 index 000000000..87502e25c --- /dev/null +++ b/drivers/video/riva/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the Riva framebuffer driver +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := rivafb.o +O_OBJS := fbdev.o riva_hw.o +M_OBJS := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c new file mode 100644 index 000000000..4e8c4e9d4 --- /dev/null +++ b/drivers/video/riva/fbdev.c @@ -0,0 +1,1705 @@ +/* + * linux/drivers/video/rivafb.c - nVidia RIVA 128/TNT/TNT2 fb driver + * + * Copyright 1999 Jeff Garzik + * + * Contributors: + * + * Ani Joshi: Lots of debugging and cleanup work, really helped + * get the driver going + * + * Ferenc Bakonyi: Bug fixes, cleanup, modularization + * + * Initial template from skeletonfb.c, created 28 Dec 1997 by Geert Uytterhoeven + * Includes riva_hw.c from nVidia, see copyright below. + * KGI code provided the basis for state storage, init, and mode switching. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file README.legal in the main directory of this archive + * for more details. + */ + +/* version number of this driver */ +#define RIVAFB_VERSION "0.7.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include