From alsa-devel-owner@alsa.jcu.cz  Tue Mar 17 18:03:46 1998
Received: from ife.ee.ethz.ch (ife-ife2.ee.ethz.ch [129.132.25.129])
	by marvin.jcu.cz (8.8.8/8.8.8) with ESMTP id SAA11016;
	Tue, 17 Mar 1998 18:03:31 +0100
Received: from eldrich.ee.ethz.ch (eldrich.ee.ethz.ch [129.132.24.203])
	by ife.ee.ethz.ch (8.8.5/8.8.5) with SMTP id SAA20179;
	Tue, 17 Mar 1998 18:03:30 +0100 (MET)
Message-ID: <350EACE1.73AE@ife.ee.ethz.ch>
Date: Tue, 17 Mar 1998 18:03:29 +0100
From: Thomas Sailer <sailer@ife.ee.ethz.ch>
MIME-Version: 1.0
To: Jaroslav Kysela <perex@jcu.cz>
CC: alsa-devel@jcu.cz
Subject: Re: driver 0.0.6 & library 0.0.1
References: <Pine.LNX.3.96.980313183946.16116A-100000@entry.jcu.cz>
Content-Type: multipart/mixed; boundary="------------2DE06F763E5A"
Reply-To: alsa-devel@alsa.jcu.cz
Sender: alsa-devel-owner@alsa.jcu.cz
Precedence: list

This is a multi-part message in MIME format.

--------------2DE06F763E5A
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

> What about this:
> 
> kernel -> general sound multiplexer -> PCM multiplexer

Here, you'll have to be _very careful_ not to make the code
over-general, otherwise you'll be in danger of ending up
where OSS now is...
> PCM multiplexer -> midlevel ISA PCM -> lowlevel code
> PCM multiplexer -> midlevel PCI PCM -> lowlevel code

This is good: :-)
> PCM multiplexer -> lowlevel code [-> library]

> Above diagram covers all. I'm not much sure if PCI midlevel will be
> different from ISA midlevel, but we should create some backdoor for far
> future...

The question is if there will be a "generic" PCI midlayer at all.
Remember the ISA midlayer is common for every soundcard because
all ISA soundcards use the ISA DMA. With PCI however every card
has its own DMA, and they might differ substantially.
However, for the ES1370 (Ensoniq AudioPCI) the PCI midlayer
should look quite like the ISA midlayer with the exception
of the GFP_DMA flag :-)

> Most limitations is given by API. I assume that most of code (to manage
> fragments for example) will be (and probably must be) same for all
> midlevel codes.

Maybe this is just impossible, with the ES1370 for example I don't
know how to "pause" the chip and "resume" it where it was,
i.e. if I understood (the rather lousy) docs correctly stopping
the channel resets its pointers (without being able to set them)

> > Another long standing project is a fullduplex no DMA no IRQ
> > driver for PSS cards.
> 
> I don't know anything about PSS cards, but if these cards use only
> polled mode thus this type of soundcards isn't very good for Linux
> environment.

Not necessarily. When the card runs it will be pretty accurate
to assume that there's work to do every 10ms (eg. with the
scheduler tick). If the card can buffer some 100ms of audio
the CPU can poll it and read the data out using burst transfer.
People even do things like that for harddisks :-)
And generally I'm quite DMA starved on my PC's :-)))

