Commit 4f412172 authored by Linus Torvalds's avatar Linus Torvalds

Import 2.3.30pre6

parent 0c38a3e2
......@@ -156,12 +156,9 @@ S: Notre Dame, Indiana
S: USA
N: James Banks
E: james.banks@caldera.com
E: james@sovereign.org
D: TLAN network driver
S: Caldera, Inc.
S: 633 South 550 East
S: Provo, Utah 84606
S: USA
D: Logitech Busmouse driver
N: Krzysztof G. Baranowski
E: kgb@manjak.knm.org.pl
......
......@@ -4,7 +4,7 @@
Richard Gooch <rgooch@atnf.csiro.au>
23-APR-1999
5-JUL-1999
Conventions used in this document <section>
......@@ -41,10 +41,11 @@ mounted.
Opening a File <subsection>
--------------
The VFS implements the open(2) system call. The pathname argument is
used by the VFS to search through the directory entry cache (dentry
cache or "dcache"). This provides a very fast lookup mechanism to
translate a pathname (filename) into a specific dentry.
The VFS implements the open(2), stat(2), chmod(2) and similar system
calls. The pathname argument is used by the VFS to search through the
directory entry cache (dentry cache or "dcache"). This provides a very
fast lookup mechanism to translate a pathname (filename) into a
specific dentry.
An individual dentry usually has a pointer to an inode. Inodes are the
things that live on disc drives, and can be regular files (you know:
......@@ -53,7 +54,8 @@ beasts. Dentries live in RAM and are never saved to disc: they exist
only for performance. Inodes live on disc and are copied into memory
when required. Later any changes are written back to disc. The inode
that lives in RAM is a VFS inode, and it is this which the dentry
points to.
points to. A single inode can be pointed to by multiple dentries
(think about hardlinks).
The dcache is meant to be a view into your entire filespace. Unlike
Linus, most of us losers can't fit enough dentries into RAM to cover
......@@ -76,10 +78,10 @@ back to userspace.
Opening a file requires another operation: allocation of a file
structure (this is the kernel-side implementation of file
descriptors). The freshly allocated file structure is initialised with
a pointer to the dentry and a set of file operation member
functions. These are taken from the inode data. The open() file method
is then called so the specific filesystem implementation can do it's
work. You can see that this is another switch performed by the VFS.
a pointer to the dentry and a set of file operation member functions.
These are taken from the inode data. The open() file method is then
called so the specific filesystem implementation can do it's work. You
can see that this is another switch performed by the VFS.
The file structure is placed into the file descriptor table for the
process.
......@@ -92,6 +94,14 @@ function to do whatever is required.
For as long as the file is open, it keeps the dentry "open" (in use),
which in turn means that the VFS inode is still in use.
All VFS system calls (i.e. open(2), stat(2), read(2), write(2),
chmod(2) and so on) are called from a process context. You should
assume that these calls are made without any kernel locks being
held. This means that the processes may be executing the same piece of
filesystem or driver code at the same time, on different
processors. You should ensure that access to shared resources is
protected by appropriate locks.
Registering and Mounting a Filesystem <subsection>
-------------------------------------
......@@ -249,8 +259,11 @@ struct inode_operations {
int (*revalidate) (struct dentry *);
};
Again, all methods are called without any locks being held, unless
otherwise noted.
default_file_ops: this is a pointer to a "struct file_operations"
which describes how to manipulate open files
which describes how to open and then manipulate open files
create: called by the open(2) and creat(2) system calls. Only
required if you want to support regular files. The dentry you
......@@ -270,7 +283,7 @@ struct inode_operations {
If you wish to overload the dentry methods then you should
initialise the "d_dop" field in the dentry; this is a pointer
to a struct "dentry_operations".
This method is called with the directory semaphore held
This method is called with the directory inode semaphore held
link: called by the link(2) system call. Only required if you want
to support hard links. You will probably need to call
......@@ -327,17 +340,20 @@ struct file_operations {
int (*lock) (struct file *, int, struct file_lock *);
};
Again, all methods are called without any locks being held, unless
otherwise noted.
llseek: called when the VFS needs to move the file position index
read: called by the read(2) system call
read: called by read(2) and related system calls
write: called by the write(2) system call
write: called by write(2) and related system calls
readdir: called when the VFS needs to read the directory contents
poll: called by the VFS when a process wants to check if there is
activity on this file and (optionally) go to sleep until there
is activity
is activity. Called by the select(2) and poll(2) system calls
ioctl: called by the ioctl(2) system call
......@@ -380,7 +396,9 @@ struct dentry_operations <section>
This describes how a filesystem can overload the standard dentry
operations. Dentries and the dcache are the domain of the VFS and the
individual filesystem implementations. Device drivers have no business
here. As of kernel 2.1.99, the following members are defined:
here. These methods may be set to NULL, as they are either optional or
the VFS uses a default. As of kernel 2.1.99, the following members are
defined:
struct dentry_operations {
int (*d_revalidate)(struct dentry *);
......@@ -391,7 +409,10 @@ struct dentry_operations {
void (*d_iput)(struct dentry *, struct inode *);
};
d_revalidate: called when the VFS needs to revalidate a dentry
d_revalidate: called when the VFS needs to revalidate a dentry. This
is called whenever a name lookup finds a dentry in the
dcache. Most filesystems leave this as NULL, because all their
dentries in the dcache are valid
d_hash: called when the VFS adds a dentry to the hash table
......@@ -401,7 +422,7 @@ struct dentry_operations {
deleted. This means no-one is using the dentry, however it is
still valid and in the dcache
d_release: called when a dentry is deallocated
d_release: called when a dentry is really deallocated
d_iput: called when a dentry looses its inode (just prior to its
being deallocated). The default when this is NULL is that the
......
TLAN driver for Linux, version 1.0
README
Well, I'm back. The TLAN driver seems pretty stable, so I'm
declaring this cycle of development finished, and calling the
driver 1.0. I will, of course continue to work on improving
the driver, and work towards a 2.0 release.
I haven't had any time to do anything for a long time, and this isn't
likely to change. So there's a driver here for anyone looking to
carry forward a project :)
For those who are looking for help, I can't. I haven't looked at
a kernel since the early 2.0 series, so I won't know what's going on.
Your best chance at help would be joining the TLAN mailing list and
posting your question there.
You can join by sending "subscribe tlan" in the body of an email to
majordomo@vuser.vu.union.edu.
Thanks to those who have (and who will ;) put work in to keep the TLAN
driver working as the kernel moves on.
James
james@sovereign.org
TLAN driver for Linux, version 1.0
README
I. Supported Devices.
......
......@@ -422,12 +422,6 @@ M: langa2@kph.uni-mainz.de
W: http://www.uni-mainz.de/~langm000/linux.html
S: Maintained
IBM ServeRAID RAID DRIVER
P: Keith Mitchell
M: ipslinux@us.ibm.com
W: http://www.developer.ibm.com/welcome/netfinity/serveraid_beta.html
S: Supported
IBM ServeRAID RAID DRIVER
P: Keith Mitchell
M: ipslinux@us.ibm.com
......@@ -889,10 +883,8 @@ M: kgb@manjak.knm.org.pl
S: Maintained
TLAN NETWORK DRIVER
P: James Banks
M: james@sovereign.org
L: tlan@vuser.vu.union.edu
S: Maintained
S: Orphan
TOKEN-RING NETWORK DRIVER
P: Paul Norton
......
......@@ -24,7 +24,6 @@
* 2 USB ports
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/mm.h>
......
......@@ -76,7 +76,7 @@ static pmd_t * get_bad_pmd_table(void)
pmd_t v;
int i;
pmd_val(v) = _PAGE_TABLE + __pa(empty_bad_pte_table);
set_pmd(&v, __pmd(_PAGE_TABLE + __pa(empty_bad_pte_table)));
for (i = 0; i < PAGE_SIZE/sizeof(pmd_t); i++)
empty_bad_pmd_table[i] = v;
......@@ -103,13 +103,13 @@ static pte_t * get_bad_pte_table(void)
void __handle_bad_pmd(pmd_t *pmd)
{
pmd_ERROR(*pmd);
pmd_val(*pmd) = _PAGE_TABLE + __pa(get_bad_pte_table());
set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(get_bad_pte_table())));
}
void __handle_bad_pmd_kernel(pmd_t *pmd)
{
pmd_ERROR(*pmd);
pmd_val(*pmd) = _KERNPG_TABLE + __pa(get_bad_pte_table());
set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(get_bad_pte_table())));
}
pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset)
......@@ -120,10 +120,10 @@ pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset)
if (pmd_none(*pmd)) {
if (pte) {
clear_page(pte);
pmd_val(*pmd) = _KERNPG_TABLE + __pa(pte);
set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte)));
return pte + offset;
}
pmd_val(*pmd) = _KERNPG_TABLE + __pa(get_bad_pte_table());
set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(get_bad_pte_table())));
return NULL;
}
free_page((unsigned long)pte);
......@@ -142,10 +142,10 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
if (pmd_none(*pmd)) {
if (pte) {
clear_page((void *)pte);
pmd_val(*pmd) = _PAGE_TABLE + __pa(pte);
set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte)));
return (pte_t *)pte + offset;
}
pmd_val(*pmd) = _PAGE_TABLE + __pa(get_bad_pte_table());
set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(get_bad_pte_table())));
return NULL;
}
free_page(pte);
......@@ -268,7 +268,7 @@ void set_fixmap (enum fixed_addresses idx, unsigned long phys)
printk("Invalid set_fixmap\n");
return;
}
set_pte_phys (address,phys);
set_pte_phys(address,phys);
}
static void __init fixrange_init (unsigned long start, unsigned long end, pgd_t *pgd_base)
......@@ -286,7 +286,7 @@ static void __init fixrange_init (unsigned long start, unsigned long end, pgd_t
#if CONFIG_X86_PAE
if (pgd_none(*pgd)) {
pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
pgd_val(*pgd) = __pa(pmd) + 0x1;
set_pgd(pgd, __pgd(__pa(pmd) + 0x1));
if (pmd != pmd_offset(pgd, start))
BUG();
}
......@@ -297,7 +297,7 @@ static void __init fixrange_init (unsigned long start, unsigned long end, pgd_t
for (; (j < PTRS_PER_PMD) && start; pmd++, j++) {
if (pmd_none(*pmd)) {
pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
pmd_val(*pmd) = _KERNPG_TABLE + __pa(pte);
set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte)));
if (pte != pte_offset(pmd, 0))
BUG();
}
......@@ -326,7 +326,7 @@ static void __init pagetable_init(void)
vaddr = i*PGDIR_SIZE;
#if CONFIG_X86_PAE
pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
pgd_val(*pgd) = __pa(pmd) + 0x1;
set_pgd(pgd, __pgd(__pa(pmd) + 0x1));
#else
pmd = (pmd_t *)pgd;
#endif
......@@ -345,12 +345,12 @@ static void __init pagetable_init(void)
set_in_cr4(X86_CR4_PGE);
__pe += _PAGE_GLOBAL;
}
pmd_val(*pmd) = __pe;
set_pmd(pmd, __pmd(__pe));
continue;
}
pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
pmd_val(*pmd) = _KERNPG_TABLE + __pa(pte);
set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte)));
if (pte != pte_offset(pmd, 0))
BUG();
......@@ -412,7 +412,7 @@ void __init zap_low_mappings (void)
#if CONFIG_X86_PAE
pgd_clear(swapper_pg_dir+i);
#else
pgd_val(swapper_pg_dir[i]) = 0;
set_pgd(swapper_pg_dir+i, __pgd(0));
#endif
flush_tlb_all();
}
......
......@@ -13,6 +13,7 @@
* Gadi Oxman <gadio@netvision.net.il>
*/
#define __NO_VERSION__
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
......
......@@ -53,7 +53,7 @@ static struct pc110pad_params current_params;
static wait_queue_head_t queue;
static struct fasync_struct *asyncptr;
static int active=0; /* number of concurrent open()s */
static struct semaphore read_lock;
static struct semaphore reader_lock;
/*
* Utility to reset a timer to go off some time in the future.
......@@ -561,7 +561,7 @@ static ssize_t read_pad(struct file * file, char * buffer, size_t count, loff_t
{
int r;
down(&read_lock);
down(&reader_lock);
for(r=0; r<count; r++)
{
if(!read_byte_count)
......@@ -573,7 +573,7 @@ static ssize_t read_pad(struct file * file, char * buffer, size_t count, loff_t
}
read_byte_count = (read_byte_count+1)%3;
}
up(&read_lock);
up(&reader_lock);
return r;
}
......@@ -691,7 +691,7 @@ static void pc110pad_unload(void)
int init_module(void)
{
init_MUTEX(&read_lock);
init_MUTEX(&reader_lock);
return pc110pad_init();
}
......
......@@ -2,11 +2,11 @@
** hp100.c
** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters
**
** $Id: hp100.c,v 1.58 1999/11/30 17:20:20 perex Exp perex $
** $Id: hp100.c,v 1.57 1998/04/10 16:27:23 perex Exp perex $
**
** Based on the HP100 driver written by Jaroslav Kysela <perex@jcu.cz>
** Extended for new busmaster capable chipsets by
** Siegfried "Frieder" Loeffler (dg1sek) <loeffler@cdi.fr>
** Siegfried "Frieder" Loeffler (dg1sek) <floeff@mathematik.uni-stuttgart.de>
**
** Maintained by: Jaroslav Kysela <perex@jcu.cz>
**
......@@ -45,8 +45,6 @@
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
** 1.57 -> 1.58
** - 'no connection found' message is time limited now
**
** 1.56 -> 1.57
** - updates for new PCI interface for 2.1 kernels
......@@ -1670,20 +1668,7 @@ static int hp100_start_xmit_bm( struct sk_buff *skb, struct net_device *dev )
hp100_stop_interface( dev );
if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 )
{
/* Added Mon Nov 1 06:13:19 1999 by Brian Moore
* (mooreb@iname.com) to prevent too much kernel IO
* in the case of not having a network connection
*/
{
int thistime = jiffies;
static int delaytime = 10*HZ;
static int lasttime = thistime - delaytime;
// We don't worry about rollover
if(thistime >= (lasttime + delaytime)) {
printk( "hp100: %s: no connection found - check wire\n", dev->name );
lasttime = thistime;
}
}
printk( "hp100: %s: no connection found - check wire\n", dev->name );
hp100_start_interface( dev ); /* 10Mb/s RX pkts maybe handled */
return -EIO;
}
......
......@@ -1640,7 +1640,7 @@ aic7xxx_setup(char *s)
}
}
__setup("aic7xxx=", aix7xxx_setup);
__setup("aic7xxx=", aic7xxx_setup);
/*+F*************************************************************************
* Function:
......
......@@ -28,6 +28,8 @@ if [ "$CONFIG_VISWS" = "y" ]; then
dep_tristate ' SGI Visual Workstation Sound' CONFIG_SOUND_VWSND $CONFIG_SOUND
fi
dep_tristate ' Trident 4DWave-DX/NX' CONFIG_SOUND_TRIDENT $CONFIG_SOUND
dep_tristate ' Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_MSNDCLAS $CONFIG_SOUND
if [ "$CONFIG_SOUND_MSNDCLAS" = "y" -o "$CONFIG_SOUND_MSNDCLAS" = "m" ]; then
if [ "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then
......
......@@ -84,6 +84,7 @@ obj-$(CONFIG_SOUND_ES1370) += es1370.o
obj-$(CONFIG_SOUND_ES1371) += es1371.o
obj-$(CONFIG_SOUND_ESSSOLO1) += esssolo1.o
obj-$(CONFIG_SOUND_MAESTRO) += maestro.o
obj-$(CONFIG_SOUND_TRIDENT) += trident.o
# Declare multi-part drivers.
......
/*****************************************************************************
*
* Trident 4D-Wave OSS driver for Linux 2.2.x
*
* Driver: Alan Cox <alan@redhat.com>
*
* Built from:
* Low level code: <audio@tridentmicro.com> from ALSA
* Framework: Thomas Sailer <sailer@ife.ee.ethz.ch>
* Extended by: Zach Brown <zab@redhat.com>
*
* Hacked up by:
* Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/*****************************************************************************/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/sound.h>
#include <linux/malloc.h>
#include <linux/soundcard.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
#include <asm/hardirq.h>
#ifdef CONFIG_APM
#include <linux/apm_bios.h>
#endif
#include "trident.h"
#include "ac97.h"
/* --------------------------------------------------------------------- */
#define M_DEBUG 1
#ifdef M_DEBUG
static int debug=0;
#define M_printk(args...) {if (debug) printk(args);}
#else
#define M_printk(x)
#endif
/* --------------------------------------------------------------------- */
#define DRIVER_VERSION "0.01"
#define TRIDENT_FMT_STEREO 0x01
#define TRIDENT_FMT_16BIT 0x02
#define TRIDENT_FMT_MASK 0x03
#define TRIDENT_DAC_SHIFT 0
#define TRIDENT_ADC_SHIFT 4
#define TRIDENT_ENABLE_PE 1
#define TRIDENT_ENABLE_RE 2
#define TRIDENT_CARD_MAGIC 0x5072696E
#define TRIDENT_STATE_MAGIC 0x63657373
#define DAC_RUNNING 1
#define ADC_RUNNING 2
#define NR_DSPS 8
#define SND_DEV_DSP16 5
static const unsigned sample_size[] = { 1, 2, 2, 4 };
static const unsigned sample_shift[] = { 0, 1, 1, 2 };
enum card_types_t {
TYPE_4DWAVE_DX,
TYPE_4DWAVE_NX
};
static const char *card_names[]={
[TYPE_4DWAVE_DX] = "Trident 4DWave DX",
[TYPE_4DWAVE_NX] = "Trident 4DWave NX",
};
typedef struct tChannelControl
{
// register data
unsigned int * lpChStart;
unsigned int * lpChStop;
unsigned int * lpChAint;
unsigned int * lpChAinten;
// register addresses
unsigned int * lpAChStart;
unsigned int * lpAChStop;
unsigned int * lpAChAint;
unsigned int * lpAChAinten;
unsigned int data[16];
} CHANNELCONTROL;
/* --------------------------------------------------------------------- */
struct trident_state {
unsigned int magic;
int channel;
struct trident_card *card; /* Card info */
/* wave stuff */
unsigned int rateadc, ratedac;
unsigned char fmt, enable;
struct semaphore open_sem;
mode_t open_mode;
wait_queue_head_t open_wait;
/* soundcore stuff */
int dev_audio;
struct dmabuf {
void *rawbuf;
unsigned buforder;
unsigned numfrag;
unsigned fragshift;
int chan[2]; /* Hardware channel */
/* XXX zab - swptr only in here so that it can be referenced by
clear_advance, as far as I can tell :( */
unsigned hwptr, swptr;
unsigned total_bytes;
int count;
unsigned error; /* over/underrun */
wait_queue_head_t wait;
/* redundant, but makes calculations easier */
unsigned fragsize;
unsigned dmasize;
unsigned fragsamples;
/* OSS stuff */
unsigned mapped:1;
unsigned ready:1;
unsigned endcleared:1;
unsigned ossfragshift;
int ossmaxfrags;
unsigned subdivision;
u16 base; /* Offset for ptr */
} dma_dac, dma_adc;
u8 bDMAStart;
};
struct trident_card {
unsigned int magic;
/* We keep trident cards in a linked list */
struct trident_card *next;
/* The trident has a certain amount of cross channel interaction
so we use a single per card lock */
spinlock_t lock;
int dev_mixer;
int card_type;
/* as most of this is static,
perhaps it should be a pointer to a global struct */
struct mixer_goo {
int modcnt;
int supported_mixers;
int stereo_mixers;
int record_sources;
/* the caller must guarantee arg sanity before calling these */
/* int (*read_mixer)(struct trident_card *card, int index);*/
void (*write_mixer)(struct trident_card *card,int mixer, unsigned int left,unsigned int right);
int (*recmask_io)(struct trident_card *card,int rw,int mask);
unsigned int mixer_state[SOUND_MIXER_NRDEVICES];
} mix;
struct trident_state channels[NR_DSPS];
/* hardware resources */
unsigned long iobase;
u32 irq;
u32 ChanMap[2];
int ChanPCMcnt;
int ChanPCM;
CHANNELCONTROL ChRegs;
int ChanDwordCount;
};
static struct timer_list debug_timer;
#define IWriteAinten( x ) \
{int i; \
for( i= 0; i < ChanDwordCount; i++) \
outl((x)->lpChAinten[i], TRID_REG(trident, (x)->lpAChAinten[i]));}
#define IReadAinten( x ) \
{int i; \
for( i= 0; i < ChanDwordCount; i++) \
(x)->lpChAinten[i] = inl(TRID_REG(trident, (x)->lpAChAinten[i]));}
#define ReadAint( x ) \
IReadAint( x )
#define WriteAint( x ) \
IWriteAint( x )
#define IWriteAint( x ) \
{int i; \
for( i= 0; i < ChanDwordCount; i++) \
outl((x)->lpChAint[i], TRID_REG(trident, (x)->lpAChAint[i]));}
#define IReadAint( x ) \
{int i; \
for( i= 0; i < ChanDwordCount; i++) \
(x)->lpChAint[i] = inl(TRID_REG(trident, (x)->lpAChAint[i]));}
/*
* Trident support library routines
*/
/*---------------------------------------------------------------------------
void ResetAinten( struct trident_state *trident, int ChannelNum)
Description: This routine will disable interrupts and ack any
existing interrupts for specified channel.
Parameters: trident - pointer to target device class for 4DWave.
ChannelNum - channel number
returns: TRUE if everything went ok, else FALSE.
---------------------------------------------------------------------------*/
static void ResetAinten(struct trident_card * trident, int ChannelNum)
{
unsigned int dwMask;
unsigned int x = ChannelNum >> 5;
unsigned int ChanDwordCount = trident->ChanDwordCount;
IReadAinten(&trident->ChRegs);
dwMask = 1 << (ChannelNum & 0x1f);
trident->ChRegs.lpChAinten[x] &= ~dwMask;
IWriteAinten(&trident->ChRegs);
// Ack the channel in case the interrupt was set before we disable it.
outl(dwMask, TRID_REG(trident, trident->ChRegs.lpAChAint[x]));
}
/*---------------------------------------------------------------------------
void EnableEndInterrupts( struct trident_card *trident)
Description: This routine will enable end of loop interrupts.
End of loop interrupts will occur when a running
channel reaches ESO.
Parameters: trident - pointer to target device class for 4DWave.
returns: TRUE if everything went ok, else FALSE.
---------------------------------------------------------------------------*/
static int EnableEndInterrupts(struct trident_card * trident)
{
unsigned int GlobalControl;
GlobalControl = inb(TRID_REG(trident, T4D_LFO_GC_CIR + 1));
GlobalControl |= 0x10;
outb(GlobalControl, TRID_REG(trident, T4D_LFO_GC_CIR + 1));
M_printk("(trident) globctl=%02X\n", GlobalControl);
M_printk("(trident) enabled end interrupts\n");
return (TRUE);
}
/*---------------------------------------------------------------------------
e void DisableEndInterrupts( struct trident_card *trident)
Description: This routine will disable end of loop interrupts.
End of loop interrupts will occur when a running
channel reaches ESO.
Parameters:
trident - pointer to target device class for 4DWave.
returns: TRUE if everything went ok, else FALSE.
---------------------------------------------------------------------------*/
static int DisableEndInterrupts(struct trident_card * trident)
{
unsigned int GlobalControl;
GlobalControl = inb(TRID_REG(trident, T4D_LFO_GC_CIR + 1));
GlobalControl &= ~0x10;
outb(GlobalControl, TRID_REG(trident, T4D_LFO_GC_CIR + 1));
M_printk("(trident) disabled end interrupts\n");
M_printk("(trident) globctl=%02X\n", GlobalControl);
return (TRUE);
}
/*---------------------------------------------------------------------------
void trident_enable_voice_irq( unsigned int HwChannel )
Description: Enable an interrupt channel, any channel 0 thru n.
This routine automatically handles the fact that there are
more than 32 channels available.
Parameters : HwChannel - Channel number 0 thru n.
trident - pointer to target device class for 4DWave.
Return Value: None.
---------------------------------------------------------------------------*/
void trident_enable_voice_irq(struct trident_card * trident, unsigned int HwChannel)
{
unsigned int x, Data, ChanDwordCount;
x = HwChannel >> 5;
Data = 1 << (HwChannel & 0x1f);
ChanDwordCount = trident->ChanDwordCount;
IReadAinten(&trident->ChRegs);
trident->ChRegs.lpChAinten[x] |= Data;
IWriteAinten(&trident->ChRegs);
M_printk("(trident) enabled voice IRQ %d\n", HwChannel);
}
/*---------------------------------------------------------------------------
void trident_disable_voice_irq( unsigned int HwChannel )
Description: Disable an interrupt channel, any channel 0 thru n.
This routine automatically handles the fact that there are
more than 32 channels available.
Parameters : HwChannel - Channel number 0 thru n.
trident - pointer to target device class for 4DWave.
Return Value: None.
---------------------------------------------------------------------------*/
void trident_disable_voice_irq(struct trident_card * trident, unsigned int HwChannel)
{
unsigned int x, Data, ChanDwordCount;
x = HwChannel >> 5;
Data = 1 << (HwChannel & 0x1f);
ChanDwordCount = trident->ChanDwordCount;
IReadAinten(&trident->ChRegs);
trident->ChRegs.lpChAinten[x] &= ~Data;
IWriteAinten(&trident->ChRegs);
M_printk("(trident) disabled voice IRQ %d\n", HwChannel);
}
/*---------------------------------------------------------------------------
unsigned int AllocateChannelPCM( void )
Description: Allocate hardware channel by reverse order (63-0).
Parameters : trident - pointer to target device class for 4DWave.
Return Value: hardware channel - 0-63 or -1 when no channel is available
---------------------------------------------------------------------------*/
static int AllocateChannelPCM(struct trident_card *trident)
{
int idx;
if (trident->ChanPCMcnt >= trident->ChanPCM)
{
M_printk(KERN_DEBUG "(trident) no channels available.\n");
return -1;
}
for (idx = 31; idx >= 0; idx--) {
if (!(trident->ChanMap[1] & (1 << idx))) {
trident->ChanMap[1] |= 1 << idx;
trident->ChanPCMcnt++;
return idx + 32;
}
}
for (idx = 31; idx >= 0; idx--) {
if (!(trident->ChanMap[0] & (1 << idx))) {
trident->ChanMap[0] |= 1 << idx;
trident->ChanPCMcnt++;
return idx;
}
}
return -1;
}
/*---------------------------------------------------------------------------
void FreeChannelPCM( int channel )
Description: Free hardware channel.
Parameters : trident - pointer to target device class for 4DWave.
channel - hardware channel number 0-63
Return Value: none
---------------------------------------------------------------------------*/
static void FreeChannelPCM(struct trident_card *trident, int channel)
{
if (channel < 0 || channel > 63)
return;
if (trident->ChanMap[channel>>5] & (1 << (channel & 0x1f))) {
trident->ChanMap[channel>>5] &= ~(1 << (channel & 0x1f));
trident->ChanPCMcnt--;
}
}
/*---------------------------------------------------------------------------
void trident_start_voice( ULONG HwChannel )
Description: Start a channel, any channel 0 thru n.
This routine automatically handles the fact that there are
more than 32 channels available.
Parameters : HwChannel - Channel number 0 thru n.
trident - pointer to target device class for 4DWave.
Return Value: None.
---------------------------------------------------------------------------*/
void trident_start_voice(struct trident_card * trident, unsigned int HwChannel)
{
unsigned int x = HwChannel >> 5;
unsigned int Data = 1 << (HwChannel & 0x1f);
outl(Data, TRID_REG(trident, trident->ChRegs.lpAChStart[x]));
M_printk("(trident) start voice %d\n", HwChannel);
}
/*---------------------------------------------------------------------------
void trident_stop_voice( ULONG HwChannel )
Description: Stop a channel, any channel 0 thru n.
This routine automatically handles the fact that there are
more than 32 channels available.
Parameters : HwChannel - Channel number 0 thru n.
trident - pointer to target device class for 4DWave.
Return Value: None.
---------------------------------------------------------------------------*/
void trident_stop_voice(struct trident_card * trident, unsigned int HwChannel)
{
unsigned int x = HwChannel >> 5;
unsigned int Data = 1 << (HwChannel & 0x1f);
outl(Data, TRID_REG(trident, trident->ChRegs.lpAChStop[x]));
M_printk("(trident) stop voice %d\n", HwChannel);
}
/*---------------------------------------------------------------------------
int DidChannelInterrupt( )
Description: Check if interrupt channel occurred.
Parameters : trident - pointer to target device class for 4DWave.
Return Value: TRUE if interrupt occurred, else FALSE.
---------------------------------------------------------------------------*/
static int DidChannelInterrupt(struct trident_card * trident, int channel)
{
unsigned int ChanDwordCount = trident->ChanDwordCount;
unsigned int x = channel >> 5;
unsigned int dwMask = 1 << (channel & 0x1f);
ReadAint(&trident->ChRegs);
return (trident->ChRegs.lpChAint[x] & dwMask) ? TRUE : FALSE;
}
/*---------------------------------------------------------------------------
void AckChannelInterrupt( )
Description: Acknowledge the interrupt bit for channel intrs.
Parameters : trident - pointer to target device class for 4DWave.
Return Value: None
---------------------------------------------------------------------------*/
static void AckChannelInterrupt(struct trident_card * trident, int channel)
{
unsigned int ChanDwordCount = trident->ChanDwordCount;
unsigned int x = channel >> 5;
unsigned int dwMask = 1 << (channel & 0x1f);
ReadAint(&trident->ChRegs);
trident->ChRegs.lpChAint[x] &= dwMask;
IWriteAint(&trident->ChRegs);
}
/*---------------------------------------------------------------------------
int LoadDeltaHw( unsigned int HwChannel, unsigned int Delta )
Description: This routine writes Delta to the hardware.
Parameters: Delta - data to write (2 Bytes only)
HwChannel - Hardware channel to write to.
trident - pointer to target device class for 4DWave.
Returns: TRUE if all goes well, else FALSE.
---------------------------------------------------------------------------*/
static int LoadDeltaHw(struct trident_card * trident, unsigned int HwChannel, unsigned int Delta)
{
outb(HwChannel, TRID_REG(trident, T4D_LFO_GC_CIR));
if (trident->card_type != TYPE_4DWAVE_NX) {
outw((unsigned short) Delta, TRID_REG(trident, CH_DX_ESO_DELTA));
}
else // ID_4DWAVE_NX
{
outb((unsigned char) Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3));
outb((unsigned char) (Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3));
}
return TRUE;
}
/*---------------------------------------------------------------------------
int LoadVirtualChannel( ULONG *Data, ULONG HwChannel)
Description: This routine writes all required channel registers to hardware.
Parameters: *Data - a pointer to the data to write (5 ULONGS always).
HwChannel - Hardware channel to write to.
trident - pointer to target device class for 4DWave.
Returns: TRUE if all goes well, else FALSE.
---------------------------------------------------------------------------*/
static int LoadVirtualChannel(struct trident_card * trident, unsigned int *Data, unsigned int HwChannel)
{
unsigned int ChanData[CHANNEL_REGS];
unsigned int ULONGSToDo = CHANNEL_REGS;
unsigned int i;
unsigned int Address = CHANNEL_START;
/* Copy the data first... Hack... Before mucking with Volume! */
memcpy((unsigned char *) ChanData, (unsigned char *) Data, ULONGSToDo * 4);
outb((unsigned char) HwChannel, TRID_REG(trident, T4D_LFO_GC_CIR));
for (i = 0; i < ULONGSToDo; i++, Address += 4)
outl(ChanData[i], TRID_REG(trident, Address));
M_printk("(trident) load virtual channel %d\n", HwChannel);
return TRUE;
}
/*---------------------------------------------------------------------------
trident_write_voice_regs
Description: This routine will write the 5 hardware channel registers
to hardware.
Paramters: trident - pointer to target device class for 4DWave.
Channel - Real or Virtual channel number.
Each register field.
Returns: TRUE if all goes well, else FALSE.
---------------------------------------------------------------------------*/
int trident_write_voice_regs(struct trident_card * trident,
unsigned int Channel,
unsigned int LBA,
unsigned int CSO,
unsigned int ESO,
unsigned int DELTA,
unsigned int ALPHA_FMS,
unsigned int FMC_RVOL_CVOL,
unsigned int GVSEL,
unsigned int PAN,
unsigned int VOL,
unsigned int CTRL,
unsigned int EC)
{
unsigned int ChanData[CHANNEL_REGS + 1], FmcRvolCvol;
ChanData[1] = LBA;
ChanData[4] = (GVSEL << 31) |
((PAN & 0x0000007f) << 24) |
((VOL & 0x000000ff) << 16) |
((CTRL & 0x0000000f) << 12) |
(EC & 0x00000fff);
FmcRvolCvol = FMC_RVOL_CVOL & 0x0000ffff;
if (trident->card_type != TYPE_4DWAVE_NX)
{
ChanData[0] = (CSO << 16) | (ALPHA_FMS & 0x0000ffff);
ChanData[2] = (ESO << 16) | (DELTA & 0x0ffff);
ChanData[3] = FmcRvolCvol;
}
else // ID_4DWAVE_NX
{
ChanData[0] = (DELTA << 24) | (CSO & 0x00ffffff);
ChanData[2] = ((DELTA << 16) & 0xff000000) | (ESO & 0x00ffffff);
ChanData[3] = (ALPHA_FMS << 16) | FmcRvolCvol;
}
LoadVirtualChannel(trident, ChanData, Channel);
return TRUE;
}
static int compute_rate(u32 rate)
{
int delta;
// We special case 44100 and 8000 since rounding with the equation
// does not give us an accurate enough value. For 11025 and 22050
// the equation gives us the best answer. All other frequencies will
// also use the equation. JDW
if (rate == 44100)
delta = 0xeb3;
else if (rate == 8000)
delta = 0x2ab;
else if (rate == 48000)
delta = 0x1000;
else
delta = (((rate << 12) + rate) / 48000) & 0x0000ffff;
return delta;
}
/*---------------------------------------------------------------------------
trident_set_dac_rate
Description: This routine will set the sample rate for playback.
Paramters: trident - pointer to target device class for 4DWave.
rate - desired sample rate
set - actually write hardware if set is true.
Returns: The rate allowed by device.
---------------------------------------------------------------------------*/
static unsigned int trident_set_dac_rate(struct trident_state * trident,
unsigned int rate, int set)
{
unsigned int delta;
if (rate > 48000)
rate = 48000;
if (rate < 4000)
rate = 4000;
delta = compute_rate(rate);
if(set)
{
//Select channel window
outb(trident->dma_dac.chan[1], TRID_REG(trident->card, T4D_LFO_GC_CIR));
if (trident->card->card_type != TYPE_4DWAVE_NX)
outw(delta,TRID_REG(trident->card,CH_DX_ESO_DELTA+2));
else // ID_4DWAVE_NX
{
outb( delta & 0xff,TRID_REG(trident->card,CH_NX_DELTA_CSO));
outb((delta>>8) & 0xff,TRID_REG(trident->card,CH_NX_DELTA_ESO));
}
}
M_printk("(trident) called trident_set_dac_rate : rate = %d, set = %d delta = %4x\n",
rate, set,delta);
trident->ratedac = rate;
return rate;
}
/*---------------------------------------------------------------------------
trident_set_adc_rate
Description: This routine will set the sample rate for capture.
Paramters: trident - pointer to target device class for 4DWave.
rate - desired sample rate
set - actually write hardware if set is true.
Returns: The rate allowed by device.
---------------------------------------------------------------------------*/
static unsigned int trident_set_adc_rate(struct trident_state * trident,
unsigned int rate,
int set)
{
//snd_printk("trid: called trident_set_adc_rate\n");
if (rate > 48000)
rate = 48000;
if (rate < 4000)
rate = 4000;
trident->rateadc = rate;
/*
* FIXME: hit the hardware
*/
return rate;
}
extern __inline__ unsigned ld2(unsigned int x)
{
unsigned r = 0;
if (x >= 0x10000) {
x >>= 16;
r += 16;
}
if (x >= 0x100) {
x >>= 8;
r += 8;
}
if (x >= 0x10) {
x >>= 4;
r += 4;
}
if (x >= 4) {
x >>= 2;
r += 2;
}
if (x >= 2)
r++;
return r;
}
/* --------------------------------------------------------------------- */
static struct trident_card *devs = NULL;
/* --------------------------------------------------------------------- */
/*
* Trident AC97 codec programming interface.
*/
static void trident_ac97_set(struct trident_card *trident, u8 cmd, u16 val)
{
unsigned int address, data;
unsigned short count = 0xffff;
data = ((unsigned long)val) << 16;
if (trident->card_type != TYPE_4DWAVE_NX)
{
address = DX_ACR0_AC97_W;
/* read AC-97 write register status */
do
{
if ((inw(TRID_REG(trident,address))&0x8000) == 0)
break;
}
while(count--);
data |= (0x8000 | (cmd & 0x000000ff));
}
else // ID_4DWAVE_NX
{
address = NX_ACR1_AC97_W;
/* read AC-97 write register status */
do
{
if ((inw(TRID_REG(trident, address )) & 0x0800) == 0)
break;
}
while (count--);
data |= (0x0800 | (cmd & 0x000000ff));
}
if (count == 0)
{
printk(KERN_ERR "trident: AC97 CODEC write timed out.\n");
return;
}
outl(data, TRID_REG(trident, address));
}
static u16 trident_ac97_get(struct trident_card *trident, u8 cmd)
{
unsigned int data = 0;
unsigned short count = 0xffff;
if (trident->card_type != TYPE_4DWAVE_NX)
{
data = (0x00008000L | (cmd & 0x000000ff));
outl(data, TRID_REG(trident, DX_ACR1_AC97_R));
do
{
data = inl(TRID_REG(trident, DX_ACR1_AC97_R));
if ( ( data & 0x0008000 ) == 0 )
break;
}
while(count--);
}
else // ID_4DWAVE_NX
{
data = (0x00000800L | (cmd & 0x000000ff));
outl(data, TRID_REG(trident, NX_ACR2_AC97_R_PRIMARY));
do
{
data = inl(TRID_REG(trident, NX_ACR2_AC97_R_PRIMARY));
if ( ( data & 0x00000C00 ) == 0 )
break;
}
while(count--);
}
if ( count == 0 )
{
printk("trident: AC97 CODEC read timed out.\n");
data = 0;
}
return ((unsigned short)(data >> 16));
}
/* OSS interface to the ac97s.. */
#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|\
SOUND_MASK_PCM|SOUND_MASK_LINE|SOUND_MASK_CD|\
SOUND_MASK_VIDEO|SOUND_MASK_LINE1|SOUND_MASK_IGAIN)
#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \
SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_MIC|\
SOUND_MASK_SPEAKER)
#define AC97_RECORD_MASK (SOUND_MASK_MIC|\
SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\
SOUND_MASK_PHONEIN)
#define supported_mixer(CARD,FOO) ( CARD->mix.supported_mixers & (1<<FOO) )
/* this table has default mixer values for all OSS mixers.
be sure to fill it in if you add oss mixers
to anyone's supported mixer defines */
/* possible __init */
static struct mixer_defaults {
int mixer;
unsigned int value;
} mixer_defaults[SOUND_MIXER_NRDEVICES] = {
/* all values 0 -> 100 in bytes */
{SOUND_MIXER_VOLUME, 0x3232},
{SOUND_MIXER_BASS, 0x3232},
{SOUND_MIXER_TREBLE, 0x3232},
{SOUND_MIXER_SPEAKER, 0x3232},
{SOUND_MIXER_MIC, 0x3232},
{SOUND_MIXER_LINE, 0x3232},
{SOUND_MIXER_CD, 0x3232},
{SOUND_MIXER_VIDEO, 0x3232},
{SOUND_MIXER_LINE1, 0x3232},
{SOUND_MIXER_PCM, 0x3232},
{SOUND_MIXER_IGAIN, 0x3232},
{-1,0}
};
static struct ac97_mixer_hw {
unsigned char offset;
int scale;
} ac97_hw[SOUND_MIXER_NRDEVICES]= {
[SOUND_MIXER_VOLUME] = {0x02,63},
[SOUND_MIXER_BASS] = {0x08,15},
[SOUND_MIXER_TREBLE] = {0x08,15},
[SOUND_MIXER_SPEAKER] = {0x0a,15},
[SOUND_MIXER_MIC] = {0x0e,31},
[SOUND_MIXER_LINE] = {0x10,31},
[SOUND_MIXER_CD] = {0x12,31},
[SOUND_MIXER_VIDEO] = {0x14,31},
[SOUND_MIXER_LINE1] = {0x16,31},
[SOUND_MIXER_PCM] = {0x18,31},
[SOUND_MIXER_IGAIN] = {0x1c,31}
};
#if 0 /* *shrug* removed simply because we never used it.
feel free to implement again if needed */
/* reads the given OSS mixer from the ac97
the caller must have insured that the ac97 knows
about that given mixer, and should be holding a
spinlock for the card */
static int ac97_read_mixer(struct trident_card *card, int mixer)
{
u16 val;
int ret=0;
struct ac97_mixer_hw *mh = &ac97_hw[mixer];
val = trident_ac97_get(card , mh->offset);
if(AC97_STEREO_MASK & (1<<mixer)) {
/* nice stereo mixers .. */
int left,right;
left = (val >> 8) & 0x7f;
right = val & 0x7f;
if (mixer == SOUND_MIXER_IGAIN) {
right = (right * 100) / mh->scale;
left = (left * 100) / mh->scale;
else {
right = 100 - ((right * 100) / mh->scale);
left = 100 - ((left * 100) / mh->scale);
}
ret = left | (right << 8);
} else if (mixer == SOUND_MIXER_SPEAKER) {
ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale);
} else if (mixer == SOUND_MIXER_MIC) {
ret = 100 - (((val & 0x1f) * 100) / mh->scale);
/* the low bit is optional in the tone sliders and masking
it lets is avoid the 0xf 'bypass'.. */
} else if (mixer == SOUND_MIXER_BASS) {
ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale);
} else if (mixer == SOUND_MIXER_TREBLE) {
ret = 100 - (((val & 0xe) * 100) / mh->scale);
}
printk("read mixer %d (0x%x) %x -> %x\n",mixer,mh->offset,val,ret);
return ret;
}
#endif
/* write the OSS encoded volume to the given OSS encoded mixer,
again caller's job to make sure all is well in arg land,
call with spinlock held */
static void ac97_write_mixer(struct trident_card *card, int mixer, unsigned int left, unsigned int right)
{
u16 val=0;
struct ac97_mixer_hw *mh = &ac97_hw[mixer];
printk("(trident) wrote ac97 mixer %d (0x%x) %d,%d",mixer,mh->offset,left,right);
if(AC97_STEREO_MASK & (1<<mixer)) {
/* stereo mixers */
if (mixer == SOUND_MIXER_IGAIN) {
right = (right * mh->scale) / 100;
left = (left * mh->scale) / 100;
} else {
right = ((100 - right) * mh->scale) / 100;
left = ((100 - left) * mh->scale) / 100;
}
val = (left << 8) | right;
} else if (mixer == SOUND_MIXER_SPEAKER) {
val = (((100 - left) * mh->scale) / 100) << 1;
} else if (mixer == SOUND_MIXER_MIC) {
val = trident_ac97_get(card , mh->offset) & ~0x801f;
val |= (((100 - left) * mh->scale) / 100);
/* the low bit is optional in the tone sliders and masking
it lets us avoid the 0xf 'bypass'.. */
} else if (mixer == SOUND_MIXER_BASS) {
val = trident_ac97_get(card , mh->offset) & ~0x0f00;
val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00;
} else if (mixer == SOUND_MIXER_TREBLE) {
val = trident_ac97_get(card , mh->offset) & ~0x000f;
val |= (((100 - left) * mh->scale) / 100) & 0x000e;
}
trident_ac97_set(card, mh->offset, val);
printk(" -> %x\n",val);
}
/* the following tables allow us to go from
OSS <-> ac97 quickly. */
enum ac97_recsettings {
AC97_REC_MIC=0,
AC97_REC_CD,
AC97_REC_VIDEO,
AC97_REC_AUX,
AC97_REC_LINE,
AC97_REC_STEREO, /* combination of all enabled outputs.. */
AC97_REC_MONO, /*.. or the mono equivalent */
AC97_REC_PHONE
};
static unsigned int ac97_rm2oss[] = {
[AC97_REC_MIC] = SOUND_MIXER_MIC,
[AC97_REC_CD] = SOUND_MIXER_CD,
[AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,
[AC97_REC_AUX] = SOUND_MIXER_LINE1,
[AC97_REC_LINE] = SOUND_MIXER_LINE,
[AC97_REC_PHONE] = SOUND_MIXER_PHONEIN
};
/* indexed by bit position */
static unsigned int ac97_oss_rm[] = {
[SOUND_MIXER_MIC] = AC97_REC_MIC,
[SOUND_MIXER_CD] = AC97_REC_CD,
[SOUND_MIXER_VIDEO] = AC97_REC_VIDEO,
[SOUND_MIXER_LINE1] = AC97_REC_AUX,
[SOUND_MIXER_LINE] = AC97_REC_LINE,
[SOUND_MIXER_PHONEIN] = AC97_REC_PHONE
};
/* read or write the recmask
the ac97 can really have left and right recording
inputs independantly set, but OSS doesn't seem to
want us to express that to the user.
the caller guarantees that we have a supported bit set,
and they must be holding the card's spinlock */
static int ac97_recmask_io(struct trident_card *card, int rw, int mask)
{
unsigned int val;
if (rw) {
/* read it from the card */
val = trident_ac97_get(card, 0x1a) & 0x7;
return ac97_rm2oss[val];
}
/* else, write the first set in the mask as the
output */
val = ffs(mask);
val = ac97_oss_rm[val-1];
val |= val << 8; /* set both channels */
printk("trident: setting ac97 recmask to 0x%x\n",val);
trident_ac97_set(card,0x1a,val);
return 0;
};
/*
* Generic AC97 codec initialisation. Need to check there are no
* quirks for the Trident cards.
*/
static u16 trident_ac97_init(struct trident_card *trident)
{
trident->mix.supported_mixers = AC97_SUPPORTED_MASK;
trident->mix.stereo_mixers = AC97_STEREO_MASK;
trident->mix.record_sources = AC97_RECORD_MASK;
/* trident->mix.read_mixer = ac97_read_mixer;*/
trident->mix.write_mixer = ac97_write_mixer;
trident->mix.recmask_io = ac97_recmask_io;
if(trident->card_type == TYPE_4DWAVE_NX)
{
unsigned short VendorID1, VendorID2;
outl(0x02, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
// 4 Speaker Codec initialization
VendorID1 = trident_ac97_get(trident, AC97_VENDOR_ID1);
VendorID2 = trident_ac97_get(trident, AC97_VENDOR_ID2);
if( (VendorID1 == 0x8384) && (VendorID2 == 0x7608) )
{
// Sigmatel 9708.
unsigned short TestReg;
unsigned int DTemp;
trident_ac97_set(trident, AC97_SIGMATEL_CIC1, 0xABBAL);
trident_ac97_set(trident, AC97_SIGMATEL_CIC2, 0x1000L);
TestReg = trident_ac97_get(trident, AC97_SIGMATEL_BIAS2);
if( TestReg != 0x8000 ) // Errata Notice.
{
trident_ac97_set(trident, AC97_SIGMATEL_BIAS1, 0xABBAL );
trident_ac97_set(trident, AC97_SIGMATEL_BIAS2, 0x0007L );
}
else // Newer version
{
trident_ac97_set(trident, AC97_SIGMATEL_CIC2, 0x1001L ); // recommended
trident_ac97_set(trident, AC97_SIGMATEL_DAC2INVERT, 0x0008L );
}
trident_ac97_set( trident, AC97_SURROUND_MASTER, 0x0000L );
trident_ac97_set( trident, AC97_HEADPHONE_VOL, 0x8000L );
DTemp = (unsigned int)trident_ac97_get( trident, AC97_GENERAL_PURPOSE );
trident_ac97_set( trident, AC97_GENERAL_PURPOSE, DTemp & 0x0000FDFFL ); // bit9 = 0.
DTemp = (unsigned int)trident_ac97_get( trident, AC97_MIC_VOL );
trident_ac97_set( trident, AC97_MIC_VOL, DTemp | 0x00008000L ); // bit15 = 1.
DTemp = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
outl(DTemp | 0x0010, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
}
else if((VendorID1 == 0x5452) && (VendorID2 == 0x4108) )
{ // TriTech TR28028
trident_ac97_set( trident, AC97_SURROUND_MASTER, 0x0000L );
trident_ac97_set( trident, AC97_EXTENDED_STATUS, 0x0000L );
}
else if((VendorID1 == 0x574D) &&
(VendorID2 >= 0x4C00) && (VendorID2 <= 0x4C0f))
{ // Wolfson WM9704
trident_ac97_set( trident, AC97_SURROUND_MASTER, 0x0000L );
}
else
{
#if 0
printk("trident: No four Speaker Support with on board CODEC\n") ;
#endif
}
// S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled
outl(0x200004, TRID_REG(trident, NX_SPCSTATUS));
// Enable S/PDIF out, 48khz only from ac97 fifo
outb(0x28, TRID_REG(trident, NX_SPCTRL_SPCSO+3));
}
else
{
outl(0x02, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
}
return 0;
}
/* this only fixes the output apu mode to be later set by start_dac and
company. output apu modes are set in trident_rec_setup */
static void set_fmt(struct trident_state *s, unsigned char mask, unsigned char data)
{
s->fmt = (s->fmt & mask) | data;
/* Set the chip ? */
}
/*
* Native play back driver
*/
/* the mode passed should be already shifted and masked */
static void trident_play_setup(struct trident_state *trident, int mode, u32 rate, void *buffer, int size)
{
unsigned int LBA;
unsigned int Delta;
unsigned int ESO;
unsigned int CTRL;
unsigned int FMC_RVOL_CVOL;
unsigned int GVSEL;
unsigned int PAN;
unsigned int VOL;
unsigned int EC;
/* set Loop Back Address */
LBA = virt_to_bus(buffer);
Delta = compute_rate(rate);
M_printk("(trident) rate, delta = %d %d\n", rate, Delta);
/* set ESO */
ESO = size;
if (mode & TRIDENT_FMT_16BIT)
ESO /= 2;
if (mode & TRIDENT_FMT_STEREO)
ESO /= 2;
ESO = ESO - 1;
//snd_printk("trid: ESO = %d\n", ESO);
/* set ctrl mode
CTRL default: 8-bit (unsigned) mono, loop mode enabled
*/
CTRL = 0x00000001;
if (mode & TRIDENT_FMT_16BIT)
{
CTRL |= 0x00000008; // 16-bit data
CTRL |= 0x00000002; // signed data
}
if (mode&TRIDENT_FMT_STEREO)
CTRL |= 0x00000004; // stereo data
//FMC_RVOL_CVOL = 0x0000c000;
FMC_RVOL_CVOL = 0x0000ffff;
GVSEL = 1;
PAN = 0;
VOL = 0;
EC = 0;
trident_write_voice_regs(trident->card,
trident->dma_dac.chan[1],
LBA,
0, /* cso */
ESO,
Delta,
0, /* alpha */
FMC_RVOL_CVOL,
GVSEL,
PAN,
VOL,
CTRL,
EC);
}
/*
* Native record driver
*/
/* again, passed mode is alrady shifted/masked */
static void trident_rec_setup(struct trident_state *trident, int mode, u32 rate, void *buffer, int size)
{
unsigned int LBA;
unsigned int Delta;
unsigned int ESO;
unsigned int CTRL;
unsigned int FMC_RVOL_CVOL;
unsigned int GVSEL;
unsigned int PAN;
unsigned int VOL;
unsigned int EC;
unsigned char bValue;
unsigned short wValue;
unsigned int dwValue;
unsigned short wRecCODECSamples;
unsigned int dwChanFlags;
struct trident_card *card = trident->card;
// Enable AC-97 ADC (capture), disable capture interrupt
if (trident->card->card_type != TYPE_4DWAVE_NX)
{
bValue = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT));
outb(bValue | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT));
}
else
{
wValue = inw(TRID_REG(card, T4D_MISCINT));
outw(wValue | 0x1000, TRID_REG(card, T4D_MISCINT));
}
// Initilize the channel and set channel Mode
outb(0, TRID_REG(card, LEGACY_DMAR15));
// Set DMA channel operation mode register
bValue = inb(TRID_REG(card, LEGACY_DMAR11)) & 0x03;
outb(bValue | 0x54, TRID_REG(card, LEGACY_DMAR11));
// Set channel buffer Address
LBA = virt_to_bus(buffer);
outl(LBA, TRID_REG(card, LEGACY_DMAR0));
/* set ESO */
ESO = size;
dwValue = inl(TRID_REG(card, LEGACY_DMAR4)) & 0xff000000;
dwValue |= (ESO - 1) & 0x0000ffff;
outl(dwValue, TRID_REG(card, LEGACY_DMAR4));
// Set channel sample rate , 4.12 format
dwValue = (((unsigned int) 48000L << 12) / (unsigned long) (rate));
outw((unsigned short) dwValue, TRID_REG(card, T4D_SBDELTA_DELTA_R));
// Set channel interrupt blk length
if (mode & TRIDENT_FMT_16BIT) {
wRecCODECSamples = (unsigned short) ((ESO >> 1) - 1);
dwChanFlags = 0xffffb000;
} else {
wRecCODECSamples = (unsigned short) (ESO - 1);
dwChanFlags = 0xffff1000;
}
dwValue = ((unsigned int) wRecCODECSamples) << 16;
dwValue |= (unsigned int) (wRecCODECSamples) & 0x0000ffff;
outl(dwValue, TRID_REG(card, T4D_SBBL_SBCL));
// Right now, set format and start to run capturing,
// continuous run loop enable.
trident->bDMAStart = 0x19; // 0001 1001b
if (mode & TRIDENT_FMT_16BIT)
trident->bDMAStart |= 0xa0;
if (mode & TRIDENT_FMT_STEREO)
trident->bDMAStart |= 0x40;
// Prepare capture intr channel
Delta = ((((unsigned int) rate) << 12) / ((unsigned long) (48000L)));
/* set Loop Back Address */
LBA = virt_to_bus(buffer);
/* set ESO */
ESO = size;
if (mode & TRIDENT_FMT_16BIT)
ESO /= 2;
if (mode & TRIDENT_FMT_STEREO)
ESO /= 2;
ESO = ESO - 1;
//snd_printk("trid: ESO = %d\n", ESO);
/* set ctrl mode
CTRL default: 8-bit (unsigned) mono, loop mode enabled
*/
CTRL = 0x00000001;
if (mode & TRIDENT_FMT_16BIT)
CTRL |= 0x00000008; // 16-bit data
/* XXX DO UNSIGNED XXX */
//if (!(mode & SND_PCM1_MODE_U))
// CTRL |= 0x00000002; // signed data
if (mode& TRIDENT_FMT_STEREO)
CTRL |= 0x00000004; // stereo data
FMC_RVOL_CVOL = 0x0000ffff;
GVSEL = 1;
PAN = 0xff;
VOL = 0xff;
EC = 0;
trident_write_voice_regs(card,
trident->dma_adc.chan[0],
LBA,
0, /* cso */
ESO,
Delta,
0, /* alpha */
FMC_RVOL_CVOL,
GVSEL,
PAN,
VOL,
CTRL,
EC);
}
/* Playback pointer */
__inline__ unsigned int get_dmaa(struct trident_state *trident)
{
unsigned int cso;
unsigned int eso;
#if 0
if (!(trident->enable & ADC_RUNNING))
return 0;
#endif
outb(trident->dma_dac.chan[1], TRID_REG(trident->card, T4D_LFO_GC_CIR));
if (trident->card->card_type != TYPE_4DWAVE_NX)
{
cso = inw(TRID_REG(trident->card, CH_DX_CSO_ALPHA_FMS + 2));
eso = inw(TRID_REG(trident->card, CH_DX_ESO_DELTA + 2));
}
else // ID_4DWAVE_NX
{
cso = (unsigned int) inl(TRID_REG(trident->card, CH_NX_DELTA_CSO)) & 0x00ffffff;
eso = (unsigned int) inl(TRID_REG(trident->card, CH_NX_DELTA_ESO)) & 0x00ffffff;
}
M_printk("(trident) get_dmaa: chip reported %d.%d\n", cso, eso);
cso++;
if (cso > eso)
cso = eso;
if (trident->fmt & TRIDENT_FMT_16BIT)
cso *= 2;
if (trident->fmt & TRIDENT_FMT_STEREO)
cso *= 2;
return cso;
}
/* Record pointer */
extern __inline__ unsigned get_dmac(struct trident_state *trident)
{
unsigned int cso;
if (!(trident->enable&DAC_RUNNING))
return 0;
outb(trident->dma_adc.chan[0], TRID_REG(trident->card, T4D_LFO_GC_CIR));
if (trident->card->card_type != TYPE_4DWAVE_NX) {
cso = inw(TRID_REG(trident->card, CH_DX_CSO_ALPHA_FMS + 2));
} else { // ID_4DWAVE_NX
cso = (unsigned int) inl(TRID_REG(trident->card, CH_NX_DELTA_CSO)) & 0x00ffffff;
}
printk("(trident) get_dmac: chip reported %d\n", cso);
cso++;
if (trident->fmt & TRIDENT_FMT_16BIT)
cso *= 2;
if (trident->fmt & TRIDENT_FMT_STEREO)
cso *= 2;
return cso;
}
static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void trident_kick(unsigned long plop)
{
trident_interrupt(5, (void *)plop, NULL);
debug_timer.expires=jiffies+1;
add_timer(&debug_timer);
}
/* Stop recording (lock held) */
extern inline void __stop_adc(struct trident_state *s)
{
struct trident_card *trident = s->card;
M_printk("(trident) stopping ADC\n");
s->enable &= ~ADC_RUNNING;
trident_disable_voice_irq(trident, s->dma_adc.chan[0]);
outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
trident_disable_voice_irq(trident, s->dma_adc.chan[0]);
trident_stop_voice(trident, s->dma_adc.chan[0]);
ResetAinten(trident, s->dma_adc.chan[0]);
}
extern inline void stop_adc(struct trident_state *s)
{
unsigned long flags;
struct trident_card *trident = s->card;
spin_lock_irqsave(&trident->lock, flags);
__stop_adc(s);
spin_unlock_irqrestore(&trident->lock, flags);
}
/* stop playback (lock held) */
extern inline void __stop_dac(struct trident_state *s)
{
struct trident_card *trident = s->card;
M_printk("(trident) stopping DAC\n");
//trident_stop_voice(trident, s->dma_dac.chan[0]);
//trident_disable_voice_irq(trident, s->dma_dac.chan[0]);
trident_stop_voice(trident, s->dma_dac.chan[1]);
trident_disable_voice_irq(trident, s->dma_dac.chan[1]);
s->enable &= ~DAC_RUNNING;
}
extern inline void stop_dac(struct trident_state *s)
{
struct trident_card *trident = s->card;
unsigned long flags;
spin_lock_irqsave(&trident->lock, flags);
__stop_dac(s);
spin_unlock_irqrestore(&trident->lock, flags);
}
static void start_dac(struct trident_state *s)
{
unsigned long flags;
struct trident_card *trident = s->card;
spin_lock_irqsave(&s->card->lock, flags);
if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready)
{
s->enable |= DAC_RUNNING;
trident_enable_voice_irq(trident, s->dma_dac.chan[1]);
trident_start_voice(trident, s->dma_dac.chan[1]);
//trident_start_voice(trident, s->dma_dac.chan[0]);
M_printk("(trident) starting DAC\n");
}
spin_unlock_irqrestore(&s->card->lock, flags);
}
static void start_adc(struct trident_state *s)
{
unsigned long flags;
spin_lock_irqsave(&s->card->lock, flags);
if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize))
&& s->dma_adc.ready) {
s->enable |= ADC_RUNNING;
trident_enable_voice_irq(s->card, s->dma_adc.chan[0]);
outb(s->bDMAStart, TRID_REG(s->card, T4D_SBCTRL_SBE2R_SBDD));
trident_start_voice(s->card, s->dma_adc.chan[0]);
M_printk("(trident) starting ADC\n");
}
spin_unlock_irqrestore(&s->card->lock, flags);
}
/* --------------------------------------------------------------------- */
/* we allocate both buffers at once */
#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)
#define DMABUF_MINORDER 2
static void dealloc_dmabuf(struct dmabuf *db)
{
unsigned long map, mapend;
if (db->rawbuf)
{
M_printk("(trident) freeing %p\n",db->rawbuf);
/* undo marking the pages as reserved */
mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
clear_bit(PG_reserved, &mem_map[map].flags);
free_pages((unsigned long)db->rawbuf, db->buforder);
}
db->rawbuf = NULL;
db->mapped = db->ready = 0;
}
static int prog_dmabuf(struct trident_state *s, unsigned rec)
{
struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac;
unsigned rate = rec ? s->rateadc : s->ratedac;
int order;
unsigned bytepersec;
unsigned bufs;
unsigned long map, mapend;
unsigned char fmt;
unsigned long flags;
spin_lock_irqsave(&s->card->lock, flags);
fmt = s->fmt;
if (rec) {
s->enable &= ~TRIDENT_ENABLE_RE;
fmt >>= TRIDENT_ADC_SHIFT;
} else {
s->enable &= ~TRIDENT_ENABLE_PE;
fmt >>= TRIDENT_DAC_SHIFT;
}
spin_unlock_irqrestore(&s->card->lock, flags);
fmt &= TRIDENT_FMT_MASK;
db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
if (!db->rawbuf) {
void *rawbuf;
/* haha, this thing is hacked to hell and back.
this is so ugly. */
s->dma_dac.ready = s->dma_dac.mapped = 0;
s->dma_adc.ready = s->dma_adc.mapped = 0;
/* alloc as big a chunk as we can */
for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order)))
break;
if (!rawbuf)
return -ENOMEM;
/* we allocated both buffers */
s->dma_adc.rawbuf = rawbuf;
s->dma_dac.rawbuf = rawbuf + ( PAGE_SIZE << (order - 1) );
M_printk("(trident) allocated %ld bytes at %p\n",PAGE_SIZE<<order, db->rawbuf);
s->dma_adc.buforder = s->dma_dac.buforder = order - 1;
/* XXX these checks are silly now */
#if 0
if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << order) - 1)) & ~0xffff)
printk(KERN_DEBUG "trident: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n",
virt_to_bus(db->rawbuf), PAGE_SIZE << order);
#endif
if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << order) - 1) & ~0xffffff)
M_printk(KERN_DEBUG "(trident) DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n",
virt_to_bus(db->rawbuf), PAGE_SIZE << order);
/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << order) - 1);
for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
set_bit(PG_reserved, &mem_map[map].flags);
}
bytepersec = rate << sample_shift[fmt];
bufs = PAGE_SIZE << db->buforder;
if (db->ossfragshift) {
if ((1000 << db->ossfragshift) < bytepersec)
db->fragshift = ld2(bytepersec/1000);
else
db->fragshift = db->ossfragshift;
} else {
/* lets hand out reasonable big ass buffers by default */
db->fragshift = (db->buforder + PAGE_SHIFT -2);
#if 0
db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
if (db->fragshift < 3)
db->fragshift = 3;
#endif
}
db->numfrag = bufs >> db->fragshift;
while (db->numfrag < 4 && db->fragshift > 3) {
db->fragshift--;
db->numfrag = bufs >> db->fragshift;
}
db->fragsize = 1 << db->fragshift;
if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
db->numfrag = db->ossmaxfrags;
db->fragsamples = db->fragsize >> sample_shift[fmt];
db->dmasize = db->numfrag << db->fragshift;
memset(db->rawbuf, (fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80, db->dmasize);
spin_lock_irqsave(&s->card->lock, flags);
if (rec) {
trident_rec_setup(s, fmt, s->rateadc,
db->rawbuf, db->numfrag << db->fragshift);
} else {
trident_play_setup(s, fmt, s->ratedac,
db->rawbuf, db->numfrag << db->fragshift);
}
spin_unlock_irqrestore(&s->card->lock, flags);
db->ready = 1;
return 0;
}
/* only called by trident_write */
extern __inline__ void clear_advance(struct trident_state *s)
{
unsigned char c = ((s->fmt >> TRIDENT_DAC_SHIFT) & TRIDENT_FMT_16BIT) ? 0 : 0x80;
unsigned char *buf = s->dma_dac.rawbuf;
unsigned bsize = s->dma_dac.dmasize;
unsigned bptr = s->dma_dac.swptr;
unsigned len = s->dma_dac.fragsize;
if (bptr + len > bsize) {
unsigned x = bsize - bptr;
memset(buf + bptr, c, x);
/* account for wrapping? */
bptr = 0;
len -= x;
}
memset(buf + bptr, c, len);
}
/* call with spinlock held! */
static void trident_update_ptr(struct trident_state *s)
{
unsigned hwptr;
int diff;
/* update ADC pointer */
if (s->dma_adc.ready) {
hwptr = get_dmac(s) % s->dma_adc.dmasize;
diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;
s->dma_adc.hwptr = hwptr;
s->dma_adc.total_bytes += diff;
s->dma_adc.count += diff;
if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
wake_up(&s->dma_adc.wait);
if (!s->dma_adc.mapped) {
if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
s->enable &= ~TRIDENT_ENABLE_RE;
__stop_adc(s);
s->dma_adc.error++;
}
}
}
/* update DAC pointer */
if (s->dma_dac.ready)
{
/* this is so gross. */
hwptr = (/*s->dma_dac.dmasize -*/ get_dmaa(s)) % s->dma_dac.dmasize;
diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;
M_printk("(trident) updating dac: hwptr: %d diff: %d\n",hwptr,diff);
s->dma_dac.hwptr = hwptr;
s->dma_dac.total_bytes += diff;
if (s->dma_dac.mapped)
{
s->dma_dac.count += diff;
if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)
wake_up(&s->dma_dac.wait);
}
else
{
s->dma_dac.count -= diff;
M_printk("(trident) trident_update_ptr: diff: %d, count: %d\n", diff, s->dma_dac.count);
if (s->dma_dac.count <= 0)
{
s->enable &= ~TRIDENT_ENABLE_PE;
/* Lock already held */
__stop_dac(s);
/* brute force everyone back in sync, sigh */
s->dma_dac.count = 0;
s->dma_dac.swptr = 0;
s->dma_dac.hwptr = 0;
s->dma_dac.error++;
}
else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize &&
!s->dma_dac.endcleared)
{
clear_advance(s);
s->dma_dac.endcleared = 1;
}
if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize)
wake_up(&s->dma_dac.wait);
}
}
}
/*
* Trident interrupt handlers.
*/
static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct trident_state *s;
struct trident_card *c = (struct trident_card *)dev_id;
int i;
u32 event;
spin_lock(&c->lock);
event = inl(TRID_REG(c, T4D_MISCINT));
// if(event & 0x28)
// printk("IRQ %04X (%08lX, %08lX)\n", event, c->iobase, TRID_REG(c, T4D_MISCINT));
if(event & 8)
{
/* Midi - TODO */
}
if(event & 0x20)
{
/*
* Update the pointers for all channels we are running.
*/
for(i=0;i<NR_DSPS;i++)
{
s=&c->channels[i];
if(DidChannelInterrupt(c, i))
{
AckChannelInterrupt(c,i);
if(s->dev_audio != -1)
trident_update_ptr(s);
else
{
/* Spurious ? */
M_printk("(trident) spurious channel irq %d.\n", i);
trident_stop_voice(c, i);
trident_disable_voice_irq(c,i);
}
}
}
}
spin_unlock(&c->lock);
}
/* --------------------------------------------------------------------- */
static const char invalid_magic[] = KERN_CRIT "trident: invalid magic value in %s\n";
#define VALIDATE_MAGIC(FOO,MAG) \
({ \
if (!(FOO) || (FOO)->magic != MAG) { \
printk(invalid_magic,__FUNCTION__); \
return -ENXIO; \
} \
})
#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,TRIDENT_STATE_MAGIC)
#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,TRIDENT_CARD_MAGIC)
static void set_mixer(struct trident_card *card,unsigned int mixer, unsigned int val )
{
unsigned int left,right;
/* cleanse input a little */
right = ((val >> 8) & 0xff) ;
left = (val & 0xff) ;
if(right > 100) right = 100;
if(left > 100) left = 100;
card->mix.mixer_state[mixer]=(right << 8) | left;
card->mix.write_mixer(card,mixer,left,right);
}
static int mixer_ioctl(struct trident_card *card, unsigned int cmd, unsigned long arg)
{
unsigned long flags;
int i, val=0;
VALIDATE_CARD(card);
if (cmd == SOUND_MIXER_INFO) {
mixer_info info;
strncpy(info.id, card_names[card->card_type], sizeof(info.id));
strncpy(info.name,card_names[card->card_type],sizeof(info.name));
info.modify_counter = card->mix.modcnt;
if (copy_to_user((void *)arg, &info, sizeof(info)))
return -EFAULT;
return 0;
}
if (cmd == SOUND_OLD_MIXER_INFO) {
_old_mixer_info info;
strncpy(info.id, card_names[card->card_type], sizeof(info.id));
strncpy(info.name,card_names[card->card_type],sizeof(info.name));
if (copy_to_user((void *)arg, &info, sizeof(info)))
return -EFAULT;
return 0;
}
if (cmd == OSS_GETVERSION)
return put_user(SOUND_VERSION, (int *)arg);
if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))
return -EINVAL;
if (_IOC_DIR(cmd) == _IOC_READ) {
switch (_IOC_NR(cmd)) {
case SOUND_MIXER_RECSRC: /* give them the current record source */
if(!card->mix.recmask_io) {
val = 0;
} else {
spin_lock_irqsave(&card->lock, flags);
val = card->mix.recmask_io(card,1,0);
spin_unlock_irqrestore(&card->lock, flags);
}
break;
case SOUND_MIXER_DEVMASK: /* give them the supported mixers */
val = card->mix.supported_mixers;
break;
case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
val = card->mix.record_sources;
break;
case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
val = card->mix.stereo_mixers;
break;
case SOUND_MIXER_CAPS:
val = SOUND_CAP_EXCL_INPUT;
break;
default: /* read a specific mixer */
i = _IOC_NR(cmd);
if ( ! supported_mixer(card,i))
return -EINVAL;
/* do we ever want to touch the hardware? */
/* spin_lock_irqsave(&s->lock, flags);
val = card->mix.read_mixer(card,i);
spin_unlock_irqrestore(&s->lock, flags);*/
val = card->mix.mixer_state[i];
/* printk("returned 0x%x for mixer %d\n",val,i);*/
break;
}
return put_user(val,(int *)arg);
}
if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ))
return -EINVAL;
card->mix.modcnt++;
get_user_ret(val, (int *)arg, -EFAULT);
switch (_IOC_NR(cmd)) {
case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
if (!card->mix.recmask_io) return -EINVAL;
if(! (val &= card->mix.record_sources)) return -EINVAL;
spin_lock_irqsave(&card->lock, flags);
card->mix.recmask_io(card,0,val);
spin_unlock_irqrestore(&card->lock, flags);
return 0;
default:
i = _IOC_NR(cmd);
if ( ! supported_mixer(card,i))
return -EINVAL;
spin_lock_irqsave(&card->lock, flags);
set_mixer(card,i,val);
spin_unlock_irqrestore(&card->lock, flags);
return 0;
}
}
/* --------------------------------------------------------------------- */
static loff_t trident_llseek(struct file *file, loff_t offset, int origin)
{
return -ESPIPE;
}
/* --------------------------------------------------------------------- */
static int trident_open_mixdev(struct inode *inode, struct file *file)
{
int minor = MINOR(inode->i_rdev);
struct trident_card *card = devs;
while (card && card->dev_mixer != minor)
card = card->next;
if (!card)
return -ENODEV;
file->private_data = card;
//FIXME put back in
//MOD_INC_USE_COUNT;
return 0;
}
static int trident_release_mixdev(struct inode *inode, struct file *file)
{
struct trident_card *card = (struct trident_card *)file->private_data;
VALIDATE_CARD(card);
//FIXME put back in
//MOD_DEC_USE_COUNT;
return 0;
}
static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct trident_card *card = (struct trident_card *)file->private_data;
VALIDATE_CARD(card);
return mixer_ioctl(card, cmd, arg);
}
static /*const*/ struct file_operations trident_mixer_fops = {
&trident_llseek,
NULL, /* read */
NULL, /* write */
NULL, /* readdir */
NULL, /* poll */
&trident_ioctl_mixdev,
NULL, /* mmap */
&trident_open_mixdev,
NULL, /* flush */
&trident_release_mixdev,
NULL, /* fsync */
NULL, /* fasync */
NULL, /* check_media_change */
NULL, /* revalidate */
NULL, /* lock */
};
/* --------------------------------------------------------------------- */
static int drain_dac(struct trident_state *s, int nonblock)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
int count;
signed long tmo;
if (s->dma_dac.mapped || !s->dma_dac.ready)
return 0;
current->state = TASK_INTERRUPTIBLE;
add_wait_queue(&s->dma_dac.wait, &wait);
for (;;)
{
spin_lock_irqsave(&s->card->lock, flags);
count = s->dma_dac.count;
spin_unlock_irqrestore(&s->card->lock, flags);
if (count <= 0)
break;
if (signal_pending(current))
break;
if (nonblock)
{
remove_wait_queue(&s->dma_dac.wait, &wait);
current->state = TASK_RUNNING;
return -EBUSY;
}
tmo = (count * HZ) / s->ratedac;
tmo >>= sample_shift[(s->fmt >> TRIDENT_DAC_SHIFT) & TRIDENT_FMT_MASK];
/* XXX this is just broken. someone is waking us up alot, or schedule_timeout is broken.
or something. who cares. - zach */
if (!schedule_timeout(tmo ? tmo : 1) && tmo)
printk(KERN_DEBUG "trident: dma timed out?? %ld\n",jiffies);
}
remove_wait_queue(&s->dma_dac.wait, &wait);
current->state = TASK_RUNNING;
if (signal_pending(current))
return -ERESTARTSYS;
return 0;
}
/* --------------------------------------------------------------------- */
/* in this loop, dma_adc.count signifies the amount of data thats waiting
to be copied to the user's buffer. it is filled by the interrupt
handler and drained by this loop. */
static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
struct trident_state *s = (struct trident_state *)file->private_data;
ssize_t ret;
unsigned long flags;
unsigned swptr;
int cnt;
VALIDATE_STATE(s);
if (ppos != &file->f_pos)
return -ESPIPE;
if (s->dma_adc.mapped)
return -ENXIO;
if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
return ret;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
ret = 0;
while (count > 0) {
spin_lock_irqsave(&s->card->lock, flags);
/* remember, all these things are expressed in bytes to be
sent to the user.. hence the evil / 2 down below */
swptr = s->dma_adc.swptr;
cnt = s->dma_adc.dmasize-swptr;
if (s->dma_adc.count < cnt)
cnt = s->dma_adc.count;
spin_unlock_irqrestore(&s->card->lock, flags);
if (cnt > count)
cnt = count;
if (cnt <= 0) {
start_adc(s);
if (file->f_flags & O_NONBLOCK)
{
ret = ret ? ret : -EAGAIN;
return ret;
}
if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) {
M_printk(KERN_DEBUG "(trident) read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count,
s->dma_adc.hwptr, s->dma_adc.swptr);
stop_adc(s);
spin_lock_irqsave(&s->card->lock, flags);
// set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift);
s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0;
spin_unlock_irqrestore(&s->card->lock, flags);
}
if (signal_pending(current))
{
ret = ret ? ret : -ERESTARTSYS;
return ret;
}
continue;
}
if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
ret = ret ? ret : -EFAULT;
return ret;
}
swptr = (swptr + cnt) % s->dma_adc.dmasize;
spin_lock_irqsave(&s->card->lock, flags);
s->dma_adc.swptr = swptr;
s->dma_adc.count -= cnt;
spin_unlock_irqrestore(&s->card->lock, flags);
count -= cnt;
buffer += cnt;
ret += cnt;
start_adc(s);
}
return ret;
}
static ssize_t trident_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
struct trident_state *s = (struct trident_state *)file->private_data;
ssize_t ret;
unsigned long flags;
unsigned swptr;
int cnt;
int mode = (s->fmt >> TRIDENT_DAC_SHIFT) & TRIDENT_FMT_MASK;
M_printk("(trident) trident_write: count %d\n", count);
VALIDATE_STATE(s);
if (ppos != &file->f_pos)
return -ESPIPE;
if (s->dma_dac.mapped)
return -ENXIO;
if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
return ret;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
ret = 0;
while (count > 0) {
spin_lock_irqsave(&s->card->lock, flags);
if (s->dma_dac.count < 0)
{
s->dma_dac.count = 0;
s->dma_dac.swptr = s->dma_dac.hwptr;
}
swptr = s->dma_dac.swptr;
cnt = s->dma_dac.dmasize-swptr;
if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
cnt = s->dma_dac.dmasize - s->dma_dac.count;
spin_unlock_irqrestore(&s->card->lock, flags);
if (cnt > count)
cnt = count;
if (cnt <= 0)
{
/* buffer is full, wait for it to be played */
start_dac(s);
if (file->f_flags & O_NONBLOCK)
{
if(!ret) ret = -EAGAIN;
return ret;
}
if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ))
{
M_printk(KERN_DEBUG
"trident: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, s->dma_dac.hwptr,
s->dma_dac.swptr);
stop_dac(s);
spin_lock_irqsave(&s->card->lock, flags);
// set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift);
s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0;
spin_unlock_irqrestore(&s->card->lock, flags);
}
if (signal_pending(current))
{
if (!ret) ret = -ERESTARTSYS;
return ret;
}
continue;
}
if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt))
{
if (!ret)
ret = -EFAULT;
return ret;
}
swptr = (swptr + cnt) % s->dma_dac.dmasize;
spin_lock_irqsave(&s->card->lock, flags);
s->dma_dac.swptr = swptr;
s->dma_dac.count += cnt;
s->dma_dac.endcleared = 0;
spin_unlock_irqrestore(&s->card->lock, flags);
count -= cnt;
buffer += cnt;
ret += cnt;
start_dac(s);
}
return ret;
}
static unsigned int trident_poll(struct file *file, struct poll_table_struct *wait)
{
struct trident_state *s = (struct trident_state *)file->private_data;
unsigned long flags;
unsigned int mask = 0;
VALIDATE_STATE(s);
if (file->f_mode & FMODE_WRITE)
poll_wait(file, &s->dma_dac.wait, wait);
if (file->f_mode & FMODE_READ)
poll_wait(file, &s->dma_adc.wait, wait);
spin_lock_irqsave(&s->card->lock, flags);
trident_update_ptr(s);
if (file->f_mode & FMODE_READ) {
if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
mask |= POLLIN | POLLRDNORM;
}
if (file->f_mode & FMODE_WRITE) {
if (s->dma_dac.mapped) {
if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)
mask |= POLLOUT | POLLWRNORM;
} else {
if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize)
mask |= POLLOUT | POLLWRNORM;
}
}
spin_unlock_irqrestore(&s->card->lock, flags);
return mask;
}
/* this needs to be fixed to deal with the dual apus/buffers */
#if 0
static int trident_mmap(struct file *file, struct vm_area_struct *vma)
{
struct trident_state *s = (struct trident_state *)file->private_data;
struct dmabuf *db;
int ret;
unsigned long size;
VALIDATE_STATE(s);
if (vma->vm_flags & VM_WRITE) {
if ((ret = prog_dmabuf(s, 1)) != 0)
return ret;
db = &s->dma_dac;
} else if (vma->vm_flags & VM_READ) {
if ((ret = prog_dmabuf(s, 0)) != 0)
return ret;
db = &s->dma_adc;
} else
return -EINVAL;
if (vma->vm_offset != 0)
return -EINVAL;
size = vma->vm_end - vma->vm_start;
if (size > (PAGE_SIZE << db->buforder))
return -EINVAL;
if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
return -EAGAIN;
db->mapped = 1;
return 0;
}
#endif
static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct trident_state *s = (struct trident_state *)file->private_data;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
int val, mapped, ret;
unsigned char fmtm, fmtd;
/* printk("trident: trident_ioctl: cmd %d\n", cmd);*/
VALIDATE_STATE(s);
mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
switch (cmd)
{
case OSS_GETVERSION:
return put_user(SOUND_VERSION, (int *)arg);
case SNDCTL_DSP_SYNC:
if (file->f_mode & FMODE_WRITE)
return drain_dac(s, file->f_flags & O_NONBLOCK);
return 0;
case SNDCTL_DSP_SETDUPLEX:
/* XXX fix */
return 0;
case SNDCTL_DSP_GETCAPS:
return put_user(0/*DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP*/, (int *)arg);
case SNDCTL_DSP_RESET:
if (file->f_mode & FMODE_WRITE)
{
stop_dac(s);
synchronize_irq();
s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0;
}
if (file->f_mode & FMODE_READ)
{
stop_adc(s);
synchronize_irq();
s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
}
return 0;
case SNDCTL_DSP_SPEED:
get_user_ret(val, (int *)arg, -EFAULT);
if (val >= 0)
{
if (file->f_mode & FMODE_READ)
{
stop_adc(s);
s->dma_adc.ready = 0;
trident_set_adc_rate(s, val, 1);
}
if (file->f_mode & FMODE_WRITE)
{
stop_dac(s);
s->dma_dac.ready = 0;
trident_set_dac_rate(s, val, 1);
}
}
return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
case SNDCTL_DSP_STEREO:
get_user_ret(val, (int *)arg, -EFAULT);
fmtd = 0;
fmtm = ~0;
if (file->f_mode & FMODE_READ)
{
stop_adc(s);
s->dma_adc.ready = 0;
if (val)
fmtd |= TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT;
else
fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT);
}
if (file->f_mode & FMODE_WRITE)
{
stop_dac(s);
s->dma_dac.ready = 0;
if (val)
fmtd |= TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT;
else
fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT);
}
set_fmt(s, fmtm, fmtd);
return 0;
case SNDCTL_DSP_CHANNELS:
get_user_ret(val, (int *)arg, -EFAULT);
if (val != 0)
{
fmtd = 0;
fmtm = ~0;
if (file->f_mode & FMODE_READ)
{
stop_adc(s);
s->dma_adc.ready = 0;
if (val >= 2)
fmtd |= TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT;
else
fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT);
}
if (file->f_mode & FMODE_WRITE)
{
stop_dac(s);
s->dma_dac.ready = 0;
if (val >= 2)
fmtd |= TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT;
else
fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT);
}
set_fmt(s, fmtm, fmtd);
}
return put_user((s->fmt & ((file->f_mode & FMODE_READ) ?
(TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT) :
(TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT))) ? 2 : 1, (int *)arg);
case SNDCTL_DSP_GETFMTS: /* Returns a mask */
return put_user(AFMT_S8|AFMT_S16_LE, (int *)arg);
case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
get_user_ret(val, (int *)arg, -EFAULT);
if (val != AFMT_QUERY)
{
fmtd = 0;
fmtm = ~0;
if (file->f_mode & FMODE_READ)
{
stop_adc(s);
s->dma_adc.ready = 0;
/* fixed at 16bit for now */
fmtd |= TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT;
#if 0
if (val == AFMT_S16_LE)
fmtd |= TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT;
else
fmtm &= ~(TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT);
#endif
}
if (file->f_mode & FMODE_WRITE)
{
stop_dac(s);
s->dma_dac.ready = 0;
if (val == AFMT_S16_LE)
fmtd |= TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT;
else
fmtm &= ~(TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT);
}
set_fmt(s, fmtm, fmtd);
}
return put_user((s->fmt & ((file->f_mode & FMODE_READ) ?
(TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT) :
(TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT))) ?
AFMT_S16_LE : AFMT_S8, (int *)arg);
case SNDCTL_DSP_POST:
return 0;
case SNDCTL_DSP_GETTRIGGER:
val = 0;
if (file->f_mode & FMODE_READ && s->enable & TRIDENT_ENABLE_RE)
val |= PCM_ENABLE_INPUT;
if (file->f_mode & FMODE_WRITE && s->enable & TRIDENT_ENABLE_PE)
val |= PCM_ENABLE_OUTPUT;
return put_user(val, (int *)arg);
case SNDCTL_DSP_SETTRIGGER:
get_user_ret(val, (int *)arg, -EFAULT);
if (file->f_mode & FMODE_READ)
{
if (val & PCM_ENABLE_INPUT)
{
if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
return ret;
start_adc(s);
}
else
stop_adc(s);
}
if (file->f_mode & FMODE_WRITE)
{
if (val & PCM_ENABLE_OUTPUT)
{
if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
return ret;
start_dac(s);
}
else
stop_dac(s);
}
return 0;
case SNDCTL_DSP_GETOSPACE:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
if (!(s->enable & TRIDENT_ENABLE_PE) && (val = prog_dmabuf(s, 0)) != 0)
return val;
spin_lock_irqsave(&s->card->lock, flags);
trident_update_ptr(s);
abinfo.fragsize = s->dma_dac.fragsize;
abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count;
abinfo.fragstotal = s->dma_dac.numfrag;
abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
spin_unlock_irqrestore(&s->card->lock, flags);
return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_GETISPACE:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
if (!(s->enable & TRIDENT_ENABLE_RE) && (val = prog_dmabuf(s, 1)) != 0)
return val;
spin_lock_irqsave(&s->card->lock, flags);
trident_update_ptr(s);
abinfo.fragsize = s->dma_adc.fragsize;
abinfo.bytes = s->dma_adc.count;
abinfo.fragstotal = s->dma_adc.numfrag;
abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;
spin_unlock_irqrestore(&s->card->lock, flags);
return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_NONBLOCK:
file->f_flags |= O_NONBLOCK;
return 0;
case SNDCTL_DSP_GETODELAY:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
spin_lock_irqsave(&s->card->lock, flags);
trident_update_ptr(s);
val = s->dma_dac.count;
spin_unlock_irqrestore(&s->card->lock, flags);
return put_user(val, (int *)arg);
case SNDCTL_DSP_GETIPTR:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
spin_lock_irqsave(&s->card->lock, flags);
trident_update_ptr(s);
cinfo.bytes = s->dma_adc.total_bytes;
cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
cinfo.ptr = s->dma_adc.hwptr;
if (s->dma_adc.mapped)
s->dma_adc.count &= s->dma_adc.fragsize-1;
spin_unlock_irqrestore(&s->card->lock, flags);
return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
case SNDCTL_DSP_GETOPTR:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
spin_lock_irqsave(&s->card->lock, flags);
trident_update_ptr(s);
cinfo.bytes = s->dma_dac.total_bytes;
cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift;
cinfo.ptr = s->dma_dac.hwptr;
if (s->dma_dac.mapped)
s->dma_dac.count &= s->dma_dac.fragsize-1;
spin_unlock_irqrestore(&s->card->lock, flags);
return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
case SNDCTL_DSP_GETBLKSIZE:
if (file->f_mode & FMODE_WRITE)
{
if ((val = prog_dmabuf(s, 0)))
return val;
return put_user(s->dma_dac.fragsize, (int *)arg);
}
if ((val = prog_dmabuf(s, 1)))
return val;
return put_user(s->dma_adc.fragsize, (int *)arg);
case SNDCTL_DSP_SETFRAGMENT:
get_user_ret(val, (int *)arg, -EFAULT);
if (file->f_mode & FMODE_READ)
{
s->dma_adc.ossfragshift = val & 0xffff;
s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
if (s->dma_adc.ossfragshift < 4)
s->dma_adc.ossfragshift = 4;
if (s->dma_adc.ossfragshift > 15)
s->dma_adc.ossfragshift = 15;
if (s->dma_adc.ossmaxfrags < 4)
s->dma_adc.ossmaxfrags = 4;
}
if (file->f_mode & FMODE_WRITE)
{
s->dma_dac.ossfragshift = val & 0xffff;
s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
if (s->dma_dac.ossfragshift < 4)
s->dma_dac.ossfragshift = 4;
if (s->dma_dac.ossfragshift > 15)
s->dma_dac.ossfragshift = 15;
if (s->dma_dac.ossmaxfrags < 4)
s->dma_dac.ossmaxfrags = 4;
}
return 0;
case SNDCTL_DSP_SUBDIVIDE:
if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
(file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
return -EINVAL;
get_user_ret(val, (int *)arg, -EFAULT);
if (val != 1 && val != 2 && val != 4)
return -EINVAL;
if (file->f_mode & FMODE_READ)
s->dma_adc.subdivision = val;
if (file->f_mode & FMODE_WRITE)
s->dma_dac.subdivision = val;
return 0;
case SOUND_PCM_READ_RATE:
return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
case SOUND_PCM_READ_CHANNELS:
return put_user((s->fmt & ((file->f_mode & FMODE_READ) ?
(TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT) :
(TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT))) ? 2 : 1, (int *)arg);
case SOUND_PCM_READ_BITS:
return put_user((s->fmt & ((file->f_mode & FMODE_READ) ?
(TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT) :
(TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT))) ? 16 : 8, (int *)arg);
case SOUND_PCM_WRITE_FILTER:
case SNDCTL_DSP_SETSYNCRO:
case SOUND_PCM_READ_FILTER:
return -EINVAL;
}
return -EINVAL;
}
static int trident_open(struct inode *inode, struct file *file)
{
int minor = MINOR(inode->i_rdev);
struct trident_card *c = devs;
struct trident_state *s = NULL, *sp;
int i;
unsigned char fmtm = ~0, fmts = 0;
/*
* Scan the cards and find the channel. We only
* do this at open time so it is ok
*/
while (c!=NULL)
{
for(i=0;i<NR_DSPS;i++)
{
sp=&c->channels[i];
if(sp->dev_audio < 0)
continue;
if((sp->dev_audio ^ minor) & ~0xf)
continue;
s=sp;
}
c=c->next;
}
if (!s)
return -ENODEV;
VALIDATE_STATE(s);
file->private_data = s;
/* wait for device to become free */
down(&s->open_sem);
while (s->open_mode & file->f_mode)
{
if (file->f_flags & O_NONBLOCK)
{
up(&s->open_sem);
return -EWOULDBLOCK;
}
up(&s->open_sem);
interruptible_sleep_on(&s->open_wait);
if (signal_pending(current))
return -ERESTARTSYS;
down(&s->open_sem);
}
if (file->f_mode & FMODE_READ)
{
/*
fmtm &= ~((TRIDENT_FMT_STEREO | TRIDENT_FMT_16BIT) << TRIDENT_ADC_SHIFT);
if ((minor & 0xf) == SND_DEV_DSP16)
fmts |= TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT; */
fmtm = (TRIDENT_FMT_STEREO|TRIDENT_FMT_16BIT) << TRIDENT_ADC_SHIFT;
s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
trident_set_adc_rate(s, 8000, 0);
}
if (file->f_mode & FMODE_WRITE)
{
fmtm &= ~((TRIDENT_FMT_STEREO | TRIDENT_FMT_16BIT) << TRIDENT_DAC_SHIFT);
if ((minor & 0xf) == SND_DEV_DSP16)
fmts |= TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT;
s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0;
trident_set_dac_rate(s, 8000, 1);
}
set_fmt(s, fmtm, fmts);
s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
up(&s->open_sem);
//FIXME put back in
//MOD_INC_USE_COUNT;
return 0;
}
static int trident_release(struct inode *inode, struct file *file)
{
struct trident_state *s = (struct trident_state *)file->private_data;
VALIDATE_STATE(s);
if (file->f_mode & FMODE_WRITE)
drain_dac(s, file->f_flags & O_NONBLOCK);
down(&s->open_sem);
if (file->f_mode & FMODE_WRITE) {
stop_dac(s);
}
if (file->f_mode & FMODE_READ) {
stop_adc(s);
}
/* free our shared dma buffers */
dealloc_dmabuf(&s->dma_adc);
dealloc_dmabuf(&s->dma_dac);
s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
/* we're covered by the open_sem */
up(&s->open_sem);
wake_up(&s->open_wait);
//FIXME put back in
//MOD_DEC_USE_COUNT;
return 0;
}
static /*const*/ struct file_operations trident_audio_fops = {
&trident_llseek,
&trident_read,
&trident_write,
NULL, /* readdir */
&trident_poll,
&trident_ioctl,
NULL, /* XXX &trident_mmap, */
&trident_open,
NULL, /* flush */
&trident_release,
NULL, /* fsync */
NULL, /* fasync */
NULL, /* check_media_change */
NULL, /* revalidate */
NULL, /* lock */
};
#ifdef CONFIG_APM
int trident_apm_callback(apm_event_t ae) {
}
#endif
/* --------------------------------------------------------------------- */
static int trident_install(struct pci_dev *pcidev, int card_type)
{
u16 w;
u32 l;
unsigned long iobase;
int i;
struct trident_card *card;
struct trident_state *trident;
int num = 0;
u32 ChanDwordCount;
iobase = pcidev->resource[0].start;
if(check_region(iobase, 256))
{
M_printk(KERN_WARNING "(trident) can't allocate 256 bytes I/O at 0x%4.4lx\n", iobase);
return 0;
}
/* this was tripping up some machines */
if(pcidev->irq == 0)
{
printk(KERN_WARNING "(trident) pci subsystem reports irq 0, this might not be correct.\n");
}
/* just to be sure */
pci_set_master(pcidev);
pci_read_config_word(pcidev, PCI_COMMAND, &w);
if((w&(PCI_COMMAND_IO|PCI_COMMAND_MASTER)) != (PCI_COMMAND_IO|PCI_COMMAND_MASTER))
{
printk("(trident) BIOS did not enable I/O access.\n");
w|=PCI_COMMAND_IO|PCI_COMMAND_MASTER;
pci_write_config_word(pcidev, PCI_COMMAND,w);
}
card = kmalloc(sizeof(struct trident_card), GFP_KERNEL);
if(card == NULL)
{
printk(KERN_WARNING "(trident) out of memory\n");
return 0;
}
memset(card, 0, sizeof(*card));
#ifdef CONFIG_APM
printk("(trident) apm_reg_callback: %d\n",apm_register_callback(trident_apm_callback));
#endif
card->iobase = iobase;
card->card_type = card_type;
card->irq = pcidev->irq;
card->next = devs;
card->magic = TRIDENT_CARD_MAGIC;
devs = card;
ChanDwordCount = card->ChanDwordCount = 2;
card->ChanPCM = 32;
card->ChRegs.lpChStart = card->ChRegs.data;
card->ChRegs.lpChStop = card->ChRegs.lpChStart + ChanDwordCount;
card->ChRegs.lpChAint = card->ChRegs.lpChStop + ChanDwordCount;
card->ChRegs.lpChAinten = card->ChRegs.lpChAint + ChanDwordCount;
card->ChRegs.lpAChStart = card->ChRegs.lpChAinten + ChanDwordCount;
card->ChRegs.lpAChStop = card->ChRegs.lpAChStart + ChanDwordCount;
card->ChRegs.lpAChAint = card->ChRegs.lpAChStop + ChanDwordCount;
card->ChRegs.lpAChAinten = card->ChRegs.lpAChAint + ChanDwordCount;
// Assign addresses.
card->ChRegs.lpAChStart[0] = T4D_START_A;
card->ChRegs.lpAChStop[0] = T4D_STOP_A;
card->ChRegs.lpAChAint[0] = T4D_AINT_A;
card->ChRegs.lpAChAinten[0] = T4D_AINTEN_A;
card->ChRegs.lpAChStart[1] = T4D_START_B;
card->ChRegs.lpAChStop[1] = T4D_STOP_B;
card->ChRegs.lpAChAint[1] = T4D_AINT_B;
card->ChRegs.lpAChAinten[1] = T4D_AINTEN_B;
outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
trident_ac97_set(card, 0x0L , 0L);
trident_ac97_set(card, 0x02L , 0L);
trident_ac97_set(card, 0x18L , 0L);
if(card->card_type == TYPE_4DWAVE_NX)
{
// Enable rear channels
outl(0x12, TRID_REG(card, NX_ACR0_AC97_COM_STAT));
// ...or not, since they sound ugly. :)
// outl(0x02, TRID_REG(card, NX_ACR0_AC97_COM_STAT));
// S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled
// outl(0x200004, TRID_REG(card, NX_SPCSTATUS));
// Disable S/PDIF out, 48khz only from ac97 fifo
// outb(0x00, TRID_REG(card, NX_SPCTRL_SPCSO + 3));
}
else
{
outl(0x02, TRID_REG(card, DX_ACR2_AC97_COM_STAT));
}
for(i=0;i<NR_DSPS;i++)
{
struct trident_state *s=&card->channels[i];
s->card = card;
init_waitqueue_head(&s->dma_adc.wait);
init_waitqueue_head(&s->dma_dac.wait);
init_waitqueue_head(&s->open_wait);
init_MUTEX(&s->open_sem);
s->magic = TRIDENT_STATE_MAGIC;
s->channel = i;
if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf)
printk("(trident) BOTCH!\n");
/*
* Now allocate the hardware resources
*/
//s->dma_dac.chan[0] = AllocateChannelPCM(card);
s->dma_dac.chan[1] = AllocateChannelPCM(card);
//s->dma_adc.chan[0] = AllocateChannelPCM(card);
/* register devices */
if ((s->dev_audio = register_sound_dsp(&trident_audio_fops, -1)) < 0)
break;
}
num = i;
/* clear the rest if we ran out of slots to register */
for(;i<NR_DSPS;i++)
{
struct trident_state *s=&card->channels[i];
s->dev_audio = -1;
}
trident = &card->channels[0];
/*
* Ok card ready. Begin setup proper
*/
printk(KERN_INFO "(trident) Configuring %s found at IO 0x%04lX IRQ %d\n",
card_names[card_type],card->iobase,card->irq);
/* stake our claim on the iospace */
request_region(iobase, 256, card_names[card_type]);
/*
* Reset the CODEC
*/
trident_ac97_init(card);
if ((card->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0)
{
printk("(trident) couldn't register mixer!\n");
}
else
{
int i;
for(i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++)
{
struct mixer_defaults *md = &mixer_defaults[i];
if(md->mixer == -1)
break;
if(!supported_mixer(card,md->mixer))
continue;
set_mixer(card,md->mixer,md->value);
}
}
if(request_irq(card->irq, trident_interrupt, SA_SHIRQ, card_names[card_type], card))
{
printk(KERN_ERR "(trident) unable to allocate irq %d,\n", card->irq);
unregister_sound_mixer(card->dev_mixer);
for(i=0;i<NR_DSPS;i++)
{
struct trident_state *s = &card->channels[i];
if(s->dev_audio != -1)
unregister_sound_dsp(s->dev_audio);
}
release_region(card->iobase, 256);
kfree(card);
return 0;
}
init_timer(&debug_timer);
debug_timer.function = trident_kick;
debug_timer.data = (unsigned long)card;
debug_timer.expires = jiffies+1;
// add_timer(&debug_timer);
printk("(trident) %d channels configured.\n", num);
EnableEndInterrupts(card);
return 1;
}
#ifdef MODULE
int init_module(void)
#else
int __init init_trident(void)
#endif
{
struct pci_dev *pcidev = NULL;
int foundone = 0;
if (!pci_present()) /* No PCI bus in this machine! */
return -ENODEV;
printk(KERN_INFO "(trident) version " DRIVER_VERSION " time " __TIME__ " " __DATE__ "\n");
pcidev = NULL;
/*
* Find the 4DWave DX
*/
while( (pcidev = pci_find_device(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX, pcidev))!=NULL
&&
( trident_install(pcidev, TYPE_4DWAVE_DX) )) {
foundone=1;
}
/*
* Find the 4DWave NX
*/
while((pcidev = pci_find_device(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX, pcidev))!=NULL
&&
( trident_install(pcidev, TYPE_4DWAVE_NX) )) {
foundone=1;
}
if( ! foundone )
return -ENODEV;
return 0;
}
/* --------------------------------------------------------------------- */
#ifdef MODULE
MODULE_AUTHOR("Alan Cox <alan@redhat.com>");
MODULE_DESCRIPTION("Trident 4DWave Driver");
#ifdef M_DEBUG
MODULE_PARM(debug,"i");
#endif
void cleanup_module(void)
{
struct trident_card *s;
#ifdef CONFIG_APM
apm_unregister_callback(trident_apm_callback);
#endif
del_timer(&debug_timer);
while ((s = devs)) {
int i;
devs = devs->next;
/* Kill interrupts, and SP/DIF */
DisableEndInterrupts(s);
if(s->card_type == TYPE_4DWAVE_NX)
outb(0x00, TRID_REG(s, NX_SPCTRL_SPCSO+3));
free_irq(s->irq, s);
unregister_sound_mixer(s->dev_mixer);
for(i=0;i<NR_DSPS;i++)
{
struct trident_state *trident = &s->channels[i];
if(trident->dev_audio != -1)
unregister_sound_dsp(trident->dev_audio);
}
release_region(s->iobase, 256);
kfree(s);
}
printk("(trident) unloading\n");
}
#endif /* MODULE */
#ifndef __TRID4DWAVE_H
#define __TRID4DWAVE_H
/*
* audio@tridentmicro.com
* Fri Feb 19 15:55:28 MST 1999
* Definitions for Trident 4DWave DX/NX chips
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef PCI_VENDOR_ID_TRIDENT
#define PCI_VENDOR_ID_TRIDENT 0x1023
#endif
#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_DX
#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000
#endif
#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_NX
#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001
#endif
/*
* Direct registers
*/
#ifndef FALSE
#define FALSE 0
#define TRUE 1
#endif
#define TRID_REG( trident, x ) ( (trident) -> iobase + (x) )
#define CHANNEL_REGS 5
#define CHANNEL_START 0xe0 // The first bytes of the contiguous register space.
#define ID_4DWAVE_DX 0x2000
#define ID_4DWAVE_NX 0x2001
// Register definitions
// Global registers
// T2 legacy dma control registers.
#define LEGACY_DMAR0 0x00 // ADR0
#define LEGACY_DMAR4 0x04 // CNT0
#define LEGACY_DMAR11 0x0b // MOD
#define LEGACY_DMAR15 0x0f // MMR
#define T4D_START_A 0x80
#define T4D_STOP_A 0x84
#define T4D_DLY_A 0x88
#define T4D_SIGN_CSO_A 0x8c
#define T4D_CSPF_A 0x90
#define T4D_CEBC_A 0x94
#define T4D_AINT_A 0x98
#define T4D_AINTEN_A 0x9c
#define T4D_LFO_GC_CIR 0xa0
#define T4D_MUSICVOL_WAVEVOL 0xa8
#define T4D_SBDELTA_DELTA_R 0xac
#define T4D_MISCINT 0xb0
#define T4D_START_B 0xb4
#define T4D_STOP_B 0xb8
#define T4D_SBBL_SBCL 0xc0
#define T4D_SBCTRL_SBE2R_SBDD 0xc4
#define T4D_AINT_B 0xd8
#define T4D_AINTEN_B 0xdc
// MPU-401 UART
#define T4D_MPU401_BASE 0x20
#define T4D_MPUR0 0x20
#define T4D_MPUR1 0x21
#define T4D_MPUR2 0x22
#define T4D_MPUR3 0x23
// S/PDIF Registers
#define NX_SPCTRL_SPCSO 0x24
#define NX_SPLBA 0x28
#define NX_SPESO 0x2c
#define NX_SPCSTATUS 0x64
// Channel Registers
#define CH_DX_CSO_ALPHA_FMS 0xe0
#define CH_DX_ESO_DELTA 0xe8
#define CH_DX_FMC_RVOL_CVOL 0xec
#define CH_NX_DELTA_CSO 0xe0
#define CH_NX_DELTA_ESO 0xe8
#define CH_NX_ALPHA_FMS_FMC_RVOL_CVOL 0xec
#define CH_LBA 0xe4
#define CH_GVSEL_PAN_VOL_CTRL_EC 0xf0
// AC-97 Registers
#define DX_ACR0_AC97_W 0x40
#define DX_ACR1_AC97_R 0x44
#define DX_ACR2_AC97_COM_STAT 0x48
#define NX_ACR0_AC97_COM_STAT 0x40
#define NX_ACR1_AC97_W 0x44
#define NX_ACR2_AC97_R_PRIMARY 0x48
#define NX_ACR3_AC97_R_SECONDARY 0x4c
#define AC97_SIGMATEL_DAC2INVERT 0x6E
#define AC97_SIGMATEL_BIAS1 0x70
#define AC97_SIGMATEL_BIAS2 0x72
#define AC97_SIGMATEL_CIC1 0x76
#define AC97_SIGMATEL_CIC2 0x78
#endif /* __TRID4DWAVE_H */
......@@ -1449,6 +1449,8 @@ int block_write_range(struct dentry *dentry, struct page *page,
err = copy_from_user(kaddr+from, buf, to-from);
if (to < zeroto)
memset(kaddr+to, 0, zeroto-to);
else
zeroto = to;
if (err < 0)
goto out;
/*
......
......@@ -10,6 +10,6 @@ if [ "$CONFIG_NCPFS_OS2_NS" = "y" ]; then
bool ' Lowercase DOS filenames' CONFIG_NCPFS_SMALLDOS
fi
bool ' Allow mounting of volume subdirectories' CONFIG_NCPFS_MOUNT_SUBDIR
# bool ' NDS interserver authentication support' CONFIG_NCPFS_NDS_DOMAINS
bool ' NDS authentication support' CONFIG_NCPFS_NDS_DOMAINS
bool ' Use Native Language Support' CONFIG_NCPFS_NLS
bool ' Enable symbolic links and execute flags' CONFIG_NCPFS_EXTRAS
......@@ -487,7 +487,7 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
filp->f_pos = 2;
}
page = find_lock_page(&inode->i_data, 0);
page = grab_cache_page(&inode->i_data, 0);
if (!page)
goto read_really;
......@@ -516,7 +516,7 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
for (;;) {
if (ctl.ofs != 0) {
ctl.page = grab_cache_page(&inode->i_data, ctl.ofs);
ctl.page = find_lock_page(&inode->i_data, ctl.ofs);
if (!ctl.page)
goto invalid_cache;
ctl.cache = (union ncp_dir_cache *)
......@@ -661,7 +661,7 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
ctl.cache = NULL;
ctl.idx -= NCP_DIRCACHE_SIZE;
ctl.ofs += 1;
ctl.page = find_lock_page(&inode->i_data, ctl.ofs);
ctl.page = grab_cache_page(&inode->i_data, ctl.ofs);
if (ctl.page)
ctl.cache = (union ncp_dir_cache *)
kmap(ctl.page);
......
......@@ -364,8 +364,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
#ifdef CONFIG_NCPFS_NDS_DOMAINS
case NCP_IOC_GETOBJECTNAME:
if ( (permission(inode, MAY_READ) != 0)
&& (current->uid != server->m.mounted_uid)) {
if (current->uid != server->m.mounted_uid) {
return -EACCES;
}
{
......@@ -396,8 +395,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
return 0;
}
case NCP_IOC_SETOBJECTNAME:
if ( (permission(inode, MAY_WRITE) != 0)
&& (current->uid != server->m.mounted_uid)) {
if (current->uid != server->m.mounted_uid) {
return -EACCES;
}
{
......@@ -441,8 +439,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
return 0;
}
case NCP_IOC_GETPRIVATEDATA:
if ( (permission(inode, MAY_READ) != 0)
&& (current->uid != server->m.mounted_uid)) {
if (current->uid != server->m.mounted_uid) {
return -EACCES;
}
{
......@@ -471,8 +468,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
return 0;
}
case NCP_IOC_SETPRIVATEDATA:
if ( (permission(inode, MAY_WRITE) != 0)
&& (current->uid != server->m.mounted_uid)) {
if (current->uid != server->m.mounted_uid) {
return -EACCES;
}
{
......
......@@ -835,7 +835,7 @@ static int ntfs_statfs(struct super_block *sb, struct statfs *sf, int bufsize)
/* Number of files is limited by free space only, so we lie here */
fs.f_ffree=0;
mft=iget(sb,FILE_MFT);
fs.f_files=mft->i_size/vol->mft_recordsize;
fs.f_files=mft->i_size >> vol->mft_recordbits;
iput(mft);
/* should be read from volume */
......
......@@ -91,12 +91,13 @@ int ntfs_init_volume(ntfs_volume *vol,char *boot)
if(vol->mft_clusters_per_record<0 && vol->mft_clusters_per_record!=-10)
ntfs_error("Unexpected data #4 in boot block\n");
vol->clustersize=vol->blocksize*vol->clusterfactor;
if(vol->mft_clusters_per_record>0)
vol->mft_recordsize=
vol->clustersize*vol->mft_clusters_per_record;
vol->clustersize = vol->blocksize * vol->clusterfactor;
if (vol->mft_clusters_per_record > 0)
vol->mft_recordbits = vol->clustersize * vol->mft_clusters_per_record;
else
vol->mft_recordsize=1<<(-vol->mft_clusters_per_record);
vol->mft_recordbits = -vol->mft_clusters_per_record;
vol->mft_recordsize = 1 << vol->mft_recordbits;
vol->index_recordsize=vol->clustersize*vol->index_clusters_per_record;
/* FIXME: long long value */
vol->mft_cluster=NTFS_GETU64(boot+0x30);
......
......@@ -50,6 +50,9 @@ typedef struct { unsigned long pgprot; } pgprot_t;
#define pgd_val(x) ((x).pgd)
#define pgprot_val(x) ((x).pgprot)
#define __pte(x) ((pte_t) { (x) } )
#define __pmd(x) ((pmd_t) { (x) } )
#define __pgd(x) ((pgd_t) { (x) } )
#define __pgprot(x) ((pgprot_t) { (x) } )
#endif /* !__ASSEMBLY__ */
......
......@@ -54,7 +54,7 @@ extern inline pmd_t * pmd_alloc(pgd_t *pgd, unsigned long address)
page = get_pmd_slow();
if (page) {
if (pgd_none(*pgd)) {
pgd_val(*pgd) = 1 + __pa(page);
set_pgd(pgd, __pgd(1 + __pa(page)));
__flush_tlb();
return page + address;
} else
......
......@@ -107,7 +107,7 @@ extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address)
if (!page)
return get_pte_kernel_slow(pmd, address);
pmd_val(*pmd) = _KERNPG_TABLE + __pa(page);
set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(page)));
return page + address;
}
if (pmd_bad(*pmd)) {
......@@ -132,7 +132,7 @@ extern inline pte_t * pte_alloc(pmd_t * pmd, unsigned long address)
if (!page)
return get_pte_slow(pmd, address);
pmd_val(*pmd) = _PAGE_TABLE + __pa(page);
set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(page)));
return (pte_t *)page + address;
}
fix:
......
......@@ -34,6 +34,19 @@ extern inline int pgd_bad(pgd_t pgd) { return 0; }
extern inline int pgd_present(pgd_t pgd) { return 1; }
#define pgd_clear(xp) do { } while (0)
/*
* Certain architectures need to do special things when PTEs
* within a page table are directly modified. Thus, the following
* hook is made available.
*/
#define set_pte(pteptr, pteval) (*(pteptr) = pteval)
/*
* (pmds are folded into pgds so this doesnt get actually called,
* but the define is needed for a generic inline function.)
*/
#define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval)
#define set_pgd(pgdptr, pgdval) (*(pgdptr) = pgdval)
#define pgd_page(pgd) \
((unsigned long) __va(pgd_val(pgd) & PAGE_MASK))
......
......@@ -35,11 +35,22 @@
/*
* Subtle, in PAE mode we cannot have zeroes in the top level
* page directory, the CPU enforces this.
* page directory, the CPU enforces this. (ie. the PGD entry
* always has to have the present bit set.) The CPU caches
* the 4 pgd entries internally, so there is no extra memory
* load on TLB miss, despite one more level of indirection.
*/
#define pgd_none(x) (pgd_val(x) == 1ULL)
extern inline int pgd_bad(pgd_t pgd) { return 0; }
extern inline int pgd_present(pgd_t pgd) { return !pgd_none(pgd); }
#define set_pte(pteptr,pteval) \
set_64bit((unsigned long long *)(pteptr),pte_val(pteval))
#define set_pmd(pmdptr,pmdval) \
set_64bit((unsigned long long *)(pmdptr),pmd_val(pmdval))
#define set_pgd(pgdptr,pgdval) \
set_64bit((unsigned long long *)(pgdptr),pgd_val(pgdval))
/*
* Pentium-II errata A13: in PAE mode we explicitly have to flush
* the TLB via cr3 if the top-level pgd is changed... This was one tough
......@@ -48,7 +59,7 @@ extern inline int pgd_present(pgd_t pgd) { return !pgd_none(pgd); }
*/
extern inline void __pgd_clear (pgd_t * pgd)
{
pgd_val(*pgd) = 1; // no zero allowed!
set_pgd(pgd, __pgd(1ULL));
}
extern inline void pgd_clear (pgd_t * pgd)
......
......@@ -52,13 +52,6 @@ __asm__ __volatile__("invlpg %0": :"m" (*(char *) addr))
#endif
#endif
/*
* Certain architectures need to do special things when PTEs
* within a page table are directly modified. Thus, the following
* hook is made available.
*/
#define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval))
#define __beep() asm("movb $0x3,%al; outb %al,$0x61")
#define PMD_SIZE (1UL << PMD_SHIFT)
......@@ -166,13 +159,13 @@ extern void __handle_bad_pmd_kernel(pmd_t * pmd);
#define pte_none(x) (!pte_val(x))
#define pte_present(x) (pte_val(x) & (_PAGE_PRESENT | _PAGE_PROTNONE))
#define pte_clear(xp) do { pte_val(*(xp)) = 0; } while (0)
#define pte_clear(xp) do { set_pte(xp, __pte(0)); } while (0)
#define pte_pagenr(x) ((unsigned long)((pte_val(x) >> PAGE_SHIFT)))
#define pmd_none(x) (!pmd_val(x))
#define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE)
#define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT)
#define pmd_clear(xp) do { pmd_val(*(xp)) = 0; } while (0)
#define pmd_clear(xp) do { set_pmd(xp, __pmd(0)); } while (0)
#define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE)
/*
* Permanent address of a page. Obviously must never be
......@@ -193,16 +186,16 @@ extern inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY; }
extern inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; }
extern inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_RW; }
extern inline pte_t pte_rdprotect(pte_t pte) { pte_val(pte) &= ~_PAGE_USER; return pte; }
extern inline pte_t pte_exprotect(pte_t pte) { pte_val(pte) &= ~_PAGE_USER; return pte; }
extern inline pte_t pte_mkclean(pte_t pte) { pte_val(pte) &= ~_PAGE_DIRTY; return pte; }
extern inline pte_t pte_mkold(pte_t pte) { pte_val(pte) &= ~_PAGE_ACCESSED; return pte; }
extern inline pte_t pte_wrprotect(pte_t pte) { pte_val(pte) &= ~_PAGE_RW; return pte; }
extern inline pte_t pte_mkread(pte_t pte) { pte_val(pte) |= _PAGE_USER; return pte; }
extern inline pte_t pte_mkexec(pte_t pte) { pte_val(pte) |= _PAGE_USER; return pte; }
extern inline pte_t pte_mkdirty(pte_t pte) { pte_val(pte) |= _PAGE_DIRTY; return pte; }
extern inline pte_t pte_mkyoung(pte_t pte) { pte_val(pte) |= _PAGE_ACCESSED; return pte; }
extern inline pte_t pte_mkwrite(pte_t pte) { pte_val(pte) |= _PAGE_RW; return pte; }
extern inline pte_t pte_rdprotect(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_USER)); return pte; }
extern inline pte_t pte_exprotect(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_USER)); return pte; }
extern inline pte_t pte_mkclean(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_DIRTY)); return pte; }
extern inline pte_t pte_mkold(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_ACCESSED)); return pte; }
extern inline pte_t pte_wrprotect(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_RW)); return pte; }
extern inline pte_t pte_mkread(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_USER)); return pte; }
extern inline pte_t pte_mkexec(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_USER)); return pte; }
extern inline pte_t pte_mkdirty(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_DIRTY)); return pte; }
extern inline pte_t pte_mkyoung(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_ACCESSED)); return pte; }
extern inline pte_t pte_mkwrite(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_RW)); return pte; }
/*
* Conversion functions: convert a page and protection to a page entry,
......@@ -213,17 +206,17 @@ extern inline pte_t pte_mkwrite(pte_t pte) { pte_val(pte) |= _PAGE_RW; return pt
({ \
pte_t __pte; \
\
pte_val(__pte) = ((page)-mem_map)*(unsigned long long)PAGE_SIZE + \
pgprot_val(pgprot); \
set_pte(&__pte, __pte(((page)-mem_map) * \
(unsigned long long)PAGE_SIZE + pgprot_val(pgprot))); \
__pte; \
})
/* This takes a physical page address that is used by the remapping functions */
#define mk_pte_phys(physpage, pgprot) \
({ pte_t __pte; pte_val(__pte) = physpage + pgprot_val(pgprot); __pte; })
({ pte_t __pte; set_pte(&__pte, __pte(physpage + pgprot_val(pgprot))); __pte; })
extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
{ pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); return pte; }
{ set_pte(&pte, __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot))); return pte; }
#define page_pte(page) page_pte_prot(page, __pgprot(0))
......
......@@ -122,12 +122,62 @@ static inline unsigned long get_limit(unsigned long segment)
#define nop() __asm__ __volatile__ ("nop")
#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
#define xchg(ptr,v) ((__typeof__(*(ptr)))__xchg((unsigned long)(v),(ptr),sizeof(*(ptr))))
#define tas(ptr) (xchg((ptr),1))
struct __xchg_dummy { unsigned long a[100]; };
#define __xg(x) ((struct __xchg_dummy *)(x))
/*
* The semantics of XCHGCMP8B are a bit strange, this is why
* there is a loop and the loading of %%eax and %%edx has to
* be inside. This inlines well in most cases, the cached
* cost is around ~38 cycles. (in the future we might want
* to do an SIMD/3DNOW!/MMX/FPU 64-bit store here, but that
* might have an implicit FPU-save as a cost, so it's not
* clear which path to go.)
*/
extern inline void __set_64bit (unsigned long long * ptr,
unsigned int low, unsigned int high)
{
__asm__ __volatile__ (
"1: movl (%0), %%eax;
movl 4(%0), %%edx;
lock; cmpxchg8b (%0);
jnz 1b"
:: "D"(ptr),
"b"(low),
"c"(high)
:
"ax","dx","memory");
}
extern void inline __set_64bit_constant (unsigned long long *ptr,
unsigned long long value)
{
__set_64bit(ptr,(unsigned int)(value), (unsigned int)((value)>>32ULL));
}
#define ll_low(x) *(((unsigned int*)&(x))+0)
#define ll_high(x) *(((unsigned int*)&(x))+1)
extern void inline __set_64bit_var (unsigned long long *ptr,
unsigned long long value)
{
__set_64bit(ptr,ll_low(value), ll_high(value));
}
#define set_64bit(ptr,value) \
(__builtin_constant_p(value) ? \
__set_64bit_constant(ptr, value) : \
__set_64bit_var(ptr, value) )
#define _set_64bit(ptr,value) \
(__builtin_constant_p(value) ? \
__set_64bit(ptr, (unsigned int)(value), (unsigned int)((value)>>32ULL) ) : \
__set_64bit(ptr, ll_low(value), ll_high(value)) )
/*
* Note: no "lock" prefix even on SMP: xchg always implies lock anyway
* Note 2: xchg has side effect, so that attribute volatile is necessary,
......
#ifndef _PPC_PGALLOC_H
#define _PPC_PGALLOC_H
#include <linux/config.h>
#include <linux/threads.h>
#include <asm/processor.h>
......
......@@ -26,6 +26,7 @@ struct ntfs_sb_info{
int clusterfactor;
int clustersize;
int mft_recordsize;
int mft_recordbits;
int mft_clusters_per_record;
int index_recordsize;
int index_clusters_per_record;
......
......@@ -1323,8 +1323,9 @@ static int sprintf_wireless_stats(char *buffer, struct net_device *dev)
int size;
if(stats != (struct iw_statistics *) NULL)
{
size = sprintf(buffer,
"%6s: %02x %3d%c %3d%c %3d%c %5d %5d %5d\n",
"%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d\n",
dev->name,
stats->status,
stats->qual.qual,
......@@ -1336,6 +1337,8 @@ static int sprintf_wireless_stats(char *buffer, struct net_device *dev)
stats->discard.nwid,
stats->discard.code,
stats->discard.misc);
stats->qual.updated = 0;
}
else
size = 0;
......@@ -1357,8 +1360,9 @@ static int dev_get_wireless_info(char * buffer, char **start, off_t offset,
struct net_device * dev;
size = sprintf(buffer,
"Inter-|sta| Quality | Discarded packets\n"
" face |tus|link level noise| nwid crypt misc\n");
"Inter-| sta-| Quality | Discarded packets\n"
" face | tus | link level noise | nwid crypt misc\n"
);
pos+=size;
len+=size;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment