diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-01-04 16:03:48 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-01-04 16:03:48 +0000 |
commit | 78c388aed2b7184182c08428db1de6c872d815f5 (patch) | |
tree | 4b2003b1b4ceb241a17faa995da8dd1004bb8e45 /drivers/sbus/audio | |
parent | eb7a5bf93aaa4be1d7c6181100ab7639e74d67f7 (diff) |
Merge with Linux 2.1.131 and more MIPS goodies.
(Did I mention that CVS is buggy ...)
Diffstat (limited to 'drivers/sbus/audio')
-rw-r--r-- | drivers/sbus/audio/Config.in | 2 | ||||
-rw-r--r-- | drivers/sbus/audio/Makefile | 38 | ||||
-rw-r--r-- | drivers/sbus/audio/amd7930.c | 359 | ||||
-rw-r--r-- | drivers/sbus/audio/amd7930.h | 21 | ||||
-rw-r--r-- | drivers/sbus/audio/audio.c | 2859 | ||||
-rw-r--r-- | drivers/sbus/audio/cs4215.h | 32 | ||||
-rw-r--r-- | drivers/sbus/audio/cs4231.c | 523 | ||||
-rw-r--r-- | drivers/sbus/audio/cs4231.h | 5 | ||||
-rw-r--r-- | drivers/sbus/audio/dbri.c | 977 | ||||
-rw-r--r-- | drivers/sbus/audio/dbri.h | 37 |
10 files changed, 3617 insertions, 1236 deletions
diff --git a/drivers/sbus/audio/Config.in b/drivers/sbus/audio/Config.in index 71a75c763..34f8124cd 100644 --- a/drivers/sbus/audio/Config.in +++ b/drivers/sbus/audio/Config.in @@ -9,4 +9,6 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Audio support (EXPERIMENTAL)' CONFIG_SPARCAUDIO 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 diff --git a/drivers/sbus/audio/Makefile b/drivers/sbus/audio/Makefile index 3dcb06f82..cf38aa1d8 100644 --- a/drivers/sbus/audio/Makefile +++ b/drivers/sbus/audio/Makefile @@ -14,39 +14,61 @@ O_TARGET := sparcaudio.o O_OBJS := M_OBJS := +M := +MM := 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 -ifdef SBUS_AUDIO +ifeq ($(CONFIG_SPARCAUDIO_DBRI),y) +M=y +OX_OBJS += dbri.o +else + ifeq ($(CONFIG_SPARCAUDIO_DBRI),m) + MM=y + MX_OBJS += dbri.o + endif +endif + +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 diff --git a/drivers/sbus/audio/amd7930.c b/drivers/sbus/audio/amd7930.c index fb87bbe6f..4a0064f55 100644 --- a/drivers/sbus/audio/amd7930.c +++ b/drivers/sbus/audio/amd7930.c @@ -13,6 +13,74 @@ * * Thanks to the AMD engineer who was able to get us the AMD79C30 * databook which has all the programming information and gain tables. + * + * Advanced Micro Devices' Am79C30A is an ISDN/audio chip used in the + * SparcStation 1+. The chip provides microphone and speaker interfaces + * which provide mono-channel audio at 8K samples per second via either + * 8-bit A-law or 8-bit mu-law encoding. Also, the chip features an + * ISDN BRI Line Interface Unit (LIU), I.430 S/T physical interface, + * which performs basic D channel LAPD processing and provides raw + * B channel data. The digital audio channel, the two ISDN B channels, + * and two 64 Kbps channels to the microprocessor are all interconnected + * via a multiplexer. + * + * This driver interfaces to the Linux HiSax ISDN driver, which performs + * all high-level Q.921 and Q.931 ISDN functions. The file is not + * itself a hardware driver; rather it uses functions exported by + * the AMD7930 driver in the sparcaudio subsystem (drivers/sbus/audio), + * allowing the chip to be simultaneously used for both audio and ISDN data. + * The hardware driver does _no_ buffering, but provides several callbacks + * which are called during interrupt service and should therefore run quickly. + * + * D channel transmission is performed by passing the hardware driver the + * address and size of an skb's data area, then waiting for a callback + * to signal successful transmission of the packet. A task is then + * queued to notify the HiSax driver that another packet may be transmitted. + * + * D channel reception is quite simple, mainly because of: + * 1) the slow speed of the D channel - 16 kbps, and + * 2) the presence of an 8- or 32-byte (depending on chip version) FIFO + * to buffer the D channel data on the chip + * Worst case scenario of back-to-back packets with the 8 byte buffer + * at 16 kbps yields an service time of 4 ms - long enough to preclude + * the need for fancy buffering. We queue a background task that copies + * data out of the receive buffer into an skb, and the hardware driver + * simply does nothing until we're done with the receive buffer and + * reset it for a new packet. + * + * B channel processing is more complex, because of: + * 1) the faster speed - 64 kbps, + * 2) the lack of any on-chip buffering (it interrupts for every byte), and + * 3) the lack of any chip support for HDLC encapsulation + * + * The HiSax driver can put each B channel into one of three modes - + * L1_MODE_NULL (channel disabled), L1_MODE_TRANS (transparent data relay), + * and L1_MODE_HDLC (HDLC encapsulation by low-level driver). + * L1_MODE_HDLC is the most common, used for almost all "pure" digital + * data sessions. L1_MODE_TRANS is used for ISDN audio. + * + * HDLC B channel transmission is performed via a large buffer into + * which the skb is copied while performing HDLC bit-stuffing. A CRC + * is computed and attached to the end of the buffer, which is then + * passed to the low-level routines for raw transmission. Once + * transmission is complete, the hardware driver is set to enter HDLC + * idle by successive transmission of mark (all 1) bytes, waiting for + * the ISDN driver to prepare another packet for transmission and + * deliver it. + * + * HDLC B channel reception is performed via an X-byte ring buffer + * divided into N sections of X/N bytes each. Defaults: X=256 bytes, N=4. + * As the hardware driver notifies us that each section is full, we + * hand it the next section and schedule a background task to peruse + * the received section, bit-by-bit, with an HDLC decoder. As + * packets are detected, they are copied into a large buffer while + * decoding HDLC bit-stuffing. The ending CRC is verified, and if + * it is correct, we alloc a new skb of the correct length (which we + * now know), copy the packet into it, and hand it to the upper layers. + * Optimization: for large packets, we hand the buffer (which also + * happens to be an skb) directly to the upper layer after an skb_trim, + * and alloc a new large buffer for future packets, thus avoiding a copy. + * Then we return to HDLC processing; state is saved between calls. */ #include <linux/module.h> @@ -22,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> @@ -32,6 +102,12 @@ #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 static struct sparcaudio_driver drivers[MAX_DRIVERS]; @@ -274,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); } @@ -296,9 +366,6 @@ static void amd7930_update_map(struct sparcaudio_driver *drv) * driver, since D.output_callback_arg is assumed to be a certain struct ptr */ -#include "../../isdn/hisax/hisax.h" -#include "../../isdn/hisax/isdnl1.h" - #ifdef L2FRAME_DEBUG inline void debug_info(struct amd7930_info *info, char c) { @@ -453,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); } } } @@ -498,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; } @@ -640,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; @@ -681,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) { @@ -697,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 */ @@ -724,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 */ @@ -741,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; } @@ -909,8 +1072,8 @@ static int amd7930_get_monitor_volume(struct sparcaudio_driver *drv) * */ - -int amd7930_get_irqnum(int dev) +#if defined (AMD79C30_ISDN) || defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff +static int amd7930_get_irqnum(int dev) { struct amd7930_info *info; @@ -923,7 +1086,7 @@ int amd7930_get_irqnum(int dev) return info->irq; } -int amd7930_get_liu_state(int dev) +static int amd7930_get_liu_state(int dev) { struct amd7930_info *info; @@ -936,7 +1099,7 @@ int amd7930_get_liu_state(int dev) return info->liu_state; } -void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg) +static void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg) { struct amd7930_info *info; register unsigned long flags; @@ -971,7 +1134,7 @@ void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg) restore_flags(flags); } -void amd7930_liu_activate(int dev, int priority) +static void amd7930_liu_activate(int dev, int priority) { struct amd7930_info *info; register unsigned long flags; @@ -1003,7 +1166,7 @@ void amd7930_liu_activate(int dev, int priority) restore_flags(flags); } -void amd7930_liu_deactivate(int dev) +static void amd7930_liu_deactivate(int dev) { struct amd7930_info *info; register unsigned long flags; @@ -1024,8 +1187,8 @@ void amd7930_liu_deactivate(int dev) restore_flags(flags); } -void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count, - void (*callback)(void *, int), void *callback_arg) +static void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count, + void (*callback)(void *, int), void *callback_arg) { struct amd7930_info *info; register unsigned long flags; @@ -1069,9 +1232,9 @@ void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count, restore_flags(flags); } -void amd7930_drecv(int dev, __u8 *buffer, unsigned int size, - void (*callback)(void *, int, unsigned int), - void *callback_arg) +static void amd7930_drecv(int dev, __u8 *buffer, unsigned int size, + void (*callback)(void *, int, unsigned int), + void *callback_arg) { struct amd7930_info *info; register unsigned long flags; @@ -1115,7 +1278,8 @@ void amd7930_drecv(int dev, __u8 *buffer, unsigned int size, restore_flags(flags); } -int amd7930_bopen(int dev, int chan, u_char xmit_idle_char) +static int amd7930_bopen(int dev, unsigned int chan, + int mode, u_char xmit_idle_char) { struct amd7930_info *info; register unsigned long flags; @@ -1124,6 +1288,10 @@ int amd7930_bopen(int dev, int chan, u_char xmit_idle_char) return -1; } + if (mode == L1_MODE_HDLC) { + return -1; + } + info = (struct amd7930_info *) drivers[dev].private; save_and_cli(flags); @@ -1167,7 +1335,7 @@ int amd7930_bopen(int dev, int chan, u_char xmit_idle_char) return 0; } -void amd7930_bclose(int dev, int chan) +static void amd7930_bclose(int dev, unsigned int chan) { struct amd7930_info *info; register unsigned long flags; @@ -1202,8 +1370,9 @@ void amd7930_bclose(int dev, int chan) restore_flags(flags); } -void amd7930_bxmit(int dev, int chan, __u8 * buffer, unsigned long count, - void (*callback)(void *), void *callback_arg) +static void amd7930_bxmit(int dev, unsigned int chan, + __u8 * buffer, unsigned long count, + void (*callback)(void *, int), void *callback_arg) { struct amd7930_info *info; struct amd7930_channel *Bchan; @@ -1228,8 +1397,10 @@ void amd7930_bxmit(int dev, int chan, __u8 * buffer, unsigned long count, } } -void amd7930_brecv(int dev, int chan, __u8 * buffer, unsigned long size, - void (*callback)(void *), void *callback_arg) +static void amd7930_brecv(int dev, unsigned int chan, + __u8 * buffer, unsigned long size, + void (*callback)(void *, int, unsigned int), + void *callback_arg) { struct amd7930_info *info; struct amd7930_channel *Bchan; @@ -1254,17 +1425,21 @@ void amd7930_brecv(int dev, int chan, __u8 * buffer, unsigned long size, } } -EXPORT_SYMBOL(amd7930_get_irqnum); -EXPORT_SYMBOL(amd7930_get_liu_state); -EXPORT_SYMBOL(amd7930_liu_init); -EXPORT_SYMBOL(amd7930_liu_activate); -EXPORT_SYMBOL(amd7930_liu_deactivate); -EXPORT_SYMBOL(amd7930_dxmit); -EXPORT_SYMBOL(amd7930_drecv); -EXPORT_SYMBOL(amd7930_bopen); -EXPORT_SYMBOL(amd7930_bclose); -EXPORT_SYMBOL(amd7930_bxmit); -EXPORT_SYMBOL(amd7930_brecv); +struct foreign_interface amd7930_foreign_interface = { + amd7930_get_irqnum, + amd7930_get_liu_state, + amd7930_liu_init, + amd7930_liu_activate, + amd7930_liu_deactivate, + amd7930_dxmit, + amd7930_drecv, + amd7930_bopen, + amd7930_bclose, + amd7930_bxmit, + amd7930_brecv +}; +EXPORT_SYMBOL(amd7930_foreign_interface); +#endif /* @@ -1274,7 +1449,7 @@ EXPORT_SYMBOL(amd7930_brecv); static struct sparcaudio_operations amd7930_ops = { amd7930_open, amd7930_release, - NULL, /* amd7930_ioctl */ + amd7930_ioctl, amd7930_start_output, amd7930_stop_output, amd7930_start_input, @@ -1290,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. */ @@ -1334,10 +1522,7 @@ static int amd7930_attach(struct sparcaudio_driver *drv, int node, /* Point at the information structure and initialize it. */ drv->ops = &amd7930_ops; info = (struct amd7930_info *)drv->private; - info->Bb.output_ptr = info->Bb.input_ptr = NULL; - info->Bb.output_count = info->Bb.input_count = 0; - info->Bc.output_ptr = info->Bc.input_ptr = NULL; - info->Bc.output_count = info->Bc.input_count = 0; + memset(info, 0, sizeof(*info)); info->ints_on = 1; /* force disable below */ drv->dev = sdev; @@ -1376,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); @@ -1402,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); diff --git a/drivers/sbus/audio/amd7930.h b/drivers/sbus/audio/amd7930.h index 8df197061..76c4fd506 100644 --- a/drivers/sbus/audio/amd7930.h +++ b/drivers/sbus/audio/amd7930.h @@ -14,26 +14,7 @@ #define _AMD7930_H_ #include <linux/types.h> - -/* Exported ISDN functions */ - -int amd7930_get_irqnum(int dev); -int amd7930_get_liu_state(int dev); -void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg); -void amd7930_liu_activate(int dev, int priority); -void amd7930_liu_deactivate(int dev); -void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count, - void (*callback)(void *, int), void *callback_arg); -void amd7930_drecv(int dev, __u8 *buffer, unsigned int size, - void (*callback)(void *, int, unsigned int), - void *callback_arg); -int amd7930_bopen(int dev, int chan, u_char xmit_idle_char); -void amd7930_bclose(int dev, int chan); -void amd7930_bxmit(int dev, int chan, __u8 * buffer, unsigned long count, - void (*callback)(void *), void *callback_arg); -void amd7930_brecv(int dev, int chan, __u8 * buffer, unsigned long size, - void (*callback)(void *), void *callback_arg); - +#include <linux/version.h> /* Register interface presented to the CPU by the amd7930. */ struct amd7930 diff --git a/drivers/sbus/audio/audio.c b/drivers/sbus/audio/audio.c index c98923da4..36d925daf 100644 --- a/drivers/sbus/audio/audio.c +++ b/drivers/sbus/audio/audio.c @@ -1,13 +1,17 @@ /* * 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,803 +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, +#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) @@ -1023,17 +2058,28 @@ 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_DUMMY + dummy_init(); +#endif return 0; } @@ -1046,6 +2092,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 * adjust the settings for this buffer only. This must remain at the end diff --git a/drivers/sbus/audio/cs4215.h b/drivers/sbus/audio/cs4215.h index ec8de8fad..105dad4da 100644 --- a/drivers/sbus/audio/cs4215.h +++ b/drivers/sbus/audio/cs4215.h @@ -41,22 +41,22 @@ static struct { unsigned char xtal; unsigned char csval; } CS4215_FREQ[] = { - { 8000, 1, (0<<3) }, - { 16000, 1, (1<<3) }, - { 27429, 1, (2<<3) }, /* Actually 24428.57 */ - { 32000, 1, (3<<3) }, - /* { NA, 1, (4<<3) }, */ - /* { NA, 1, (5<<3) }, */ - { 48000, 1, (6<<3) }, - { 9600, 1, (7<<3) }, - { 5513, 2, (0<<3) }, /* Actually 5512.5 */ - { 11025, 2, (1<<3) }, - { 18900, 2, (2<<3) }, - { 22050, 2, (3<<3) }, - { 37800, 2, (4<<3) }, - { 44100, 2, (5<<3) }, - { 33075, 2, (6<<3) }, - { 6615, 2, (7<<3) }, + { 8000, (1<<4), (0<<3) }, + { 16000, (1<<4), (1<<3) }, + { 27429, (1<<4), (2<<3) }, /* Actually 24428.57 */ + { 32000, (1<<4), (3<<3) }, + /* { NA, (1<<4), (4<<3) }, */ + /* { NA, (1<<4), (5<<3) }, */ + { 48000, (1<<4), (6<<3) }, + { 9600, (1<<4), (7<<3) }, + { 5513, (2<<4), (0<<3) }, /* Actually 5512.5 */ + { 11025, (2<<4), (1<<3) }, + { 18900, (2<<4), (2<<3) }, + { 22050, (2<<4), (3<<3) }, + { 37800, (2<<4), (4<<3) }, + { 44100, (2<<4), (5<<3) }, + { 33075, (2<<4), (6<<3) }, + { 6615, (2<<4), (7<<3) }, { 0, 0, 0 } }; #define CS4215_HPF (1<<7) /* High Pass Filter, 1: Enabled */ diff --git a/drivers/sbus/audio/cs4231.c b/drivers/sbus/audio/cs4231.c index 24a460973..add2737fd 100644 --- a/drivers/sbus/audio/cs4231.c +++ b/drivers/sbus/audio/cs4231.c @@ -1,10 +1,10 @@ /* * 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. @@ -22,6 +22,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 +37,7 @@ #undef __CS4231_DEBUG #undef __CS4231_TRACE -#undef __CS4231_ERROR +#define __CS4231_ERROR #ifdef __CS4231_ERROR #define eprintk(x) printk x #else @@ -70,7 +72,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 +271,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 +287,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 +301,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 +331,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 +347,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 +361,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 +378,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 +389,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 +423,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 +439,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 +515,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 +589,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 +675,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 +768,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 +809,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 +847,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 +871,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 +960,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 +1014,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 +1027,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 +1036,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 +1056,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,53 +1104,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((char *)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 = - mmu_get_scsi_one((char *) cs4231_chip->output_ptr, 4096, - drv->dev->my_bus); - cs4231_chip->regs->dmapnva = 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; } } @@ -1057,19 +1165,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) 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; } @@ -1078,6 +1218,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; @@ -1088,16 +1229,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) @@ -1108,17 +1254,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((char *)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((char *)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) @@ -1137,6 +1283,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; @@ -1145,12 +1294,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) @@ -1160,11 +1317,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); - /* need adjust the end pointer, process the input, and clean up the dma */ + return value; +} - cs4231_disable_rec(drv); +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; +} + +static int cs4231_set_input_pause(struct sparcaudio_driver *drv, int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + + cs4231_chip->perchip_info.record.pause = value; + + if (value) + cs4231_stop_input(drv); + + return value; } static void cs4231_audio_getdev(struct sparcaudio_driver *drv, @@ -1185,7 +1420,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) @@ -1237,9 +1472,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? */ } @@ -1253,16 +1488,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); } @@ -1317,15 +1561,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); @@ -1342,8 +1602,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, @@ -1356,9 +1623,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); @@ -1367,7 +1648,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"); @@ -1395,8 +1676,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! */ @@ -1438,7 +1719,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); diff --git a/drivers/sbus/audio/cs4231.h b/drivers/sbus/audio/cs4231.h index 569950062..31dc849c0 100644 --- a/drivers/sbus/audio/cs4231.h +++ b/drivers/sbus/audio/cs4231.h @@ -48,11 +48,14 @@ struct cs4231_chip { /* Current buffer that the driver is playing. */ volatile __u8 * output_ptr; volatile unsigned long output_size; - volatile __u32 * output_dma_handle, output_next_dma_handle; + 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; diff --git a/drivers/sbus/audio/dbri.c b/drivers/sbus/audio/dbri.c index 0456282ef..c943aacd4 100644 --- a/drivers/sbus/audio/dbri.c +++ b/drivers/sbus/audio/dbri.c @@ -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> @@ -59,12 +60,16 @@ #include <asm/delay.h> #include <asm/sbus.h> -#include "audio.h" +#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 +/* #define DBRI_DEBUG */ #ifdef DBRI_DEBUG @@ -84,10 +89,13 @@ static char *cmds[] = { /* Bit hunting */ #define dumpcmd {int i; for(i=0; i<n; i++) printk("DBRI: %x\n", dbri->cmd[i]); } +#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (1 << 27) | value) + #else #define dprintk(a, x) #define dumpcmd +#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (intr << 27) | value) #endif /* DBRI_DEBUG */ @@ -101,50 +109,60 @@ static char *cmds[] = { static struct sparcaudio_driver drivers[MAX_DRIVERS]; static char drv_name[] = "DBRI/audio"; static int num_drivers; -static int dbri_cmdlocked = 0; +static void * output_callback_arg; /* - * Make sure, that we can send a command to the dbri + * Commands are sent to the DBRI by building a list of them in memory, + * then writing the address of the first list item to DBRI register 8. + * The list is terminated with a WAIT command, which can generate a + * CPU interrupt if required. + * + * Since the DBRI can run asynchronously to the CPU, several means of + * synchronization present themselves. The original scheme (Rudolf's) + * was to set a flag when we "cmdlock"ed the DBRI, clear the flag when + * an interrupt signaled completion, and wait on a wait_queue if a routine + * attempted to cmdlock while the flag was set. The problems arose when + * we tried to cmdlock from inside an interrupt handler, which might + * cause scheduling in an interrupt (if we waited), etc, etc + * + * A more sophisticated scheme might involve a circular command buffer + * or an array of command buffers. A routine could fill one with + * commands and link it onto a list. When a interrupt signaled + * completion of the current command buffer, look on the list for + * the next one. + * + * I've decided to implement something much simpler - after each command, + * the CPU waits for the DBRI to finish the command by polling the P bit + * in DBRI register 0. I've tried to implement this in such a way + * that might make implementing a more sophisticated scheme easier. + * + * Every time a routine wants to write commands to the DBRI, it + * must first call dbri_cmdlock() and get an initial index into dbri->cmd + * (currently always 0) in return. After the commands have been + * write (index incremented after each one), dbri_cmdsend() is called + * with the final index value. */ + static int dbri_cmdlock(struct dbri *dbri) { - unsigned long flags; - int was_sleeping = 0; - - save_flags(flags); - cli(); + return 0; +} - if(dbri_cmdlocked) { - interruptible_sleep_on(&dbri->wait); - was_sleeping = 1; - } - if(dbri_cmdlocked) - return -EINTR; - dbri_cmdlocked = 1; +static void dbri_cmdsend(struct dbri *dbri, int n) +{ + int maxloops = 1000000; - restore_flags(flags); + dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, WAIT_INTR1); + dbri->regs->reg8 = (int)dbri->cmd; - if(was_sleeping) - dprintk(D_INT, ("DBRI: Just woke up\n")); - return 0; -} + while (maxloops > 0 && (dbri->regs->reg0 & D_P)); -static void dummy() -{ + if (maxloops == 0) { + printk("DBRI: Maxloops exceeded in dbri_cmdsend\n"); + } } -static struct sparcaudio_operations dbri_ops = { - dummy, /* dbri_open, */ - dummy, /* dbri_release, */ - dummy, /* dbri_ioctl, */ - dummy, /* dbri_start_output, */ - dummy, /* dbri_stop_output, */ - dummy, /* dbri_start_input, */ - dummy, /* dbri_stop_input, */ - dummy, /* dbri_audio_getdev, */ -}; - static void dbri_reset(struct sparcaudio_driver *drv) { struct dbri *dbri = (struct dbri *)drv->private; @@ -164,14 +182,14 @@ 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); } -static void dbri_init(struct sparcaudio_driver *drv) +static void dbri_initialize(struct sparcaudio_driver *drv) { struct dbri *dbri = (struct dbri *)drv->private; int n; @@ -191,18 +209,23 @@ static void dbri_init(struct sparcaudio_driver *drv) dbri->intr[n * DBRI_INT_BLK] = (int)(dbri->intr); dbri->dbri_irqp = 1; +#ifdef USE_SBUS_BURSTS + /* Enable 4-word, 8-word, and 16-word SBus Bursts */ dbri->regs->reg0 |= (D_G|D_S|D_E); +#else + /* Disable 4-word, 8-word, and 16-word SBus Bursts */ + dbri->regs->reg0 &= ~(D_G|D_S|D_E); +#endif /* * Set up the interrupt queue */ - (void)dbri_cmdlock(dbri); + n = dbri_cmdlock(dbri); - n = 0; dbri->cmd[n++] = DBRI_CMD(D_IIQ, 0, 0); dbri->cmd[n++] = (int)(dbri->intr); - dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, WAIT_INTR1); - dbri->regs->reg8 = (int)dbri->cmd; + + dbri_cmdsend(dbri, n); } @@ -236,7 +259,7 @@ static void mmcodec_default(struct cs4215 *mm) */ mm->data[0] = CS4215_LO(0x20) | CS4215_HE|CS4215_LE; mm->data[1] = CS4215_RO(0x20) | CS4215_SE; - mm->data[2] = CS4215_LG( 0x8) | CS4215_IS; + mm->data[2] = CS4215_LG( 0x8) | CS4215_IS | CS4215_PIO0 | CS4215_PIO1; mm->data[3] = CS4215_RG( 0x8) | CS4215_MA(0xf); /* @@ -248,89 +271,104 @@ static void mmcodec_default(struct cs4215 *mm) */ mm->ctrl[0] = CS4215_RSRVD_1; mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval; - mm->ctrl[2] = CS4215_XEN | CS4215_XCLK | + mm->ctrl[2] = CS4215_XCLK | CS4215_BSEL_128 | CS4215_FREQ[0].xtal; mm->ctrl[3] = 0; } static void mmcodec_init_data(struct dbri *dbri) { - int val, n = 0; + int val, n; - dbri_cmdlock(dbri); + n = dbri_cmdlock(dbri); /* * Data mode: * Pipe 4: Send timeslots 1-4 (audio data) * Pipe 17: Send timeslots 5-8 (part of ctrl data) * Pipe 6: Receive timeslots 1-4 (audio data) - * Pipe 19: Receive timeslots 6-7. We can only receive 20 bits via + * Pipe 20: Receive timeslots 6-7. We can only receive 20 bits via * interrupt, and the rest of the data (slot 5 and 8) is * not relevant for us (only for doublechecking). + * + * Just like in control mode, the time slots are all offset by eight + * bits. The CS4215, it seems, observes TSIN (the delayed signal) + * even if it's the CHI master. Don't ask me... */ - /* Transmit & Receive Memory setup */ - dbri->mm.td.flags = DBRI_TD_F|DBRI_TD_D|DBRI_TD_CNT(0); - dbri->mm.td.ba = 0; - dbri->mm.td.nda = (__u32)&dbri->mm.td; - dbri->mm.td.status = 0; - - dbri->mm.td.flags = DBRI_RD_BCNT(0); - dbri->mm.td.ba = 0; - dbri->mm.td.nda = (__u32)&dbri->mm.rd; - dbri->mm.td.status = 0; - - /* Pipe 4: SDP + DTS */ - val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_C|D_SDP_MSB|D_PIPE(D_P_4); - dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = (__u32)&dbri->mm.td; - val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_4); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); + /* Pipe 4: SDP */ + val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_4); + dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); dbri->cmd[n++] = 0; - dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_16); - /* Pipe 17: SDP + DTS + SSP */ + /* Pipe 17: SDP */ val = D_SDP_FIXED|D_SDP_TO_SER|D_SDP_C|D_PIPE(D_P_17); dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); dbri->cmd[n++] = 0; /* Fixed data */ - val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_4) | D_PIPE(D_P_17); - dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = 0; - dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(32) | D_TS_NONCONTIG | - D_TS_MON(D_P_4) | D_TS_NEXT(D_P_16); - + /* Pipe 17: SSP */ dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17)); dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.data, 4); - /* Pipe 6: SDP + DTS */ - val=D_SDP_MEM|D_SDP_FROM_SER|D_SDP_C|D_SDP_MSB|D_PIPE(D_P_6); + /* Pipe 6: SDP */ + val=D_SDP_MEM|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_6); dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); - dbri->cmd[n++] = (__u32)&dbri->mm.rd; - - val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_6); - 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_16); dbri->cmd[n++] = 0; - /* Pipe 19: SDP + DTS */ - val = D_SDP_FIXED|D_SDP_FROM_SER|D_SDP_P|D_SDP_C|D_PIPE(D_P_19); + /* Pipe 20: SDP */ + val = D_SDP_FIXED|D_SDP_FROM_SER|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_20); dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); dbri->cmd[n++] = 0; /* Fixed data */ - val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_6) | D_PIPE(D_P_19); + + + dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0); + + + /* Pipe 4: DTS */ + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_4); dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(40) | D_TS_NONCONTIG | - D_TS_MON(D_P_6) | D_TS_NEXT(D_P_16); dbri->cmd[n++] = 0; +#if 0 + /* Full blown, four time slots, 16 bit stereo */ + dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16); +#else + /* Single time slot, 8 bit mono */ + dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16); +#endif - dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, WAIT_INTR1); + /* Pipe 17: DTS */ + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_4) | D_PIPE(D_P_17); + dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); + dbri->cmd[n++] = 0; + dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(40) | D_TS_NEXT(D_P_16); - dbri->regs->reg8 = (int)dbri->cmd; + /* Pipe 6: DTS */ + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_6); + dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); +#if 0 + /* Full blown, four time slots, 16 bit stereo */ + dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16); +#else + /* Single time slot, 8 bit mono */ + dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16); +#endif + dbri->cmd[n++] = 0; + + /* Pipe 20: DTS */ + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_6) | D_PIPE(D_P_20); + dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); + dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(48) | D_TS_NEXT(D_P_16); + dbri->cmd[n++] = 0; + + /* 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); } @@ -339,17 +377,36 @@ static void mmcodec_init_data(struct dbri *dbri) */ static void mmcodec_setctrl(struct dbri *dbri) { - int n = 0, val; + int n, val; /* - * Enable Command mode: Set PIO3 to 0, then wait + * Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait * 12 cycles <= 12/(5512.5*64) sec = 34.01 usec */ val = D_ENPIO | D_PIO1 | (dbri->mm.onboard ? D_PIO0 : D_PIO2); dbri->regs->reg2 = val; - udelay(34); - - dbri_cmdlock(dbri); + udelay(34); + + /* In Control mode, the CS4215 is a slave device, so the DBRI must + * operate as CHI master, supplying clocking and frame synchronization. + * + * In Data mode, however, the CS4215 must be CHI master to insure + * that its data stream is synchronous with its codec. + * + * The upshot of all this? We start by putting the DBRI into master + * mode, program the CS4215 in Control mode, then switch the CS4215 + * into Data mode and put the DBRI into slave mode. Various timing + * requirements must be observed along the way. + * + * Oh, and one more thing - when the DBRI is master (and only when + * the DBRI is master), the addressing of the CS4215's time slots + * is offset by eight bits, so we add eight to all the "cycle" + * values in the Define Time Slot (DTS) commands. This is done in + * hardware by a TI 248 that delays the DBRI->4215 frame sync signal + * by eight clock cycles. Anybody know why? + */ + + n = dbri_cmdlock(dbri); /* * Control mode: @@ -371,11 +428,11 @@ static void mmcodec_setctrl(struct dbri *dbri) dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); dbri->cmd[n++] = 0; - val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_P|D_SDP_C|D_PIPE(D_P_18); + val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_18); dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); dbri->cmd[n++] = 0; - val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_P|D_SDP_C|D_PIPE(D_P_19); + val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_19); dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); dbri->cmd[n++] = 0; @@ -389,76 +446,88 @@ static void mmcodec_setctrl(struct dbri *dbri) /* Link the timeslots */ + + /* Pipe 17 - CS4215 Status, Data Format, Serial Control, Test - output + * time slots 1, 2, 3 and 4 - 32 bits + */ + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_17); dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); dbri->cmd[n++] = 0; - dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(256) | D_TS_NEXT(D_P_16); + dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8) | D_TS_NEXT(D_P_16); + + /* Pipe 18 - CS4215 Status and Data Format - input + * time slots 1 & 2 - 16 bits + */ val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_18); dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(0) | D_TS_NEXT(D_P_16); + dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(8) | D_TS_NEXT(D_P_16); dbri->cmd[n++] = 0; + /* Pipe 19 - CS4215 Revision - time slot 7, eight bits - input + */ + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_18) | D_PIPE(D_P_19); dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); - dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(48) | D_TS_NEXT(D_P_16); - /* - * According to the manual we should also specify - * D_TS_NONCONTIG | D_TS_MON(D_P_18), but the machine freezes - * if we do that. Can somebody explain me why? - */ + dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(56) | D_TS_NEXT(D_P_16); dbri->cmd[n++] = 0; - /* Setup DBRI for CHI Master */ - dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_REN); - dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(6) | D_CHI_FD | - D_CHI_IR | D_CHI_EN); - dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0); + /* Setup DBRI for CHI Master + * + * BPF = 128 (128 bits per 8 kHz frame = 1.024 MHz clock rate) + * CHICM = 12 (12.288 MHz / 24 = 1.024 MHz clock rate) + * FD = 1 - drive CHIFS on rising edge of CHICK + * + * RCE = 0 - receive on falling edge of CHICK + * XCE = 1 - transmit on rising edge of CHICK + */ + dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(12) | D_CHI_FD | + D_CHI_IR | D_CHI_EN | D_CHI_BPF(128)); 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); + + /* Switch CS4215 to data mode - data sheet says + * "Set CLB=1 and send two more frames of valid control info" + */ + n = dbri_cmdlock(dbri); + + dbri->mm.ctrl[0] |= CS4215_CLB; + dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17)); + dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4); - dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, 0); - dbri->regs->reg8 = (int)dbri->cmd; + dbri_cmdsend(dbri, n); - /* Wait for the data from the CS4215 */ - interruptible_sleep_on(&dbri->int_wait); -printk("Woke up (1) reg2: %x\n", dbri->regs->reg2); + /* Two frames of control info @ 8kHz frame rate = 250 us delay */ + udelay(250); + n = dbri_cmdlock(dbri); /* Now switch back to data mode */ - n = 0; - /* CHI Anchor: Stop Send/Receive */ + /* Reset CHI Anchor: Stop Send/Receive */ val = D_DTS_VI | D_DTS_VO | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16); dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16); dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16); - dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, 0x17); - dbri->regs->reg8 = (int)dbri->cmd; - -#if 0 - dbri->mm.ctrl[0] |= CS4215_CLB; - dbri->cmd[n++] = DBRI_CMD(D_SSP, 1, D_PIPE(D_P_17)); - dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4); /* Setup DBRI for CHI Slave */ - dbri->cmd[n++] = DBRI_CMD(D_CDM, 1, D_CDM_XCE|D_CDM_REN); - dbri->cmd[n++] = DBRI_CMD(D_CHI, 1, D_CHI_CHICM(1) | D_CHI_FD | - D_CHI_IR | D_CHI_EN); - dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 1, 0x16); - dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN); + dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0)); + /* dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0) | D_CHI_IR | D_CHI_EN); */ + dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0x16); - dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, 0x17); - dbri->regs->reg8 = (int)dbri->cmd; - dbri->regs->reg2 = D_ENPIO | D_PIO3 | - (dbri->mm.onboard ? D_PIO0 : D_PIO2); -#endif + /* Wait for command to complete */ + dbri_cmdsend(dbri, n); - /* We are ready */ - dbri_cmdlocked = 0; - wake_up(&dbri->wait); + /* Switch CS4215 to data mode - set PIO3 to 1 */ + dbri->regs->reg2 = D_ENPIO | D_PIO1 | D_PIO3 | + (dbri->mm.onboard ? D_PIO0 : D_PIO2); } static int mmcodec_init(struct sparcaudio_driver *drv) @@ -500,13 +569,99 @@ static int mmcodec_init(struct sparcaudio_driver *drv) if(dbri->mm.version == 0xff) return -EIO; - /* - mmcodec_init_data(dbri, &n); - */ + mmcodec_init_data(dbri); return 0; } +void dbri_isdn_init(struct dbri *dbri) +{ + int n, val; + + /* Pipe 0: Receive D channel + * Pipe 8: Receive B1 channel + * Pipe 9: Receive B2 channel + * Pipe 1: Transmit D channel + * Pipe 10: Transmit B1 channel + * Pipe 11: Transmit B2 channel + */ + + 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 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 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 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); + + /* 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; + + /* 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; + + /* 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; + + /* 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); + + /* 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); + + /* 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); + + + /* Wait for command to complete */ + 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; @@ -518,11 +673,13 @@ void dbri_intr(int irq, void *dev_id, struct pt_regs *regs) * Read it, so the interrupt goes away. */ x = dbri->regs->reg1; +#if 0 if(numint++ > 20) { dbri->regs->reg0 = D_R; /* Soft Reset */ numint = 0; printk("Soft reset\n"); } +#endif if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) { /* @@ -552,8 +709,16 @@ void dbri_intr(int irq, void *dev_id, struct pt_regs *regs) val = D_INTR_GETVAL(x); + if (D_INTR_GETCODE(x) == D_INTR_SBRI) { + int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7}; + dbri->liu_state = liu_states[val & 0x7]; + if (dbri->liu_callback) + dbri->liu_callback(dbri->liu_callback_arg); + } + switch(D_INTR_GETCHAN(x)) { case D_INTR_CMD: +#if 0 if(D_INTR_GETCMD(x) == D_WAIT) if(val == WAIT_INTR1) { dbri_cmdlocked = 0; @@ -561,19 +726,86 @@ void dbri_intr(int irq, void *dev_id, struct pt_regs *regs) } if(val == WAIT_INTR2) wake_up(&dbri->int_wait); +#endif + break; + + case D_P_0: + /* Pipe 0 - D channel receive */ + if (D_INTR_GETCODE(x) == D_INTR_BRDY && + dbri->D.input_callback) { + dbri->D.input_callback(dbri->D.input_callback_arg, + DBRI_RD_STATUS(dbri->D.rd.flags), + DBRI_RD_CNT(dbri->D.rd.flags)-2); + } + break; + + case D_P_1: + /* Pipe 1 - D channel transmit */ + if (D_INTR_GETCODE(x) == D_INTR_XCMP && + dbri->D.output_callback) { + dbri->D.output_callback(dbri->D.output_callback_arg, + DBRI_TD_STATUS(dbri->D.rd.flags)&0xe); + } + break; + + case D_P_4: + /* Pipe 4 - audio transmit */ + if (D_INTR_GETCODE(x) == D_INTR_XCMP) { + sparcaudio_output_done(output_callback_arg, 1); + } break; + + case D_P_8: + /* Pipe 8 - B1 channel receive */ + if (D_INTR_GETCODE(x) == D_INTR_BRDY && + dbri->B[0].input_callback) { + dbri->B[0].input_callback(dbri->B[0].input_callback_arg, + DBRI_RD_STATUS(dbri->B[0].rd.flags), + DBRI_RD_CNT(dbri->B[0].rd.flags)-2); + } + break; + + case D_P_9: + /* Pipe 9 - B2 channel receive */ + if (D_INTR_GETCODE(x) == D_INTR_BRDY && + dbri->B[1].input_callback) { + dbri->B[1].input_callback(dbri->B[1].input_callback_arg, + DBRI_RD_STATUS(dbri->B[1].rd.flags), + DBRI_RD_CNT(dbri->B[1].rd.flags)-2); + } + break; + + case D_P_10: + /* Pipe 10 - B1 channel transmit */ + if (D_INTR_GETCODE(x) == D_INTR_XCMP && + dbri->B[0].output_callback) { + dbri->B[0].output_callback(dbri->B[0].output_callback_arg, + DBRI_TD_STATUS(dbri->B[0].rd.flags)&0xfe); + } + break; + + case D_P_11: + /* Pipe 11 - B2 channel transmit */ + if (D_INTR_GETCODE(x) == D_INTR_XCMP && + dbri->B[1].output_callback) { + dbri->B[1].output_callback(dbri->B[1].output_callback_arg, + DBRI_TD_STATUS(dbri->B[1].rd.flags)&0xfe); + } + break; + case D_P_18: + /* Pipe 18 - receive CS4215 status */ if(val != 0) { 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("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); + } } break; case D_P_19: + /* Pipe 19 - receive CS4215 version */ if(val != 0) { dbri->mm.version = reverse_bytes(val, 1) & 0xf; @@ -591,8 +823,448 @@ printk("Comp ok\n"); } +/* +**************************************************************************** +******************** Interface with sparcaudio midlevel ******************** +**************************************************************************** +*/ +static void dummy() +{ +} + +static int dbri_open(struct inode * inode, struct file * file, + struct sparcaudio_driver *drv) +{ + struct dbri *dbri = (struct dbri *)drv->private; + + MOD_INC_USE_COUNT; + + return 0; +} + +static void dbri_release(struct inode * inode, struct file * file, + struct sparcaudio_driver *drv) +{ + MOD_DEC_USE_COUNT; +} + +static void dbri_start_output(struct sparcaudio_driver *drv, + __u8 * buffer, unsigned long count) +{ + struct dbri *dbri = (struct dbri *)drv->private; + int val, n; + + if (count > (1 << 14) - 1) { + printk("dbri_start_output called with count=%d; truncated", count); + count = (1 << 14) - 1; + } + + n = dbri_cmdlock(dbri); + + dbri->mm.td.flags = DBRI_TD_F | DBRI_TD_B | DBRI_TD_D | DBRI_TD_CNT(count); + dbri->mm.td.ba = (__u32) buffer; + dbri->mm.td.nda = 0; + dbri->mm.td.status = 0; + + /* Pipe 4 is audio transmit */ + val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_4); + dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); + dbri->cmd[n++] = (__u32)&dbri->mm.td; + + output_callback_arg = drv; + + dbri_cmdsend(dbri, n); +} + +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, + dummy, /* dbri_ioctl, */ + dbri_start_output, + dbri_stop_output, + dummy, /* dbri_start_input, */ + dummy, /* dbri_stop_input, */ + dummy, /* dbri_audio_getdev, */ + dummy, /* dbri_set_output_volume, */ + dummy, /* dbri_get_output_volume, */ + dummy, /* dbri_set_input_volume, */ + dummy, /* dbri_get_input_volume, */ + dummy, /* dbri_set_monitor_volume, */ + dummy, /* dbri_get_monitor_volume, */ + dummy, /* dbri_set_output_balance */ + dummy, /* dbri_get_output_balance, */ + dummy, /* dbri_set_input_balance */ + dummy, /* dbri_get_input_balance, */ + dummy, /* dbri_set_output_channels */ + dummy, /* dbri_get_output_channels, */ + dummy, /* dbri_set_input_channels */ + dummy, /* dbri_get_input_channels, */ + dummy, /* dbri_set_output_precision */ + dummy, /* dbri_get_output_precision, */ + dummy, /* dbri_set_input_precision */ + dummy, /* dbri_get_input_precision, */ + dummy, /* dbri_set_output_port */ + dummy, /* dbri_get_output_port, */ + dummy, /* dbri_set_input_port */ + dummy, /* dbri_get_input_port, */ + dummy, /* dbri_set_output_encoding */ + dummy, /* dbri_get_output_encoding, */ + dummy, /* dbri_set_input_encoding */ + dummy, /* dbri_get_input_encoding, */ + dummy, /* dbri_set_output_rate */ + dummy, /* dbri_get_output_rate, */ + dummy, /* dbri_set_input_rate */ + dummy, /* dbri_get_input_rate, */ + dummy, /* dbri_sunaudio_getdev_sunos, */ + dummy, /* dbri_get_output_ports, */ + dummy, /* dbri_get_input_ports, */ + dummy, /* dbri_set_output_muted */ + dummy, /* dbri_get_output_muted, */ +}; + +/* +**************************************************************************** +************************** ISDN (Hisax) Interface ************************** +**************************************************************************** +*/ + +int dbri_get_irqnum(int dev) +{ + struct dbri *dbri; + + if (dev > num_drivers) { + return(0); + } + + 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); +} + +int dbri_get_liu_state(int dev) +{ + struct dbri *dbri; + + if (dev > num_drivers) { + return(0); + } + + dbri = (struct dbri *) drivers[dev].private; + + return dbri->liu_state; +} + +void dbri_liu_init(int dev, void (*callback)(void *), void *callback_arg) +{ + struct dbri *dbri; + + if (dev > num_drivers) { + return; + } + + dbri = (struct dbri *) drivers[dev].private; + + /* Set callback for LIU state change */ + dbri->liu_callback = callback; + dbri->liu_callback_arg = callback_arg; + + dbri_isdn_init(dbri); +} + +void dbri_liu_activate(int dev, int priority) +{ + struct dbri *dbri; + int n, val; + + if (dev > num_drivers) { + return; + } + + dbri = (struct dbri *) drivers[dev].private; + + n = dbri_cmdlock(dbri); + + /* Turn on the ISDN TE interface and request activation */ + 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); + + /* Activate the interface */ + dbri->regs->reg0 |= D_T; +} + +void dbri_liu_deactivate(int dev) +{ + struct dbri *dbri; + + if (dev > num_drivers) { + return; + } + + dbri = (struct dbri *) drivers[dev].private; + + /* Turn off the ISDN TE interface */ + dbri->regs->reg0 &= ~D_T; +} + +void dbri_dxmit(int dev, __u8 *buffer, unsigned int count, + void (*callback)(void *, int), void *callback_arg) +{ + struct dbri *dbri; + int n, val; + + if (dev > num_drivers) { + return; + } + + dbri = (struct dbri *) drivers[dev].private; + + if (count > (1 << 14) - 1) { + printk("dbri_dxmit called with count=%d; truncated", count); + count = (1 << 14) - 1; + } + + n = dbri_cmdlock(dbri); + + /* XXX - Shouldn't I check to make sure D.td isn't is use? */ + + dbri->D.td.flags = DBRI_TD_F | DBRI_TD_B | DBRI_TD_CNT(count) | DBRI_TD_I; + dbri->D.td.ba = (__u32) buffer; + dbri->D.td.nda = 0; + 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; + + dbri->D.output_callback = callback; + dbri->D.output_callback_arg = callback_arg; + + 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; + int n, val; + + if (dev > num_drivers) { + return; + } + + dbri = (struct dbri *) drivers[dev].private; + + if (size > (1 << 14) - 1) { + printk("dbri_drecv called with size=%d; truncated", size); + size = (1 << 14) - 1; + } + + /* Make sure size is a multiple of four */ + size &= ~3; + + n = dbri_cmdlock(dbri); + + /* XXX - Shouldn't I check to make sure D.rd isn't is use? */ + + dbri->D.rd.flags = 0; + dbri->D.rd.ba = (__u32) buffer; + dbri->D.rd.nda = 0; + 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; + + dbri_cmdsend(dbri, n); + + dbri->D.input_callback = callback; + dbri->D.input_callback_arg = callback_arg; +} + +int dbri_bopen(int dev, unsigned int chan, + int hdlcmode, u_char xmit_idle_char) +{ + struct dbri *dbri; + int n, val; + + if (dev > num_drivers || chan > 1) { + return -1; + } + + dbri = (struct dbri *) drivers[dev].private; + + if (hdlcmode) { + + return -1; + + /* Pipe 8/9: receive B1/B2 channel */ + dbri->B[chan].recvSDP = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_8+chan); + + /* Pipe 10/11: transmit B1/B2 channel */ + dbri->B[chan].xmitSDP = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_10+chan); + + } else { /* !hdlcmode means transparent */ + + /* Pipe 8/9: receive B1/B2 channel */ + dbri->B[chan].recvSDP = D_SDP_MEM|D_SDP_FROM_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_8+chan); + + /* Pipe 10/11: transmit B1/B2 channel */ + dbri->B[chan].xmitSDP = D_SDP_MEM|D_SDP_TO_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_10+chan); + + } + + n = dbri_cmdlock(dbri); + + /* Pipe 8/9: receive B1/B2 channel */ + val = dbri->B[chan].recvSDP | D_SDP_C; + dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); + dbri->cmd[n++] = 0; + + /* Pipe 10/11: transmit B1/B2 channel */ + val = dbri->B[chan].xmitSDP | D_SDP_C; + dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); + dbri->cmd[n++] = 0; + + dbri->B[chan].output_callback = NULL; + dbri->B[chan].input_callback = NULL; + + dbri_cmdsend(dbri, n); + + return 0; +} + +void dbri_bclose(int dev, unsigned int chan) +{ + struct dbri *dbri; + + if (dev > num_drivers || chan > 1) { + return; + } + + dbri = (struct dbri *) drivers[dev].private; + + dbri->B[chan].output_callback = NULL; + dbri->B[chan].input_callback = NULL; +} + +void dbri_bxmit(int dev, unsigned int chan, + __u8 *buffer, unsigned long count, + void (*callback)(void *, int), + void *callback_arg) +{ + struct dbri *dbri; + int n, val; + + if (dev > num_drivers || chan > 1) { + return; + } + + dbri = (struct dbri *) drivers[dev].private; + + if (count > (1 << 14) - 1) { + printk("dbri_bxmit called with count=%ld; truncated", count); + count = (1 << 14) - 1; + } + + n = dbri_cmdlock(dbri); + + /* XXX - Shouldn't I check to make sure td isn't is use? */ + + dbri->B[chan].td.flags = DBRI_TD_F | DBRI_TD_B | DBRI_TD_CNT(count); + dbri->B[chan].td.ba = (__u32) buffer; + dbri->B[chan].td.nda = 0; + 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; + + dbri->B[chan].output_callback = callback; + dbri->B[chan].output_callback_arg = callback_arg; + + dbri_cmdsend(dbri, n); +} + +void dbri_brecv(int dev, unsigned int chan, + __u8 *buffer, unsigned long size, + void (*callback)(void *, int, unsigned int), + void *callback_arg) +{ + struct dbri *dbri; + int n, val; + + if (dev > num_drivers || chan > 1) { + return; + } + + dbri = (struct dbri *) drivers[dev].private; + + if (size > (1 << 14) - 1) { + printk("dbri_brecv called with size=%ld; truncated", size); + size = (1 << 14) - 1; + } + + /* Make sure size is a multiple of four */ + size &= ~3; + + n = dbri_cmdlock(dbri); + + /* XXX - Shouldn't I check to make sure RD isn't is use? */ + + dbri->B[chan].rd.flags = 0; + dbri->B[chan].rd.ba = (__u32) buffer; + dbri->B[chan].rd.nda = 0; + 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; + + 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, + dbri_liu_init, + dbri_liu_activate, + dbri_liu_deactivate, + dbri_dxmit, + dbri_drecv, + dbri_bopen, + dbri_bclose, + dbri_bxmit, + dbri_brecv +}; +EXPORT_SYMBOL(dbri_foreign_interface); +#endif + +/* +**************************************************************************** +**************************** Initialization ******************************** +**************************************************************************** +*/ static int dbri_attach(struct sparcaudio_driver *drv, struct linux_sbus_device *sdev) @@ -642,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); @@ -651,7 +1323,7 @@ static int dbri_attach(struct sparcaudio_driver *drv, return err; } - dbri_init(drv); + dbri_initialize(drv); err = mmcodec_init(drv); if(err) { dbri_detach(drv); @@ -713,3 +1385,22 @@ void cleanup_module(void) } } #endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local Variables: + * c-indent-level: 8 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -8 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff --git a/drivers/sbus/audio/dbri.h b/drivers/sbus/audio/dbri.h index 098616ac8..af3a8a4d7 100644 --- a/drivers/sbus/audio/dbri.h +++ b/drivers/sbus/audio/dbri.h @@ -33,6 +33,17 @@ struct dbri_mem { __u32 status; }; +struct dbri_channel { + struct dbri_mem td; + struct dbri_mem rd; + unsigned int recvSDP; + unsigned int xmitSDP; + void (*output_callback)(void *, int); + void *output_callback_arg; + void (*input_callback)(void *, int, unsigned int); + void *input_callback_arg; +}; + #include "cs4215.h" /* This structure holds the information for both chips (DBRI & CS4215) */ @@ -49,6 +60,15 @@ struct dbri { struct wait_queue *wait, *int_wait; /* Where to sleep if busy */ struct audio_info perchip_info; + + /* Track ISDN LIU and notify changes */ + int liu_state; + void (*liu_callback)(void *); + void *liu_callback_arg; + + /* Callback routines and descriptors for ISDN channels */ + struct dbri_channel D; + struct dbri_channel B[2]; }; @@ -106,11 +126,10 @@ struct dbri { #define D_TEST 0xd /* No comment */ #define D_CDM 0xe /* CHI Data mode command */ -#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (intr << 27) | value) /* 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 */ @@ -140,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 */ @@ -151,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 */ @@ -268,7 +287,7 @@ struct dbri { /* Transmit descriptor defines */ #define DBRI_TD_F (1<<31) /* End of Frame */ -#define DBRI_TD_D (1<<31) /* Do not append CRC */ +#define DBRI_TD_D (1<<30) /* Do not append CRC */ #define DBRI_TD_CNT(v) (v<<16) /* Number of valid bytes in the buffer */ #define DBRI_TD_B (1<<15) /* Final interrupt */ #define DBRI_TD_M (1<<14) /* Marker interrupt */ @@ -277,17 +296,19 @@ 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 */ /* Receive descriptor defines */ #define DBRI_RD_F (1<<31) /* End of Frame */ #define DBRI_RD_C (1<<30) /* Completed buffer */ #define DBRI_RD_B (1<<15) /* Final interrupt */ #define DBRI_RD_M (1<<14) /* Marker interrupt */ -#define DBRI_RD_CNT(v) (v<<16) /* Number of valid bytes in the buffer */ #define DBRI_RD_BCNT(v) v /* Buffer size */ #define DBRI_RD_CRC (1<<7) /* 0: CRC is correct */ #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 */ #endif /* _DBRI_H_ */ |