> Indirect callback to midlevel code, but this is really problem if we will
> have more midlevel codes with different data structures :-((((

Then maybe a kernel thread would be better?

Tom

NB: In case anyone wants to play with an ES1370 driver,
here's something to play with. It doesn't work correctly however,
and it may even crash the machine, I have to figure out first
how the chip really works :-(

--------------2DE06F763E5A
Content-Type: text/plain; charset=us-ascii; name="es1370.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="es1370.c"

/*****************************************************************************/

/*
 *      es1370.c  --  Ensoniq ES1370/Ashai Kasei AK4531 audio driver.
 *
 *      Copyright (C) 1998  Thomas Sailer (sailer@ife.ee.ethz.ch)
 *        Swiss Federal Institute of Technology (ETH), Electronics Lab
 *
 *      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.
 *
 * This is _not_ working yet!!
 *
 * Special thanks to David C. Niemi
 *
 * TODO:
 * a lot, but most badly needed is to
 * find out the exact relationship between
 * the frequency of the LRCK2 (CODEC) pin and 
 * the value in PCLKDIV.
 *
 * Experimental results:  play the same waveforms on both DAC1 and DAC2
 * and vary PCLKDIV to obtain zero beat.
 *  5512sps:  254
 * 44100sps:   30
 * seems to be fs = 1411200/(PCLKDIV+2)
 *
 * should find out when curr_sample_ct is cleared and
 * where exactly the CCB fetches data
 *
 */

/*****************************************************************************/
      
#include <linux/version.h>
#include <linux/module.h>
#include <linux/modversions.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/miscdevice.h>
#include <linux/malloc.h>
#include <linux/soundcard.h>
#include <linux/bios32.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/dma.h>

/* --------------------------------------------------------------------- */

#ifndef PCI_VENDOR_ID_ENSONIQ
#define PCI_VENDOR_ID_ENSONIQ        0x1274    
#endif
#ifndef PCI_DEVICE_ID_ENSONIQ_ES1370
#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000
#endif

#define ES1370_MAGIC  ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1370)

#define ES1370_EXTENT             0x40

#define ES1370_REG_CONTROL        0x00
#define ES1370_REG_STATUS         0x04
#define ES1370_REG_UART_DATA      0x08
#define ES1370_REG_UART_STATUS    0x09
#define ES1370_REG_UART_CONTROL   0x09
#define ES1370_REG_UART_TEST      0x0a
#define ES1370_REG_MEMPAGE        0x0c
#define ES1370_REG_CODEC          0x10
#define ES1370_REG_SERIAL_CONTROL 0x20
#define ES1370_REG_DAC1_SCOUNT    0x24
#define ES1370_REG_DAC2_SCOUNT    0x28
#define ES1370_REG_ADC_SCOUNT     0x2c

#define ES1370_REG_DAC1_FRAMEADR  0xc30
#define ES1370_REG_DAC1_FRAMECNT  0xc34
#define ES1370_REG_DAC2_FRAMEADR  0xc38
#define ES1370_REG_DAC2_FRAMECNT  0xc3c
#define ES1370_REG_ADC_FRAMEADR   0xd30
#define ES1370_REG_ADC_FRAMECNT   0xd34

#define ES1370_FMT_U8_MONO     0
#define ES1370_FMT_U8_STEREO   1
#define ES1370_FMT_S16_MONO    2
#define ES1370_FMT_S16_STEREO  3

static const sample_size[] = { 1, 2, 2, 4 };
static const sample_shift[] = { 0, 1, 1, 2 };

static const dac1_samplerate[] = { 5512, 11025, 22050, 44100 };

#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2)
#define DAC2_DIVTOSR(x) (1411200/((x)+2))

/* --------------------------------------------------------------------- */

/* Linux 2.0 compatibility stuff */

#ifndef SNDCTL_DSP_GETODELAY
#define SNDCTL_DSP_GETODELAY _IOR ('P', 23, int)
#endif

#if LINUX_VERSION_CODE < 131328

#define __init
#define __initdata
#define __initfunc(x) x

typedef unsigned long mm_segment_t;

#include <asm/segment.h>
#include <linux/mm.h>

#undef put_user
#undef get_user

#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })

#define put_user_ret(x,ptr,ret) ({ if (put_user(x,ptr)) return ret; })
#define get_user_ret(x,ptr,ret) ({ if (get_user(x,ptr)) return ret; })

extern inline int copy_to_user(void *to, const void *from, unsigned long n)
{
        int i = verify_area(VERIFY_WRITE, to, n);
        if (i)
                return i;
        memcpy_tofs(to, from, n);
        return 0;
}

extern inline int copy_from_user(void *to, const void *from, unsigned long n)
{
        int i = verify_area(VERIFY_READ, from, n);
        if (i)
                return i;
        memcpy_fromfs(to, from, n);
        return 0;
}

#define access_ok(x,y,z) (!verify_area(x,y,z))

typedef struct { } spinlock_t;
#define SPIN_LOCK_UNLOCKED { }

#define spin_lock_init(lock)    do { } while(0)
#define spin_lock(lock)         do { } while(0)
#define spin_trylock(lock)      do { } while(0)
#define spin_unlock_wait(lock)  do { } while(0)
#define spin_unlock(lock)       do { } while(0)
#define spin_lock_irq(lock)     cli()
#define spin_unlock_irq(lock)   sti()

#define spin_lock_irqsave(lock, flags) \
        do { save_flags(flags); cli(); } while (0)
#define spin_unlock_irqrestore(lock, flags) \
        restore_flags(flags)

#define signal_pending(x) ((x)->signal & ~(x)->blocked)

#define synchronize_irq() 

#else 

#include <linux/init.h>
#include <asm/spinlock.h>
#include <asm/uaccess.h>
#include <asm/hardirq.h>

#endif

/* --------------------------------------------------------------------- */

struct es1370_state {
	/* magic */
	unsigned int magic;

	/* we keep sb cards in a linked list */
	struct es1370_state *next;

	/* miscdevice structs for audio dev and mixer dev */
	struct miscdevice misc_audio;
	struct miscdevice misc_mixer;
	struct miscdevice misc_dac;
	
	/* hardware resources */
	unsigned char bus, dev_fn;
	unsigned int io, irq;

	/* mixer registers; there is no HW readback */
	struct {
		unsigned short vol[10];
		unsigned int recsrc;
		unsigned int modcnt;
		unsigned short micpreamp;
	} mix;

	/* wave stuff */
	spinlock_t lock;
	struct semaphore open_sem;
	mode_t open_mode;
	struct wait_queue *open_wait;
	struct semaphore dac_open_sem;
	mode_t dac_open_mode;
	struct wait_queue *dac_open_wait;

	/* wave params */
	unsigned short fmt_dac1, fmt_dac2, fmt_adc;
	unsigned short sr1, sr2;

	struct dmabuf {
		void *rawbuf;
		unsigned buforder;
		unsigned numfrag;
		unsigned fragshift;
		unsigned hwptr, swptr;
		unsigned total_bytes;
		int count;
		unsigned error;
		unsigned mapped;
		struct wait_queue *wait;
	} dma_dac1, dma_dac2, dma_adc;

};

/* --------------------------------------------------------------------- */

struct es1370_state *devs = NULL;

/* --------------------------------------------------------------------- */

static void wrcodec(struct es1370_state *s, unsigned char idx, unsigned char data)
{
	unsigned long tmo = jiffies + HZ/10;
	
	do {
		if (!(inl(s->io+ES1370_REG_STATUS) & (1<<10))) {
			outw((((unsigned short)idx)<<8)|data, s->io+ES1370_REG_CODEC);
			return;
		}
		schedule();
	} while ((signed)(tmo-jiffies) > 0);
	printk(KERN_ERR "es1370: write to codec register timeout\n");
}

extern inline unsigned get_hwptr(struct es1370_state *s, struct dmabuf *db, unsigned reg)
{
	unsigned hwptr, diff, bufs;

	outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE);
	hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc;
	bufs = db->numfrag << db->fragshift;
	diff = bufs + hwptr - db->hwptr;
	db->hwptr = hwptr;
	if (diff >= bufs)
		diff -= bufs;
	if (diff >= bufs)
		diff -= bufs;
	return diff;
}

extern inline void stop_adc(struct es1370_state *s)
{
	outl(inl(s->io+ES1370_REG_CONTROL) & ~0x10, s->io+ES1370_REG_CONTROL);
}

extern inline void stop_dac1(struct es1370_state *s)
{
	outl(inl(s->io+ES1370_REG_CONTROL) & ~0x40, s->io+ES1370_REG_CONTROL);
}

extern inline void stop_dac2(struct es1370_state *s)
{
	outl(inl(s->io+ES1370_REG_CONTROL) & ~0x20, s->io+ES1370_REG_CONTROL);
}

/* call with spinlock held! */
static void es1370_update_ptr(struct es1370_state *s)
{
	int diff;

	/* update ADC pointer */
	diff = get_hwptr(s, &s->dma_adc, ES1370_REG_ADC_FRAMECNT);
	s->dma_adc.count += diff;
	s->dma_adc.total_bytes += diff;
	if (!s->dma_adc.mapped && 
	    s->dma_adc.count >= ((s->dma_adc.numfrag-2) << s->dma_adc.fragshift)) { /* soon overrun! stop */
		stop_adc(s);
		s->dma_adc.error++;
	}
	if (s->dma_adc.count > 0)
		wake_up(&s->dma_adc.wait);
	/* update DAC1 pointer */
	diff = get_hwptr(s, &s->dma_dac1, ES1370_REG_DAC1_FRAMECNT);
	s->dma_dac1.count -= diff;
	s->dma_dac1.total_bytes += diff;
	if (!s->dma_dac1.mapped && s->dma_dac1.count <= 0) { 
		stop_dac1(s);
		s->dma_dac1.count = 0;
		s->dma_dac1.swptr = s->dma_dac1.hwptr;
	}
	if (s->dma_dac1.count < (s->dma_dac1.numfrag << s->dma_dac1.fragshift))
		wake_up(&s->dma_dac1.wait);
	/* update DAC2 pointer */
	diff = get_hwptr(s, &s->dma_dac2, ES1370_REG_DAC2_FRAMECNT);
	s->dma_dac2.count -= diff;
	s->dma_dac2.total_bytes += diff;
	if (!s->dma_dac2.mapped && s->dma_dac2.count <= 0) {
		stop_dac2(s);
		s->dma_dac2.count = 0;
		s->dma_dac2.swptr = s->dma_dac2.hwptr;
	}
	if (s->dma_dac2.count < (s->dma_dac2.numfrag << s->dma_dac2.fragshift))
		wake_up(&s->dma_dac2.wait);

}

static void es1370_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
        struct es1370_state *s = (struct es1370_state *)dev_id;
	unsigned int intsrc, sctl1, sctl2;

	spin_lock(&s->lock);
	intsrc = inl(s->io+ES1370_REG_STATUS);
	/* clear audio interrupts first */
	sctl1 = sctl2 = inl(s->io+ES1370_REG_SERIAL_CONTROL);
	if (intsrc & 1)
		sctl1 &= ~0x400;
	if (intsrc & 2)
		sctl1 &= ~0x200;
	if (intsrc & 4)
		sctl1 &= ~0x100;
	outl(sctl1, s->io+ES1370_REG_SERIAL_CONTROL);
	outl(sctl2, s->io+ES1370_REG_SERIAL_CONTROL);
	es1370_update_ptr(s);
	spin_unlock(&s->lock);
}

/* --------------------------------------------------------------------- */

static const char invalid_magic[] = KERN_CRIT "es1370: invalid magic value\n";

#define VALIDATE_STATE(s)                         \
({                                                \
	if (!(s) || (s)->magic != ES1370_MAGIC) { \
		printk(invalid_magic);            \
		return -ENXIO;                    \
	}                                         \
})

/* --------------------------------------------------------------------- */

static const struct {
	unsigned volidx:4;
	unsigned left:4;
	unsigned right:4;
	unsigned stereo:1;
	unsigned recmask:13;
	unsigned avail:1;
} mixtable[SOUND_MIXER_NRDEVICES] = {
	[SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 },   /* master */
	[SOUND_MIXER_PCM]    = { 1, 0x2, 0x3, 1, 0x0400, 1 },   /* voice */
	[SOUND_MIXER_SYNTH]  = { 2, 0x4, 0x5, 1, 0x0060, 1 },   /* FM */
	[SOUND_MIXER_CD]     = { 3, 0x6, 0x7, 1, 0x0006, 1 },   /* CD */
	[SOUND_MIXER_LINE]   = { 4, 0x8, 0x9, 1, 0x0018, 1 },   /* Line */
	[SOUND_MIXER_LINE1]  = { 5, 0xa, 0xb, 1, 0x1800, 1 },   /* AUX */
	[SOUND_MIXER_LINE2]  = { 6, 0xc, 0x0, 1, 0x0100, 1 },   /* Mono1 */
	[SOUND_MIXER_LINE3]  = { 7, 0xd, 0x0, 1, 0x0200, 1 },   /* Mono2 */
	[SOUND_MIXER_MIC]    = { 8, 0xe, 0x0, 1, 0x0001, 1 },   /* Mic */
	[SOUND_MIXER_OGAIN]  = { 9, 0xf, 0x0, 1, 0x0000, 1 }    /* mono out */
};

static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg)
{
	int i, val, j;
	unsigned char l, r, rl, rr;

	VALIDATE_STATE(s);

	if (cmd == SOUND_MIXER_PRIVATE1) {
		get_user_ret(val, (int *)arg, -EFAULT);
		if (val != -1) {
			s->mix.micpreamp = !!val;
			wrcodec(s, 0x19, s->mix.micpreamp);
		}
		return put_user(s->mix.micpreamp, (int *)arg);
	}
#if LINUX_VERSION_CODE < 131328
        if (cmd == SOUND_MIXER_INFO) {
		mixer_info info;
		strncpy(info.id, "ES1370", sizeof(info.id));
		strncpy(info.name, "Ensoniq ES1370", sizeof(info.name));
		if (copy_to_user((void *)arg, &info, sizeof(info)))
			return -EFAULT;
		return 0;
	}
#else
        if (cmd == SOUND_MIXER_INFO) {
		mixer_info info;
		strncpy(info.id, "ES1370", sizeof(info.id));
		strncpy(info.name, "Ensoniq ES1370", sizeof(info.name));
		info.modify_counter = s->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, "ES1370", sizeof(info.id));
		strncpy(info.name, "Ensoniq ES1370", sizeof(info.name));
		if (copy_to_user((void *)arg, &info, sizeof(info)))
			return -EFAULT;
		return 0;
	}
#endif
        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: /* Arg contains a bit for each recording source */
			return put_user(s->mix.recsrc, (int *)arg);
			
                case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
				if (mixtable[i].avail)
					val |= 1 << i;
			return put_user(val, (int *)arg);

                case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
				if (mixtable[i].recmask)
					val |= 1 << i;
			return put_user(val, (int *)arg);
			
                case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
				if (mixtable[i].stereo)
					val |= 1 << i;
			return put_user(val, (int *)arg);
			
                case SOUND_MIXER_CAPS:
			return put_user(0, (int *)arg);

		default:
			i = _IOC_NR(cmd);
                        if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail)
                                return -EINVAL;
			return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg);
		}
	}
        if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) 
		return -EINVAL;
	s->mix.modcnt++;
	switch (_IOC_NR(cmd)) {
	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
		get_user_ret(val, (int *)arg, -EFAULT);
		for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
			if (!(val & (1 << i)))
				continue;
			if (!mixtable[i].recmask) {
				val &= ~(1 << i);
				continue;
			}
			j |= mixtable[i].recmask;
		}
		s->mix.recsrc = val;
		wrcodec(s, 0x12, j & 0xd5);
		wrcodec(s, 0x13, j & 0xaa);
		wrcodec(s, 0x14, (j >> 8) & 0x17);
		wrcodec(s, 0x15, (j >> 8) & 0x0f);
		i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc30;
		wrcodec(s, 0x10, i);
		wrcodec(s, 0x11, i >> 8);
		return 0;

	default:
		i = _IOC_NR(cmd);
		if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail)
			return -EINVAL;
		get_user_ret(val, (int *)arg, -EFAULT);
		l = val & 0xff;
		if (l > 100)
			l = 100;
		if (mixtable[i].stereo) {
			r = (val >> 8) & 0xff;
			if (r > 100)
				r = 100;
			if (l < 10) {
				rl = 0x80;
				l = 0;
			} else {
				rl = 15 - ((l - 10) / 6);
				l = (15 - rl) * 6 + 10;
			}
			if (r < 10) {
				rr = 0x80;
				r = 0;
			} else {
				rr =  15 - ((r - 10) / 6);
				r = (15 - rr) * 6 + 10;
			}
			wrcodec(s, mixtable[i].right, rr);
		} else { 
			if (mixtable[i].left == 15) {
				if (l < 2) {
					rr = rl = 0x80;
					r = l = 0;
				} else {
					rl = 7 - ((l - 2) / 14);
					r = l = (7 - rl) * 14 + 2;
				}
			} else {
				if (l < 10) {
					rl = 0x80;
					r = l = 0;
				} else {
					rl = 15 - ((l - 10) / 6);
					r = l = (15 - rl) * 6 + 10;
				}
			}
		}			
		wrcodec(s, mixtable[i].left, rl);
		s->mix.vol[mixtable[i].volidx] = ((unsigned int)r << 8) | l;
		return 0;
	}
}

