- 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()
<chapter id="intro">
<title>Introduction</title>
<para>
- 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.
</para>
<para>
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.
</para>
<para>
This driver supports any Linux kernel version after 2.3.50.
<title>Known Bugs And Assumptions</title>
<para>
<variablelist>
- <varlistentry><term>Recording support</term>
- <listitem>
- <para>
- Recording support is currently missing.
- </para>
- </listitem></varlistentry>
-
<varlistentry><term>MMAP support</term>
<listitem>
<para>
</para>
</listitem></varlistentry>
- <varlistentry><term>Broken apps</term>
- <listitem>
- <para>
- 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.
- </para>
- </listitem></varlistentry>
-
</variablelist>
</para>
<title>Random Notes</title>
<para>
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:
</para>
a vendor id of 0x1106, and a device id of 0x3058. Subsystem vendor
and device ids are not examined.
</para>
- <para>
- 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.
- </para>
- <para>
- 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.
- </para>
<para>
GNU indent formatting options: -kr -i8 -pcs
</para>
<chapter id="changelog">
<title>Driver ChangeLog</title>
+<sect1 id="version1112"><title>
+Version 1.1.12
+</title>
+ <itemizedlist spacing=compact>
+ <listitem>
+ <para>
+ mmap bug fixes from Linus.
+ </para>
+ </listitem>
+ </itemizedlist>
+</sect1>
+
+<sect1 id="version1111"><title>
+Version 1.1.11
+</title>
+ <itemizedlist spacing=compact>
+ <listitem>
+ <para>
+ Many more bug fixes. mmap enabled by default, but may still be buggy.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Uses new and spiffy method of mmap'ing the DMA buffer, based
+ on a suggestion from Linus.
+ </para>
+ </listitem>
+ </itemizedlist>
+</sect1>
+
+<sect1 id="version1110"><title>
+Version 1.1.10
+</title>
+ <itemizedlist spacing=compact>
+ <listitem>
+ <para>
+ Many bug fixes. mmap enabled by default, but may still be buggy.
+ </para>
+ </listitem>
+ </itemizedlist>
+</sect1>
+
+<sect1 id="version119"><title>
+Version 1.1.9
+</title>
+ <itemizedlist spacing=compact>
+ <listitem>
+ <para>
+ Redesign and rewrite audio playback implementation. (faster and smaller, hopefully)
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Implement recording and full duplex (DSP_CAP_DUPLEX) support.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Make procfs support optional.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Quick interrupt status check, to lessen overhead in interrupt
+ sharing situations.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Add mmap(2) support. Disabled for now, it is still buggy and experimental.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Surround all syscalls with a semaphore for cheap and easy SMP protection.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Fix bug in channel shutdown (hardware channel reset) code.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Remove unnecessary spinlocks (better performance).
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Eliminate "unknown AFMT" message by using a different method
+ of selecting the best AFMT_xxx sound sample format for use.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Support for realtime hardware pointer position reporting
+ (DSP_CAP_REALTIME, SNDCTL_DSP_GETxPTR ioctls)
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Support for capture/playback triggering
+ (DSP_CAP_TRIGGER, SNDCTL_DSP_SETTRIGGER ioctls)
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ SNDCTL_DSP_SETDUPLEX and SNDCTL_DSP_POST ioctls now handled.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ 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.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Reviewed code to ensure that SMP and multiple audio devices
+ are fully supported.
+ </para>
+ </listitem>
+
+ </itemizedlist>
+</sect1>
+
<sect1 id="version118"><title>
Version 1.1.8
</title>
#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.
*/
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 )
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:
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;
}
/* Pentium IV. */
if (c->x86 == 15) {
get_model_name(c);
- goto name_decoded;
+ return;
}
/* Names for the Pentium II/Celeron processors
#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");
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() */
}
* 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)
{
}
+/* 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;
-/* $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)
# 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
-/* $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)
/* 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 */
-/* $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)
-/* $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)
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 */
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();
}
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;
}
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);
}
/*
};
-spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
/*
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);
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();
}
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 <worm@dkik.dk>
+ 0.52 16-Oct-00 Fixes for 2.3 io memory accesses
+ Fix show-stopper (ints left masked) in depca_interrupt
+ by <peterd@pnd-pc.demon.co.uk>
=========================================================================
*/
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;
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; i<ETH_ALEN - 1; i++) { /* get the ethernet address */
- printk("%2.2x:", dev->dev_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; i<ETH_ALEN - 1; i++) { /* get the ethernet address */
+ printk("%2.2x:", dev->dev_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;
}
\f
nicsr = inb(DEPCA_NICSR);
/* Make sure the shadow RAM is enabled */
- if (adapter != DEPCA) {
+ if (lp->adapter != DEPCA) {
nicsr |= SHE;
outb(nicsr, DEPCA_NICSR);
}
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 */
}
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);
}
/*
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);
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 */
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;
}
{
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++) {
*/
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 */
{
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;i<lp->rxRingMask;i++){
if (i < 3) {
printk("0x%8.8lx ", (long) &lp->rx_ring[i].base);
}
}
printk("...0x%8.8lx\n", (long) &lp->tx_ring[i].base);
- printk("\nDescriptor buffers:\nRX: ");
+ printk("\nDescriptor buffers (Device):\nRX: ");
for (i=0;i<lp->rxRingMask;i++){
if (i < 3) {
printk("0x%8.8x ", readl(&lp->rx_ring[i].base));
}
}
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;i<ETH_ALEN-1;i++){
- printk("%2.2x:",(u_char)readb(&p->phys_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);
{
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);
Version 1.0.1:
- Spinlock fixes
- Bug fixes and better intr performance (Tjeerd)
+ Version 1.0.2:
+ - Now reads correct MAC address from eeprom
*/
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:
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);
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);
}
-/* $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)
-/* $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 <linux/version.h>
#include <linux/config.h>
#include <linux/module.h>
-
-#define __KERNEL_SYSCALLS__
#include <linux/sched.h>
-#include <linux/unistd.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/init.h>
-#include <linux/malloc.h>
#include <linux/miscdevice.h>
-#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
#include <asm/ebus.h>
#include <asm/uaccess.h>
#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
#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;
#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);
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;
}
}
pointer);
}
- release_region ((unsigned long) temp, 16);
+ iounmap (temp);
}
/*
* http://gtf.org/garzik/drivers/via82cxxx/
*
*/
-
-#define VIA_VERSION "1.1.8"
+
+#define VIA_VERSION "1.1.12"
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
+#include <linux/mm.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/soundcard.h>
#include <linux/ac97_codec.h>
#include <linux/smp_lock.h>
+#include <linux/ioport.h>
+#include <linux/wrapper.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <asm/hardirq.h>
+#include <asm/semaphore.h>
#undef VIA_DEBUG /* define to enable debugging output and checks */
#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
}
#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 ": "
#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
#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 */
#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)
#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 {
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;
};
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;
};
static unsigned via_num_cards = 0;
+
/****************************************************************
*
* prototypes
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);
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
/****************************************************************
* 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)
* 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);
}
* 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;
+}
/**
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.
* 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,
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 */
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));
* 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);
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;
/**
* 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.
* 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);
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");
}
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;
}
"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;
}
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 */
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
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,
}
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
/****************************************************************
*
*
*/
-
+
/**
* via_ac97_wait_idle - Wait until AC97 codec is not busy
* @card: Private info for specified board
{
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;
}
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);
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");
u32 data;
struct via_info *card;
int counter;
-
+
DPRINTK ("ENTER\n");
assert (codec != NULL);
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");
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;
}
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;
}
};
-#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;
inl (card->baseaddr + 0x80),
inl (card->baseaddr + 0x84));
spin_unlock_irq (&card->lock);
-
+
}
#endif
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);
/* 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");
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;
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");
* 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;
DPRINTK ("ENTER\n");
assert (card != NULL);
-
+
spin_lock_irqsave (&card->lock, flags);
pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8);
}
+/**
+ * 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",
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);
}
+/**
+ * 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");
* OSS DSP device
*
*/
-
+
static struct file_operations via_dsp_fops = {
owner: THIS_MODULE,
open: via_dsp_open,
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);
}
+#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;
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;
}
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);
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),
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,
}
+/**
+ * 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,
(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;
}
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);
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;
+}
/****************************************************************
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");
rc = -EIO;
goto err_out_none;
}
-
+
card = kmalloc (sizeof (*card), GFP_KERNEL);
if (!card) {
printk (KERN_ERR PFX "out of memory, aborting\n");
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)
rc = -ENODEV;
goto err_out_kfree;
}
-
- /*
+
+ /*
* init AC97 mixer and codec
*/
rc = via_ac97_init (card);
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");
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);
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;
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;
}
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);
#endif
kfree (card);
- pdev->driver_data = NULL;
-
+ pci_set_drvdata (pdev, NULL);
+
pci_set_power_state (pdev, 3); /* ...zzzzzz */
DPRINTK ("EXIT\n");
return 0;
}
-
+
static void __exit cleanup_via82cxxx_audio(void)
{
DPRINTK("ENTER\n");
-
+
pci_unregister_driver (&via_driver);
via_cleanup_proc ();
module_init(init_via82cxxx_audio);
module_exit(cleanup_via82cxxx_audio);
+
+MODULE_AUTHOR("Jeff Garzik <jgarzik@mandrakesoft.com>");
+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 */
# 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.
#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.
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;
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;
}
+++ /dev/null
-/*
- * 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 <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/usb.h>
-
-/*
- * 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);
#include <linux/malloc.h>
#include <linux/interrupt.h> /* for in_interrupt() */
#include <linux/kmod.h>
+#include <linux/init.h>
#ifdef CONFIG_USB_DEBUG
0;
#endif
+extern int usb_hub_init(void);
+extern void usb_hub_cleanup(void);
+
/*
* Prototypes for the device driver probing/loading functions
*/
}
#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
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)
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);
set_pte(ptep, pte_mkdirty(old_pte));
}
+#define pte_same(A,B) (pte_val(A) == pte_val(B))
+
#endif /* _ASM_GENERIC_PGTABLE_H */
printk(KERN_INFO "Enabling unmasked SIMD FPU exception support... ");
set_in_cr4(X86_CR4_OSXMMEXCPT);
printk("done.\n");
- load_mxcsr(0x1f80);
}
#endif
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)
{
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)
#include <asm/sigcontext.h>
#include <asm/user.h>
+extern void init_fpu(void);
/*
* FPU lazy state save handling...
*/
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 */
};
-/* $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
-/* $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 <linux/ioctl.h>
-#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) */
-/* $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)
#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
NET_TCP_RMEM=85,
NET_TCP_APP_WIN=86,
NET_TCP_ADV_WIN_SCALE=87,
+ NET_IPV4_NONLOCAL_BIND=88,
};
enum {
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);
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);
* all sorts of fun problems ...
*/
ClearPageDirty(page);
+ ClearPageUptodate(page);
remove_inode_page(page);
page_cache_release(page);
}
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
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);
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
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);
*
* 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, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
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;
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;
}
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,
*
* 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, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
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);
/*
* 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]
#include <net/route.h>
#include <net/tcp.h>
+/* 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;
{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",
*
* 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
*
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);
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);
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);
*
* 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, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
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);
*
* 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
*
* 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
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);
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $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
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);
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);
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);
*
* 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
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);
*
* 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, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* 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.