From c35f2ad2af82c8f614a85db687724e15a83becdb Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:39:35 -0500 Subject: [PATCH] Linux 2.4.0-test10pre5 - Mikael Pettersson: more Pentium IV cleanup. - David Miller: non-x86 platforms missed "pte_same()". - Russell King: NFS invalidate_inode_pages() can do bad things! - Randy Dunlap: usb-core.c is gone - module fix - Ben LaHaise: swapcache fixups for the new atomic pte update code - Oleg Drokin: fix nm256_audio memory region confusion - Randy Dunlap: USB printer fixes - David Miller: sparc updates - David Miller: off-by-one error in /proc socket dumper - David Miller: restore non-local bind() behaviour. - David Miller: wakeups on socket shutdown() - Jeff Garzik: DEPCA net drvr fixes and CodingStyle - Jeff Garzik: netsemi net drvr fix - Jeff Garzik & Andrea Arkangeli: keyboard cleanup - Jeff Garzik: VIA audio update - Andrea Arkangeli: mxcsr initialization cleanup and fix - Gabriel Paubert: better twd_i387_to_fxsr() emulation - Andries Brouwer: proper error return in ext2 mkdir() --- Documentation/DocBook/via-audio.tmpl | 184 +- arch/i386/kernel/i387.c | 39 +- arch/i386/kernel/microcode.c | 2 +- arch/i386/kernel/setup.c | 6 +- arch/i386/kernel/traps.c | 6 +- arch/i386/mm/init.c | 47 +- arch/sparc/mm/init.c | 2 +- arch/sparc64/defconfig | 1 + arch/sparc64/kernel/ioctl32.c | 15 +- arch/sparc64/mm/init.c | 2 +- arch/sparc64/solaris/socksys.c | 2 +- drivers/char/pc_keyb.c | 20 +- drivers/char/q40_keyb.c | 7 +- drivers/net/depca.c | 532 +++--- drivers/net/natsemi.c | 16 +- drivers/sbus/audio/audio.c | 2 +- drivers/sbus/char/envctrl.c | 2112 ++++++++-------------- drivers/sound/nm256_audio.c | 6 +- drivers/sound/via82cxxx_audio.c | 2443 ++++++++++++++++---------- drivers/usb/Makefile | 2 +- drivers/usb/printer.c | 67 +- drivers/usb/usb-core.c | 53 - drivers/usb/usb.c | 30 + drivers/video/mdacon.c | 1 - fs/ext2/namei.c | 2 +- include/asm-generic/pgtable.h | 2 + include/asm-i386/bugs.h | 2 +- include/asm-i386/elf.h | 2 +- include/asm-i386/i387.h | 1 + include/asm-i386/user.h | 4 +- include/asm-sparc/pgtable.h | 2 +- include/asm-sparc64/envctrl.h | 92 +- include/asm-sparc64/pgtable.h | 2 +- include/linux/mm.h | 1 + include/linux/sysctl.h | 1 + mm/filemap.c | 8 + mm/vmscan.c | 20 +- net/ipv4/af_inet.c | 28 +- net/ipv4/raw.c | 4 +- net/ipv4/sysctl_net_ipv4.c | 8 +- net/ipv4/tcp_ipv4.c | 8 +- net/ipv4/udp.c | 4 +- net/ipv6/af_inet6.c | 2 +- net/ipv6/raw.c | 4 +- net/ipv6/tcp_ipv6.c | 8 +- net/ipv6/udp.c | 4 +- net/packet/af_packet.c | 2 +- net/unix/af_unix.c | 2 +- 48 files changed, 3022 insertions(+), 2788 deletions(-) delete mode 100644 drivers/usb/usb-core.c diff --git a/Documentation/DocBook/via-audio.tmpl b/Documentation/DocBook/via-audio.tmpl index f1fc19a5d8aa..6c0677322d8c 100644 --- a/Documentation/DocBook/via-audio.tmpl +++ b/Documentation/DocBook/via-audio.tmpl @@ -56,15 +56,16 @@ Introduction - The Via VT82C686A and VT82C686A "super southbridge" chips contain - AC97-compatible audio logic which features dual full-duplex 16-bit stereo - PCM sound channels, plus a third PCM channel intended for use + The Via VT82C686A "super southbridge" chips contain + AC97-compatible audio logic which features dual 16-bit stereo + PCM sound channels (full duplex), plus a third PCM channel intended for use in hardware-assisted FM synthesis. The current Linux kernel audio driver for this family of chips - supports audio playback, but recording and hardware-assisted - FM support features are not yet available. + supports audio playback and recording, but hardware-assisted + FM features, and hardware buffer direct-access (mmap) + support are not yet available. This driver supports any Linux kernel version after 2.3.50. @@ -148,13 +149,6 @@ Known Bugs And Assumptions - Recording support - - - Recording support is currently missing. - - - MMAP support @@ -188,17 +182,6 @@ - Broken apps - - - Applications which attempt to open the sound device in read/write - mode (O_RDWR) will fail. This is incorrect OSS behavior, but since - this driver will eventually support recording as well as playback, - we will be able to (in the future) support even broken programs which - unconditionally use O_RDWR. - - - @@ -221,8 +204,8 @@ Random Notes Two /proc pseudo-files provide diagnostic information. This is generally - not useful to most users. Power users can disable VIA_PROC_FS macro in the - driver source code, and remove the /proc support code. In any case, once + not useful to most users. Power users can disable CONFIG_SOUND_VIA82CXXX_PROCFS, + and remove the /proc support code. Once version 2.0.0 is released, the /proc support code will be disabled by default. Available /proc pseudo-files: @@ -235,16 +218,6 @@ a vendor id of 0x1106, and a device id of 0x3058. Subsystem vendor and device ids are not examined. - - Only supports a single sound chip, as this is a motherboard chipset. - Some architecture remains for multiple cards, feel free to submit - a patch to clean some of that up. - - - No consideration for SMP, this chipset is not known to be found on - any SMP motherboards. However, spin_locks must be used anyway in order - to handle interrupts correctly. - GNU indent formatting options: -kr -i8 -pcs @@ -265,6 +238,147 @@ Driver ChangeLog + +Version 1.1.12 + + + + + mmap bug fixes from Linus. + + + + + + +Version 1.1.11 + + + + + Many more bug fixes. mmap enabled by default, but may still be buggy. + + + + + + Uses new and spiffy method of mmap'ing the DMA buffer, based + on a suggestion from Linus. + + + + + + +Version 1.1.10 + + + + + Many bug fixes. mmap enabled by default, but may still be buggy. + + + + + + +Version 1.1.9 + + + + + Redesign and rewrite audio playback implementation. (faster and smaller, hopefully) + + + + + + Implement recording and full duplex (DSP_CAP_DUPLEX) support. + + + + + + Make procfs support optional. + + + + + + Quick interrupt status check, to lessen overhead in interrupt + sharing situations. + + + + + + Add mmap(2) support. Disabled for now, it is still buggy and experimental. + + + + + + Surround all syscalls with a semaphore for cheap and easy SMP protection. + + + + + + Fix bug in channel shutdown (hardware channel reset) code. + + + + + + Remove unnecessary spinlocks (better performance). + + + + + + Eliminate "unknown AFMT" message by using a different method + of selecting the best AFMT_xxx sound sample format for use. + + + + + + Support for realtime hardware pointer position reporting + (DSP_CAP_REALTIME, SNDCTL_DSP_GETxPTR ioctls) + + + + + + Support for capture/playback triggering + (DSP_CAP_TRIGGER, SNDCTL_DSP_SETTRIGGER ioctls) + + + + + + SNDCTL_DSP_SETDUPLEX and SNDCTL_DSP_POST ioctls now handled. + + + + + + Rewrite open(2) and close(2) logic to allow only one user at + a time. All other open(2) attempts will sleep until they succeed. + FIXME: open(O_RDONLY) and open(O_WRONLY) should be allowed to succeed. + + + + + + Reviewed code to ensure that SMP and multiple audio devices + are fully supported. + + + + + + Version 1.1.8 diff --git a/arch/i386/kernel/i387.c b/arch/i386/kernel/i387.c index 5044c7cadb37..c3d052e8a2e1 100644 --- a/arch/i386/kernel/i387.c +++ b/arch/i386/kernel/i387.c @@ -32,6 +32,21 @@ #define HAVE_HWFP 1 #endif +/* + * The _current_ task is using the FPU for the first time + * so initialize it and set the mxcsr to its default + * value at reset if we support FXSR and then + * remeber the current task has used the FPU. + */ +void init_fpu(void) +{ + __asm__("fninit"); + if ( HAVE_FXSR ) + load_mxcsr(0x1f80); + + current->used_math = 1; +} + /* * FPU lazy state save handling. */ @@ -66,16 +81,16 @@ void restore_fpu( struct task_struct *tsk ) static inline unsigned short twd_i387_to_fxsr( unsigned short twd ) { - unsigned short ret = 0; - int i; - - for ( i = 0 ; i < 8 ; i++ ) { - if ( (twd & 0x3) != 0x3 ) { - ret |= (1 << i); - } - twd = twd >> 2; - } - return ret; + unsigned int tmp; /* to avoid 16 bit prefixes in the code */ + + /* Transform each pair of bits into 01 (valid) or 00 (empty) */ + tmp = ~twd; + tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ + /* and move the valid bits to the lower byte. */ + tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ + tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ + tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ + return tmp; } static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave ) @@ -92,8 +107,8 @@ static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave if ( twd & 0x1 ) { st = (struct _fpxreg *) FPREG_ADDR( fxsave, i ); - switch ( st->exponent ) { - case 0xffff: + switch ( st->exponent & 0x7fff ) { + case 0x7fff: tag = 2; /* Special */ break; case 0x0000: diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index 80536a1c4deb..f0e3ae633122 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c @@ -177,7 +177,7 @@ static void do_update_one(void *unused) req->err = 1; /* assume the worst */ - if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6){ + if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 != 6){ printk(KERN_ERR "microcode: CPU%d not an Intel P6\n", cpu_num); return; } diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 9059adcbcb4a..7ac7fdb366f7 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -1549,7 +1549,7 @@ void __init identify_cpu(struct cpuinfo_x86 *c) /* Pentium IV. */ if (c->x86 == 15) { get_model_name(c); - goto name_decoded; + return; } /* Names for the Pentium II/Celeron processors @@ -1688,12 +1688,12 @@ int get_cpuinfo(char * buffer) #endif p += sprintf(p,"processor\t: %d\n" "vendor_id\t: %s\n" - "cpu family\t: %c\n" + "cpu family\t: %d\n" "model\t\t: %d\n" "model name\t: %s\n", n, c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown", - c->x86 + '0', + c->x86, c->x86_model, c->x86_model_id[0] ? c->x86_model_id : "unknown"); diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 953c23d5504c..ae87ded927f5 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -741,11 +741,7 @@ asmlinkage void math_state_restore(struct pt_regs regs) if (current->used_math) { restore_fpu(current); } else { - /* - * Our first FPU usage, clean the chip. - */ - __asm__("fninit"); - current->used_math = 1; + init_fpu(); } current->flags |= PF_USEDFPU; /* So we fnsave on switch_to() */ } diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index 4b3cc4dfc3b6..39a6ce0f808d 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -495,28 +495,7 @@ void __init paging_init(void) * This function cannot be __init, since exceptions don't work in that * section. */ -static int do_test_wp_bit(unsigned long vaddr) -{ - char tmp_reg; - int flag; - - __asm__ __volatile__( - " movb %0,%1 \n" - "1: movb %1,%0 \n" - " xorl %2,%2 \n" - "2: \n" - ".section __ex_table,\"a\"\n" - " .align 4 \n" - " .long 1b,2b \n" - ".previous \n" - :"=m" (*(char *) vaddr), - "=q" (tmp_reg), - "=r" (flag) - :"2" (1) - :"memory"); - - return flag; -} +static int do_test_wp_bit(unsigned long vaddr); void __init test_wp_bit(void) { @@ -652,6 +631,30 @@ void __init mem_init(void) } +/* Put this after the callers, so that it cannot be inlined */ +static int do_test_wp_bit(unsigned long vaddr) +{ + char tmp_reg; + int flag; + + __asm__ __volatile__( + " movb %0,%1 \n" + "1: movb %1,%0 \n" + " xorl %2,%2 \n" + "2: \n" + ".section __ex_table,\"a\"\n" + " .align 4 \n" + " .long 1b,2b \n" + ".previous \n" + :"=m" (*(char *) vaddr), + "=q" (tmp_reg), + "=r" (flag) + :"2" (1) + :"memory"); + + return flag; +} + void free_initmem(void) { unsigned long addr; diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c index 457d4fed03b1..9cbbb1c9c9c9 100644 --- a/arch/sparc/mm/init.c +++ b/arch/sparc/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.93 2000/08/31 11:40:55 anton Exp $ +/* $Id: init.c,v 1.94 2000/10/19 00:49:51 davem Exp $ * linux/arch/sparc/mm/init.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index 0637e5bbdb9d..571038083206 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -264,6 +264,7 @@ CONFIG_BLK_DEV_NS87415=y # CONFIG_BLK_DEV_PDC202XX is not set # CONFIG_PDC202XX_BURST is not set # CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set # CONFIG_BLK_DEV_TRM290 is not set # CONFIG_BLK_DEV_VIA82CXXX is not set # CONFIG_IDE_CHIPSETS is not set diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index 84b4224e54c6..9f7cd59e9057 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.98 2000/08/16 12:33:00 davem Exp $ +/* $Id: ioctl32.c,v 1.99 2000/10/17 16:20:33 davem Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) @@ -3221,9 +3221,16 @@ COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+7, int)) /* Little p (/dev/rtc, /dev/envctrl, etc.) */ COMPATIBLE_IOCTL(RTCGET) COMPATIBLE_IOCTL(RTCSET) -COMPATIBLE_IOCTL(I2CIOCSADR) -COMPATIBLE_IOCTL(I2CIOCGADR) -COMPATIBLE_IOCTL(D7SIOCRD) +COMPATIBLE_IOCTL(ENVCTRL_RD_WARNING_TEMPERATURE) +COMPATIBLE_IOCTL(ENVCTRL_RD_SHUTDOWN_TEMPERATURE) +COMPATIBLE_IOCTL(ENVCTRL_RD_CPU_TEMPERATURE) +COMPATIBLE_IOCTL(ENVCTRL_RD_FAN_STATUS) +COMPATIBLE_IOCTL(ENVCTRL_RD_VOLTAGE_STATUS) +COMPATIBLE_IOCTL(ENVCTRL_RD_SCSI_TEMPERATURE) +COMPATIBLE_IOCTL(ENVCTRL_RD_ETHERNET_TEMPERATURE) +COMPATIBLE_IOCTL(ENVCTRL_RD_MTHRBD_TEMPERATURE) +COMPATIBLE_IOCTL(ENVCTRL_RD_CPU_VOLTAGE) +/* COMPATIBLE_IOCTL(D7SIOCRD) same value as ENVCTRL_RD_VOLTAGE_STATUS */ COMPATIBLE_IOCTL(D7SIOCWR) COMPATIBLE_IOCTL(D7SIOCTM) /* Little m */ diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index eafbe27cae47..65fbd6e3762d 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.156 2000/09/21 06:34:48 anton Exp $ +/* $Id: init.c,v 1.157 2000/10/19 00:49:52 davem Exp $ * arch/sparc64/mm/init.c * * Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc64/solaris/socksys.c b/arch/sparc64/solaris/socksys.c index 2ad56ad9d052..bf2182381ec3 100644 --- a/arch/sparc64/solaris/socksys.c +++ b/arch/sparc64/solaris/socksys.c @@ -1,4 +1,4 @@ -/* $Id: socksys.c,v 1.16 2000/07/27 00:03:48 davem Exp $ +/* $Id: socksys.c,v 1.17 2000/10/19 00:49:53 davem Exp $ * socksys.c: /dev/inet/ stuff for Solaris emulation. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index 117ae4ec1100..4a77583256a8 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -65,7 +65,7 @@ static void aux_write_ack(int val); static void __aux_write_ack(int val); #endif -spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; static unsigned char handle_kbd_event(void); /* used only by send_data - set by keyboard_interrupt */ @@ -448,7 +448,7 @@ static unsigned char handle_kbd_event(void) unsigned char status = kbd_read_status(); unsigned int work = 10000; - while (status & KBD_STAT_OBF) { + while ((--work > 0) && (status & KBD_STAT_OBF)) { unsigned char scancode; scancode = kbd_read_input(); @@ -467,13 +467,10 @@ static unsigned char handle_kbd_event(void) } status = kbd_read_status(); - - if (!--work) { - printk(KERN_ERR "pc_keyb: controller jammed (0x%02X).\n", - status); - break; - } } + + if (!work) + printk(KERN_ERR "pc_keyb: controller jammed (0x%02X).\n", status); return status; } @@ -481,14 +478,13 @@ static unsigned char handle_kbd_event(void) static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - unsigned long flags; - #ifdef CONFIG_VT kbd_pt_regs = regs; #endif - spin_lock_irqsave(&kbd_controller_lock, flags); + + spin_lock_irq(&kbd_controller_lock); handle_kbd_event(); - spin_unlock_irqrestore(&kbd_controller_lock, flags); + spin_unlock_irq(&kbd_controller_lock); } /* diff --git a/drivers/char/q40_keyb.c b/drivers/char/q40_keyb.c index 7ee9818efbdf..a0e41b189130 100644 --- a/drivers/char/q40_keyb.c +++ b/drivers/char/q40_keyb.c @@ -94,7 +94,7 @@ static unsigned char q40ecl[]= }; -spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; /* @@ -340,11 +340,10 @@ static int qprev=0; static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - unsigned long flags; unsigned char status; disable_keyboard(); - spin_lock_irqsave(&kbd_controller_lock, flags); + spin_lock(&kbd_controller_lock); kbd_pt_regs = regs; status = IRQ_KEYB_MASK & master_inb(INTERRUPT_REG); @@ -386,7 +385,7 @@ static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) keyup=1; } exit: - spin_unlock_irqrestore(&kbd_controller_lock, flags); + spin_unlock(&kbd_controller_lock); master_outb(-1,KEYBOARD_UNLOCK_REG); /* keyb ints reenabled herewith */ enable_keyboard(); } diff --git a/drivers/net/depca.c b/drivers/net/depca.c index eb78e1e0ebc0..448baa8007d7 100644 --- a/drivers/net/depca.c +++ b/drivers/net/depca.c @@ -223,6 +223,9 @@ 0.5 14-Nov-98 Re-spin for 2.1.x kernels. 0.51 27-Jun-99 Correct received packet length for CRC from report by + 0.52 16-Oct-00 Fixes for 2.3 io memory accesses + Fix show-stopper (ints left masked) in depca_interrupt + by ========================================================================= */ @@ -378,14 +381,21 @@ struct depca_private { char devname[DEPCA_STRLEN]; /* Device Product String */ char adapter_name[DEPCA_STRLEN];/* /proc/ioports string */ char adapter; /* Adapter type */ - char mca_slot; /* MCA slot, if MCA else -1 */ struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */ - struct depca_tx_desc *tx_ring; /* Pointer to start of TX descriptor ring */ + char mca_slot; /* MCA slot, if MCA else -1 */ struct depca_init init_block;/* Shadow Initialization block */ - char *rx_memcpy[NUM_RX_DESC]; /* CPU virt address of sh'd memory buffs */ - char *tx_memcpy[NUM_TX_DESC]; /* CPU virt address of sh'd memory buffs */ - u_long bus_offset; /* (E)ISA bus address offset vs LANCE */ - u_long sh_mem; /* Physical start addr of shared mem area */ - u_long dma_buffs; /* LANCE Rx and Tx buffers start address. */ +/* CPU address space fields */ + struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */ + struct depca_tx_desc *tx_ring; /* Pointer to start of TX descriptor ring */ + void *rx_buff[NUM_RX_DESC]; /* CPU virt address of sh'd memory buffs */ + void *tx_buff[NUM_TX_DESC]; /* CPU virt address of sh'd memory buffs */ + void *sh_mem; /* CPU mapped virt address of device RAM */ +/* Device address space fields */ + u_long device_ram_start; /* Start of RAM in device addr space */ +/* Offsets used in both address spaces */ + u_long rx_ring_offset; /* Offset from start of RAM to rx_ring */ + u_long tx_ring_offset; /* Offset from start of RAM to tx_ring */ + u_long buffs_offset; /* LANCE Rx and Tx buffers start address. */ +/* Kernel-only (not device) fields */ int rx_new, tx_new; /* The next free ring entry */ int rx_old, tx_old; /* The ring entries to be free()ed. */ struct net_device_stats stats; @@ -517,215 +527,228 @@ depca_probe(struct net_device *dev) static int __init depca_hw_init(struct net_device *dev, u_long ioaddr, int mca_slot) { - struct depca_private *lp; - int i, j, offset, netRAM, mem_len, status=0; - s16 nicsr; - u_long mem_start=0, mem_base[] = DEPCA_RAM_BASE_ADDRESSES; + struct depca_private *lp; + int i, j, offset, netRAM, mem_len, status=0; + s16 nicsr; + u_long mem_start=0, mem_base[] = DEPCA_RAM_BASE_ADDRESSES; - STOP_DEPCA; + STOP_DEPCA; - nicsr = inb(DEPCA_NICSR); - nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM); - outb(nicsr, DEPCA_NICSR); - - if (inw(DEPCA_DATA) == STOP) { - do { - strcpy(name, (adapter_name ? adapter_name : "")); - mem_start = (mem ? mem & 0xf0000 : mem_base[mem_chkd++]); - DepcaSignature(name, mem_start); - } while (!mem && mem_base[mem_chkd] && (adapter == unknown)); - - if ((adapter != unknown) && mem_start) { /* found a DEPCA device */ - dev->base_addr = ioaddr; - - if (mca_slot != -1) { - printk("%s: %s at 0x%04lx (MCA slot %d)", dev->name, name, - ioaddr, mca_slot); - } else if ((ioaddr & 0x0fff) == DEPCA_EISA_IO_PORTS) { /* EISA slot address */ - printk("%s: %s at 0x%04lx (EISA slot %d)", - dev->name, name, ioaddr, (int)((ioaddr>>12)&0x0f)); - } else { /* ISA port address */ - printk("%s: %s at 0x%04lx", dev->name, name, ioaddr); - } + nicsr = inb(DEPCA_NICSR); + nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM); + outb(nicsr, DEPCA_NICSR); - printk(", h/w address "); - status = get_hw_addr(dev); - for (i=0; idev_addr[i]); - } - printk("%2.2x", dev->dev_addr[i]); + if (inw(DEPCA_DATA) != STOP) { + return -ENXIO; + } + + do { + strcpy(name, (adapter_name ? adapter_name : "")); + mem_start = (mem ? mem & 0xf0000 : mem_base[mem_chkd++]); + DepcaSignature(name, mem_start); + } while (!mem && mem_base[mem_chkd] && (adapter == unknown)); + + if ((adapter == unknown) || !mem_start) { /* DEPCA device not found */ + return -ENXIO; + } + + dev->base_addr = ioaddr; + + if (mca_slot != -1) { + printk("%s: %s at 0x%04lx (MCA slot %d)", dev->name, name, + ioaddr, mca_slot); + } else if ((ioaddr & 0x0fff) == DEPCA_EISA_IO_PORTS) { /* EISA slot address */ + printk("%s: %s at 0x%04lx (EISA slot %d)", + dev->name, name, ioaddr, (int)((ioaddr>>12)&0x0f)); + } else { /* ISA port address */ + printk("%s: %s at 0x%04lx", dev->name, name, ioaddr); + } + + printk(", h/w address "); + status = get_hw_addr(dev); + for (i=0; idev_addr[i]); + } + printk("%2.2x", dev->dev_addr[i]); + + if (status != 0) { + printk(" which has an Ethernet PROM CRC error.\n"); + return -ENXIO; + } - if (status == 0) { /* Set up the maximum amount of network RAM(kB) */ netRAM = ((adapter != DEPCA) ? 64 : 48); - if ((nicsr & _128KB) && (adapter == de422)) netRAM = 128; + if ((nicsr & _128KB) && (adapter == de422)) + netRAM = 128; offset = 0x0000; /* Shared Memory Base Address */ if (nicsr & BUF) { - offset = 0x8000; /* 32kbyte RAM offset*/ - nicsr &= ~BS; /* DEPCA RAM in top 32k */ - netRAM -= 32; + offset = 0x8000; /* 32kbyte RAM offset*/ + nicsr &= ~BS; /* DEPCA RAM in top 32k */ + netRAM -= 32; } mem_start += offset; /* (E)ISA start address */ if ((mem_len = (NUM_RX_DESC*(sizeof(struct depca_rx_desc)+RX_BUFF_SZ) + NUM_TX_DESC*(sizeof(struct depca_tx_desc)+TX_BUFF_SZ) + - sizeof(struct depca_init))) <= - (netRAM<<10)) { - printk(",\n has %dkB RAM at 0x%.5lx", netRAM, mem_start); - - /* Enable the shadow RAM. */ - if (adapter != DEPCA) { - nicsr |= SHE; - outb(nicsr, DEPCA_NICSR); - } + sizeof(struct depca_init))) + > (netRAM<<10)) { + printk(",\n requests %dkB RAM: only %dkB is available!\n", + (mem_len >> 10), netRAM); + return -ENXIO; + } + + printk(",\n has %dkB RAM at 0x%.5lx", netRAM, mem_start); + + /* Enable the shadow RAM. */ + if (adapter != DEPCA) { + nicsr |= SHE; + outb(nicsr, DEPCA_NICSR); + } - /* Define the device private memory */ - dev->priv = (void *) kmalloc(sizeof(struct depca_private), GFP_KERNEL); - if (dev->priv == NULL) - return -ENOMEM; - lp = (struct depca_private *)dev->priv; - memset((char *)dev->priv, 0, sizeof(struct depca_private)); - lp->adapter = adapter; - lp->mca_slot = mca_slot; - lp->lock = SPIN_LOCK_UNLOCKED; - sprintf(lp->adapter_name,"%s (%s)", name, dev->name); - request_region(ioaddr, DEPCA_TOTAL_SIZE, lp->adapter_name); - - /* Initialisation Block */ - lp->sh_mem = mem_start; - mem_start += sizeof(struct depca_init); - - /* Tx & Rx descriptors (aligned to a quadword boundary) */ - mem_start = (mem_start + ALIGN) & ~ALIGN; - lp->rx_ring = (struct depca_rx_desc *)mem_start; - - mem_start += (sizeof(struct depca_rx_desc) * NUM_RX_DESC); - lp->tx_ring = (struct depca_tx_desc *)mem_start; - - mem_start += (sizeof(struct depca_tx_desc) * NUM_TX_DESC); - lp->bus_offset = mem_start & 0x00ff0000; - mem_start &= LA_MASK; /* LANCE re-mapped start address */ - - lp->dma_buffs = mem_start; - - /* Finish initialising the ring information. */ - lp->rxRingMask = NUM_RX_DESC - 1; - lp->txRingMask = NUM_TX_DESC - 1; - - /* Calculate Tx/Rx RLEN size for the descriptors. */ - for (i=0, j = lp->rxRingMask; j>0; i++) { - j >>= 1; - } - lp->rx_rlen = (s32)(i << 29); - for (i=0, j = lp->txRingMask; j>0; i++) { - j >>= 1; - } - lp->tx_rlen = (s32)(i << 29); + /* Define the device private memory */ + dev->priv = (void *) kmalloc(sizeof(struct depca_private), GFP_KERNEL); + if (dev->priv == NULL) + return -ENOMEM; + lp = (struct depca_private *)dev->priv; + memset((char *)dev->priv, 0, sizeof(struct depca_private)); + lp->adapter = adapter; + lp->mca_slot = mca_slot; + lp->lock = SPIN_LOCK_UNLOCKED; + sprintf(lp->adapter_name,"%s (%s)", name, dev->name); + if (!request_region(ioaddr, DEPCA_TOTAL_SIZE, lp->adapter_name)) { + printk(KERN_ERR "depca: I/O resource 0x%x @ 0x%lx busy\n", + DEPCA_TOTAL_SIZE, ioaddr); + return -EBUSY; + } + + /* Initialisation Block */ + lp->sh_mem = ioremap(mem_start, mem_len); + if (lp->sh_mem == NULL) { + printk(KERN_ERR "depca: cannot remap ISA memory, aborting\n"); + return -EIO; + } + lp->device_ram_start = mem_start & LA_MASK; + + offset = 0; + offset += sizeof(struct depca_init); + + /* Tx & Rx descriptors (aligned to a quadword boundary) */ + offset = (offset + ALIGN) & ~ALIGN; + lp->rx_ring = (struct depca_rx_desc *)(lp->sh_mem + offset); + lp->rx_ring_offset = offset; + + offset += (sizeof(struct depca_rx_desc) * NUM_RX_DESC); + lp->tx_ring = (struct depca_tx_desc *)(lp->sh_mem + offset); + lp->tx_ring_offset = offset; + + offset += (sizeof(struct depca_tx_desc) * NUM_TX_DESC); + + lp->buffs_offset = offset; + + /* Finish initialising the ring information. */ + lp->rxRingMask = NUM_RX_DESC - 1; + lp->txRingMask = NUM_TX_DESC - 1; + + /* Calculate Tx/Rx RLEN size for the descriptors. */ + for (i=0, j = lp->rxRingMask; j>0; i++) { + j >>= 1; + } + lp->rx_rlen = (s32)(i << 29); + for (i=0, j = lp->txRingMask; j>0; i++) { + j >>= 1; + } + lp->tx_rlen = (s32)(i << 29); - /* Load the initialisation block */ - depca_init_ring(dev); + /* Load the initialisation block */ + depca_init_ring(dev); - /* Initialise the control and status registers */ - LoadCSRs(dev); + /* Initialise the control and status registers */ + LoadCSRs(dev); - /* Enable DEPCA board interrupts for autoprobing */ - nicsr = ((nicsr & ~IM)|IEN); - outb(nicsr, DEPCA_NICSR); + /* Enable DEPCA board interrupts for autoprobing */ + nicsr = ((nicsr & ~IM)|IEN); + outb(nicsr, DEPCA_NICSR); - /* To auto-IRQ we enable the initialization-done and DMA err, - interrupts. For now we will always get a DMA error. */ - if (dev->irq < 2) { + /* To auto-IRQ we enable the initialization-done and DMA err, + interrupts. For now we will always get a DMA error. */ + if (dev->irq < 2) { #ifndef MODULE - unsigned char irqnum; - autoirq_setup(0); - - /* Assign the correct irq list */ - switch (lp->adapter) { - case DEPCA: - case de100: - case de101: - depca_irq = de1xx_irq; - break; - case de200: - case de201: - case de202: - case de210: - case de212: - depca_irq = de2xx_irq; - break; - case de422: - depca_irq = de422_irq; - break; - } + unsigned char irqnum; + autoirq_setup(0); + + /* Assign the correct irq list */ + switch (lp->adapter) { + case DEPCA: + case de100: + case de101: + depca_irq = de1xx_irq; + break; + case de200: + case de201: + case de202: + case de210: + case de212: + depca_irq = de2xx_irq; + break; + case de422: + depca_irq = de422_irq; + break; + } - /* Trigger an initialization just for the interrupt. */ - outw(INEA | INIT, DEPCA_DATA); + /* Trigger an initialization just for the interrupt. */ + outw(INEA | INIT, DEPCA_DATA); - irqnum = autoirq_report(1); - if (!irqnum) { - printk(" and failed to detect IRQ line.\n"); - status = -ENXIO; - } else { - for (dev->irq=0,i=0; (depca_irq[i]) && (!dev->irq); i++) { - if (irqnum == depca_irq[i]) { - dev->irq = irqnum; - printk(" and uses IRQ%d.\n", dev->irq); - } - } + irqnum = autoirq_report(1); + if (!irqnum) { + printk(" and failed to detect IRQ line.\n"); + status = -ENXIO; + } else { + for (dev->irq=0,i=0; (depca_irq[i]) && (!dev->irq); i++) { + if (irqnum == depca_irq[i]) { + dev->irq = irqnum; + printk(" and uses IRQ%d.\n", dev->irq); + } + } - if (!dev->irq) { - printk(" but incorrect IRQ line detected.\n"); - status = -ENXIO; - } - } + if (!dev->irq) { + printk(" but incorrect IRQ line detected.\n"); + status = -ENXIO; + } + } #endif /* MODULE */ - } else { - printk(" and assigned IRQ%d.\n", dev->irq); - } - if (status) release_region(ioaddr, DEPCA_TOTAL_SIZE); } else { - printk(",\n requests %dkB RAM: only %dkB is available!\n", - (mem_len>>10), netRAM); - status = -ENXIO; + printk(" and assigned IRQ%d.\n", dev->irq); } - } else { - printk(" which has an Ethernet PROM CRC error.\n"); - status = -ENXIO; - } - } else { - status = -ENXIO; - } - if (!status) { - if (depca_debug > 1) { - printk(version); - } - /* The DEPCA-specific entries in the device structure. */ - dev->open = &depca_open; - dev->hard_start_xmit = &depca_start_xmit; - dev->stop = &depca_close; - dev->get_stats = &depca_get_stats; - dev->set_multicast_list = &set_multicast_list; - dev->do_ioctl = &depca_ioctl; - dev->tx_timeout = depca_tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; - - dev->mem_start = 0; - - /* Fill in the generic field of the device structure. */ - ether_setup(dev); - } else { /* Incorrectly initialised hardware */ - if (dev->priv) { - kfree(dev->priv); - dev->priv = NULL; - } - } - } else { - status = -ENXIO; - } + if (!status) { + if (depca_debug > 1) { + printk(version); + } - return status; + /* The DEPCA-specific entries in the device structure. */ + dev->open = &depca_open; + dev->hard_start_xmit = &depca_start_xmit; + dev->stop = &depca_close; + dev->get_stats = &depca_get_stats; + dev->set_multicast_list = &set_multicast_list; + dev->do_ioctl = &depca_ioctl; + dev->tx_timeout = depca_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + + dev->mem_start = 0; + + /* Fill in the generic field of the device structure. */ + ether_setup(dev); + } else { /* Incorrectly initialised hardware */ + release_region(ioaddr, DEPCA_TOTAL_SIZE); + if (dev->priv) { + kfree(dev->priv); + dev->priv = NULL; + } + } + + return status; } @@ -741,7 +764,7 @@ depca_open(struct net_device *dev) nicsr = inb(DEPCA_NICSR); /* Make sure the shadow RAM is enabled */ - if (adapter != DEPCA) { + if (lp->adapter != DEPCA) { nicsr |= SHE; outb(nicsr, DEPCA_NICSR); } @@ -781,39 +804,43 @@ depca_open(struct net_device *dev) static void depca_init_ring(struct net_device *dev) { - struct depca_private *lp = (struct depca_private *)dev->priv; - u_int i; - u_long p; - - /* Lock out other processes whilst setting up the hardware */ - netif_stop_queue(dev); - - lp->rx_new = lp->tx_new = 0; - lp->rx_old = lp->tx_old = 0; + struct depca_private *lp = (struct depca_private *)dev->priv; + u_int i; + u_long offset; + + /* Lock out other processes whilst setting up the hardware */ + netif_stop_queue(dev); + + lp->rx_new = lp->tx_new = 0; + lp->rx_old = lp->tx_old = 0; + + /* Initialize the base address and length of each buffer in the ring */ + for (i = 0; i <= lp->rxRingMask; i++) { + offset = lp->buffs_offset + i*RX_BUFF_SZ; + writel((lp->device_ram_start + offset) | R_OWN, + &lp->rx_ring[i].base); + writew(-RX_BUFF_SZ, &lp->rx_ring[i].buf_length); + lp->rx_buff[i] = lp->sh_mem + offset; + } - /* Initialize the base addresses and length of each buffer in the ring */ - for (i = 0; i <= lp->rxRingMask; i++) { - writel((p=lp->dma_buffs+i*RX_BUFF_SZ) | R_OWN, &lp->rx_ring[i].base); - writew(-RX_BUFF_SZ, &lp->rx_ring[i].buf_length); - lp->rx_memcpy[i]=(char *)(p+lp->bus_offset); - } - for (i = 0; i <= lp->txRingMask; i++) { - writel((p=lp->dma_buffs+(i+lp->txRingMask+1)*TX_BUFF_SZ) & 0x00ffffff, - &lp->tx_ring[i].base); - lp->tx_memcpy[i]=(char *)(p+lp->bus_offset); - } + for (i = 0; i <= lp->txRingMask; i++) { + offset = lp->buffs_offset + (i + lp->rxRingMask+1)*TX_BUFF_SZ; + writel((lp->device_ram_start + offset) & 0x00ffffff, + &lp->tx_ring[i].base); + lp->tx_buff[i] = lp->sh_mem + offset; + } - /* Set up the initialization block */ - lp->init_block.rx_ring = ((u32)((u_long)lp->rx_ring)&LA_MASK) | lp->rx_rlen; - lp->init_block.tx_ring = ((u32)((u_long)lp->tx_ring)&LA_MASK) | lp->tx_rlen; + /* Set up the initialization block */ + lp->init_block.rx_ring = (lp->device_ram_start + lp->rx_ring_offset) | lp->rx_rlen; + lp->init_block.tx_ring = (lp->device_ram_start + lp->tx_ring_offset) | lp->tx_rlen; - SetMulticastFilter(dev); + SetMulticastFilter(dev); - for (i = 0; i < ETH_ALEN; i++) { - lp->init_block.phys_addr[i] = dev->dev_addr[i]; - } + for (i = 0; i < ETH_ALEN; i++) { + lp->init_block.phys_addr[i] = dev->dev_addr[i]; + } - lp->init_block.mode = 0x0000; /* Enable the Tx and Rx */ + lp->init_block.mode = 0x0000; /* Enable the Tx and Rx */ } @@ -951,10 +978,10 @@ depca_rx(struct net_device *dev) skb->dev = dev; if (entry < lp->rx_old) { /* Wrapped buffer */ len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ; - memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], len); - memcpy_fromio(buf + len, lp->rx_memcpy[0], pkt_len-len); + memcpy_fromio(buf, lp->rx_buff[lp->rx_old], len); + memcpy_fromio(buf + len, lp->rx_buff[0], pkt_len-len); } else { /* Linear buffer */ - memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], pkt_len); + memcpy_fromio(buf, lp->rx_buff[lp->rx_old], pkt_len); } /* @@ -1103,9 +1130,9 @@ static void LoadCSRs(struct net_device *dev) u_long ioaddr = dev->base_addr; outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */ - outw((u16)(lp->sh_mem & LA_MASK), DEPCA_DATA); + outw((u16)lp->device_ram_start, DEPCA_DATA); outw(CSR2, DEPCA_ADDR); /* initialisation block address MSW */ - outw((u16)((lp->sh_mem & LA_MASK) >> 16), DEPCA_DATA); + outw((u16)(lp->device_ram_start >> 16), DEPCA_DATA); outw(CSR3, DEPCA_ADDR); /* ALE control */ outw(ACON, DEPCA_DATA); @@ -1121,7 +1148,7 @@ static int InitRestartDepca(struct net_device *dev) int i, status=0; /* Copy the shadow init_block to shared memory */ - memcpy_toio((char *)lp->sh_mem, &lp->init_block, sizeof(struct depca_init)); + memcpy_toio(lp->sh_mem, &lp->init_block, sizeof(struct depca_init)); outw(CSR0, DEPCA_ADDR); /* point back to CSR0 */ outw(INIT, DEPCA_DATA); /* initialize DEPCA */ @@ -1134,11 +1161,11 @@ static int InitRestartDepca(struct net_device *dev) outw(IDON | INEA | STRT, DEPCA_DATA); if (depca_debug > 2) { printk("%s: DEPCA open after %d ticks, init block 0x%08lx csr0 %4.4x.\n", - dev->name, i, lp->sh_mem, inw(DEPCA_DATA)); + dev->name, i, virt_to_phys(lp->sh_mem), inw(DEPCA_DATA)); } } else { printk("%s: DEPCA unopen after %d ticks, init block 0x%08lx csr0 %4.4x.\n", - dev->name, i, lp->sh_mem, inw(DEPCA_DATA)); + dev->name, i, virt_to_phys(lp->sh_mem), inw(DEPCA_DATA)); status = -1; } @@ -1569,12 +1596,20 @@ DepcaSignature(char *name, u_long paddr) { u_int i,j,k; const char *signatures[] = DEPCA_SIGNATURE; + void *ptr; char tmpstr[16]; /* Copy the first 16 bytes of ROM */ + ptr = ioremap(paddr + 0xc000, 16); + if (ptr == NULL) { + printk(KERN_ERR "depca: I/O remap failed at %lx\n", paddr+0xc000); + adapter = unknown; + return; + } for (i=0;i<16;i++) { - tmpstr[i] = readb(paddr+0xc000+i); + tmpstr[i] = readb(ptr + i); } + iounmap(ptr); /* Check if PROM contains a valid string */ for (i=0;*signatures[i]!='\0';i++) { @@ -1716,10 +1751,10 @@ static int load_packet(struct net_device *dev, struct sk_buff *skb) */ if (end < entry) { /* wrapped buffer */ len = (lp->txRingMask - entry + 1) * TX_BUFF_SZ; - memcpy_toio(lp->tx_memcpy[entry], skb->data, len); - memcpy_toio(lp->tx_memcpy[0], skb->data + len, skb->len - len); + memcpy_toio(lp->tx_buff[entry], skb->data, len); + memcpy_toio(lp->tx_buff[0], skb->data + len, skb->len - len); } else { /* linear buffer */ - memcpy_toio(lp->tx_memcpy[entry], skb->data, skb->len); + memcpy_toio(lp->tx_buff[entry], skb->data, skb->len); } /* set up the buffer descriptors */ @@ -1795,17 +1830,17 @@ static void depca_dbg_open(struct net_device *dev) { struct depca_private *lp = (struct depca_private *)dev->priv; u_long ioaddr = dev->base_addr; - struct depca_init *p = (struct depca_init *)lp->sh_mem; + struct depca_init *p = &lp->init_block; int i; if (depca_debug > 1){ - /* Copy the shadow init_block to shared memory */ - memcpy_toio((char *)lp->sh_mem,&lp->init_block,sizeof(struct depca_init)); - + /* Do not copy the shadow init block into shared memory */ + /* Debugging should not affect normal operation! */ + /* The shadow init block will get copied across during InitRestartDepca */ printk("%s: depca open with irq %d\n",dev->name,dev->irq); - printk("Descriptor head addresses:\n"); - printk("\t0x%lx 0x%lx\n",(u_long)lp->rx_ring, (u_long)lp->tx_ring); - printk("Descriptor addresses:\nRX: "); + printk("Descriptor head addresses (CPU):\n"); + printk(" 0x%lx 0x%lx\n",(u_long)lp->rx_ring, (u_long)lp->tx_ring); + printk("Descriptor addresses (CPU):\nRX: "); for (i=0;irxRingMask;i++){ if (i < 3) { printk("0x%8.8lx ", (long) &lp->rx_ring[i].base); @@ -1819,7 +1854,7 @@ static void depca_dbg_open(struct net_device *dev) } } printk("...0x%8.8lx\n", (long) &lp->tx_ring[i].base); - printk("\nDescriptor buffers:\nRX: "); + printk("\nDescriptor buffers (Device):\nRX: "); for (i=0;irxRingMask;i++){ if (i < 3) { printk("0x%8.8x ", readl(&lp->rx_ring[i].base)); @@ -1833,21 +1868,21 @@ static void depca_dbg_open(struct net_device *dev) } } printk("...0x%8.8x\n", readl(&lp->tx_ring[i].base)); - printk("Initialisation block at 0x%8.8lx\n",lp->sh_mem); - printk("\tmode: 0x%4.4x\n",readw(&p->mode)); - printk("\tphysical address: "); + printk("Initialisation block at 0x%8.8lx(Phys)\n",virt_to_phys(lp->sh_mem)); + printk(" mode: 0x%4.4x\n",p->mode); + printk(" physical address: "); for (i=0;iphys_addr[i])); + printk("%2.2x:", p->phys_addr[i]); } - printk("%2.2x\n",(u_char)readb(&p->phys_addr[i])); - printk("\tmulticast hash table: "); + printk("%2.2x\n", p->phys_addr[i]); + printk(" multicast hash table: "); for (i=0;i<(HASH_TABLE_LEN >> 3)-1;i++){ - printk("%2.2x:",(u_char)readb(&p->mcast_table[i])); + printk("%2.2x:", p->mcast_table[i]); } - printk("%2.2x\n",(u_char)readb(&p->mcast_table[i])); - printk("\trx_ring at: 0x%8.8x\n",readl(&p->rx_ring)); - printk("\ttx_ring at: 0x%8.8x\n",readl(&p->tx_ring)); - printk("dma_buffs: 0x%8.8lx\n",lp->dma_buffs); + printk("%2.2x\n", p->mcast_table[i]); + printk(" rx_ring at: 0x%8.8x\n", p->rx_ring); + printk(" tx_ring at: 0x%8.8x\n", p->tx_ring); + printk("buffers (Phys): 0x%8.8lx\n",virt_to_phys(lp->sh_mem)+lp->buffs_offset); printk("Ring size:\nRX: %d Log2(rxRingMask): 0x%8.8x\n", (int)lp->rxRingMask + 1, lp->rx_rlen); @@ -2031,6 +2066,7 @@ cleanup_module(void) { struct depca_private *lp = thisDepca.priv; if (lp) { + iounmap(lp->sh_mem); #ifdef CONFIG_MCA if(lp->mca_slot != -1) mca_mark_as_unused(lp->mca_slot); diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index e02298ded5c2..254b42880b40 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -24,6 +24,8 @@ Version 1.0.1: - Spinlock fixes - Bug fixes and better intr performance (Tjeerd) + Version 1.0.2: + - Now reads correct MAC address from eeprom */ @@ -33,7 +35,7 @@ static const char version1[] = static const char version2[] = " http://www.scyld.com/network/natsemi.html\n"; static const char version3[] = -" (unofficial 2.4.x kernel port, version 1.0.1, September 5, 2000 Jeff Garzik, Tjeerd Mulder)\n"; +" (unofficial 2.4.x kernel port, version 1.0.2, October 6, 2000 Jeff Garzik, Tjeerd Mulder)\n"; /* Updated to recommendations in pci-skeleton v2.03. */ /* Automatically extracted configuration info: @@ -405,8 +407,13 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev, printk(KERN_INFO "%s: %s at 0x%lx, ", dev->name, natsemi_pci_info[chip_idx].name, ioaddr); - for (i = 0; i < ETH_ALEN/2; i++) - ((u16 *)dev->dev_addr)[i] = be16_to_cpu(eeprom_read(ioaddr, i + 7)); + for (i = 0; i < ETH_ALEN/2; i++) { + /* weird organization */ + unsigned short a; + a = (le16_to_cpu(eeprom_read(ioaddr, i + 6)) >> 15) + + (le16_to_cpu(eeprom_read(ioaddr, i + 7)) << 1); + ((u16 *)dev->dev_addr)[i] = a; + } for (i = 0; i < ETH_ALEN-1; i++) printk("%2.2x:", dev->dev_addr[i]); printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); @@ -513,7 +520,8 @@ static int eeprom_read(long addr, int location) for (i = 16; i > 0; i--) { writel(EE_ChipSelect | EE_ShiftClk, ee_addr); eeprom_delay(ee_addr); - retval = (retval << 1) | ((readl(ee_addr) & EE_DataOut) ? 1 : 0); + /* data bits are LSB first */ + retval = (retval >> 1) | ((readl(ee_addr) & EE_DataOut) ? 0x8000 : 0); writel(EE_ChipSelect, ee_addr); eeprom_delay(ee_addr); } diff --git a/drivers/sbus/audio/audio.c b/drivers/sbus/audio/audio.c index 9f2d52d5ff49..5062a4300b0c 100644 --- a/drivers/sbus/audio/audio.c +++ b/drivers/sbus/audio/audio.c @@ -1,4 +1,4 @@ -/* $Id: audio.c,v 1.55 2000/10/10 01:07:39 davem Exp $ +/* $Id: audio.c,v 1.56 2000/10/19 00:50:02 davem Exp $ * drivers/sbus/audio/audio.c * * Copyright 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu) diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c index dbba073de9a2..31c9de932fd2 100644 --- a/drivers/sbus/char/envctrl.c +++ b/drivers/sbus/char/envctrl.c @@ -1,23 +1,29 @@ -/* $Id: envctrl.c,v 1.17 2000/06/19 06:24:47 davem Exp $ +/* $Id: envctrl.c,v 1.18 2000/10/17 16:20:35 davem Exp $ * envctrl.c: Temperature and Fan monitoring on Machines providing it. * * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 2000 Vinh Truong (vinh.truong@eng.sun.com) + * VT - The implementation is to support Sun Microelectronics (SME) platform + * environment monitoring. SME platforms use pcf8584 as the i2c bus + * controller to access pcf8591 (8-bit A/D and D/A converter) and + * pcf8571 (256 x 8-bit static low-voltage RAM with I2C-bus interface). + * At board level, it follows SME Firmware I2C Specification. Reference: + * http://www-eu2.semiconductors.com/pip/PCF8584P + * http://www-eu2.semiconductors.com/pip/PCF8574AP + * http://www-eu2.semiconductors.com/pip/PCF8591P + * */ -#include #include #include - -#define __KERNEL_SYSCALLS__ #include -#include #include #include #include #include -#include #include -#include +#include +#include #include #include @@ -25,18 +31,6 @@ #define ENVCTRL_MINOR 162 - -#undef U450_SUPPORT /* might fry you machine, careful here !!! */ - - -#undef DEBUG -#undef DEBUG_BUS_SCAN - - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) -#define schedule_timeout(a) { current->timeout = jiffies + (a); schedule(); } -#endif - #define PCF8584_ADDRESS 0x55 #define CONTROL_PIN 0x80 @@ -71,1532 +65,879 @@ #define CLK_8 0x18 #define CLK_12 0x1c +#define OBD_SEND_START 0xc5 /* value to generate I2c_bus START condition */ +#define OBD_SEND_STOP 0xc3 /* value to generate I2c_bus STOP condition */ -#define I2C_WRITE 0x00 -#define I2C_READ 0x01 - -/* PCF8584 register offsets */ -#define I2C_DATA 0x00UL -#define I2C_CSR 0x01UL -#define I2C_REG_SIZE 0x02UL - -struct i2c_device { - unsigned char addr; - struct i2c_device *next; -}; - -static unsigned long i2c_regs; -static struct i2c_device *i2c_devices; - -static int errno; - -#define MAX_TEMPERATURE 111 -#define MAX_FAN_SPEED 63 - - -/* - * UltraAXi constants. +/* Monitor type of i2c child device. + * Firmware definitions. */ -#define AXI_THERM_ADDR 0x9e -#define AXI_THERM_PORT_CPU 0 -#define AXI_THERM_PORT_MOD 1 -#define AXI_THERM_PORT_PCI 2 -#define AXI_THERM_PORT_DISK 3 - -#define AXI_FAN_ADDR 0x4e -#define AXI_FAN_PORT_FRONT 0 -#define AXI_FAN_PORT_BACK 1 - -#define AXI_PIO_ADDR 0x70 +#define PCF8584_MAX_CHANNELS 8 +#define PCF8584_FANSTAT_TYPE 3 /* fan status monitor */ +#define PCF8584_VOLTAGE_TYPE 2 /* voltage monitor */ +#define PCF8584_TEMP_TYPE 1 /* temperature monitor*/ -/* - * Ultra 450 constants. +/* Monitor type of i2c child device. + * Driver definitions. */ -#define U450_FAN_ADDR 0x4e -#define U450_FAN_PORT_CPU 0 -#define U450_FAN_PORT_PS 1 - -#define U450_PIO_ADDR 0x70 -#define U450_TIMER_ADDR 0xa0 - -static unsigned char -axi_cpu_temp_table[256] = -{ - 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x67, - 0x66, 0x65, 0x64, 0x63, 0x61, 0x60, 0x5f, 0x5e, - 0x5d, 0x5b, 0x5a, 0x59, 0x58, 0x57, 0x55, 0x54, - 0x53, 0x52, 0x50, 0x4f, 0x4e, 0x4d, 0x4c, 0x4a, - 0x49, 0x48, 0x47, 0x46, 0x44, 0x43, 0x42, 0x41, - 0x40, 0x3e, 0x3d, 0x3c, 0x3c, 0x3b, 0x3b, 0x3a, - 0x3a, 0x39, 0x39, 0x38, 0x38, 0x37, 0x37, 0x36, - 0x36, 0x35, 0x35, 0x34, 0x34, 0x33, 0x33, 0x32, - 0x32, 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, - 0x2d, 0x2d, 0x2c, 0x2c, 0x2b, 0x2b, 0x2a, 0x2a, - 0x29, 0x29, 0x28, 0x28, 0x27, 0x27, 0x26, 0x26, - 0x25, 0x25, 0x24, 0x24, 0x23, 0x23, 0x22, 0x22, - 0x21, 0x21, 0x20, 0x20, 0x1f, 0x1f, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1b, 0x1b, 0x1b, 0x1b, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x19, 0x19, 0x18, - 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15, - 0x15, 0x14, 0x14, 0x14, 0x14, 0x13, 0x13, 0x13, - 0x13, 0x12, 0x12, 0x12, 0x12, 0x12, 0x11, 0x11, - 0x11, 0x11, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, - 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, - 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static unsigned char -axi_mod_temp_table[256] = -{ - 0x65, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, - 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, 0x57, 0x56, - 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0x4e, - 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46, - 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3f, 0x3e, - 0x3d, 0x3c, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x39, - 0x39, 0x38, 0x38, 0x37, 0x37, 0x36, 0x36, 0x35, - 0x35, 0x35, 0x34, 0x34, 0x33, 0x33, 0x32, 0x32, - 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, 0x2e, - 0x2e, 0x2d, 0x2d, 0x2c, 0x2c, 0x2b, 0x2b, 0x2a, - 0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x27, 0x27, - 0x26, 0x26, 0x25, 0x25, 0x24, 0x24, 0x23, 0x23, - 0x23, 0x22, 0x22, 0x21, 0x21, 0x20, 0x20, 0x1f, - 0x1f, 0x1e, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x19, 0x19, - 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x16, 0x16, 0x16, 0x16, 0x15, - 0x15, 0x15, 0x15, 0x14, 0x14, 0x14, 0x14, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x12, - 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, - 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, - 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, - 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static unsigned char -axi_fan_speeds[112] = -{ - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, - 0x22, 0x23, 0x25, 0x27, 0x28, 0x2a, 0x2b, 0x2d, - 0x2f, 0x30, 0x32, 0x33, 0x35, 0x37, 0x38, 0x3a, - 0x3b, 0x3d, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f -}; - - -struct therm_regs { - u32 addr; - u32 port; - u32 min_temp; - u32 warning; - u32 shutdown; - u32 num; - u32 den; -}; +#define ENVCTRL_NOMON 0 +#define ENVCTRL_CPUTEMP_MON 1 /* cpu temperature monitor */ +#define ENVCTRL_CPUVOLTAGE_MON 2 /* voltage monitor */ +#define ENVCTRL_FANSTAT_MON 3 /* fan status monitor */ +#define ENVCTRL_ETHERTEMP_MON 4 /* ethernet temperarture */ + /* monitor */ +#define ENVCTRL_VOLTAGESTAT_MON 5 /* voltage status monitor */ +#define ENVCTRL_MTHRBDTEMP_MON 6 /* motherboard temperature */ +#define ENVCTRL_SCSITEMP_MON 7 /* scsi temperarture */ + +/* Child device type. + * Driver definitions. + */ +#define I2C_ADC 0 /* pcf8591 */ +#define I2C_GPIO 1 /* pcf8571 */ -struct thermistor { - char name[8]; - struct therm_regs regs; - unsigned char (*temperature) (struct thermistor *); - unsigned char (*fan_speed) (struct thermistor *); - struct thermistor *next; /* all thermistors */ - struct thermistor *chain; /* thermistors for one fan */ +/* Data read from child device may need to decode + * through a data table and a scale. + * Translation type as defined by firmware. + */ +#define ENVCTRL_TRANSLATE_NO 0 +#define ENVCTRL_TRANSLATE_PARTIAL 1 +#define ENVCTRL_TRANSLATE_COMBINED 2 +#define ENVCTRL_TRANSLATE_FULL 3 /* table[data] */ +#define ENVCTRL_TRANSLATE_SCALE 4 /* table[data]/scale */ + +/* Driver miscellaneous definitions. */ +#define ENVCTRL_MAX_CPU 4 +#define CHANNEL_DESC_SZ 256 + +struct pcf8584_reg { + unsigned char data; + unsigned char csr; }; -struct fan_regs { - u32 addr; - u32 port; +/* Each child device can be monitored by up to PCF8584_MAX_CHANNELS. + * Property of a port or channel as defined by the firmware. + */ +struct pcf8584_channel { + unsigned char chnl_no; + unsigned char io_direction; + unsigned char type; + unsigned char last; }; -struct fan { - char name[8]; - struct fan_regs regs; - int (*set_speed)(struct fan *, unsigned char value); - int (*check_failure)(struct fan *); - unsigned char value; - struct thermistor *monitor; - struct fan *next; +/* Each child device may have one or more tables of bytes to help decode + * data. Table property as defined by the firmware. + */ +struct pcf8584_tblprop { + unsigned int type; + unsigned int scale; + unsigned int offset; /* offset from the beginning of the table */ + unsigned int size; }; - -struct environment { - struct thermistor *thermistors; - struct fan *fans; - unsigned char *cpu_temp_table; - unsigned char *cpu_fan_speeds; - unsigned char *ps_temp_table; - unsigned char *ps_fan_speeds; - void (*enable) (struct environment *); - void (*disable) (struct environment *); - void (*keep_alive) (struct environment *); - int interval; - pid_t kenvd_pid; - wait_queue_head_t kenvd_wait; - int terminate; +/* i2c child */ +struct i2c_child_t { + /* Either ADC or GPIO. */ + unsigned char i2ctype; + unsigned long addr; + struct pcf8584_channel chnl_array[PCF8584_MAX_CHANNELS]; + + /* Channel info. */ + unsigned int total_chnls; /* Number of monitor channels. */ + unsigned char fan_mask; /* Byte mask for fan status channels. */ + unsigned char voltage_mask; /* Byte mask for voltage status channels. */ + struct pcf8584_tblprop tblprop_array[PCF8584_MAX_CHANNELS]; + + /* Properties of all monitor channels. */ + unsigned int total_tbls; /* Number of monitor tables. */ + char *tables; /* Pointer to table(s). */ + char chnls_desc[CHANNEL_DESC_SZ]; /* Channel description. */ + char mon_type[PCF8584_MAX_CHANNELS]; }; +volatile static struct pcf8584_reg *i2c = NULL; +static struct i2c_child_t i2c_childlist[ENVCTRL_MAX_CPU*2]; +static unsigned char chnls_mask[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; +static unsigned int warning_temperature = 0; +static unsigned int shutdown_temperature = 0; +static char read_cpu; -static struct environment envctrl; - +/* Forward declarations. */ +static struct i2c_child_t *envctrl_get_i2c_child(unsigned char); -#ifdef DEBUG_BUS_SCAN -struct i2c_addr_map { - unsigned char addr; - unsigned char mask; - char *name; -}; - -static struct i2c_addr_map devmap[] = { - { 0x70, 0xf0, "PCF8574A" }, - { 0x40, 0xf0, "TDA8444" }, - { 0x90, 0xf0, "PCF8591" }, - { 0xa0, 0xf0, "PCF8583" }, -}; -#define NR_DEVMAP (sizeof(devmap) / sizeof(devmap[0])) -#endif - -static __inline__ int -PUT_DATA(unsigned long data, char *buffer, int user) +/* Function description: Read a byte from an i2c controller register. + * Return: A byte from the passed in address. + */ +static inline unsigned char envctrl_readb(volatile unsigned char *p) { - if (user) { - u8 tmp = readb(data); - if (put_user(tmp, buffer)) - return -EFAULT; - } else { - *buffer = readb(data); - } - return 0; + return readb(p); } -static __inline__ int -GET_DATA(unsigned long data, const char *buffer, int user) +/* Function description: Write a byte to an i2c controller register. + * Return: Nothing. + */ +static inline void envctrl_writeb(unsigned char val, volatile unsigned char *p) { - if (user) { - u8 tmp; - if (get_user(tmp, buffer)) - return -EFAULT; - writeb(tmp, data); - } else { - writeb(*buffer, data); - } - return 0; + writeb(val, p); } - -static int -i2c_read(unsigned char dev, char *buffer, int len, int user) +/* Function Description: Test the PIN bit (Pending Interrupt Not) + * to test when serial transmission is completed . + * Return : None. + */ +static void envtrl_i2c_test_pin(void) { - unsigned char dummy; - unsigned char stat; - int error = -ENODEV; - int count = 0; - - writeb((dev & 0xfe) | I2C_READ, i2c_regs + I2C_DATA); - - while (!(readb(i2c_regs + I2C_CSR) & STATUS_BB)) - udelay(1); - - writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK, - i2c_regs + I2C_CSR); - - do { - udelay(1); - while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN) - udelay(1); - - if (stat & STATUS_LRB) - goto stop; + int limit = 1000000; - error = 0; - if (len == 0) { - count--; + while (--limit > 0) { + if (!(envctrl_readb(&i2c->csr) & STATUS_PIN)) break; - } - - if (count == (len - 1)) - break; - - if (count++ > 0) { - error = PUT_DATA(i2c_regs + I2C_DATA, buffer++, user); - if (error) - break; - } else - dummy = readb(i2c_regs + I2C_DATA); - } while (1); - - writeb(CONTROL_ES0, i2c_regs + I2C_CSR); - if (!error && (count++ > 0)) - error = PUT_DATA(i2c_regs + I2C_DATA, buffer++, user); - else - dummy = readb(i2c_regs + I2C_DATA); - - udelay(1); - while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN) udelay(1); + } -stop: - writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK, - i2c_regs + I2C_CSR); - if (!error && (count++ > 0)) - error = PUT_DATA(i2c_regs + I2C_DATA, buffer++, user); - else - dummy = readb(i2c_regs + I2C_DATA); - - if (error) - return error; - return count - 1; + if (limit <= 0) + printk(KERN_INFO "envctrl: Pin status will not clear.\n"); } -static int -i2c_write(unsigned char dev, const char *buffer, int len, int user) +/* Function Description: Test busy bit. + * Return : None. + */ +static void envctrl_i2c_test_bb(void) { - int error = -ENODEV; - int count = 0; - int timeout; - - timeout = 1000000; - while (!(readb(i2c_regs + I2C_CSR) & STATUS_BB) && --timeout) - udelay(1); - if (!timeout) { - printk("%s [%d]: TIMEOUT\n", __FUNCTION__, __LINE__); - return -ENODEV; - } - - writeb((dev & 0xfe) | I2C_WRITE, i2c_regs + I2C_DATA); - writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK, - i2c_regs + I2C_CSR); - - do { - unsigned char stat; - - udelay(1); - timeout = 1000000; - while (((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN) && --timeout) - udelay(1); - - if (!timeout) { - printk("%s [%d]: TIMEOUT\n", __FUNCTION__, __LINE__); - break; - } + int limit = 1000000; - if (stat & STATUS_LRB) + while (--limit > 0) { + /* Busy bit 0 means busy. */ + if (envctrl_readb(&i2c->csr) & STATUS_BB) break; + udelay(1); + } - error = count; - if (count == len) - break; - - error = GET_DATA(i2c_regs + I2C_DATA, buffer++, user); - if (error) - break; - - count++; - } while (1); - - writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK, - i2c_regs + I2C_CSR); - return error; + if (limit <= 0) + printk(KERN_INFO "envctrl: Busy bit will not clear.\n"); } -#ifdef U450_SUPPORT -static int -i2c_write_read(unsigned char dev, char *outbuf, int outlen, - char *inbuf, int inlen, int user) +/* Function Description: Send the adress for a read access. + * Return : 0 if not acknowledged, otherwise acknowledged. + */ +static int envctrl_i2c_read_addr(unsigned char addr) { - unsigned char dummy; - unsigned char stat; - int error = -ENODEV; - int count = 0; - - while (!(readb(i2c_regs + I2C_CSR) & STATUS_BB)) - udelay(1); - - writeb((dev & 0xfe) | I2C_WRITE, i2c_regs + I2C_DATA); - writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK, - i2c_regs + I2C_CSR); - - do { - unsigned char stat; - - udelay(1); - while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN) - udelay(1); + envctrl_i2c_test_bb(); - if (stat & STATUS_LRB) - break; + /* Load address. */ + envctrl_writeb(addr + 1, &i2c->data); - error = count; - if (count == outlen) - break; + envctrl_i2c_test_bb(); - error = GET_DATA(i2c_regs + I2C_DATA, outbuf++, user); - if (error) - break; + envctrl_writeb(OBD_SEND_START, &i2c->csr); - count++; - } while (1); + /* Wait for PIN. */ + envtrl_i2c_test_pin(); - if (error < 0) { - writeb(CONTROL_PIN | CONTROL_ES0 | - CONTROL_STO | CONTROL_ACK, i2c_regs + I2C_CSR); - return error; + /* CSR 0 means acknowledged. */ + if (!(envctrl_readb(&i2c->csr) & STATUS_LRB)) { + return envctrl_readb(&i2c->data); + } else { + envctrl_writeb(OBD_SEND_STOP, &i2c->csr); + return 0; } - - writeb(CONTROL_ES0 | CONTROL_STA | CONTROL_ACK, i2c_regs + I2C_CSR); - udelay(1); - writeb((dev & 0xfe) | I2C_READ, i2c_regs + I2C_DATA); - - count = 0; - do { - udelay(1); - while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN) - udelay(1); - - if (stat & STATUS_LRB) - goto stop; - - error = 0; - if (inlen == 0) { - count--; - break; - } - - if (count == (inlen - 1)) - break; - - if (count++ > 0) { - error = PUT_DATA(i2c_regs + I2C_DATA, inbuf++, user); - if (error) - break; - } else - dummy = readb(i2c_regs + I2C_DATA); - } while (1); - - writeb(CONTROL_ES0, i2c_regs + I2C_CSR); - if (!error && (count++ > 0)) - error = PUT_DATA(i2c_regs + I2C_DATA, inbuf++, user); - else - dummy = readb(i2c_regs + I2C_DATA); - - udelay(1); - while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN) - udelay(1); - -stop: - writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK, - i2c_regs + I2C_CSR); - if (!error && (count++ > 0)) - error = PUT_DATA(i2c_regs + I2C_DATA, inbuf++, user); - else - dummy = readb(i2c_regs + I2C_DATA); - - if (error) - return error; - return count - 1; } -#endif /* U450_SUPPORT */ -static struct i2c_device * -i2c_find_device(unsigned char addr) +/* Function Description: Send the adress for write mode. + * Return : None. + */ +static void envctrl_i2c_write_addr(unsigned char addr) { - struct i2c_device *dev; + envctrl_i2c_test_bb(); + envctrl_writeb(addr, &i2c->data); - for (dev = i2c_devices; dev; dev = dev->next) { - if (dev->addr == addr) - return dev; - } - return 0; + /* Generate Start condition. */ + envctrl_writeb(OBD_SEND_START, &i2c->csr); } -static void -i2c_free_devices(void) +/* Function Description: Read 1 byte of data from addr + * set by envctrl_i2c_read_addr() + * Return : Data from address set by envctrl_i2c_read_addr(). + */ +static unsigned char envctrl_i2c_read_data(void) { - struct i2c_device *dev; - - dev = i2c_devices; - while (dev) { - i2c_devices = dev->next; - kfree(dev); - dev = i2c_devices; - } + envtrl_i2c_test_pin(); + envctrl_writeb(CONTROL_ES0, &i2c->csr); /* Send neg ack. */ + return envctrl_readb(&i2c->data); } -static __init int i2c_scan_bus(void) +/* Function Description: Instruct the device which port to read data from. + * Return : None. + */ +static void envctrl_i2c_write_data(unsigned char port) { - struct i2c_device *dev, **last; - unsigned int addr; - int count = 0; - - last = &i2c_devices; - for (addr = 0; addr < 256; addr += 2) { - if (i2c_write(addr, 0, 0, 0) == 0) { -#ifdef DEBUG_BUS_SCAN - int i; - for (i = 0; i < NR_DEVMAP; i++) - if ((addr & devmap[i].mask) == devmap[i].addr) - break; - printk("envctrl: i2c device at %02x: %s\n", addr, - i < NR_DEVMAP ? devmap[i].name : "unknown"); -#endif - - dev = kmalloc(sizeof(struct i2c_device), GFP_KERNEL); - if (!dev) { - printk("i2c: can't alloc i2c_device\n"); - i2c_free_devices(); - return -ENOMEM; - } - memset(dev, 0, sizeof(struct i2c_device)); - - dev->addr = addr; - - *last = dev; - last = &dev->next; - - count++; - } - } - if (!count) { - printk("%s: no devices found\n", __FUNCTION__); - return -ENODEV; - } - return 0; + envtrl_i2c_test_pin(); + envctrl_writeb(port, &i2c->data); } - -static int -read_8591(unsigned char dev, unsigned char offset, unsigned char *value) +/* Function Description: Generate Stop condition after last byte is sent. + * Return : None. + */ +static void envctrl_i2c_stop(void) { - unsigned char data[2]; - - data[0] = 0x40 | offset; - if (i2c_write(dev, data, 1, 0) != 1) - return -1; - if (i2c_read(dev, data, 2, 0) != 2) - return -1; - *value = data[1]; - return 0; + envtrl_i2c_test_pin(); + envctrl_writeb(OBD_SEND_STOP, &i2c->csr); } -static int -write_8444(unsigned char dev, unsigned char offset, unsigned char value) +/* Function Description: Read adc device. + * Return : Data at address and port. + */ +static unsigned char envctrl_i2c_read_8591(unsigned char addr, unsigned char port) { - unsigned char data[2]; + /* Send address. */ + envctrl_i2c_write_addr(addr); - data[0] = offset; - data[1] = value; - if (i2c_write(dev, data, 2, 0) != 2) - return -1; - return 0; -} + /* Setup port to read. */ + envctrl_i2c_write_data(port); + envctrl_i2c_stop(); -#ifdef U450_SUPPORT -static int -read_8583(unsigned char dev, unsigned char offset, unsigned char *value) -{ - unsigned char data; + /* Read port. */ + envctrl_i2c_read_addr(addr); - data = offset; - if (i2c_write_read(dev, &data, 1, &data, 1, 0) != 1) - return -1; - *value = data; - return 0; -} - -static int -write_8583(unsigned char dev, unsigned char offset, unsigned char value) -{ - unsigned char data[2]; + /* Do a single byte read and send stop. */ + envctrl_i2c_read_data(); + envctrl_i2c_stop(); - data[0] = offset; - data[1] = value; - if (i2c_write(dev, data, 2, 0) != 2) - return -1; - return 0; + return envctrl_readb(&i2c->data); } -#endif /* U450_SUPPORT */ -struct thermistor * -find_thermistor(const char *name, struct thermistor *from) +/* Function Description: Read gpio device. + * Return : Data at address. + */ +static unsigned char envctrl_i2c_read_8574(unsigned char addr) { - int n; + unsigned char rd; - if (!from) - from = envctrl.thermistors; - else - from = from->next; + envctrl_i2c_read_addr(addr); - n = strlen(name); - while (from && strncmp(from->name, name, n)) - from = from->next; + /* Do a single byte read and send stop. */ + rd = envctrl_i2c_read_data(); + envctrl_i2c_stop(); - return from; + return rd; } -void -check_temperatures(struct environment *env) +/* Function Description: Decode data read from an adc device using firmware + * table. + * Return: Number of read bytes. Data is stored in bufdata in ascii format. + */ +static int envctrl_i2c_data_translate(unsigned char data, int translate_type, + int scale, char *tbl, char *bufdata) { - struct thermistor *t; - - for (t = env->thermistors; t; t = t->next) { -#ifdef DEBUG - printk("Thermistor `%s' [%02x:%d]: " - "%d C (%d C, %d C)\n", - t->name, t->regs.addr, t->regs.port, - t->temperature(t), t->regs.warning, t->regs.shutdown); -#endif + int len = 0; - /* - * Implement slow-down or shutdown here... - */ - } -} + switch (translate_type) { + case ENVCTRL_TRANSLATE_NO: + /* No decode necessary. */ + len = 1; + bufdata[0] = data; + break; -void -check_fan_speeds(struct environment *env) -{ - unsigned char speed, max; - struct thermistor *t; - struct fan *f; - - for (f = env->fans; f; f = f->next) { -#ifdef DEBUG - printk("Fan `%s' [%02x:%d]:", f->name, - f->regs.addr, f->regs.port); -#endif - max = 0; - for (t = f->monitor; t; t = t->chain) { - speed = t->fan_speed(t); - if (speed > max) - max = speed; -#ifdef DEBUG - printk(" %s:%02x", t->name, speed); -#endif - } + case ENVCTRL_TRANSLATE_FULL: + /* Decode this way: data = table[data]. */ + len = 1; + bufdata[0] = tbl[data]; + break; - f->set_speed(f, max); -#ifdef DEBUG - printk(" -> %02x\n", f->value); -#endif - } -} + case ENVCTRL_TRANSLATE_SCALE: + /* Decode this way: data = table[data]/scale */ + sprintf(bufdata,"%d ", (tbl[data] * 10) / (scale)); + len = strlen(bufdata); + bufdata[len - 1] = bufdata[len - 2]; + bufdata[len - 2] = '.'; + break; -void -envctrl_fans_blast(struct environment *env) -{ - struct fan *f; + default: + break; + }; - for (f = env->fans; f; f = f->next) - f->set_speed(f, MAX_FAN_SPEED); + return len; } -int -kenvd(void *data) +/* Function Description: Read cpu-related data such as cpu temperature, voltage. + * Return: Number of read bytes. Data is stored in bufdata in ascii format. + */ +static int envctrl_read_cpu_info(struct i2c_child_t *pchild, + char mon_type, unsigned char *bufdata) { - struct environment *env = data; - - MOD_INC_USE_COUNT; - lock_kernel(); - - env->kenvd_pid = current->pid; - - exit_files(current); - exit_mm(current); - - spin_lock_irq(¤t->sigmask_lock); - siginitsetinv(¤t->blocked, sigmask(SIGKILL)); - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); - - current->session = 1; - current->pgrp = 1; - strcpy(current->comm, "kenvd"); - - if (env->enable) - env->enable(env); - - while (!env->terminate) { - - check_temperatures(env); - check_fan_speeds(env); - if (env->keep_alive) - env->keep_alive(env); - - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(env->interval * HZ); + unsigned char data; + int i; + char *tbl, j = -1; - if (signal_pending(current)) { - spin_lock_irq(¤t->sigmask_lock); - flush_signals(current); - spin_unlock_irq(¤t->sigmask_lock); - break; + /* Find the right monitor type and channel. */ + for (i = 0; i < PCF8584_MAX_CHANNELS; i++) { + if (pchild->mon_type[i] == mon_type) { + if (++j == read_cpu) { + break; + } } } - if (env->disable) - env->disable(env); + if (j != read_cpu) + return 0; - env->kenvd_pid = 0; - wake_up(&envctrl.kenvd_wait); + /* Read data from address and port. */ + data = envctrl_i2c_read_8591((unsigned char)pchild->addr, + (unsigned char)pchild->chnl_array[i].chnl_no); - MOD_DEC_USE_COUNT; - return 0; -} + /* Find decoding table. */ + tbl = pchild->tables + pchild->tblprop_array[i].offset; -void -envctrl_stop(void) -{ - DECLARE_WAITQUEUE(wait, current); - struct thermistor *t; - struct fan *f; - pid_t pid; - - if (envctrl.kenvd_pid) { - pid = envctrl.kenvd_pid; - - current->state = TASK_INTERRUPTIBLE; - add_wait_queue(&envctrl.kenvd_wait, &wait); - - envctrl.terminate = 1; - kill_proc(pid, SIGKILL, 1); - - schedule(); - - remove_wait_queue(&envctrl.kenvd_wait, &wait); - current->state = TASK_RUNNING; - } - - t = envctrl.thermistors; - while (t) { - envctrl.thermistors = t->next; - kfree(t); - t = envctrl.thermistors; - } - - f = envctrl.fans; - while (f) { - envctrl.fans = f->next; - kfree(f); - f = envctrl.fans; - } - - if (envctrl.cpu_temp_table) - kfree(envctrl.cpu_temp_table); - - if (envctrl.cpu_fan_speeds) - kfree(envctrl.cpu_fan_speeds); - - if (envctrl.ps_temp_table) - kfree(envctrl.ps_temp_table); - - if (envctrl.ps_fan_speeds) - kfree(envctrl.ps_fan_speeds); -} - - -static unsigned char -axi_get_temperature(struct thermistor *t) -{ - unsigned char value; - - if (read_8591(t->regs.addr, t->regs.port, &value) < 0) - return MAX_TEMPERATURE; - if (t->regs.port == AXI_THERM_PORT_CPU) - return axi_cpu_temp_table[value]; - else - return axi_mod_temp_table[value]; + return envctrl_i2c_data_translate(data, pchild->tblprop_array[i].type, + pchild->tblprop_array[i].scale, + tbl, bufdata); } -static unsigned char -axi_get_fan_speed(struct thermistor *t) +/* Function Description: Read noncpu-related data such as motherboard + * temperature. + * Return: Number of read bytes. Data is stored in bufdata in ascii format. + */ +static int envctrl_read_noncpu_info(struct i2c_child_t *pchild, + char mon_type, unsigned char *bufdata) { - unsigned char temp; - - temp = t->temperature(t); - if (temp >= MAX_TEMPERATURE) - return MAX_FAN_SPEED; - - return axi_fan_speeds[temp]; -} + unsigned char data; + int i; + char *tbl = NULL; -static int -axi_set_fan_speed(struct fan *f, unsigned char value) -{ - if (value != f->value) { - if (write_8444(f->regs.addr, f->regs.port, value)) - return -1; - f->value = value; + for (i = 0; i < PCF8584_MAX_CHANNELS; i++) { + if (pchild->mon_type[i] == mon_type) + break; } - return 0; -} -static void -axi_toggle_i2c_int(struct environment *env) -{ - unsigned char data; + if (i >= PCF8584_MAX_CHANNELS) + return 0; - if (i2c_read(AXI_PIO_ADDR, &data, 1, 0) != 1) - return; + /* Read data from address and port. */ + data = envctrl_i2c_read_8591((unsigned char)pchild->addr, + (unsigned char)pchild->chnl_array[i].chnl_no); - data &= ~(0x08); - if (i2c_write(AXI_PIO_ADDR, &data, 1, 0) != 1) - return; - mdelay(1); + /* Find decoding table. */ + tbl = pchild->tables + pchild->tblprop_array[i].offset; - data |= 0x08; - if (i2c_write(AXI_PIO_ADDR, &data, 1, 0) != 1) - return; - mdelay(1); + return envctrl_i2c_data_translate(data, pchild->tblprop_array[i].type, + pchild->tblprop_array[i].scale, + tbl, bufdata); } - -static int -rasctrl_setup(int node) +/* Function Description: Read fan status. + * Return : Always 1 byte. Status stored in bufdata. + */ +static int envctrl_i2c_fan_status(struct i2c_child_t *pchild, + unsigned char data, + char *bufdata) { - struct thermistor *t, **tlast; - struct fan *f, **flast; - char tmp[32]; - int monitor; - int shutdown; - int warning; - int i; + unsigned char tmp, ret = 0; + int i, j = 0; - prom_getstring(prom_root_node, "name", tmp, sizeof(tmp)); - if (strcmp(tmp, "SUNW,UltraSPARC-IIi-Engine")) { - printk("SUNW,rasctrl will work only on Ultra AXi\n"); - return -ENODEV; - } + tmp = data & pchild->fan_mask; - monitor = prom_getintdefault(node, "env-monitor", 0); - if (monitor == 0) - return -ENODEV; + if (tmp == pchild->fan_mask) { + /* All bits are on. All fans are functioning. */ + ret = ENVCTRL_ALL_FANS_GOOD; + } else if (tmp == 0) { + /* No bits are on. No fans are functioning. */ + ret = ENVCTRL_ALL_FANS_BAD; + } else { + /* Go through all channels, mark 'on' the matched bits. + * Notice that fan_mask may have discontiguous bits but + * return mask are always contiguous. For example if we + * monitor 4 fans at channels 0,1,2,4, the return mask + * should be 00010000 if only fan at channel 4 is working. + */ + for (i = 0; i < PCF8584_MAX_CHANNELS;i++) { + if (pchild->fan_mask & chnls_mask[i]) { + if (!(chnls_mask[i] & tmp)) + ret |= chnls_mask[j]; - envctrl.interval = prom_getintdefault(node, "env-mon-interval", 60); - warning = prom_getintdefault(node, "warning-temp", 55); - shutdown = prom_getintdefault(node, "shutdown-temp", 58); - - tlast = &envctrl.thermistors; - for (i = 0; i < 4; i++) { - t = kmalloc(sizeof(struct thermistor), GFP_KERNEL); - if (!t) - goto out; - memset(t, 0, sizeof(struct thermistor)); - - t->regs.addr = AXI_THERM_ADDR; - t->regs.port = i; - t->regs.warning = warning; - t->regs.shutdown = shutdown; - - switch (i) { - case AXI_THERM_PORT_CPU: - sprintf(t->name, "%.7s", "CPU"); - break; - case AXI_THERM_PORT_MOD: - sprintf(t->name, "%.7s", "MOD"); - break; - case AXI_THERM_PORT_PCI: - sprintf(t->name, "%.7s", "PCI"); - break; - case AXI_THERM_PORT_DISK: - sprintf(t->name, "%.7s", "DISK"); - break; + j++; + } } + } - t->temperature = axi_get_temperature; - t->fan_speed = axi_get_fan_speed; + bufdata[0] = ret; + return 1; +} - if (!i2c_find_device(t->regs.addr)) { - printk("envctrl: `%s': i2c device %02x not found\n", - t->name, t->regs.addr); - kfree(t); - continue; +/* Function Description: Read voltage and power supply status. + * Return : Always 1 byte. Status stored in bufdata. + */ +static unsigned char envctrl_i2c_voltage_status(struct i2c_child_t *pchild, + unsigned char data, + char *bufdata) +{ + unsigned char tmp, ret = 0; + int i, j = 0; + + tmp = data & pchild->voltage_mask; + + /* Two channels are used to monitor voltage and power supply. */ + if (tmp == pchild->voltage_mask) { + /* All bits are on. Voltage and power supply are okay. */ + ret = ENVCTRL_VOLTAGE_POWERSUPPLY_GOOD; + } else if (tmp == 0) { + /* All bits are off. Voltage and power supply are bad */ + ret = ENVCTRL_VOLTAGE_POWERSUPPLY_BAD; + } else { + /* Either voltage or power supply has problem. */ + for (i = 0; i < PCF8584_MAX_CHANNELS; i++) { + if (pchild->voltage_mask & chnls_mask[i]) { + j++; + + /* Break out when there is a mismatch. */ + if (!(chnls_mask[i] & tmp)) + break; + } } - *tlast = t; - tlast = &t->next; + /* Make a wish that hardware will always use the + * first channel for voltage and the second for + * power supply. + */ + if (j == 1) + ret = ENVCTRL_VOLTAGE_BAD; + else + ret = ENVCTRL_POWERSUPPLY_BAD; } - flast = &envctrl.fans; - for (i = 0; i < 2; i++) { - f = kmalloc(sizeof(struct fan), GFP_KERNEL); - if (!f) - goto out; - memset(f, 0, sizeof(struct fan)); - - f->regs.addr = AXI_FAN_ADDR; - f->regs.port = i; - - switch (i) { - case AXI_FAN_PORT_FRONT: - sprintf(f->name, "%.7s", "FRONT"); - t = NULL; - while ((t = find_thermistor("CPU", t))) { - t->chain = f->monitor; - f->monitor = t; - } - break; - case AXI_FAN_PORT_BACK: - sprintf(f->name, "%.7s", "BACK"); - t = NULL; - while ((t = find_thermistor("PCI", t))) { - t->chain = f->monitor; - f->monitor = t; - } - break; - } + bufdata[0] = ret; + return 1; +} - if (!f->monitor) { - kfree(f); - continue; - } +/* Function Description: Read a byte from /dev/envctrl. Mapped to user read(). + * Return: Number of read bytes. 0 for error. + */ +static ssize_t +envctrl_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct i2c_child_t *pchild; + unsigned char data[10]; + int ret = 0; + + /* Get the type of read as decided in ioctl() call. + * Find the appropriate i2c child. + * Get the data and put back to the user buffer. + */ + + switch ((int)(long)file->private_data) { + case ENVCTRL_RD_WARNING_TEMPERATURE: + if (warning_temperature == 0) + return 0; + + data[0] = (unsigned char)(warning_temperature); + ret = 1; + copy_to_user((unsigned char *)buf, data, ret); + break; + + case ENVCTRL_RD_SHUTDOWN_TEMPERATURE: + if (shutdown_temperature == 0) + return 0; + + data[0] = (unsigned char)(shutdown_temperature); + ret = 1; + copy_to_user((unsigned char *)buf, data, ret); + break; + + case ENVCTRL_RD_MTHRBD_TEMPERATURE: + if (!(pchild = envctrl_get_i2c_child(ENVCTRL_MTHRBDTEMP_MON))) + return 0; + ret = envctrl_read_noncpu_info(pchild, ENVCTRL_MTHRBDTEMP_MON, data); + copy_to_user((unsigned char *)buf, data, ret); + break; + + case ENVCTRL_RD_CPU_TEMPERATURE: + if (!(pchild = envctrl_get_i2c_child(ENVCTRL_CPUTEMP_MON))) + return 0; + ret = envctrl_read_cpu_info(pchild, ENVCTRL_CPUTEMP_MON, data); + + /* Reset cpu to the default cpu0. */ + copy_to_user((unsigned char *)buf, data, ret); + break; + + case ENVCTRL_RD_CPU_VOLTAGE: + if (!(pchild = envctrl_get_i2c_child(ENVCTRL_CPUVOLTAGE_MON))) + return 0; + ret = envctrl_read_cpu_info(pchild, ENVCTRL_CPUVOLTAGE_MON, data); + + /* Reset cpu to the default cpu0. */ + copy_to_user((unsigned char *)buf, data, ret); + break; + + case ENVCTRL_RD_SCSI_TEMPERATURE: + if (!(pchild = envctrl_get_i2c_child(ENVCTRL_SCSITEMP_MON))) + return 0; + ret = envctrl_read_noncpu_info(pchild, ENVCTRL_SCSITEMP_MON, data); + copy_to_user((unsigned char *)buf, data, ret); + break; + + case ENVCTRL_RD_ETHERNET_TEMPERATURE: + if (!(pchild = envctrl_get_i2c_child(ENVCTRL_ETHERTEMP_MON))) + return 0; + ret = envctrl_read_noncpu_info(pchild, ENVCTRL_ETHERTEMP_MON, data); + copy_to_user((unsigned char *)buf, data, ret); + break; + + case ENVCTRL_RD_FAN_STATUS: + if (!(pchild = envctrl_get_i2c_child(ENVCTRL_FANSTAT_MON))) + return 0; + data[0] = envctrl_i2c_read_8574(pchild->addr); + ret = envctrl_i2c_fan_status(pchild,data[0], data); + copy_to_user((unsigned char *)buf, data, ret); + break; + + case ENVCTRL_RD_VOLTAGE_STATUS: + if (!(pchild = envctrl_get_i2c_child(ENVCTRL_VOLTAGESTAT_MON))) + return 0; + data[0] = envctrl_i2c_read_8574(pchild->addr); + ret = envctrl_i2c_voltage_status(pchild, data[0], data); + copy_to_user((unsigned char *)buf, data, ret); + break; + + default: + break; + + }; + + return ret; +} + +/* Function Description: Command what to read. Mapped to user ioctl(). + * Return: Gives 0 for implemented commands, -EINVAL otherwise. + */ +static int +envctrl_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + char *infobuf; - if (!i2c_find_device(f->regs.addr)) { - printk("envctrl: `%s': i2c device %02x not found\n", - f->name, f->regs.addr); - kfree(f); - continue; + switch (cmd) { + case ENVCTRL_RD_WARNING_TEMPERATURE: + case ENVCTRL_RD_SHUTDOWN_TEMPERATURE: + case ENVCTRL_RD_MTHRBD_TEMPERATURE: + case ENVCTRL_RD_FAN_STATUS: + case ENVCTRL_RD_VOLTAGE_STATUS: + case ENVCTRL_RD_ETHERNET_TEMPERATURE: + case ENVCTRL_RD_SCSI_TEMPERATURE: + file->private_data = (void *)(long)cmd; + break; + + case ENVCTRL_RD_CPU_TEMPERATURE: + case ENVCTRL_RD_CPU_VOLTAGE: + /* Check to see if application passes in any cpu number, + * the default is cpu0. + */ + infobuf = (char *) arg; + if (infobuf == NULL) { + read_cpu = 0; + }else { + get_user(read_cpu, infobuf); } - *flast = f; - flast = &f->next; + /* Save the command for use when reading. */ + file->private_data = (void *)(long)cmd; + break; - f->check_failure = NULL; - f->set_speed = axi_set_fan_speed; - } - - envctrl.enable = axi_toggle_i2c_int; - envctrl.disable = envctrl_fans_blast; + default: + return -EINVAL; + }; -#ifdef DEBUG - printk("Warn: %d C, Shutdown %d C, Interval %d s, Monitor %d\n", - warning, shutdown, envctrl.interval, monitor); -#endif return 0; - -out: - return -ENODEV; -} - - -#ifdef U450_SUPPORT - -static unsigned char -envctrl_get_temperature(struct thermistor *t) -{ - unsigned char value; - - if (read_8591(t->regs.addr, t->regs.port, &value) < 0) - return MAX_TEMPERATURE; - if (!strncmp(t->name, "CPU", 3)) - return envctrl.cpu_temp_table[value]; - else - return envctrl.ps_temp_table[value]; } -static unsigned char -envctrl_get_fan_speed(struct thermistor *t) +/* Function Description: open device. Mapped to user open(). + * Return: Always 0. + */ +static int +envctrl_open(struct inode *inode, struct file *file) { - unsigned char temp; - - temp = t->temperature(t); - if (temp >= MAX_TEMPERATURE) - return MAX_FAN_SPEED; - - if (!strncmp(t->name, "CPU", 3)) - return envctrl.cpu_fan_speeds[temp]; - else - return envctrl.ps_fan_speeds[temp]; + file->private_data = 0; + MOD_INC_USE_COUNT; + return 0; } +/* Function Description: Open device. Mapped to user close(). + * Return: Always 0. + */ static int -envctrl_set_fan_speed(struct fan *f, unsigned char value) +envctrl_release(struct inode *inode, struct file *file) { - if (value != f->value) { - if (write_8444(f->regs.addr, f->regs.port, value)) - return -1; - f->value = value; - } - + MOD_DEC_USE_COUNT; return 0; } -static unsigned char u450_default_thermisters[] = -{ - /* CPU0 */ - 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46, - 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x55, 0x30, 0x00, - /* CPU1 */ - 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46, - 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x55, 0x31, 0x00, - /* CPU2 */ - 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46, - 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x55, 0x32, 0x00, - /* CPU3 */ - 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46, - 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x55, 0x33, 0x00, - /* PS0 */ - 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x5a, - 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x50, 0x53, 0x30, 0x00, - /* PS1 */ - 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x5a, - 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x50, 0x53, 0x31, 0x00, - /* PS2 */ - 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x5a, - 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x50, 0x53, 0x32, 0x00, - /* AMB */ - 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x28, - 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x41, 0x4d, 0x42, 0x00 -}; - -static unsigned char u450_default_cpu_temp_factors[] = -{ - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x94, 0x92, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, - 0x8a, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, - 0x81, 0x80, 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, - 0x79, 0x79, 0x78, 0x78, 0x77, 0x76, 0x75, 0x74, - 0x73, 0x72, 0x71, 0x70, 0x70, 0x6f, 0x6f, 0x6e, - 0x6e, 0x6e, 0x6d, 0x6d, 0x6c, 0x6b, 0x6a, 0x6a, - 0x69, 0x69, 0x68, 0x67, 0x66, 0x65, 0x65, 0x64, - 0x64, 0x64, 0x63, 0x63, 0x62, 0x62, 0x61, 0x61, - 0x60, 0x60, 0x5f, 0x5f, 0x5e, 0x5e, 0x5d, 0x5d, - 0x5c, 0x5c, 0x5b, 0x5b, 0x5b, 0x5a, 0x5a, 0x5a, - 0x59, 0x59, 0x58, 0x58, 0x57, 0x57, 0x56, 0x56, - 0x55, 0x55, 0x54, 0x54, 0x53, 0x53, 0x52, 0x52, - 0x52, 0x51, 0x51, 0x50, 0x50, 0x50, 0x50, 0x4f, - 0x4f, 0x4f, 0x4e, 0x4e, 0x4e, 0x4d, 0x4d, 0x4d, - 0x4c, 0x4c, 0x4c, 0x4b, 0x4b, 0x4b, 0x4a, 0x4a, - 0x4a, 0x49, 0x49, 0x49, 0x48, 0x48, 0x48, 0x47, - 0x47, 0x47, 0x46, 0x46, 0x46, 0x46, 0x45, 0x45, - 0x45, 0x44, 0x44, 0x44, 0x44, 0x43, 0x43, 0x43, - 0x43, 0x42, 0x42, 0x42, 0x42, 0x41, 0x41, 0x41, - 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, 0x3e, - 0x3e, 0x3d, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, - 0x3c, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, - 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, 0x37, - 0x37, 0x37, 0x36, 0x36, 0x36, 0x35, 0x35, 0x35, - 0x34, 0x34, 0x34, 0x33, 0x33, 0x33, 0x33, 0x32, - 0x32, 0x32, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, - 0x2f, 0x2f, 0x2f, 0x2e, 0x2e, 0x2e, 0x2d, 0x2d, - 0x2d, 0x2c, 0x2c, 0x2c, 0x2b, 0x2b, 0x2b, 0x2a, - 0x2a, 0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x28 -}; - -static unsigned char u450_default_cpu_fan_speeds[] = -{ - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x21, 0x22, 0x23, - 0x24, 0x25, 0x26, 0x27, 0x28, 0x2a, 0x2b, 0x2d, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f -}; - -static unsigned char u450_default_ps_temp_factors[] = -{ - 0x9a, 0x96, 0x82, 0x7d, 0x78, 0x73, 0x6e, 0x6b, - 0x69, 0x67, 0x64, 0x5f, 0x5a, 0x57, 0x55, 0x53, - 0x51, 0x50, 0x4e, 0x4d, 0x4c, 0x4b, 0x49, 0x47, - 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3f, - 0x3e, 0x3d, 0x3c, 0x3c, 0x3b, 0x3a, 0x39, 0x39, - 0x38, 0x37, 0x37, 0x36, 0x35, 0x35, 0x34, 0x33, - 0x32, 0x32, 0x32, 0x31, 0x31, 0x30, 0x30, 0x2f, - 0x2f, 0x2e, 0x2e, 0x2d, 0x2d, 0x2c, 0x2c, 0x2b, - 0x2a, 0x2a, 0x29, 0x29, 0x28, 0x28, 0x27, 0x27, - 0x26, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x24, - 0x23, 0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21, - 0x21, 0x20, 0x20, 0x20, 0x1f, 0x1f, 0x1e, 0x1e, - 0x1e, 0x1d, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, 0x1c, - 0x1b, 0x1b, 0x1b, 0x1a, 0x1a, 0x1a, 0x19, 0x19, - 0x19, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, - 0x17, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15, - 0x14, 0x14, 0x14, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x12, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x11, - 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; +static struct file_operations envctrl_fops = { + owner: THIS_MODULE, + read: envctrl_read, + ioctl: envctrl_ioctl, + open: envctrl_open, + release: envctrl_release, +}; -static unsigned char u450_default_ps_fan_speeds[] = -{ - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x21, 0x22, 0x23, - 0x24, 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x2a, - 0x2b, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, - 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f +static struct miscdevice envctrl_dev = { + ENVCTRL_MINOR, + "envctrl", + &envctrl_fops }; -static void -u450_toggle_i2c_int(struct environment *env) +/* Function Description: Set monitor type based on firmware description. + * Return: None. + */ +static void envctrl_set_mon(struct i2c_child_t *pchild, + char *chnl_desc, + int chnl_no) +{ + /* Firmware only has temperature type. It does not distinguish + * different kinds of temperatures. We use channel description + * to disinguish them. + */ + if (!(strcmp(chnl_desc,"temp,cpu")) || + !(strcmp(chnl_desc,"temp,cpu0")) || + !(strcmp(chnl_desc,"temp,cpu1")) || + !(strcmp(chnl_desc,"temp,cpu2")) || + !(strcmp(chnl_desc,"temp,cpu3"))) + pchild->mon_type[chnl_no] = ENVCTRL_CPUTEMP_MON; + + if (!(strcmp(chnl_desc,"vddcore,cpu0")) || + !(strcmp(chnl_desc,"vddcore,cpu1")) || + !(strcmp(chnl_desc,"vddcore,cpu2")) || + !(strcmp(chnl_desc,"vddcore,cpu3"))) + pchild->mon_type[chnl_no] = ENVCTRL_CPUVOLTAGE_MON; + + if (!(strcmp(chnl_desc,"temp,motherboard"))) + pchild->mon_type[chnl_no] = ENVCTRL_MTHRBDTEMP_MON; + + if (!(strcmp(chnl_desc,"temp,scsi"))) + pchild->mon_type[chnl_no] = ENVCTRL_SCSITEMP_MON; + + if (!(strcmp(chnl_desc,"temp,ethernet"))) + pchild->mon_type[chnl_no] = ENVCTRL_ETHERTEMP_MON; + + if (!(strcmp(chnl_desc,"temp,ethernet"))) + pchild->mon_type[chnl_no] = ENVCTRL_ETHERTEMP_MON; +} + +/* Function Description: Initialize monitor channel with channel desc, + * decoding tables, monitor type, optional properties. + * Return: None. + */ +static void envctrl_init_adc(struct i2c_child_t *pchild, int node) +{ + char chnls_desc[CHANNEL_DESC_SZ]; + int i, len, j = 0; + char *ptr; + + /* Firmware describe channels into a stream separated by a '\0'. + * Replace all '\0' with a space. + */ + len = prom_getproperty(node, "channels-description", chnls_desc, + CHANNEL_DESC_SZ); + for (i = 0; i < len; i++) { + if (chnls_desc[i] == '\0') + chnls_desc[i] = ' '; + } + + ptr = strtok(chnls_desc, " "); + while (ptr != NULL) { + envctrl_set_mon(pchild, ptr, j); + ptr = strtok(NULL, " "); + j++; + } + + /* Get optional properties. */ + len = prom_getproperty(node, "warning-temp", (char *)&warning_temperature, + sizeof(warning_temperature)); + len = prom_getproperty(node, "shutdown-temp", (char *)&shutdown_temperature, + sizeof(shutdown_temperature)); +} + +/* Function Description: Initialize child device monitoring fan status. + * Return: None. + */ +static void envctrl_init_fanstat(struct i2c_child_t *pchild) { - unsigned char tmp[80]; - unsigned char data; - int i, n; + int i; - write_8583(U450_TIMER_ADDR, 0, 0x84); - write_8583(U450_TIMER_ADDR, 8, 0x0a); - write_8583(U450_TIMER_ADDR, 7, 0x00); - write_8583(U450_TIMER_ADDR, 0, 0x04); + /* Go through all channels and set up the mask. */ + for (i = 0; i < pchild->total_chnls; i++) + pchild->fan_mask |= chnls_mask[(pchild->chnl_array[i]).chnl_no]; - n = sprintf(tmp, "envctrl: PCF8583:"); - for (i = 0; i < 16; i++) { - if (read_8583(U450_TIMER_ADDR, i, &data) < 0) { - printk("envctrl: error reading PCF8583\n"); - break; - } - n += sprintf(tmp+n, " %02x", data); - } - printk("%s\n", tmp); - -#if 1 - data = 0x70; - if (i2c_write(U450_PIO_ADDR, &data, 1, 0) != 1) - return; - mdelay(1); - - data = 0x78; - if (i2c_write(U450_PIO_ADDR, &data, 1, 0) != 1) - return; - mdelay(1); -#endif + /* We only need to know if this child has fan status monitored. + * We dont care which channels since we have the mask already. + */ + pchild->mon_type[0] = ENVCTRL_FANSTAT_MON; } -static void -u450_set_egg_timer(struct environment *env) +/* Initialize child device monitoring voltage status. */ +static void envctrl_init_voltage_status(struct i2c_child_t *pchild) { - unsigned char value; - -#if 0 - write_8583(U450_TIMER_ADDR, 0x00, 0x84); - read_8583(U450_TIMER_ADDR, 0x07, &value); - write_8583(U450_TIMER_ADDR, 0x07, 0x00); - write_8583(U450_TIMER_ADDR, 0x00, 0x04); -#else - read_8583(U450_TIMER_ADDR, 0x07, &value); - printk("envctrl: TIMER [%02x:07]: %02x\n", U450_TIMER_ADDR, value); - read_8583(U450_TIMER_ADDR, 0x00, &value); - printk("envctrl: TIMER [%02x:00]: %02x\n", U450_TIMER_ADDR, value); -#endif -} + int i; -static int -envctrl_setup(int node) -{ - struct thermistor *t, **tlast; - struct fan *f, **flast; - unsigned char *tmp = NULL, *p; - int len, n, err; - int defaults = 0; - - len = prom_getproplen(node, "thermisters"); - if (len <= 0) { - printk("envctrl: no property `thermisters', using defaults\n"); - defaults++; - len = sizeof(u450_default_thermisters); - } + /* Go through all channels and set up the mask. */ + for (i = 0; i < pchild->total_chnls; i++) + pchild->voltage_mask |= chnls_mask[(pchild->chnl_array[i]).chnl_no]; - tmp = (unsigned char *)kmalloc(len, GFP_KERNEL); - if (!tmp) { - printk("envctrl: can't allocate property buffer\n"); - return -ENODEV; - } + /* We only need to know if this child has voltage status monitored. + * We dont care which channels since we have the mask already. + */ + pchild->mon_type[0] = ENVCTRL_VOLTAGESTAT_MON; +} - if (defaults) { - memcpy(tmp, u450_default_thermisters, len); - } else { - err = prom_getproperty(node, "thermisters", tmp, len); - if (err < 0) { - printk("envctrl: error reading property `thermisters'\n"); - kfree(tmp); - return -ENODEV; +/* Function Description: Initialize i2c child device. + * Return: None. + */ +static void envctrl_init_i2c_child(struct linux_ebus_child *edev_child, + struct i2c_child_t *pchild) +{ + int node, len, i, tbls_size = 0; + + node = edev_child->prom_node; + + /* Get device address. */ + len = prom_getproperty(node, "reg", + (char *) &(pchild->addr), + sizeof(pchild->addr)); + + /* Get tables property. Read firmware temperature tables. */ + len = prom_getproperty(node, "translation", + (char *) pchild->tblprop_array, + (PCF8584_MAX_CHANNELS * + sizeof(struct pcf8584_tblprop))); + if (len > 0) { + pchild->total_tbls = len / sizeof(struct pcf8584_tblprop); + for (i = 0; i < pchild->total_tbls; i++) { + if ((pchild->tblprop_array[i].size + pchild->tblprop_array[i].offset) > tbls_size) { + tbls_size = pchild->tblprop_array[i].size + pchild->tblprop_array[i].offset; + } } - } - - p = tmp; - err = -ENOMEM; - tlast = &envctrl.thermistors; - while (len > sizeof(struct therm_regs)) { - t = kmalloc(sizeof(struct thermistor), GFP_KERNEL); - if (!t) { - printk("envctrl: can't allocate thermistor struct\n"); - goto out; - } - memset(t, 0, sizeof(struct thermistor)); - - memcpy(&t->regs, p, sizeof(struct therm_regs)); - p += sizeof(struct therm_regs); - len -= sizeof(struct therm_regs); - - n = strlen(p) + 1; - strncpy(t->name, p, 7); - p += n; - len -= n; - - if (!i2c_find_device(t->regs.addr)) { - printk("envctrl: `%s': i2c device %02x not found\n", - t->name, t->regs.addr); - kfree(t); - continue; + pchild->tables = kmalloc(tbls_size, GFP_KERNEL); + len = prom_getproperty(node, "tables", + (char *) pchild->tables, tbls_size); + if (len <= 0) { + printk("envctrl: Failed to get table.\n"); + return; } + } - t->temperature = envctrl_get_temperature; - t->fan_speed = envctrl_get_fan_speed; + /* Get the monitor channels. */ + len = prom_getproperty(node, "channels-in-use", + (char *) pchild->chnl_array, + (PCF8584_MAX_CHANNELS * + sizeof(struct pcf8584_channel))); + pchild->total_chnls = len / sizeof(struct pcf8584_channel); - *tlast = t; - tlast = &t->next; - } + for (i = 0; i < pchild->total_chnls; i++) { + switch (pchild->chnl_array[i].type) { + case PCF8584_TEMP_TYPE: + envctrl_init_adc(pchild, node); + break; - flast = &envctrl.fans; - for (n = 0; n < 2; n++) { - f = kmalloc(sizeof(struct fan), GFP_KERNEL); - if (!f) - goto out; - memset(f, 0, sizeof(struct fan)); - - f->regs.addr = U450_FAN_ADDR; - f->regs.port = n; - - switch (n) { - case U450_FAN_PORT_CPU: - sprintf(f->name, "%.7s", "CPU"); - t = NULL; - while ((t = find_thermistor("CPU", t))) { - t->chain = f->monitor; - f->monitor = t; - } + case PCF8584_FANSTAT_TYPE: + envctrl_init_fanstat(pchild); + i = pchild->total_chnls; break; - case U450_FAN_PORT_PS: - sprintf(f->name, "%.7s", "PS"); - t = NULL; - while ((t = find_thermistor("PS", t))) { - t->chain = f->monitor; - f->monitor = t; + + case PCF8584_VOLTAGE_TYPE: + if (pchild->i2ctype == I2C_ADC) { + envctrl_init_adc(pchild,node); + } else { + envctrl_init_voltage_status(pchild); } + i = pchild->total_chnls; break; - } - - if (!f->monitor) { - kfree(f); - continue; - } - - if (!i2c_find_device(f->regs.addr)) { - printk("envctrl: `%s': i2c device %02x not found\n", - f->name, f->regs.addr); - kfree(f); - continue; - } - - *flast = f; - flast = &f->next; - f->check_failure = NULL; - f->set_speed = envctrl_set_fan_speed; - } - - envctrl.cpu_temp_table = kmalloc(256, GFP_KERNEL); - if (!envctrl.cpu_temp_table) { - printk("envctrl: can't allocate temperature table\n"); - goto out; - } - if (defaults) { - memcpy(envctrl.cpu_temp_table, - u450_default_cpu_temp_factors, 256); - } else { - err = prom_getproperty(node, "cpu-temp-factors", - envctrl.cpu_temp_table, 256); - if (err < 0) { - printk("envctrl: can't read `cpu-temp-factors'\n"); - goto out; - } - } - - envctrl.cpu_fan_speeds = kmalloc(112, GFP_KERNEL); - if (!envctrl.cpu_fan_speeds) { - printk("envctrl: can't allocate fan speed table\n"); - goto out; - } - if (defaults) { - memcpy(envctrl.cpu_fan_speeds, - u450_default_cpu_fan_speeds, 112); - } else { - err = prom_getproperty(node, "cpu-fan-speeds", - envctrl.cpu_fan_speeds, 112); - if (err < 0) { - printk("envctrl: can't read `cpu-fan-speeds'\n"); - goto out; - } - } - - envctrl.ps_temp_table = kmalloc(256, GFP_KERNEL); - if (!envctrl.ps_temp_table) { - printk("envctrl: can't allocate temperature table\n"); - goto out; - } - if (defaults) { - memcpy(envctrl.ps_temp_table, - u450_default_ps_temp_factors, 256); - } else { - err = prom_getproperty(node, "ps-temp-factors", - envctrl.ps_temp_table, 256); - if (err < 0) { - printk("envctrl: can't read `ps-temp-factors'\n"); - goto out; - } - } - - envctrl.ps_fan_speeds = kmalloc(112, GFP_KERNEL); - if (!envctrl.ps_fan_speeds) { - printk("envctrl: can't allocate fan speed table\n"); - goto out; - } - if (defaults) { - memcpy(envctrl.ps_fan_speeds, - u450_default_ps_fan_speeds, 112); - } else { - err = prom_getproperty(node, "ps-fan-speeds", - envctrl.ps_fan_speeds, 112); - if (err < 0) { - printk("envctrl: can't read `ps-fan-speeds'\n"); - goto out; - } + default: + break; + }; } - - envctrl.enable = u450_toggle_i2c_int; - envctrl.keep_alive = u450_set_egg_timer; - envctrl.disable = envctrl_fans_blast; - envctrl.interval = 60; - - kfree(tmp); - return 0; - -out: - if (tmp) - kfree(tmp); - return err; -} -#endif /* U450_SUPPORT */ - - - -static loff_t -envctrl_llseek(struct file *file, loff_t offset, int type) -{ - return -ESPIPE; -} - -static ssize_t -envctrl_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - unsigned long addr = (unsigned long)file->private_data; - - return i2c_read(addr, buf, count, 1); -} - -static ssize_t -envctrl_write(struct file *file, const char *buf, size_t count, loff_t *ppos) -{ - unsigned long addr = (unsigned long)file->private_data; - - return i2c_write(addr, buf, count, 1); } -static int -envctrl_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +/* Function Description: Search the child device list for a device. + * Return : The i2c child if found. NULL otherwise. + */ +static struct i2c_child_t *envctrl_get_i2c_child(unsigned char mon_type) { - unsigned long data; - int addr; + int i, j; - switch (cmd) { - case I2CIOCSADR: - if (get_user(addr, (int *)arg)) - return -EFAULT; - data = addr & 0xfe; - if (!i2c_find_device(addr & 0xfe)) - return -ENODEV; - file->private_data = (void *)data; - break; - case I2CIOCGADR: - addr = (unsigned long)file->private_data; - if (put_user(addr, (int *)arg)) - return -EFAULT; - break; - default: - return -EINVAL; + for (i = 0; i < ENVCTRL_MAX_CPU*2; i++) { + for (j = 0; j < PCF8584_MAX_CHANNELS; j++) { + if (i2c_childlist[i].mon_type[j] == mon_type) { + return (struct i2c_child_t*)(&(i2c_childlist[i])); + } + } } - return 0; + return NULL; } -static int -envctrl_open(struct inode *inode, struct file *file) -{ - file->private_data = 0; - return 0; -} - -static struct file_operations envctrl_fops = { - owner: THIS_MODULE, - llseek: envctrl_llseek, - read: envctrl_read, - write: envctrl_write, - ioctl: envctrl_ioctl, - open: envctrl_open, -}; - -static struct miscdevice envctrl_dev = { - ENVCTRL_MINOR, - "envctrl", - &envctrl_fops -}; - -#ifdef MODULE -int init_module(void) -#else -int __init envctrl_init(void) -#endif +static int __init envctrl_init(void) { #ifdef CONFIG_PCI - struct linux_ebus *ebus; - struct linux_ebus_device *edev = 0; - pid_t pid; - int err; - + struct linux_ebus *ebus = NULL; + struct linux_ebus_device *edev = NULL; + struct linux_ebus_child *edev_child = NULL; + int i = 0; + + /* Traverse through ebus and ebus device list for i2c device and + * adc and gpio nodes. + */ for_each_ebus(ebus) { for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->prom_name, "SUNW,envctrl")) - goto ebus_done; - if (!strcmp(edev->prom_name, "SUNW,rasctrl")) - goto ebus_done; + if (!strcmp(edev->prom_name, "i2c")) { + i2c = ioremap( edev->resource[0].start, + sizeof(struct pcf8584_reg)); + for_each_edevchild(edev, edev_child) { + if (!strcmp("gpio", edev_child->prom_name)) { + i2c_childlist[i].i2ctype = I2C_GPIO; + envctrl_init_i2c_child(edev_child, &(i2c_childlist[i++])); + } + if (!strcmp("adc", edev_child->prom_name)) { + i2c_childlist[i].i2ctype = I2C_ADC; + envctrl_init_i2c_child(edev_child, &(i2c_childlist[i++])); + } + } + goto done; + } } } -ebus_done: - if (!edev) { - printk("%s: ebus device not found\n", __FUNCTION__); - return -ENODEV; - } - - i2c_regs = (unsigned long) ioremap(edev->resource[0].start, I2C_REG_SIZE); - writeb(CONTROL_PIN, i2c_regs + I2C_CSR); - writeb(PCF8584_ADDRESS >> 1, i2c_regs + I2C_DATA); - writeb(CONTROL_PIN | CONTROL_ES1, i2c_regs + I2C_CSR); - writeb(CLK_4_43 | BUS_CLK_90, i2c_regs + I2C_DATA); - writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_ACK, i2c_regs + I2C_CSR); - mdelay(10); - if (misc_register(&envctrl_dev)) { - printk("%s: unable to get misc minor %d\n", - __FUNCTION__, envctrl_dev.minor); +done: + if (!edev) { + printk("envctrl: I2C device not found.\n"); return -ENODEV; } - err = i2c_scan_bus(); - if (err) { - i2c_free_devices(); - misc_deregister(&envctrl_dev); - return err; - } + /* Set device address. */ + envctrl_writeb(CONTROL_PIN, &i2c->csr); + envctrl_writeb(PCF8584_ADDRESS, &i2c->data); - memset(&envctrl, 0, sizeof(struct environment)); + /* Set system clock and SCL frequencies. */ + envctrl_writeb(CONTROL_PIN | CONTROL_ES1, &i2c->csr); + envctrl_writeb(CLK_4_43 | BUS_CLK_90, &i2c->data); - err = -ENODEV; - if (!strcmp(edev->prom_name, "SUNW,rasctrl")) - err = rasctrl_setup(edev->prom_node); -#ifdef U450_SUPPORT - else if (!strcmp(edev->prom_name, "SUNW,envctrl")) - err = envctrl_setup(edev->prom_node); -#endif + /* Enable serial interface. */ + envctrl_writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_ACK, &i2c->csr); + udelay(200); - if (err) { - envctrl_stop(); - i2c_free_devices(); - misc_deregister(&envctrl_dev); - return err; + /* Register the device as a minor miscellaneous device. */ + if (misc_register(&envctrl_dev)) { + printk("envctrl: Unable to get misc minor %d\n", + envctrl_dev.minor); } - init_waitqueue_head(&envctrl.kenvd_wait); - - pid = kernel_thread(kenvd, (void *)&envctrl, CLONE_FS); - if (pid < 0) { - envctrl_stop(); - i2c_free_devices(); - misc_deregister(&envctrl_dev); - return -ENODEV; + /* Note above traversal routine post-incremented 'i' to accomodate + * a next child device, so we decrement before reverse-traversal of + * child devices. + */ + printk("envctrl: initialized "); + for(--i; i >= 0; --i) + { + printk("[%s 0x%lx]%s", + (I2C_ADC == i2c_childlist[i].i2ctype) ? ("adc") : + ((I2C_GPIO == i2c_childlist[i].i2ctype) ? ("gpio") : ("unknown")), + i2c_childlist[i].addr, (0 == i) ? ("\n") : (" ")); } return 0; @@ -1605,13 +946,18 @@ ebus_done: #endif } - -#ifdef MODULE -void cleanup_module(void) +static void __exit envctrl_cleanup(void) { - envctrl_stop(); - i2c_free_devices(); - iounmap(i2c_regs); + int i; + + iounmap(i2c); misc_deregister(&envctrl_dev); + + for (i = 0; i < ENVCTRL_MAX_CPU * 2; i++) { + if (i2c_childlist[i].tables) + kfree(i2c_childlist[i].tables); + } } -#endif + +module_init(envctrl_init); +module_exit(envctrl_cleanup); diff --git a/drivers/sound/nm256_audio.c b/drivers/sound/nm256_audio.c index c41c858d085d..27f432e173fd 100644 --- a/drivers/sound/nm256_audio.c +++ b/drivers/sound/nm256_audio.c @@ -58,9 +58,7 @@ nm256_release_ports (struct nm256_info *card) for (x = 0; x < 2; x++) { if (card->port[x].ptr != NULL) { - u32 size = - card->port[x].end_offset - card->port[x].start_offset; - release_region ((unsigned long) card->port[x].ptr, size); + iounmap (card->port[x].ptr); card->port[x].ptr = NULL; } } @@ -1025,7 +1023,7 @@ nm256_peek_for_sig (struct nm256_info *card) pointer); } - release_region ((unsigned long) temp, 16); + iounmap (temp); } /* diff --git a/drivers/sound/via82cxxx_audio.c b/drivers/sound/via82cxxx_audio.c index a58b333fd138..c527b6aec1fe 100644 --- a/drivers/sound/via82cxxx_audio.c +++ b/drivers/sound/via82cxxx_audio.c @@ -13,15 +13,16 @@ * http://gtf.org/garzik/drivers/via82cxxx/ * */ - -#define VIA_VERSION "1.1.8" + +#define VIA_VERSION "1.1.12" #include #include #include #include +#include #include #include #include @@ -31,10 +32,13 @@ #include #include #include +#include +#include #include #include #include #include +#include #undef VIA_DEBUG /* define to enable debugging output and checks */ @@ -45,7 +49,7 @@ #define DPRINTK(fmt, args...) #endif -#define VIA_NDEBUG /* define to disable lightweight runtime checks */ +#undef VIA_NDEBUG /* define to disable lightweight runtime checks */ #ifdef VIA_NDEBUG #define assert(expr) #else @@ -56,20 +60,15 @@ } #endif -/* user switch: undefine to exclude /proc data */ +#if defined(CONFIG_PROC_FS) && \ + defined(CONFIG_SOUND_VIA82CXXX_PROCFS) #define VIA_PROC_FS 1 - -/* don't mess with this */ -#ifndef CONFIG_PROC_FS -#undef VIA_PROC_FS #endif -#define arraysize(x) (sizeof(x)/sizeof(*(x))) +#define VIA_SUPPORT_MMAP 1 /* buggy, for now... */ #define MAX_CARDS 1 -#define LINE_SIZE 10 - #define VIA_CARD_NAME "VIA 82Cxxx Audio driver " VIA_VERSION #define VIA_MODULE_NAME "via82cxxx" #define PFX VIA_MODULE_NAME ": " @@ -80,6 +79,10 @@ #define VIA_DMA_BUFFERS 16 #define VIA_DMA_BUF_SIZE PAGE_SIZE +#ifndef AC97_PCM_LR_ADC_RATE +# define AC97_PCM_LR_ADC_RATE AC97_PCM_LR_DAC_RATE +#endif + /* 82C686 function 5 (audio codec) PCI configuration registers */ #define VIA_ACLINK_CTRL 0x41 #define VIA_FUNC_ENABLE 0x42 @@ -121,6 +124,10 @@ #define VIA_BASE0_AC97_CTRL 0x80 #define VIA_BASE0_SGD_STATUS_SHADOW 0x84 #define VIA_BASE0_GPI_INT_ENABLE 0x8C +#define VIA_INTR_OUT ((1<<0) | (1<<4) | (1<<8)) +#define VIA_INTR_IN ((1<<1) | (1<<5) | (1<<9)) +#define VIA_INTR_FM ((1<<2) | (1<<6) | (1<<10)) +#define VIA_INTR_MASK (VIA_INTR_OUT | VIA_INTR_IN | VIA_INTR_FM) /* VIA_BASE0_AUDIO_xxx_CHAN_TYPE bits */ #define VIA_IRQ_ON_FLAG (1<<0) /* int on each flagged scatter block */ @@ -130,11 +137,10 @@ #define VIA_INT_SEL_ONE_LINE_LEFT (1<<3) /* int at less than one line to send */ #define VIA_PCM_FMT_STEREO (1<<4) /* PCM stereo format (bit clear == mono) */ #define VIA_PCM_FMT_16BIT (1<<5) /* PCM 16-bit format (bit clear == 8-bit) */ -#define VIA_PCM_FIFO (1<<6) /* enable FIFO? documented as "reserved" */ +#define VIA_PCM_REC_FIFO (1<<6) /* PCM Recording FIFO */ #define VIA_RESTART_SGD_ON_EOL (1<<7) /* restart scatter-gather at EOL */ #define VIA_PCM_FMT_MASK (VIA_PCM_FMT_STEREO|VIA_PCM_FMT_16BIT) #define VIA_CHAN_TYPE_MASK (VIA_RESTART_SGD_ON_EOL | \ - VIA_PCM_FIFO | \ VIA_IRQ_ON_FLAG | \ VIA_IRQ_ON_EOL) #define VIA_CHAN_TYPE_INT_SELECT (VIA_INT_SEL_LAST_SAMPLE_SENT) @@ -191,25 +197,24 @@ #define VIA_CR80_WRITE_MODE 0 #define VIA_CR80_REG_IDX(idx) ((((idx) & 0xFF) >> 1) << 16) -/* h r puff n stuff */ -#define VIA_FMT_STEREO 0x01 -#define VIA_FMT_16BIT 0x02 -#define VIA_FMT_MASK 0x03 -#define VIA_DAC_SHIFT 0 -#define VIA_ADC_SHIFT 4 - -/* undocumented(?) values for setting rate, from Via's source */ -#define VIA_SET_RATE_IN 0x00320000 /* set input rate */ -#define VIA_SET_RATE_OUT 0x002c0000 /* set output rate */ - +/* capabilities we announce */ +#ifdef VIA_SUPPORT_MMAP +#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | DSP_CAP_MMAP | \ + DSP_CAP_TRIGGER | DSP_CAP_REALTIME) +#else +#define VIA_DSP_CAP (DSP_CAP_REVISION | DSP_CAP_DUPLEX | \ + DSP_CAP_TRIGGER | DSP_CAP_REALTIME) +#endif /* scatter-gather DMA table entry, exactly as passed to hardware */ struct via_sgd_table { u32 addr; - u32 count; /* includes additional bits also */ + u32 count; /* includes additional VIA_xxx bits also */ }; + #define VIA_EOL (1 << 31) #define VIA_FLAG (1 << 30) +#define VIA_STOP (1 << 29) enum via_channel_states { @@ -220,27 +225,36 @@ enum via_channel_states { struct via_sgd_data { dma_addr_t handle; - volatile void *cpuaddr; + void *cpuaddr; }; struct via_channel { - unsigned rate; /* sample rate */ + atomic_t n_bufs; + atomic_t hw_ptr; + wait_queue_head_t wait; + unsigned int sw_ptr; + unsigned int slop_len; + unsigned int n_irqs; + int bytes; + + unsigned is_active : 1; + unsigned is_record : 1; + unsigned is_mapped : 1; + unsigned is_enabled : 1; u8 pcm_fmt; /* VIA_PCM_FMT_xxx */ - - atomic_t state; - atomic_t buf_in_use; - atomic_t next_buf; - + + unsigned rate; /* sample rate */ + volatile struct via_sgd_table *sgtable; dma_addr_t sgt_handle; - + struct via_sgd_data sgbuf [VIA_DMA_BUFFERS]; - - wait_queue_head_t wait; - + long iobase; + + const char *name; }; @@ -248,20 +262,21 @@ struct via_channel { struct via_info { struct pci_dev *pdev; long baseaddr; - + struct ac97_codec ac97; spinlock_t lock; int card_num; /* unique card number, from 0 */ int dev_dsp; /* /dev/dsp index from register_sound_dsp() */ - + unsigned rev_h : 1; - wait_queue_head_t open_wait; - int open_mode; - + struct semaphore syscall_sem; + struct semaphore open_sem; + struct via_channel ch_in; struct via_channel ch_out; + struct via_channel ch_fm; }; @@ -269,6 +284,7 @@ struct via_info { static unsigned via_num_cards = 0; + /**************************************************************** * * prototypes @@ -285,6 +301,9 @@ static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wa static int via_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); static int via_dsp_open (struct inode *inode, struct file *file); static int via_dsp_release(struct inode *inode, struct file *file); +#ifdef VIA_SUPPORT_MMAP +static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma); +#endif static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg); static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value); @@ -292,8 +311,19 @@ static u8 via_ac97_wait_idle (struct via_info *card); static void via_chan_free (struct via_info *card, struct via_channel *chan); static void via_chan_clear (struct via_channel *chan); -static void via_chan_pcm_fmt (struct via_info *card, - struct via_channel *chan, int reset); +static void via_chan_pcm_fmt (struct via_channel *chan, int reset); + +#ifdef VIA_PROC_FS +static int via_init_proc (void); +static void via_cleanup_proc (void); +static int via_card_init_proc (struct via_info *card); +static void via_card_cleanup_proc (struct via_info *card); +#else +static inline int via_init_proc (void) { return 0; } +static inline void via_cleanup_proc (void) {} +static inline int via_card_init_proc (struct via_info *card) { return 0; } +static inline void via_card_cleanup_proc (struct via_info *card) {} +#endif /**************************************************************** @@ -338,7 +368,7 @@ static struct pci_driver via_driver = { * one of three PCM channels supported by the chip. * */ - + static inline void via_chan_stop (int iobase) { if (inb (iobase + VIA_PCM_STATUS) & VIA_SGD_ACTIVE) @@ -359,11 +389,11 @@ static inline void via_chan_stop (int iobase) * one of three PCM channels supported by the chip. * */ - + static inline void via_chan_status_clear (int iobase) { u8 tmp = inb (iobase + VIA_PCM_STATUS); - + if (tmp != 0) outb (tmp, iobase + VIA_PCM_STATUS); } @@ -376,70 +406,43 @@ static inline void via_chan_status_clear (int iobase) * Start scatter-gather DMA for the given channel. * */ - + static inline void sg_begin (struct via_channel *chan) { outb (VIA_SGD_START, chan->iobase + VIA_PCM_CONTROL); } -/** - * via_chan_bufs_in_use - Number of buffers waiting to be consumed - * @chan: Channel for which DMA buffers will be counted - * - * Count the number of buffers waiting to be consumed. For a - * playback operation, this is the number of buffers which have - * yet to be sent to the DAC. For a recording operation, this - * is the number of buffers waiting to be consumed by software - * calling read() system call. +/**************************************************************** * - */ - -static inline int via_chan_bufs_in_use (struct via_channel *chan) -{ - return atomic_read(&chan->next_buf) - - atomic_read(&chan->buf_in_use); -} - - -/** - * via_chan_full - Check for no-free-buffers condition - * @chan: Channel for which DMA full condition will be checked + * Miscellaneous debris * - * Count the number of buffers waiting to be consumed, and return - * true (non-zero) if no buffers are available to be filled on the - * given DMA channel. * */ - -static inline int via_chan_full (struct via_channel *chan) -{ - return (via_chan_bufs_in_use (chan) == VIA_DMA_BUFFERS); -} /** - * via_chan_empty - Check for no-buffers-in-use condition - * @chan: Channel for which DMA empty condition will be checked + * via_syscall_down - down the card-specific syscell semaphore + * @card: Private info for specified board + * @nonblock: boolean, non-zero if O_NONBLOCK is set * - * Count the number of buffers waiting to be consumed, and return - * true (non-zero) if no buffers are currently in use. + * Encapsulates standard method of acquiring the syscall sem. * + * Returns negative errno on error, or zero for success. */ - -static inline int via_chan_empty (struct via_channel *chan) -{ - return (atomic_read(&chan->next_buf) == - atomic_read(&chan->buf_in_use)); -} +static inline int via_syscall_down (struct via_info *card, int nonblock) +{ + if (nonblock) { + if (down_trylock (&card->syscall_sem)) + return -EAGAIN; + } else { + if (down_interruptible (&card->syscall_sem)) + return -ERESTARTSYS; + } -/**************************************************************** - * - * Miscellaneous debris - * - * - */ + return 0; +} /** @@ -469,101 +472,115 @@ static void via_stop_everything (struct via_info *card) via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN); via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN); via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN); - + /* * clear any enabled interrupt bits, reset to 8-bit mono PCM mode */ - outb (0, card->baseaddr + VIA_BASE0_PCM_OUT_CHAN); - outb (0, card->baseaddr + VIA_BASE0_PCM_IN_CHAN); - outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN); + outb (0, card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE); + outb (0, card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE); + outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE); DPRINTK ("EXIT\n"); } /** * via_set_rate - Set PCM rate for given channel - * @card: Private info for specified board + * @ac97: Pointer to generic codec info struct + * @chan: Private info for specified channel * @rate: Desired PCM sample rate, in Khz - * @inhale_deeply: Boolean. If non-zero (true), the recording sample rate - * is set. If zero (false), the playback sample rate - * is set. * * Sets the PCM sample rate for a channel. * * Values for @rate are clamped to a range of 4000 Khz through 48000 Khz, * due to hardware constraints. - * - * FIXME: @inhale_deeply argument is ignored, and %AC97_PCM_FRONT_DAC_RATE - * is the only rate which is really set. This needs to be fixed when - * recording support is added. */ -static int via_set_rate (struct via_info *card, unsigned rate, int inhale_deeply) +static int via_set_rate (struct ac97_codec *ac97, + struct via_channel *chan, unsigned rate) { + int rate_reg; - DPRINTK ("ENTER, rate = %d, inhale = %s\n", - rate, inhale_deeply ? "yes" : "no"); + DPRINTK ("ENTER, rate = %d\n", rate); if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; - - via_ac97_write_reg (&card->ac97, AC97_POWER_CONTROL, - (via_ac97_read_reg (&card->ac97, AC97_POWER_CONTROL) & ~0x0200) | + + rate_reg = chan->is_record ? AC97_PCM_LR_ADC_RATE : + AC97_PCM_FRONT_DAC_RATE; + + via_ac97_write_reg (ac97, AC97_POWER_CONTROL, + (via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200) | 0x0200); - via_ac97_write_reg (&card->ac97, AC97_PCM_FRONT_DAC_RATE, rate); - via_ac97_write_reg (&card->ac97, AC97_POWER_CONTROL, - via_ac97_read_reg (&card->ac97, AC97_POWER_CONTROL) & ~0x0200); - DPRINTK ("EXIT, returning 0\n"); - return rate; + via_ac97_write_reg (ac97, rate_reg, rate); + + via_ac97_write_reg (ac97, AC97_POWER_CONTROL, + via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200); + + udelay (10); + + /* the hardware might return a value different than what we + * passed to it, so read the rate value back from hardware + * to see what we came up with + */ + chan->rate = via_ac97_read_reg (ac97, rate_reg); + + DPRINTK ("EXIT, returning rate %d Hz\n", chan->rate); + return chan->rate; } -/** - * via_set_adc_rate - Set PCM rate for recording channel - * @card: Private info for specified board - * @rate: Desired PCM sample rate, in Khz +/**************************************************************** + * + * Channel-specific operations * - * Sets the PCM sample rate for a recording channel. * - * FIXME: @inhale_deeply argument to via_set_rate is ignored, and %AC97_PCM_FRONT_DAC_RATE - * is the only rate which is really set. Thus, this function will - * not work until via_set_rate is fixed. */ -static inline int via_set_adc_rate (struct via_info *card, int rate) -{ - return via_set_rate (card, rate, 1); -} - /** - * via_set_dac_rate - Set PCM rate for playback channel - * @card: Private info for specified board - * @rate: Desired PCM sample rate, in Khz + * via_chan_init_defaults - Initialize a struct via_channel + * @card: Private audio chip info + * @chan: Channel to be initialized * - * Sets the PCM sample rate for a playback channel. + * Zero @chan, and then set all static defaults for the structure. */ -static inline int via_set_dac_rate (struct via_info *card, int rate) +static void via_chan_init_defaults (struct via_info *card, struct via_channel *chan) { - return via_set_rate (card, rate, 0); + memset (chan, 0, sizeof (*chan)); + + if (chan == &card->ch_out) { + chan->name = "PCM-OUT"; + chan->iobase = card->baseaddr + VIA_BASE0_PCM_OUT_CHAN; + } else if (chan == &card->ch_in) { + chan->name = "PCM-IN"; + chan->iobase = card->baseaddr + VIA_BASE0_PCM_IN_CHAN; + chan->is_record = 1; + } else if (chan == &card->ch_fm) { + chan->name = "PCM-OUT-FM"; + chan->iobase = card->baseaddr + VIA_BASE0_FM_OUT_CHAN; + } else { + BUG(); + } + + init_waitqueue_head (&chan->wait); + + chan->pcm_fmt = VIA_PCM_FMT_MASK; + chan->is_enabled = 1; + + if (chan->is_record) + atomic_set (&chan->n_bufs, 0); + else + atomic_set (&chan->n_bufs, VIA_DMA_BUFFERS); + atomic_set (&chan->hw_ptr, 0); } -/**************************************************************** - * - * Channel-specific operations - * - * - */ - /** * via_chan_init - Initialize PCM channel * @card: Private audio chip info * @chan: Channel to be initialized - * @chan_ofs: Offset from PCI address, which determines the - * set of SGD registers to use. * * Performs all the preparations necessary to begin * using a PCM channel. @@ -579,15 +596,14 @@ static inline int via_set_dac_rate (struct via_info *card, int rate) * always "take" the address. */ -static int via_chan_init (struct via_info *card, - struct via_channel *chan, long chan_ofs) +static int via_chan_init (struct via_info *card, struct via_channel *chan) { int i; - unsigned long flags; - + DPRINTK ("ENTER\n"); - memset (chan, 0, sizeof (*chan)); + /* bzero channel structure, and init members to defaults */ + via_chan_init_defaults (card, chan); /* alloc DMA-able memory for scatter-gather table */ chan->sgtable = pci_alloc_consistent (card->pdev, @@ -598,8 +614,8 @@ static int via_chan_init (struct via_info *card, DPRINTK ("EXIT\n"); return -ENOMEM; } - - memset ((void*)chan->sgtable, 0, + + memset ((void*)chan->sgtable, 0, (sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS)); /* alloc DMA-able memory for scatter-gather buffers */ @@ -628,33 +644,26 @@ static int via_chan_init (struct via_info *card, virt_to_phys(chan->sgbuf[i].cpuaddr), chan->sgbuf[i].cpuaddr); #endif - } - init_waitqueue_head (&chan->wait); - - chan->pcm_fmt = VIA_PCM_FMT_MASK; - chan->iobase = card->baseaddr + chan_ofs; - - spin_lock_irqsave (&card->lock, flags); + assert ((VIA_DMA_BUF_SIZE % PAGE_SIZE) == 0); + } /* stop any existing channel output */ via_chan_clear (chan); via_chan_status_clear (chan->iobase); - via_chan_pcm_fmt (card, chan, 1); - - spin_unlock_irqrestore (&card->lock, flags); + via_chan_pcm_fmt (chan, 1); /* set location of DMA-able scatter-gather info table */ DPRINTK("outl (0x%X, 0x%04lX)\n", cpu_to_le32 (chan->sgt_handle), - card->baseaddr + chan_ofs + VIA_PCM_TABLE_ADDR); + chan->iobase + VIA_PCM_TABLE_ADDR); via_ac97_wait_idle (card); outl (cpu_to_le32 (chan->sgt_handle), - card->baseaddr + chan_ofs + VIA_PCM_TABLE_ADDR); + chan->iobase + VIA_PCM_TABLE_ADDR); udelay (20); via_ac97_wait_idle (card); - + DPRINTK("inl (0x%lX) = %x\n", chan->iobase + VIA_PCM_TABLE_ADDR, inl(chan->iobase + VIA_PCM_TABLE_ADDR)); @@ -683,24 +692,23 @@ err_out_nomem: * back to a known state, and releasing any allocated * sound buffers. */ - + static void via_chan_free (struct via_info *card, struct via_channel *chan) { int i; - unsigned long flags; - + DPRINTK ("ENTER\n"); - + synchronize_irq(); - spin_lock_irqsave (&card->lock, flags); + spin_lock_irq (&card->lock); /* stop any existing channel output */ via_chan_stop (chan->iobase); via_chan_status_clear (chan->iobase); - via_chan_pcm_fmt (card, chan, 1); + via_chan_pcm_fmt (chan, 1); - spin_unlock_irqrestore (&card->lock, flags); + spin_unlock_irq (&card->lock); /* zero location of DMA-able scatter-gather info table */ via_ac97_wait_idle(card); @@ -708,15 +716,16 @@ static void via_chan_free (struct via_info *card, struct via_channel *chan) for (i = 0; i < VIA_DMA_BUFFERS; i++) if (chan->sgbuf[i].cpuaddr) { + assert ((VIA_DMA_BUF_SIZE % PAGE_SIZE) == 0); pci_free_consistent (card->pdev, VIA_DMA_BUF_SIZE, - (void*)chan->sgbuf[i].cpuaddr, + chan->sgbuf[i].cpuaddr, chan->sgbuf[i].handle); chan->sgbuf[i].cpuaddr = NULL; chan->sgbuf[i].handle = 0; } if (chan->sgtable) { - pci_free_consistent (card->pdev, + pci_free_consistent (card->pdev, (sizeof (struct via_sgd_table) * VIA_DMA_BUFFERS), (void*)chan->sgtable, chan->sgt_handle); chan->sgtable = NULL; @@ -728,7 +737,6 @@ static void via_chan_free (struct via_info *card, struct via_channel *chan) /** * via_chan_pcm_fmt - Update PCM channel settings - * @card: Private audio chip info * @chan: Channel to be updated * @reset: Boolean. If non-zero, channel will be reset * to 8-bit mono mode. @@ -741,25 +749,26 @@ static void via_chan_free (struct via_info *card, struct via_channel *chan) * is set to the values stored in the channel * information struct @chan. */ - -static void via_chan_pcm_fmt (struct via_info *card, - struct via_channel *chan, int reset) + +static void via_chan_pcm_fmt (struct via_channel *chan, int reset) { DPRINTK ("ENTER, pcm_fmt=0x%02X, reset=%s\n", chan->pcm_fmt, reset ? "yes" : "no"); - assert (card != NULL); assert (chan != NULL); if (reset) /* reset to 8-bit mono mode */ chan->pcm_fmt = 0; - + /* enable interrupts on FLAG and EOL */ chan->pcm_fmt |= VIA_CHAN_TYPE_MASK; - + + /* if we are recording, enable recording fifo bit */ + if (chan->is_record) + chan->pcm_fmt |= VIA_PCM_REC_FIFO; /* set interrupt select bits where applicable (PCM & FM out channels) */ - if (chan == &card->ch_out) + if (!chan->is_record) chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT; outb (chan->pcm_fmt, chan->iobase + 2); @@ -780,10 +789,20 @@ static void via_chan_pcm_fmt (struct via_info *card, static void via_chan_clear (struct via_channel *chan) { + DPRINTK ("ENTER\n"); via_chan_stop (chan->iobase); - atomic_set (&chan->state, sgd_stopped); - atomic_set (&chan->buf_in_use, 0); - atomic_set (&chan->next_buf, 0); + chan->is_active = 0; + chan->is_mapped = 0; + chan->is_enabled = 1; + chan->slop_len = 0; + chan->sw_ptr = 0; + chan->n_irqs = 0; + atomic_set (&chan->hw_ptr, 0); + if (chan->is_record) + atomic_set (&chan->n_bufs, 0); + else + atomic_set (&chan->n_bufs, VIA_DMA_BUFFERS); + DPRINTK ("EXIT\n"); } @@ -809,8 +828,8 @@ static int via_chan_set_speed (struct via_info *card, via_chan_clear (chan); - val = via_set_rate (card, val, chan == &card->ch_in); - + val = via_set_rate (&card->ac97, chan, val); + DPRINTK ("EXIT, returning %d\n", val); return val; } @@ -840,23 +859,30 @@ static int via_chan_set_fmt (struct via_info *card, "unknown"); via_chan_clear (chan); - + + assert (val != AFMT_QUERY); /* this case is handled elsewhere */ + switch (val) { - case AFMT_U8: - chan->pcm_fmt &= ~VIA_PCM_FMT_16BIT; - via_chan_pcm_fmt (card, chan, 0); - break; case AFMT_S16_LE: - chan->pcm_fmt |= VIA_PCM_FMT_16BIT; - via_chan_pcm_fmt (card, chan, 0); + if ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) == 0) { + chan->pcm_fmt |= VIA_PCM_FMT_16BIT; + via_chan_pcm_fmt (chan, 0); + } break; - default: - printk (KERN_WARNING PFX "unknown AFMT\n"); - val = -EINVAL; + + case AFMT_U8: + if (chan->pcm_fmt & VIA_PCM_FMT_16BIT) { + chan->pcm_fmt &= ~VIA_PCM_FMT_16BIT; + via_chan_pcm_fmt (chan, 0); + } break; + + default: + DPRINTK ("unknown AFMT: 0x%X\n", val); + val = AFMT_S16_LE; } - - DPRINTK ("EXIT, returning %d\n", val); + + DPRINTK ("EXIT\n"); return val; } @@ -882,19 +908,19 @@ static int via_chan_set_stereo (struct via_info *card, DPRINTK ("ENTER, channels = %d\n", val); via_chan_clear (chan); - + switch (val) { /* mono */ case 1: chan->pcm_fmt &= ~VIA_PCM_FMT_STEREO; - via_chan_pcm_fmt (card, chan, 0); + via_chan_pcm_fmt (chan, 0); break; /* stereo */ case 2: chan->pcm_fmt |= VIA_PCM_FMT_STEREO; - via_chan_pcm_fmt (card, chan, 0); + via_chan_pcm_fmt (chan, 0); break; /* unknown */ @@ -903,13 +929,13 @@ static int via_chan_set_stereo (struct via_info *card, val = -EINVAL; break; } - + DPRINTK ("EXIT, returning %d\n", val); return val; } -#if 0 +#ifdef VIA_CHAN_DUMP_BUFS /** * via_chan_dump_bufs - Display DMA table contents * @chan: Channel whose DMA table will be displayed @@ -921,7 +947,7 @@ static int via_chan_set_stereo (struct via_info *card, static void via_chan_dump_bufs (struct via_channel *chan) { int i; - + for (i = 0; i < VIA_DMA_BUFFERS; i++) { DPRINTK ("#%02d: addr=%x, count=%u, flag=%d, eol=%d\n", i, chan->sgtable[i].addr, @@ -931,9 +957,55 @@ static void via_chan_dump_bufs (struct via_channel *chan) } DPRINTK ("buf_in_use = %d, nextbuf = %d\n", atomic_read (&chan->buf_in_use), - atomic_read (&chan->next_buf)); + atomic_read (&chan->sw_ptr)); +} +#endif /* VIA_CHAN_DUMP_BUFS */ + + +/** + * via_chan_flush_frag - Flush partially-full playback buffer to hardware + * @chan: Channel whose DMA table will be displayed + * + * Flushes partially-full playback buffer to hardware. + */ + +static void via_chan_flush_frag (struct via_channel *chan) +{ + DPRINTK ("ENTER\n"); + + assert (chan->slop_len > 0); + + if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1)) + chan->sw_ptr = 0; + else + chan->sw_ptr++; + + chan->slop_len = 0; + + assert (atomic_read (&chan->n_bufs) > 0); + atomic_dec (&chan->n_bufs); + + DPRINTK ("EXIT\n"); +} + + + +/** + * via_chan_maybe_start - Initiate audio hardware DMA operation + * @chan: Channel whose DMA is to be started + * + * Initiate DMA operation, if the DMA engine for the given + * channel @chan is not already active. + */ + +static inline void via_chan_maybe_start (struct via_channel *chan) +{ + if (!chan->is_active && chan->is_enabled) { + chan->is_active = 1; + sg_begin (chan); + DPRINTK("starting channel %s\n", chan->name); + } } -#endif /**************************************************************** @@ -942,7 +1014,7 @@ static void via_chan_dump_bufs (struct via_channel *chan) * * */ - + /** * via_ac97_wait_idle - Wait until AC97 codec is not busy * @card: Private info for specified board @@ -956,23 +1028,20 @@ static u8 via_ac97_wait_idle (struct via_info *card) { u8 tmp8; int counter = VIA_COUNTER_LIMIT; - + DPRINTK ("ENTER/EXIT\n"); assert (card != NULL); assert (card->pdev != NULL); - + do { - if (current->need_resched) - schedule (); - else - udelay (10); + udelay (15); - spin_lock_irq (&card->lock); tmp8 = inb (card->baseaddr + 0x83); - spin_unlock_irq (&card->lock); } while ((tmp8 & VIA_CR83_BUSY) && (counter-- > 0)); + if (tmp8 & VIA_CR83_BUSY) + printk (KERN_WARNING PFX "timeout waiting on AC97 codec\n"); return tmp8; } @@ -994,10 +1063,10 @@ static u8 via_ac97_wait_idle (struct via_info *card) static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg) { - u32 data; + unsigned long data; struct via_info *card; int counter; - + DPRINTK ("ENTER\n"); assert (codec != NULL); @@ -1013,26 +1082,25 @@ static u16 via_ac97_read_reg (struct ac97_codec *codec, u8 reg) for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) { if (inl (card->baseaddr + 0x80) & VIA_CR80_VALID) goto out; - udelay(10); - if (current->need_resched) - schedule (); + + udelay (15); } - printk (KERN_WARNING PFX "timeout while reading AC97 codec\n"); + printk (KERN_WARNING PFX "timeout while reading AC97 codec (0x%lX)\n", data); goto err_out; out: - data = inl (card->baseaddr + 0x80); + data = (unsigned long) inl (card->baseaddr + 0x80); outb (0x02, card->baseaddr + 0x83); if (((data & 0x007F0000) >> 16) == reg) { - DPRINTK ("EXIT, success, data=0x%x, retval=0x%x\n", + DPRINTK ("EXIT, success, data=0x%lx, retval=0x%lx\n", data, data & 0x0000FFFF); return data & 0x0000FFFF; } - DPRINTK ("WARNING: not our index: reg=0x%x, newreg=0x%x\n", - reg, ((data & 0x007F0000) >> 16)); + printk (KERN_WARNING "via82cxxx_audio: not our index: reg=0x%x, newreg=0x%lx\n", + reg, ((data & 0x007F0000) >> 16)); err_out: DPRINTK ("EXIT, returning 0\n"); @@ -1058,7 +1126,7 @@ static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value) u32 data; struct via_info *card; int counter; - + DPRINTK ("ENTER\n"); assert (codec != NULL); @@ -1068,17 +1136,16 @@ static void via_ac97_write_reg (struct ac97_codec *codec, u8 reg, u16 value) data = (reg << 16) + value; outl (data, card->baseaddr + VIA_BASE0_AC97_CTRL); - udelay (20); + udelay (10); for (counter = VIA_COUNTER_LIMIT; counter > 0; counter--) { if ((inb (card->baseaddr + 0x83) & VIA_CR83_BUSY) == 0) goto out; - udelay(10); - if (current->need_resched) - schedule (); + + udelay (15); } - - printk (KERN_WARNING PFX "timeout after AC97 codec write\n"); + + printk (KERN_WARNING PFX "timeout after AC97 codec write (0x%X, 0x%X)\n", reg, value); out: DPRINTK ("EXIT\n"); @@ -1091,15 +1158,15 @@ static int via_mixer_open (struct inode *inode, struct file *file) struct via_info *card; struct pci_dev *pdev; struct pci_driver *drvr; - + DPRINTK ("ENTER\n"); pci_for_each_dev(pdev) { drvr = pci_dev_driver (pdev); if (drvr == &via_driver) { - assert (pdev->driver_data != NULL); - - card = pdev->driver_data; + assert (pci_get_drvdata (pdev) != NULL); + + card = pci_get_drvdata (pdev); if (card->ac97.dev_mixer == minor) goto match; } @@ -1119,12 +1186,25 @@ static int via_mixer_ioctl (struct inode *inode, struct file *file, unsigned int unsigned long arg) { struct ac97_codec *codec = file->private_data; + struct via_info *card; + int nonblock = (file->f_flags & O_NONBLOCK); int rc; + DPRINTK ("ENTER\n"); + assert (codec != NULL); - + card = codec->private_data; + assert (card != NULL); + + rc = via_syscall_down (card, nonblock); + if (rc) goto out; + rc = codec->mixer_ioctl(codec, cmd, arg); - + + up (&card->syscall_sem); + +out: + DPRINTK ("EXIT, returning %d\n", rc); return rc; } @@ -1146,39 +1226,16 @@ static struct file_operations via_mixer_fops = { }; -#if 0 /* values reasoned from debugging dumps of via's driver */ -static struct { - u8 reg; - u16 data; -} mixer_init_vals[] __devinitdata = { - { 0x2, 0x404 }, - { 0x4, 0x404 }, - { 0x6, 0x404 }, - { 0x18, 0x404 }, - { 0x10, 0x404 }, - { 0x1a, 0x404 }, - { 0x1c, 0x404 }, - { 0x1a, 0x404 }, - { 0x1c, 0xc0c }, - { 0x12, 0x808 }, - { 0x10, 0x808 }, - { 0xe, 0x2 }, - { 0x2, 0x808 }, - { 0x18, 0x808 }, -}; -#endif - - static int __init via_ac97_reset (struct via_info *card) { struct pci_dev *pdev = card->pdev; u8 tmp8; u16 tmp16; - + DPRINTK ("ENTER\n"); assert (pdev != NULL); - + #ifndef NDEBUG { u8 r40,r41,r42,r43,r44,r48; @@ -1201,7 +1258,7 @@ static int __init via_ac97_reset (struct via_info *card) inl (card->baseaddr + 0x80), inl (card->baseaddr + 0x84)); spin_unlock_irq (&card->lock); - + } #endif @@ -1212,10 +1269,10 @@ static int __init via_ac97_reset (struct via_info *card) pci_write_config_byte (pdev, VIA_ACLINK_CTRL, VIA_CR41_AC97_ENABLE | VIA_CR41_AC97_RESET | VIA_CR41_AC97_WAKEUP); udelay (100); - + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, 0); udelay (100); - + pci_write_config_byte (pdev, VIA_ACLINK_CTRL, VIA_CR41_AC97_ENABLE | VIA_CR41_PCM_ENABLE | VIA_CR41_VRA | VIA_CR41_AC97_RESET); @@ -1230,45 +1287,50 @@ static int __init via_ac97_reset (struct via_info *card) /* route FM trap to IRQ, disable FM trap */ pci_write_config_byte (pdev, 0x48, 0x05); udelay(10); - + /* disable all codec GPI interrupts */ outl (0, pci_resource_start (pdev, 0) + 0x8C); - /* enable variable rate */ + /* WARNING: this line is magic. Remove this + * and things break. */ + /* enable variable rate, variable rate MIC ADC */ tmp16 = via_ac97_read_reg (&card->ac97, 0x2A); - via_ac97_write_reg (&card->ac97, 0x2A, tmp16 | 0x01); - - /* boost headphone vol if disabled */ - tmp16 = via_ac97_read_reg (&card->ac97, AC97_HEADPHONE_VOL); - if (tmp16 == 0) - via_ac97_write_reg (&card->ac97, AC97_HEADPHONE_VOL, 0x1F1F); - + via_ac97_write_reg (&card->ac97, 0x2A, tmp16 | (1<<0)); + pci_read_config_byte (pdev, VIA_ACLINK_CTRL, &tmp8); if ((tmp8 & (VIA_CR41_AC97_ENABLE | VIA_CR41_AC97_RESET)) == 0) { printk (KERN_ERR PFX "cannot enable AC97 controller, aborting\n"); DPRINTK ("EXIT, tmp8=%X, returning -ENODEV\n", tmp8); return -ENODEV; } - + DPRINTK ("EXIT, returning 0\n"); return 0; } +static void via_ac97_codec_wait (struct ac97_codec *codec) +{ + assert (codec->private_data != NULL); + via_ac97_wait_idle (codec->private_data); +} + + static int __init via_ac97_init (struct via_info *card) { int rc; u16 tmp16; - + DPRINTK ("ENTER\n"); assert (card != NULL); - memset (&card->ac97, 0, sizeof (card->ac97)); + memset (&card->ac97, 0, sizeof (card->ac97)); card->ac97.private_data = card; card->ac97.codec_read = via_ac97_read_reg; card->ac97.codec_write = via_ac97_write_reg; - + card->ac97.codec_wait = via_ac97_codec_wait; + card->ac97.dev_mixer = register_sound_mixer (&via_mixer_fops, -1); if (card->ac97.dev_mixer < 0) { printk (KERN_ERR PFX "unable to register AC97 mixer, aborting\n"); @@ -1288,9 +1350,10 @@ static int __init via_ac97_init (struct via_info *card) goto err_out; } + /* enable variable rate, variable rate MIC ADC */ tmp16 = via_ac97_read_reg (&card->ac97, 0x2A); - via_ac97_write_reg (&card->ac97, 0x2A, tmp16 | 0x01); - + via_ac97_write_reg (&card->ac97, 0x2A, tmp16 | (1<<0)); + DPRINTK ("EXIT, returning 0\n"); return 0; @@ -1304,10 +1367,10 @@ err_out: static void via_ac97_cleanup (struct via_info *card) { DPRINTK("ENTER\n"); - + assert (card != NULL); assert (card->ac97.dev_mixer >= 0); - + unregister_sound_mixer (card->ac97.dev_mixer); DPRINTK("EXIT\n"); @@ -1320,68 +1383,130 @@ static void via_ac97_cleanup (struct via_info *card) * Interrupt-related code * */ - -static inline void via_interrupt_write (struct via_channel *chan) + +/** + * via_intr_channel - handle an interrupt for a single channel + * @chan: handle interrupt for this channel + * + * This is the "meat" of the interrupt handler, + * containing the actions taken each time an interrupt + * occurs. All communication and coordination with + * userspace takes place here. + * + * Locking: inside card->lock + */ + +static void via_intr_channel (struct via_channel *chan) { - assert (atomic_read(&chan->buf_in_use) < - atomic_read(&chan->next_buf)); + u8 status; + int n; - /* XXX sanity check: read h/w counter to - * ensure no lost frames */ + /* check pertinent bits of status register for action bits */ + status = inb (chan->iobase) & (VIA_SGD_FLAG | VIA_SGD_EOL | VIA_SGD_STOPPED); + if (!status) + return; - atomic_inc (&chan->buf_in_use); + /* acknowledge any flagged bits ASAP */ + outb (status, chan->iobase); - /* if SG ptr catches up with userland ptr, stop playback */ - if (atomic_read(&chan->buf_in_use) == atomic_read(&chan->next_buf)) { - atomic_set (&chan->state, sgd_stopped); - via_chan_stop (chan->iobase); + /* grab current h/w ptr value */ + n = atomic_read (&chan->hw_ptr); + + /* sanity check: make sure our h/w ptr doesn't have a weird value */ + assert (n >= 0); + assert (n < VIA_DMA_BUFFERS); + + /* reset SGD data structure in memory to reflect a full buffer, + * and advance the h/w ptr, wrapping around to zero if needed + */ + if (n == (VIA_DMA_BUFFERS - 1)) { + chan->sgtable[n].count = (VIA_DMA_BUF_SIZE | VIA_EOL); + atomic_set (&chan->hw_ptr, 0); + } else { + chan->sgtable[n].count = (VIA_DMA_BUF_SIZE | VIA_FLAG); + atomic_inc (&chan->hw_ptr); } - /* wake up anybody listening */ + /* accounting crap for SNDCTL_DSP_GETxPTR */ + chan->n_irqs++; + chan->bytes += VIA_DMA_BUF_SIZE; + if (chan->bytes < 0) /* handle overflow of 31-bit value */ + chan->bytes = VIA_DMA_BUF_SIZE; + + /* wake up anyone listening to see when interrupts occur */ if (waitqueue_active (&chan->wait)) - wake_up (&chan->wait); -} + wake_up_all (&chan->wait); + + DPRINTK ("%s intr, status=0x%02X, hwptr=0x%lX, chan->hw_ptr=%d\n", + chan->name, status, (long) inl (chan->iobase + 0x04), + atomic_read (&chan->hw_ptr)); + + /* all following checks only occur when not in mmap(2) mode */ + if (chan->is_mapped) + return; + + /* If we are recording, then n_bufs represents the number + * of buffers waiting to be handled by userspace. + * If we are playback, then n_bufs represents the number + * of buffers remaining to be filled by userspace. + * We increment here. If we reach max buffers (VIA_DMA_BUFFERS), + * this indicates an underrun/overrun. For this case under OSS, + * we stop the record/playback process. + */ + if (atomic_read (&chan->n_bufs) < VIA_DMA_BUFFERS) + atomic_inc (&chan->n_bufs); + assert (atomic_read (&chan->n_bufs) <= VIA_DMA_BUFFERS); + + if (atomic_read (&chan->n_bufs) == VIA_DMA_BUFFERS) { + chan->is_active = 0; + via_chan_stop (chan->iobase); + } + DPRINTK ("%s intr, channel n_bufs == %d\n", chan->name, + atomic_read (&chan->n_bufs)); +} static void via_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct via_info *card = dev_id; - struct via_channel *chan; - u8 status; + u32 status32; - status = inb (card->baseaddr + 0x00); - if (status) { - assert (card->open_mode & FMODE_WRITE); - - chan = &card->ch_out; - - if (status & VIA_SGD_FLAG) { - assert ((status & VIA_SGD_EOL) == 0); - outb (VIA_SGD_FLAG, chan->iobase + 0x00); - DPRINTK("FLAG intr, status=0x%02X\n", status); - via_interrupt_write (chan); - } - - if (status & VIA_SGD_EOL) { - assert ((status & VIA_SGD_FLAG) == 0); - outb (VIA_SGD_EOL, chan->iobase + 0x00); - DPRINTK("EOL intr, status=0x%02X\n", status); - via_interrupt_write (chan); - } - - if (status & VIA_SGD_STOPPED) { - outb (VIA_SGD_STOPPED, chan->iobase + 0x00); - DPRINTK("STOPPED intr, status=0x%02X\n", status); - } + /* to minimize interrupt sharing costs, we use the SGD status + * shadow register to check the status of all inputs and + * outputs with a single 32-bit bus read. If no interrupt + * conditions are flagged, we exit immediately + */ + status32 = inl (card->baseaddr + VIA_BASE0_SGD_STATUS_SHADOW); + if (!(status32 & VIA_INTR_MASK)) + return; -#if 0 - via_chan_dump_bufs (&card->ch_out); -#endif - } + DPRINTK ("intr, status32 == 0x%08X\n", status32); + + /* synchronize interrupt handling under SMP. this spinlock + * goes away completely on UP + */ + spin_lock (&card->lock); + + if (status32 & VIA_INTR_OUT) + via_intr_channel (&card->ch_out); + if (status32 & VIA_INTR_IN) + via_intr_channel (&card->ch_in); + if (status32 & VIA_INTR_FM) + via_intr_channel (&card->ch_fm); + + spin_unlock (&card->lock); } +/** + * via_interrupt_disable - Disable all interrupt-generating sources + * @card: Private info for specified board + * + * Disables all interrupt-generation flags in the Via + * audio hardware registers. + */ + static void via_interrupt_disable (struct via_info *card) { u8 tmp8; @@ -1390,7 +1515,7 @@ static void via_interrupt_disable (struct via_info *card) DPRINTK ("ENTER\n"); assert (card != NULL); - + spin_lock_irqsave (&card->lock, flags); pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8); @@ -1415,13 +1540,22 @@ static void via_interrupt_disable (struct via_info *card) } +/** + * via_interrupt_init - Initialize interrupt handling + * @card: Private info for specified board + * + * Obtain and reserve IRQ for using in handling audio events. + * Also, disable any IRQ-generating resources, to make sure + * we don't get interrupts before we want them. + */ + static int via_interrupt_init (struct via_info *card) { DPRINTK ("ENTER\n"); assert (card != NULL); assert (card->pdev != NULL); - + /* check for sane IRQ number. can this ever happen? */ if (card->pdev->irq < 2) { printk (KERN_ERR PFX "insane IRQ %d, aborting\n", @@ -1429,7 +1563,7 @@ static int via_interrupt_init (struct via_info *card) DPRINTK ("EXIT, returning -EIO\n"); return -EIO; } - + if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) { printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n", card->pdev->irq); @@ -1445,6 +1579,15 @@ static int via_interrupt_init (struct via_info *card) } +/** + * via_interrupt_cleanup - Shutdown driver interrupt handling + * @card: Private info for specified board + * + * Disable any potential interrupt sources in the Via audio + * hardware, and then release (un-reserve) the IRQ line + * in the kernel core. + */ + static void via_interrupt_cleanup (struct via_info *card) { DPRINTK ("ENTER\n"); @@ -1465,7 +1608,7 @@ static void via_interrupt_cleanup (struct via_info *card) * OSS DSP device * */ - + static struct file_operations via_dsp_fops = { owner: THIS_MODULE, open: via_dsp_open, @@ -1475,22 +1618,28 @@ static struct file_operations via_dsp_fops = { poll: via_dsp_poll, llseek: via_llseek, ioctl: via_dsp_ioctl, +#ifdef VIA_SUPPORT_MMAP + mmap: via_dsp_mmap, +#endif }; static int __init via_dsp_init (struct via_info *card) { u8 tmp8; - + DPRINTK ("ENTER\n"); assert (card != NULL); /* turn off legacy features, if not already */ pci_read_config_byte (card->pdev, VIA_FUNC_ENABLE, &tmp8); - tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_MIDI_ENABLE | - VIA_CR42_FM_ENABLE); - pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8); + if (tmp8 & (VIA_CR42_SB_ENABLE | VIA_CR42_MIDI_ENABLE | + VIA_CR42_FM_ENABLE)) { + tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_MIDI_ENABLE | + VIA_CR42_FM_ENABLE); + pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8); + } via_stop_everything (card); @@ -1519,232 +1668,404 @@ static void via_dsp_cleanup (struct via_info *card) } +#ifdef VIA_SUPPORT_MMAP +static struct page * via_mm_nopage (struct vm_area_struct * vma, + unsigned long address, int write_access) +{ + struct via_info *card = vma->vm_private_data; + struct via_channel *chan = &card->ch_out; + struct page *dmapage; + unsigned long pgoff; + int rd, wr; + + DPRINTK ("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh, wr %d\n", + vma->vm_start, + address - vma->vm_start, + (address - vma->vm_start) >> PAGE_SHIFT, + address, + write_access); + + assert (VIA_DMA_BUF_SIZE == PAGE_SIZE); + + if (address > vma->vm_end) { + DPRINTK ("EXIT, returning NOPAGE_SIGBUS\n"); + return NOPAGE_SIGBUS; /* Disallow mremap */ + } + if (!card) { + DPRINTK ("EXIT, returning NOPAGE_OOM\n"); + return NOPAGE_OOM; /* Nothing allocated */ + } + + pgoff = vma->vm_pgoff + (address - vma->vm_start) >> PAGE_SHIFT; + rd = card->ch_in.is_mapped; + wr = card->ch_out.is_mapped; + +#ifndef VIA_NDEBUG + { + unsigned long max_bufs = VIA_DMA_BUFFERS; + if (rd && wr) max_bufs *= 2; + /* via_dsp_mmap() should ensure this */ + assert (pgoff < max_bufs); + } +#endif + + /* if full-duplex (read+write) and we have two sets of bufs, + * then the playback buffers come first, sez soundcard.c */ + if (pgoff >= VIA_DMA_BUFFERS) { + pgoff -= VIA_DMA_BUFFERS; + chan = &card->ch_in; + } else if (!wr) + chan = &card->ch_in; + + assert ((((unsigned long)chan->sgbuf[pgoff].cpuaddr) % PAGE_SIZE) == 0); + + dmapage = virt_to_page (chan->sgbuf[pgoff].cpuaddr); + DPRINTK ("EXIT, returning page %p for cpuaddr %lXh\n", + dmapage, (unsigned long) chan->sgbuf[pgoff].cpuaddr); + get_page (dmapage); + return dmapage; +} + + +static int via_mm_swapout (struct page *page, struct file *filp) +{ + return 0; +} + + +struct vm_operations_struct via_mm_ops = { + nopage: via_mm_nopage, + swapout: via_mm_swapout, +}; + + +static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct via_info *card; + int nonblock = (file->f_flags & O_NONBLOCK); + int rc = -EINVAL, rd=0, wr=0; + unsigned long max_size, size, start, offset; + + assert (file != NULL); + assert (vma != NULL); + card = file->private_data; + assert (card != NULL); + + DPRINTK ("ENTER, start %lXh, size %ld, pgoff %ld\n", + vma->vm_start, + vma->vm_end - vma->vm_start, + vma->vm_pgoff); + + assert (VIA_DMA_BUF_SIZE == PAGE_SIZE); + + max_size = 0; + if (file->f_mode & FMODE_READ) { + rd = 1; + max_size += (VIA_DMA_BUFFERS * VIA_DMA_BUF_SIZE); + } + if (file->f_mode & FMODE_WRITE) { + wr = 1; + max_size += (VIA_DMA_BUFFERS * VIA_DMA_BUF_SIZE); + } + + start = vma->vm_start; + offset = (vma->vm_pgoff << PAGE_SHIFT); + size = vma->vm_end - vma->vm_start; + + /* some basic size/offset sanity checks */ + if (size > max_size) + goto out; + if (offset > max_size - size) + goto out; + + rc = via_syscall_down (card, nonblock); + if (rc) goto out; + + vma->vm_ops = &via_mm_ops; + vma->vm_private_data = card; + + if (rd) + card->ch_in.is_mapped = 1; + if (wr) + card->ch_out.is_mapped = 1; + + up (&card->syscall_sem); + rc = 0; + +out: + DPRINTK("EXIT, returning %d\n", rc); + return rc; +} +#endif /* VIA_SUPPORT_MMAP */ + + +static ssize_t via_dsp_do_read (struct via_info *card, + char *userbuf, size_t count, + int nonblock) +{ + const char *orig_userbuf = userbuf; + struct via_channel *chan = &card->ch_in; + size_t size; + int n, tmp; + + /* if SGD has not yet been started, start it */ + via_chan_maybe_start (chan); + +handle_one_block: + /* just to be a nice neighbor */ + if (current->need_resched) + schedule (); + + /* grab current channel software pointer. In the case of + * recording, this is pointing to the next buffer that + * will receive data from the audio hardware. + */ + n = chan->sw_ptr; + + /* n_bufs represents the number of buffers waiting + * to be copied to userland. sleep until at least + * one buffer has been read from the audio hardware. + */ + tmp = atomic_read (&chan->n_bufs); + assert (tmp >= 0); + assert (tmp <= VIA_DMA_BUFFERS); + while (tmp == 0) { + if (nonblock || !chan->is_active) + return -EAGAIN; + + DPRINTK ("Sleeping on block %d\n", n); + interruptible_sleep_on (&chan->wait); + + if (signal_pending (current)) + return -ERESTARTSYS; + + tmp = atomic_read (&chan->n_bufs); + } + + /* Now that we have a buffer we can read from, send + * as much as sample data possible to userspace. + */ + while ((count > 0) && (chan->slop_len < VIA_DMA_BUF_SIZE)) { + size_t slop_left = VIA_DMA_BUF_SIZE - chan->slop_len; + + size = (count < slop_left) ? count : slop_left; + if (copy_to_user (userbuf, + chan->sgbuf[n].cpuaddr + chan->slop_len, + size)) + return -EFAULT; + + count -= size; + chan->slop_len += size; + userbuf += size; + } + + /* If we didn't copy the buffer completely to userspace, + * stop now. + */ + if (chan->slop_len < VIA_DMA_BUF_SIZE) + goto out; + + /* + * If we get to this point, we copied one buffer completely + * to userspace, give the buffer back to the hardware. + */ + + /* advance channel software pointer to point to + * the next buffer from which we will copy + */ + if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1)) + chan->sw_ptr = 0; + else + chan->sw_ptr++; + + /* mark one less buffer waiting to be processed */ + assert (atomic_read (&chan->n_bufs) > 0); + atomic_dec (&chan->n_bufs); + + /* we are at a block boundary, there is no fragment data */ + chan->slop_len = 0; + + DPRINTK("Flushed block %u, sw_ptr now %u, n_bufs now %d\n", + n, chan->sw_ptr, atomic_read (&chan->n_bufs)); + + DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", + inb (card->baseaddr + 0x00), + inb (card->baseaddr + 0x01), + inb (card->baseaddr + 0x02), + inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x0C), + inl (card->baseaddr + 0x80), + inl (card->baseaddr + 0x84)); + + if (count > 0) + goto handle_one_block; + +out: + return userbuf - orig_userbuf; +} + + static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct via_info *card; + int nonblock = (file->f_flags & O_NONBLOCK); + int rc; + + DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n", + file, buffer, count, ppos ? ((unsigned long)*ppos) : 0); - DPRINTK ("ENTER\n"); - assert (file != NULL); assert (buffer != NULL); card = file->private_data; assert (card != NULL); - DPRINTK("EXIT, returning -EINVAL\n"); - return -EINVAL; -} + if (ppos != &file->f_pos) { + DPRINTK ("EXIT, returning -ESPIPE\n"); + return -ESPIPE; + } + rc = via_syscall_down (card, nonblock); + if (rc) goto out; + + if (card->ch_in.is_mapped) { + rc = -ENXIO; + goto out_up; + } + + rc = via_dsp_do_read (card, buffer, count, nonblock); + +out_up: + up (&card->syscall_sem); +out: + DPRINTK("EXIT, returning %ld\n",(long) rc); + return rc; +} -#define sgcount(n) (sgtable[(n)].count & 0x00FFFFFF) -#define NEXTBUF (atomic_read(&chan->next_buf) % VIA_DMA_BUFFERS) -#define BUF_IN_USE (atomic_read(&chan->buf_in_use) % VIA_DMA_BUFFERS) -#define STATE_STOPPED (atomic_read (state) == sgd_stopped) -#define STATE_STARTED (atomic_read (state) == sgd_in_progress) static ssize_t via_dsp_do_write (struct via_info *card, const char *userbuf, size_t count, - int non_blocking) + int nonblock) { const char *orig_userbuf = userbuf; struct via_channel *chan = &card->ch_out; volatile struct via_sgd_table *sgtable = chan->sgtable; - atomic_t *state = &chan->state; size_t size; - int nextbuf, prevbuf, n, realcount; - ssize_t rc; - - while (count > 0) { - if (current->need_resched) - schedule (); - - spin_lock_irq (&card->lock); - DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", - inb (card->baseaddr + 0x00), - inb (card->baseaddr + 0x01), - inb (card->baseaddr + 0x02), - inl (card->baseaddr + 0x04), - inl (card->baseaddr + 0x0C), - inl (card->baseaddr + 0x80), - inl (card->baseaddr + 0x84)); - spin_unlock_irq (&card->lock); - - size = (count < VIA_DMA_BUF_SIZE) ? count : VIA_DMA_BUF_SIZE; + int n, tmp; - /* case 1: SGD not active, list is ours for the mangling */ +handle_one_block: + /* just to be a nice neighbor */ + if (current->need_resched) + schedule (); - if (STATE_STOPPED) { - DPRINTK ("case 1\n"); - - if (copy_from_user ((void*)chan->sgbuf[0].cpuaddr, - userbuf, size)) - return -EFAULT; + /* grab current channel software pointer. In the case of + * playback, this is pointing to the next buffer that + * should receive data from userland. + */ + n = chan->sw_ptr; - assert (sgtable[0].addr == cpu_to_le32 (chan->sgbuf[0].handle)); - sgtable[0].count = size | VIA_FLAG; + /* n_bufs represents the number of buffers remaining + * to be filled by userspace. Sleep until + * at least one buffer is available for our use. + */ + tmp = atomic_read (&chan->n_bufs); + assert (tmp >= 0); + assert (tmp <= VIA_DMA_BUFFERS); + while (tmp == 0) { + if (nonblock || !chan->is_enabled) + return -EAGAIN; - atomic_set (state, sgd_in_progress); - atomic_set (&chan->buf_in_use, 0); - atomic_set (&chan->next_buf, 1); - - count -= size; - userbuf += size; + DPRINTK ("Sleeping on block %d, tmp==%d, ir==%d\n", n, tmp, chan->is_record); + interruptible_sleep_on (&chan->wait); - spin_lock_irq (&card->lock); - sg_begin (chan); - spin_unlock_irq (&card->lock); + if (signal_pending (current)) + return -ERESTARTSYS; - continue; - } + tmp = atomic_read (&chan->n_bufs); + } - nextbuf = NEXTBUF; - if (nextbuf) - prevbuf = nextbuf - 1; - else - prevbuf = VIA_DMA_BUFFERS - 1; - - /* case 2: if final buffer is (a) a fragment, and (b) not - * currently being consumed by the SGD engine, then append - * as much data as possible to the fragment. */ - - realcount = sgcount(prevbuf); - if (STATE_STARTED && (prevbuf != BUF_IN_USE) && - (realcount < VIA_DMA_BUF_SIZE)) { - DPRINTK ("case 2\n"); - DPRINTK ("st=%d, fb=%d -- nb=%d, pb=%d, n=%d, rc=%d\n", - atomic_read (state), - BUF_IN_USE, - nextbuf, - prevbuf, - prevbuf /* n */, - realcount); - - n = prevbuf; - - if ((VIA_DMA_BUF_SIZE - realcount) < size) - size = VIA_DMA_BUF_SIZE - realcount; - - if (copy_from_user ((void*)(chan->sgbuf[n].cpuaddr + - realcount), - userbuf, size)) - return -EFAULT; - - /* slack way to try and prevent races */ - if (prevbuf == BUF_IN_USE || !STATE_STARTED) - continue; - - assert (sgtable[n].addr == cpu_to_le32 (chan->sgbuf[n].handle)); - if (n == (VIA_DMA_BUFFERS - 1)) - sgtable[n].count = (realcount + size) | VIA_EOL; - else - sgtable[n].count = (realcount + size) | VIA_FLAG; + /* Now that we have a buffer we can write to, fill it up + * as much as possible with data from userspace. + */ + while ((count > 0) && (chan->slop_len < VIA_DMA_BUF_SIZE)) { + size_t slop_left = VIA_DMA_BUF_SIZE - chan->slop_len; - count -= size; - userbuf += size; - continue; - } + size = (count < slop_left) ? count : slop_left; + if (copy_from_user (chan->sgbuf[n].cpuaddr + chan->slop_len, + userbuf, size)) + return -EFAULT; - /* case 3: if there are buffers left, use one - * XXX needs more review for possible races */ + count -= size; + chan->slop_len += size; + userbuf += size; + } - else if (STATE_STARTED && !via_chan_full (chan)) { - DPRINTK ("case 3\n"); - DPRINTK ("st=%d, fb=%d -- nb=%d, pb=%d, n=%d\n", - atomic_read (state), - BUF_IN_USE, - nextbuf, - prevbuf, - nextbuf /* n */); + /* If we didn't fill up the buffer with data, stop now. + * Put a 'stop' marker in the DMA table too, to tell the + * audio hardware to stop if it gets here. + */ + if (chan->slop_len < VIA_DMA_BUF_SIZE) { + sgtable[n].count = cpu_to_le32 (chan->slop_len | VIA_EOL | VIA_STOP); + goto out; + } - n = nextbuf; + /* + * If we get to this point, we have filled a buffer with + * audio data, flush the buffer to audio hardware. + */ - if (copy_from_user ((void*)chan->sgbuf[n].cpuaddr, - userbuf, size)) - return -EFAULT; + /* Record the true size for the audio hardware to notice */ + if (n == (VIA_DMA_BUFFERS - 1)) + sgtable[n].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_EOL); + else + sgtable[n].count = cpu_to_le32 (VIA_DMA_BUF_SIZE | VIA_FLAG); - if (n == (VIA_DMA_BUFFERS - 1)) - sgtable[n].count = size | VIA_EOL; - else - sgtable[n].count = size | VIA_FLAG; + /* advance channel software pointer to point to + * the next buffer we will fill with data + */ + if (chan->sw_ptr == (VIA_DMA_BUFFERS - 1)) + chan->sw_ptr = 0; + else + chan->sw_ptr++; - /* if SGD stopped during data copy or SG table update, - * then loop back to the beginning without updating - * any pointers. - * ie. slack way to prevent race */ - if (!STATE_STARTED) - continue; + /* mark one less buffer as being available for userspace consumption */ + assert (atomic_read (&chan->n_bufs) > 0); + atomic_dec (&chan->n_bufs); - atomic_inc (&chan->next_buf); + /* we are at a block boundary, there is no fragment data */ + chan->slop_len = 0; - count -= size; - userbuf += size; - continue; - } + /* if SGD has not yet been started, start it */ + via_chan_maybe_start (chan); - /* case 4, final SGT active case: no free buffers, wait for one */ - - else { - DPRINTK ("case 4\n"); - DPRINTK ("st=%d, fb=%d -- nb=%d, pb=%d\n", - atomic_read (state), - BUF_IN_USE, - nextbuf, - prevbuf); - - /* if playback stopped, no need to sleep */ - if (!STATE_STARTED) - continue; - - /* if buffer free, no need to sleep */ - if (!via_chan_full (chan)) - continue; - - if (non_blocking) { - rc = userbuf - orig_userbuf; - if (rc == 0) - rc = -EAGAIN; - return rc; - } + DPRINTK("Flushed block %u, sw_ptr now %u, n_bufs now %d\n", + n, chan->sw_ptr, atomic_read (&chan->n_bufs)); - DPRINTK ("sleeping\n"); - interruptible_sleep_on (&chan->wait); - if (signal_pending (current)) - return -ERESTARTSYS; - } - } + DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", + inb (card->baseaddr + 0x00), + inb (card->baseaddr + 0x01), + inb (card->baseaddr + 0x02), + inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x0C), + inl (card->baseaddr + 0x80), + inl (card->baseaddr + 0x84)); -#if 0 - { - u8 r40,r41,r42,r43,r44,r48; - pci_read_config_byte (card->pdev, 0x40, &r40); - pci_read_config_byte (card->pdev, 0x41, &r41); - pci_read_config_byte (card->pdev, 0x42, &r42); - pci_read_config_byte (card->pdev, 0x43, &r43); - pci_read_config_byte (card->pdev, 0x44, &r44); - pci_read_config_byte (card->pdev, 0x48, &r48); - DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n", - r40,r41,r42,r43,r44,r48); - } -#endif + if (count > 0) + goto handle_one_block; - DPRINTK ("EXIT, returning %d\n", - userbuf - orig_userbuf); +out: return userbuf - orig_userbuf; } -#undef sgcount -#undef NEXTBUF -#undef BUF_IN_USE -#undef STATE_STOPPED -#undef STATE_STARTED static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct via_info *card; ssize_t rc; + int nonblock = (file->f_flags & O_NONBLOCK); DPRINTK ("ENTER, file=%p, buffer=%p, count=%u, ppos=%lu\n", file, buffer, count, ppos ? ((unsigned long)*ppos) : 0); - + assert (file != NULL); assert (buffer != NULL); card = file->private_data; @@ -1755,8 +2076,19 @@ static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count return -ESPIPE; } - rc = via_dsp_do_write (card, buffer, count, (file->f_flags & O_NONBLOCK)); + rc = via_syscall_down (card, nonblock); + if (rc) goto out; + + if (card->ch_out.is_mapped) { + rc = -ENXIO; + goto out_up; + } + + rc = via_dsp_do_write (card, buffer, count, nonblock); +out_up: + up (&card->syscall_sem); +out: DPRINTK("EXIT, returning %ld\n",(long) rc); return rc; } @@ -1765,45 +2097,105 @@ static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait) { struct via_info *card; - unsigned int mask = 0; + unsigned int mask = 0, rd, wr; DPRINTK ("ENTER\n"); assert (file != NULL); - assert (wait != NULL); card = file->private_data; assert (card != NULL); - if ((file->f_mode & FMODE_WRITE) && - (atomic_read (&card->ch_out.state) != sgd_stopped)) { - poll_wait(file, &card->ch_out.wait, wait); - - /* XXX is this correct */ - if (atomic_read (&card->ch_out.buf_in_use) < - atomic_read (&card->ch_out.next_buf)) - mask |= POLLOUT | POLLWRNORM; + rd = (file->f_mode & FMODE_READ); + wr = (file->f_mode & FMODE_WRITE); + + if (wr && (atomic_read (&card->ch_out.n_bufs) == 0)) { + assert (card->ch_out.is_active); + poll_wait(file, &card->ch_out.wait, wait); } + if (rd) { + /* XXX is it ok, spec-wise, to start DMA here? */ + via_chan_maybe_start (&card->ch_in); + if (atomic_read (&card->ch_in.n_bufs) == 0) + poll_wait(file, &card->ch_in.wait, wait); + } + + if (wr && (atomic_read (&card->ch_out.n_bufs) > 0)) + mask |= POLLOUT | POLLWRNORM; + if (rd && (atomic_read (&card->ch_in.n_bufs) > 0)) + mask |= POLLIN | POLLRDNORM; DPRINTK("EXIT, returning %u\n", mask); return mask; } -static int via_dsp_drain_dac (struct via_info *card, int non_block) +/** + * via_dsp_drain_playback - sleep until all playback samples are flushed + * @card: Private info for specified board + * @chan: Channel to drain + * @nonblock: boolean, non-zero if O_NONBLOCK is set + * + * Sleeps until all playback has been flushed to the audio + * hardware. + * + * Locking: inside card->syscall_sem + */ + +static int via_dsp_drain_playback (struct via_info *card, + struct via_channel *chan, int nonblock) { - DPRINTK ("ENTER, non_block = %d\n", non_block); + DPRINTK ("ENTER, nonblock = %d\n", nonblock); + + if (chan->slop_len > 0) + via_chan_flush_frag (chan); + + if (atomic_read (&chan->n_bufs) == VIA_DMA_BUFFERS) + goto out; - while (!via_chan_empty (&card->ch_out)) { - if (non_block) { - DPRINTK ("EXIT, returning -EBUSY\n"); - return -EBUSY; + via_chan_maybe_start (chan); + + while (atomic_read (&chan->n_bufs) < VIA_DMA_BUFFERS) { + if (nonblock) { + DPRINTK ("EXIT, returning -EAGAIN\n"); + return -EAGAIN; + } + +#ifdef VIA_DEBUG + { + u8 r40,r41,r42,r43,r44,r48; + pci_read_config_byte (card->pdev, 0x40, &r40); + pci_read_config_byte (card->pdev, 0x41, &r41); + pci_read_config_byte (card->pdev, 0x42, &r42); + pci_read_config_byte (card->pdev, 0x43, &r43); + pci_read_config_byte (card->pdev, 0x44, &r44); + pci_read_config_byte (card->pdev, 0x48, &r48); + DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n", + r40,r41,r42,r43,r44,r48); + + DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", + inb (card->baseaddr + 0x00), + inb (card->baseaddr + 0x01), + inb (card->baseaddr + 0x02), + inl (card->baseaddr + 0x04), + inl (card->baseaddr + 0x0C), + inl (card->baseaddr + 0x80), + inl (card->baseaddr + 0x84)); } + + if (!chan->is_active) + printk (KERN_ERR "sleeping but not active\n"); +#endif + + DPRINTK ("sleeping, nbufs=%d\n", atomic_read (&chan->n_bufs)); + interruptible_sleep_on (&chan->wait); + if (signal_pending (current)) { DPRINTK ("EXIT, returning -ERESTARTSYS\n"); return -ERESTARTSYS; } + } -#ifndef NDEBUG +#ifdef VIA_DEBUG { u8 r40,r41,r42,r43,r44,r48; pci_read_config_byte (card->pdev, 0x40, &r40); @@ -1815,7 +2207,6 @@ static int via_dsp_drain_dac (struct via_info *card, int non_block) DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n", r40,r41,r42,r43,r44,r48); - spin_lock_irq (&card->lock); DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n", inb (card->baseaddr + 0x00), inb (card->baseaddr + 0x01), @@ -1824,39 +2215,49 @@ static int via_dsp_drain_dac (struct via_info *card, int non_block) inl (card->baseaddr + 0x0C), inl (card->baseaddr + 0x80), inl (card->baseaddr + 0x84)); - spin_unlock_irq (&card->lock); - + + DPRINTK ("final nbufs=%d\n", atomic_read (&chan->n_bufs)); } #endif - DPRINTK ("sleeping\n"); - interruptible_sleep_on (&card->ch_out.wait); - } - - DPRINTK ("EXIT\n"); +out: + DPRINTK ("EXIT, returning 0\n"); return 0; } +/** + * via_dsp_ioctl_space - get information about channel buffering + * @card: Private info for specified board + * @chan: pointer to channel-specific info + * @arg: user buffer for returned information + * + * Handles SNDCTL_DSP_GETISPACE and SNDCTL_DSP_GETOSPACE. + * + * Locking: inside card->syscall_sem + */ + static int via_dsp_ioctl_space (struct via_info *card, struct via_channel *chan, void *arg) { audio_buf_info info; - int n; info.fragstotal = VIA_DMA_BUFFERS; info.fragsize = VIA_DMA_BUF_SIZE; - /* number of full fragments we can read without blocking */ - n = atomic_read (&chan->next_buf) - atomic_read (&chan->buf_in_use); - info.fragments = VIA_DMA_BUFFERS - n; + /* number of full fragments we can read/write without blocking */ + info.fragments = atomic_read (&chan->n_bufs); + + if ((chan->slop_len > 0) && (info.fragments > 0)) + info.fragments--; /* number of bytes that can be read or written immediately - * without blocking. FIXME: we are lazy and ignore partially-full - * buffers. + * without blocking. */ - info.bytes = info.fragments * VIA_DMA_BUF_SIZE; + info.bytes = (info.fragments * VIA_DMA_BUF_SIZE); + if (chan->slop_len > 0) + info.bytes += VIA_DMA_BUF_SIZE - chan->slop_len; DPRINTK ("EXIT, returning fragstotal=%d, fragsize=%d, fragments=%d, bytes=%d\n", info.fragstotal, @@ -1868,205 +2269,375 @@ static int via_dsp_ioctl_space (struct via_info *card, } +/** + * via_dsp_ioctl_ptr - get information about hardware buffer ptr + * @card: Private info for specified board + * @chan: pointer to channel-specific info + * @arg: user buffer for returned information + * + * Handles SNDCTL_DSP_GETIPTR and SNDCTL_DSP_GETOPTR. + * + * Locking: inside card->syscall_sem + */ -static int via_dsp_ioctl (struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int via_dsp_ioctl_ptr (struct via_info *card, + struct via_channel *chan, + void *arg) { - int rc = -EINVAL, rd=0, wr=0, val=0; - struct via_info *card; + count_info info; - DPRINTK ("ENTER, cmd = 0x%08X\n", cmd); + spin_lock_irq (&card->lock); - assert (file != NULL); - card = file->private_data; - assert (card != NULL); + info.bytes = chan->bytes; + info.blocks = chan->n_irqs; + chan->n_irqs = 0; - if (file->f_mode & FMODE_WRITE) - wr = 1; - if (file->f_mode & FMODE_READ) - rd = 1; - - switch (cmd) { + spin_unlock_irq (&card->lock); - /* OSS API version. XXX unverified */ - case OSS_GETVERSION: - DPRINTK("EXIT, returning SOUND_VERSION\n"); - return put_user (SOUND_VERSION, (int *)arg); + if (chan->is_active) { + unsigned long extra; + info.ptr = atomic_read (&chan->hw_ptr) * VIA_DMA_BUF_SIZE; + extra = VIA_DMA_BUF_SIZE - inl (chan->iobase + VIA_BASE0_PCM_OUT_BLOCK_COUNT); + info.ptr += extra; + info.bytes += extra; + } else { + info.ptr = 0; + } + + DPRINTK ("EXIT, returning bytes=%d, blocks=%d, ptr=%d\n", + info.bytes, + info.blocks, + info.ptr); + + return copy_to_user (arg, &info, sizeof (info)); +} + + +static int via_dsp_ioctl_trigger (struct via_channel *chan, int val) +{ + int enable, do_something; + + if (chan->is_record) + enable = (val & PCM_ENABLE_INPUT); + else + enable = (val & PCM_ENABLE_OUTPUT); + + if (!chan->is_enabled && enable) { + do_something = 1; + } else if (chan->is_enabled && !enable) { + do_something = -1; + } else { + do_something = 0; + } + + DPRINTK ("enable=%d, do_something=%d\n", + enable, do_something); + + if (chan->is_active && do_something) + return -EINVAL; + + if (do_something == 1) { + chan->is_enabled = 1; + via_chan_maybe_start (chan); + DPRINTK ("Triggering input\n"); + } + + else if (do_something == -1) { + chan->is_enabled = 0; + DPRINTK ("Setup input trigger\n"); + } + + return 0; +} + + +static int via_dsp_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int rc, rd=0, wr=0, val=0; + struct via_info *card; + struct via_channel *chan; + int nonblock = (file->f_flags & O_NONBLOCK); + + assert (file != NULL); + card = file->private_data; + assert (card != NULL); + + if (file->f_mode & FMODE_WRITE) + wr = 1; + if (file->f_mode & FMODE_READ) + rd = 1; + + rc = via_syscall_down (card, nonblock); + if (rc) + return rc; + rc = -EINVAL; + + switch (cmd) { + + /* OSS API version. XXX unverified */ + case OSS_GETVERSION: + DPRINTK("ioctl OSS_GETVERSION, EXIT, returning SOUND_VERSION\n"); + rc = put_user (SOUND_VERSION, (int *)arg); + break; /* list of supported PCM data formats */ case SNDCTL_DSP_GETFMTS: - DPRINTK("EXIT, returning AFMT U8|S16_LE\n"); - return put_user (AFMT_U8 | AFMT_S16_LE, (int *)arg); + DPRINTK("DSP_GETFMTS, EXIT, returning AFMT U8|S16_LE\n"); + rc = put_user (AFMT_U8 | AFMT_S16_LE, (int *)arg); + break; /* query or set current channel's PCM data format */ case SNDCTL_DSP_SETFMT: - if (get_user(val, (int *)arg)) - return -EFAULT; + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK("DSP_SETFMT, val==%d\n", val); if (val != AFMT_QUERY) { rc = 0; - spin_lock_irq (&card->lock); if (rc == 0 && rd) rc = via_chan_set_fmt (card, &card->ch_in, val); if (rc == 0 && wr) rc = via_chan_set_fmt (card, &card->ch_out, val); - spin_unlock_irq (&card->lock); - if (rc <= 0) - return rc ? rc : -EINVAL; + if (rc <= 0) { + if (rc == 0) + rc = -EINVAL; + break; + } val = rc; } else { - spin_lock_irq (&card->lock); if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_16BIT)) || (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_16BIT))) val = AFMT_S16_LE; else val = AFMT_U8; - spin_unlock_irq (&card->lock); } DPRINTK("SETFMT EXIT, returning %d\n", val); - return put_user (val, (int *)arg); + rc = put_user (val, (int *)arg); + break; /* query or set number of channels (1=mono, 2=stereo) */ case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *)arg)) - return -EFAULT; + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK("DSP_CHANNELS, val==%d\n", val); if (val != 0) { rc = 0; - spin_lock_irq (&card->lock); if (rc == 0 && rd) rc = via_chan_set_stereo (card, &card->ch_in, val); if (rc == 0 && wr) rc = via_chan_set_stereo (card, &card->ch_out, val); - spin_unlock_irq (&card->lock); - if (rc <= 0) - return rc ? rc : -EINVAL; + if (rc <= 0) { + if (rc == 0) + rc = -EINVAL; + break; + } val = rc; } else { - spin_lock_irq (&card->lock); if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_STEREO)) || (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_STEREO))) val = 2; else val = 1; - spin_unlock_irq (&card->lock); } DPRINTK("CHANNELS EXIT, returning %d\n", val); - return put_user (val, (int *)arg); - + rc = put_user (val, (int *)arg); + break; + /* enable (val is not zero) or disable (val == 0) stereo */ case SNDCTL_DSP_STEREO: - if (get_user(val, (int *)arg)) - return -EFAULT; + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK("DSP_STEREO, val==%d\n", val); rc = 0; - spin_lock_irq (&card->lock); + if (rc == 0 && rd) rc = via_chan_set_stereo (card, &card->ch_in, val ? 2 : 1); if (rc == 0 && wr) rc = via_chan_set_stereo (card, &card->ch_out, val ? 2 : 1); - spin_unlock_irq (&card->lock); - if (rc <= 0) - return rc ? rc : -EINVAL; + + if (rc <= 0) { + if (rc == 0) + rc = -EINVAL; + break; + } DPRINTK("STEREO EXIT, returning %d\n", val); - return 0; - + rc = 0; + break; + /* query or set sampling rate */ case SNDCTL_DSP_SPEED: - if (get_user(val, (int *)arg)) - return -EFAULT; - if (val < 0) - return -EINVAL; + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK("DSP_SPEED, val==%d\n", val); + if (val < 0) { + rc = -EINVAL; + break; + } if (val > 0) { rc = 0; - spin_lock_irq (&card->lock); + if (rc == 0 && rd) rc = via_chan_set_speed (card, &card->ch_in, val); if (rc == 0 && wr) rc = via_chan_set_speed (card, &card->ch_out, val); - spin_unlock_irq (&card->lock); - if (rc <= 0) - return rc ? rc : -EINVAL; + + if (rc <= 0) { + if (rc == 0) + rc = -EINVAL; + break; + } val = rc; } else { - spin_lock_irq (&card->lock); if (rd) val = card->ch_in.rate; else if (wr) val = card->ch_out.rate; else val = 0; - spin_unlock_irq (&card->lock); } DPRINTK("SPEED EXIT, returning %d\n", val); - return put_user (val, (int *)arg); - + rc = put_user (val, (int *)arg); + break; + /* wait until all buffers have been played, and then stop device */ case SNDCTL_DSP_SYNC: + DPRINTK ("DSP_SYNC\n"); if (wr) { - DPRINTK("SYNC EXIT (after calling via_dsp_drain_dac)\n"); - return via_dsp_drain_dac (card, file->f_flags & O_NONBLOCK); + DPRINTK("SYNC EXIT (after calling via_dsp_drain_playback)\n"); + rc = via_dsp_drain_playback (card, &card->ch_out, nonblock); } break; /* stop recording/playback immediately */ case SNDCTL_DSP_RESET: - spin_lock_irq (&card->lock); + DPRINTK ("DSP_RESET\n"); if (rd) { via_chan_clear (&card->ch_in); - via_chan_pcm_fmt (card, &card->ch_in, 1); + via_chan_pcm_fmt (&card->ch_in, 1); } if (wr) { via_chan_clear (&card->ch_out); - via_chan_pcm_fmt (card, &card->ch_out, 1); + via_chan_pcm_fmt (&card->ch_out, 1); } - spin_unlock_irq (&card->lock); - DPRINTK("RESET EXIT, returning 0\n"); - return 0; + + rc = 0; + break; /* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */ case SNDCTL_DSP_GETCAPS: - DPRINTK("GETCAPS EXIT\n"); - return put_user(DSP_CAP_REVISION, (int *)arg); - + DPRINTK("DSP_GETCAPS\n"); + rc = put_user(VIA_DSP_CAP, (int *)arg); + break; + /* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */ case SNDCTL_DSP_GETBLKSIZE: - DPRINTK("GETBLKSIZE EXIT\n"); - return put_user(VIA_DMA_BUF_SIZE, (int *)arg); - + DPRINTK("DSP_GETBLKSIZE\n"); + rc = put_user(VIA_DMA_BUF_SIZE, (int *)arg); + break; + /* obtain information about input buffering */ case SNDCTL_DSP_GETISPACE: - DPRINTK("GETISPACE EXIT\n"); - return via_dsp_ioctl_space (card, &card->ch_in, (void*) arg); - + DPRINTK("DSP_GETISPACE\n"); + if (rd) + rc = via_dsp_ioctl_space (card, &card->ch_in, (void*) arg); + break; + /* obtain information about output buffering */ case SNDCTL_DSP_GETOSPACE: - DPRINTK("GETOSPACE EXIT\n"); - return via_dsp_ioctl_space (card, &card->ch_out, (void*) arg); + DPRINTK("DSP_GETOSPACE\n"); + if (wr) + rc = via_dsp_ioctl_space (card, &card->ch_out, (void*) arg); + break; + + /* obtain information about input hardware pointer */ + case SNDCTL_DSP_GETIPTR: + DPRINTK("DSP_GETIPTR\n"); + if (rd) + rc = via_dsp_ioctl_ptr (card, &card->ch_in, (void*) arg); + break; + + /* obtain information about output hardware pointer */ + case SNDCTL_DSP_GETOPTR: + DPRINTK("DSP_GETOPTR\n"); + if (wr) + rc = via_dsp_ioctl_ptr (card, &card->ch_out, (void*) arg); + break; /* return number of bytes remaining to be played by DMA engine */ case SNDCTL_DSP_GETODELAY: { - int n; - - n = atomic_read (&card->ch_out.next_buf) - - atomic_read (&card->ch_out.buf_in_use); - assert (n >= 0); - - if (n == 0) - val = 0; - else { - val = (n - 1) * VIA_DMA_BUF_SIZE; - val += inl (card->ch_out.iobase + VIA_BASE0_PCM_OUT_BLOCK_COUNT); + DPRINTK("DSP_GETODELAY\n"); + + chan = &card->ch_out; + + if (!wr) + break; + + val = VIA_DMA_BUFFERS - atomic_read (&chan->n_bufs); + + if (val > 0) { + val *= VIA_DMA_BUF_SIZE; + val -= VIA_DMA_BUF_SIZE - + inl (chan->iobase + VIA_BASE0_PCM_OUT_BLOCK_COUNT); } - + val += chan->slop_len; + + assert (val <= (VIA_DMA_BUF_SIZE * VIA_DMA_BUFFERS)); + DPRINTK("GETODELAY EXIT, val = %d bytes\n", val); - return put_user (val, (int *)arg); + rc = put_user (val, (int *)arg); + break; } + /* handle the quick-start of a channel, + * or the notification that a quick-start will + * occur in the future + */ + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK("DSP_SETTRIGGER, rd=%d, wr=%d, act=%d/%d, en=%d/%d\n", + rd, wr, card->ch_in.is_active, card->ch_out.is_active, + card->ch_in.is_enabled, card->ch_out.is_enabled); + + rc = 0; + + if (rd) + rc = via_dsp_ioctl_trigger (&card->ch_in, val); + if (!rc && wr) + rc = via_dsp_ioctl_trigger (&card->ch_out, val); + + break; + + /* Enable full duplex. Since we do this as soon as we are opened + * with O_RDWR, this is mainly a no-op that always returns success. + */ + case SNDCTL_DSP_SETDUPLEX: + DPRINTK("DSP_SETDUPLEX\n"); + if (!rd || !wr) + break; + rc = 0; + break; + /* set fragment size. implemented as a successful no-op for now */ case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *)arg)) - return -EFAULT; + if (get_user(val, (int *)arg)) { + rc = -EFAULT; + break; + } + DPRINTK("DSP_SETFRAGMENT, val==%d\n", val); DPRINTK ("SNDCTL_DSP_SETFRAGMENT (fragshift==0x%04X (%d), maxfrags==0x%04X (%d))\n", val & 0xFFFF, @@ -2075,19 +2646,29 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file, (val >> 16) & 0xFFFF); /* just to shut up some programs */ - return 0; + rc = 0; + break; - /* inform device of an upcoming pause in input (or output). not implemented */ + /* inform device of an upcoming pause in input (or output). */ case SNDCTL_DSP_POST: - DPRINTK("POST EXIT (null ioctl, returning -EINVAL)\n"); - return -EINVAL; + DPRINTK("DSP_POST\n"); + if (wr) { + if (card->ch_out.slop_len > 0) + via_chan_flush_frag (&card->ch_out); + via_chan_maybe_start (&card->ch_out); + } + + rc = 0; + break; /* not implemented */ default: - DPRINTK ("unhandled ioctl\n"); + DPRINTK ("unhandled ioctl, cmd==%u, arg==%p\n", + cmd, (void*) arg); break; } - + + up (&card->syscall_sem); DPRINTK("EXIT, returning %d\n", rc); return rc; } @@ -2095,25 +2676,28 @@ static int via_dsp_ioctl (struct inode *inode, struct file *file, static int via_dsp_open (struct inode *inode, struct file *file) { - int open_mode, rc = -EINVAL, minor = MINOR(inode->i_rdev); - int got_read_chan = 0, is_busy; + int rc, minor = MINOR(inode->i_rdev); + int got_read_chan = 0; struct via_info *card; struct pci_dev *pdev; + struct via_channel *chan; struct pci_driver *drvr; - unsigned long flags; + int nonblock = (file->f_flags & O_NONBLOCK); DPRINTK ("ENTER, minor=%d, file->f_mode=0x%x\n", minor, file->f_mode); - - if (file->f_mode & FMODE_READ) /* no input ATM */ - goto err_out; + + if (!(file->f_mode & (FMODE_READ | FMODE_WRITE))) { + DPRINTK ("EXIT, returning -EINVAL\n"); + return -EINVAL; + } card = NULL; pci_for_each_dev(pdev) { drvr = pci_dev_driver (pdev); if (drvr == &via_driver) { - assert (pdev->driver_data != NULL); - - card = pdev->driver_data; + assert (pci_get_drvdata (pdev) != NULL); + + card = pci_get_drvdata (pdev); DPRINTK ("dev_dsp = %d, minor = %d, assn = %d\n", card->dev_dsp, minor, (card->dev_dsp ^ minor) & ~0xf); @@ -2122,299 +2706,109 @@ static int via_dsp_open (struct inode *inode, struct file *file) goto match; } } - + DPRINTK ("no matching %s found\n", card ? "minor" : "driver"); - rc = -ENODEV; - goto err_out; + return -ENODEV; match: - file->private_data = card; - - /* wait for device to become free */ - spin_lock_irqsave (&card->lock, flags); - open_mode = card->open_mode; - if (open_mode & file->f_mode) - is_busy = 1; - else { - is_busy = 0; - card->open_mode |= file->f_mode; - open_mode = card->open_mode; - } - spin_unlock_irqrestore (&card->lock, flags); - if (is_busy) { - rc = -EBUSY; - goto err_out; + if (nonblock) { + if (down_trylock (&card->open_sem)) { + DPRINTK ("EXIT, returning -EAGAIN\n"); + return -EAGAIN; + } + } else { + if (down_interruptible (&card->open_sem)) { + DPRINTK ("EXIT, returning -ERESTARTSYS\n"); + return -ERESTARTSYS; + } } - DPRINTK("open_mode now 0x%x\n", open_mode); + file->private_data = card; + DPRINTK("file->f_mode == 0x%x\n", file->f_mode); /* handle input from analog source */ if (file->f_mode & FMODE_READ) { - rc = via_chan_init (card, &card->ch_in, 0x10); + chan = &card->ch_in; + + rc = via_chan_init (card, chan); if (rc) - goto err_out_clear_mode; - + goto err_out; + got_read_chan = 1; - - /* why is this forced to 16-bit stereo in all drivers? */ - card->ch_in.pcm_fmt = - VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO; - spin_lock_irqsave (&card->lock, flags); - via_chan_pcm_fmt (card, &card->ch_out, 0); - spin_unlock_irqrestore (&card->lock, flags); + /* why is this forced to 16-bit stereo in all drivers? */ + chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO; - via_set_adc_rate (card, 8000); + via_chan_pcm_fmt (chan, 0); + via_set_rate (&card->ac97, chan, 44100); } /* handle output to analog source */ if (file->f_mode & FMODE_WRITE) { - rc = via_chan_init (card, &card->ch_out, 0x00); - if (rc) - goto err_out_clear_mode; - - if ((minor & 0xf) == SND_DEV_DSP16) - card->ch_out.pcm_fmt |= VIA_PCM_FMT_16BIT; - - spin_lock_irqsave (&card->lock, flags); - via_chan_pcm_fmt (card, &card->ch_out, 0); - spin_unlock_irqrestore (&card->lock, flags); - - via_set_dac_rate (card, 8000); - } - - DPRINTK ("EXIT, returning 0\n"); - return 0; - -err_out_clear_mode: - if (got_read_chan) - via_chan_free (card, &card->ch_in); - spin_lock_irqsave (&card->lock, flags); - card->open_mode &= ~file->f_mode; - spin_unlock_irqrestore (&card->lock, flags); -err_out: - DPRINTK("ERROR EXIT, returning %d\n", rc); - return rc; -} - - -static int via_dsp_release(struct inode *inode, struct file *file) -{ - struct via_info *card; - unsigned long flags; - - DPRINTK ("ENTER\n"); - - assert (file != NULL); - card = file->private_data; - assert (card != NULL); - - lock_kernel(); - if (file->f_mode & FMODE_READ) - via_chan_free (card, &card->ch_in); - - if (file->f_mode & FMODE_WRITE) { - via_dsp_drain_dac (card, file->f_flags & O_NONBLOCK); - via_chan_free (card, &card->ch_out); - } - - spin_lock_irqsave (&card->lock, flags); - card->open_mode &= ~(file->f_mode); - spin_unlock_irqrestore (&card->lock, flags); - - wake_up (&card->open_wait); - unlock_kernel(); - - DPRINTK("EXIT, returning 0\n"); - return 0; -} - - -#ifdef VIA_PROC_FS - -/**************************************************************** - * - * /proc/driver/via/info - * - * - */ - -static int via_info_read_proc (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ -#define YN(val,bit) (((val) & (bit)) ? "yes" : "no") -#define ED(val,bit) (((val) & (bit)) ? "enable" : "disable") - - int len = 0; - u8 r40, r41, r42, r44; - struct via_info *card = data; - - DPRINTK ("ENTER\n"); - - assert (card != NULL); - - len += sprintf (page+len, VIA_CARD_NAME "\n\n"); - - pci_read_config_byte (card->pdev, 0x40, &r40); - pci_read_config_byte (card->pdev, 0x41, &r41); - pci_read_config_byte (card->pdev, 0x42, &r42); - pci_read_config_byte (card->pdev, 0x44, &r44); - - len += sprintf (page+len, - "Via 82Cxxx PCI registers:\n" - "\n" - "40 Codec Ready: %s\n" - " Codec Low-power: %s\n" - " Secondary Codec Ready: %s\n" - "\n" - "41 Interface Enable: %s\n" - " De-Assert Reset: %s\n" - " Force SYNC high: %s\n" - " Force SDO high: %s\n" - " Variable Sample Rate On-Demand Mode: %s\n" - " SGD Read Channel PCM Data Out: %s\n" - " FM Channel PCM Data Out: %s\n" - " SB PCM Data Out: %s\n" - "\n" - "42 Game port enabled: %s\n" - " SoundBlaster enabled: %s\n" - " FM enabled: %s\n" - " MIDI enabled: %s\n" - "\n" - "44 AC-Link Interface Access: %s\n" - " Secondary Codec Support: %s\n" - - "\n", - - YN (r40, VIA_CR40_AC97_READY), - YN (r40, VIA_CR40_AC97_LOW_POWER), - YN (r40, VIA_CR40_SECONDARY_READY), - - ED (r41, VIA_CR41_AC97_ENABLE), - YN (r41, (1 << 6)), - YN (r41, (1 << 5)), - YN (r41, (1 << 4)), - ED (r41, (1 << 3)), - ED (r41, (1 << 2)), - ED (r41, (1 << 1)), - ED (r41, (1 << 0)), - - YN (r42, VIA_CR42_GAME_ENABLE), - YN (r42, VIA_CR42_SB_ENABLE), - YN (r42, VIA_CR42_FM_ENABLE), - YN (r42, VIA_CR42_MIDI_ENABLE), - - YN (r44, VIA_CR44_AC_LINK_ACCESS), - YN (r44, VIA_CR44_SECOND_CODEC_SUPPORT) - - ); - - DPRINTK("EXIT, returning %d\n", len); - return len; - -#undef YN -#undef ED -} - - -/**************************************************************** - * - * /proc/driver/via/... setup and cleanup - * - * - */ - -static int __init via_init_proc (void) -{ - DPRINTK ("ENTER\n"); - - if (!proc_mkdir ("driver/via", 0)) - return -EIO; - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -static void via_cleanup_proc (void) -{ - DPRINTK ("ENTER\n"); - - remove_proc_entry ("driver/via", NULL); - - DPRINTK ("EXIT\n"); -} - - -static int __init via_card_init_proc (struct via_info *card) -{ - char s[32]; - int rc; - - DPRINTK ("ENTER\n"); - - sprintf (s, "driver/via/%d", card->card_num); - if (!proc_mkdir (s, 0)) { - rc = -EIO; - goto err_out_none; - } - - sprintf (s, "driver/via/%d/info", card->card_num); - if (!create_proc_read_entry (s, 0, 0, via_info_read_proc, card)) { - rc = -EIO; - goto err_out_dir; - } + chan = &card->ch_out; - sprintf (s, "driver/via/%d/ac97", card->card_num); - if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) { - rc = -EIO; - goto err_out_info; + rc = via_chan_init (card, chan); + if (rc) + goto err_out_read_chan; + + if ((minor & 0xf) == SND_DEV_DSP16) { + chan->pcm_fmt |= VIA_PCM_FMT_16BIT; + via_set_rate (&card->ac97, chan, 44100); + } else { + via_set_rate (&card->ac97, chan, 8000); + } + + via_chan_pcm_fmt (chan, 0); } DPRINTK ("EXIT, returning 0\n"); return 0; -err_out_info: - sprintf (s, "driver/via/%d/info", card->card_num); - remove_proc_entry (s, NULL); - -err_out_dir: - sprintf (s, "driver/via/%d", card->card_num); - remove_proc_entry (s, NULL); - -err_out_none: - DPRINTK ("EXIT, returning %d\n", rc); +err_out_read_chan: + if (got_read_chan) + via_chan_free (card, &card->ch_in); +err_out: + up (&card->open_sem); + DPRINTK("ERROR EXIT, returning %d\n", rc); return rc; } -static void via_card_cleanup_proc (struct via_info *card) +static int via_dsp_release(struct inode *inode, struct file *file) { - char s[32]; + struct via_info *card; + int nonblock = (file->f_flags & O_NONBLOCK); + int rc; DPRINTK ("ENTER\n"); - sprintf (s, "driver/via/%d/ac97", card->card_num); - remove_proc_entry (s, NULL); - - sprintf (s, "driver/via/%d/info", card->card_num); - remove_proc_entry (s, NULL); + assert (file != NULL); + card = file->private_data; + assert (card != NULL); - sprintf (s, "driver/via/%d", card->card_num); - remove_proc_entry (s, NULL); + rc = via_syscall_down (card, nonblock); + if (rc) { + DPRINTK ("EXIT (syscall_down error), rc=%d\n", rc); + return rc; + } - DPRINTK ("EXIT\n"); -} + if (file->f_mode & FMODE_WRITE) { + rc = via_dsp_drain_playback (card, &card->ch_out, nonblock); + if (rc) + printk (KERN_DEBUG "via_audio: ignoring drain playback error %d\n", rc); + via_chan_free (card, &card->ch_out); + } -#else + if (file->f_mode & FMODE_READ) + via_chan_free (card, &card->ch_in); -static inline int via_init_proc (void) { return 0; } -static inline void via_cleanup_proc (void) {} -static inline int via_card_init_proc (struct via_info *card) { return 0; } -static inline void via_card_cleanup_proc (struct via_info *card) {} + up (&card->syscall_sem); + up (&card->open_sem); -#endif /* VIA_PROC_FS */ + DPRINTK("EXIT, returning 0\n"); + return 0; +} /**************************************************************** @@ -2430,9 +2824,9 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id struct via_info *card; u8 tmp; static int printed_version = 0; - + DPRINTK ("ENTER\n"); - + if (printed_version++ == 0) printk (KERN_INFO "Via 686a audio driver " VIA_VERSION "\n"); @@ -2448,7 +2842,7 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id rc = -EIO; goto err_out_none; } - + card = kmalloc (sizeof (*card), GFP_KERNEL); if (!card) { printk (KERN_ERR PFX "out of memory, aborting\n"); @@ -2456,15 +2850,21 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id goto err_out_none; } - pdev->driver_data = card; + pci_set_drvdata (pdev, card); memset (card, 0, sizeof (*card)); card->pdev = pdev; card->baseaddr = pci_resource_start (pdev, 0); card->card_num = via_num_cards++; spin_lock_init (&card->lock); - init_waitqueue_head(&card->open_wait); - + init_MUTEX (&card->syscall_sem); + init_MUTEX (&card->open_sem); + + /* we must init these now, in case the intr handler needs them */ + via_chan_init_defaults (card, &card->ch_out); + via_chan_init_defaults (card, &card->ch_in); + via_chan_init_defaults (card, &card->ch_fm); + /* if BAR 2 is present, chip is Rev H or later, * which means it has a few extra features */ if (pci_resource_start (pdev, 2) > 0) @@ -2481,8 +2881,8 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id rc = -ENODEV; goto err_out_kfree; } - - /* + + /* * init AC97 mixer and codec */ rc = via_ac97_init (card); @@ -2499,10 +2899,10 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id printk (KERN_ERR PFX "DSP device init failed, aborting\n"); goto err_out_have_mixer; } - + /* * per-card /proc info - */ + */ rc = via_card_init_proc (card); if (rc) { printk (KERN_ERR PFX "card-specific /proc init failed, aborting\n"); @@ -2517,10 +2917,11 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id printk (KERN_ERR PFX "interrupt init failed, aborting\n"); goto err_out_have_proc; } - + pci_read_config_byte (pdev, 0x3C, &tmp); if ((tmp & 0x0F) != pdev->irq) { printk (KERN_WARNING PFX "IRQ fixup, 0x3C==0x%02X\n", tmp); + udelay (15); tmp &= 0xF0; tmp |= pdev->irq; pci_write_config_byte (pdev, 0x3C, tmp); @@ -2532,7 +2933,7 @@ static int __init via_init_one (struct pci_dev *pdev, const struct pci_device_id printk (KERN_INFO PFX "board #%d at 0x%04lX, IRQ %d\n", card->card_num + 1, card->baseaddr, pdev->irq); - + DPRINTK ("EXIT, returning 0\n"); return 0; @@ -2554,7 +2955,7 @@ err_out_kfree: err_out_none: release_region (pci_resource_start (pdev, 0), pci_resource_len (pdev, 0)); err_out: - pdev->driver_data = NULL; + pci_set_drvdata (pdev, NULL); DPRINTK ("EXIT - returning %d\n", rc); return rc; } @@ -2563,13 +2964,13 @@ err_out: static void __exit via_remove_one (struct pci_dev *pdev) { struct via_info *card; - + DPRINTK ("ENTER\n"); - + assert (pdev != NULL); - card = pdev->driver_data; + card = pci_get_drvdata (pdev); assert (card != NULL); - + via_interrupt_cleanup (card); via_card_cleanup_proc (card); via_dsp_cleanup (card); @@ -2582,8 +2983,8 @@ static void __exit via_remove_one (struct pci_dev *pdev) #endif kfree (card); - pdev->driver_data = NULL; - + pci_set_drvdata (pdev, NULL); + pci_set_power_state (pdev, 3); /* ...zzzzzz */ DPRINTK ("EXIT\n"); @@ -2623,11 +3024,11 @@ static int __init init_via82cxxx_audio(void) return 0; } - + static void __exit cleanup_via82cxxx_audio(void) { DPRINTK("ENTER\n"); - + pci_unregister_driver (&via_driver); via_cleanup_proc (); @@ -2637,3 +3038,187 @@ static void __exit cleanup_via82cxxx_audio(void) module_init(init_via82cxxx_audio); module_exit(cleanup_via82cxxx_audio); + +MODULE_AUTHOR("Jeff Garzik "); +MODULE_DESCRIPTION("DSP audio and mixer driver for Via 82Cxxx audio devices"); +EXPORT_NO_SYMBOLS; + + + +#ifdef VIA_PROC_FS + +/**************************************************************** + * + * /proc/driver/via/info + * + * + */ + +static int via_info_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ +#define YN(val,bit) (((val) & (bit)) ? "yes" : "no") +#define ED(val,bit) (((val) & (bit)) ? "enable" : "disable") + + int len = 0; + u8 r40, r41, r42, r44; + struct via_info *card = data; + + DPRINTK ("ENTER\n"); + + assert (card != NULL); + + len += sprintf (page+len, VIA_CARD_NAME "\n\n"); + + pci_read_config_byte (card->pdev, 0x40, &r40); + pci_read_config_byte (card->pdev, 0x41, &r41); + pci_read_config_byte (card->pdev, 0x42, &r42); + pci_read_config_byte (card->pdev, 0x44, &r44); + + len += sprintf (page+len, + "Via 82Cxxx PCI registers:\n" + "\n" + "40 Codec Ready: %s\n" + " Codec Low-power: %s\n" + " Secondary Codec Ready: %s\n" + "\n" + "41 Interface Enable: %s\n" + " De-Assert Reset: %s\n" + " Force SYNC high: %s\n" + " Force SDO high: %s\n" + " Variable Sample Rate On-Demand Mode: %s\n" + " SGD Read Channel PCM Data Out: %s\n" + " FM Channel PCM Data Out: %s\n" + " SB PCM Data Out: %s\n" + "\n" + "42 Game port enabled: %s\n" + " SoundBlaster enabled: %s\n" + " FM enabled: %s\n" + " MIDI enabled: %s\n" + "\n" + "44 AC-Link Interface Access: %s\n" + " Secondary Codec Support: %s\n" + + "\n", + + YN (r40, VIA_CR40_AC97_READY), + YN (r40, VIA_CR40_AC97_LOW_POWER), + YN (r40, VIA_CR40_SECONDARY_READY), + + ED (r41, VIA_CR41_AC97_ENABLE), + YN (r41, (1 << 6)), + YN (r41, (1 << 5)), + YN (r41, (1 << 4)), + ED (r41, (1 << 3)), + ED (r41, (1 << 2)), + ED (r41, (1 << 1)), + ED (r41, (1 << 0)), + + YN (r42, VIA_CR42_GAME_ENABLE), + YN (r42, VIA_CR42_SB_ENABLE), + YN (r42, VIA_CR42_FM_ENABLE), + YN (r42, VIA_CR42_MIDI_ENABLE), + + YN (r44, VIA_CR44_AC_LINK_ACCESS), + YN (r44, VIA_CR44_SECOND_CODEC_SUPPORT) + + ); + + DPRINTK("EXIT, returning %d\n", len); + return len; + +#undef YN +#undef ED +} + + +/**************************************************************** + * + * /proc/driver/via/... setup and cleanup + * + * + */ + +static int __init via_init_proc (void) +{ + DPRINTK ("ENTER\n"); + + if (!proc_mkdir ("driver/via", 0)) + return -EIO; + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +static void via_cleanup_proc (void) +{ + DPRINTK ("ENTER\n"); + + remove_proc_entry ("driver/via", NULL); + + DPRINTK ("EXIT\n"); +} + + +static int __init via_card_init_proc (struct via_info *card) +{ + char s[32]; + int rc; + + DPRINTK ("ENTER\n"); + + sprintf (s, "driver/via/%d", card->card_num); + if (!proc_mkdir (s, 0)) { + rc = -EIO; + goto err_out_none; + } + + sprintf (s, "driver/via/%d/info", card->card_num); + if (!create_proc_read_entry (s, 0, 0, via_info_read_proc, card)) { + rc = -EIO; + goto err_out_dir; + } + + sprintf (s, "driver/via/%d/ac97", card->card_num); + if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, &card->ac97)) { + rc = -EIO; + goto err_out_info; + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out_info: + sprintf (s, "driver/via/%d/info", card->card_num); + remove_proc_entry (s, NULL); + +err_out_dir: + sprintf (s, "driver/via/%d", card->card_num); + remove_proc_entry (s, NULL); + +err_out_none: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + + +static void via_card_cleanup_proc (struct via_info *card) +{ + char s[32]; + + DPRINTK ("ENTER\n"); + + sprintf (s, "driver/via/%d/ac97", card->card_num); + remove_proc_entry (s, NULL); + + sprintf (s, "driver/via/%d/info", card->card_num); + remove_proc_entry (s, NULL); + + sprintf (s, "driver/via/%d", card->card_num); + remove_proc_entry (s, NULL); + + DPRINTK ("EXIT\n"); +} + +#endif /* VIA_PROC_FS */ diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index bf60b916740c..c1441ac7bd34 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -21,7 +21,7 @@ export-objs := usb.o # Multipart objects. list-multi := usbcore.o -usbcore-objs := usb.o usb-debug.o usb-core.o hub.o +usbcore-objs := usb.o usb-debug.o hub.o # Optional parts of multipart objects. diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c index 59e1c4007bc9..9c77b41a0d2f 100644 --- a/drivers/usb/printer.c +++ b/drivers/usb/printer.c @@ -52,6 +52,7 @@ #define IOCNR_GET_DEVICE_ID 1 #define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len) /* get device_id string */ +#define LPGETSTATUS 0x060b /* same as in drivers/char/lp.c */ /* * A DEVICE_ID string may include the printer's serial number. @@ -179,10 +180,17 @@ static int usblp_open(struct inode *inode, struct file *file) if (usblp->used) goto out; + /* + * TODO: need to implement LP_ABORTOPEN + O_NONBLOCK as in drivers/char/lp.c ??? + * This is #if 0-ed because we *don't* want to fail an open + * just because the printer is off-line. + */ +#if 0 if ((retval = usblp_check_status(usblp, 0))) { retval = retval > 1 ? -EIO : -ENOSPC; goto out; } +#endif usblp->used = 1; file->private_data = usblp; @@ -232,30 +240,59 @@ static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct usblp *usblp = file->private_data; - int length; + int length, err; + unsigned char status; - if ((_IOC_TYPE(cmd) != 'P') || (_IOC_DIR(cmd) != _IOC_READ)) - return -EINVAL; + if (_IOC_TYPE(cmd) == 'P') /* new-style ioctl number */ + + switch (_IOC_NR(cmd)) { - switch (_IOC_NR(cmd)) { + case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */ + if (_IOC_DIR(cmd) != _IOC_READ) + return -EINVAL; - case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */ + err = usblp_get_id(usblp, 0, usblp->device_id_string, DEVICE_ID_SIZE - 1); + if (err < 0) { + dbg ("usblp%d: error = %d reading IEEE-1284 Device ID string", + usblp->minor, err); + usblp->device_id_string[0] = usblp->device_id_string[1] = '\0'; + return -EIO; + } - length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */ + length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */ + if (length < DEVICE_ID_SIZE) + usblp->device_id_string[length] = '\0'; + else + usblp->device_id_string[DEVICE_ID_SIZE - 1] = '\0'; - dbg ("usblp_ioctl GET_DEVICE_ID actlen: %d, size: %d, string: '%s'", - length, _IOC_SIZE(cmd), &usblp->device_id_string[2]); + dbg ("usblp%d Device ID string [%d/max %d]='%s'", + usblp->minor, length, _IOC_SIZE(cmd), &usblp->device_id_string[2]); - if (length > _IOC_SIZE(cmd)) length = _IOC_SIZE(cmd); /* truncate */ + if (length > _IOC_SIZE(cmd)) length = _IOC_SIZE(cmd); /* truncate */ - if (copy_to_user((unsigned char *) arg, usblp->device_id_string, (unsigned long) length)) - return -EFAULT; + if (copy_to_user((unsigned char *) arg, usblp->device_id_string, (unsigned long) length)) + return -EFAULT; - break; + break; - default: - return -EINVAL; - } + default: + return -EINVAL; + } + else /* old-style ioctl value */ + switch (cmd) { + + case LPGETSTATUS: + if (usblp_read_status(usblp, &status)) { + err("usblp%d: failed reading printer status", usblp->minor); + return -EIO; + } + if (copy_to_user ((unsigned char *)arg, &status, 1)) + return -EFAULT; + break; + + default: + return -EINVAL; + } return 0; } diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c deleted file mode 100644 index 8e2dd4da5dac..000000000000 --- a/drivers/usb/usb-core.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * driver/usb/usb-core.c - * - * (C) Copyright David Waite 1999 - * based on code from usb.c, by Linus Torvalds - * - * The purpose of this file is to pull any and all generic modular code from - * usb.c and put it in a separate file. This way usb.c is kept as a generic - * library, while this file handles starting drivers, etc. - * - */ - -#include -#include -#include -#include - -/* - * USB core - */ - -int usb_hub_init(void); -void usb_hub_cleanup(void); -int usb_major_init(void); -void usb_major_cleanup(void); - - -/* - * Cleanup - */ - -static void __exit usb_exit(void) -{ - usb_major_cleanup(); - usbdevfs_cleanup(); - usb_hub_cleanup(); -} - -/* - * Init - */ - -static int __init usb_init(void) -{ - usb_major_init(); - usbdevfs_init(); - usb_hub_init(); - - return 0; -} - -module_init(usb_init); -module_exit(usb_exit); diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index df469e7d81ff..bb93ec898db6 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -26,6 +26,7 @@ #include #include /* for in_interrupt() */ #include +#include #ifdef CONFIG_USB_DEBUG @@ -47,6 +48,9 @@ static const int usb_bandwidth_option = 0; #endif +extern int usb_hub_init(void); +extern void usb_hub_cleanup(void); + /* * Prototypes for the device driver probing/loading functions */ @@ -2029,6 +2033,32 @@ struct list_head *usb_bus_get_list(void) } #endif + +/* + * Init + */ +static int __init usb_init(void) +{ + usb_major_init(); + usbdevfs_init(); + usb_hub_init(); + + return 0; +} + +/* + * Cleanup + */ +static void __exit usb_exit(void) +{ + usb_major_cleanup(); + usbdevfs_cleanup(); + usb_hub_cleanup(); +} + +module_init(usb_init); +module_exit(usb_exit); + /* * USB may be built into the kernel or be built as modules. * If the USB core [and maybe a host controller driver] is built diff --git a/drivers/video/mdacon.c b/drivers/video/mdacon.c index 88896352854f..d988d9e39984 100644 --- a/drivers/video/mdacon.c +++ b/drivers/video/mdacon.c @@ -129,7 +129,6 @@ static void write_mda_w(unsigned int val, unsigned char reg) outb_p(reg+1, mda_index_port); outb_p(val & 0xff, mda_value_port); spin_unlock_irqrestore(&mda_lock, flags); - restore_flags(flags); } static int test_mda_b(unsigned char val, unsigned char reg) diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 10169c62ce43..3c981f75c5f9 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -434,7 +434,7 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) inode->i_nlink--; /* is this nlink == 0? */ mark_inode_dirty(inode); iput (inode); - return -EIO; + return err; } de = (struct ext2_dir_entry_2 *) dir_block->b_data; de->inode = cpu_to_le32(inode->i_ino); diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 07c393d2e4e4..a655468204c8 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -38,4 +38,6 @@ static inline void ptep_mkdirty(pte_t *ptep) set_pte(ptep, pte_mkdirty(old_pte)); } +#define pte_same(A,B) (pte_val(A) == pte_val(B)) + #endif /* _ASM_GENERIC_PGTABLE_H */ diff --git a/include/asm-i386/bugs.h b/include/asm-i386/bugs.h index c60710120160..4b95d09cd68d 100644 --- a/include/asm-i386/bugs.h +++ b/include/asm-i386/bugs.h @@ -94,7 +94,6 @@ static void __init check_fpu(void) printk(KERN_INFO "Enabling unmasked SIMD FPU exception support... "); set_in_cr4(X86_CR4_OSXMMEXCPT); printk("done.\n"); - load_mxcsr(0x1f80); } #endif @@ -166,6 +165,7 @@ __asm__(".align 4\nvide: ret"); static void __init check_amd_k6(void) { if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && + boot_cpu_data.x86 == 5 && boot_cpu_data.x86_model == 6 && boot_cpu_data.x86_mask == 1) { diff --git a/include/asm-i386/elf.h b/include/asm-i386/elf.h index de59b793a5c6..55ffacff16a2 100644 --- a/include/asm-i386/elf.h +++ b/include/asm-i386/elf.h @@ -93,7 +93,7 @@ typedef struct user_fxsr_struct elf_fpxregset_t; For the moment, we have only optimizations for the Intel generations, but that could change... */ -#define ELF_PLATFORM ("i386\0i486\0i586\0i686"+((boot_cpu_data.x86-3)*5)) +#define ELF_PLATFORM ("i386\0i486\0i586\0i686"+(((boot_cpu_data.x86>6?6:boot_cpu_data.x86)-3)*5)) #ifdef __KERNEL__ #define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX) diff --git a/include/asm-i386/i387.h b/include/asm-i386/i387.h index e41ca1da0ed6..f8ebabe1b7d7 100644 --- a/include/asm-i386/i387.h +++ b/include/asm-i386/i387.h @@ -16,6 +16,7 @@ #include #include +extern void init_fpu(void); /* * FPU lazy state save handling... */ diff --git a/include/asm-i386/user.h b/include/asm-i386/user.h index dcd555159c7d..ddc06ea014a6 100644 --- a/include/asm-i386/user.h +++ b/include/asm-i386/user.h @@ -48,8 +48,8 @@ struct user_i387_struct { long twd; long fip; long fcs; - long fdp; - long fds; + long foo; + long fos; long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */ }; diff --git a/include/asm-sparc/pgtable.h b/include/asm-sparc/pgtable.h index 202ebe6d9f31..e3fcd317a229 100644 --- a/include/asm-sparc/pgtable.h +++ b/include/asm-sparc/pgtable.h @@ -1,4 +1,4 @@ -/* $Id: pgtable.h,v 1.103 2000/10/16 14:32:49 anton Exp $ */ +/* $Id: pgtable.h,v 1.104 2000/10/19 00:50:16 davem Exp $ */ #ifndef _SPARC_PGTABLE_H #define _SPARC_PGTABLE_H diff --git a/include/asm-sparc64/envctrl.h b/include/asm-sparc64/envctrl.h index 1b5e4da9c4a0..e16de8fb2bac 100644 --- a/include/asm-sparc64/envctrl.h +++ b/include/asm-sparc64/envctrl.h @@ -1,17 +1,101 @@ -/* $Id: envctrl.h,v 1.1 1998/05/16 17:26:07 ecd Exp $ +/* $Id: envctrl.h,v 1.2 2000/10/17 16:20:36 davem Exp $ * * envctrl.h: Definitions for access to the i2c environment * monitoring on Ultrasparc systems. * * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 2000 Vinh Truong (vinh.truong@eng.sun.com) + * VT - Add all ioctl commands and environment status definitions + * VT - Add application note */ - #ifndef _SPARC64_ENVCTRL_H #define _SPARC64_ENVCTRL_H 1 #include -#define I2CIOCSADR _IOW('p', 0x40, int) -#define I2CIOCGADR _IOR('p', 0x41, int) +/* Application note: + * + * The driver supports 4 operations: open(), close(), ioctl(), read() + * The device name is /dev/envctrl. + * Below is sample usage: + * + * fd = open("/dev/envtrl", O_RDONLY); + * if (ioctl(fd, ENVCTRL_READ_SHUTDOWN_TEMPERATURE, 0) < 0) + * printf("error\n"); + * ret = read(fd, buf, 10); + * close(fd); + * + * Notice in the case of cpu voltage and temperature, the default is + * cpu0. If we need to know the info of cpu1, cpu2, cpu3, we need to + * pass in cpu number in ioctl() last parameter. For example, to + * get the voltage of cpu2: + * + * ioctlbuf[0] = 2; + * if (ioctl(fd, ENVCTRL_READ_CPU_VOLTAGE, ioctlbuf) < 0) + * printf("error\n"); + * ret = read(fd, buf, 10); + * + * All the return values are in ascii. So check read return value + * and do appropriate conversions in your application. + */ + +/* IOCTL commands */ + +/* Note: these commands reflect possible monitor features. + * Some boards choose to support some of the features only. + */ +#define ENVCTRL_RD_CPU_TEMPERATURE _IOR('p', 0x40, int) +#define ENVCTRL_RD_CPU_VOLTAGE _IOR('p', 0x41, int) +#define ENVCTRL_RD_FAN_STATUS _IOR('p', 0x42, int) +#define ENVCTRL_RD_WARNING_TEMPERATURE _IOR('p', 0x43, int) +#define ENVCTRL_RD_SHUTDOWN_TEMPERATURE _IOR('p', 0x44, int) +#define ENVCTRL_RD_VOLTAGE_STATUS _IOR('p', 0x45, int) +#define ENVCTRL_RD_SCSI_TEMPERATURE _IOR('p', 0x46, int) +#define ENVCTRL_RD_ETHERNET_TEMPERATURE _IOR('p', 0x47, int) +#define ENVCTRL_RD_MTHRBD_TEMPERATURE _IOR('p', 0x48, int) + +/* Read return values for a voltage status request. */ +#define ENVCTRL_VOLTAGE_POWERSUPPLY_GOOD 0x01 +#define ENVCTRL_VOLTAGE_BAD 0x02 +#define ENVCTRL_POWERSUPPLY_BAD 0x03 +#define ENVCTRL_VOLTAGE_POWERSUPPLY_BAD 0x04 + +/* Read return values for a fan status request. + * A failure match means either the fan fails or + * the fan is not connected. Some boards have optional + * connectors to connect extra fans. + * + * There are maximum 8 monitor fans. Some are cpu fans + * some are system fans. The mask below only indicates + * fan by order number. + * Below is a sample application: + * + * if (ioctl(fd, ENVCTRL_READ_FAN_STATUS, 0) < 0) { + * printf("ioctl fan failed\n"); + * } + * if (read(fd, rslt, 1) <= 0) { + * printf("error or fan not monitored\n"); + * } else { + * if (rslt[0] == ENVCTRL_ALL_FANS_GOOD) { + * printf("all fans good\n"); + * } else if (rslt[0] == ENVCTRL_ALL_FANS_BAD) { + * printf("all fans bad\n"); + * } else { + * if (rslt[0] & ENVCTRL_FAN0_FAILURE_MASK) { + * printf("fan 0 failed or not connected\n"); + * } + * ...... + */ + +#define ENVCTRL_ALL_FANS_GOOD 0x00 +#define ENVCTRL_FAN0_FAILURE_MASK 0x01 +#define ENVCTRL_FAN1_FAILURE_MASK 0x02 +#define ENVCTRL_FAN2_FAILURE_MASK 0x04 +#define ENVCTRL_FAN3_FAILURE_MASK 0x08 +#define ENVCTRL_FAN4_FAILURE_MASK 0x10 +#define ENVCTRL_FAN5_FAILURE_MASK 0x20 +#define ENVCTRL_FAN6_FAILURE_MASK 0x40 +#define ENVCTRL_FAN7_FAILURE_MASK 0x80 +#define ENVCTRL_ALL_FANS_BAD 0xFF #endif /* !(_SPARC64_ENVCTRL_H) */ diff --git a/include/asm-sparc64/pgtable.h b/include/asm-sparc64/pgtable.h index 6eb90b44c70a..62c4c4ef4a2e 100644 --- a/include/asm-sparc64/pgtable.h +++ b/include/asm-sparc64/pgtable.h @@ -1,4 +1,4 @@ -/* $Id: pgtable.h,v 1.131 2000/08/11 03:00:14 davem Exp $ +/* $Id: pgtable.h,v 1.132 2000/10/19 00:50:16 davem Exp $ * pgtable.h: SpitFire page table operations. * * Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu) diff --git a/include/linux/mm.h b/include/linux/mm.h index f67d6be5261b..59355eb8aa49 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -95,6 +95,7 @@ struct vm_area_struct { #define VM_DONTCOPY 0x00020000 /* Do not copy this vma on fork */ #define VM_DONTEXPAND 0x00040000 /* Cannot expand with mremap() */ +#define VM_RESERVED 0x00080000 /* Don't unmap it from swap_out */ #define VM_STACK_FLAGS 0x00000177 diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index d6d8d841de42..07bc8b261fad 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -274,6 +274,7 @@ enum NET_TCP_RMEM=85, NET_TCP_APP_WIN=86, NET_TCP_ADV_WIN_SCALE=87, + NET_IPV4_NONLOCAL_BIND=88, }; enum { diff --git a/mm/filemap.c b/mm/filemap.c index 2a1248effe7f..d2b4e5a00093 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -135,6 +135,12 @@ void invalidate_inode_pages(struct inode * inode) if (TryLockPage(page)) continue; + /* Neither can we invalidate something in use.. */ + if (page_count(page) != 1) { + UnlockPage(page); + continue; + } + __lru_cache_del(page); __remove_inode_page(page); UnlockPage(page); @@ -156,6 +162,7 @@ static inline void truncate_partial_page(struct page *page, unsigned partial) static inline void truncate_complete_page(struct page *page) { + /* Leave it on the LRU if it gets converted into anonymous buffers */ if (!page->buffers || block_flushpage(page, 0)) lru_cache_del(page); @@ -167,6 +174,7 @@ static inline void truncate_complete_page(struct page *page) * all sorts of fun problems ... */ ClearPageDirty(page); + ClearPageUptodate(page); remove_inode_page(page); page_cache_release(page); } diff --git a/mm/vmscan.c b/mm/vmscan.c index ccbe6de9ec54..2edeb618e7f4 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -87,6 +87,13 @@ static int try_to_swap_out(struct mm_struct * mm, struct vm_area_struct* vma, un if (TryLockPage(page)) goto out_failed; + /* From this point on, the odds are that we're going to + * nuke this pte, so read and clear the pte. This hook + * is needed on CPUs which update the accessed and dirty + * bits in hardware. + */ + pte = ptep_get_and_clear(page_table); + /* * Is the page already in the swap cache? If so, then * we can just drop our reference to it without doing @@ -98,10 +105,6 @@ static int try_to_swap_out(struct mm_struct * mm, struct vm_area_struct* vma, un if (PageSwapCache(page)) { entry.val = page->index; swap_duplicate(entry); - if (pte_dirty(pte)) - BUG(); - if (pte_write(pte)) - BUG(); set_pte(page_table, swp_entry_to_pte(entry)); drop_pte: UnlockPage(page); @@ -112,13 +115,6 @@ drop_pte: goto out_failed; } - /* From this point on, the odds are that we're going to - * nuke this pte, so read and clear the pte. This hook - * is needed on CPUs which update the accessed and dirty - * bits in hardware. - */ - pte = ptep_get_and_clear(page_table); - /* * Is it a clean page? Then it must be recoverable * by just paging it in again, and we can just drop @@ -318,7 +314,7 @@ static int swap_out_vma(struct mm_struct * mm, struct vm_area_struct * vma, unsi unsigned long end; /* Don't swap out areas which are locked down */ - if (vma->vm_flags & VM_LOCKED) + if (vma->vm_flags & (VM_LOCKED|VM_RESERVED)) return 0; pgdir = pgd_offset(mm, address); diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 544800d7e990..db4f5d626aa3 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -5,7 +5,7 @@ * * PF_INET protocol family socket handler. * - * Version: $Id: af_inet.c,v 1.116 2000/10/15 01:34:45 davem Exp $ + * Version: $Id: af_inet.c,v 1.118 2000/10/19 15:51:02 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -447,6 +447,9 @@ int inet_release(struct socket *sock) return(0); } +/* It is off by default, see below. */ +int sysctl_ip_nonlocal_bind; + static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sockaddr_in *addr=(struct sockaddr_in *)uaddr; @@ -464,6 +467,20 @@ static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) chk_addr_ret = inet_addr_type(addr->sin_addr.s_addr); + /* Not specified by any standard per-se, however it breaks too + * many applications when removed. It is unfortunate since + * allowing applications to make a non-local bind solves + * several problems with systems using dynamic addressing. + * (ie. your servers still start up even if your ISDN link + * is temporarily down) + */ + if (sysctl_ip_nonlocal_bind == 0 && + addr->sin_addr.s_addr != INADDR_ANY && + chk_addr_ret != RTN_LOCAL && + chk_addr_ret != RTN_MULTICAST && + chk_addr_ret != RTN_BROADCAST) + return -EADDRNOTAVAIL; + snum = ntohs(addr->sin_port); if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) return -EACCES; @@ -753,14 +770,15 @@ int inet_shutdown(struct socket *sock, int how) } switch (sk->state) { - default: + case TCP_CLOSE: + err = -ENOTCONN; + /* Hack to wake up other listeners, who can poll for + POLLHUP, even on eg. unconnected UDP sockets -- RR */ + default: sk->shutdown |= how; if (sk->prot->shutdown) sk->prot->shutdown(sk, how); break; - case TCP_CLOSE: - err = -ENOTCONN; - break; /* Remaining two branches are temporary solution for missing * close() in multithreaded environment. It is _not_ a good idea, diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 81f20361a566..9387778ee7da 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -5,7 +5,7 @@ * * RAW - implementation of IP "raw" sockets. * - * Version: $Id: raw.c,v 1.53 2000/08/09 11:59:04 davem Exp $ + * Version: $Id: raw.c,v 1.54 2000/10/18 18:04:23 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -653,7 +653,7 @@ int raw_get_info(char *buffer, char **start, off_t offset, int length) if (sk->family != PF_INET) continue; pos += 128; - if (pos < offset) + if (pos <= offset) continue; get_raw_sock(sk, tmpbuf, i); len += sprintf(buffer+len, "%-127s\n", tmpbuf); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index d9f05c671f91..68d536e108bf 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -1,7 +1,7 @@ /* * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem. * - * $Id: sysctl_net_ipv4.c,v 1.46 2000/09/16 09:38:30 davem Exp $ + * $Id: sysctl_net_ipv4.c,v 1.47 2000/10/19 15:51:02 davem Exp $ * * Begun April 1, 1996, Mike Shaver. * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS] @@ -15,6 +15,9 @@ #include #include +/* From af_inet.c */ +extern int sysctl_ip_nonlocal_bind; + /* From icmp.c */ extern int sysctl_icmp_echo_ignore_all; extern int sysctl_icmp_echo_ignore_broadcasts; @@ -110,6 +113,9 @@ ctl_table ipv4_table[] = { {NET_IPV4_NO_PMTU_DISC, "ip_no_pmtu_disc", &ipv4_config.no_pmtu_disc, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_NONLOCAL_BIND, "ip_nonlocal_bind", + &sysctl_ip_nonlocal_bind, sizeof(int), 0644, NULL, + &proc_dointvec}, {NET_IPV4_TCP_SYN_RETRIES, "tcp_syn_retries", &sysctl_tcp_syn_retries, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_TCP_SYNACK_RETRIES, "tcp_synack_retries", diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 082848dda1fa..1a0f278b4239 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.217 2000/10/15 13:15:19 davem Exp $ + * Version: $Id: tcp_ipv4.c,v 1.218 2000/10/18 18:04:22 davem Exp $ * * IPv4 specific functions * @@ -2097,7 +2097,7 @@ skip_listen: continue; pos += TMPSZ; - if (pos < offset) + if (pos <= offset) continue; get_openreq(sk, req, tmpbuf, num, uid); len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf); @@ -2129,7 +2129,7 @@ skip_listen: if (!TCP_INET_FAMILY(sk->family)) continue; pos += TMPSZ; - if (pos < offset) + if (pos <= offset) continue; get_tcp_sock(sk, tmpbuf, num); len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf); @@ -2144,7 +2144,7 @@ skip_listen: if (!TCP_INET_FAMILY(tw->family)) continue; pos += TMPSZ; - if (pos < offset) + if (pos <= offset) continue; get_timewait_sock(tw, tmpbuf, num); len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 970fbfdde5ab..e098b9ff25cc 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -5,7 +5,7 @@ * * The User Datagram Protocol (UDP). * - * Version: $Id: udp.c,v 1.89 2000/10/03 07:29:01 anton Exp $ + * Version: $Id: udp.c,v 1.90 2000/10/18 18:04:22 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -1028,7 +1028,7 @@ int udp_get_info(char *buffer, char **start, off_t offset, int length) if (sk->family != PF_INET) continue; pos += 128; - if (pos < offset) + if (pos <= offset) continue; get_udp_sock(sk, tmpbuf, i); len += sprintf(buffer+len, "%-127s\n", tmpbuf); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 2528e7c7ba62..c243a6ec915e 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -7,7 +7,7 @@ * * Adapted from linux/net/ipv4/af_inet.c * - * $Id: af_inet6.c,v 1.59 2000/10/15 01:34:45 davem Exp $ + * $Id: af_inet6.c,v 1.60 2000/10/19 01:05:34 davem Exp $ * * Fixes: * Hideaki YOSHIFUJI : sin6_scope_id support diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 0b7e0025236e..43ef2e87d5da 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -7,7 +7,7 @@ * * Adapted from linux/net/ipv4/raw.c * - * $Id: raw.c,v 1.40 2000/08/09 11:59:04 davem Exp $ + * $Id: raw.c,v 1.41 2000/10/18 18:04:23 davem Exp $ * * Fixes: * Hideaki YOSHIFUJI : sin6_scope_id support @@ -793,7 +793,7 @@ int raw6_get_info(char *buffer, char **start, off_t offset, int length) if (sk->family != PF_INET6) continue; pos += LINE_LEN+1; - if (pos < offset) + if (pos <= offset) continue; get_raw6_sock(sk, tmpbuf, i); len += sprintf(buffer+len, LINE_FMT, tmpbuf); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 952e2476516a..b830884e88b6 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: tcp_ipv6.c,v 1.125 2000/08/09 11:59:04 davem Exp $ + * $Id: tcp_ipv6.c,v 1.126 2000/10/18 18:04:23 davem Exp $ * * Based on: * linux/net/ipv4/tcp.c @@ -2009,7 +2009,7 @@ int tcp6_get_info(char *buffer, char **start, off_t offset, int length) if (req->class->family != PF_INET6) continue; pos += LINE_LEN+1; - if (pos < offset) + if (pos <= offset) continue; get_openreq6(sk, req, tmpbuf, num, uid); len += sprintf(buffer+len, LINE_FMT, tmpbuf); @@ -2041,7 +2041,7 @@ int tcp6_get_info(char *buffer, char **start, off_t offset, int length) if (sk->family != PF_INET6) continue; pos += LINE_LEN+1; - if (pos < offset) + if (pos <= offset) continue; get_tcp6_sock(sk, tmpbuf, num); len += sprintf(buffer+len, LINE_FMT, tmpbuf); @@ -2056,7 +2056,7 @@ int tcp6_get_info(char *buffer, char **start, off_t offset, int length) if (tw->family != PF_INET6) continue; pos += LINE_LEN+1; - if (pos < offset) + if (pos <= offset) continue; get_timewait6_sock(tw, tmpbuf, num); len += sprintf(buffer+len, LINE_FMT, tmpbuf); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 4d95aa40be8c..187db4414a11 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -7,7 +7,7 @@ * * Based on linux/ipv4/udp.c * - * $Id: udp.c,v 1.57 2000/09/18 05:59:48 davem Exp $ + * $Id: udp.c,v 1.58 2000/10/18 18:04:23 davem Exp $ * * Fixes: * Hideaki YOSHIFUJI : sin6_scope_id support @@ -956,7 +956,7 @@ int udp6_get_info(char *buffer, char **start, off_t offset, int length) if (sk->family != PF_INET6) continue; pos += LINE_LEN+1; - if (pos < offset) + if (pos <= offset) continue; get_udp6_sock(sk, tmpbuf, i); len += sprintf(buffer+len, LINE_FMT, tmpbuf); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 906bcffbe303..31a340933053 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -5,7 +5,7 @@ * * PACKET - implements raw packet sockets. * - * Version: $Id: af_packet.c,v 1.44 2000/10/15 01:34:47 davem Exp $ + * Version: $Id: af_packet.c,v 1.45 2000/10/19 01:05:35 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 62e8b1b85328..7603a9221e1f 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -8,7 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: af_unix.c,v 1.106 2000/10/15 01:34:48 davem Exp $ + * Version: $Id: af_unix.c,v 1.107 2000/10/19 01:05:36 davem Exp $ * * Fixes: * Linus Torvalds : Assorted bug cures. -- 2.39.5