/* --------------------------------------------------------------------- */

#if LINUX_VERSION_CODE < 131328
static int es1370_llseek(struct inode *ino, struct file *file, off_t offset, int origin)
{
	return -ESPIPE;
}
#else
static loff_t es1370_llseek(struct file *file, loff_t offset, int origin)
{
	return -ESPIPE;
}
#endif

/* --------------------------------------------------------------------- */

static int es1370_open_mixdev(struct inode *inode, struct file *file)
{
#if LINUX_VERSION_CODE < 131328
	int minor = MINOR(file->f_inode->i_rdev);
#else
	int minor = MINOR(file->f_dentry->d_inode->i_rdev);
#endif
	struct es1370_state *s = devs;

	while (s && s->misc_mixer.minor != minor)
		s = s->next;
	if (!s)
		return -ENODEV;
       	VALIDATE_STATE(s);
	file->private_data = s;
	MOD_INC_USE_COUNT;
	return 0;
}

static int es1370_release_mixdev(struct inode *inode, struct file *file)
{
	struct es1370_state *s = (struct es1370_state *)file->private_data;
	
	VALIDATE_STATE(s);
	MOD_DEC_USE_COUNT;
	return 0;
}

static int es1370_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	return mixer_ioctl((struct es1370_state *)file->private_data, cmd, arg);
}

static const struct file_operations es1370_mixer_fops = {
	&es1370_llseek,
	NULL,  /* read */
	NULL,  /* write */
	NULL,  /* readdir */
	NULL,  /* poll */
	&es1370_ioctl_mixdev,
	NULL,  /* mmap */
	&es1370_open_mixdev,
	&es1370_release_mixdev,
	NULL,  /* fsync */
	NULL,  /* fasync */
	NULL,  /* check_media_change */
#if 0
	NULL,  /* revalidate */
	NULL,  /* lock */
#endif
};

/* --------------------------------------------------------------------- */

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 int allocbuf(struct dmabuf *db)
{
	int order;
	unsigned maxfrag;

	if (db->rawbuf)
		return 0;
#if LINUX_VERSION_CODE < 131328
	for (order = db->buforder; order >= 0 && !db->rawbuf; order--)
		db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order, 0);
#else
	for (order = db->buforder; order >= 0 && !db->rawbuf; order--)
		db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order);
#endif
	if (!db->rawbuf)
		return -ENOMEM;
	db->buforder = order;
	db->count = db->error = db->total_bytes = db->hwptr = db->swptr = db->mapped = 0;
	maxfrag = (PAGE_SIZE << db->buforder) >> db->fragshift;
	if (maxfrag < 4) {
		db->numfrag = 4;
		db->fragshift = ld2((PAGE_SIZE << db->buforder) >> 1);
	} else if (db->numfrag > maxfrag)
		db->numfrag = maxfrag;
	return 0;
}

static void deallocbuf(struct dmabuf *db)
{
	if (!db->rawbuf)
		return;
	free_pages((unsigned int)db->rawbuf, db->buforder);
	db->rawbuf = NULL;
	db->count = db->error = db->total_bytes = db->hwptr = db->swptr = 0;
}

