Commit c54c8322 authored by Linus Torvalds's avatar Linus Torvalds

Linux 2.1.129

To a large degree is more merges for PPC and Sparc (and
somehow I must have missed ARM _again_, so I'll have to find that).

But there's a few other things in there:
 - ncr53c8xx tag fix
 - more sound fixes.
 - NFS fixed
 - some subtle TCP issues fixed
 - and lots of mm smoothness tweaks (most of those have been floating
   around for some time - like getting rid of the last vestiges of page
   ages which just complicated and hurt the code)

Have fun with it, and tell me if it breaks. But it won't. I'm finally
getting the old "greased weasel" feeling back. In short, this is the much
awaited perfect and bug-free release, and the only reason I don't call it
2.2 is that I'm chicken.

	Kvaa, kvaa,
			Linus
parent 03c31052
......@@ -69,6 +69,7 @@ CONFIG_SUN_MOSTEK_RTC=y
# CONFIG_SPARCAUDIO_AMD7930 is not set
# CONFIG_SPARCAUDIO_CS4231 is not set
# CONFIG_SPARCAUDIO_DBRI is not set
# CONFIG_SPARCAUDIO_DUMMY is not set
CONFIG_SUN_OPENPROMFS=m
CONFIG_NET=y
CONFIG_SYSVIPC=y
......
# $Id: config.in,v 1.57 1998/09/17 11:05:14 jj Exp $
# $Id: config.in,v 1.58 1998/11/16 04:47:30 davem Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
......
......@@ -74,6 +74,7 @@ CONFIG_OBP_FLASH=m
# CONFIG_SPARCAUDIO_AMD7930 is not set
# CONFIG_SPARCAUDIO_CS4231 is not set
# CONFIG_SPARCAUDIO_DBRI is not set
# CONFIG_SPARCAUDIO_DUMMY is not set
CONFIG_SUN_OPENPROMFS=m
CONFIG_PCI_OLD_PROC=y
CONFIG_NET=y
......@@ -238,7 +239,6 @@ CONFIG_HAPPYMEAL=y
CONFIG_SUNQE=m
CONFIG_MYRI_SBUS=m
CONFIG_DE4X5=m
CONFIG_VORTEX=m
#
# Filesystems
......
/* $Id: ioctl32.c,v 1.54 1998/11/11 17:09:04 jj Exp $
/* $Id: ioctl32.c,v 1.55 1998/11/17 07:43:17 davem Exp $
* ioctl32.c: Conversion between 32bit and 64bit native ioctls.
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
......@@ -53,6 +53,8 @@
#include <asm/envctrl.h>
#include <asm/audioio.h>
#include <linux/soundcard.h>
/* Use this to get at 32-bit user passed pointers.
See sys_sparc32.c for description about these. */
#define A(__x) ((unsigned long)(__x))
......@@ -1885,6 +1887,152 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
case AUDIO_GETDEV_SUNOS:
case AUDIO_FLUSH:
/* Big Q for sound/OSS */
case SNDCTL_SEQ_RESET:
case SNDCTL_SEQ_SYNC:
case SNDCTL_SYNTH_INFO:
case SNDCTL_SEQ_CTRLRATE:
case SNDCTL_SEQ_GETOUTCOUNT:
case SNDCTL_SEQ_GETINCOUNT:
case SNDCTL_SEQ_PERCMODE:
case SNDCTL_FM_LOAD_INSTR:
case SNDCTL_SEQ_TESTMIDI:
case SNDCTL_SEQ_RESETSAMPLES:
case SNDCTL_SEQ_NRSYNTHS:
case SNDCTL_SEQ_NRMIDIS:
case SNDCTL_MIDI_INFO:
case SNDCTL_SEQ_THRESHOLD:
case SNDCTL_SYNTH_MEMAVL:
case SNDCTL_FM_4OP_ENABLE:
case SNDCTL_SEQ_PANIC:
case SNDCTL_SEQ_OUTOFBAND:
case SNDCTL_SEQ_GETTIME:
case SNDCTL_SYNTH_ID:
case SNDCTL_SYNTH_CONTROL:
case SNDCTL_SYNTH_REMOVESAMPLE:
/* Big T for sound/OSS */
case SNDCTL_TMR_TIMEBASE:
case SNDCTL_TMR_START:
case SNDCTL_TMR_STOP:
case SNDCTL_TMR_CONTINUE:
case SNDCTL_TMR_TEMPO:
case SNDCTL_TMR_SOURCE:
case SNDCTL_TMR_METRONOME:
case SNDCTL_TMR_SELECT:
/* Little m for sound/OSS */
case SNDCTL_MIDI_PRETIME:
case SNDCTL_MIDI_MPUMODE:
case SNDCTL_MIDI_MPUCMD:
/* Big P for sound/OSS */
case SNDCTL_DSP_RESET:
case SNDCTL_DSP_SYNC:
case SNDCTL_DSP_SPEED:
case SNDCTL_DSP_STEREO:
case SNDCTL_DSP_GETBLKSIZE:
case SNDCTL_DSP_CHANNELS:
case SOUND_PCM_WRITE_FILTER:
case SNDCTL_DSP_POST:
case SNDCTL_DSP_SUBDIVIDE:
case SNDCTL_DSP_SETFRAGMENT:
case SNDCTL_DSP_GETFMTS:
case SNDCTL_DSP_SETFMT:
case SNDCTL_DSP_GETOSPACE:
case SNDCTL_DSP_GETISPACE:
case SNDCTL_DSP_NONBLOCK:
case SNDCTL_DSP_GETCAPS:
case SNDCTL_DSP_GETTRIGGER:
case SNDCTL_DSP_SETTRIGGER:
case SNDCTL_DSP_GETIPTR:
case SNDCTL_DSP_GETOPTR:
/* case SNDCTL_DSP_MAPINBUF: XXX needs translation */
/* case SNDCTL_DSP_MAPOUTBUF: XXX needs translation */
case SNDCTL_DSP_SETSYNCRO:
case SNDCTL_DSP_SETDUPLEX:
case SNDCTL_DSP_GETODELAY:
case SNDCTL_DSP_PROFILE:
case SOUND_PCM_READ_RATE:
case SOUND_PCM_READ_CHANNELS:
case SOUND_PCM_READ_BITS:
case SOUND_PCM_READ_FILTER:
/* Big C for sound/OSS */
case SNDCTL_COPR_RESET:
case SNDCTL_COPR_LOAD:
case SNDCTL_COPR_RDATA:
case SNDCTL_COPR_RCODE:
case SNDCTL_COPR_WDATA:
case SNDCTL_COPR_WCODE:
case SNDCTL_COPR_RUN:
case SNDCTL_COPR_HALT:
case SNDCTL_COPR_SENDMSG:
case SNDCTL_COPR_RCVMSG:
/* Big M for sound/OSS */
case SOUND_MIXER_READ_VOLUME:
case SOUND_MIXER_READ_BASS:
case SOUND_MIXER_READ_TREBLE:
case SOUND_MIXER_READ_SYNTH:
case SOUND_MIXER_READ_PCM:
case SOUND_MIXER_READ_SPEAKER:
case SOUND_MIXER_READ_LINE:
case SOUND_MIXER_READ_MIC:
case SOUND_MIXER_READ_CD:
case SOUND_MIXER_READ_IMIX:
case SOUND_MIXER_READ_ALTPCM:
case SOUND_MIXER_READ_RECLEV:
case SOUND_MIXER_READ_IGAIN:
case SOUND_MIXER_READ_OGAIN:
case SOUND_MIXER_READ_LINE1:
case SOUND_MIXER_READ_LINE2:
case SOUND_MIXER_READ_LINE3:
case SOUND_MIXER_READ_MUTE:
/* case SOUND_MIXER_READ_ENHANCE: same value as READ_MUTE */
/* case SOUND_MIXER_READ_LOUD: same value as READ_MUTE */
case SOUND_MIXER_READ_RECSRC:
case SOUND_MIXER_READ_DEVMASK:
case SOUND_MIXER_READ_RECMASK:
case SOUND_MIXER_READ_STEREODEVS:
case SOUND_MIXER_READ_CAPS:
case SOUND_MIXER_WRITE_VOLUME:
case SOUND_MIXER_WRITE_BASS:
case SOUND_MIXER_WRITE_TREBLE:
case SOUND_MIXER_WRITE_SYNTH:
case SOUND_MIXER_WRITE_PCM:
case SOUND_MIXER_WRITE_SPEAKER:
case SOUND_MIXER_WRITE_LINE:
case SOUND_MIXER_WRITE_MIC:
case SOUND_MIXER_WRITE_CD:
case SOUND_MIXER_WRITE_IMIX:
case SOUND_MIXER_WRITE_ALTPCM:
case SOUND_MIXER_WRITE_RECLEV:
case SOUND_MIXER_WRITE_IGAIN:
case SOUND_MIXER_WRITE_OGAIN:
case SOUND_MIXER_WRITE_LINE1:
case SOUND_MIXER_WRITE_LINE2:
case SOUND_MIXER_WRITE_LINE3:
case SOUND_MIXER_WRITE_MUTE:
/* case SOUND_MIXER_WRITE_ENHANCE: same value as WRITE_MUTE */
/* case SOUND_MIXER_WRITE_LOUD: same value as WRITE_MUTE */
case SOUND_MIXER_WRITE_RECSRC:
case SOUND_MIXER_INFO:
case SOUND_OLD_MIXER_INFO:
case SOUND_MIXER_ACCESS:
case SOUND_MIXER_PRIVATE1:
case SOUND_MIXER_PRIVATE2:
case SOUND_MIXER_PRIVATE3:
case SOUND_MIXER_PRIVATE4:
case SOUND_MIXER_PRIVATE5:
case SOUND_MIXER_GETLEVELS:
case SOUND_MIXER_SETLEVELS:
case OSS_GETVERSION:
/* AUTOFS */
case AUTOFS_IOC_READY:
case AUTOFS_IOC_FAIL:
......
/* $Id: parport_ax.c,v 1.13 1998/10/26 20:01:59 davem Exp $
/* $Id: parport_ax.c,v 1.14 1998/11/16 04:48:02 davem Exp $
* Parallel-port routines for Sun Ultra/AX architecture
*
* Author: Eddie C. Dost <ecd@skynet.be>
......
......@@ -10,4 +10,5 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
dep_tristate ' AMD7930 Lowlevel Driver' CONFIG_SPARCAUDIO_AMD7930 $CONFIG_SPARCAUDIO
dep_tristate ' CS4231 Lowlevel Driver' CONFIG_SPARCAUDIO_CS4231 $CONFIG_SPARCAUDIO
dep_tristate ' DBRI Lowlevel Driver' CONFIG_SPARCAUDIO_DBRI $CONFIG_SPARCAUDIO
dep_tristate ' Dummy Lowlevel Driver' CONFIG_SPARCAUDIO_DUMMY $CONFIG_SPARCAUDIO
fi
......@@ -16,47 +16,57 @@ O_OBJS :=
M_OBJS :=
ifeq ($(CONFIG_SPARCAUDIO),y)
SBUS_AUDIO=y
M=y
else
ifeq ($(CONFIG_SPARCAUDIO),m)
SBUS_AUDIO_MODULE=y
MM=y
endif
endif
ifeq ($(CONFIG_SPARCAUDIO_AMD7930),y)
SBUS_AUDIO=y
M=y
OX_OBJS += amd7930.o
else
ifeq ($(CONFIG_SPARCAUDIO_AMD7930),m)
SBUS_AUDIO_MODULE=y
MM=y
MX_OBJS += amd7930.o
endif
endif
ifeq ($(CONFIG_SPARCAUDIO_CS4231),y)
SBUS_AUDIO=y
M=y
O_OBJS += cs4231.o
else
ifeq ($(CONFIG_SPARCAUDIO_CS4231),m)
SBUS_AUDIO_MODULE=y
MM=y
M_OBJS += cs4231.o
endif
endif
ifeq ($(CONFIG_SPARCAUDIO_DBRI),y)
SBUS_AUDIO=y
M=y
OX_OBJS += dbri.o
else
ifeq ($(CONFIG_SPARCAUDIO_DBRI),m)
SBUS_AUDIO_MODULE=y
MM=y
MX_OBJS += dbri.o
endif
endif
ifdef SBUS_AUDIO
ifeq ($(CONFIG_SPARCAUDIO_DUMMY),y)
M=y
O_OBJS += dmy.o
else
ifeq ($(CONFIG_SPARCAUDIO_DUMMY),m)
MM=y
M_OBJS += dmy.o
endif
endif
ifdef M
OX_OBJS += audio.o
else
ifdef SBUS_AUDIO_MODULE
ifdef MM
MX_OBJS += audio.o
endif
endif
......
......@@ -90,6 +90,8 @@
#include <linux/interrupt.h>
#include <linux/malloc.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/soundcard.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/system.h>
......@@ -100,9 +102,11 @@
#include <asm/audioio.h>
#include "amd7930.h"
#if defined (AMD79C30_ISDN) || defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
#include "../../isdn/hisax/hisax.h"
#include "../../isdn/hisax/isdnl1.h"
#include "../../isdn/hisax/foreign.h"
#endif
#define MAX_DRIVERS 1
......@@ -346,12 +350,6 @@ static void amd7930_update_map(struct sparcaudio_driver *drv)
map->gr = gx_coeff[level];
}
/* force output to speaker for now */
map->mmr2 |= AM_MAP_MMR2_LS;
/* input from external microphone */
map->mmr2 |= AM_MAP_MMR2_AINB;
amd7930_write_map(drv);
}
......@@ -522,7 +520,7 @@ static void transceive_Bchannel(struct amd7930_channel *channel,
channel->input_count = 0;
if (channel->input_callback)
(*channel->input_callback)
(channel->input_callback_arg);
(channel->input_callback_arg, 1);
}
}
}
......@@ -567,16 +565,7 @@ static void amd7930_interrupt(int irq, void *dev_id, struct pt_regs *intr_regs)
static int amd7930_open(struct inode * inode, struct file * file,
struct sparcaudio_driver *drv)
{
struct amd7930_info *info = (struct amd7930_info *)drv->private;
/* Set the default audio parameters. */
info->rgain = 128;
info->pgain = 200;
info->mgain = 0;
amd7930_update_map(drv);
MOD_INC_USE_COUNT;
return 0;
}
......@@ -709,6 +698,21 @@ static int amd7930_sunaudio_getdev_sunos(struct sparcaudio_driver *drv)
return AUDIO_DEV_AMD;
}
static int amd7930_get_formats(struct sparcaudio_driver *drv)
{
return (AFMT_MU_LAW | AFMT_A_LAW);
}
static int amd7930_get_output_ports(struct sparcaudio_driver *drv)
{
return (AUDIO_SPEAKER | AUDIO_HEADPHONE);
}
static int amd7930_get_input_ports(struct sparcaudio_driver *drv)
{
return (AUDIO_MICROPHONE);
}
static int amd7930_set_output_volume(struct sparcaudio_driver *drv, int vol)
{
struct amd7930_info *info = (struct amd7930_info *)drv->private;
......@@ -750,6 +754,13 @@ static int amd7930_set_monitor_volume(struct sparcaudio_driver *drv, int vol)
return 0;
}
static int amd7930_get_monitor_volume(struct sparcaudio_driver *drv)
{
struct amd7930_info *info = (struct amd7930_info *)drv->private;
return info->mgain;
}
/* Cheats. The amd has the minimum capabilities we support */
static int amd7930_get_output_balance(struct sparcaudio_driver *drv)
{
......@@ -766,25 +777,68 @@ static int amd7930_get_output_channels(struct sparcaudio_driver *drv)
return AUDIO_MIN_PLAY_CHANNELS;
}
static int amd7930_set_output_channels(struct sparcaudio_driver *drv,
int value)
{
return (value == AUDIO_MIN_PLAY_CHANNELS) ? 0 : -EINVAL;
}
static int amd7930_get_input_channels(struct sparcaudio_driver *drv)
{
return AUDIO_MIN_REC_CHANNELS;
}
static int
amd7930_set_input_channels(struct sparcaudio_driver *drv, int value)
{
return (value == AUDIO_MIN_REC_CHANNELS) ? 0 : -EINVAL;
}
static int amd7930_get_output_precision(struct sparcaudio_driver *drv)
{
return AUDIO_MIN_PLAY_PRECISION;
}
static int
amd7930_set_output_precision(struct sparcaudio_driver *drv, int value)
{
return (value == AUDIO_MIN_PLAY_PRECISION) ? 0 : -EINVAL;
}
static int amd7930_get_input_precision(struct sparcaudio_driver *drv)
{
return AUDIO_MIN_REC_PRECISION;
}
/* This should eventually be made to DTRT, whatever that ends up */
static int
amd7930_set_input_precision(struct sparcaudio_driver *drv, int value)
{
return (value == AUDIO_MIN_REC_PRECISION) ? 0 : -EINVAL;
}
static int amd7930_get_output_port(struct sparcaudio_driver *drv)
{
return AUDIO_SPEAKER; /* some of these have only HEADPHONE */
struct amd7930_info *info = (struct amd7930_info *)drv->private;
if (info->map.mmr2 & AM_MAP_MMR2_LS)
return AUDIO_SPEAKER;
return AUDIO_HEADPHONE;
}
static int amd7930_set_output_port(struct sparcaudio_driver *drv, int value)
{
struct amd7930_info *info = (struct amd7930_info *)drv->private;
switch (value) {
case AUDIO_HEADPHONE:
info->map.mmr2 &= ~AM_MAP_MMR2_LS;
break;
case AUDIO_SPEAKER:
info->map.mmr2 |= AM_MAP_MMR2_LS;
break;
default:
return -EINVAL;
}
amd7930_update_map(drv);
return 0;
}
/* Only a microphone here, so no troubles */
......@@ -793,15 +847,31 @@ static int amd7930_get_input_port(struct sparcaudio_driver *drv)
return AUDIO_MICROPHONE;
}
/* This chip also supports AUDIO_ENCODING_ALAW, add support later */
static int amd7930_get_output_encoding(struct sparcaudio_driver *drv)
static int amd7930_get_encoding(struct sparcaudio_driver *drv)
{
return AUDIO_ENCODING_ULAW;
struct amd7930_info *info = (struct amd7930_info *)drv->private;
if (info->map.mmr1 & AM_MAP_MMR1_ALAW)
return AUDIO_ENCODING_ALAW;
return AUDIO_ENCODING_ULAW;
}
static int amd7930_get_input_encoding(struct sparcaudio_driver *drv)
static int
amd7930_set_encoding(struct sparcaudio_driver *drv, int value)
{
return AUDIO_ENCODING_ULAW;
struct amd7930_info *info = (struct amd7930_info *)drv->private;
switch (value) {
case AUDIO_ENCODING_ULAW:
info->map.mmr1 &= ~AM_MAP_MMR1_ALAW;
break;
case AUDIO_ENCODING_ALAW:
info->map.mmr1 |= AM_MAP_MMR1_ALAW;
break;
default:
return -EINVAL;
}
amd7930_update_map(drv);
return 0;
}
/* This is what you get. Take it or leave it */
......@@ -810,31 +880,55 @@ static int amd7930_get_output_rate(struct sparcaudio_driver *drv)
return AMD7930_RATE;
}
static int
amd7930_set_output_rate(struct sparcaudio_driver *drv, int value)
{
return (value == AMD7930_RATE) ? 0 : -EINVAL;
}
static int amd7930_get_input_rate(struct sparcaudio_driver *drv)
{
return AMD7930_RATE;
}
static int amd7930_get_output_muted(struct sparcaudio_driver *drv)
static int
amd7930_set_input_rate(struct sparcaudio_driver *drv, int value)
{
return 0;
return (value == AMD7930_RATE) ? 0 : -EINVAL;
}
static int amd7930_get_output_ports(struct sparcaudio_driver *drv)
static int amd7930_get_output_muted(struct sparcaudio_driver *drv)
{
return AUDIO_SPEAKER | AUDIO_HEADPHONE;
return 0;
}
static int amd7930_get_input_ports(struct sparcaudio_driver *drv)
static void amd7930_loopback(struct sparcaudio_driver *drv, unsigned int value)
{
return AUDIO_MICROPHONE;
struct amd7930_info *info = (struct amd7930_info *)drv->private;
if (value)
info->map.mmr1 |= AM_MAP_MMR1_LOOPBACK;
else
info->map.mmr1 &= ~AM_MAP_MMR1_LOOPBACK;
amd7930_update_map(drv);
return;
}
static int amd7930_get_monitor_volume(struct sparcaudio_driver *drv)
static int amd7930_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg,
struct sparcaudio_driver *drv)
{
struct amd7930_info *info = (struct amd7930_info *)drv->private;
return info->mgain;
int retval = 0;
switch (cmd) {
case AUDIO_DIAG_LOOPBACK:
amd7930_loopback(drv, (unsigned int)arg);
break;
default:
retval = -EINVAL;
}
return retval;
}
......@@ -978,7 +1072,7 @@ static int amd7930_get_monitor_volume(struct sparcaudio_driver *drv)
*
*/
#if defined (AMD79C30_ISDN) || defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
static int amd7930_get_irqnum(int dev)
{
struct amd7930_info *info;
......@@ -1094,7 +1188,7 @@ static void amd7930_liu_deactivate(int dev)
}
static void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count,
void (*callback)(void *, int), void *callback_arg)
void (*callback)(void *, int), void *callback_arg)
{
struct amd7930_info *info;
register unsigned long flags;
......@@ -1139,8 +1233,8 @@ static void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count,
}
static void amd7930_drecv(int dev, __u8 *buffer, unsigned int size,
void (*callback)(void *, int, unsigned int),
void *callback_arg)
void (*callback)(void *, int, unsigned int),
void *callback_arg)
{
struct amd7930_info *info;
register unsigned long flags;
......@@ -1184,7 +1278,7 @@ static void amd7930_drecv(int dev, __u8 *buffer, unsigned int size,
restore_flags(flags);
}
static int amd7930_bopen(int dev, unsigned int chan,
static int amd7930_bopen(int dev, unsigned int chan,
int mode, u_char xmit_idle_char)
{
struct amd7930_info *info;
......@@ -1194,10 +1288,10 @@ static int amd7930_bopen(int dev, unsigned int chan,
return -1;
}
if (mode == L1_MODE_HDLC) {
return -1;
}
if (mode == L1_MODE_HDLC) {
return -1;
}
info = (struct amd7930_info *) drivers[dev].private;
save_and_cli(flags);
......@@ -1278,7 +1372,7 @@ static void amd7930_bclose(int dev, unsigned int chan)
static void amd7930_bxmit(int dev, unsigned int chan,
__u8 * buffer, unsigned long count,
void (*callback)(void *, int), void *callback_arg)
void (*callback)(void *, int), void *callback_arg)
{
struct amd7930_info *info;
struct amd7930_channel *Bchan;
......@@ -1303,7 +1397,7 @@ static void amd7930_bxmit(int dev, unsigned int chan,
}
}
static void amd7930_brecv(int dev, unsigned int chan,
static void amd7930_brecv(int dev, unsigned int chan,
__u8 * buffer, unsigned long size,
void (*callback)(void *, int, unsigned int),
void *callback_arg)
......@@ -1345,6 +1439,7 @@ struct foreign_interface amd7930_foreign_interface = {
amd7930_brecv
};
EXPORT_SYMBOL(amd7930_foreign_interface);
#endif
/*
......@@ -1354,7 +1449,7 @@ EXPORT_SYMBOL(amd7930_foreign_interface);
static struct sparcaudio_operations amd7930_ops = {
amd7930_open,
amd7930_release,
NULL, /* amd7930_ioctl */
amd7930_ioctl,
amd7930_start_output,
amd7930_stop_output,
amd7930_start_input,
......@@ -1370,31 +1465,44 @@ static struct sparcaudio_operations amd7930_ops = {
amd7930_get_output_balance,
NULL, /* amd7930_set_input_balance */
amd7930_get_input_balance,
NULL, /* amd7930_set_output_channels */
amd7930_set_output_channels,
amd7930_get_output_channels,
NULL, /* amd7930_set_input_channels */
amd7930_set_input_channels,
amd7930_get_input_channels,
NULL, /* amd7930_set_output_precision */
amd7930_set_output_precision,
amd7930_get_output_precision,
NULL, /* amd7930_set_input_precision */
amd7930_set_input_precision,
amd7930_get_input_precision,
NULL, /* amd7930_set_output_port */
amd7930_set_output_port,
amd7930_get_output_port,
NULL, /* amd7930_set_input_port */
amd7930_get_input_port,
NULL, /* amd7930_set_output_encoding */
amd7930_get_output_encoding,
NULL, /* amd7930_set_input_encoding */
amd7930_get_input_encoding,
NULL, /* amd7930_set_output_rate */
amd7930_set_encoding,
amd7930_get_encoding,
amd7930_set_encoding,
amd7930_get_encoding,
amd7930_set_output_rate,
amd7930_get_output_rate,
NULL, /* amd7930_set_input_rate */
amd7930_set_input_rate,
amd7930_get_input_rate,
amd7930_sunaudio_getdev_sunos,
amd7930_get_output_ports,
amd7930_get_input_ports,
NULL, /* amd7930_set_output_muted */
NULL, /* amd7930_set_output_muted */
amd7930_get_output_muted,
NULL, /* amd7930_set_output_pause */
NULL, /* amd7930_get_output_pause */
NULL, /* amd7930_set_input_pause */
NULL, /* amd7930_get_input_pause */
NULL, /* amd7930_set_output_samples */
NULL, /* amd7930_get_output_samples */
NULL, /* amd7930_set_input_samples */
NULL, /* amd7930_get_input_samples */
NULL, /* amd7930_set_output_error */
NULL, /* amd7930_get_output_error */
NULL, /* amd7930_set_input_error */
NULL, /* amd7930_get_input_error */
amd7930_get_formats,
};
/* Attach to an amd7930 chip given its PROM node. */
......@@ -1453,9 +1561,17 @@ static int amd7930_attach(struct sparcaudio_driver *drv, int node,
memset(&info->map, 0, sizeof(info->map));
info->map.mmr1 = AM_MAP_MMR1_GX | AM_MAP_MMR1_GER
| AM_MAP_MMR1_GR | AM_MAP_MMR1_STG;
/* Start out with speaker, microphone */
info->map.mmr2 |= (AM_MAP_MMR2_LS | AM_MAP_MMR2_AINB);
/* Set the default audio parameters. */
info->rgain = 128;
info->pgain = 200;
info->mgain = 0;
amd7930_update_map(drv);
/* Register the amd7930 with the midlevel audio driver. */
err = register_sparcaudio_driver(drv);
err = register_sparcaudio_driver(drv, 1);
if (err < 0) {
printk(KERN_ERR "amd7930: unable to register\n");
disable_irq(info->irq);
......@@ -1479,7 +1595,7 @@ static void amd7930_detach(struct sparcaudio_driver *drv)
{
struct amd7930_info *info = (struct amd7930_info *)drv->private;
unregister_sparcaudio_driver(drv);
unregister_sparcaudio_driver(drv, 1);
amd7930_idle(info);
disable_irq(info->irq);
free_irq(info->irq, drv);
......
......@@ -14,6 +14,7 @@
#define _AMD7930_H_
#include <linux/types.h>
#include <linux/version.h>
/* Register interface presented to the CPU by the amd7930. */
struct amd7930
......
/*
* drivers/sbus/audio/audio.c
*
* Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu)
* Copyright (C) 1997 Derrick J. Brashear (shadow@dementia.org)
* Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
* Copyright (C) 1997,1998 Derrick J. Brashear (shadow@dementia.org)
* Copyright (C) 1997 Brent Baccala (baccala@freesoft.org)
*
* Mixer code adapted from code contributed by and
* Copyright (C) 1998 Michael Mraka (michael@fi.muni.cz)
* The mixer code cheats; Sparc hardware doesn't generally allow independent
* line control, and this fakes it badly.
*
* SNDCTL_DSP_SETFMT based on code contributed by
* Ion Badulescu (ionut@moisil.cs.columbia.edu)
*
* This is the audio midlayer that sits between the VFS character
* devices and the low-level audio hardware device drivers.
......@@ -26,191 +30,345 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/soundcard.h>
#include <asm/uaccess.h>
#include <linux/version.h>
#include <asm/delay.h>
#include <asm/pgtable.h>
#include <asm/audioio.h>
#undef __AUDIO_DEBUG
#define __AUDIO_ERROR
#undef __AUDIO_TRACE
#ifdef __AUDIO_DEBUG
#define dprintk(x) printk x
#else
#define dprintk(x)
#endif
#ifdef __AUDIO_ERROR
#define eprintk(x) printk x
#else
#define eprintk(x)
#endif
#ifdef __AUDIO_TRACE
#define tprintk(x) printk x
#else
#define tprintk(x)
#endif
/*
* Low-level driver interface.
*/
static short lis_get_elist_ent( strevent_t *list, pid_t pid );
static int lis_add_to_elist( strevent_t **list, pid_t pid, short events );
static int lis_del_from_elist( strevent_t **list, pid_t pid, short events );
static void lis_free_elist( strevent_t **list);
static void kill_procs( struct strevent *elist, int sig, short e);
static struct sparcaudio_driver *drivers[SPARCAUDIO_MAX_DEVICES] = {NULL};
/* This crap to be pulled off into a local include file */
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
/* We only support one low-level audio driver currently. */
static struct sparcaudio_driver *driver = NULL;
#define COPY_IN(arg, get) verify_area(VERIFY_READ, (void *)arg, sizeof(long)); memcpy_fromfs(&get, (long *)arg, sizeof(get));
#define COPY_OUT(arg, ret) verify_area(VERIFY_WRITE, (void *)arg, sizeof(long)); memcpy_tofs((long *)arg, &ret, sizeof(ret));
#define copy_to_user memcpy_tofs
#define copy_from_user memcpy_fromfs
#define signal_pending(x) (((x)->signal) & ~((x)->blocked))
int register_sparcaudio_driver(struct sparcaudio_driver *drv)
#else
#include <asm/uaccess.h>
#include <linux/poll.h>
#define COPY_IN(arg, get) get_user(get, (int *)arg)
#define COPY_OUT(arg, ret) put_user(ret, (int *)arg)
#define sparcaudio_release_ret sparcaudio_release
#define sparcaudioctl_release_ret sparcaudioctl_release
#define sparcaudio_select sparcaudio_poll
#endif
int register_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
{
int i;
int i, dev;
/* If a driver is already present, don't allow the register. */
if (driver)
return -EIO;
/* If we've used up SPARCAUDIO_MAX_DEVICES, fail */
for (dev = 0; dev < SPARCAUDIO_MAX_DEVICES; dev++) {
if (drivers[dev] == NULL) {
break;
}
}
if (drivers[dev]) {
return -EIO;
}
/* Ensure that the driver has a proper operations structure. */
if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output)
if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output ||
!drv->ops->start_input || !drv->ops->stop_input)
return -EINVAL;
/* Setup the circular queues of output and input buffers
*
* Each buffer is a single page, but output buffers might
* be partially filled (by a write with count < 4096),
* so each output buffer also has a paired output size.
*
* Input buffers, on the other hand, always fill completely,
* so we don't need input counts - each contains 4096
* bytes of audio data.
*
* TODO: Make number of input/output buffers tunable parameters
*/
drv->num_output_buffers = 32;
drv->playing_count = 0;
drv->output_front = 0;
drv->output_rear = 0;
drv->output_count = 0;
drv->output_active = 0;
drv->output_buffers = kmalloc(drv->num_output_buffers * sizeof(__u8 *), GFP_KERNEL);
drv->output_sizes = kmalloc(drv->num_output_buffers * sizeof(size_t), GFP_KERNEL);
if (!drv->output_buffers || !drv->output_sizes) goto kmalloc_failed1;
/* Allocate the pages for each output buffer. */
for (i = 0; i < drv->num_output_buffers; i++) {
drv->output_buffers[i] = (void *) __get_free_page(GFP_KERNEL);
if (!drv->output_buffers[i]) goto kmalloc_failed2;
}
/* Setup the circular queues of output and input buffers
*
* Each buffer is a single page, but output buffers might
* be partially filled (by a write with count < output_buffer_size),
* so each output buffer also has a paired output size.
*
* Input buffers, on the other hand, always fill completely,
* so we don't need input counts - each contains input_buffer_size
* bytes of audio data.
*
* TODO: Make number of input/output buffers tunable parameters
*/
drv->num_output_buffers = 8;
drv->output_buffer_size = (4096 * 2);
drv->playing_count = 0;
drv->output_offset = 0;
drv->output_eof = 0;
drv->output_front = 0;
drv->output_rear = 0;
drv->output_count = 0;
drv->output_active = 0;
drv->output_buffers = kmalloc(drv->num_output_buffers *
sizeof(__u8 *), GFP_KERNEL);
drv->output_sizes = kmalloc(drv->num_output_buffers *
sizeof(size_t), GFP_KERNEL);
drv->output_notify = kmalloc(drv->num_output_buffers *
sizeof(char), GFP_KERNEL);
if (!drv->output_buffers || !drv->output_sizes || !drv->output_notify)
goto kmalloc_failed1;
drv->output_buffer = kmalloc((drv->output_buffer_size *
drv->num_output_buffers),
GFP_KERNEL);
if (!drv->output_buffer) goto kmalloc_failed2;
/* Allocate the pages for each output buffer. */
for (i = 0; i < drv->num_output_buffers; i++) {
drv->output_buffers[i] = (void *)(drv->output_buffer +
(i * drv->output_buffer_size));
drv->output_sizes[i] = 0;
drv->output_notify[i] = 0;
}
/* Setup the circular queue of input buffers. */
drv->num_input_buffers = 32;
drv->recording_count = 0;
drv->input_front = 0;
drv->input_rear = 0;
drv->input_count = 0;
drv->input_active = 0;
drv->input_buffers = kmalloc(drv->num_input_buffers * sizeof(__u8 *), GFP_KERNEL);
if (!drv->input_buffers) goto kmalloc_failed3;
/* Allocate the pages for each input buffer. */
for (i = 0; i < drv->num_input_buffers; i++) {
drv->input_buffers[i] = (void *) __get_free_page(GFP_KERNEL);
if (!drv->input_buffers[i]) goto kmalloc_failed4;
/* Setup the circular queue of input buffers. */
drv->num_input_buffers = 8;
drv->input_buffer_size = (4096 * 2);
drv->recording_count = 0;
drv->input_front = 0;
drv->input_rear = 0;
drv->input_count = 0;
drv->input_offset = 0;
drv->input_size = 0;
drv->input_active = 0;
drv->input_buffers = kmalloc(drv->num_input_buffers * sizeof(__u8 *),
GFP_KERNEL);
drv->input_sizes = kmalloc(drv->num_input_buffers *
sizeof(size_t), GFP_KERNEL);
if (!drv->input_buffers || !drv->input_sizes) goto kmalloc_failed3;
/* Allocate the pages for each input buffer. */
if (duplex == 1) {
drv->input_buffer = kmalloc((drv->input_buffer_size *
drv->num_input_buffers),
GFP_KERNEL);
if (!drv->input_buffer) goto kmalloc_failed4;
for (i = 0; i < drv->num_input_buffers; i++) {
drv->input_buffers[i] = (void *)(drv->input_buffer +
(i * drv->input_buffer_size));
}
} else {
if (duplex == 2) {
drv->input_buffer = drv->output_buffer;
drv->input_buffer_size = drv->output_buffer_size;
drv->num_input_buffers = drv->num_output_buffers;
for (i = 0; i < drv->num_input_buffers; i++)
drv->input_buffers[i] = drv->output_buffers[i];
} else {
for (i = 0; i < drv->num_input_buffers; i++)
drv->input_buffers[i] = NULL;
}
}
/* Take note of our duplexity */
drv->duplex = duplex;
/* Ensure that the driver is marked as not being open. */
drv->flags = 0;
MOD_INC_USE_COUNT;
driver = drv;
return 0;
/* Take driver slot, note which we took */
drv->index = dev;
drivers[dev] = drv;
return 0;
kmalloc_failed4:
for (i--; i >= 0; i--)
free_page((unsigned long) drv->input_buffers[i]);
kfree(drv->input_buffer);
kmalloc_failed3:
if (drv->input_buffers)
kfree(drv->input_buffers);
i = drv->num_output_buffers;
if (drv->input_sizes)
kfree(drv->input_sizes);
if (drv->input_buffers)
kfree(drv->input_buffers);
i = drv->num_output_buffers;
kmalloc_failed2:
for (i--; i >= 0; i--)
free_page((unsigned long) drv->output_buffers[i]);
kfree(drv->output_buffer);
kmalloc_failed1:
if (drv->output_buffers)
kfree(drv->output_buffers);
if (drv->output_sizes)
kfree(drv->output_sizes);
return -ENOMEM;
if (drv->output_buffers)
kfree(drv->output_buffers);
if (drv->output_sizes)
kfree(drv->output_sizes);
if (drv->output_notify)
kfree(drv->output_notify);
return -ENOMEM;
}
int unregister_sparcaudio_driver(struct sparcaudio_driver *drv)
int unregister_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
{
int i;
/* Make sure that the current driver is unregistering. */
if (driver != drv)
/* Figure out which driver is unregistering */
if (drivers[drv->index] != drv)
return -EIO;
/* Deallocate the queue of output buffers. */
for (i = 0; i < driver->num_output_buffers; i++)
free_page((unsigned long) driver->output_buffers[i]);
kfree(driver->output_buffers);
kfree(driver->output_sizes);
kfree(drv->output_buffer);
kfree(drv->output_buffers);
kfree(drv->output_sizes);
kfree(drv->output_notify);
/* Deallocate the queue of input buffers. */
if (duplex == 1) {
kfree(drv->input_buffer);
kfree(drv->input_sizes);
}
kfree(drv->input_buffers);
/* Deallocate the queue of input buffers. */
for (i = 0; i < driver->num_input_buffers; i++)
free_page((unsigned long) driver->input_buffers[i]);
kfree(driver->input_buffers);
if (&(drv->sd_siglist) != NULL)
lis_free_elist( &(drv->sd_siglist) );
MOD_DEC_USE_COUNT;
driver = NULL;
/* Null the appropriate driver */
drivers[drv->index] = NULL;
return 0;
}
void sparcaudio_output_done(struct sparcaudio_driver * drv, int reclaim)
void sparcaudio_output_done(struct sparcaudio_driver * drv, int status)
{
/* Reclaim a buffer unless it's still in the DMA pipe */
if (reclaim) {
if (drv->output_count > 0)
drv->output_count--;
else
if (drv->playing_count > 0)
drv->playing_count--;
} else
drv->playing_count++;
/* Point the queue after the "done" buffer. */
drv->output_size -= drv->output_sizes[drv->output_front];
drv->output_front = (drv->output_front + 1) % drv->num_output_buffers;
/* If the output queue is empty, shutdown the driver. */
if (drv->output_count == 0) {
if (drv->playing_count == 0) {
/* Stop the lowlevel driver from outputing. */
drv->ops->stop_output(drv);
drv->output_active = 0;
/* Wake up any waiting writers or syncers and return. */
wake_up_interruptible(&drv->output_write_wait);
wake_up_interruptible(&drv->output_drain_wait);
return;
}
}
/* If we got back a buffer, see if anyone wants to write to it */
if (reclaim || ((drv->output_count + drv->playing_count)
< drv->num_output_buffers))
wake_up_interruptible(&drv->output_write_wait);
/*
* If !status, just restart current output.
* If status & 1, a buffer is finished; make it available again.
* If status & 2, a buffer was claimed for DMA and is still in use.
*
* The playing_count for non-DMA hardware should never be non-zero.
*/
if (status & 1) {
if (drv->playing_count)
drv->playing_count--;
else {
drv->output_count--;
drv->output_size -= drv->output_sizes[drv->output_front];
if (drv->output_notify[drv->output_front] == 1) {
drv->output_eof++;
drv->output_notify[drv->output_front] = 0;
kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
}
drv->output_front = (drv->output_front + 1) %
drv->num_output_buffers;
}
}
if (status & 2) {
drv->output_count--;
drv->playing_count++;
drv->output_size -= drv->output_sizes[drv->output_front];
if (drv->output_notify[drv->output_front] == 1) {
drv->output_eof++;
drv->output_notify[drv->output_front] = 0;
kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
}
drv->output_front = (drv->output_front + 1) %
drv->num_output_buffers;
}
/* If we've played everything go inactive. */
if ((drv->output_count < 1) && (drv->playing_count < 1))
drv->output_active = 0;
/* If we got back a buffer, see if anyone wants to write to it */
if ((status & 1) || ((drv->output_count + drv->playing_count)
< drv->num_output_buffers))
wake_up_interruptible(&drv->output_write_wait);
/* If the output queue is empty, shut down the driver. */
if ((drv->output_count < 1) && (drv->playing_count < 1)) {
kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
/* Stop the lowlevel driver from outputing. */
/* drv->ops->stop_output(drv); Should not be necessary -- DJB 5/25/98 */
drv->output_active = 0;
/* Wake up any waiting writers or syncers and return. */
wake_up_interruptible(&drv->output_write_wait);
wake_up_interruptible(&drv->output_drain_wait);
return;
}
/* Start next block of output if we have it */
if (drv->output_count > 0) {
drv->ops->start_output(drv, drv->output_buffers[drv->output_front],
drv->output_sizes[drv->output_front]);
drv->output_sizes[drv->output_front]);
drv->output_active = 1;
} else
drv->output_active = 0;
}
void sparcaudio_input_done(struct sparcaudio_driver * drv)
void sparcaudio_input_done(struct sparcaudio_driver * drv, int status)
{
/* Point the queue after the "done" buffer. */
drv->input_front = (drv->input_front + 1) % drv->num_input_buffers;
drv->input_count++;
/* If the input queue is full, shutdown the driver. */
if (drv->input_count == drv->num_input_buffers) {
/* Stop the lowlevel driver from inputing. */
drv->ops->stop_input(drv);
drv->input_active = 0;
} else {
/* Otherwise, give the driver the next buffer. */
drv->ops->start_input(drv, drv->input_buffers[drv->input_front],
4096);
}
/* Wake up any tasks that are waiting. */
wake_up_interruptible(&drv->input_read_wait);
/* Deal with the weird case here */
if (drv->duplex == 2) {
if (drv->input_count < drv->num_input_buffers)
drv->input_count++;
drv->ops->start_input(drv, drv->input_buffers[drv->input_front],
drv->input_buffer_size);
wake_up_interruptible(&drv->input_read_wait);
return;
}
/*
* If status % 2, they filled a buffer for us.
* If status & 2, they took a buffer from us.
*/
if ((status % 2) == 1) {
drv->input_count++;
drv->recording_count--;
drv->input_size+=drv->input_buffer_size;
}
if (status > 1) {
drv->recording_count++;
drv->input_front = (drv->input_front + 1) % drv->num_input_buffers;
}
dprintk(("f%d r%d c%d u%d\n", drv->input_front, drv->input_rear, drv->input_count, drv->recording_count));
/* If the input queue is full, shutdown the driver. */
if ((drv->input_count + drv->recording_count) == drv->num_input_buffers) {
kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
/* Stop the lowlevel driver from inputing. */
drv->ops->stop_input(drv);
drv->input_active = 0;
} else {
/* Otherwise, give the driver the next buffer. */
drv->ops->start_input(drv, drv->input_buffers[drv->input_front],
drv->input_buffer_size);
}
/* Wake up any tasks that are waiting. */
wake_up_interruptible(&drv->input_read_wait);
}
......@@ -219,804 +377,1680 @@ void sparcaudio_input_done(struct sparcaudio_driver * drv)
* VFS layer interface
*/
static loff_t sparcaudio_llseek(struct file * file, loff_t offset, int origin)
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
static int sparcaudio_select(struct inode * inode, struct file * file,
int sel_type, select_table * wait)
{
return -ESPIPE;
}
struct sparcaudio_driver *drv = drivers[(MINOR(inode->i_rdev) >>
SPARCAUDIO_DEVICE_SHIFT)];
switch (sel_type) {
case SEL_IN:
if (((!file->f_flags & O_NONBLOCK) && drv->input_count) ||
(drv->input_size > drv->buffer_size)) {
dprintk(("read ready: c%d o%d\n", drv->input_count, drv->input_offset));
return 1;
}
select_wait(&drv->input_read_wait, wait);
break;
case SEL_OUT:
dprintk(("sel out: c%d o%d p%d\n", drv->output_count, drv->output_offset, drv->playing_count));
if ((drv->output_count + drv->playing_count) < (drv->num_output_buffers)) {
return 1;
}
select_wait(&drv->output_write_wait, wait);
break;
case SEL_EX:
break;
}
static ssize_t sparcaudio_read(struct file * file,
char *buf, size_t count, loff_t *ppos)
return 0;
}
#else
static unsigned int sparcaudio_poll(struct file *file, poll_table * wait)
{
int bytes_to_copy;
if (! file->f_mode & FMODE_READ)
return -EINVAL;
if (driver->input_count == 0) {
interruptible_sleep_on(&driver->input_read_wait);
if (signal_pending(current))
return -EINTR;
}
unsigned int mask = 0;
struct inode *inode = file->f_dentry->d_inode;
struct sparcaudio_driver *drv = drivers[(MINOR(inode->i_rdev) >>
SPARCAUDIO_DEVICE_SHIFT)];
poll_wait(file, &drv->input_read_wait, wait);
poll_wait(file, &drv->output_write_wait, wait);
if (((!file->f_flags & O_NONBLOCK) && drv->input_count) ||
(drv->input_size > drv->buffer_size)) {
mask |= POLLIN | POLLRDNORM;
}
if ((drv->output_count + drv->playing_count) < (drv->num_output_buffers)) {
mask |= POLLOUT | POLLWRNORM;
}
return mask;
}
#endif
bytes_to_copy = 4096 - driver->input_offset;
if (bytes_to_copy > count)
bytes_to_copy = count;
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
static int sparcaudio_lseek(struct inode * inode, struct file * file,
off_t offset, int origin)
#else
static loff_t sparcaudio_lseek(struct file * file, loff_t offset, int origin)
#endif
{
return -ESPIPE;
}
copy_to_user_ret(buf, driver->input_buffers[driver->input_rear]+driver->input_offset,
bytes_to_copy, -EFAULT);
driver->input_offset += bytes_to_copy;
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
static int sparcaudio_read(struct inode * inode, struct file * file,
char *buf, int count)
#else
static ssize_t sparcaudio_read(struct file * file, char *buf,
size_t count, loff_t *ppos)
#endif
{
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
struct inode *inode = file->f_dentry->d_inode;
#endif
struct sparcaudio_driver *drv = drivers[(MINOR(inode->i_rdev) >>
SPARCAUDIO_DEVICE_SHIFT)];
int bytes_to_copy, bytes_read = 0, err;
if (driver->input_offset >= 4096) {
driver->input_rear = (driver->input_rear + 1) % driver->num_input_buffers;
driver->input_count--;
driver->input_offset = 0;
}
if (! file->f_mode & FMODE_READ)
return -EINVAL;
return bytes_to_copy;
}
if ((file->f_flags & O_NONBLOCK) && (drv->input_size < count))
return -EAGAIN;
while (count > 0) {
if (drv->input_count == 0) {
/* This *should* never happen. */
if (file->f_flags & O_NONBLOCK) {
printk("Warning: audio input leak!\n");
return -EAGAIN;
}
interruptible_sleep_on(&drv->input_read_wait);
if (signal_pending(current))
return -EINTR;
}
bytes_to_copy = drv->input_buffer_size - drv->input_offset;
if (bytes_to_copy > count)
bytes_to_copy = count;
err = verify_area(VERIFY_WRITE, buf, bytes_to_copy);
if (err)
return err;
copy_to_user(buf, drv->input_buffers[drv->input_rear]+drv->input_offset,
bytes_to_copy);
drv->input_offset += bytes_to_copy;
drv->input_size -= bytes_to_copy;
buf += bytes_to_copy;
count -= bytes_to_copy;
bytes_read += bytes_to_copy;
if (drv->input_offset >= drv->input_buffer_size) {
drv->input_rear = (drv->input_rear + 1) %
drv->num_input_buffers;
drv->input_count--;
drv->input_offset = 0;
}
/* If we're in "loop audio" mode, try waking up the other side
* in case they're waiting for us to eat a block.
*/
if (drv->duplex == 2) {
wake_up_interruptible(&drv->output_write_wait);
}
}
static void sparcaudio_reorganize_buffers(struct sparcaudio_driver * driver)
{
/* It may never matter but if it does this routine will pack */
/* buffers to free space for more data */
return bytes_read;
}
static void sparcaudio_sync_output(struct sparcaudio_driver * driver)
static void sparcaudio_sync_output(struct sparcaudio_driver * drv)
{
unsigned long flags;
/* If the low-level driver is not active, activate it. */
save_and_cli(flags);
if ((!driver->output_active) && (driver->output_count > 0)) {
driver->ops->start_output(driver,
driver->output_buffers[driver->output_front],
driver->output_sizes[driver->output_front]);
driver->output_active = 1;
}
restore_flags(flags);
unsigned long flags;
/* If the low-level driver is not active, activate it. */
save_and_cli(flags);
if ((!drv->output_active) && (drv->output_count > 0)) {
drv->ops->start_output(drv,
drv->output_buffers[drv->output_front],
drv->output_sizes[drv->output_front]);
drv->output_active = 1;
}
restore_flags(flags);
}
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
static int sparcaudio_write(struct inode * inode, struct file * file,
const char *buf, int count)
#else
static ssize_t sparcaudio_write(struct file * file, const char *buf,
size_t count, loff_t *ppos)
size_t count, loff_t *ppos)
#endif
{
int bytes_written = 0, bytes_to_copy;
if (! file->f_mode & FMODE_WRITE)
return -EINVAL;
/* Loop until all output is written to device. */
while (count > 0) {
/* Check to make sure that an output buffer is available. */
/* If not, make valiant attempt */
if (driver->num_output_buffers ==
(driver->output_count + driver->playing_count))
sparcaudio_reorganize_buffers(driver);
if (driver->num_output_buffers ==
(driver->output_count + driver->playing_count)) {
/* We need buffers, so... */
sparcaudio_sync_output(driver);
interruptible_sleep_on(&driver->output_write_wait);
if (signal_pending(current))
return bytes_written > 0 ? bytes_written : -EINTR;
}
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
struct inode *inode = file->f_dentry->d_inode;
#endif
struct sparcaudio_driver *drv = drivers[(MINOR(inode->i_rdev) >>
SPARCAUDIO_DEVICE_SHIFT)];
int bytes_written = 0, bytes_to_copy, err;
if (! file->f_mode & FMODE_WRITE)
return -EINVAL;
/*
* A signal they want notification when this is processed. Too bad
* sys_write doesn't tell us unless you patch it, in 2.0 kernels.
*/
if (count == 0) {
#ifndef notdef
drv->output_eof++;
kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
#else
/* Nice code, but the world isn't ready yet... */
drv->output_notify[drv->output_rear] = 1;
#endif
}
/* Loop until all output is written to device. */
while (count > 0) {
/* Check to make sure that an output buffer is available. */
if (drv->num_output_buffers == (drv->output_count + drv->playing_count)) {
/* We need buffers, so... */
sparcaudio_sync_output(drv);
if (file->f_flags & O_NONBLOCK) {
return -EAGAIN;
}
interruptible_sleep_on(&drv->output_write_wait);
if (signal_pending(current))
return bytes_written > 0 ? bytes_written : -EINTR;
}
/* No buffers were freed. Go back to sleep */
if (driver->num_output_buffers ==
(driver->output_count + driver->playing_count))
continue;
/* Determine how much we can copy in this iteration. */
bytes_to_copy = count;
if (bytes_to_copy > 4096)
bytes_to_copy = 4096;
copy_from_user_ret(driver->output_buffers[driver->output_rear],
buf, bytes_to_copy, -EFAULT);
/* Update the queue pointers. */
buf += bytes_to_copy;
count -= bytes_to_copy;
bytes_written += bytes_to_copy;
driver->output_sizes[driver->output_rear] = bytes_to_copy;
driver->output_rear = (driver->output_rear + 1) % driver->num_output_buffers;
driver->output_count++;
driver->output_size += bytes_to_copy;
}
sparcaudio_sync_output(driver);
/* No buffers were freed. Go back to sleep */
if (drv->num_output_buffers == (drv->output_count + drv->playing_count))
continue;
/* Deal with the weird case of a reader in the write area by trying to
* let them keep ahead of us... Go to sleep until they start servicing.
*/
if ((drv->duplex == 2) && (drv->flags & SDF_OPEN_READ) &&
(drv->output_rear == drv->input_rear) && (drv->input_count > 0)) {
if (file->f_flags & O_NONBLOCK) {
return -EAGAIN;
}
interruptible_sleep_on(&drv->output_write_wait);
if (signal_pending(current))
return bytes_written > 0 ? bytes_written : -EINTR;
}
/* Return the number of bytes written to the caller. */
return bytes_written;
}
/* Determine how much we can copy in this iteration. */
bytes_to_copy = count;
if (bytes_to_copy > drv->output_buffer_size - drv->output_offset)
bytes_to_copy = drv->output_buffer_size - drv->output_offset;
err = verify_area(VERIFY_READ, buf, bytes_to_copy);
if (err)
return err;
#define COPY_IN(arg, get) get_user(get, (int *)arg)
#define COPY_OUT(arg, ret) put_user(ret, (int *)arg)
copy_from_user(drv->output_buffers[drv->output_rear]+drv->output_offset, buf, bytes_to_copy);
/* Update the queue pointers. */
buf += bytes_to_copy;
count -= bytes_to_copy;
bytes_written += bytes_to_copy;
/* A block can get orphaned in a flush and not cleaned up. */
if (drv->output_offset)
drv->output_sizes[drv->output_rear] += bytes_to_copy;
else
drv->output_sizes[drv->output_rear] = bytes_to_copy;
drv->output_notify[drv->output_rear] = 0;
if (drv->output_sizes[drv->output_rear] == drv->output_buffer_size) {
drv->output_rear = (drv->output_rear + 1)
% drv->num_output_buffers;
drv->output_count++;
drv->output_offset = 0;
} else
drv->output_offset += bytes_to_copy;
drv->output_size+=bytes_to_copy;
}
sparcaudio_sync_output(drv);
/* Return the number of bytes written to the caller. */
return bytes_written;
}
/* Add these in as new devices are supported. Belongs in audioio.h, actually */
#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_VOLUME)
#define MONO_DEVICES (SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SPEAKER | SOUND_MASK_MIC)
#define MONO_DEVICES (SOUND_MASK_SPEAKER | SOUND_MASK_MIC)
static inline int sparcaudio_mixer_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
static int sparcaudio_mixer_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
int i = 0, j = 0;
if (_IOC_DIR(cmd) & _IOC_WRITE) {
/* For any missing routines, pretend we changed things anyhow for now */
switch (cmd & 0xff) {
case SOUND_MIXER_VOLUME:
if (driver->ops->get_output_channels)
j = driver->ops->get_output_channels(driver);
COPY_IN(arg, i);
if (j == 1) {
i = s_to_m(i);
if (driver->ops->set_output_volume)
driver->ops->set_output_volume(driver, i * 255/100);
if (driver->ops->get_output_volume)
i = driver->ops->get_output_volume(driver);
i = m_to_s(i);
} else {
/* there should be stuff here which calculates balance and
volume on a stereo device. will do it eventually */
i = s_to_g(i);
if (driver->ops->set_output_volume)
driver->ops->set_output_volume(driver, i * 255/100);
if (driver->ops->get_output_volume)
i = driver->ops->get_output_volume(driver);
j = s_to_b(i);
if (driver->ops->set_output_balance)
driver->ops->set_output_balance(driver, j);
if (driver->ops->get_output_balance)
j = driver->ops->get_output_balance(driver);
i = b_to_s(i,j);
}
return COPY_OUT(arg, i);
default:
/* Play like we support other things */
return COPY_OUT(arg, i);
}
} else {
switch (cmd & 0xff) {
case SOUND_MIXER_RECSRC:
if (driver->ops->get_input_port)
i = driver->ops->get_input_port(driver);
/* only one should ever be selected */
if (i & AUDIO_ANALOG_LOOPBACK) j = SOUND_MASK_IMIX; /* ? */
if (i & AUDIO_CD) j = SOUND_MASK_CD;
if (i & AUDIO_LINE_IN) j = SOUND_MASK_LINE;
if (i & AUDIO_MICROPHONE) j = SOUND_MASK_MIC;
return COPY_OUT(arg, j);
case SOUND_MIXER_RECMASK:
if (driver->ops->get_input_ports)
i = driver->ops->get_input_ports(driver);
/* what do we support? */
if (i & AUDIO_MICROPHONE) j |= SOUND_MASK_MIC;
if (i & AUDIO_LINE_IN) j |= SOUND_MASK_LINE;
if (i & AUDIO_CD) j |= SOUND_MASK_CD;
if (i & AUDIO_ANALOG_LOOPBACK) j |= SOUND_MASK_IMIX; /* ? */
return COPY_OUT(arg, j);
case SOUND_MIXER_CAPS: /* mixer capabilities */
i = SOUND_CAP_EXCL_INPUT;
return COPY_OUT(arg, i);
case SOUND_MIXER_DEVMASK: /* all supported devices */
case SOUND_MIXER_STEREODEVS: /* what supports stereo */
if (driver->ops->get_input_ports)
i = driver->ops->get_input_ports(driver);
/* what do we support? */
if (i & AUDIO_MICROPHONE) j |= SOUND_MASK_MIC;
if (i & AUDIO_LINE_IN) j |= SOUND_MASK_LINE;
if (i & AUDIO_CD) j |= SOUND_MASK_CD;
if (i & AUDIO_ANALOG_LOOPBACK) j |= SOUND_MASK_IMIX; /* ? */
if (driver->ops->get_output_ports)
i = driver->ops->get_output_ports(driver);
if (i & AUDIO_SPEAKER) j |= SOUND_MASK_SPEAKER;
if (i & AUDIO_HEADPHONE) j |= SOUND_MASK_LINE; /* ? */
if (i & AUDIO_LINE_OUT) j |= SOUND_MASK_LINE;
j |= SOUND_MASK_VOLUME;
if ((cmd & 0xff) == SOUND_MIXER_STEREODEVS)
j &= ~(MONO_DEVICES);
return COPY_OUT(arg, j);
case SOUND_MIXER_VOLUME:
if (driver->ops->get_output_channels)
j = driver->ops->get_output_channels(driver);
if (j == 1) {
if (driver->ops->get_output_volume)
i = driver->ops->get_output_volume(driver);
i = m_to_s(i);
} else {
/* there should be stuff here which calculates balance and
volume on a stereo device. will do it eventually */
if (driver->ops->get_output_volume)
i = driver->ops->get_output_volume(driver);
if (driver->ops->get_output_balance)
j = driver->ops->get_output_balance(driver);
i = b_to_s(i,j);
}
return COPY_OUT(arg, i);
default:
/* Play like we support other things */
return COPY_OUT(arg, i);
}
}
struct sparcaudio_driver *drv = drivers[(MINOR(inode->i_rdev) >>
SPARCAUDIO_DEVICE_SHIFT)];
unsigned long i = 0, j = 0, k = 0;
k = (unsigned long) &arg;
switch (cmd) {
case SOUND_MIXER_WRITE_RECLEV:
case SOUND_MIXER_WRITE_MIC:
case SOUND_MIXER_WRITE_CD:
case SOUND_MIXER_WRITE_LINE:
tprintk(("setting input volume (0x%x)", k));
if (drv->ops->get_input_channels)
j = drv->ops->get_input_channels(drv);
if (j == 1) {
i = s_to_m(k);
tprintk((" for mono to %d\n", i));
if (drv->ops->set_input_volume)
drv->ops->set_input_volume(drv, i);
if (drv->ops->get_input_volume)
i = drv->ops->get_input_volume(drv);
i = m_to_s(i);
} else {
i = s_to_g(k);
j = s_to_b(k);
tprintk((" for stereo to to %d (bal %d)\n", i, j));
if (drv->ops->set_input_volume)
drv->ops->set_input_volume(drv, i);
if (drv->ops->get_input_volume)
i = drv->ops->get_input_volume(drv);
if (drv->ops->set_input_balance)
drv->ops->set_input_balance(drv, j);
if (drv->ops->get_input_balance)
j = drv->ops->get_input_balance(drv);
i = b_to_s(i,j);
}
return COPY_OUT(arg, i);
case SOUND_MIXER_WRITE_PCM:
case SOUND_MIXER_WRITE_VOLUME:
case SOUND_MIXER_WRITE_SPEAKER:
tprintk(("setting output volume (0x%x)", k));
if (drv->ops->get_output_channels)
j = drv->ops->get_output_channels(drv);
if (j == 1) {
i = s_to_m(k);
tprintk((" for mono to %d\n", i));
if (drv->ops->set_output_volume)
drv->ops->set_output_volume(drv, i);
if (drv->ops->get_output_volume)
i = drv->ops->get_output_volume(drv);
i = m_to_s(i);
} else {
i = s_to_g(k);
j = s_to_b(k);
tprintk((" for stereo to to %d (bal %d)\n", i, j));
if (drv->ops->set_output_volume)
drv->ops->set_output_volume(drv, i);
if (drv->ops->get_output_volume)
i = drv->ops->get_output_volume(drv);
if (drv->ops->set_output_balance)
drv->ops->set_output_balance(drv, j);
if (drv->ops->get_output_balance)
j = drv->ops->get_output_balance(drv);
i = b_to_s(i,j);
}
return COPY_OUT(arg, i);
case SOUND_MIXER_READ_RECSRC:
if (drv->ops->get_input_port)
i = drv->ops->get_input_port(drv);
/* only one should ever be selected */
if (i & AUDIO_ANALOG_LOOPBACK) j = SOUND_MASK_IMIX; /* ? */
if (i & AUDIO_CD) j = SOUND_MASK_CD;
if (i & AUDIO_LINE_IN) j = SOUND_MASK_LINE;
if (i & AUDIO_MICROPHONE) j = SOUND_MASK_MIC;
return COPY_OUT(arg, j);
case SOUND_MIXER_WRITE_RECSRC:
if (!drv->ops->set_input_port)
return -EINVAL;
if (arg & SOUND_MASK_IMIX) j |= AUDIO_ANALOG_LOOPBACK;
if (arg & SOUND_MASK_CD) j |= AUDIO_CD;
if (arg & SOUND_MASK_LINE) j |= AUDIO_LINE_IN;
if (arg & SOUND_MASK_MIC) j |= AUDIO_MICROPHONE;
tprintk(("setting inport to %d\n", j));
i = drv->ops->set_input_port(drv, j);
return COPY_OUT(arg, i);
case SOUND_MIXER_READ_RECMASK:
if (drv->ops->get_input_ports)
i = drv->ops->get_input_ports(drv);
/* what do we support? */
if (i & AUDIO_MICROPHONE) j |= SOUND_MASK_MIC;
if (i & AUDIO_LINE_IN) j |= SOUND_MASK_LINE;
if (i & AUDIO_CD) j |= SOUND_MASK_CD;
if (i & AUDIO_ANALOG_LOOPBACK) j |= SOUND_MASK_IMIX; /* ? */
return COPY_OUT(arg, j);
case SOUND_MIXER_READ_CAPS: /* mixer capabilities */
i = SOUND_CAP_EXCL_INPUT;
return COPY_OUT(arg, i);
case SOUND_MIXER_READ_DEVMASK: /* all supported devices */
case SOUND_MIXER_READ_STEREODEVS: /* what supports stereo */
if (drv->ops->get_input_ports)
i = drv->ops->get_input_ports(drv);
/* what do we support? */
if (i & AUDIO_MICROPHONE) j |= SOUND_MASK_MIC;
if (i & AUDIO_LINE_IN) j |= SOUND_MASK_LINE;
if (i & AUDIO_CD) j |= SOUND_MASK_CD;
if (i & AUDIO_ANALOG_LOOPBACK) j |= SOUND_MASK_IMIX; /* ? */
if (drv->ops->get_output_ports)
i = drv->ops->get_output_ports(drv);
if (i & AUDIO_SPEAKER) j |= SOUND_MASK_SPEAKER;
if (i & AUDIO_HEADPHONE) j |= SOUND_MASK_LINE; /* ? */
if (i & AUDIO_LINE_OUT) j |= SOUND_MASK_LINE;
j |= SOUND_MASK_VOLUME;
if (cmd == SOUND_MIXER_READ_STEREODEVS)
j &= ~(MONO_DEVICES);
return COPY_OUT(arg, j);
case SOUND_MIXER_READ_PCM:
case SOUND_MIXER_READ_SPEAKER:
case SOUND_MIXER_READ_VOLUME:
if (drv->ops->get_output_channels)
j = drv->ops->get_output_channels(drv);
if (j == 1) {
if (drv->ops->get_output_volume)
i = drv->ops->get_output_volume(drv);
i = m_to_s(i);
} else {
if (drv->ops->get_output_volume)
i = drv->ops->get_output_volume(drv);
if (drv->ops->get_output_balance)
j = drv->ops->get_output_balance(drv);
i = b_to_s(i,j);
}
return COPY_OUT((int *)arg, i);
case SOUND_MIXER_READ_RECLEV:
case SOUND_MIXER_READ_MIC:
case SOUND_MIXER_READ_CD:
case SOUND_MIXER_READ_LINE:
if (drv->ops->get_input_channels)
j = drv->ops->get_input_channels(drv);
if (j == 1) {
if (drv->ops->get_input_volume)
i = drv->ops->get_input_volume(drv);
i = m_to_s(i);
} else {
if (drv->ops->get_input_volume)
i = drv->ops->get_input_volume(drv);
if (drv->ops->get_input_balance)
j = drv->ops->get_input_balance(drv);
i = b_to_s(i,j);
}
return COPY_OUT((int *)arg, i);
default:
return -EINVAL;
}
}
static int sparcaudio_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
/* AUDIO_SETINFO uses these to set values if possible. */
static __inline__ int
__sparcaudio_if_set_do(struct sparcaudio_driver *drv,
int (*set_function)(struct sparcaudio_driver *, int),
int (*get_function)(struct sparcaudio_driver *),
unsigned int value)
{
int retval = 0;
struct audio_info ainfo;
if (((cmd >> 8) & 0xff) == 'M') {
return sparcaudio_mixer_ioctl(inode, file, cmd, arg);
}
switch (cmd) {
case SNDCTL_DSP_SYNC:
case AUDIO_DRAIN:
if (driver->output_count > 0) {
interruptible_sleep_on(&driver->output_drain_wait);
retval = signal_pending(current) ? -EINTR : 0;
}
break;
case AUDIO_FLUSH:
if (driver->output_active && (file->f_mode & FMODE_WRITE)) {
wake_up_interruptible(&driver->output_write_wait);
driver->ops->stop_output(driver);
driver->output_active = 0;
driver->output_front = 0;
driver->output_rear = 0;
driver->output_count = 0;
driver->output_size = 0;
driver->playing_count = 0;
}
if (driver->input_active && (file->f_mode & FMODE_READ)) {
wake_up_interruptible(&driver->input_read_wait);
driver->ops->stop_input(driver);
driver->input_active = 0;
driver->input_front = 0;
driver->input_rear = 0;
driver->input_count = 0;
driver->recording_count = 0;
}
if ((file->f_mode & FMODE_READ) &&
!(driver->flags & SDF_OPEN_READ)) {
driver->ops->start_input(driver,
driver->input_buffers[driver->input_front],
4096);
driver->input_active = 1;
}
if ((file->f_mode & FMODE_WRITE) &&
!(driver->flags & SDF_OPEN_WRITE)) {
sparcaudio_sync_output(driver);
}
break;
case AUDIO_GETDEV:
if (driver->ops->sunaudio_getdev) {
audio_device_t tmp;
driver->ops->sunaudio_getdev(driver, &tmp);
copy_to_user_ret((audio_device_t *)arg, &tmp, sizeof(tmp), -EFAULT);
} else
retval = -EINVAL;
break;
case AUDIO_GETDEV_SUNOS:
if (driver->ops->sunaudio_getdev_sunos) {
int tmp=driver->ops->sunaudio_getdev_sunos(driver);
if (put_user(tmp, (int *)arg))
retval = -EFAULT;
} else
retval = -EINVAL;
if (set_function && Modify(value))
return (int)set_function(drv, value);
else if (get_function)
return (int)get_function(drv);
else
return 0;
}
break;
static __inline__ int
__sparcaudio_if_setc_do(struct sparcaudio_driver *drv,
int (*set_function)(struct sparcaudio_driver *, int),
int (*get_function)(struct sparcaudio_driver *),
unsigned char value)
{
if (set_function && Modifyc(value))
return (char)set_function(drv, (int)value);
else if (get_function)
return (char)get_function(drv);
else
return 0;
}
case AUDIO_GETINFO:
AUDIO_INITINFO(&ainfo);
if (driver->ops->get_input_rate)
ainfo.record.sample_rate =
driver->ops->get_input_rate(driver);
if (driver->ops->get_input_channels)
ainfo.record.channels =
driver->ops->get_input_channels(driver);
if (driver->ops->get_input_precision)
ainfo.record.precision =
driver->ops->get_input_precision(driver);
if (driver->ops->get_input_encoding)
ainfo.record.encoding =
driver->ops->get_input_encoding(driver);
if (driver->ops->get_input_volume)
ainfo.record.gain =
driver->ops->get_input_volume(driver);
if (driver->ops->get_input_port)
ainfo.record.port =
driver->ops->get_input_port(driver);
if (driver->ops->get_input_ports)
ainfo.record.avail_ports =
driver->ops->get_input_ports(driver);
ainfo.record.buffer_size = 4096;
ainfo.record.samples = 0;
ainfo.record.eof = 0;
ainfo.record.pause = 0;
ainfo.record.error = 0;
ainfo.record.waiting = 0;
if (driver->ops->get_input_balance)
ainfo.record.balance =
driver->ops->get_input_balance(driver);
ainfo.record.minordev = 4;
ainfo.record.open = 1;
ainfo.record.active = 0;
if (driver->ops->get_output_rate)
ainfo.play.sample_rate =
driver->ops->get_output_rate(driver);
if (driver->ops->get_output_channels)
ainfo.play.channels =
driver->ops->get_output_channels(driver);
if (driver->ops->get_output_precision)
ainfo.play.precision =
driver->ops->get_output_precision(driver);
if (driver->ops->get_output_encoding)
ainfo.play.encoding =
driver->ops->get_output_encoding(driver);
if (driver->ops->get_output_volume)
ainfo.play.gain =
driver->ops->get_output_volume(driver);
if (driver->ops->get_output_port)
ainfo.play.port =
driver->ops->get_output_port(driver);
if (driver->ops->get_output_ports)
ainfo.play.avail_ports =
driver->ops->get_output_ports(driver);
/* This is not defined in the play context in Solaris */
ainfo.play.buffer_size = 0;
ainfo.play.samples = 0;
ainfo.play.eof = 0;
ainfo.play.pause = 0;
ainfo.play.error = 0;
ainfo.play.waiting = waitqueue_active(&driver->open_wait);
if (driver->ops->get_output_balance)
ainfo.play.balance =
(unsigned char)driver->ops->get_output_balance(driver);
ainfo.play.minordev = 4;
ainfo.play.open = 1;
ainfo.play.active = driver->output_active;
if (driver->ops->get_monitor_volume)
ainfo.monitor_gain =
driver->ops->get_monitor_volume(driver);
if (driver->ops->get_output_muted)
ainfo.output_muted =
(unsigned char)driver->ops->get_output_muted(driver);
copy_to_user_ret((struct audio_info *)arg, &ainfo,
sizeof(ainfo), -EFAULT);
/* I_FLUSH, I_{G,S}ETSIG, I_NREAD provided for SunOS compatibility
*
* I must admit I'm quite ashamed of the state of the ioctl handling,
* but I do have several optimizations which I'm planning. -- DJB
*/
break;
static int sparcaudio_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
int retval = 0, i, j, k;
int minor = MINOR(inode->i_rdev);
struct audio_info ainfo;
audio_buf_info binfo;
count_info cinfo;
struct sparcaudio_driver *drv =
drivers[(minor >> SPARCAUDIO_DEVICE_SHIFT)];
switch (minor & 0xf) {
case SPARCAUDIO_MIXER_MINOR:
return sparcaudio_mixer_ioctl(inode, file, cmd, arg);
case SPARCAUDIO_DSP16_MINOR:
case SPARCAUDIO_DSP_MINOR:
case SPARCAUDIO_AUDIO_MINOR:
case SPARCAUDIO_AUDIOCTL_MINOR:
switch (cmd) {
case I_GETSIG:
case I_GETSIG_SOLARIS:
j = (int)lis_get_elist_ent(drv->sd_siglist,current->pid);
COPY_OUT(arg, j);
retval = drv->input_count;
break;
case AUDIO_SETINFO:
{
audio_info_t curinfo, newinfo;
copy_from_user_ret(&ainfo, (audio_info_t *) arg, sizeof(audio_info_t), -EFAULT);
/* Without these there's no point in trying */
if (!driver->ops->get_input_precision ||
!driver->ops->get_input_channels ||
!driver->ops->get_input_rate ||
!driver->ops->get_input_encoding ||
!driver->ops->get_output_precision ||
!driver->ops->get_output_channels ||
!driver->ops->get_output_rate ||
!driver->ops->get_output_encoding)
{
retval = -EINVAL;
break;
case I_SETSIG:
case I_SETSIG_SOLARIS:
if ((minor & 0xf) == SPARCAUDIO_AUDIOCTL_MINOR) {
if (!arg){
if (lis_del_from_elist(&(drv->sd_siglist),current->pid,S_ALL))
retval = -EINVAL;
else
if (!drv->sd_siglist)
drv->sd_sigflags=0;
}
/* Do bounds checking for things which always apply.
* Follow with enforcement of basic tenets of certain
* encodings. Everything over and above generic is
* enforced by the driver, which can assume that
* Martian cases are taken care of here. */
if (Modify(ainfo.play.gain) &&
((ainfo.play.gain > AUDIO_MAX_GAIN) ||
(ainfo.play.gain < AUDIO_MIN_GAIN))) {
/* Need to differentiate this from e.g. the above error */
else
if (lis_add_to_elist(&(drv->sd_siglist),current->pid,
(short)arg))
retval = -EAGAIN;
else
((drv->sd_sigflags) |= (arg));
}
break;
case I_NREAD:
case I_NREAD_SOLARIS:
/* According to the Solaris man page, this copies out
* the size of the first streams buffer and returns
* the number of streams messages on the read queue as
* as its retval. (streamio(7I)) This should work. */
j = (drv->input_count > 0) ? drv->input_buffer_size : 0;
COPY_OUT(arg, j);
retval = drv->input_count;
break;
/*
* A poor substitute until we do true resizable buffers.
*/
case SNDCTL_DSP_GETISPACE:
binfo.fragstotal = drv->num_input_buffers;
binfo.fragments = drv->num_input_buffers -
(drv->input_count + drv->recording_count);
binfo.fragsize = drv->input_buffer_size;
binfo.bytes = binfo.fragments*binfo.fragsize;
retval = verify_area(VERIFY_WRITE, (int *)arg, sizeof(binfo));
if (retval) break;
copy_to_user(&((char *)arg)[0], (char *)&binfo, sizeof(binfo));
break;
case SNDCTL_DSP_GETOSPACE:
binfo.fragstotal = drv->num_output_buffers;
binfo.fragments = drv->num_output_buffers -
(drv->output_count + drv->playing_count +
(drv->output_offset ? 1 : 0));
binfo.fragsize = drv->output_buffer_size;
binfo.bytes = binfo.fragments*binfo.fragsize +
(drv->output_buffer_size - drv->output_offset);
retval = verify_area(VERIFY_WRITE, (int *)arg, sizeof(binfo));
if (retval) break;
copy_to_user(&((char *)arg)[0], (char *)&binfo, sizeof(binfo));
break;
case SNDCTL_DSP_GETIPTR:
case SNDCTL_DSP_GETOPTR:
/*
* int bytes (number of bytes read/written since last)
* int blocks (number of frags read/wrote since last call)
* int ptr (current position of dma in buffer)
*/
retval = 0;
cinfo.bytes = 0;
cinfo.ptr = 0;
cinfo.blocks = 0;
cinfo.bytes += cinfo.ptr;
retval = verify_area(VERIFY_WRITE, (int *)arg, sizeof(cinfo));
if (retval) break;
copy_to_user(&((char *)arg)[0], (char *)&cinfo, sizeof(cinfo));
break;
case SNDCTL_DSP_SETFRAGMENT:
/* XXX Small hack to get ESD/Enlightenment to work. --DaveM */
retval = 0;
break;
case SNDCTL_DSP_SUBDIVIDE:
/*
* I don't understand what I need to do yet.
*/
retval = -EINVAL;
break;
case SNDCTL_DSP_SETTRIGGER:
/* This may not be 100% correct */
if ((arg & PCM_ENABLE_INPUT) && drv->ops->get_input_pause &&
drv->ops->set_input_pause) {
if (drv->ops->get_input_pause(drv))
drv->ops->set_input_pause(drv, 0);
} else {
if (!drv->ops->get_input_pause(drv))
drv->ops->set_input_pause(drv, 1);
}
if ((arg & PCM_ENABLE_OUTPUT) && drv->ops->get_output_pause &&
drv->ops->set_output_pause) {
if (drv->ops->get_output_pause(drv))
drv->ops->set_output_pause(drv, 0);
} else {
if (!drv->ops->get_output_pause(drv))
drv->ops->set_output_pause(drv, 1);
}
break;
case SNDCTL_DSP_GETTRIGGER:
j = 0;
if (drv->ops->get_input_pause)
if (drv->ops->get_input_pause(drv))
j = PCM_ENABLE_INPUT;
if (drv->ops->get_output_pause)
if (drv->ops->get_output_pause(drv))
j |= PCM_ENABLE_OUTPUT;
COPY_OUT(arg, j);
break;
case SNDCTL_DSP_GETBLKSIZE:
j = drv->input_buffer_size;
COPY_OUT(arg, j);
break;
case SNDCTL_DSP_SPEED:
if ((!drv->ops->set_output_rate) &&
(!drv->ops->set_input_rate)) {
retval = -EINVAL;
break;
}
if (Modify(ainfo.record.gain) &&
((ainfo.record.gain > AUDIO_MAX_GAIN) ||
(ainfo.record.gain < AUDIO_MIN_GAIN))) {
COPY_IN(arg, i);
tprintk(("setting speed to %d\n", i));
drv->ops->set_input_rate(drv, i);
drv->ops->set_output_rate(drv, i);
j = drv->ops->get_output_rate(drv);
COPY_OUT(arg, j);
break;
case SNDCTL_DSP_GETCAPS:
/*
* All Sparc audio hardware is full duplex.
* 4231 supports DMA pointer reading, 7930 is byte at a time.
* Pause functionality emulates trigger
*/
j = DSP_CAP_DUPLEX | DSP_CAP_TRIGGER | DSP_CAP_REALTIME;
COPY_OUT(arg, j);
break;
case SNDCTL_DSP_GETFMTS:
if (drv->ops->get_formats) {
j = drv->ops->get_formats(drv);
COPY_OUT(arg, j);
} else
retval = -EINVAL;
break;
case SNDCTL_DSP_SETFMT:
/* need to decode into encoding, precision */
COPY_IN(arg, i);
/* handle special case here */
if (i == AFMT_QUERY) {
j = drv->ops->get_output_encoding(drv);
k = drv->ops->get_output_precision(drv);
if (j == AUDIO_ENCODING_DVI)
i = AFMT_IMA_ADPCM;
else if (k == 8) {
switch (j) {
case AUDIO_ENCODING_ULAW:
i = AFMT_MU_LAW;
break;
case AUDIO_ENCODING_ALAW:
i = AFMT_A_LAW;
break;
case AUDIO_ENCODING_LINEAR8:
i = AFMT_U8;
break;
}
} else if (k == 16) {
case AUDIO_ENCODING_LINEAR:
i = AFMT_S16_BE;
break;
case AUDIO_ENCODING_LINEARLE:
i = AFMT_S16_LE;
break;
}
COPY_OUT(arg, i);
break;
}
if (Modify(ainfo.monitor_gain) &&
((ainfo.monitor_gain > AUDIO_MAX_GAIN) ||
(ainfo.monitor_gain < AUDIO_MIN_GAIN))) {
/* Without these there's no point in trying */
if (!drv->ops->set_input_precision ||
!drv->ops->set_input_encoding ||
!drv->ops->set_output_precision ||
!drv->ops->set_output_encoding) {
eprintk(("missing set routines: failed\n"));
retval = -EINVAL;
break;
}
/* Don't need to check less than zero on these */
if (Modifyc(ainfo.play.balance) &&
(ainfo.play.balance > AUDIO_RIGHT_BALANCE)) {
}
if (drv->ops->get_formats)
if (!(drv->ops->get_formats(drv) & i)) {
dprintk(("format not supported\n"));
return -EINVAL;
}
switch (i) {
case AFMT_S16_LE:
ainfo.record.precision = ainfo.play.precision = 16;
ainfo.record.encoding = ainfo.play.encoding =
AUDIO_ENCODING_LINEARLE;
break;
case AFMT_S16_BE:
ainfo.record.precision = ainfo.play.precision = 16;
ainfo.record.encoding = ainfo.play.encoding =
AUDIO_ENCODING_LINEAR;
break;
case AFMT_MU_LAW:
ainfo.record.precision = ainfo.play.precision = 8;
ainfo.record.encoding = ainfo.play.encoding =
AUDIO_ENCODING_ULAW;
break;
case AFMT_A_LAW:
ainfo.record.precision = ainfo.play.precision = 8;
ainfo.record.encoding = ainfo.play.encoding =
AUDIO_ENCODING_ALAW;
break;
case AFMT_U8:
ainfo.record.precision = ainfo.play.precision = 8;
ainfo.record.encoding = ainfo.play.encoding =
AUDIO_ENCODING_LINEAR8;
break;
}
tprintk(("setting fmt to enc %d pr %d\n", ainfo.play.encoding,
ainfo.play.precision));
if ((drv->ops->set_input_precision(drv,
ainfo.record.precision)
< 0) ||
(drv->ops->set_output_precision(drv,
ainfo.play.precision)
< 0) ||
(drv->ops->set_input_encoding(drv,
ainfo.record.encoding)
< 0) ||
(drv->ops->set_output_encoding(drv,
ainfo.play.encoding)
< 0)) {
dprintk(("setting format: failed\n"));
return -EINVAL;
}
COPY_OUT(arg, i);
break;
case SNDCTL_DSP_CHANNELS:
if ((!drv->ops->set_output_channels) &&
(!drv->ops->set_input_channels)) {
retval = -EINVAL;
break;
}
if (Modifyc(ainfo.record.balance) &&
(ainfo.record.balance > AUDIO_RIGHT_BALANCE)) {
COPY_IN(arg, i);
drv->ops->set_input_channels(drv, i);
drv->ops->set_output_channels(drv, i);
i = drv->ops->get_output_channels(drv);
COPY_OUT(arg, i);
break;
case SNDCTL_DSP_STEREO:
if ((!drv->ops->set_output_channels) &&
(!drv->ops->set_input_channels)) {
retval = -EINVAL;
break;
}
COPY_IN(arg, i);
drv->ops->set_input_channels(drv, (i + 1));
drv->ops->set_output_channels(drv, (i + 1));
i = ((drv->ops->get_output_channels(drv)) - 1);
COPY_OUT(arg, i);
break;
case SNDCTL_DSP_POST:
case SNDCTL_DSP_SYNC:
case AUDIO_DRAIN:
/* Deal with weirdness so we can fill buffers */
if (drv->output_offset) {
drv->output_offset = 0;
drv->output_rear = (drv->output_rear + 1)
% drv->num_output_buffers;
drv->output_count++;
}
if (drv->output_count > 0) {
sparcaudio_sync_output(drv);
/* Only pause for DRAIN/SYNC, not POST */
if (cmd != SNDCTL_DSP_POST) {
interruptible_sleep_on(&drv->output_drain_wait);
retval = (signal_pending(current)) ? -EINTR : 0;
}
}
break;
case I_FLUSH:
case I_FLUSH_SOLARIS:
if (((unsigned int)arg == FLUSHW) ||
((unsigned int)arg == FLUSHRW)) {
if (file->f_mode & FMODE_WRITE) {
sparcaudio_sync_output(drv);
if (drv->output_active) {
wake_up_interruptible(&drv->output_write_wait);
drv->ops->stop_output(drv);
}
drv->output_offset = 0;
drv->output_active = 0;
drv->output_front = 0;
drv->output_rear = 0;
drv->output_count = 0;
drv->output_size = 0;
drv->playing_count = 0;
drv->output_eof = 0;
}
}
if (((unsigned int)arg == FLUSHR) ||
((unsigned int)arg == FLUSHRW)) {
if (drv->input_active && (file->f_mode & FMODE_READ)) {
wake_up_interruptible(&drv->input_read_wait);
drv->ops->stop_input(drv);
drv->input_active = 0;
drv->input_front = 0;
drv->input_rear = 0;
drv->input_count = 0;
drv->input_size = 0;
drv->input_offset = 0;
drv->recording_count = 0;
}
if ((file->f_mode & FMODE_READ) &&
(drv->flags & SDF_OPEN_READ)) {
if (drv->duplex == 2)
drv->input_count = drv->output_count;
drv->ops->start_input(drv,
drv->input_buffers[drv->input_front],
drv->input_buffer_size);
drv->input_active = 1;
}
}
if (((unsigned int)arg == FLUSHW) ||
((unsigned int)arg == FLUSHRW)) {
if ((file->f_mode & FMODE_WRITE) &&
!(drv->flags & SDF_OPEN_WRITE)) {
kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
sparcaudio_sync_output(drv);
}
}
break;
case SNDCTL_DSP_RESET:
case AUDIO_FLUSH:
if (drv->output_active && (file->f_mode & FMODE_WRITE)) {
wake_up_interruptible(&drv->output_write_wait);
drv->ops->stop_output(drv);
drv->output_active = 0;
drv->output_front = 0;
drv->output_rear = 0;
drv->output_count = 0;
drv->output_size = 0;
drv->playing_count = 0;
drv->output_offset = 0;
drv->output_eof = 0;
}
if (drv->input_active && (file->f_mode & FMODE_READ)) {
wake_up_interruptible(&drv->input_read_wait);
drv->ops->stop_input(drv);
drv->input_active = 0;
drv->input_front = 0;
drv->input_rear = 0;
drv->input_count = 0;
drv->input_size = 0;
drv->input_offset = 0;
drv->recording_count = 0;
}
if ((file->f_mode & FMODE_READ) &&
!(drv->flags & SDF_OPEN_READ)) {
drv->ops->start_input(drv,
drv->input_buffers[drv->input_front],
drv->input_buffer_size);
drv->input_active = 1;
}
if ((file->f_mode & FMODE_WRITE) &&
!(drv->flags & SDF_OPEN_WRITE)) {
sparcaudio_sync_output(drv);
}
break;
case AUDIO_GETDEV:
if (drv->ops->sunaudio_getdev) {
audio_device_t tmp;
retval = verify_area(VERIFY_WRITE, (void *)arg,
sizeof(audio_device_t));
if (!retval)
drv->ops->sunaudio_getdev(drv, &tmp);
copy_to_user((audio_device_t *)arg, &tmp, sizeof(tmp));
} else
retval = -EINVAL;
break;
case AUDIO_GETDEV_SUNOS:
if (drv->ops->sunaudio_getdev_sunos) {
int tmp = drv->ops->sunaudio_getdev_sunos(drv);
retval = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int));
if (!retval)
copy_to_user((int *)arg, &tmp, sizeof(tmp));
} else
retval = -EINVAL;
break;
case AUDIO_GETINFO:
AUDIO_INITINFO(&ainfo);
if (drv->ops->get_input_rate)
ainfo.record.sample_rate =
drv->ops->get_input_rate(drv);
else
ainfo.record.sample_rate = (8000);
if (drv->ops->get_input_channels)
ainfo.record.channels =
drv->ops->get_input_channels(drv);
else
ainfo.record.channels = (1);
if (drv->ops->get_input_precision)
ainfo.record.precision =
drv->ops->get_input_precision(drv);
else
ainfo.record.precision = (8);
if (drv->ops->get_input_encoding)
ainfo.record.encoding =
drv->ops->get_input_encoding(drv);
else
ainfo.record.encoding = (AUDIO_ENCODING_ULAW);
if (drv->ops->get_input_volume)
ainfo.record.gain =
drv->ops->get_input_volume(drv);
else
ainfo.record.gain = (0);
if (drv->ops->get_input_port)
ainfo.record.port =
drv->ops->get_input_port(drv);
else
ainfo.record.port = (0);
if (drv->ops->get_input_ports)
ainfo.record.avail_ports =
drv->ops->get_input_ports(drv);
else
ainfo.record.avail_ports = (0);
/* To make e.g. vat happy, we let them think they control this */
ainfo.record.buffer_size = drv->buffer_size;
if (drv->ops->get_input_samples)
ainfo.record.samples = drv->ops->get_input_samples(drv);
else
ainfo.record.samples = 0;
/* This is undefined in the record context in Solaris */
ainfo.record.eof = 0;
if (drv->ops->get_input_pause)
ainfo.record.pause =
drv->ops->get_input_pause(drv);
else
ainfo.record.pause = 0;
if (drv->ops->get_input_error)
ainfo.record.error =
(unsigned char)drv->ops->get_input_error(drv);
else
ainfo.record.error = 0;
ainfo.record.waiting = 0;
if (drv->ops->get_input_balance)
ainfo.record.balance =
(unsigned char)drv->ops->get_input_balance(drv);
else
ainfo.record.balance = (unsigned char)(AUDIO_MID_BALANCE);
ainfo.record.minordev = 4 + (minor << SPARCAUDIO_DEVICE_SHIFT);
ainfo.record.open = (drv->flags & SDF_OPEN_READ);
ainfo.record.active = 0;
if (drv->ops->get_output_rate)
ainfo.play.sample_rate =
drv->ops->get_output_rate(drv);
else
ainfo.play.sample_rate = (8000);
if (drv->ops->get_output_channels)
ainfo.play.channels =
drv->ops->get_output_channels(drv);
else
ainfo.play.channels = (1);
if (drv->ops->get_output_precision)
ainfo.play.precision =
drv->ops->get_output_precision(drv);
else
ainfo.play.precision = (8);
if (drv->ops->get_output_encoding)
ainfo.play.encoding =
drv->ops->get_output_encoding(drv);
else
ainfo.play.encoding = (AUDIO_ENCODING_ULAW);
if (drv->ops->get_output_volume)
ainfo.play.gain =
drv->ops->get_output_volume(drv);
else
ainfo.play.gain = (0);
if (drv->ops->get_output_port)
ainfo.play.port =
drv->ops->get_output_port(drv);
else
ainfo.play.port = (0);
if (drv->ops->get_output_ports)
ainfo.play.avail_ports =
drv->ops->get_output_ports(drv);
else
ainfo.play.avail_ports = (0);
/* This is not defined in the play context in Solaris */
ainfo.play.buffer_size = 0;
if (drv->ops->get_output_samples)
ainfo.play.samples = drv->ops->get_output_samples(drv);
else
ainfo.play.samples = 0;
ainfo.play.eof = drv->output_eof;
if (drv->ops->get_output_pause)
ainfo.play.pause =
drv->ops->get_output_pause(drv);
else
ainfo.play.pause = 0;
if (drv->ops->get_output_error)
ainfo.play.error =
(unsigned char)drv->ops->get_output_error(drv);
else
ainfo.play.error = 0;
ainfo.play.waiting = waitqueue_active(&drv->open_wait);
if (drv->ops->get_output_balance)
ainfo.play.balance =
(unsigned char)drv->ops->get_output_balance(drv);
else
ainfo.play.balance = (unsigned char)(AUDIO_MID_BALANCE);
ainfo.play.minordev = 4 + (minor << SPARCAUDIO_DEVICE_SHIFT);
ainfo.play.open = (drv->flags & SDF_OPEN_WRITE);
ainfo.play.active = drv->output_active;
/* If any of these changed, record them all, then make
* changes atomically. If something fails, back it all out. */
if (Modify(ainfo.record.precision) ||
Modify(ainfo.record.sample_rate) ||
Modify(ainfo.record.channels) ||
Modify(ainfo.record.encoding) ||
Modify(ainfo.play.precision) ||
Modify(ainfo.play.sample_rate) ||
Modify(ainfo.play.channels) ||
Modify(ainfo.play.encoding))
{
/* If they're trying to change something we
* have no routine for, they lose */
if ((!driver->ops->set_input_encoding &&
Modify(ainfo.record.encoding)) ||
(!driver->ops->set_input_rate &&
Modify(ainfo.record.sample_rate)) ||
(!driver->ops->set_input_precision &&
Modify(ainfo.record.precision)) ||
(!driver->ops->set_input_channels &&
Modify(ainfo.record.channels))) {
retval = -EINVAL;
break;
}
curinfo.record.encoding = driver->ops->get_input_encoding(driver);
curinfo.record.sample_rate = driver->ops->get_input_rate(driver);
curinfo.record.precision = driver->ops->get_input_precision(driver);
curinfo.record.channels = driver->ops->get_input_channels(driver);
newinfo.record.encoding = Modify(ainfo.record.encoding) ?
ainfo.record.encoding : curinfo.record.encoding;
newinfo.record.sample_rate = Modify(ainfo.record.sample_rate)?
ainfo.record.sample_rate : curinfo.record.sample_rate;
newinfo.record.precision = Modify(ainfo.record.precision) ?
ainfo.record.precision : curinfo.record.precision;
newinfo.record.channels = Modify(ainfo.record.channels) ?
ainfo.record.channels : curinfo.record.channels;
switch (newinfo.record.encoding) {
case AUDIO_ENCODING_ALAW:
case AUDIO_ENCODING_ULAW:
if (newinfo.record.precision != 8) {
retval = -EINVAL;
break;
}
if (newinfo.record.channels != 1) {
retval = -EINVAL;
break;
}
break;
case AUDIO_ENCODING_LINEAR:
case AUDIO_ENCODING_LINEARLE:
if (newinfo.record.precision != 16) {
retval = -EINVAL;
break;
}
if (newinfo.record.channels != 1 &&
newinfo.record.channels != 2)
{
retval = -EINVAL;
break;
}
break;
case AUDIO_ENCODING_LINEAR8:
if (newinfo.record.precision != 8) {
retval = -EINVAL;
break;
}
if (newinfo.record.channels != 1 &&
newinfo.record.channels != 2)
{
retval = -EINVAL;
break;
}
}
if (retval < 0)
break;
if (drv->ops->get_monitor_volume)
ainfo.monitor_gain =
drv->ops->get_monitor_volume(drv);
else
ainfo.monitor_gain = (0);
if (drv->ops->get_output_muted)
ainfo.output_muted =
(unsigned char)drv->ops->get_output_muted(drv);
else
ainfo.output_muted = (unsigned char)(0);
retval = verify_area(VERIFY_WRITE, (void *)arg,
sizeof(struct audio_info));
if (retval < 0)
break;
/* If they're trying to change something we
* have no routine for, they lose */
if ((!driver->ops->set_output_encoding &&
Modify(ainfo.play.encoding)) ||
(!driver->ops->set_output_rate &&
Modify(ainfo.play.sample_rate)) ||
(!driver->ops->set_output_precision &&
Modify(ainfo.play.precision)) ||
(!driver->ops->set_output_channels &&
Modify(ainfo.play.channels))) {
copy_to_user((struct audio_info *)arg, &ainfo, sizeof(ainfo));
break;
case AUDIO_SETINFO:
{
audio_info_t curinfo, newinfo;
if (verify_area(VERIFY_READ, (audio_info_t *)arg,
sizeof(audio_info_t))) {
dprintk(("verify_area failed\n"));
return -EINVAL;
}
copy_from_user(&ainfo, (audio_info_t *)arg, sizeof(audio_info_t));
/* Without these there's no point in trying */
if (!drv->ops->get_input_precision ||
!drv->ops->get_input_channels ||
!drv->ops->get_input_rate ||
!drv->ops->get_input_encoding ||
!drv->ops->get_output_precision ||
!drv->ops->get_output_channels ||
!drv->ops->get_output_rate ||
!drv->ops->get_output_encoding)
{
eprintk(("missing get routines: failed\n"));
retval = -EINVAL;
break;
}
curinfo.play.encoding = driver->ops->get_output_encoding(driver);
curinfo.play.sample_rate = driver->ops->get_output_rate(driver);
curinfo.play.precision = driver->ops->get_output_precision(driver);
curinfo.play.channels = driver->ops->get_output_channels(driver);
newinfo.play.encoding = Modify(ainfo.play.encoding) ?
ainfo.play.encoding : curinfo.play.encoding;
newinfo.play.sample_rate = Modify(ainfo.play.sample_rate) ?
ainfo.play.sample_rate : curinfo.play.sample_rate;
newinfo.play.precision = Modify(ainfo.play.precision) ?
ainfo.play.precision : curinfo.play.precision;
newinfo.play.channels = Modify(ainfo.play.channels) ?
ainfo.play.channels : curinfo.play.channels;
switch (newinfo.play.encoding) {
case AUDIO_ENCODING_ALAW:
case AUDIO_ENCODING_ULAW:
if (newinfo.play.precision != 8) {
retval = -EINVAL;
break;
}
if (newinfo.play.channels != 1) {
retval = -EINVAL;
break;
}
break;
case AUDIO_ENCODING_LINEAR:
case AUDIO_ENCODING_LINEARLE:
if (newinfo.play.precision != 16) {
retval = -EINVAL;
break;
}
if (newinfo.play.channels != 1 &&
newinfo.play.channels != 2)
{
retval = -EINVAL;
break;
}
break;
case AUDIO_ENCODING_LINEAR8:
if (newinfo.play.precision != 8) {
retval = -EINVAL;
break;
}
if (newinfo.play.channels != 1 &&
newinfo.play.channels != 2)
{
retval = -EINVAL;
break;
}
}
if (retval < 0)
break;
}
/* If we got this far, we're at least sane with
* respect to generics. Try the changes. */
if ((driver->ops->set_input_precision(driver, ainfo.record.precision) < 0) ||
(driver->ops->set_output_precision(driver, ainfo.play.precision) < 0) ||
(driver->ops->set_input_channels(driver, ainfo.record.channels) < 0) ||
(driver->ops->set_output_channels(driver, ainfo.play.channels) < 0) ||
(driver->ops->set_input_rate(driver, ainfo.record.sample_rate) < 0) ||
(driver->ops->set_output_rate(driver, ainfo.play.sample_rate) < 0) ||
(driver->ops->set_input_encoding(driver, ainfo.record.encoding) < 0) ||
(driver->ops->set_output_encoding(driver, ainfo.play.encoding) < 0))
{
/* Pray we can set it all back. If not, uh... */
driver->ops->set_input_precision(driver, curinfo.record.precision);
driver->ops->set_output_precision(driver, curinfo.play.precision);
driver->ops->set_input_channels(driver, curinfo.record.channels);
driver->ops->set_output_channels(driver, curinfo.play.channels);
driver->ops->set_input_rate(driver, curinfo.record.sample_rate);
driver->ops->set_output_rate(driver, curinfo.play.sample_rate);
driver->ops->set_input_encoding(driver, curinfo.record.encoding);
driver->ops->set_output_encoding(driver, curinfo.play.encoding);
/* Do bounds checking for things which always apply.
* Follow with enforcement of basic tenets of certain
* encodings. Everything over and above generic is
* enforced by the driver, which can assume that
* Martian cases are taken care of here. */
if (Modify(ainfo.play.gain) &&
((ainfo.play.gain > AUDIO_MAX_GAIN) ||
(ainfo.play.gain < AUDIO_MIN_GAIN))) {
/* Need to differentiate this from e.g. the above error */
eprintk(("play gain bounds: failed %d\n", ainfo.play.gain));
retval = -EINVAL;
break;
}
if (Modify(ainfo.record.gain) &&
((ainfo.record.gain > AUDIO_MAX_GAIN) ||
(ainfo.record.gain < AUDIO_MIN_GAIN))) {
eprintk(("rec gain bounds: failed %d\n", ainfo.record.gain));
retval = -EINVAL;
break;
}
if (Modify(ainfo.monitor_gain) &&
((ainfo.monitor_gain > AUDIO_MAX_GAIN) ||
(ainfo.monitor_gain < AUDIO_MIN_GAIN))) {
eprintk(("monitor gain bounds: failed\n"));
retval = -EINVAL;
break;
}
/* Don't need to check less than zero on these */
if (Modifyc(ainfo.play.balance) &&
(ainfo.play.balance > AUDIO_RIGHT_BALANCE)) {
eprintk(("play balance bounds: %d failed\n",
(int)ainfo.play.balance));
retval = -EINVAL;
break;
}
if (Modifyc(ainfo.record.balance) &&
(ainfo.record.balance > AUDIO_RIGHT_BALANCE)) {
eprintk(("rec balance bounds: failed\n"));
retval = -EINVAL;
break;
}
/* If any of these changed, record them all, then make
* changes atomically. If something fails, back it all out. */
if (Modify(ainfo.record.precision) ||
Modify(ainfo.record.sample_rate) ||
Modify(ainfo.record.channels) ||
Modify(ainfo.record.encoding) ||
Modify(ainfo.play.precision) ||
Modify(ainfo.play.sample_rate) ||
Modify(ainfo.play.channels) ||
Modify(ainfo.play.encoding))
{
/* If they're trying to change something we
* have no routine for, they lose */
if ((!drv->ops->set_input_encoding &&
Modify(ainfo.record.encoding)) ||
(!drv->ops->set_input_rate &&
Modify(ainfo.record.sample_rate)) ||
(!drv->ops->set_input_precision &&
Modify(ainfo.record.precision)) ||
(!drv->ops->set_input_channels &&
Modify(ainfo.record.channels))) {
eprintk(("rec set no routines: failed\n"));
retval = -EINVAL;
break;
}
curinfo.record.encoding =
drv->ops->get_input_encoding(drv);
curinfo.record.sample_rate =
drv->ops->get_input_rate(drv);
curinfo.record.precision =
drv->ops->get_input_precision(drv);
curinfo.record.channels =
drv->ops->get_input_channels(drv);
newinfo.record.encoding = Modify(ainfo.record.encoding) ?
ainfo.record.encoding : curinfo.record.encoding;
newinfo.record.sample_rate =
Modify(ainfo.record.sample_rate)?
ainfo.record.sample_rate : curinfo.record.sample_rate;
newinfo.record.precision = Modify(ainfo.record.precision) ?
ainfo.record.precision : curinfo.record.precision;
newinfo.record.channels = Modify(ainfo.record.channels) ?
ainfo.record.channels : curinfo.record.channels;
switch (newinfo.record.encoding) {
case AUDIO_ENCODING_ALAW:
case AUDIO_ENCODING_ULAW:
if (newinfo.record.precision != 8) {
eprintk(("rec law precision bounds: failed\n"));
retval = -EINVAL;
break;
}
if (newinfo.record.channels != 1) {
eprintk(("rec law channel bounds: failed\n"));
retval = -EINVAL;
break;
}
break;
case AUDIO_ENCODING_LINEAR:
case AUDIO_ENCODING_LINEARLE:
if (newinfo.record.precision != 16) {
eprintk(("rec lin precision bounds: failed\n"));
retval = -EINVAL;
break;
}
if (newinfo.record.channels != 1 &&
newinfo.record.channels != 2)
{
eprintk(("rec lin channel bounds: failed\n"));
retval = -EINVAL;
break;
}
break;
case AUDIO_ENCODING_LINEAR8:
if (newinfo.record.precision != 8) {
eprintk(("rec lin8 precision bounds: failed\n"));
retval = -EINVAL;
break;
}
if (newinfo.record.channels != 1 &&
newinfo.record.channels != 2)
{
eprintk(("rec lin8 channel bounds: failed\n"));
retval = -EINVAL;
break;
}
}
if (retval < 0)
break;
/* If they're trying to change something we
* have no routine for, they lose */
if ((!drv->ops->set_output_encoding &&
Modify(ainfo.play.encoding)) ||
(!drv->ops->set_output_rate &&
Modify(ainfo.play.sample_rate)) ||
(!drv->ops->set_output_precision &&
Modify(ainfo.play.precision)) ||
(!drv->ops->set_output_channels &&
Modify(ainfo.play.channels))) {
eprintk(("play set no routine: failed\n"));
retval = -EINVAL;
break;
}
curinfo.play.encoding =
drv->ops->get_output_encoding(drv);
curinfo.play.sample_rate =
drv->ops->get_output_rate(drv);
curinfo.play.precision =
drv->ops->get_output_precision(drv);
curinfo.play.channels =
drv->ops->get_output_channels(drv);
newinfo.play.encoding = Modify(ainfo.play.encoding) ?
ainfo.play.encoding : curinfo.play.encoding;
newinfo.play.sample_rate = Modify(ainfo.play.sample_rate) ?
ainfo.play.sample_rate : curinfo.play.sample_rate;
newinfo.play.precision = Modify(ainfo.play.precision) ?
ainfo.play.precision : curinfo.play.precision;
newinfo.play.channels = Modify(ainfo.play.channels) ?
ainfo.play.channels : curinfo.play.channels;
switch (newinfo.play.encoding) {
case AUDIO_ENCODING_ALAW:
case AUDIO_ENCODING_ULAW:
if (newinfo.play.precision != 8) {
eprintk(("play law precision bounds: failed\n"));
retval = -EINVAL;
break;
}
if (newinfo.play.channels != 1) {
eprintk(("play law channel bounds: failed\n"));
retval = -EINVAL;
break;
}
break;
case AUDIO_ENCODING_LINEAR:
case AUDIO_ENCODING_LINEARLE:
if (newinfo.play.precision != 16) {
eprintk(("play lin precision bounds: failed\n"));
retval = -EINVAL;
break;
}
if (newinfo.play.channels != 1 &&
newinfo.play.channels != 2)
{
eprintk(("play lin channel bounds: failed\n"));
retval = -EINVAL;
break;
}
break;
case AUDIO_ENCODING_LINEAR8:
if (newinfo.play.precision != 8) {
eprintk(("play lin8 precision bounds: failed\n"));
retval = -EINVAL;
break;
}
if (newinfo.play.channels != 1 &&
newinfo.play.channels != 2)
{
eprintk(("play lin8 channel bounds: failed\n"));
retval = -EINVAL;
break;
}
}
if (retval < 0)
break;
/* If we got this far, we're at least sane with
* respect to generics. Try the changes. */
if ((drv->ops->set_input_channels &&
(drv->ops->set_input_channels(drv,
newinfo.record.channels)
< 0)) ||
(drv->ops->set_output_channels &&
(drv->ops->set_output_channels(drv,
newinfo.play.channels)
< 0)) ||
(drv->ops->set_input_rate &&
(drv->ops->set_input_rate(drv,
newinfo.record.sample_rate)
< 0)) ||
(drv->ops->set_output_rate &&
(drv->ops->set_output_rate(drv,
newinfo.play.sample_rate)
< 0)) ||
(drv->ops->set_input_precision &&
(drv->ops->set_input_precision(drv,
newinfo.record.precision)
< 0)) ||
(drv->ops->set_output_precision &&
(drv->ops->set_output_precision(drv,
newinfo.play.precision)
< 0)) ||
(drv->ops->set_input_encoding &&
(drv->ops->set_input_encoding(drv,
newinfo.record.encoding)
< 0)) ||
(drv->ops->set_output_encoding &&
(drv->ops->set_output_encoding(drv,
newinfo.play.encoding)
< 0)))
{
dprintk(("setting format: failed\n"));
/* Pray we can set it all back. If not, uh... */
if (drv->ops->set_input_channels)
drv->ops->set_input_channels(drv,
curinfo.record.channels);
if (drv->ops->set_output_channels)
drv->ops->set_output_channels(drv,
curinfo.play.channels);
if (drv->ops->set_input_rate)
drv->ops->set_input_rate(drv,
curinfo.record.sample_rate);
if (drv->ops->set_output_rate)
drv->ops->set_output_rate(drv,
curinfo.play.sample_rate);
if (drv->ops->set_input_precision)
drv->ops->set_input_precision(drv,
curinfo.record.precision);
if (drv->ops->set_output_precision)
drv->ops->set_output_precision(drv,
curinfo.play.precision);
if (drv->ops->set_input_encoding)
drv->ops->set_input_encoding(drv,
curinfo.record.encoding);
if (drv->ops->set_output_encoding)
drv->ops->set_output_encoding(drv,
curinfo.play.encoding);
retval = -EINVAL;
break;
}
}
if (retval < 0)
break;
newinfo.record.balance =
__sparcaudio_if_setc_do(drv,
drv->ops->set_input_balance,
drv->ops->get_input_balance,
ainfo.record.balance);
newinfo.play.balance =
__sparcaudio_if_setc_do(drv,
drv->ops->set_output_balance,
drv->ops->get_output_balance,
ainfo.play.balance);
newinfo.record.error =
__sparcaudio_if_setc_do(drv,
drv->ops->set_input_error,
drv->ops->get_input_error,
ainfo.record.error);
newinfo.play.error =
__sparcaudio_if_setc_do(drv,
drv->ops->set_output_error,
drv->ops->get_output_error,
ainfo.play.error);
newinfo.output_muted =
__sparcaudio_if_setc_do(drv,
drv->ops->set_output_muted,
drv->ops->get_output_muted,
ainfo.output_muted);
newinfo.record.gain =
__sparcaudio_if_set_do(drv,
drv->ops->set_input_volume,
drv->ops->get_input_volume,
ainfo.record.gain);
newinfo.play.gain =
__sparcaudio_if_set_do(drv,
drv->ops->set_output_volume,
drv->ops->get_output_volume,
ainfo.play.gain);
newinfo.record.port =
__sparcaudio_if_set_do(drv,
drv->ops->set_input_port,
drv->ops->get_input_port,
ainfo.record.port);
newinfo.play.port =
__sparcaudio_if_set_do(drv,
drv->ops->set_output_port,
drv->ops->get_output_port,
ainfo.play.port);
newinfo.record.samples =
__sparcaudio_if_set_do(drv,
drv->ops->set_input_samples,
drv->ops->get_input_samples,
ainfo.record.samples);
newinfo.play.samples =
__sparcaudio_if_set_do(drv,
drv->ops->set_output_samples,
drv->ops->get_output_samples,
ainfo.play.samples);
newinfo.monitor_gain =
__sparcaudio_if_set_do(drv,
drv->ops->set_monitor_volume,
drv->ops->get_monitor_volume,
ainfo.monitor_gain);
if (Modify(ainfo.record.buffer_size)) {
/* Should sanity check this */
newinfo.record.buffer_size = ainfo.record.buffer_size;
drv->buffer_size = ainfo.record.buffer_size;
} else
newinfo.record.buffer_size = drv->buffer_size;
if (Modify(ainfo.play.eof)) {
ainfo.play.eof = newinfo.play.eof;
newinfo.play.eof = drv->output_eof;
drv->output_eof = ainfo.play.eof;
} else
newinfo.play.eof = drv->output_eof;
if (drv->flags & SDF_OPEN_READ) {
newinfo.record.pause =
__sparcaudio_if_setc_do(drv,
drv->ops->set_input_pause,
drv->ops->get_input_pause,
ainfo.record.pause);
} else if (drv->ops->get_input_pause) {
newinfo.record.pause = drv->ops->get_input_pause(drv);
} else newinfo.record.pause = 0;
if (drv->flags & SDF_OPEN_WRITE) {
newinfo.play.pause =
__sparcaudio_if_setc_do(drv,
drv->ops->set_output_pause,
drv->ops->get_output_pause,
ainfo.play.pause);
} else if (drv->ops->get_output_pause) {
newinfo.play.pause = drv->ops->get_output_pause(drv);
} else newinfo.play.pause = 0;
retval = verify_area(VERIFY_WRITE, (void *)arg,
sizeof(struct audio_info));
/* Even if we fail, if we made changes let's try notification */
if (!retval)
copy_to_user((struct audio_info *)arg, &newinfo,
sizeof(newinfo));
#ifdef REAL_AUDIO_SIGNALS
kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
#endif
break;
}
/* Maybe this should be a routine instead of a macro */
#define IF_SET_DO(x,y) if ((x) && Modify(y)) x(driver, y)
#define IF_SETC_DO(x,y) if ((x) && Modifyc(y)) x(driver, y)
IF_SETC_DO(driver->ops->set_input_balance, (int)ainfo.record.balance);
IF_SETC_DO(driver->ops->set_output_balance, (int)ainfo.play.balance);
IF_SET_DO(driver->ops->set_input_volume, ainfo.record.gain);
IF_SET_DO(driver->ops->set_output_volume, ainfo.play.gain);
IF_SET_DO(driver->ops->set_input_port, ainfo.record.port);
IF_SET_DO(driver->ops->set_output_port, ainfo.play.port);
IF_SET_DO(driver->ops->set_monitor_volume, ainfo.monitor_gain);
IF_SETC_DO(driver->ops->set_output_muted, (int)ainfo.output_muted);
#undef IF_SET_DO
#undef IF_SETC_DO
break;
default:
if (drv->ops->ioctl)
retval = drv->ops->ioctl(inode,file,cmd,arg,drv);
else
retval = -EINVAL;
}
break;
case SPARCAUDIO_STATUS_MINOR:
eprintk(("status minor not yet implemented\n"));
retval = -EINVAL;
default:
if (driver->ops->ioctl)
retval = driver->ops->ioctl(inode,file,cmd,arg,driver);
else {
retval = -EINVAL;
}
eprintk(("unknown minor device number\n"));
retval = -EINVAL;
}
return retval;
}
static int sparcaudioctl_release(struct inode * inode, struct file * file)
static int sparcaudioctl_release_ret(struct inode * inode, struct file * file)
{
MOD_DEC_USE_COUNT;
MOD_DEC_USE_COUNT;
return 0;
}
return 0;
/* For 2.0 kernels */
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
static void sparcaudioctl_release(struct inode * inode, struct file * file)
{
sparcaudioctl_release_ret(inode, file);
}
#endif
static struct file_operations sparcaudioctl_fops = {
NULL,
NULL,
NULL,
NULL, /* sparcaudio_readdir */
NULL, /* sparcaudio_select */
sparcaudio_ioctl,
NULL, /* sparcaudio_mmap */
NULL,
NULL, /* flush */
sparcaudioctl_release
NULL,
NULL,
NULL,
NULL, /* sparcaudio_readdir */
sparcaudio_select,
sparcaudio_ioctl,
NULL, /* sparcaudio_mmap */
NULL,
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
NULL, /* sparcaudio_flush */
#endif
sparcaudioctl_release,
};
static int sparcaudio_open(struct inode * inode, struct file * file)
{
int minor = MINOR(inode->i_rdev);
int err;
/* A low-level audio driver must exist. */
if (!driver)
return -ENODEV;
switch (minor) {
case SPARCAUDIO_AUDIOCTL_MINOR:
file->f_op = &sparcaudioctl_fops;
break;
case SPARCAUDIO_DSP16_MINOR:
case SPARCAUDIO_DSP_MINOR:
case SPARCAUDIO_AUDIO_MINOR:
/* If the driver is busy, then wait to get through. */
retry_open:
if (file->f_mode & FMODE_READ && driver->flags & SDF_OPEN_READ) {
if (file->f_flags & O_NONBLOCK)
return -EBUSY;
interruptible_sleep_on(&driver->open_wait);
int minor = MINOR(inode->i_rdev);
struct sparcaudio_driver *drv =
drivers[(minor >> SPARCAUDIO_DEVICE_SHIFT)];
int err;
/* A low-level audio driver must exist. */
if (!drv)
return -ENODEV;
switch (minor & 0xf) {
case SPARCAUDIO_AUDIOCTL_MINOR:
file->f_op = &sparcaudioctl_fops;
break;
case SPARCAUDIO_DSP16_MINOR:
case SPARCAUDIO_DSP_MINOR:
case SPARCAUDIO_AUDIO_MINOR:
/* If the driver is busy, then wait to get through. */
retry_open:
if (file->f_mode & FMODE_READ && drv->flags & SDF_OPEN_READ) {
if (file->f_flags & O_NONBLOCK)
return -EBUSY;
/* If something is now waiting, signal control device */
kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
interruptible_sleep_on(&drv->open_wait);
if (signal_pending(current))
return -EINTR;
goto retry_open;
}
if (file->f_mode & FMODE_WRITE && driver->flags & SDF_OPEN_WRITE) {
if (file->f_flags & O_NONBLOCK)
return -EBUSY;
interruptible_sleep_on(&driver->open_wait);
return -EINTR;
goto retry_open;
}
if (file->f_mode & FMODE_WRITE && drv->flags & SDF_OPEN_WRITE) {
if (file->f_flags & O_NONBLOCK)
return -EBUSY;
/* If something is now waiting, signal control device */
kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
interruptible_sleep_on(&drv->open_wait);
if (signal_pending(current))
return -EINTR;
goto retry_open;
}
return -EINTR;
goto retry_open;
}
/* Allow the low-level driver to initialize itself. */
if (driver->ops->open) {
err = driver->ops->open(inode,file,driver);
if (err < 0)
return err;
}
/* Mark the driver as locked for read and/or write. */
if (file->f_mode & FMODE_READ) {
driver->input_offset = 0;
driver->input_front = 0;
driver->input_rear = 0;
driver->input_count = 0;
driver->recording_count = 0;
driver->ops->start_input(driver, driver->input_buffers[driver->input_front],
4096);
driver->input_active = 1;
driver->flags |= SDF_OPEN_READ;
/* Allow the low-level driver to initialize itself. */
if (drv->ops->open) {
err = drv->ops->open(inode,file,drv);
if (err < 0)
return err;
}
/* Mark the driver as locked for read and/or write. */
if (file->f_mode & FMODE_READ) {
drv->input_offset = 0;
drv->input_front = 0;
drv->input_rear = 0;
drv->input_count = 0;
drv->input_size = 0;
drv->recording_count = 0;
/* Clear pause */
if (drv->ops->set_input_pause)
drv->ops->set_input_pause(drv, 0);
drv->ops->start_input(drv, drv->input_buffers[drv->input_front],
drv->input_buffer_size);
drv->input_active = 1;
drv->flags |= SDF_OPEN_READ;
}
if (file->f_mode & FMODE_WRITE) {
drv->output_offset = 0;
drv->output_eof = 0;
drv->playing_count = 0;
drv->output_size = 0;
drv->output_front = 0;
drv->output_rear = 0;
drv->output_count = 0;
drv->output_active = 0;
/* Clear pause */
if (drv->ops->set_output_pause)
drv->ops->set_output_pause(drv, 0);
drv->flags |= SDF_OPEN_WRITE;
}
break;
case SPARCAUDIO_MIXER_MINOR:
file->f_op = &sparcaudioctl_fops;
break;
default:
return -ENXIO;
}
if (file->f_mode & FMODE_WRITE) {
driver->playing_count = 0;
driver->output_size = 0;
driver->output_front = 0;
driver->output_rear = 0;
driver->output_count = 0;
driver->output_active = 0;
driver->flags |= SDF_OPEN_WRITE;
}
break;
case SPARCAUDIO_MIXER_MINOR:
file->f_op = &sparcaudioctl_fops;
break;
default:
return -ENXIO;
}
MOD_INC_USE_COUNT;
/* Success! */
return 0;
MOD_INC_USE_COUNT;
/* Success! */
return 0;
}
static int sparcaudio_release(struct inode * inode, struct file * file)
static int sparcaudio_release_ret(struct inode * inode, struct file * file)
{
/* Anything in the queue? */
sparcaudio_sync_output(driver);
struct sparcaudio_driver *drv = drivers[(MINOR(inode->i_rdev) >>
SPARCAUDIO_DEVICE_SHIFT)];
if (file->f_mode & FMODE_READ) {
/* Stop input */
drv->ops->stop_input(drv);
drv->input_active = 0;
}
if (file->f_mode & FMODE_WRITE) {
/* Anything in the queue? */
if (drv->output_offset) {
drv->output_offset = 0;
drv->output_rear = (drv->output_rear + 1)
% drv->num_output_buffers;
drv->output_count++;
}
sparcaudio_sync_output(drv);
/* Stop input */
driver->ops->stop_input(driver);
driver->input_active = 0;
/* Wait for any output still in the queue to be played. */
if ((drv->output_count > 0) || (drv->playing_count > 0))
interruptible_sleep_on(&drv->output_drain_wait);
/* Wait for any output still in the queue to be played. */
if (driver->output_count > 0)
interruptible_sleep_on(&driver->output_drain_wait);
/* Force any output to be stopped. */
drv->ops->stop_output(drv);
drv->output_active = 0;
drv->playing_count = 0;
drv->output_eof = 0;
/* Force any output to be stopped. */
driver->ops->stop_output(driver);
driver->output_active = 0;
}
/* Let the low-level driver do any release processing. */
if (driver->ops->release)
driver->ops->release(inode,file,driver);
/* Let the low-level driver do any release processing. */
if (drv->ops->release)
drv->ops->release(inode,file,drv);
if (file->f_mode & FMODE_READ)
driver->flags &= ~(SDF_OPEN_READ);
if (file->f_mode & FMODE_READ)
drv->flags &= ~(SDF_OPEN_READ);
if (file->f_mode & FMODE_WRITE)
driver->flags &= ~(SDF_OPEN_WRITE);
if (file->f_mode & FMODE_WRITE)
drv->flags &= ~(SDF_OPEN_WRITE);
MOD_DEC_USE_COUNT;
/* Status changed. Signal control device */
kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
wake_up_interruptible(&driver->open_wait);
MOD_DEC_USE_COUNT;
return 0;
wake_up_interruptible(&drv->open_wait);
return 0;
}
/* For 2.0 kernels */
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
static void sparcaudio_release(struct inode * inode, struct file * file)
{
sparcaudio_release_ret(inode, file);
}
#endif
static struct file_operations sparcaudio_fops = {
sparcaudio_llseek,
sparcaudio_lseek,
sparcaudio_read,
sparcaudio_write,
NULL, /* sparcaudio_readdir */
NULL, /* sparcaudio_select */
sparcaudio_select,
sparcaudio_ioctl,
NULL, /* sparcaudio_mmap */
sparcaudio_open,
NULL, /* sparcaudio_flush */
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
NULL, /* sparcaudio_flush */
#endif
sparcaudio_release
};
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
static struct symbol_table sparcaudio_syms = {
#include <linux/symtab_begin.h>
X(register_sparcaudio_driver),
X(unregister_sparcaudio_driver),
X(sparcaudio_output_done),
X(sparcaudio_input_done),
#include <linux/symtab_end.h>
};
#else
EXPORT_SYMBOL(register_sparcaudio_driver);
EXPORT_SYMBOL(unregister_sparcaudio_driver);
EXPORT_SYMBOL(sparcaudio_output_done);
EXPORT_SYMBOL(sparcaudio_input_done);
#endif
#ifdef MODULE
int init_module(void)
......@@ -1024,20 +2058,27 @@ int init_module(void)
__initfunc(int sparcaudio_init(void))
#endif
{
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
/* Export symbols for use by the low-level drivers. */
register_symtab(&sparcaudio_syms);
#endif
/* Register our character device driver with the VFS. */
if (register_chrdev(SOUND_MAJOR, "sparcaudio", &sparcaudio_fops))
return -EIO;
#ifdef CONFIG_SPARCAUDIO_AMD7930
amd7930_init();
#endif
#ifdef CONFIG_SPARCAUDIO_DBRI
dbri_init();
#endif
#ifdef CONFIG_SPARCAUDIO_CS4231
cs4231_init();
#endif
#ifdef CONFIG_SPARCAUDIO_DBRI
dbri_init();
#ifdef CONFIG_SPARCAUDIO_DUMMY
dummy_init();
#endif
return 0;
......@@ -1050,6 +2091,147 @@ void cleanup_module(void)
}
#endif
/*
* Code from Linux Streams, Copyright 1995 by
* Graham Wheeler, Francisco J. Ballesteros, Denis Froschauer
* and available under GPL
*/
static int
lis_add_to_elist( strevent_t **list, pid_t pid, short events )
{
strevent_t *ev = NULL;
if (*list != NULL)
{
for (ev=(*list)->se_next;
ev != *list && ev->se_pid < pid;
ev=ev->se_next
);
}
if (ev == NULL || ev == *list) /* no slot for pid in list */
{
if ((ev = (strevent_t*)kmalloc(sizeof(strevent_t),GFP_KERNEL))==NULL)
return(-ENOMEM);
if (!*list) /* create dummy head node */
{
strevent_t *hd;
if ((hd = (strevent_t*)kmalloc(sizeof(strevent_t),GFP_KERNEL)
)==NULL)
{
kfree(ev);
return(-ENOMEM);
}
(*list=hd)->se_pid=0;
hd->se_next=hd->se_prev=hd; /* empty list */
}
/* link node last in the list */
ev->se_prev=(*list)->se_prev;
(*list)->se_prev->se_next=ev;
((*list)->se_prev=ev)->se_next=*list;
ev->se_pid=pid;
ev->se_evs=0;
}
else if (ev->se_pid!=pid){ /* link node in the middle of the list */
strevent_t *new;
if ((new = (strevent_t*)kmalloc(sizeof(strevent_t),GFP_KERNEL))==NULL){
return(-ENOMEM);
}
new->se_prev=ev->se_prev;
new->se_next=ev;
ev->se_prev->se_next=new;
ev->se_prev=new;
ev = new ; /* use new element */
ev->se_pid=pid;
ev->se_evs=0;
}
ev->se_evs|=events;
return(0);
}
static int
lis_del_from_elist( strevent_t **list, pid_t pid, short events )
{
strevent_t *ev = NULL;
if (*list != NULL)
{
for (ev=(*list)->se_next;
ev != *list && ev->se_pid < pid;
ev=ev->se_next
);
}
if (ev == NULL || ev == *list || ev->se_pid != pid )
return(1);
if ( (ev->se_evs &= ~events) == 0 ){ /* unlink */
if (ev->se_next) /* should always be true */
ev->se_next->se_prev=ev->se_prev;
if (ev->se_prev) /* should always be true */
ev->se_prev->se_next=ev->se_next;
kfree(ev);
}
return(0);
}
static void
lis_free_elist( strevent_t **list )
{
strevent_t *ev;
strevent_t *nxt ;
for (ev = *list; ev != NULL; )
{
nxt = ev->se_next ;
kfree(ev) ;
ev = nxt ;
if (ev == *list) break ; /* all done */
}
*list = NULL ;
}
static short
lis_get_elist_ent( strevent_t *list, pid_t pid )
{
strevent_t *ev = NULL;
if (list == NULL) return(0) ;
for(ev = list->se_next ; ev != list && ev->se_pid < pid; ev=ev->se_next )
;
if (ev != list && ev->se_pid == pid)
return(ev->se_evs);
else
return(0);
}
static void
kill_procs( struct strevent *elist, int sig, short e)
{
strevent_t *ev;
int res;
(void) sig ;
if (elist) {
for(ev = elist->se_next ; ev != elist; ev=ev->se_next )
if ((ev->se_evs & e) != 0){
if ((res=kill_proc(ev->se_pid,SIGPOLL,1))<0) {
if (res == -3) {
lis_del_from_elist(&elist, ev->se_pid, S_ALL);
continue;
}
dprintk(("kill_proc: errno %d\n",res));
}
}
}
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
......
/*
* drivers/sbus/audio/cs4231.c
*
* Copyright (C) 1996, 1997 Derrick J Brashear (shadow@andrew.cmu.edu)
* Copyright 1996, 1997, 1998 Derrick J Brashear (shadow@andrew.cmu.edu)
*
* Based on the AMD7930 driver:
* Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
* Copyright 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
*
* This is the lowlevel driver for the CS4231 audio chip found on some
* sun4m and sun4u machines.
......@@ -14,6 +14,7 @@
* The APC DMA controller support unfortunately is not documented. Thanks, Sun
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
......@@ -22,6 +23,8 @@
#include <linux/malloc.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/soundcard.h>
#include <linux/version.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/system.h>
......@@ -35,7 +38,7 @@
#undef __CS4231_DEBUG
#undef __CS4231_TRACE
#undef __CS4231_ERROR
#define __CS4231_ERROR
#ifdef __CS4231_ERROR
#define eprintk(x) printk x
#else
......@@ -70,7 +73,7 @@ static int cs4231_length_to_samplecount(struct audio_prinfo *thisdir,
static void cs4231_getsamplecount(struct sparcaudio_driver *drv,
unsigned int length, unsigned int value);
#define CHIP_READY udelay(100); cs4231_ready(drv); mdelay(1);
#define CHIP_READY udelay(100); cs4231_ready(drv); udelay(1000);
/* Enable cs4231 interrupts atomically. */
static __inline__ void cs4231_enable_interrupts(struct sparcaudio_driver *drv)
......@@ -269,7 +272,7 @@ cs4231_set_output_encoding(struct sparcaudio_driver *drv, int value)
return 0;
}
}
eprintk(("output enc failed\n"));
dprintk(("output enc failed\n"));
return -EINVAL;
}
......@@ -285,7 +288,7 @@ cs4231_set_input_encoding(struct sparcaudio_driver *drv, int value)
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
int tmp_bits, set_bits;
dprintk(("input encoding %d\n", value));
tprintk(("input encoding %d\n", value));
if (value != 0) {
set_bits = cs4231_encoding_to_bits(drv, value);
if (set_bits >= 0) {
......@@ -299,7 +302,7 @@ cs4231_set_input_encoding(struct sparcaudio_driver *drv, int value)
return 0;
}
}
eprintk(("input enc failed\n"));
dprintk(("input enc failed\n"));
return -EINVAL;
}
......@@ -329,7 +332,7 @@ cs4231_set_output_rate(struct sparcaudio_driver *drv, int value)
return 0;
}
}
eprintk(("output rate failed\n"));
dprintk(("output rate failed\n"));
return -EINVAL;
}
......@@ -345,7 +348,7 @@ cs4231_set_input_rate(struct sparcaudio_driver *drv, int value)
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
int tmp_bits, set_bits;
dprintk(("input rate %d\n", value));
tprintk(("input rate %d\n", value));
if (value != 0) {
set_bits = cs4231_rate_to_bits(drv, value);
if (set_bits >= 0) {
......@@ -359,7 +362,7 @@ cs4231_set_input_rate(struct sparcaudio_driver *drv, int value)
return 0;
}
}
eprintk(("input rate failed\n"));
dprintk(("input rate failed\n"));
return -EINVAL;
}
......@@ -376,7 +379,7 @@ cs4231_set_input_channels(struct sparcaudio_driver *drv, int value)
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
int tmp_bits;
dprintk(("input channels %d\n", value));
tprintk(("input channels %d\n", value));
cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1c;
tmp_bits = cs4231_chip->regs->idr;
switch (value) {
......@@ -387,7 +390,7 @@ cs4231_set_input_channels(struct sparcaudio_driver *drv, int value)
cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits);
break;
default:
eprintk(("input chan failed\n"));
dprintk(("input chan failed\n"));
return -(EINVAL);
}
......@@ -421,7 +424,7 @@ cs4231_set_output_channels(struct sparcaudio_driver *drv, int value)
cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits);
break;
default:
eprintk(("output chan failed\n"));
dprintk(("output chan failed\n"));
return -(EINVAL);
}
......@@ -437,29 +440,33 @@ static int cs4231_get_output_channels(struct sparcaudio_driver *drv)
return cs4231_chip->perchip_info.play.channels;
}
static int cs4231_set_input_precision(struct sparcaudio_driver *drv, int value)
static int cs4231_get_input_precision(struct sparcaudio_driver *drv)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
cs4231_chip->perchip_info.record.precision = value;
return 0;
return cs4231_chip->perchip_info.record.precision;
}
static int cs4231_get_input_precision(struct sparcaudio_driver *drv)
static int cs4231_get_output_precision(struct sparcaudio_driver *drv)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
return cs4231_chip->perchip_info.record.precision;
return cs4231_chip->perchip_info.play.precision;
}
static int cs4231_set_output_precision(struct sparcaudio_driver *drv, int value)
static int cs4231_set_input_precision(struct sparcaudio_driver *drv, int val)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
cs4231_chip->perchip_info.play.precision = value;
return 0;
cs4231_chip->perchip_info.record.precision = val;
return cs4231_chip->perchip_info.record.precision;
}
static int cs4231_get_output_precision(struct sparcaudio_driver *drv)
static int cs4231_set_output_precision(struct sparcaudio_driver *drv, int val)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
cs4231_chip->perchip_info.play.precision = val;
return cs4231_chip->perchip_info.play.precision;
}
......@@ -509,6 +516,12 @@ static int cs4231_get_output_muted(struct sparcaudio_driver *drv)
return cs4231_chip->perchip_info.output_muted;
}
static int cs4231_get_formats(struct sparcaudio_driver *drv)
{
return (AFMT_MU_LAW | AFMT_A_LAW | AFMT_U8 | AFMT_IMA_ADPCM |
AFMT_S16_LE | AFMT_S16_BE);
}
static int cs4231_get_output_ports(struct sparcaudio_driver *drv)
{
return (AUDIO_LINE_OUT | AUDIO_SPEAKER | AUDIO_HEADPHONE);
......@@ -577,11 +590,11 @@ static int cs4231_set_input_port(struct sparcaudio_driver *drv, int value)
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
int retval = 0;
dprintk(("input port: %d\n", value));
tprintk(("input port: %d\n", value));
/* You can have one and only one. This is probably wrong, but
* appears to be how SunOS is doing it. Should be able to mix.
* More work to be done.
* More work to be done. CD input mixable, analog loopback may be.
*/
/* Ultra systems do not support AUDIO_INTERNAL_CD_IN */
......@@ -663,6 +676,58 @@ static int cs4231_get_monitor_volume(struct sparcaudio_driver *drv)
return (int)cs4231_chip->perchip_info.monitor_gain;
}
static int cs4231_get_output_error(struct sparcaudio_driver *drv)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
return (int)cs4231_chip->perchip_info.play.error;
}
static int cs4231_get_input_error(struct sparcaudio_driver *drv)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
return (int)cs4231_chip->perchip_info.record.error;
}
static int cs4231_get_output_samples(struct sparcaudio_driver *drv)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
int count =
cs4231_length_to_samplecount(&cs4231_chip->perchip_info.play,
cs4231_chip->regs->dmapc);
return (cs4231_chip->perchip_info.play.samples -
((count > cs4231_chip->perchip_info.play.samples)
? 0 : count));
}
static int cs4231_get_input_samples(struct sparcaudio_driver *drv)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
int count =
cs4231_length_to_samplecount(&cs4231_chip->perchip_info.record,
cs4231_chip->regs->dmacc);
return (cs4231_chip->perchip_info.record.samples -
((count > cs4231_chip->perchip_info.record.samples) ?
0 : count));
}
static int cs4231_get_output_pause(struct sparcaudio_driver *drv)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
return (int)cs4231_chip->perchip_info.play.pause;
}
static int cs4231_get_input_pause(struct sparcaudio_driver *drv)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
return (int)cs4231_chip->perchip_info.record.pause;
}
/* But for play/record we have these cheesy jacket routines because of
* how this crap gets set */
static int cs4231_set_input_volume(struct sparcaudio_driver *drv, int value)
......@@ -704,7 +769,7 @@ static int cs4231_set_input_balance(struct sparcaudio_driver *drv, int value)
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
cs4231_chip->perchip_info.record.balance = value;
cs4231_record_gain(drv, cs4231_chip->perchip_info.record.gain,
cs4231_record_gain(drv, cs4231_chip->perchip_info.record.gain,
cs4231_chip->perchip_info.record.balance);
return 0;
......@@ -745,13 +810,11 @@ static int cs4231_record_gain(struct sparcaudio_driver *drv, int value, unsigned
r = l = value;
if (balance < AUDIO_MID_BALANCE) {
r = (int)(value - ((AUDIO_MID_BALANCE - balance)
<< AUDIO_BALANCE_SHIFT));
if (r < 0) r = 0;
r = (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT));
if (r < 0) r = 0;
} else if (balance > AUDIO_MID_BALANCE) {
l = (int)(value - ((balance - AUDIO_MID_BALANCE)
<< AUDIO_BALANCE_SHIFT));
if (l < 0) l = 0;
l = (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT));
if (l < 0) l = 0;
}
l_adj = l * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1);
......@@ -785,20 +848,18 @@ static int cs4231_play_gain(struct sparcaudio_driver *drv, int value, unsigned c
tprintk(("in play_gain: %d %c\n", value, balance));
r = l = value;
if (balance < AUDIO_MID_BALANCE) {
r = (int)(value - ((AUDIO_MID_BALANCE - balance)
<< AUDIO_BALANCE_SHIFT));
if (r < 0) r = 0;
r = (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT));
if (r < 0) r = 0;
} else if (balance > AUDIO_MID_BALANCE) {
l = (int)(value - ((balance - AUDIO_MID_BALANCE)
<< AUDIO_BALANCE_SHIFT));
if (l < 0) l = 0;
l = (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT));
if (l < 0) l = 0;
}
(l == 0) ? (l_adj = CS4231_MAX_DEV_ATEN) : (l_adj = CS4231_MAX_ATEN -
(l * (CS4231_MAX_ATEN + 1) /
(AUDIO_MAX_GAIN + 1)));
(r == 0) ? (r_adj = CS4231_MAX_DEV_ATEN) : (r_adj = CS4231_MAX_ATEN -
(r * (CS4231_MAX_ATEN + 1) /
(r == 0) ? (r_adj = CS4231_MAX_DEV_ATEN) : (r_adj = CS4231_MAX_ATEN -
(r * (CS4231_MAX_ATEN + 1) /
(AUDIO_MAX_GAIN + 1)));
cs4231_chip->regs->iar = 0x6;
......@@ -811,10 +872,10 @@ static int cs4231_play_gain(struct sparcaudio_driver *drv, int value, unsigned c
if ((value == 0) || (value == AUDIO_MAX_GAIN)) {
tmp = value;
} else {
if (value == l)
if (value == l)
tmp = ((CS4231_MAX_ATEN - l_adj) * (AUDIO_MAX_GAIN + 1) /
(CS4231_MAX_ATEN + 1));
else if (r == value)
else if (value == r)
tmp = ((CS4231_MAX_ATEN - r_adj) * (AUDIO_MAX_GAIN + 1) /
(CS4231_MAX_ATEN + 1));
}
......@@ -900,7 +961,16 @@ static void cs4231_chip_reset(struct sparcaudio_driver *drv)
cs4231_output_muted(drv, 0);
cs4231_chip->recording_count = 0;
cs4231_chip->input_next_dma_handle = 0;
cs4231_chip->input_dma_handle = 0;
cs4231_chip->input_next_dma_size = 0;
cs4231_chip->input_dma_size = 0;
cs4231_chip->playing_count = 0;
cs4231_chip->output_next_dma_handle = 0;
cs4231_chip->output_dma_handle = 0;
cs4231_chip->output_next_dma_size = 0;
cs4231_chip->output_dma_size = 0;
}
static int
......@@ -945,6 +1015,7 @@ static void cs4231_getsamplecount(struct sparcaudio_driver *drv, unsigned int le
}
count = thisdir->samples;
length = cs4231_length_to_samplecount(thisdir, length);
/* normalize for where we are. */
thisdir->samples = ((count - nextcount) + (length - curcount));
}
......@@ -957,7 +1028,8 @@ static int cs4231_open(struct inode * inode, struct file * file, struct sparcaud
if (!(drv->flags & SDF_OPEN_WRITE) &&
(cs4231_chip->perchip_info.play.active == 0)) {
cs4231_chip->perchip_info.play.open = 1;
cs4231_set_output_port(drv, AUDIO_SPEAKER);
cs4231_chip->perchip_info.play.samples =
cs4231_chip->perchip_info.play.error = 0;
}
}
......@@ -965,7 +1037,8 @@ static int cs4231_open(struct inode * inode, struct file * file, struct sparcaud
if (!(drv->flags & SDF_OPEN_READ) &&
(cs4231_chip->perchip_info.record.active == 0)) {
cs4231_chip->perchip_info.record.open = 1;
cs4231_set_input_port(drv, AUDIO_MICROPHONE);
cs4231_chip->perchip_info.record.samples =
cs4231_chip->perchip_info.record.error = 0;
}
}
......@@ -984,11 +1057,40 @@ static void cs4231_release(struct inode * inode, struct file * file, struct spar
/* zero out any info about what data we have as well */
if (file->f_mode & FMODE_READ)
cs4231_chip->perchip_info.record.open = 0;
if (file->f_mode & FMODE_READ) {
/* stop capture here or midlevel? */
cs4231_chip->perchip_info.record.open = 0;
if (cs4231_chip->input_dma_handle) {
mmu_release_scsi_one((u32)((unsigned long)cs4231_chip->input_dma_handle),
cs4231_chip->input_dma_size, drv->dev->my_bus);
cs4231_chip->input_dma_handle = 0;
cs4231_chip->input_dma_size = 0;
}
if (cs4231_chip->input_next_dma_handle) {
mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_next_dma_handle),
cs4231_chip->input_next_dma_size, drv->dev->my_bus);
cs4231_chip->input_next_dma_handle = 0;
cs4231_chip->input_next_dma_size = 0;
}
}
if (file->f_mode & FMODE_WRITE)
if (file->f_mode & FMODE_WRITE) {
cs4231_chip->perchip_info.play.active =
cs4231_chip->perchip_info.play.open = 0;
if (cs4231_chip->output_dma_handle) {
mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_dma_handle),
cs4231_chip->output_dma_size, drv->dev->my_bus);
cs4231_chip->output_dma_handle = 0;
cs4231_chip->output_dma_size = 0;
}
if (cs4231_chip->output_next_dma_handle) {
mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_next_dma_handle),
cs4231_chip->output_next_dma_size,
drv->dev->my_bus);
cs4231_chip->output_next_dma_handle = 0;
cs4231_chip->output_next_dma_size = 0;
}
}
if (!cs4231_chip->perchip_info.play.open &&
!cs4231_chip->perchip_info.record.open &&
......@@ -1003,54 +1105,60 @@ static void cs4231_release(struct inode * inode, struct file * file, struct spar
static void cs4231_playintr(struct sparcaudio_driver *drv)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
int status = 0;
if (cs4231_chip->playlen == 0)
if (cs4231_chip->playlen == 0 && cs4231_chip->output_size > 0)
cs4231_chip->playlen = cs4231_chip->output_size;
if (cs4231_chip->output_dma_handle) {
mmu_release_scsi_one((u32)((unsigned long)cs4231_chip->output_dma_handle),
4096, drv->dev->my_bus);
cs4231_chip->output_dma_handle = 0;
mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_dma_handle),
cs4231_chip->output_dma_size, drv->dev->my_bus);
cs4231_chip->output_dma_handle = 0;
cs4231_chip->output_dma_size = 0;
cs4231_chip->playing_count--;
status++;
}
if (cs4231_chip->output_next_dma_handle) {
cs4231_chip->output_dma_handle = cs4231_chip->output_next_dma_handle;
cs4231_chip->output_next_dma_handle = 0;
cs4231_chip->output_dma_handle = cs4231_chip->output_next_dma_handle;
cs4231_chip->output_dma_size = cs4231_chip->output_next_dma_size;
cs4231_chip->output_next_dma_size = 0;
cs4231_chip->output_next_dma_handle = 0;
}
if (cs4231_chip->output_ptr && cs4231_chip->output_size > 0) {
cs4231_chip->output_next_dma_handle = (u32 *)(unsigned long)
mmu_get_scsi_one((char *) cs4231_chip->output_ptr,
4096, drv->dev->my_bus);
cs4231_chip->regs->dmapnva = (u32) (unsigned long)
cs4231_chip->output_next_dma_handle;
cs4231_chip->regs->dmapnc = cs4231_chip->output_size;
cs4231_chip->output_size = 0;
cs4231_chip->output_ptr = NULL;
cs4231_chip->playing_count++;
if ((cs4231_chip->output_ptr && cs4231_chip->output_size > 0) &&
!(cs4231_chip->perchip_info.play.pause)) {
cs4231_chip->output_next_dma_handle = (u32 *) (unsigned long)
mmu_get_scsi_one((char *) cs4231_chip->output_ptr,
cs4231_chip->output_size, drv->dev->my_bus);
cs4231_chip->regs->dmapnva = (u32) (unsigned long)
cs4231_chip->output_next_dma_handle;
cs4231_chip->output_next_dma_size = cs4231_chip->regs->dmapnc =
cs4231_chip->output_size;
cs4231_chip->output_size = 0;
cs4231_chip->output_ptr = NULL;
cs4231_chip->playing_count++;
status += 2;
} else {
cs4231_chip->regs->dmapnva = 0;
cs4231_chip->regs->dmapnc = 0;
}
/* Get two buffers into the pipe, then chain... */
if (cs4231_chip->playing_count < 3)
sparcaudio_output_done(drv, 0);
else {
cs4231_chip->playing_count--;
sparcaudio_output_done(drv, 1);
}
sparcaudio_output_done(drv, status);
return;
return;
}
static void cs4231_recmute(int fmt)
static void cs4231_recclear(int fmt, char *dmabuf, int length)
{
switch (fmt) {
case AUDIO_ENCODING_LINEAR:
/* Insert 0x00 from "here" to end of data stream */
memset(dmabuf, 0x00, length);
break;
case AUDIO_ENCODING_ALAW:
/* Insert 0xd5 from "here" to end of data stream */
memset(dmabuf, 0xd5, length);
break;
case AUDIO_ENCODING_ULAW:
/* Insert 0xff from "here" to end of data stream */
memset(dmabuf, 0xff, length);
break;
}
}
......@@ -1058,19 +1166,51 @@ static void cs4231_recmute(int fmt)
static int cs4231_recintr(struct sparcaudio_driver *drv)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
int status = 0;
if (cs4231_chip->perchip_info.record.active == 0) {
dprintk(("going inactive\n"));
cs4231_pollinput(drv);
cs4231_recmute(cs4231_chip->perchip_info.record.encoding);
cs4231_disable_rec(drv);
}
if (cs4231_chip->input_dma_handle) {
mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_dma_handle),
cs4231_chip->input_dma_size, drv->dev->my_bus);
cs4231_chip->input_dma_handle = 0;
cs4231_chip->input_dma_size = 0;
cs4231_chip->recording_count--;
status++;
}
if (cs4231_chip->input_ptr) {
cs4231_chip->regs->dmacnva = (__u32) ((unsigned long)cs4231_chip->input_ptr);
cs4231_chip->regs->dmacnc = cs4231_chip->input_size;
cs4231_chip->input_ptr = NULL;
if (cs4231_chip->input_next_dma_handle) {
cs4231_chip->input_dma_handle = cs4231_chip->input_next_dma_handle;
cs4231_chip->input_dma_size = cs4231_chip->input_next_dma_size;
cs4231_chip->input_next_dma_size = 0;
cs4231_chip->input_next_dma_handle = 0;
}
if ((cs4231_chip->input_ptr && cs4231_chip->input_size > 0) &&
!(cs4231_chip->perchip_info.record.pause)) {
cs4231_recclear(cs4231_chip->perchip_info.record.encoding,
(char *)cs4231_chip->input_ptr, cs4231_chip->input_size);
cs4231_chip->input_next_dma_handle = (u32*) (unsigned long)
mmu_get_scsi_one((char *) cs4231_chip->input_ptr,
cs4231_chip->input_size, drv->dev->my_bus);
cs4231_chip->regs->dmacnva = (u32) (unsigned long)
cs4231_chip->input_next_dma_handle;
cs4231_chip->input_next_dma_size = cs4231_chip->regs->dmacnc =
cs4231_chip->input_size;
cs4231_chip->input_size = 0;
sparcaudio_input_done(drv);
cs4231_chip->input_ptr = NULL;
cs4231_chip->recording_count++;
status += 2;
} else {
cs4231_chip->regs->dmacnva = 0;
cs4231_chip->regs->dmacnc = 0;
}
sparcaudio_input_done(drv, status);
return 1;
}
......@@ -1079,6 +1219,7 @@ static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer,
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
tprintk(("in 4231 start output\n"));
cs4231_chip->output_ptr = buffer;
cs4231_chip->output_size = count;
......@@ -1089,16 +1230,21 @@ static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer,
cs4231_ready(drv);
cs4231_chip->perchip_info.play.active = 1;
cs4231_chip->playing_count = 0;
cs4231_disable_play(drv);
cs4231_chip->regs->dmacsr &= ~CS_XINT_PLAY;
cs4231_chip->regs->dmacsr &= ~CS_PPAUSE;
cs4231_playintr(drv);
cs4231_enable_play(drv);
cs4231_chip->regs->dmacsr |= CS_PLAY_SETUP;
cs4231_ready(drv);
if ((cs4231_chip->regs->dmacsr & CS_PPAUSE) ||
!(cs4231_chip->regs->dmacsr & PDMA_READY)) {
cs4231_chip->regs->dmacsr &= ~CS_XINT_PLAY;
cs4231_chip->regs->dmacsr &= ~CS_PPAUSE;
cs4231_playintr(drv);
cs4231_chip->regs->dmacsr |= CS_PLAY_SETUP;
cs4231_enable_play(drv);
cs4231_ready(drv);
} else
cs4231_playintr(drv);
}
static void cs4231_stop_output(struct sparcaudio_driver *drv)
......@@ -1109,17 +1255,17 @@ static void cs4231_stop_output(struct sparcaudio_driver *drv)
cs4231_chip->output_ptr = NULL;
cs4231_chip->output_size = 0;
if (cs4231_chip->output_dma_handle) {
mmu_release_scsi_one((u32)((unsigned long)cs4231_chip->output_dma_handle),
4096, drv->dev->my_bus);
cs4231_chip->output_dma_handle = 0;
mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_dma_handle),
cs4231_chip->output_dma_size, drv->dev->my_bus);
cs4231_chip->output_dma_handle = 0;
cs4231_chip->output_dma_size = 0;
}
if (cs4231_chip->output_next_dma_handle) {
mmu_release_scsi_one((u32)((unsigned long)cs4231_chip->output_next_dma_handle),
4096, drv->dev->my_bus);
cs4231_chip->output_next_dma_handle = 0;
mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_next_dma_handle),
cs4231_chip->output_next_dma_size, drv->dev->my_bus);
cs4231_chip->output_next_dma_handle = 0;
cs4231_chip->output_next_dma_size = 0;
}
cs4231_chip->perchip_info.play.active = 0;
cs4231_chip->regs->dmacsr |= (CS_PPAUSE);
}
static void cs4231_pollinput(struct sparcaudio_driver *drv)
......@@ -1138,6 +1284,9 @@ static void cs4231_start_input(struct sparcaudio_driver *drv, __u8 * buffer,
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
cs4231_chip->input_ptr = buffer;
cs4231_chip->input_size = count;
if (cs4231_chip->perchip_info.record.active ||
(cs4231_chip->perchip_info.record.pause))
return;
......@@ -1146,12 +1295,20 @@ static void cs4231_start_input(struct sparcaudio_driver *drv, __u8 * buffer,
cs4231_chip->perchip_info.record.active = 1;
cs4231_chip->recording_count = 0;
/* init dma foo here */
cs4231_chip->regs->dmacsr &= ~CS_XINT_CAPT;
cs4231_chip->regs->dmacsr &= ~CS_CPAUSE;
cs4231_recintr(drv);
cs4231_chip->regs->dmacsr |= CS_CAPT_SETUP;
cs4231_enable_rec(drv);
if ((cs4231_chip->regs->dmacsr & CS_CPAUSE) ||
!(cs4231_chip->regs->dmacsr & CDMA_READY)) {
cs4231_chip->regs->dmacsr &= ~CS_XINT_CAPT;
cs4231_chip->regs->dmacsr &= ~CS_CPAUSE;
cs4231_recintr(drv);
cs4231_chip->regs->dmacsr |= CS_CAPT_SETUP;
cs4231_enable_rec(drv);
cs4231_ready(drv);
} else
cs4231_recintr(drv);
}
static void cs4231_stop_input(struct sparcaudio_driver *drv)
......@@ -1161,11 +1318,89 @@ static void cs4231_stop_input(struct sparcaudio_driver *drv)
cs4231_chip->perchip_info.record.active = 0;
cs4231_chip->regs->dmacsr |= (CS_CPAUSE);
cs4231_chip->input_ptr = NULL;
cs4231_chip->input_size = 0;
if (cs4231_chip->input_dma_handle) {
mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_dma_handle),
cs4231_chip->input_dma_size, drv->dev->my_bus);
cs4231_chip->input_dma_handle = 0;
cs4231_chip->input_dma_size = 0;
}
if (cs4231_chip->input_next_dma_handle) {
mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_next_dma_handle),
cs4231_chip->input_next_dma_size, drv->dev->my_bus);
cs4231_chip->input_next_dma_handle = 0;
cs4231_chip->input_next_dma_size = 0;
}
cs4231_pollinput(drv);
}
static int cs4231_set_output_pause(struct sparcaudio_driver *drv, int value)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
cs4231_chip->perchip_info.play.pause = value;
if (!value)
sparcaudio_output_done(drv, 0);
return value;
}
static int cs4231_set_output_error(struct sparcaudio_driver *drv, int value)
{
int i;
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
i = cs4231_chip->perchip_info.play.error;
cs4231_chip->perchip_info.play.error = value;
return i;
}
static int cs4231_set_input_error(struct sparcaudio_driver *drv, int value)
{
int i;
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
i = cs4231_chip->perchip_info.record.error;
cs4231_chip->perchip_info.record.error = value;
return i;
}
static int cs4231_set_output_samples(struct sparcaudio_driver *drv, int value)
{
int i;
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
i = cs4231_chip->perchip_info.play.samples;
cs4231_chip->perchip_info.play.samples = value;
return i;
}
static int cs4231_set_input_samples(struct sparcaudio_driver *drv, int value)
{
int i;
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
i = cs4231_chip->perchip_info.record.samples;
cs4231_chip->perchip_info.record.samples = value;
return i;
}
/* need adjust the end pointer, process the input, and clean up the dma */
static int cs4231_set_input_pause(struct sparcaudio_driver *drv, int value)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
cs4231_disable_rec(drv);
cs4231_chip->perchip_info.record.pause = value;
if (value)
cs4231_stop_input(drv);
return value;
}
static void cs4231_audio_getdev(struct sparcaudio_driver *drv,
......@@ -1186,7 +1421,7 @@ static void cs4231_audio_getdev(struct sparcaudio_driver *drv,
static int cs4231_audio_getdev_sunos(struct sparcaudio_driver *drv)
{
return AUDIO_DEV_CS4231;
return AUDIO_DEV_CS4231;
}
static void cs4231_loopback(struct sparcaudio_driver *drv, unsigned int value)
......@@ -1238,9 +1473,9 @@ void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs)
if (dummy & CS_XINT_PNVA) {
cs4231_chip->perchip_info.play.samples +=
cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.play),
cs4231_chip->playlen);
cs4231_chip->playlen);
cs4231_playintr(drv);
}
}
/* Any other conditions we need worry about? */
}
......@@ -1254,16 +1489,25 @@ void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* Any other conditions we need worry about? */
}
if ((dummy & CS_XINT_CEMP)
&& (cs4231_chip->perchip_info.record.active == 0))
{
/* Fix me */
cs4231_chip->perchip_info.record.active = 0;
if (dummy & CS_XINT_CEMP) {
if (cs4231_chip->perchip_info.record.active == 0) {
/* Fix me */
cs4231_chip->perchip_info.record.active = 0;
cs4231_chip->perchip_info.record.error = 1;
cs4231_recintr(drv);
}
if ((dummy & CS_XINT_EMPT) && (cs4231_chip->perchip_info.play.active == 0)) {
cs4231_chip->regs->dmacsr |= (CS_PPAUSE);
cs4231_disable_play(drv);
}
if (dummy & CS_XINT_EMPT) {
if (!cs4231_chip->output_next_dma_handle) {
cs4231_chip->regs->dmacsr |= (CS_PPAUSE);
cs4231_disable_play(drv);
cs4231_chip->perchip_info.play.error = 1;
}
cs4231_chip->perchip_info.play.active = 0;
cs4231_playintr(drv);
cs4231_getsamplecount(drv, cs4231_chip->playlen, 0);
}
......@@ -1318,15 +1562,31 @@ static struct sparcaudio_operations cs4231_ops = {
cs4231_get_input_ports,
cs4231_output_muted,
cs4231_get_output_muted,
cs4231_set_output_pause,
cs4231_get_output_pause,
cs4231_set_input_pause,
cs4231_get_input_pause,
cs4231_set_output_samples,
cs4231_get_output_samples,
cs4231_set_input_samples,
cs4231_get_input_samples,
cs4231_set_output_error,
cs4231_get_output_error,
cs4231_set_input_error,
cs4231_get_input_error,
cs4231_get_formats,
};
/* Attach to an cs4231 chip given its PROM node. */
static int cs4231_attach(struct sparcaudio_driver *drv,
struct linux_sbus_device *sdev)
{
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
struct linux_prom_irqs irq;
#endif
struct linux_sbus *sbus = sdev->my_bus;
struct cs4231_chip *cs4231_chip;
int err;
struct linux_sbus *sbus = sdev->my_bus;
/* Allocate our private information structure. */
drv->private = kmalloc(sizeof(struct cs4231_chip), GFP_KERNEL);
......@@ -1343,8 +1603,15 @@ static int cs4231_attach(struct sparcaudio_driver *drv,
drv->dev = sdev;
/* Map the registers into memory. */
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
prom_apply_sbus_ranges(sbus, &sdev->reg_addrs[0],
sdev->num_registers, sdev);
#else
prom_apply_sbus_ranges(sbus, sdev->reg_addrs, 1, sdev);
#endif
cs4231_chip->regs_size = sdev->reg_addrs[0].reg_size;
cs4231_chip->regs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
sdev->reg_addrs[0].reg_size,
"cs4231", sdev->reg_addrs[0].which_io,
......@@ -1357,9 +1624,23 @@ static int cs4231_attach(struct sparcaudio_driver *drv,
}
/* Attach the interrupt handler to the audio interrupt. */
#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
prom_getproperty(sdev->prom_node, "intr", (char *)&irq, sizeof(irq));
if (irq.pri < 0) {
sparc_free_io(cs4231_chip->regs, cs4231_chip->regs_size);
kfree(drv->private);
return -EIO;
}
cs4231_chip->irq = irq.pri;
#else
cs4231_chip->irq = sdev->irqs[0];
#endif
request_irq(cs4231_chip->irq, cs4231_interrupt, SA_SHIRQ, "cs4231", drv);
enable_irq(cs4231_chip->irq);
cs4231_enable_interrupts(drv);
......@@ -1368,7 +1649,7 @@ static int cs4231_attach(struct sparcaudio_driver *drv,
cs4231_chip_reset(drv);
/* Register ourselves with the midlevel audio driver. */
err = register_sparcaudio_driver(drv);
err = register_sparcaudio_driver(drv, 1);
if (err < 0) {
printk(KERN_ERR "cs4231: unable to register\n");
......@@ -1396,8 +1677,8 @@ static int cs4231_attach(struct sparcaudio_driver *drv,
AUDIO_ANALOG_LOOPBACK);
/* Announce the hardware to the user. */
printk(KERN_INFO "cs4231%c at 0x%lx irq %d\n",
(cs4231_chip->status & CS_STATUS_REV_A) ? 'a' : ' ',
printk(KERN_INFO "audio%d: cs4231%c at 0x%lx irq %d\n",
drv->index, (cs4231_chip->status & CS_STATUS_REV_A) ? 'a' : ' ',
(unsigned long)cs4231_chip->regs, cs4231_chip->irq);
/* Success! */
......@@ -1439,7 +1720,7 @@ static void cs4231_detach(struct sparcaudio_driver *drv)
struct cs4231_chip *info = (struct cs4231_chip *)drv->private;
cs4231_disable_interrupts(drv);
unregister_sparcaudio_driver(drv);
unregister_sparcaudio_driver(drv, 1);
disable_irq(info->irq);
free_irq(info->irq, drv);
sparc_free_io(info->regs, info->regs_size);
......
......@@ -49,10 +49,13 @@ struct cs4231_chip {
volatile __u8 * output_ptr;
volatile unsigned long output_size;
volatile __u32 * output_dma_handle, * output_next_dma_handle;
volatile unsigned long output_dma_size, output_next_dma_size;
/* Current record buffer. */
volatile __u8 * input_ptr;
volatile unsigned long input_size;
volatile __u32 * input_dma_handle, * input_next_dma_handle;
volatile unsigned long input_dma_size, input_next_dma_size;
/* Number of buffers in the pipe. */
volatile unsigned long playing_count;
......
......@@ -51,6 +51,7 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/malloc.h>
#include <linux/version.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/system.h>
......@@ -62,10 +63,11 @@
#include <asm/audioio.h>
#include "dbri.h"
#if defined(DBRI_ISDN) || defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
#include "../../isdn/hisax/hisax.h"
#include "../../isdn/hisax/isdnl1.h"
#include "../../isdn/hisax/foreign.h"
#endif
/* #define DBRI_DEBUG */
......@@ -106,7 +108,7 @@ static char *cmds[] = {
static struct sparcaudio_driver drivers[MAX_DRIVERS];
static char drv_name[] = "DBRI/audio";
static int num_drivers = 0;
static int num_drivers;
static void * output_callback_arg;
......@@ -144,21 +146,21 @@ static void * output_callback_arg;
static int dbri_cmdlock(struct dbri *dbri)
{
return 0;
return 0;
}
static void dbri_cmdsend(struct dbri *dbri, int n)
{
int maxloops = 1000000;
int maxloops = 1000000;
dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, WAIT_INTR1);
dbri->regs->reg8 = (int)dbri->cmd;
dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, WAIT_INTR1);
dbri->regs->reg8 = (int)dbri->cmd;
while (maxloops > 0 && (dbri->regs->reg0 & D_P));
while (maxloops > 0 && (dbri->regs->reg0 & D_P));
if (maxloops == 0) {
printk("DBRI: Maxloops exceeded in dbri_cmdsend\n");
}
if (maxloops == 0) {
printk("DBRI: Maxloops exceeded in dbri_cmdsend\n");
}
}
static void dbri_reset(struct sparcaudio_driver *drv)
......@@ -180,7 +182,7 @@ static void dbri_detach(struct sparcaudio_driver *drv)
struct dbri *info = (struct dbri *)drv->private;
dbri_reset(drv);
unregister_sparcaudio_driver(drv);
unregister_sparcaudio_driver(drv, 1);
free_irq(info->irq, drv);
sparc_free_io(info->regs, info->regs_size);
kfree(drv->private);
......@@ -223,7 +225,7 @@ static void dbri_initialize(struct sparcaudio_driver *drv)
dbri->cmd[n++] = DBRI_CMD(D_IIQ, 0, 0);
dbri->cmd[n++] = (int)(dbri->intr);
dbri_cmdsend(dbri, n);
dbri_cmdsend(dbri, n);
}
......@@ -366,7 +368,7 @@ static void mmcodec_init_data(struct dbri *dbri)
/* CHI: Slave mode; enable interrupts */
dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0) | D_CHI_IR | D_CHI_EN);
dbri_cmdsend(dbri, n);
dbri_cmdsend(dbri, n);
}
......@@ -486,8 +488,8 @@ static void mmcodec_setctrl(struct dbri *dbri)
dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
/* Wait for the command to complete */
dbri_cmdsend(dbri, n);
/* Wait for the command to complete */
dbri_cmdsend(dbri, n);
/* Switch CS4215 to data mode - data sheet says
* "Set CLB=1 and send two more frames of valid control info"
......@@ -503,7 +505,7 @@ static void mmcodec_setctrl(struct dbri *dbri)
/* Two frames of control info @ 8kHz frame rate = 250 us delay */
udelay(250);
n = dbri_cmdlock(dbri);
n = dbri_cmdlock(dbri);
/* Now switch back to data mode */
/* Reset CHI Anchor: Stop Send/Receive */
......@@ -586,81 +588,80 @@ void dbri_isdn_init(struct dbri *dbri)
n = dbri_cmdlock(dbri);
/* Pipe 0: SDP */
val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_0);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
/* Pipe 0: SDP */
val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_0);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
/* Pipe 8: SDP */
val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_8);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
/* Pipe 8: SDP */
val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_8);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
/* Pipe 9: SDP */
val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_9);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
/* Pipe 9: SDP */
val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_9);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
/* Pipe 1: SDP */
val = D_SDP_HDLC_D|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_1);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
/* Pipe 1: SDP */
val = D_SDP_HDLC_D|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_1);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
/* Pipe 10: SDP */
val = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_10);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
/* Pipe 10: SDP */
val = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_10);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
/* Pipe 11: SDP */
val = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_11);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
/* Pipe 11: SDP */
val = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_11);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
/* Pipe 0: DTS */
val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_0) | D_PIPE(D_P_0);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = D_TS_LEN(2) | D_TS_CYCLE(17)| D_TS_NEXT(D_P_0);
dbri->cmd[n++] = 0;
val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_0) | D_PIPE(D_P_0);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = D_TS_LEN(2) | D_TS_CYCLE(17)| D_TS_NEXT(D_P_0);
dbri->cmd[n++] = 0;
/* Pipe 8: DTS */
val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_0) | D_PIPE(D_P_8);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_0);
dbri->cmd[n++] = 0;
val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_0) | D_PIPE(D_P_8);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_0);
dbri->cmd[n++] = 0;
/* Pipe 9: DTS */
val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_8) | D_PIPE(D_P_9);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_0);
dbri->cmd[n++] = 0;
val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_8) | D_PIPE(D_P_9);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_0);
dbri->cmd[n++] = 0;
/* Pipe 1: DTS */
val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_1) | D_PIPE(D_P_1);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = 0;
dbri->cmd[n++] = D_TS_LEN(2) | D_TS_CYCLE(17)| D_TS_NEXT(D_P_1);
val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_1) | D_PIPE(D_P_1);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = 0;
dbri->cmd[n++] = D_TS_LEN(2) | D_TS_CYCLE(17)| D_TS_NEXT(D_P_1);
/* Pipe 10: DTS */
val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_1) | D_PIPE(D_P_10);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = 0;
dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_1);
val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_1) | D_PIPE(D_P_10);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = 0;
dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_1);
/* Pipe 11: DTS */
val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_10) | D_PIPE(D_P_11);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = 0;
dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_1);
val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_10) | D_PIPE(D_P_11);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = 0;
dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_1);
/* Wait for command to complete */
dbri_cmdsend(dbri, n);
dbri_cmdsend(dbri, n);
}
void dbri_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
......@@ -798,8 +799,8 @@ void dbri_intr(int irq, void *dev_id, struct pt_regs *regs)
x = reverse_bytes(val,2)&CS4215_12_MASK;
printk("Comparing int: %x with hi(%x)\n", x, *(int *)dbri->mm.ctrl);
if(x == (*(int *)dbri->mm.ctrl >> 16)) {
printk("Comp ok\n");
wake_up(&dbri->int_wait);
printk("Comp ok\n");
wake_up(&dbri->int_wait);
}
}
break;
......@@ -838,13 +839,6 @@ static int dbri_open(struct inode * inode, struct file * file,
{
struct dbri *dbri = (struct dbri *)drv->private;
#if 0
/* Set the default audio parameters. */
info->rgain = 128;
info->pgain = 200;
info->mgain = 0;
#endif
MOD_INC_USE_COUNT;
return 0;
......@@ -881,7 +875,7 @@ static void dbri_start_output(struct sparcaudio_driver *drv,
output_callback_arg = drv;
dbri_cmdsend(dbri, n);
dbri_cmdsend(dbri, n);
}
static void dbri_stop_output(struct sparcaudio_driver *drv)
......@@ -889,6 +883,8 @@ static void dbri_stop_output(struct sparcaudio_driver *drv)
struct dbri *dbri = (struct dbri *)drv->private;
}
static struct sparcaudio_operations dbri_ops = {
dbri_open,
dbri_release,
......@@ -935,68 +931,66 @@ static struct sparcaudio_operations dbri_ops = {
dummy, /* dbri_get_output_muted, */
};
/*
****************************************************************************
************************** ISDN (Hisax) Interface **************************
****************************************************************************
*/
int dbri_get_irqnum(int dev)
{
struct dbri *dbri;
struct dbri *dbri;
if (dev >= num_drivers) {
return(0);
}
if (dev > num_drivers) {
return(0);
}
dbri = (struct dbri *) drivers[dev].private;
dbri = (struct dbri *) drivers[dev].private;
/* On the sparc, the cpu's irq number is only part of the "irq" */
return (dbri->irq & NR_IRQS);
return (dbri->irq & NR_IRQS);
}
int dbri_get_liu_state(int dev)
{
struct dbri *dbri;
struct dbri *dbri;
if (dev >= num_drivers) {
return(0);
}
if (dev > num_drivers) {
return(0);
}
dbri = (struct dbri *) drivers[dev].private;
dbri = (struct dbri *) drivers[dev].private;
return dbri->liu_state;
return dbri->liu_state;
}
void dbri_liu_init(int dev, void (*callback)(void *), void *callback_arg)
{
struct dbri *dbri;
struct dbri *dbri;
if (dev >= num_drivers) {
return;
}
if (dev > num_drivers) {
return;
}
dbri = (struct dbri *) drivers[dev].private;
dbri = (struct dbri *) drivers[dev].private;
/* Set callback for LIU state change */
/* Set callback for LIU state change */
dbri->liu_callback = callback;
dbri->liu_callback_arg = callback_arg;
dbri->liu_callback_arg = callback_arg;
dbri_isdn_init(dbri);
}
void dbri_liu_activate(int dev, int priority)
{
struct dbri *dbri;
struct dbri *dbri;
int n, val;
if (dev >= num_drivers) {
return;
}
if (dev > num_drivers) {
return;
}
dbri = (struct dbri *) drivers[dev].private;
dbri = (struct dbri *) drivers[dev].private;
n = dbri_cmdlock(dbri);
......@@ -1004,7 +998,7 @@ void dbri_liu_activate(int dev, int priority)
val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT;
dbri->cmd[n++] = DBRI_CMD(D_TE, 0, val);
dbri_cmdsend(dbri, n);
dbri_cmdsend(dbri, n);
/* Activate the interface */
dbri->regs->reg0 |= D_T;
......@@ -1012,13 +1006,13 @@ void dbri_liu_activate(int dev, int priority)
void dbri_liu_deactivate(int dev)
{
struct dbri *dbri;
struct dbri *dbri;
if (dev >= num_drivers) {
return;
}
if (dev > num_drivers) {
return;
}
dbri = (struct dbri *) drivers[dev].private;
dbri = (struct dbri *) drivers[dev].private;
/* Turn off the ISDN TE interface */
dbri->regs->reg0 &= ~D_T;
......@@ -1027,14 +1021,14 @@ void dbri_liu_deactivate(int dev)
void dbri_dxmit(int dev, __u8 *buffer, unsigned int count,
void (*callback)(void *, int), void *callback_arg)
{
struct dbri *dbri;
struct dbri *dbri;
int n, val;
if (dev >= num_drivers) {
return;
}
if (dev > num_drivers) {
return;
}
dbri = (struct dbri *) drivers[dev].private;
dbri = (struct dbri *) drivers[dev].private;
if (count > (1 << 14) - 1) {
printk("dbri_dxmit called with count=%d; truncated", count);
......@@ -1051,28 +1045,28 @@ void dbri_dxmit(int dev, __u8 *buffer, unsigned int count,
dbri->D.td.status = 0;
/* Pipe 1 is D channel transmit */
val = D_SDP_HDLC_D|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_1);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = (__u32)&dbri->D.td;
val = D_SDP_HDLC_D|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_1);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = (__u32)&dbri->D.td;
dbri->D.output_callback = callback;
dbri->D.output_callback_arg = callback_arg;
dbri_cmdsend(dbri, n);
dbri_cmdsend(dbri, n);
}
void dbri_drecv(int dev, __u8 *buffer, unsigned int size,
void (*callback)(void *, int, unsigned int),
void *callback_arg)
{
struct dbri *dbri;
struct dbri *dbri;
int n, val;
if (dev >= num_drivers) {
return;
}
if (dev > num_drivers) {
return;
}
dbri = (struct dbri *) drivers[dev].private;
dbri = (struct dbri *) drivers[dev].private;
if (size > (1 << 14) - 1) {
printk("dbri_drecv called with size=%d; truncated", size);
......@@ -1092,11 +1086,11 @@ void dbri_drecv(int dev, __u8 *buffer, unsigned int size,
dbri->D.rd.status = DBRI_RD_B | DBRI_RD_BCNT(size);
/* Pipe 0 is D channel receive */
val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_0);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = (__u32)&dbri->D.rd;
val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_0);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = (__u32)&dbri->D.rd;
dbri_cmdsend(dbri, n);
dbri_cmdsend(dbri, n);
dbri->D.input_callback = callback;
dbri->D.input_callback_arg = callback_arg;
......@@ -1105,14 +1099,14 @@ void dbri_drecv(int dev, __u8 *buffer, unsigned int size,
int dbri_bopen(int dev, unsigned int chan,
int hdlcmode, u_char xmit_idle_char)
{
struct dbri *dbri;
struct dbri *dbri;
int n, val;
if (dev >= num_drivers || chan > 1) {
return -1;
}
if (dev > num_drivers || chan > 1) {
return -1;
}
dbri = (struct dbri *) drivers[dev].private;
dbri = (struct dbri *) drivers[dev].private;
if (hdlcmode) {
......@@ -1149,20 +1143,20 @@ int dbri_bopen(int dev, unsigned int chan,
dbri->B[chan].output_callback = NULL;
dbri->B[chan].input_callback = NULL;
dbri_cmdsend(dbri, n);
dbri_cmdsend(dbri, n);
return 0;
}
void dbri_bclose(int dev, unsigned int chan)
{
struct dbri *dbri;
struct dbri *dbri;
if (dev >= num_drivers || chan > 1) {
return;
}
if (dev > num_drivers || chan > 1) {
return;
}
dbri = (struct dbri *) drivers[dev].private;
dbri = (struct dbri *) drivers[dev].private;
dbri->B[chan].output_callback = NULL;
dbri->B[chan].input_callback = NULL;
......@@ -1173,14 +1167,14 @@ void dbri_bxmit(int dev, unsigned int chan,
void (*callback)(void *, int),
void *callback_arg)
{
struct dbri *dbri;
struct dbri *dbri;
int n, val;
if (dev >= num_drivers || chan > 1) {
return;
}
if (dev > num_drivers || chan > 1) {
return;
}
dbri = (struct dbri *) drivers[dev].private;
dbri = (struct dbri *) drivers[dev].private;
if (count > (1 << 14) - 1) {
printk("dbri_bxmit called with count=%ld; truncated", count);
......@@ -1197,14 +1191,14 @@ void dbri_bxmit(int dev, unsigned int chan,
dbri->B[chan].td.status = 0;
/* Pipe 10/11 is B1/B2 channel transmit */
val = dbri->B[chan].xmitSDP;
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = (__u32)&dbri->B[chan].td;
val = dbri->B[chan].xmitSDP;
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = (__u32)&dbri->B[chan].td;
dbri->B[chan].output_callback = callback;
dbri->B[chan].output_callback_arg = callback_arg;
dbri_cmdsend(dbri, n);
dbri_cmdsend(dbri, n);
}
void dbri_brecv(int dev, unsigned int chan,
......@@ -1212,14 +1206,14 @@ void dbri_brecv(int dev, unsigned int chan,
void (*callback)(void *, int, unsigned int),
void *callback_arg)
{
struct dbri *dbri;
struct dbri *dbri;
int n, val;
if (dev >= num_drivers || chan > 1) {
return;
}
if (dev > num_drivers || chan > 1) {
return;
}
dbri = (struct dbri *) drivers[dev].private;
dbri = (struct dbri *) drivers[dev].private;
if (size > (1 << 14) - 1) {
printk("dbri_brecv called with size=%ld; truncated", size);
......@@ -1239,16 +1233,17 @@ void dbri_brecv(int dev, unsigned int chan,
dbri->B[chan].rd.status = DBRI_RD_B | DBRI_RD_BCNT(size);
/* Pipe 8/9 is B1/B2 channel receive */
val = dbri->B[chan].recvSDP;
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = (__u32)&dbri->B[chan].rd;
val = dbri->B[chan].recvSDP;
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = (__u32)&dbri->B[chan].rd;
dbri_cmdsend(dbri, n);
dbri_cmdsend(dbri, n);
dbri->B[chan].input_callback = callback;
dbri->B[chan].input_callback_arg = callback_arg;
}
#if defined(DBRI_ISDN) || defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
struct foreign_interface dbri_foreign_interface = {
dbri_get_irqnum,
dbri_get_liu_state,
......@@ -1263,6 +1258,7 @@ struct foreign_interface dbri_foreign_interface = {
dbri_brecv
};
EXPORT_SYMBOL(dbri_foreign_interface);
#endif
/*
****************************************************************************
......@@ -1270,7 +1266,6 @@ EXPORT_SYMBOL(dbri_foreign_interface);
****************************************************************************
*/
static int dbri_attach(struct sparcaudio_driver *drv,
struct linux_sbus_device *sdev)
{
......@@ -1319,7 +1314,7 @@ static int dbri_attach(struct sparcaudio_driver *drv,
}
/* Register ourselves with the midlevel audio driver. */
err = register_sparcaudio_driver(drv);
err = register_sparcaudio_driver(drv,1);
if (err) {
printk(KERN_ERR "DBRI: unable to register audio\n");
free_irq(dbri->irq, drv);
......@@ -1397,7 +1392,7 @@ void cleanup_module(void)
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* Local Variables:
* c-indent-level: 8
* c-brace-imaginary-offset: 0
* c-brace-offset: -8
......
......@@ -129,7 +129,7 @@ struct dbri {
/* Special bits for some commands */
#define D_PIPE(v) ((v)<<0) /* Pipe Nr: 0-15 long, 16-21 short */
#define D_PIPE(v) ((v)<<0) /* Pipe Nr: 0-15 long, 16-21 short */
/* Setup Data Pipe */
/* IRM */
......@@ -159,8 +159,8 @@ struct dbri {
#define D_DTS_VO (1<<16) /* Valid Output Time-Slot Descriptor */
#define D_DTS_INS (1<<15) /* Insert Time Slot */
#define D_DTS_DEL (0<<15) /* Delete Time Slot */
#define D_DTS_PRVIN(v) ((v)<<10) /* Previous In Pipe */
#define D_DTS_PRVOUT(v) ((v)<<5) /* Previous Out Pipe */
#define D_DTS_PRVIN(v) ((v)<<10) /* Previous In Pipe */
#define D_DTS_PRVOUT(v) ((v)<<5) /* Previous Out Pipe */
/* Time Slot defines */
#define D_TS_LEN(v) (v<<24) /* Number of bits in this time slot */
......@@ -170,8 +170,8 @@ struct dbri {
#define D_TS_MONITOR (2<<10) /* Monitor pipe */
#define D_TS_NONCONTIG (3<<10) /* Non contiguous mode */
#define D_TS_ANCHOR (7<<10) /* Starting short pipes */
#define D_TS_MON(v) ((v)<<5) /* Monitor Pipe */
#define D_TS_NEXT(v) ((v)<<0) /* Pipe Nr: 0-15 long, 16-21 short */
#define D_TS_MON(v) ((v)<<5) /* Monitor Pipe */
#define D_TS_NEXT(v) ((v)<<0) /* Pipe Nr: 0-15 long, 16-21 short */
/* Concentration Highway Interface Modes */
#define D_CHI_CHICM(v) (v<<16) /* Clock mode */
......@@ -296,7 +296,7 @@ struct dbri {
#define DBRI_TD_UNR (1<<3) /* Underrun: transmitter is out of data */
#define DBRI_TD_ABT (1<<2) /* Abort: frame aborted */
#define DBRI_TD_TBC (1<<0) /* Transmit buffer Complete */
#define DBRI_TD_STATUS(v) ((v)&0xff) /* Transmit status */
#define DBRI_TD_STATUS(v) ((v)&0xff) /* Transmit status */
/* Receive descriptor defines */
#define DBRI_RD_F (1<<31) /* End of Frame */
......@@ -308,7 +308,7 @@ struct dbri {
#define DBRI_RD_BBC (1<<6) /* 1: Bad Byte recieved */
#define DBRI_RD_ABT (1<<5) /* Abort: frame aborted */
#define DBRI_RD_OVRN (1<<3) /* Overrun: data lost */
#define DBRI_RD_STATUS(v) ((v)&0xff) /* Receive status */
#define DBRI_RD_CNT(v) ((v>>16)&0x1fff) /* Number of valid bytes in the buffer */
#define DBRI_RD_STATUS(v) ((v)&0xff) /* Receive status */
#define DBRI_RD_CNT(v) ((v>>16)&0x1fff) /* Number of valid bytes in the buffer */
#endif /* _DBRI_H_ */
......@@ -1758,7 +1758,7 @@ int init_module(void) {
void cleanup_module( void)
{
struct gendisk * prev_sdgd;
struct gendisk ** prev_sdgd_link;
struct gendisk * sdgd;
int i;
int removed = 0;
......@@ -1778,16 +1778,20 @@ void cleanup_module( void)
scsi_init_free((char *) sd_hardsizes, sd_template.dev_max * sizeof(int));
scsi_init_free((char *) sd,
(sd_template.dev_max << 4) * sizeof(struct hd_struct));
/*
* Now remove sd_gendisks from the linked list
*/
for (sdgd = gendisk_head; sdgd; sdgd = sdgd->next)
{
if (sdgd->next >= sd_gendisks && sdgd->next <= LAST_SD_GENDISK.max_nr)
removed++, sdgd->next = sdgd->next->next;
else sdgd = sdgd->next;
prev_sdgd_link = &gendisk_head;
while ((sdgd = *prev_sdgd_link) != NULL) {
if (sdgd >= sd_gendisks && sdgd <= &LAST_SD_GENDISK) {
removed++;
*prev_sdgd_link = sdgd->next;
continue;
}
prev_sdgd_link = &sdgd->next;
}
if (removed != N_USED_SD_MAJORS)
printk("%s %d sd_gendisks in disk chain",
removed > N_USED_SD_MAJORS ? "total" : "just", removed);
......
/* $Id: openpromfs.c,v 1.31 1998/08/26 10:32:19 davem Exp $
/* $Id: openpromfs.c,v 1.32 1998/11/18 06:15:20 davem Exp $
* openpromfs.c: /proc/openprom handling routines
*
* Copyright (C) 1996-1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
......@@ -822,6 +822,10 @@ static int openpromfs_lookup(struct inode * dir, struct dentry *dentry)
inode->i_rdev = d->rdev;
break;
}
inode->i_gid = 0;
inode->i_uid = 0;
d_add(dentry, inode);
return 0;
}
......
......@@ -25,7 +25,7 @@
{ LONG_MAX, LONG_MAX }, \
{ LONG_MAX, LONG_MAX }, \
{ LONG_MAX, LONG_MAX }, \
{ _STK_LIM, _STK_LIM }, \
{ _STK_LIM, LONG_MAX }, \
{ 0, LONG_MAX }, \
{ LONG_MAX, LONG_MAX }, \
{ MAX_TASKS_PER_USER, MAX_TASKS_PER_USER }, \
......
......@@ -88,6 +88,7 @@ typedef struct audio_info {
#define AUDIO_ENCODING_ULAW (1) /* u-law encoding */
#define AUDIO_ENCODING_ALAW (2) /* A-law encoding */
#define AUDIO_ENCODING_LINEAR (3) /* Linear PCM encoding */
#define AUDIO_ENCODING_FLOAT (4) /* IEEE float (-1. <-> +1.) */
#define AUDIO_ENCODING_DVI (104) /* DVI ADPCM */
#define AUDIO_ENCODING_LINEAR8 (105) /* 8 bit UNSIGNED */
#define AUDIO_ENCODING_LINEARLE (106) /* Linear PCM LE encoding */
......@@ -139,8 +140,7 @@ typedef struct audio_info {
#define AUDIO_LINE_IN 0x02 /* input from line in */
#define AUDIO_CD 0x04 /* input from on-board CD inputs */
#define AUDIO_INTERNAL_CD_IN AUDIO_CD /* input from internal CDROM */
/* Supposedly an undocumented feature of the 4231 */
#define AUDIO_ANALOG_LOOPBACK 0x40
#define AUDIO_ANALOG_LOOPBACK 0x40 /* input from output */
/*
......@@ -227,22 +227,10 @@ typedef struct audio_device {
* if the hardware supports this. The argument is TRUE to set loopback,
* FALSE to reset to normal operation. If the hardware does not support
* internal loopback, the ioctl should fail with EINVAL.
* Causes ADC data to be digitally mixed in and sent to the DAC.
*/
#define AUDIO_DIAG_LOOPBACK _IOW('A', 101, int)
#ifdef notneeded
/*
* Structure sent up as a M_PROTO message on trace streams
*/
typedef struct audtrace_hdr audtrace_hdr_t;
struct audtrace_hdr {
unsigned int seq; /* Sequence number (per-aud_stream) */
int type; /* device-dependent */
struct timeval timestamp;
char _f[8]; /* filler */
};
#endif
/*
* Linux kernel internal implementation.
*/
......@@ -257,48 +245,72 @@ struct audtrace_hdr {
#define SDF_OPEN_WRITE 0x00000001
#define SDF_OPEN_READ 0x00000002
struct sparcaudio_ringbuffer
{
__u8 *rb_start, *rb_end; /* start, end of this memory buffer */
__u8 *rb_in, *rb_out; /* input, output pointers */
int rb_fragsize; /* size of an audio frag */
int rb_numfrags; /* number of frags */
int rb_count, rb_hiwat, rb_lowat; /* bytes in use, hi/lo wat points */
int rb_bufsize; /* total size of buffer */
};
struct sparcaudio_driver
{
const char * name;
struct sparcaudio_operations *ops;
void *private;
unsigned long flags;
struct strevent *sd_siglist;
/* duplex: 0=simplex, 1=duplex, 2=loop */
int sd_sigflags, duplex;
/* Which audio device are we? */
int index;
/* This device */
struct linux_sbus_device *dev;
/* Processes blocked on open() sit here. */
struct wait_queue *open_wait;
/* Task queue for this driver's bottom half. */
struct tq_struct tqueue;
/* Start of ring buffer support */
__u8 *input_buffer, *output_buffer;
/* Support for a circular queue of output buffers. */
__u8 **output_buffers;
size_t *output_sizes, output_size;
int num_output_buffers, output_front, output_rear;
int output_count, output_active, playing_count;
size_t *output_sizes, output_size, output_buffer_size;
int num_output_buffers, output_front, output_rear, output_offset;
int output_count, output_active, playing_count, output_eof;
struct wait_queue *output_write_wait, *output_drain_wait;
char *output_notify;
/* Support for a circular queue of input buffers. */
__u8 **input_buffers;
int input_offset;
int num_input_buffers, input_front, input_rear;
size_t *input_sizes, input_size, input_buffer_size;
int num_input_buffers, input_front, input_rear, input_offset;
int input_count, input_active, recording_count;
struct wait_queue *input_read_wait;
/* Hack to make it look like we support variable size buffers. */
int buffer_size;
};
struct sparcaudio_operations
{
int (*open)(struct inode *, struct file *, struct sparcaudio_driver *);
void (*release)(struct inode *, struct file *, struct
sparcaudio_driver *);
int (*ioctl)(struct inode *, struct file *, unsigned int,
unsigned long, struct sparcaudio_driver *);
void (*release)(struct inode *, struct file *, struct sparcaudio_driver *);
int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long,
struct sparcaudio_driver *);
/* Ask driver to begin playing a buffer. */
void (*start_output)(struct sparcaudio_driver *, __u8 *,
unsigned long);
void (*start_output)(struct sparcaudio_driver *, __u8 *, unsigned long);
/* Ask driver to stop playing a buffer. */
void (*stop_output)(struct sparcaudio_driver *);
......@@ -382,35 +394,125 @@ struct sparcaudio_operations
/* Get and set output mute */
int (*set_output_muted)(struct sparcaudio_driver *, int);
int (*get_output_muted)(struct sparcaudio_driver *);
/* Get and set output pause */
int (*set_output_pause)(struct sparcaudio_driver *, int);
int (*get_output_pause)(struct sparcaudio_driver *);
/* Get and set input pause */
int (*set_input_pause)(struct sparcaudio_driver *, int);
int (*get_input_pause)(struct sparcaudio_driver *);
/* Get and set output samples */
int (*set_output_samples)(struct sparcaudio_driver *, int);
int (*get_output_samples)(struct sparcaudio_driver *);
/* Get and set input samples */
int (*set_input_samples)(struct sparcaudio_driver *, int);
int (*get_input_samples)(struct sparcaudio_driver *);
/* Get and set output error */
int (*set_output_error)(struct sparcaudio_driver *, int);
int (*get_output_error)(struct sparcaudio_driver *);
/* Get and set input error */
int (*set_input_error)(struct sparcaudio_driver *, int);
int (*get_input_error)(struct sparcaudio_driver *);
/* Get supported encodings */
int (*get_formats)(struct sparcaudio_driver *);
};
extern int register_sparcaudio_driver(struct sparcaudio_driver *);
extern int unregister_sparcaudio_driver(struct sparcaudio_driver *);
extern int register_sparcaudio_driver(struct sparcaudio_driver *, int);
extern int unregister_sparcaudio_driver(struct sparcaudio_driver *, int);
extern void sparcaudio_output_done(struct sparcaudio_driver *, int);
extern void sparcaudio_input_done(struct sparcaudio_driver *);
extern void sparcaudio_input_done(struct sparcaudio_driver *, int);
extern int sparcaudio_init(void);
extern int amd7930_init(void);
extern int cs4231_init(void);
#endif
/* Mixer helper ioctls */
#define right(a) (((a >> 8) & 0xff) % 101)
#define left(a) ((a & 0xff) % 101)
/* Macros to convert between mixer stereo volumes and gain (mono) */
#define s_to_m(a) (((((a) >> 8) & 0x7f) + ((a) & 0x7f)) / 2)
#define m_to_s(a) (((a) << 8) + (a))
#define s_to_m(a) ((((left(a) + right(a)) * 255) / 200) % 256)
#define m_to_s(a) ((a * 100 / 255) + ((a * 100 / 255) << 8))
/* convert mixer stereo volume to balance */
#define s_to_b(a) (AUDIO_RIGHT_BALANCE * ((((a) >> 8) & 0xff) / (((((a) >> 8) & 0xff) + ((a) & 0xff)) / 2)))
#define s_to_b(a) (s_to_g(a) == 0) ? 32 : ((left(a) * AUDIO_RIGHT_BALANCE / (left(a) + right(a))))
/* convert mixer stereo volume to audio gain */
#define s_to_g(a) (((((a) >> 8) & 0xff) + ((a) & 0xff)) / 2)
#define s_to_g(a) ((((right(a) + left(a)) * 255) / 200) % 256)
/* convert gain a and balance b to mixer volume */
#define b_to_s(a,b) ((a * (b / AUDIO_RIGHT_BALANCE) << 8) + (a * (1 - (b / AUDIO_RIGHT_BALANCE))))
#define b_to_s(a,b) (((((b * a * 200) / (AUDIO_RIGHT_BALANCE * 255)) % 100) << 8) + ((((AUDIO_RIGHT_BALANCE - b) * a * 200) / (AUDIO_RIGHT_BALANCE * 255)) % 100))
/* Device minor numbers */
#define SPARCAUDIO_MIXER_MINOR 0
#define SPARCAUDIO_DSP16_MINOR 1
/* No sequencer (1) */
/* No midi (2) */
#define SPARCAUDIO_DSP_MINOR 3
#define SPARCAUDIO_AUDIO_MINOR 4
#define SPARCAUDIO_AUDIOCTL_MINOR 5
#define SPARCAUDIO_DSP16_MINOR 5
#define SPARCAUDIO_STATUS_MINOR 6
#define SPARCAUDIO_AUDIOCTL_MINOR 7
/* No sequencer l2 (8) */
/* No sound processor (9) */
/* allocate 2^SPARCAUDIO_DEVICE_SHIFT minors per audio device */
#define SPARCAUDIO_DEVICE_SHIFT 4
/* With the coming of dummy devices this should perhaps be as high as 5? */
#define SPARCAUDIO_MAX_DEVICES 3
/* Streams crap for realaudio */
typedef
struct strevent {
struct strevent *se_next; /* next event for this stream or NULL*/
struct strevent *se_prev; /* previous event for this stream or last
* event if this is the first one*/
pid_t se_pid; /* process to be signaled */
short se_evs; /* events wanted */
} strevent_t;
typedef
struct stdata
{
struct stdata *sd_next ; /* all stdatas are linked together */
struct stdata *sd_prev ;
struct strevent *sd_siglist; /* processes to be sent SIGPOLL */
int sd_sigflags; /* logical OR of all siglist events */
} stdata_t;
#define I_NREAD _IOR('S',01, int)
#define I_NREAD_SOLARIS (('S'<<8)|1)
#define I_FLUSH _IO('S',05)
#define I_FLUSH_SOLARIS (('S'<<8)|5)
#define FLUSHR 1 /* flush read queue */
#define FLUSHW 2 /* flush write queue */
#define FLUSHRW 3 /* flush both queues */
#define I_SETSIG _IO('S',011)
#define I_SETSIG_SOLARIS (('S'<<8)|11)
#define S_INPUT 0x01
#define S_HIPRI 0x02
#define S_OUTPUT 0x04
#define S_MSG 0x08
#define S_ERROR 0x0010
#define S_HANGUP 0x0020
#define S_RDNORM 0x0040
#define S_WRNORM S_OUTPUT
#define S_RDBAND 0x0080
#define S_WRBAND 0x0100
#define S_BANDURG 0x0200
#define S_ALL 0x03FF
#define I_GETSIG _IOR('S',012,int)
#define I_GETSIG_SOLARIS (('S'<<8)|12)
#endif
......@@ -88,6 +88,7 @@ typedef struct audio_info {
#define AUDIO_ENCODING_ULAW (1) /* u-law encoding */
#define AUDIO_ENCODING_ALAW (2) /* A-law encoding */
#define AUDIO_ENCODING_LINEAR (3) /* Linear PCM encoding */
#define AUDIO_ENCODING_FLOAT (4) /* IEEE float (-1. <-> +1.) */
#define AUDIO_ENCODING_DVI (104) /* DVI ADPCM */
#define AUDIO_ENCODING_LINEAR8 (105) /* 8 bit UNSIGNED */
#define AUDIO_ENCODING_LINEARLE (106) /* Linear PCM LE encoding */
......@@ -139,8 +140,7 @@ typedef struct audio_info {
#define AUDIO_LINE_IN 0x02 /* input from line in */
#define AUDIO_CD 0x04 /* input from on-board CD inputs */
#define AUDIO_INTERNAL_CD_IN AUDIO_CD /* input from internal CDROM */
/* Supposedly an undocumented feature of the 4231 */
#define AUDIO_ANALOG_LOOPBACK 0x40
#define AUDIO_ANALOG_LOOPBACK 0x40 /* input from output */
/*
......@@ -227,22 +227,10 @@ typedef struct audio_device {
* if the hardware supports this. The argument is TRUE to set loopback,
* FALSE to reset to normal operation. If the hardware does not support
* internal loopback, the ioctl should fail with EINVAL.
* Causes ADC data to be digitally mixed in and sent to the DAC.
*/
#define AUDIO_DIAG_LOOPBACK _IOW('A', 101, int)
#ifdef notneeded
/*
* Structure sent up as a M_PROTO message on trace streams
*/
typedef struct audtrace_hdr audtrace_hdr_t;
struct audtrace_hdr {
unsigned int seq; /* Sequence number (per-aud_stream) */
int type; /* device-dependent */
struct timeval timestamp;
char _f[8]; /* filler */
};
#endif
/*
* Linux kernel internal implementation.
*/
......@@ -257,15 +245,34 @@ struct audtrace_hdr {
#define SDF_OPEN_WRITE 0x00000001
#define SDF_OPEN_READ 0x00000002
struct sparcaudio_ringbuffer
{
__u8 *rb_start, *rb_end; /* start, end of this memory buffer */
__u8 *rb_in, *rb_out; /* input, output pointers */
int rb_fragsize; /* size of an audio frag */
int rb_numfrags; /* number of frags */
int rb_count, rb_hiwat, rb_lowat; /* bytes in use, hi/lo wat points */
int rb_bufsize; /* total size of buffer */
};
struct sparcaudio_driver
{
const char * name;
struct sparcaudio_operations *ops;
void *private;
unsigned long flags;
struct strevent *sd_siglist;
/* duplex: 0=simplex, 1=duplex, 2=loop */
int sd_sigflags, duplex;
/* Which audio device are we? */
int index;
/* This device */
struct linux_sbus_device *dev;
/* This device */
struct linux_sbus_device *dev;
/* Processes blocked on open() sit here. */
struct wait_queue *open_wait;
......@@ -273,32 +280,37 @@ struct sparcaudio_driver
/* Task queue for this driver's bottom half. */
struct tq_struct tqueue;
/* Start of ring buffer support */
__u8 *input_buffer, *output_buffer;
/* Support for a circular queue of output buffers. */
__u8 **output_buffers;
size_t *output_sizes, output_size;
int num_output_buffers, output_front, output_rear;
int output_count, output_active, playing_count;
size_t *output_sizes, output_size, output_buffer_size;
int num_output_buffers, output_front, output_rear, output_offset;
int output_count, output_active, playing_count, output_eof;
struct wait_queue *output_write_wait, *output_drain_wait;
char *output_notify;
/* Support for a circular queue of input buffers. */
__u8 **input_buffers;
int input_offset;
int num_input_buffers, input_front, input_rear;
size_t *input_sizes, input_size, input_buffer_size;
int num_input_buffers, input_front, input_rear, input_offset;
int input_count, input_active, recording_count;
struct wait_queue *input_read_wait;
/* Hack to make it look like we support variable size buffers. */
int buffer_size;
};
struct sparcaudio_operations
{
int (*open)(struct inode *, struct file *, struct sparcaudio_driver *);
void (*release)(struct inode *, struct file *,
struct sparcaudio_driver *);
int (*ioctl)(struct inode *, struct file *, unsigned int,
unsigned long, struct sparcaudio_driver *);
void (*release)(struct inode *, struct file *, struct sparcaudio_driver *);
int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long,
struct sparcaudio_driver *);
/* Ask driver to begin playing a buffer. */
void (*start_output)(struct sparcaudio_driver *, __u8 *,
unsigned long);
void (*start_output)(struct sparcaudio_driver *, __u8 *, unsigned long);
/* Ask driver to stop playing a buffer. */
void (*stop_output)(struct sparcaudio_driver *);
......@@ -382,35 +394,125 @@ struct sparcaudio_operations
/* Get and set output mute */
int (*set_output_muted)(struct sparcaudio_driver *, int);
int (*get_output_muted)(struct sparcaudio_driver *);
/* Get and set output pause */
int (*set_output_pause)(struct sparcaudio_driver *, int);
int (*get_output_pause)(struct sparcaudio_driver *);
/* Get and set input pause */
int (*set_input_pause)(struct sparcaudio_driver *, int);
int (*get_input_pause)(struct sparcaudio_driver *);
/* Get and set output samples */
int (*set_output_samples)(struct sparcaudio_driver *, int);
int (*get_output_samples)(struct sparcaudio_driver *);
/* Get and set input samples */
int (*set_input_samples)(struct sparcaudio_driver *, int);
int (*get_input_samples)(struct sparcaudio_driver *);
/* Get and set output error */
int (*set_output_error)(struct sparcaudio_driver *, int);
int (*get_output_error)(struct sparcaudio_driver *);
/* Get and set input error */
int (*set_input_error)(struct sparcaudio_driver *, int);
int (*get_input_error)(struct sparcaudio_driver *);
/* Get supported encodings */
int (*get_formats)(struct sparcaudio_driver *);
};
extern int register_sparcaudio_driver(struct sparcaudio_driver *);
extern int unregister_sparcaudio_driver(struct sparcaudio_driver *);
extern int register_sparcaudio_driver(struct sparcaudio_driver *, int);
extern int unregister_sparcaudio_driver(struct sparcaudio_driver *, int);
extern void sparcaudio_output_done(struct sparcaudio_driver *, int);
extern void sparcaudio_input_done(struct sparcaudio_driver *);
extern void sparcaudio_input_done(struct sparcaudio_driver *, int);
extern int sparcaudio_init(void);
extern int amd7930_init(void);
extern int cs4231_init(void);
#endif
/* Mixer helper ioctls */
#define right(a) (((a >> 8) & 0xff) % 101)
#define left(a) ((a & 0xff) % 101)
/* Macros to convert between mixer stereo volumes and gain (mono) */
#define s_to_m(a) (((((a) >> 8) & 0x7f) + ((a) & 0x7f)) / 2)
#define m_to_s(a) (((a) << 8) + (a))
#define s_to_m(a) ((((left(a) + right(a)) * 255) / 200) % 256)
#define m_to_s(a) ((a * 100 / 255) + ((a * 100 / 255) << 8))
/* convert mixer stereo volume to balance */
#define s_to_b(a) (AUDIO_RIGHT_BALANCE * ((((a) >> 8) & 0xff) / (((((a) >> 8) & 0xff) + ((a) & 0xff)) / 2)))
#define s_to_b(a) (s_to_g(a) == 0) ? 32 : ((left(a) * AUDIO_RIGHT_BALANCE / (left(a) + right(a))))
/* convert mixer stereo volume to audio gain */
#define s_to_g(a) (((((a) >> 8) & 0xff) + ((a) & 0xff)) / 2)
#define s_to_g(a) ((((right(a) + left(a)) * 255) / 200) % 256)
/* convert gain a and balance b to mixer volume */
#define b_to_s(a,b) ((a * (b / AUDIO_RIGHT_BALANCE) << 8) + (a * (1 - (b / AUDIO_RIGHT_BALANCE))))
#define b_to_s(a,b) (((((b * a * 200) / (AUDIO_RIGHT_BALANCE * 255)) % 100) << 8) + ((((AUDIO_RIGHT_BALANCE - b) * a * 200) / (AUDIO_RIGHT_BALANCE * 255)) % 100))
/* Device minor numbers */
#define SPARCAUDIO_MIXER_MINOR 0
#define SPARCAUDIO_DSP16_MINOR 1
/* No sequencer (1) */
/* No midi (2) */
#define SPARCAUDIO_DSP_MINOR 3
#define SPARCAUDIO_AUDIO_MINOR 4
#define SPARCAUDIO_AUDIOCTL_MINOR 5
#define SPARCAUDIO_DSP16_MINOR 5
#define SPARCAUDIO_STATUS_MINOR 6
#define SPARCAUDIO_AUDIOCTL_MINOR 7
/* No sequencer l2 (8) */
/* No sound processor (9) */
/* allocate 2^SPARCAUDIO_DEVICE_SHIFT minors per audio device */
#define SPARCAUDIO_DEVICE_SHIFT 4
/* With the coming of dummy devices this should perhaps be as high as 5? */
#define SPARCAUDIO_MAX_DEVICES 3
/* Streams crap for realaudio */
typedef
struct strevent {
struct strevent *se_next; /* next event for this stream or NULL*/
struct strevent *se_prev; /* previous event for this stream or last
* event if this is the first one*/
pid_t se_pid; /* process to be signaled */
short se_evs; /* events wanted */
} strevent_t;
typedef
struct stdata
{
struct stdata *sd_next ; /* all stdatas are linked together */
struct stdata *sd_prev ;
struct strevent *sd_siglist; /* processes to be sent SIGPOLL */
int sd_sigflags; /* logical OR of all siglist events */
} stdata_t;
#define I_NREAD _IOR('S',01, int)
#define I_NREAD_SOLARIS (('S'<<8)|1)
#define I_FLUSH _IO('S',05)
#define I_FLUSH_SOLARIS (('S'<<8)|5)
#define FLUSHR 1 /* flush read queue */
#define FLUSHW 2 /* flush write queue */
#define FLUSHRW 3 /* flush both queues */
#define I_SETSIG _IO('S',011)
#define I_SETSIG_SOLARIS (('S'<<8)|11)
#define S_INPUT 0x01
#define S_HIPRI 0x02
#define S_OUTPUT 0x04
#define S_MSG 0x08
#define S_ERROR 0x0010
#define S_HANGUP 0x0020
#define S_RDNORM 0x0040
#define S_WRNORM S_OUTPUT
#define S_RDBAND 0x0080
#define S_WRBAND 0x0100
#define S_BANDURG 0x0200
#define S_ALL 0x03FF
#define I_GETSIG _IOR('S',012,int)
#define I_GETSIG_SOLARIS (('S'<<8)|12)
#endif
......@@ -117,7 +117,7 @@ typedef struct page {
unsigned long offset;
struct page *next_hash;
atomic_t count;
unsigned int age;
unsigned int unused;
unsigned long flags; /* atomic flags, some possibly updated asynchronously */
struct wait_queue *wait;
struct page **pprev_hash;
......@@ -226,8 +226,6 @@ typedef struct page {
* For choosing which pages to swap out, inode pages carry a
* page->referenced bit, which is set any time the system accesses
* that page through the (inode,offset) hash table.
* There is also the page->age counter, which implements a linear
* decay (why not an exponential decay?), see swapctl.h.
*/
extern mem_map_t * mem_map;
......
......@@ -83,7 +83,6 @@ static inline void remove_page_from_hash_queue(struct page * page)
static inline void __add_page_to_hash_queue(struct page * page, struct page **p)
{
page_cache_size++;
page->age = PAGE_AGE_VALUE;
if((page->next_hash = *p) != NULL)
(*p)->pprev_hash = &page->next_hash;
*p = page;
......
......@@ -94,32 +94,6 @@ static inline int AGE_CLUSTER_SIZE(int resources)
return n;
}
static inline void touch_page(struct page *page)
{
if (page->age < (MAX_PAGE_AGE - PAGE_ADVANCE))
page->age += PAGE_ADVANCE;
else
page->age = MAX_PAGE_AGE;
}
static inline void age_page(struct page *page)
{
if (page->age > PAGE_DECLINE)
page->age -= PAGE_DECLINE;
else
page->age = 0;
}
static inline int age_of(unsigned long addr)
{
return mem_map[MAP_NR(addr)].age;
}
static inline void set_page_new(unsigned long addr)
{
mem_map[MAP_NR(addr)].age = PAGE_INITIAL_AGE;
}
#endif /* __KERNEL */
#endif /* _LINUX_SWAPCTL_H */
......@@ -273,6 +273,7 @@ static int do_acct_process(long exitcode, struct file *file)
struct acct ac;
mm_segment_t fs;
unsigned long vsize;
struct inode *inode;
/*
* First check to see if there is enough free_space to continue
......@@ -336,8 +337,11 @@ static int do_acct_process(long exitcode, struct file *file)
*/
fs = get_fs();
set_fs(KERNEL_DS);
inode = file->f_dentry->d_inode;
down(&inode->i_sem);
file->f_op->write(file, (char *)&ac,
sizeof(struct acct), &file->f_pos);
up(&inode->i_sem);
set_fs(fs);
fput(file);
return 0;
......
......@@ -273,9 +273,9 @@ printk("SIG queue (%s:%d): %d ", t->comm, t->pid, sig);
goto out_nolock;
/* The null signal is a permissions and process existance probe.
No signal is actually delivered. */
No signal is actually delivered. Same goes for zombies. */
ret = 0;
if (!sig)
if (!sig || !t->sig)
goto out_nolock;
spin_lock_irqsave(&t->sigmask_lock, flags);
......
......@@ -290,10 +290,9 @@ int vsprintf(char *buf, const char *fmt, va_list args)
if (qualifier == 'l')
num = va_arg(args, unsigned long);
else if (qualifier == 'h') {
num = (unsigned short) va_arg(args, int);
if (flags & SIGN)
num = va_arg(args, short);
else
num = va_arg(args, unsigned short);
num = (short) num;
} else if (flags & SIGN)
num = va_arg(args, int);
else
......
......@@ -172,10 +172,8 @@ static inline int shrink_one_page(struct page *page, int gfp_mask)
delete_from_swap_cache(page);
return 1;
}
if (test_and_clear_bit(PG_referenced, &page->flags)) {
touch_page(page);
if (test_and_clear_bit(PG_referenced, &page->flags))
break;
}
if (pgcache_under_min())
break;
remove_inode_page(page);
......@@ -209,8 +207,8 @@ int shrink_mmap(int priority, int gfp_mask)
struct page * page;
int count_max, count_min;
count_max = (limit<<2) >> (priority>>1);
count_min = (limit<<2) >> (priority);
count_max = (limit<<1) >> (priority>>1);
count_min = (limit<<1) >> (priority);
page = mem_map + clock;
do {
......
......@@ -231,7 +231,6 @@ do { unsigned long size = 1 << high; \
map += size; \
} \
atomic_set(&map->count, 1); \
map->age = PAGE_INITIAL_AGE; \
} while (0)
unsigned long __get_free_pages(int gfp_mask, unsigned long order)
......
/* linux/net/inet/arp.c
*
* Version: $Id: arp.c,v 1.74 1998/11/01 22:15:06 davem Exp $
* Version: $Id: arp.c,v 1.75 1998/11/16 04:51:56 davem Exp $
*
* Copyright (C) 1994 by Florian La Roche
*
......
......@@ -5,7 +5,7 @@
*
* Implementation of the Transmission Control Protocol(TCP).
*
* Version: $Id: tcp_input.c,v 1.140 1998/11/12 06:45:15 davem Exp $
* Version: $Id: tcp_input.c,v 1.141 1998/11/18 02:12:07 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
......@@ -1978,8 +1978,27 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
int queued = 0;
/* state == CLOSED, hash lookup always fails, so no worries. -DaveM */
switch (sk->state) {
case TCP_CLOSE:
/* When state == CLOSED, hash lookup always fails.
*
* But, there is a back door, the backlog queue.
* If we have a sequence of packets in the backlog
* during __release_sock() which have a sequence such
* that:
* packet X causes entry to TCP_CLOSE state
* ...
* packet X + N has FIN bit set
*
* We report a (luckily) harmless error in this case.
* The issue is that backlog queue processing bypasses
* any hash lookups (we know which socket packets are for).
* The correct behavior here is what 2.0.x did, since
* a TCP_CLOSE socket does not exist. Drop the frame
* and send a RST back to the other end.
*/
return 1;
case TCP_LISTEN:
/* These use the socket TOS..
* might want to be the received TOS
......
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