static void kick_dac1(struct es1370_state *s)
{
	unsigned long flags;
	unsigned int ctrl, sctrl;

	spin_lock_irqsave(&s->lock, flags);
	ctrl = inl(s->io+ES1370_REG_CONTROL);
	if (!(ctrl & 0x40) && (s->dma_dac1.mapped || s->dma_dac1.count > 0) && s->dma_dac1.rawbuf) {
		ctrl = (ctrl & ~0x3000) | ((s->sr1 << 12) & 0x3000) | 0x40;
		s->dma_dac1.count = s->dma_dac1.swptr;
		s->dma_dac1.hwptr = 0;
		outl(((1 << s->dma_dac1.fragshift) >> sample_shift[s->fmt_dac1]) - 1, 
		     s->io+ES1370_REG_DAC1_SCOUNT);
		sctrl = inl(s->io+ES1370_REG_SERIAL_CONTROL);
		sctrl = (sctrl & (~0x803)) | (s->fmt_dac1 & 3) | 0x100;
		outl(sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
		outl((ES1370_REG_DAC1_FRAMEADR >> 8) & 15, s->io+ES1370_REG_MEMPAGE);
		outl(virt_to_bus(s->dma_dac1.rawbuf), s->io+(ES1370_REG_DAC1_FRAMEADR & 0xff));
		outl(((s->dma_dac1.numfrag << s->dma_dac1.fragshift) >> 2)-1,
		     s->io+(ES1370_REG_DAC1_FRAMECNT & 0xff));
		outl(ctrl, s->io+ES1370_REG_CONTROL);
	}
	spin_unlock_irqrestore(&s->lock, flags);
}

static void kick_dac2(struct es1370_state *s)
{
	unsigned long flags;
	unsigned int ctrl, sctrl;

	spin_lock_irqsave(&s->lock, flags);
	ctrl = inl(s->io+ES1370_REG_CONTROL);
	if (!(ctrl & 0x20) && (s->dma_dac2.mapped || s->dma_dac2.count > 0) && s->dma_dac2.rawbuf) {
		ctrl = (ctrl & ~0x1fff0000) | ((s->sr2 << 16) & 0x1fff0000) | 0x20;
		s->dma_dac2.count = s->dma_dac2.swptr;
		s->dma_dac2.hwptr = 0;
		outl(((1 << s->dma_dac2.fragshift) >> sample_shift[s->fmt_dac2]) - 1, 
		     s->io+ES1370_REG_DAC2_SCOUNT);
		sctrl = inl(s->io+ES1370_REG_SERIAL_CONTROL);
		sctrl = (sctrl & (~0x3f500c)) | ((s->fmt_dac2 & 3) << 2) | 0x200 | (sample_size[s->fmt_dac2] << 19);
		outl(sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
		outl((ES1370_REG_DAC2_FRAMEADR >> 8) & 15, s->io+ES1370_REG_MEMPAGE);
		outl(virt_to_bus(s->dma_dac2.rawbuf), s->io+(ES1370_REG_DAC2_FRAMEADR & 0xff));
		outl(((s->dma_dac2.numfrag << s->dma_dac2.fragshift) >> 2)-1,
		     s->io+(ES1370_REG_DAC2_FRAMECNT & 0xff));
		outl(ctrl, s->io+ES1370_REG_CONTROL);
	}
	spin_unlock_irqrestore(&s->lock, flags);
}

static void kick_adc(struct es1370_state *s)
{
	unsigned long flags;
	unsigned int ctrl, sctrl;

	spin_lock_irqsave(&s->lock, flags);
	ctrl = inl(s->io+ES1370_REG_CONTROL);
	if (!(ctrl & 0x10) && (s->dma_adc.mapped || s->dma_adc.count < (s->dma_adc.numfrag << s->dma_adc.fragshift))
	    && s->dma_adc.rawbuf) {
		ctrl = (ctrl & ~0x9fff0000) | ((s->sr2 << 16) & 0x1fff0000) | 0x10;
		s->dma_adc.count = s->dma_adc.swptr = s->dma_adc.hwptr = 0;
		outl(((1 << s->dma_adc.fragshift) >> sample_shift[s->fmt_adc]) - 1, 
		     s->io+ES1370_REG_ADC_SCOUNT);
		sctrl = inl(s->io+ES1370_REG_SERIAL_CONTROL);
		sctrl = (sctrl & (~0x8030)) | ((s->fmt_adc & 3) << 8) | 0x400;
		outl(sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
		outl((ES1370_REG_ADC_FRAMEADR >> 8) & 15, s->io+ES1370_REG_MEMPAGE);
		outl(virt_to_bus(s->dma_adc.rawbuf), s->io+(ES1370_REG_ADC_FRAMEADR & 0xff));
		outl(((s->dma_adc.numfrag << s->dma_adc.fragshift) >> 2)-1,
		     s->io+(ES1370_REG_ADC_FRAMECNT & 0xff));
		outl(ctrl, s->io+ES1370_REG_CONTROL);
	}
	spin_unlock_irqrestore(&s->lock, flags);
}

static int drain_dac1(struct es1370_state *s, int nonblock)
{
        struct wait_queue wait = { current, NULL };
	unsigned long flags;
	int count;

        current->state = TASK_INTERRUPTIBLE;
        add_wait_queue(&s->dma_dac1.wait, &wait);
        for (;;) {
                spin_lock_irqsave(&s->lock, flags);
		count = s->dma_dac1.count;
                spin_unlock_irqrestore(&s->lock, flags);
		if (count <= 0)
			break;
		if (signal_pending(current))
                        break;
                if (nonblock) {
                        remove_wait_queue(&s->dma_dac1.wait, &wait);
                        current->state = TASK_RUNNING;
                        return -EBUSY;
                }
                schedule();
        }
        remove_wait_queue(&s->dma_dac1.wait, &wait);
        current->state = TASK_RUNNING;
        if (signal_pending(current))
                return -ERESTARTSYS;
        return 0;
}

static int drain_dac2(struct es1370_state *s, int nonblock)
{
        struct wait_queue wait = { current, NULL };
	unsigned long flags;
	int count;

        current->state = TASK_INTERRUPTIBLE;
        add_wait_queue(&s->dma_dac2.wait, &wait);
        for (;;) {
                spin_lock_irqsave(&s->lock, flags);
		count = s->dma_dac2.count;
                spin_unlock_irqrestore(&s->lock, flags);
		if (count <= 0)
			break;
		if (signal_pending(current))
                        break;
                if (nonblock) {
                        remove_wait_queue(&s->dma_dac2.wait, &wait);
                        current->state = TASK_RUNNING;
                        return -EBUSY;
                }
                schedule();
        }
        remove_wait_queue(&s->dma_dac2.wait, &wait);
        current->state = TASK_RUNNING;
        if (signal_pending(current))
                return -ERESTARTSYS;
        return 0;
}

/* --------------------------------------------------------------------- */

#if LINUX_VERSION_CODE < 131328
static int es1370_read(struct inode *ino, struct file *file, char *buffer, int count)
#else
static ssize_t es1370_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
#endif
{
	struct es1370_state *s = (struct es1370_state *)file->private_data;
	ssize_t ret;
	unsigned long flags;
	unsigned swptr, bufs;
	int cnt;

	VALIDATE_STATE(s);
#if LINUX_VERSION_CODE >= 131328
	if (ppos != &file->f_pos)
		return -ESPIPE;
#endif
	if ((ret = allocbuf(&s->dma_adc)))
		return ret;
	if (!access_ok(VERIFY_WRITE, buffer, count))
		return -EFAULT;
	bufs = s->dma_adc.numfrag << s->dma_adc.fragshift;
	while (count > 0) {
		spin_lock_irqsave(&s->lock, flags);
		swptr = s->dma_adc.swptr;
		cnt = bufs-swptr;
		if (s->dma_adc.count < cnt)
			cnt = s->dma_adc.count;
		spin_unlock_irqrestore(&s->lock, flags);
		if (cnt > count)
			cnt = count;
		if (cnt <= 0) {
			kick_adc(s);
			if (file->f_flags & O_NONBLOCK)
				return ret ? ret : -EBUSY;
			interruptible_sleep_on(&s->dma_adc.wait);
			if (signal_pending(current))
				return ret ? ret : -ERESTARTSYS;
			continue;
		}
		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt))
			return ret ? ret : -EFAULT;
		spin_lock_irqsave(&s->lock, flags);
		swptr += cnt;
		if (swptr >= bufs)
			swptr -= bufs;
		s->dma_adc.swptr = swptr;
		s->dma_adc.count -= cnt;
		spin_unlock_irqrestore(&s->lock, flags);
		count -= cnt;
		buffer += cnt;
		ret += cnt;
		kick_adc(s);
	}
	return ret;
}

#if LINUX_VERSION_CODE < 131328
static int es1370_write(struct inode *ino, struct file *file, const char *buffer, int count)
#else
static ssize_t es1370_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
#endif
{
	struct es1370_state *s = (struct es1370_state *)file->private_data;
	ssize_t ret;
	unsigned long flags;
	unsigned swptr, bufs;
	int cnt;

	VALIDATE_STATE(s);
#if LINUX_VERSION_CODE >= 131328
	if (ppos != &file->f_pos)
		return -ESPIPE;
#endif
	if ((ret = allocbuf(&s->dma_dac2)))
		return ret;
	if (!access_ok(VERIFY_READ, buffer, count))
		return -EFAULT;
	bufs = s->dma_dac2.numfrag << s->dma_dac2.fragshift;
	while (count > 0) {
		spin_lock_irqsave(&s->lock, flags);
		swptr = s->dma_dac2.swptr;
		cnt = bufs-swptr;
		if (s->dma_dac2.count + cnt > bufs)
			cnt = bufs - s->dma_dac2.count;
		spin_unlock_irqrestore(&s->lock, flags);
		if (cnt > count)
			cnt = count;
		if (cnt <= 0) {
			kick_dac2(s);
			if (file->f_flags & O_NONBLOCK)
				return ret ? ret : -EBUSY;
			interruptible_sleep_on(&s->dma_dac2.wait);
			if (signal_pending(current))
				return ret ? ret : -ERESTARTSYS;
			continue;
		}
		if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt))
			return ret ? ret : -EFAULT;
		spin_lock_irqsave(&s->lock, flags);
		if (swptr == s->dma_dac2.swptr) {
			swptr += cnt;
			if (swptr >= bufs)
				swptr -= bufs;
			s->dma_dac2.swptr = swptr;
			s->dma_dac2.count += cnt;
		} else
			cnt = 0;
		spin_unlock_irqrestore(&s->lock, flags);
		if (cnt) {
			count -= cnt;
			buffer += cnt;
			ret += cnt;
			kick_dac2(s);
		}		
	}
	return ret;
}

#if LINUX_VERSION_CODE < 131328

/* well select really */
static int es1370_poll(struct inode *inode, struct file *file, int sel_type, select_table *wait)
{
	struct es1370_state *s = (struct es1370_state *)file->private_data;
	unsigned long flags;
	int count;

	VALIDATE_STATE(s);
	if (sel_type == SEL_IN) {
		spin_lock_irqsave(&s->lock, flags);
		es1370_update_ptr(s);
		count = s->dma_adc.count;
		spin_unlock_irqrestore(&s->lock, flags);
		if (count > 0)
			return 1;
		select_wait(&s->dma_adc.wait, wait);
	}
	if (sel_type == SEL_OUT) {
		spin_lock_irqsave(&s->lock, flags);
		es1370_update_ptr(s);
		count = (s->dma_dac2.numfrag << s->dma_dac2.fragshift) - s->dma_dac2.count;
		spin_unlock_irqrestore(&s->lock, flags);
		if (count > 0)
			return 1;
		select_wait(&s->dma_dac2.wait, wait);
	}
	return 0;
}

#else

static unsigned int es1370_poll(struct file *file, struct poll_table_struct *wait)
{
	struct es1370_state *s = (struct es1370_state *)file->private_data;
	unsigned long flags;
	unsigned int mask = 0;

	VALIDATE_STATE(s);
	if (file->f_flags & FMODE_WRITE)
		poll_wait(file, &s->dma_dac2.wait, wait);
	if (file->f_flags & FMODE_READ)
		poll_wait(file, &s->dma_adc.wait, wait);
	spin_lock_irqsave(&s->lock, flags);
	es1370_update_ptr(s);
	if (file->f_flags & FMODE_READ && s->dma_adc.count > 0)
		mask |= POLLIN | POLLRDNORM;
	if (file->f_flags & FMODE_WRITE && (s->dma_dac2.numfrag << s->dma_dac2.fragshift) > s->dma_dac2.count)
		mask |= POLLOUT | POLLWRNORM;
	spin_unlock_irqrestore(&s->lock, flags);
	return mask;
}

#endif

#if LINUX_VERSION_CODE < 131328
static int es1370_mmap(struct inode *ino, struct file *file, struct vm_area_struct *vma)
#else
static int es1370_mmap(struct file *file, struct vm_area_struct *vma)
#endif
{
	struct es1370_state *s = (struct es1370_state *)file->private_data;

	VALIDATE_STATE(s);
	return -EPERM;
}

static int es1370_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	struct es1370_state *s = (struct es1370_state *)file->private_data;
	unsigned long flags;
        audio_buf_info abinfo;
        count_info cinfo;
	unsigned ctrl;
	int val;

	VALIDATE_STATE(s);
	switch (cmd) {
	case SNDCTL_DSP_SYNC:
		if (file->f_mode & FMODE_WRITE)
			return drain_dac2(s, 1/*file->f_flags & O_NONBLOCK*/);
		return -EINVAL;
		
	case SNDCTL_DSP_SETDUPLEX:
		return 0;

	case SNDCTL_DSP_GETCAPS:
		return put_user(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_dac2(s);
		if (file->f_mode & FMODE_READ) {
			stop_adc(s);
			synchronize_irq();
			s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = 0;
		}
		return 0;

        case SNDCTL_DSP_SPEED:
                get_user_ret(val, (int *)arg, -EFAULT);
		if (val >= 0) {
			if (s->open_mode & (~file->f_mode))
				return -EINVAL;
			if (val < 4000)
				val = 4000;
			if (val > 50000)
				val = 50000;
			stop_adc(s);
			stop_dac2(s);
			s->sr2 = DAC2_SRTODIV(val);
		}
		return put_user(DAC2_DIVTOSR(s->sr2), (int *)arg);
		
        case SNDCTL_DSP_STEREO:
                get_user_ret(val, (int *)arg, -EFAULT);
		if (file->f_mode & FMODE_READ) {
			stop_adc(s);
			if (val)
				s->fmt_adc |= 1;
			else
				s->fmt_adc &= ~1;
		}
		if (file->f_mode & FMODE_WRITE) {
			stop_dac2(s);
			if (val)
				s->fmt_dac2 |= 1;
			else
				s->fmt_dac2 &= ~1;
                }
		return 0;

        case SNDCTL_DSP_CHANNELS:
                get_user_ret(val, (int *)arg, -EFAULT);
		if (val != 0) {
			if (file->f_mode & FMODE_READ) {
				stop_adc(s);
				if (val >= 2)
					s->fmt_adc |= 1;
				else
					s->fmt_adc &= ~1;
			}
			if (file->f_mode & FMODE_WRITE) {
				stop_dac2(s);
				if (val >= 2)
					s->fmt_dac2 |= 1;
				else
					s->fmt_dac2 &= ~1;
			}
		}
		if (file->f_mode & FMODE_READ)
			return put_user((s->fmt_adc & 1) ? 2 : 1, (int *)arg);
		return put_user((s->fmt_dac2 & 1) ? 2 : 1, (int *)arg);
		
        case SNDCTL_DSP_GETFMTS: /* Returns a mask */
                return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
		
        case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
		get_user_ret(val, (int *)arg, -EFAULT);
		if (val != AFMT_QUERY) {
			if (file->f_mode & FMODE_READ) {
				stop_adc(s);
				if (val == AFMT_S16_LE)
					s->fmt_adc |= 2;
				else
					s->fmt_adc &= ~2;
			}
			if (file->f_mode & FMODE_WRITE) {
				stop_dac2(s);
				if (val == AFMT_S16_LE)
					s->fmt_dac2 |= 2;
				else
					s->fmt_dac2 &= ~2;
			}
		}
		if (file->f_mode & FMODE_READ)
			return put_user((s->fmt_adc & 2) ? AFMT_S16_LE : AFMT_U8, (int *)arg);
		return put_user((s->fmt_dac2 & 2) ? AFMT_S16_LE : AFMT_U8, (int *)arg);

        case SNDCTL_DSP_POST:
                return 0;

        case SNDCTL_DSP_GETTRIGGER:
		val = 0;
		ctrl = inl(s->io+ES1370_REG_CONTROL);
		if (file->f_mode & FMODE_READ && ctrl & 0x10) 
			val |= PCM_ENABLE_INPUT;
		if (file->f_mode & FMODE_WRITE && ctrl & 0x20) 
			val |= PCM_ENABLE_OUTPUT;
		return put_user(val, (int *)arg);
						
	case SNDCTL_DSP_GETOSPACE:
		if (!(file->f_mode & FMODE_WRITE))
			return -EINVAL;
		spin_lock_irqsave(&s->lock, flags);
		es1370_update_ptr(s);
		abinfo.fragsize = 1 << s->dma_dac2.fragshift;
                abinfo.bytes =  (s->dma_dac2.numfrag << s->dma_dac2.fragshift) - s->dma_dac2.count;
                abinfo.fragstotal = s->dma_dac2.numfrag;
                abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift;      
		spin_unlock_irqrestore(&s->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;
		spin_lock_irqsave(&s->lock, flags);
		es1370_update_ptr(s);
		abinfo.fragsize = 1 << s->dma_adc.fragshift;
                abinfo.bytes =  s->dma_adc.count;
                abinfo.fragstotal = s->dma_adc.numfrag;
                abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
		spin_unlock_irqrestore(&s->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->lock, flags);
		es1370_update_ptr(s);
                val = s->dma_dac2.count;
		spin_unlock_irqrestore(&s->lock, flags);
		return put_user(val, (int *)arg);

        case SNDCTL_DSP_SETFRAGMENT:
                get_user_ret(val, (int *)arg, -EFAULT);
		return -EINVAL; /* not yet */

        case SNDCTL_DSP_GETIPTR:
		if (!(file->f_mode & FMODE_READ))
			return -EINVAL;
		spin_lock_irqsave(&s->lock, flags);
		es1370_update_ptr(s);
                cinfo.bytes = s->dma_adc.total_bytes;
                cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift;
                cinfo.ptr = s->dma_adc.hwptr;
		spin_unlock_irqrestore(&s->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->lock, flags);
		es1370_update_ptr(s);
                cinfo.bytes = s->dma_dac2.total_bytes;
                cinfo.blocks = s->dma_dac2.total_bytes >> s->dma_dac2.fragshift;
                cinfo.ptr = s->dma_dac2.hwptr;
		spin_unlock_irqrestore(&s->lock, flags);
                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));

        case SNDCTL_DSP_GETBLKSIZE:
		val = (file->f_mode & FMODE_WRITE) ? s->dma_dac2.fragshift : s->dma_adc.fragshift;
                return put_user((1 << val), (int *)arg);

        case SOUND_PCM_WRITE_FILTER:
        case SNDCTL_DSP_SUBDIVIDE:
        case SNDCTL_DSP_SETSYNCRO:
        case SOUND_PCM_READ_RATE:
        case SOUND_PCM_READ_CHANNELS:
        case SOUND_PCM_READ_BITS:
        case SOUND_PCM_READ_FILTER:
                return -EINVAL;
		
	}
	return mixer_ioctl(s, cmd, arg);
}

static int es1370_open(struct inode *inode, struct file *file)
{
#if LINUX_VERSION_CODE < 131328
	int minor = MINOR(file->f_inode->i_rdev);
#else
	int minor = MINOR(file->f_dentry->d_inode->i_rdev);
#endif
	struct es1370_state *s = devs;

	while (s && s->misc_audio.minor != minor)
		s = s->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 -EBUSY;
		}
		up(&s->open_sem);
		interruptible_sleep_on(&s->open_wait);
		if (signal_pending(current))
			return -ERESTARTSYS;
		down(&s->open_sem);
	}
	if (!s->open_mode)
		s->sr2 = 30;
	if (file->f_mode & FMODE_READ) {
		s->dma_adc.buforder = 3;
		s->dma_adc.fragshift = 10;
		s->dma_adc.numfrag = (PAGE_SIZE << s->dma_adc.buforder) >> s->dma_adc.fragshift;
		s->fmt_adc = ES1370_FMT_U8_MONO;
	}
	if (file->f_mode & FMODE_WRITE) {
		s->dma_dac2.buforder = 3;
		s->dma_dac2.fragshift = 10;
		s->dma_dac2.numfrag = (PAGE_SIZE << s->dma_dac2.buforder) >> s->dma_dac2.fragshift;
		s->fmt_dac2 = ES1370_FMT_U8_MONO;
	}
	s->open_mode |= (file->f_mode) & (FMODE_READ | FMODE_WRITE);
	up(&s->open_sem);
	MOD_INC_USE_COUNT;
	return 0;
}

static int es1370_release(struct inode *inode, struct file *file)
{
	struct es1370_state *s = (struct es1370_state *)file->private_data;
	unsigned long flags;

	VALIDATE_STATE(s);
	if (file->f_mode & FMODE_WRITE)
		drain_dac2(s, file->f_flags & O_NONBLOCK);
	down(&s->open_sem);
	if (file->f_flags & FMODE_WRITE) {
		spin_lock_irqsave(&s->lock, flags);
		stop_dac2(s);
		spin_unlock_irqrestore(&s->lock, flags);
		deallocbuf(&s->dma_dac2);
	}
	if (file->f_flags & FMODE_READ) {
		spin_lock_irqsave(&s->lock, flags);
		stop_adc(s);
		spin_unlock_irqrestore(&s->lock, flags);
		deallocbuf(&s->dma_adc);
	}
	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
	MOD_DEC_USE_COUNT;
	up(&s->open_sem);
	return 0;
}

static const struct file_operations es1370_audio_fops = {
	&es1370_llseek,
	&es1370_read,
	&es1370_write,
	NULL,  /* readdir */
	&es1370_poll,
	&es1370_ioctl,
	&es1370_mmap,
	&es1370_open,
	&es1370_release,
	NULL,  /* fsync */
	NULL,  /* fasync */
	NULL,  /* check_media_change */
#if 0
	NULL,  /* revalidate */
	NULL,  /* lock */
#endif
};

/* --------------------------------------------------------------------- */

#if LINUX_VERSION_CODE < 131328
static int es1370_write_dac(struct inode *ino, struct file *file, const char *buffer, int count)
#else
static ssize_t es1370_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos)
#endif
{
	struct es1370_state *s = (struct es1370_state *)file->private_data;
	ssize_t ret = 0;
	unsigned long flags;
	unsigned swptr, bufs;
	int cnt;

	VALIDATE_STATE(s);
#if LINUX_VERSION_CODE >= 131328
	if (ppos != &file->f_pos)
		return -ESPIPE;
#endif
	if ((ret = allocbuf(&s->dma_dac1)))
		return ret;
	if (!access_ok(VERIFY_READ, buffer, count))
		return -EFAULT;
	bufs = s->dma_dac1.numfrag << s->dma_dac1.fragshift;
	while (count > 0) {
		spin_lock_irqsave(&s->lock, flags);
		swptr = s->dma_dac1.swptr;
		cnt = bufs-swptr;
		if (s->dma_dac1.count + cnt > bufs)
			cnt = bufs - s->dma_dac1.count;
		spin_unlock_irqrestore(&s->lock, flags);
		if (cnt > count)
			cnt = count;
		if (cnt <= 0) {
			kick_dac1(s);
			if (file->f_flags & O_NONBLOCK)
				return ret ? ret : -EBUSY;
			interruptible_sleep_on(&s->dma_dac1.wait);
			if (signal_pending(current))
				return ret ? ret : -ERESTARTSYS;
			continue;
		}
		if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt))
			return ret ? ret : -EFAULT;
		spin_lock_irqsave(&s->lock, flags);
		if (swptr == s->dma_dac1.swptr) {
			swptr += cnt;
			if (swptr >= bufs)
				swptr -= bufs;
			s->dma_dac1.swptr = swptr;
			s->dma_dac1.count += cnt;
		} else
			cnt = 0;
		spin_unlock_irqrestore(&s->lock, flags);
		if (cnt) {
			count -= cnt;
			buffer += cnt;
			ret += cnt;
			kick_dac1(s);
		}		
	}
	return ret;
}

#if LINUX_VERSION_CODE < 131328

/* well select really */
static int es1370_poll_dac(struct inode *inode, struct file *file, int sel_type, select_table *wait)
{
	struct es1370_state *s = (struct es1370_state *)file->private_data;
	unsigned long flags;
	int count;

	VALIDATE_STATE(s);
	if (sel_type == SEL_OUT) {
		spin_lock_irqsave(&s->lock, flags);
		es1370_update_ptr(s);
		count = (s->dma_dac1.numfrag << s->dma_dac1.fragshift) - s->dma_dac1.count;
		spin_unlock_irqrestore(&s->lock, flags);
		if (count > 0)
			return 1;
		select_wait(&s->dma_dac1.wait, wait);
	}
	return 0;
}

#else

static unsigned int es1370_poll_dac(struct file *file, struct poll_table_struct *wait)
{
	struct es1370_state *s = (struct es1370_state *)file->private_data;
	unsigned long flags;
	unsigned int mask = 0;

	VALIDATE_STATE(s);
	poll_wait(file, &s->dma_dac1.wait, wait);
	spin_lock_irqsave(&s->lock, flags);
	es1370_update_ptr(s);
	if ((s->dma_dac1.numfrag << s->dma_dac1.fragshift) > s->dma_dac1.count)
		mask |= POLLOUT | POLLWRNORM;
	spin_unlock_irqrestore(&s->lock, flags);
	return mask;
}
#endif

static int es1370_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	struct es1370_state *s = (struct es1370_state *)file->private_data;
	unsigned long flags;
        audio_buf_info abinfo;
        count_info cinfo;
	unsigned ctrl;
	int val;

	VALIDATE_STATE(s);
	switch (cmd) {
	case SNDCTL_DSP_SYNC:
		return drain_dac1(s, 1/*file->f_flags & O_NONBLOCK*/);
		
	case SNDCTL_DSP_SETDUPLEX:
		return -EINVAL;

	case SNDCTL_DSP_GETCAPS:
		return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
		
        case SNDCTL_DSP_RESET:
		stop_dac1(s);
		return 0;

        case SNDCTL_DSP_SPEED:
                get_user_ret(val, (int *)arg, -EFAULT);
		if (val >= 0) {
			for (ctrl = 0; ctrl <= 2; ctrl++)
				if (val < (dac1_samplerate[ctrl] + dac1_samplerate[ctrl+1]) / 2)
					break;
			s->sr1 = ctrl;
		}
		return put_user(dac1_samplerate[s->sr1], (int *)arg);
		
        case SNDCTL_DSP_STEREO:
                get_user_ret(val, (int *)arg, -EFAULT);
		stop_dac1(s);
		if (val)
			s->fmt_dac1 |= 1;
		else
			s->fmt_dac1 &= ~1;
		return 0;

        case SNDCTL_DSP_CHANNELS:
                get_user_ret(val, (int *)arg, -EFAULT);
		if (val != 0) {
			stop_dac1(s);
			if (val >= 2)
				s->fmt_dac1 |= 1;
			else
				s->fmt_dac1 &= ~1;
		}
		return put_user((s->fmt_dac1 & 1) ? 2 : 1, (int *)arg);
		
        case SNDCTL_DSP_GETFMTS: /* Returns a mask */
                return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
		
        case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
		get_user_ret(val, (int *)arg, -EFAULT);
		if (val != AFMT_QUERY) {
			stop_dac1(s);
			if (val == AFMT_S16_LE)
				s->fmt_dac1 |= 2;
			else
				s->fmt_dac1 &= ~2;
		}
		return put_user((s->fmt_dac1 & 2) ? AFMT_S16_LE : AFMT_U8, (int *)arg);

        case SNDCTL_DSP_POST:
                return 0;

        case SNDCTL_DSP_GETTRIGGER:
		val = 0;
		ctrl = inl(s->io+ES1370_REG_CONTROL);
		if (ctrl & 0x40) 
			val |= PCM_ENABLE_OUTPUT;
		return put_user(val, (int *)arg);
						
	case SNDCTL_DSP_GETOSPACE:
		spin_lock_irqsave(&s->lock, flags);
		es1370_update_ptr(s);
		abinfo.fragsize = 1 << s->dma_dac1.fragshift;
                abinfo.bytes =  (s->dma_dac1.numfrag << s->dma_dac1.fragshift) - s->dma_dac1.count;
                abinfo.fragstotal = s->dma_dac1.numfrag;
                abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift;      
		spin_unlock_irqrestore(&s->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:
		spin_lock_irqsave(&s->lock, flags);
		es1370_update_ptr(s);
                val = s->dma_dac1.count;
		spin_unlock_irqrestore(&s->lock, flags);
		return put_user(val, (int *)arg);

        case SNDCTL_DSP_SETFRAGMENT:
                get_user_ret(val, (int *)arg, -EFAULT);
		return -EINVAL; /* not yet */

        case SNDCTL_DSP_GETOPTR:
		if (!(file->f_mode & FMODE_WRITE))
			return -EINVAL;
		spin_lock_irqsave(&s->lock, flags);
		es1370_update_ptr(s);
                cinfo.bytes = s->dma_dac1.total_bytes;
                cinfo.blocks = s->dma_dac1.total_bytes >> s->dma_dac1.fragshift;
                cinfo.ptr = s->dma_dac1.hwptr;
		spin_unlock_irqrestore(&s->lock, flags);
                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));

        case SNDCTL_DSP_GETBLKSIZE:
                return put_user((1 << s->dma_dac1.fragshift), (int *)arg);

        case SOUND_PCM_WRITE_FILTER:
        case SNDCTL_DSP_SUBDIVIDE:
        case SNDCTL_DSP_SETSYNCRO:
        case SOUND_PCM_READ_RATE:
        case SOUND_PCM_READ_CHANNELS:
        case SOUND_PCM_READ_BITS:
        case SOUND_PCM_READ_FILTER:
                return -EINVAL;
		
	}
	return mixer_ioctl(s, cmd, arg);
}

static int es1370_open_dac(struct inode *inode, struct file *file)
{
#if LINUX_VERSION_CODE < 131328
	int minor = MINOR(file->f_inode->i_rdev);
#else
	int minor = MINOR(file->f_dentry->d_inode->i_rdev);
#endif
	struct es1370_state *s = devs;

	while (s && s->misc_dac.minor != minor)
		s = s->next;
	if (!s)
		return -ENODEV;
       	VALIDATE_STATE(s);
	if (file->f_mode & FMODE_READ)
		return -EPERM;
	file->private_data = s;
	/* wait for device to become free */
	down(&s->dac_open_sem);
	while (s->dac_open_mode & file->f_mode) {
		if (file->f_flags & O_NONBLOCK) {
			up(&s->dac_open_sem);
			return -EBUSY;
		}
		up(&s->dac_open_sem);
		interruptible_sleep_on(&s->dac_open_wait);
		if (signal_pending(current))
			return -ERESTARTSYS;
		down(&s->dac_open_sem);
	}
	if (!s->dac_open_mode)
		s->sr1 = 3;
	s->dma_dac1.buforder = 3;
	s->dma_dac1.fragshift = 10;
	s->dma_dac1.numfrag = (PAGE_SIZE << s->dma_dac1.buforder) >> s->dma_dac1.fragshift;
	s->fmt_dac1 = ES1370_FMT_U8_MONO;
	s->dac_open_mode = FMODE_WRITE;
	up(&s->dac_open_sem);
	MOD_INC_USE_COUNT;
	return 0;
}

static int es1370_release_dac(struct inode *inode, struct file *file)
{
	struct es1370_state *s = (struct es1370_state *)file->private_data;
	unsigned long flags;

	VALIDATE_STATE(s);
	drain_dac1(s, file->f_flags & O_NONBLOCK);
	down(&s->dac_open_sem);
	spin_lock_irqsave(&s->lock, flags);
	stop_dac1(s);
	spin_unlock_irqrestore(&s->lock, flags);
	deallocbuf(&s->dma_dac1);
	s->dac_open_mode = 0;
	MOD_DEC_USE_COUNT;
	up(&s->dac_open_sem);
	return 0;
}

static const struct file_operations es1370_dac_fops = {
	&es1370_llseek,
	NULL,  /* read */
	&es1370_write_dac,
	NULL,  /* readdir */
	&es1370_poll_dac,
	&es1370_ioctl_dac,
	&es1370_mmap,
	&es1370_open_dac,
	&es1370_release_dac,
	NULL,  /* fsync */
	NULL,  /* fasync */
	NULL,  /* check_media_change */
#if 0
	NULL,  /* revalidate */
	NULL,  /* lock */
#endif
};

/* --------------------------------------------------------------------- */

#ifdef MODULE
__initfunc(int init_module(void))
#else
__initfunc(int init_es1370(void))
#endif
{
	struct es1370_state *s;
	unsigned char bus, dev_fn, irq;
	unsigned short index, vendid, devid;
	unsigned int ioaddr, dev = 192;
	mm_segment_t fs;
	int val;

	if (!pcibios_present())   /* No PCI bus in this machine! */
		return -ENODEV;
	for (index = 0; ; index++) {
		if (pcibios_find_class((PCI_CLASS_MULTIMEDIA_AUDIO << 8), index, &bus, &dev_fn) != PCIBIOS_SUCCESSFUL)
			break;
		pcibios_read_config_word(bus, dev_fn, PCI_VENDOR_ID, &vendid);
		pcibios_read_config_word(bus, dev_fn, PCI_DEVICE_ID, &devid);
		if (vendid != PCI_VENDOR_ID_ENSONIQ || devid != PCI_DEVICE_ID_ENSONIQ_ES1370)
			continue;
		pcibios_read_config_dword(bus, dev_fn, PCI_BASE_ADDRESS_0, &ioaddr);
		if (ioaddr == 0 || (ioaddr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
			continue;
		pcibios_read_config_byte(bus, dev_fn, PCI_INTERRUPT_LINE, &irq);
		if (irq == 0 || irq == 0xff) 
			continue;
		if (!(s = kmalloc(sizeof(struct es1370_state), GFP_KERNEL))) {
			printk(KERN_WARNING "es1370: out of memory\n");
			continue;
		}
		memset(s, 0, sizeof(struct es1370_state));
		init_waitqueue(&s->dma_adc.wait);
		init_waitqueue(&s->dma_dac1.wait);
		init_waitqueue(&s->dma_dac2.wait);
		init_waitqueue(&s->open_wait);
		s->open_sem = MUTEX;
		init_waitqueue(&s->dac_open_wait);
		s->dac_open_sem = MUTEX;
		s->magic = ES1370_MAGIC;
		s->bus = bus;
		s->dev_fn = dev_fn;
		s->io = ioaddr & PCI_BASE_ADDRESS_IO_MASK;
		s->irq = irq;
		if (check_region(s->io, ES1370_EXTENT)) {
			printk(KERN_ERR "es1370: io ports %#x-%#x in use\n", s->io, s->io+ES1370_EXTENT-1);
			goto err_region;
		}
		request_region(s->io, ES1370_EXTENT, "es1370");
		if (request_irq(s->irq, es1370_interrupt, 0, "es1370", s)) {
			printk(KERN_ERR "es1370: irq %u in use\n", s->irq);
			goto err_irq;
		}
		printk(KERN_INFO "es1370: found adapter at io %#06x irq %u\n", s->io, s->irq);
		s->misc_audio = (struct miscdevice) { dev, "es1370 audio", &es1370_audio_fops, NULL, NULL };
		s->misc_mixer = (struct miscdevice) { dev+1, "es1370 mixer", &es1370_mixer_fops, NULL, NULL };
		s->misc_dac = (struct miscdevice) { dev+2, "es1370 dac", &es1370_dac_fops, NULL, NULL };
		dev += 3;
		if (misc_register(&s->misc_audio))
			goto err_misc1;
		if (misc_register(&s->misc_mixer))
			goto err_misc2;
		if (misc_register(&s->misc_dac))
			goto err_misc3;
		/* initialize the chips */
		outl(0x000a0003, s->io+ES1370_REG_CONTROL);
		wrcodec(s, 0x16, 3); /* no RST, PD */
		wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!!  */
		wrcodec(s, 0x18, 0); /* recording source is mixer */
		fs = get_fs();
		set_fs(KERNEL_DS);
		val = SOUND_MASK_LINE|SOUND_MASK_SYNTH;
		mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned)&val);
		val = 0x6464;
		mixer_ioctl(s, SOUND_MIXER_WRITE_VOLUME, (unsigned)&val);
		mixer_ioctl(s, SOUND_MIXER_WRITE_LINE, (unsigned)&val);
		val = 0x1414;
		mixer_ioctl(s, SOUND_MIXER_WRITE_PCM, (unsigned)&val);
		mixer_ioctl(s, SOUND_MIXER_WRITE_SYNTH, (unsigned)&val);
		set_fs(fs);
		/* queue it for later freeing */
		s->next = devs;
		devs = s;
		continue;

	err_misc3:
		misc_deregister(&s->misc_mixer);
	err_misc2:
		misc_deregister(&s->misc_audio);
	err_misc1:
		printk(KERN_ERR "es1370: cannot register misc device\n");
		free_irq(s->irq, s);
	err_irq:
		release_region(s->io, ES1370_EXTENT);
	err_region:
		kfree_s(s, sizeof(struct es1370_state));
	}
	if (!devs)
		return -ENODEV;
	return 0;
}

/* --------------------------------------------------------------------- */

#ifdef MODULE

#define NR_DEVICE 5

static int dev[NR_DEVICE] = { 0, 1, 2, 3 };
#if LINUX_VERSION_CODE >= 131328
MODULE_PARM(dev, "1-" __MODULE_STRING(NR_DEVICE) "i");
MODULE_PARM_DESC(dev, "device number(s)");

MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
MODULE_DESCRIPTION("ES1370 AudioPCI Driver");
#endif

void cleanup_module(void)
{
	struct es1370_state *s;

	while ((s = devs)) {
		devs = devs->next;
		outl(0x00000001, s->io+ES1370_REG_CONTROL); /* switch everything off */
		outl(0, s->io+ES1370_REG_SERIAL_CONTROL); /* clear serial interrupts */
		synchronize_irq();
		free_irq(s->irq, s);
		release_region(s->io, ES1370_EXTENT);
		misc_deregister(&s->misc_audio);
		misc_deregister(&s->misc_mixer);
		misc_deregister(&s->misc_dac);
		kfree_s(s, sizeof(struct es1370_state));
	}
	printk(KERN_INFO "es1370: unloading\n");
}

#endif /* MODULE */

--------------2DE06F763E5A--


