S: 34131 Kassel
S: Germany
+N: Christoph Hellwig
+E: chhellwig@gmx.net
+D: Sound/OSS hacking
+S: Triftstraße 26
+S: 38644 Goslar
+S: Germany
+
N: Richard Henderson
E: rth@twiddle.net
E: rth@cygnus.com
General Information
===================
+ To use System V shared memory, you have to mount the shm filesystem
+somewhere and put the mountpoint into /proc/sys/kernel/shmpath.
+Default is /var/shm.
+
<CTRL><ALT><DEL> now performs a cold reboot instead of a warm reboot
for increased hardware compatibility. If you want a warm reboot and
know it works on your hardware, add a "reboot=warm" command line option
It is SAFEST to say N to this question.
-3ware Hardware ATA-RAID support (EXPERIMENTAL)
+3ware Hardware ATA-RAID support
CONFIG_BLK_DEV_3W_XXXX_RAID
3ware is the only hardware ATA-Raid product in Linux to date.
This card is 2,4, or 8 channel master mode support only.
This is an Ultra DMA chipset for ATA-66.
This driver adds up to 4 more EIDE devices sharing a single
- interrupt. The HPT366 chipset in its current form is a non-bootable.
- This driver requires dynamic tuning of the chipset during the
- ide-probe at boot. It is reported to support DVD II drives, by the
+ interrupt. The HPT366 chipset in its current form is a non-bootable,
+ without special LILO commands for redirecting the reference to device 0x80.
+ The other solution is to include "CONFIG_BLK_DEV_OFFBOARD" unless your
+ mainboard has the chipset native mounted. Regardless one should use the
+ fore mentioned option and call at LILO or include in your append-line:
+ "ide=reverse". This driver requires dynamic tuning of the chipset during
+ the ide-probe at boot. It is reported to support DVD II drives, by the
manufacturer.
Please read the comments at the top of drivers/block/hpt366.c
This is the frame buffer device driver for the builtin graphics
chipset found in Amigas.
+ The driver is also available as a module ( = code which can be
+ inserted and removed from the running kernel whenever you want). The
+ module will be called amifb.o. If you want to compile it as a
+ module, say M here and read Documentation/modules.txt.
+
Amiga OCS chipset support
CONFIG_FB_AMIGA_OCS
This enables support for the original Agnus and Denise video chips,
Add support for turning your DECnet Endnode into a level 1 or 2
router. This is an unfinished option for developers only. If you do
turn it on, then make sure that you also say Y to "Kernel/User
- network link driver" and "Routing messages", since rtnetlink is the
- only current method of configuration.
+ network link driver", "Routing messages" and "Network packet
+ filtering". The first two are required to allow configuration via
+ rtnetlink (currently you need Alexey Kuznetsov's iproute2 package
+ from ftp.inr.ac.ru). The "Network packet filtering" option will
+ be required for the forthcoming routing daemon to work.
-DECnet Raw Socket Support
-CONFIG_DECNET_RAW
- Add support for the SOCK_RAW type under DECnet. Used by userland
- routing programs to receive routing messages from the kernel and
- also as a general debugging aid to see what's going on "under the
- hood".
+ See Documentation/networking/decnet.txt for more information.
AppleTalk DDP
CONFIG_ATALK
to present you with more options. If unsure, say Y.
ACI mixer (miroPCM12/PCM20)
-CONFIG_ACI_MIXER
+CONFIG_SOUND_ACI_MIXER
ACI (Audio Command Interface) is a protocol used to communicate with
the microcontroller on some sound cards produced by miro, e.g. the
miroSOUND PCM12 and PCM20. The main function of the ACI is to
radio-miropcm20 driver.
SB32/AWE support
-CONFIG_AWE32_SYNTH
+CONFIG_SOUND_AWE32_SYNTH
Say Y here if you have a Sound Blaster SB32, AWE32-PnP, SB AWE64 or
similar sound card. See Documentation/sound/README.awe,
Documentation/sound/AWE32 and the Soundblaster-AWE mini-HOWTO,
info.
Gallant's Audio Excel DSP 16 support (SC-6000 and SC-6600)
-CONFIG_AEDSP16
+CONFIG_SOUND_AEDSP16
Answer Y if you have a Gallant's Audio Excel DSP 16 card. This
driver supports Audio Excel DSP 16 but not the III nor PnP versions
of this card.
Documentation/sound/AudioExcelDSP16 to get more information about
this driver and its configuration.
-I/O base for Audio Excel DSP 16
-CONFIG_AEDSP16_BASE
- This is the base I/O address of the Audio Excel DSP 16 card. It must
- be 220 or 240. If you compiled aedsp16.o as a module you can specify
- this parameter as 'io=0xNNN'.
-
Audio Excel DSP 16 (SBPro emulation)
CONFIG_AEDSP16_SBPRO
Answer Y if you want your audio card to emulate Sound Blaster Pro.
(SB16/32/64, ESS, Jazz16) support" and N to "Audio Excel DSP 16 (MSS
emulation)".
-Audio Excel DSP 16 IRQ
-CONFIG_AEDSP16_SB_IRQ
- This is the IRQ of the Audio Excel DSP 16 card. It must be 5, 7, 9,
- 10 or 11. If you compiled aedsp16.o as a module you can specify
- this parameter as 'irq=NN'.
-
-Audio Excel DSP 16 DMA
-CONFIG_AEDSP16_SB_DMA
- This is the IRQ of the Audio Excel DSP 16 card. It must be 0, 1 or
- 3. If you compiled aedsp16.o as a module you can specify this
- parameter as 'dma=NN'.
-
Audio Excel DSP 16 (MSS emulation)
CONFIG_AEDSP16_MSS
Answer Y if you want your audio card to emulate Microsoft Sound
System. You should then say Y to "Microsoft Sound System support"
and say N to "Audio Excel DSP 16 (SBPro emulation)".
-Audio Excel DSP 16 IRQ
-CONFIG_AEDSP16_MSS_IRQ
- This is the IRQ of the Audio Excel DSP 16 card. It must be 5, 7, 9,
- 10 or 11. If you compiled aedsp16.o as a module you can specify
- this parameter as 'irq=NN'.
-
-Audio Excel DSP 16 DMA
-CONFIG_AEDSP16_MSS_DMA
- This is the IRQ of the Audio Excel DSP 16 card. It must be 0, 1
- or 3. If you compiled aedsp16.o as a module you can specify this
- parameter as 'dma=NN'.
-
SC-6600 based audio cards (new Audio Excel DSP 16)
CONFIG_SC6600
The SC6600 is the new version of DSP mounted on the Audio Excel DSP
driver as a module you have to specify the MPU I/O base address with
the parameter 'mpu_base=0xNNN'.
-MPU401 IRQ for Audio Excel DSP 16
-CONFIG_AEDSP16_MPU_IRQ
- This is the IRQ of the MPU-401 emulation of your Audio Excel DSP 16
- card. It must be 5, 7, 9, 10 or 0 (to disable MPU-401 interface). If
- you compiled aedsp16.o as a module you can specify this parameter as
- 'mpu_irq=NN'.
-
Ensoniq ES1370 based PCI sound cards
CONFIG_SOUND_ES1370
Say Y or M if you have a PCI sound card utilizing the Ensoniq
if you want to try out router support (not properly debugged yet)
you'll need the following options as well...
- CONFIG_DECNET_RAW (to receive routing packets)
CONFIG_DECNET_ROUTER (to be able to add/delete routes)
CONFIG_NETLINK (to allow rtnetlink)
CONFIG_RTNETLINK (for communication with the kernel routing layer)
+ CONFIG_NETFILTER (will be required for the DECnet routing daemon)
3) Command line options
If you want to configure a DECnet router you'll need the iproute2 package
since its the _only_ way to add and delete routes currently. Eventually
there will be a routing daemon to send and receive routing messages for
-each interface and update the kernel routing tables accordingly.
+each interface and update the kernel routing tables accordingly. The
+routing daemon will use netfilter to listen to routing packets, and
+rtnetlink to update the kernels routing tables.
+
+The DECnet raw socket layer has been removed since it was there purely
+for use by the routing daemon which will now use netfilter (a much cleaner
+and more generic solution) instead.
5) How can I tell if its working ?
=============
The status of sound may be read/checked by:
- cat /proc/sound
- cat /dev/sndstat
cat (anyfile).au >/dev/audio
The status of the modules and which modules depend on
--------------------
This OSS driver exports /dev/mixer and /dev/dsp to applications, which
-mostly adhere to the OSS spec. This driver doesn't register itself
-with /dev/sndstat, so don't expect information to appear there.
+mostly adhere to the OSS spec.
The /dev/dsp device exported behaves almost as expected. Playback is
supported in all the various lovely formats. 8/16bit stereo/mono from
If you chose to build it as a module, just insmod the resulting softoss2.o
-A 'cat /dev/sndstat' with all the above options should look similar to this:
-
- OSS/Free:3.8s2++-971130
- Load type: Driver loaded as a module
- Kernel: Linux iniquity 2.1.105 #145 Mon Jun 8 11:40:47 MST 1998 i586
- Config options: 0
-
- Installed drivers:
-
- Card config:
-
- Audio devices:
- 0: MSS audio codec (CS4231) (DUPLEX)
-
- Synth devices:
- 0: Yamaha OPL-3
- 1: SoftOSS
-
- Midi devices:
- 0: OPL3-SA (MPU401)
-
- Timers:
- 0: System clock
- 1: MSS audio codec (CS4231)
-
- Mixers:
- 0: MSS audio codec (CS4231)
-
Questions? Comments?
<stiker@northlink.com>
When any sound device is opened the kernel requests auto-loading
of char-major-14. There is a built-in alias that translates this
-request to loading the main sound module. The main sound module
-contains only common code which is needed by all the sound drivers,
-and the driver for /dev/sndstat.
+request to loading the main sound module.
The sound module in its turn will request loading of a sub-driver
for mixer, audio, midi or synthesizer device. The first 3 are
at /var/adm/messages for more verbose error message.
-In general the easiest way to diagnose problems is to do "cat /dev/sndstat".
-
-If you get an error message, there are some problems with the driver setup:
-
- - "No such file or directory" tells that the device files for
- the sound driver are missing. Use the script at the end of
- linux/drivers/sound/Readme.linux to create them.
-
- - "No such device" tells that the sound driver is not in the kernel.
- You have to reconfigure and recompile the kernel to have the sound
- driver. Compiling the driver doesn't help alone. You have to boot
- with the newly compiled one before the driver becomes active.
- The Linux-HOWTO should help in this step.
-
The following errors are likely with /dev/dsp and /dev/audio.
- - "No such device or address". This error message should not happen
- with /dev/sndstat but it's possible with the other sound devices.
+ - "No such device or address".
This error indicates that there are no suitable hardware for the
device file or the sound driver has been compiled without support for
this particular device. For example /dev/audio and /dev/dsp will not
with impossible parameters. Check that the application is
for sound driver version 2.X or later.
-In general the printout of /dev/sndstat should tell what is the problem.
-It's possible that there are bugs in the sound driver but 99% of the problems
-reported to me are caused by somehow incorrect setup during "make config".
-
Linux installation
==================
Problems
--------
-If you have any kind of problems, there is a debugging feature which
-could help you to solve the problem. To use it, just execute the
-command:
-
- cat /dev/sndstat
-
-and look at the output. It should display some useful info about the
-driver configuration. If there is no /dev/sndstat
-(/dev/sndstat: No such file or directory), ensure that you have executed the
-soundinstall script (at the end of this file).
-
Common error messages:
- /dev/???????: No such file or directory.
The sound driver configuration dialog
-------------------------------------
-If you already have the sound driver installed, consult a printout of
-"cat /dev/sndstat" when configuring the driver again. It gives the I/O,
-IRQ and DMA settings you used earlier.
-
Sound configuration starts by making some yes/no questions. Be careful
when answering to these questions since answering y to a question may
prevent some later ones from being asked. For example don't answer y to
Please check the version of sound driver you are using before
complaining that your card is not supported. It's possible you are
using a driver version which was released months before your card was
-introduced. The driver's release date is listed after its version number in a
-"cat /dev/sndstat" printout and in the file linux/drivers/sound/soundvers.h.
+introduced.
First of all, there is an easy way to make most sound cards work with Linux.
Just use the DOS based driver to initialize the card to a known state, then use
The "available DRAM" line will vary depending on how much added RAM
your card has. Mine has 8MB.
-Next, check /dev/sndstat, which on my machine says:
----------------------------------------------------------------------
-OSS/Free:3.8s2++-971130
-Load type: Driver loaded as a module
-Kernel: Linux bd 2.1.106 #12 SMP Fri Jul 3 00:37:34 EDT 1998 i486
-Config options: 0
-
-Installed drivers:
-
-Card config:
-
-Audio devices:
-0: Crystal audio controller (CS4232) (DUPLEX)
-
-Synth devices:
-0: Turtle Beach WaveFront
-1: Yamaha OPL-3
-
-Midi devices:
-0: WaveFront Internal MIDI
-1: WaveFront External MIDI
-
-Timers:
-0: System clock
-1: Crystal audio controller (CS4232)
-
-Mixers:
-0: Crystal audio controller (CS4232)
------------------------------------------------------------
-
To check basically functionality, use play(1) or splay(1) to send a
.WAV or other audio file through the audio portion. Then use playmidi
to play a General MIDI file. Try the "-D 0" to hear the
Known bugs (patches/suggestions welcome)
------------------------------------------------------------------------
1) Two MIDI devices are loaded by the sound driver. Eliminate one of them.
-Sample /proc/sound output:
-
- Midi devices:
- 0: Sound Blaster
- 1: VIA 82Cxxx Audio driver 1.1.2
2) Two mixer devices are loaded by the sound driver. Eliminate one of
them. At least one bug report says that SB mixer does not work at all,
-only AC97 mixer. Sample /proc/sound output:
-
- Mixers:
- 0: via82cxxxAC97Mixer
- 1: Sound Blaster
-
-3) After unloading the driver, a SoundBlaster MIDI device is still
-listed in /proc/sound. Investigate what is not being unloaded,
-and fix it. Sample /proc/sound output, after 'rmmod via82cxxx':
-
- Midi devices:
- 0: Sound Blaster
-
-
+only AC97 mixer.
VERSION = 2
PATCHLEVEL = 3
-SUBLEVEL = 50
+SUBLEVEL = 51
EXTRAVERSION =
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
source drivers/usb/Config.in
-source drivers/misc/Config.in
+#source drivers/misc/Config.in
source fs/Config.in
#
# CONFIG_USB is not set
-#
-# Misc devices
-#
-
#
# Filesystems
#
static int do_osf_statfs(struct dentry * dentry, struct osf_statfs *buffer, unsigned long bufsiz)
{
struct statfs linux_stat;
- struct inode * inode = dentry->d_inode;
- struct super_block * sb = inode->i_sb;
- int error;
-
- error = -ENODEV;
- if (sb && sb->s_op && sb->s_op->statfs) {
- set_fs(KERNEL_DS);
- error = sb->s_op->statfs(sb, &linux_stat, sizeof(linux_stat));
- set_fs(USER_DS);
- if (!error)
- error = linux_to_osf_statfs(&linux_stat, buffer, bufsiz);
- }
+ int error = vfs_statfs(dentry->d_inode->i_sb, &linux_stat);
+ if (!error)
+ error = linux_to_osf_statfs(&linux_stat, buffer, bufsiz);
return error;
}
#
# Copyright (C) 1995-1999 by Russell King
-LD := $(CROSS_COMPILE)ld
OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
CPP := $(CC) -E
PERL := perl
source drivers/usb/Config.in
-source drivers/misc/Config.in
+#source drivers/misc/Config.in
if [ "$CONFIG_VT" = "y" ]; then
mainmenu_option next_comment
bool 'Power Management support' CONFIG_PM
-dep_bool ' ACPI support' CONFIG_ACPI $CONFIG_PM
+dep_tristate ' ACPI support' CONFIG_ACPI $CONFIG_PM
if [ "$CONFIG_ACPI" != "n" ]; then
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool ' Enter S1 for sleep (EXPERIMENTAL)' CONFIG_ACPI_S1_SLEEP
source drivers/usb/Config.in
-source drivers/misc/Config.in
+#source drivers/misc/Config.in
source fs/Config.in
#
# SCSI low-level drivers
#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
# CONFIG_SCSI_7000FASST is not set
# CONFIG_SCSI_ACARD is not set
# CONFIG_SCSI_AHA152X is not set
# CONFIG_WATCHDOG is not set
# CONFIG_NVRAM is not set
# CONFIG_RTC is not set
-# CONFIG_EFI_RTC is not set
#
# Video For Linux
#
# CONFIG_USB is not set
-#
-# Misc devices
-#
-
#
# File systems
#
*/
extern unsigned long get_cmos_time(void);
-static int acpi_control_thread(void *context);
static int acpi_do_ulong(ctl_table *ctl,
int write,
struct file *file,
void *buffer,
size_t *len);
-DECLARE_WAIT_QUEUE_HEAD(acpi_control_wait);
-
static struct ctl_table_header *acpi_sysctl = NULL;
static struct acpi_facp *acpi_facp = NULL;
static int acpi_p_lvl2_tested = 0;
static int acpi_p_lvl3_tested = 0;
-static int acpi_disabled = 0;
+enum
+{
+ ACPI_ENABLED,
+ ACPI_TABLES_ONLY,
+ ACPI_CHIPSET_ONLY,
+ ACPI_DISABLED,
+};
+
+static int acpi_enabled = ACPI_ENABLED;
// bits 8-15 are SLP_TYPa, bits 0-7 are SLP_TYPb
static unsigned long acpi_slp_typ[] =
*/
static int __init acpi_init(void)
{
- int pid;
-
- if (acpi_disabled)
- return -ENODEV;
-
- if (acpi_find_tables() && acpi_find_chipset()) {
- // no ACPI tables and not recognized chipset
+ switch(acpi_enabled)
+ {
+ case ACPI_ENABLED:
+ if (acpi_find_tables() && acpi_find_chipset())
+ return -ENODEV;
+ break;
+ case ACPI_TABLES_ONLY:
+ if (acpi_find_tables())
+ return -ENODEV;
+ break;
+ case ACPI_CHIPSET_ONLY:
+ if (acpi_find_chipset())
+ return -ENODEV;
+ break;
+ case ACPI_DISABLED:
return -ENODEV;
}
acpi_sysctl = register_sysctl_table(acpi_dir_table, 1);
- pid = kernel_thread(acpi_control_thread,
- NULL,
- CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
-
pm_power_off = acpi_power_off;
pm_active = 1;
static void __exit acpi_exit(void)
{
pm_idle = NULL;
+ pm_active = 0;
pm_power_off = NULL;
unregister_sysctl_table(acpi_sysctl);
static int __init acpi_setup(char *str)
{
while (str && *str) {
- if (strncmp(str, "off", 3) == 0)
- acpi_disabled = 1;
- else if (strncmp(str, "on", 2) == 0)
- acpi_disabled = 0;
+ if (strncmp(str, "on", 2) == 0)
+ acpi_enabled = ACPI_ENABLED;
+ else if (strncmp(str, "tables", 6) == 0)
+ acpi_enabled = ACPI_TABLES_ONLY;
+ else if (strncmp(str, "chipset", 7) == 0)
+ acpi_enabled = ACPI_CHIPSET_ONLY;
+ else if (strncmp(str, "off", 3) == 0)
+ acpi_enabled = ACPI_DISABLED;
str = strpbrk(str, ",");
if (str)
str += strspn(str, ",");
__setup("acpi=", acpi_setup);
-/*
- * Manage idle devices
- */
-static int acpi_control_thread(void *context)
-{
- exit_mm(current);
- exit_files(current);
- strcpy(current->comm, "acpi");
-
- for(;;) {
- interruptible_sleep_on(&acpi_control_wait);
- if (signal_pending(current))
- break;
-
- // find all idle devices and set idle timer
- }
-
- return 0;
-}
-
-__initcall(acpi_init);
+module_init(acpi_init);
+module_exit(acpi_exit);
printk("PCI: Discovered primary peer bus %02x [IRQ]\n", i);
}
-static void set_level_irq(unsigned irq)
+static void ali_set_level_irq(unsigned irq)
{
unsigned char mask = 1 << (irq & 7);
unsigned int port = 0x4d0 + (irq >> 3);
unsigned char val = inb(port);
if (val & mask) {
- printk("PCI irq %d was level\n", irq);
+ DBG("PCI irq %d was level\n", irq);
return;
}
- printk("PCI irq %d was edge, turning into level-triggered\n", irq);
+ DBG("PCI irq %d was edge, turning into level-triggered\n", irq);
outb(val | mask, port);
}
unsigned offset = 0x48 + (pirq >> 1);
unsigned shift = (pirq & 1) << 2;
pci_read_config_byte(router, offset, &byte);
- printk("ALI: old %04x=%02x\n", offset, byte);
+ DBG("ALI: old %04x=%02x\n", offset, byte);
byte &= ~(0xf << shift);
byte |= val << shift;
- printk("ALI: new %04x=%02x\n", offset, byte);
+ DBG("ALI: new %04x=%02x\n", offset, byte);
pci_write_config_byte(router, offset, byte);
- set_level_irq(irq);
+ ali_set_level_irq(irq);
return irq;
}
}
return 0;
}
-
/*
* In case BIOS forgets to tell us about IRQ, we try to look it up in the routing
if ((err = pcibios_enable_resources(dev)) < 0)
return err;
- if (!dev->irq && pirq_table) {
+ if (!dev->irq) {
u8 pin;
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
if (pin) {
- char *msg = pcibios_lookup_irq(dev, pirq_table, pin, 1);
- if (msg)
+ char *msg;
+ if (pirq_table && ((msg = pcibios_lookup_irq(dev, pirq_table, pin, 1))))
printk("PCI: Assigned IRQ %d to device %s [%s]\n", dev->irq, dev->slot_name, msg);
+ else
+ printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",
+ 'A' + pin - 1, dev->slot_name,
+ (pci_probe & PCI_BIOS_IRQ_SCAN) ? "" : " Please try using pci=biosirq.");
}
}
return 0;
source drivers/char/Config.in
source drivers/usb/Config.in
-source drivers/misc/Config.in
+#source drivers/misc/Config.in
source fs/Config.in
source drivers/usb/Config.in
-source drivers/misc/Config.in
+#source drivers/misc/Config.in
source fs/Config.in
#
# CONFIG_USB is not set
-#
-# Misc devices
-#
-
#
# File systems
#
#
# CONFIG_USB is not set
-#
-# Misc devices
-#
-
#
# Filesystems
#
#
# CONFIG_USB is not set
-#
-# Misc devices
-#
-
#
# File systems
#
int len, int fs_type)
{
struct dentry *dentry;
- struct inode *inode;
- mm_segment_t old_fs;
struct statfs kbuf;
int error, i;
if (IS_ERR(dentry))
goto out;
- inode = dentry->d_inode;
- old_fs = get_fs(); set_fs(get_ds());
- error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf,
- sizeof(struct statfs));
- set_fs(old_fs);
+ error = vfs_statfs(dentry->d_inode->i_sb, &kbuf);
if (error)
goto dput_and_out;
asmlinkage int irix_fstatfs(unsigned int fd, struct irix_statfs *buf)
{
- struct inode *inode;
struct statfs kbuf;
- mm_segment_t old_fs;
struct file *file;
int error, i;
goto out;
}
- if (!(inode = file->f_dentry->d_inode)) {
- error = -ENOENT;
- goto out_f;
- }
- if (!inode->i_sb) {
- error = -ENODEV;
- goto out_f;
- }
- if (!inode->i_sb->s_op->statfs) {
- error = -ENOSYS;
- goto out_f;
- }
-
- old_fs = get_fs(); set_fs(get_ds());
- error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf,
- sizeof(struct statfs));
- set_fs(old_fs);
+ error = vfs_statfs(file->f_dentry->d_inode->i_sb, &kbuf);
if (error)
goto out_f;
asmlinkage int irix_statvfs(char *fname, struct irix_statvfs *buf)
{
struct dentry *dentry;
- struct inode *inode;
- mm_segment_t old_fs;
struct statfs kbuf;
int error, i;
error = PTR_ERR(dentry);
if(!IS_ERR(dentry))
goto out;
- inode = dentry->d_inode;
-
- error = -ENOSYS;
- if(!inode->i_sb->s_op->statfs)
- goto dput_and_out;
-
- old_fs = get_fs(); set_fs(get_ds());
- error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf,
- sizeof(struct statfs));
- set_fs(old_fs);
+ error = vfs_statfs(dentry->d_inode->i_sb, &kbuf);
if (error)
goto dput_and_out;
asmlinkage int irix_fstatvfs(int fd, struct irix_statvfs *buf)
{
- struct inode *inode;
- mm_segment_t old_fs;
struct statfs kbuf;
struct file *file;
int error, i;
error = -EBADF;
goto out;
}
- if (!(inode = file->f_dentry->d_inode)) {
- error = -ENOENT;
- goto out_f;
- }
- if (!inode->i_sb) {
- error = -ENODEV;
- goto out_f;
- }
- if (!inode->i_sb->s_op->statfs) {
- error = -ENOSYS;
- goto out_f;
- }
-
- old_fs = get_fs(); set_fs(get_ds());
- error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf,
- sizeof(struct statfs));
- set_fs(old_fs);
+ error = vfs_statfs(file->f_dentry->d_inode->i_sb, &kbuf);
if (error)
goto out_f;
asmlinkage int irix_statvfs64(char *fname, struct irix_statvfs64 *buf)
{
struct dentry *dentry;
- struct inode *inode;
- mm_segment_t old_fs;
struct statfs kbuf;
int error, i;
error = PTR_ERR(dentry);
if(IS_ERR(dentry))
goto out;
- error = -ENOSYS;
- inode = dentry->d_inode;
- if(!inode->i_sb->s_op->statfs)
- goto dput_and_out;
-
- old_fs = get_fs(); set_fs(get_ds());
- error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf,
- sizeof(struct statfs));
- set_fs(old_fs);
+ error = vfs_statfs(dentry->d_inode->i_sb, &kbuf);
if (error)
goto dput_and_out;
asmlinkage int irix_fstatvfs64(int fd, struct irix_statvfs *buf)
{
- struct inode *inode;
- mm_segment_t old_fs;
struct statfs kbuf;
struct file *file;
int error, i;
error = -EBADF;
goto out;
}
- if (!(inode = file->f_dentry->d_inode)) {
- error = -ENOENT;
- goto out_f;
- }
- if (!inode->i_sb) {
- error = -ENODEV;
- goto out_f;
- }
- if (!inode->i_sb->s_op->statfs) {
- error = -ENOSYS;
- goto out_f;
- }
-
- old_fs = get_fs(); set_fs(get_ds());
- error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf,
- sizeof(struct statfs));
- set_fs(old_fs);
+ error = vfs_statfs(file->f_dentry->d_inode->i_sb, &kbuf);
if (error)
goto out_f;
source drivers/usb/Config.in
-# drivers/misc has currently only i386 specific devices.
#source drivers/misc/Config.in
source fs/Config.in
OBJECTS := head.o misc.o ../coffboot/zlib.o
CFLAGS = $(CPPFLAGS) -O2 -DSTDC_HEADERS -fno-builtin
-OBJCOPY = $(CROSS_COMPILE)objcopy
OBJCOPY_ARGS = -O elf32-powerpc
OBJECTS += vreset.o kbd.o of1275.o
CFLAGS = $(CPPFLAGS) -O -fno-builtin -DSTDC_HEADERS
LD_ARGS = -Ttext 0x00400000
-OBJCOPY = $(CROSS_COMPILE)objcopy
OBJS = crt0.o start.o main.o misc.o ../coffboot/string.o ../coffboot/zlib.o image.o sysmap.o
LIBS = $(TOPDIR)/lib/lib.a
HOSTCFLAGS = -O -I$(TOPDIR)/include
-CC = $(CROSS_COMPILE)gcc
-LD = $(CROSS_COMPILE)ld
CFLAGS = $(CPPFLAGS) -O -fno-builtin
-OBJCOPY = $(CROSS_COMPILE)objcopy
OBJCOPY_ARGS = -O aixcoff-rs6000 -R .stab -R .stabstr -R .comment
COFF_LD_ARGS = -e _start -T ld.script -Ttext 500000 -Tdata 510000 -Bstatic
CHRP_LD_ARGS = -Ttext 0x00400000
-GZ = gzip -9
COFFOBJS = coffcrt0.o start.o coffmain.o misc.o string.o zlib.o image.o
CHRPOBJS = crt0.o start.o chrpmain.o misc.o string.o zlib.o image.o
#
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
#
# CONFIG_UID16 is not set
#
# General setup
#
-# CONFIG_PCI is not set
CONFIG_PCI=y
CONFIG_PCI=y
CONFIG_NET=y
# CONFIG_BINFMT_MISC is not set
# CONFIG_PCI_NAMES is not set
# CONFIG_HOTPLUG is not set
+
+#
+# Parallel port support
+#
# CONFIG_PARPORT is not set
CONFIG_VGA_CONSOLE=y
CONFIG_FB=y
CONFIG_PMAC_PBOOK=y
CONFIG_MAC_FLOPPY=y
CONFIG_MAC_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
CONFIG_ADB=y
CONFIG_ADB_CUDA=y
CONFIG_ADB_MACIO=y
# Plug and Play configuration
#
# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
#
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
# CONFIG_BLK_DEV_HD_IDE is not set
CONFIG_BLK_DEV_IDEDISK=y
# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_BLK_DEV_IDECS is not set
CONFIG_BLK_DEV_IDECD=y
# CONFIG_BLK_DEV_IDETAPE is not set
CONFIG_BLK_DEV_IDEFLOPPY=y
CONFIG_BLK_DEV_IDESCSI=y
-
-#
-# IDE chipset support/bugfixes
-#
# CONFIG_BLK_DEV_CMD640 is not set
# CONFIG_BLK_DEV_RZ1000 is not set
CONFIG_BLK_DEV_IDEPCI=y
CONFIG_IDEDMA_AUTO=y
# CONFIG_IDE_CHIPSETS is not set
# CONFIG_BLK_CPQ_DA is not set
-
-#
-# Additional Block Devices
-#
CONFIG_BLK_DEV_LOOP=y
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
# CONFIG_IP_MROUTE is not set
CONFIG_IP_ALIAS=y
CONFIG_SYN_COOKIES=y
-
-#
-# (it is safe to leave these untouched)
-#
CONFIG_SKB_LARGE=y
# CONFIG_IPV6 is not set
# CONFIG_KHTTPD is not set
# CONFIG_ATM is not set
-
-#
-#
-#
# CONFIG_IPX is not set
CONFIG_ATALK=m
# CONFIG_DECNET is not set
# SCSI support
#
CONFIG_SCSI=y
-
-#
-# SCSI support type (disk, tape, CD-ROM)
-#
CONFIG_BLK_DEV_SD=y
CONFIG_SD_EXTRA_DEVS=40
CONFIG_CHR_DEV_ST=y
CONFIG_BLK_DEV_SR_VENDOR=y
CONFIG_SR_EXTRA_DEVS=2
CONFIG_CHR_DEV_SG=y
-
-#
-# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
-#
# CONFIG_SCSI_DEBUG_QUEUES is not set
# CONFIG_SCSI_MULTI_LUN is not set
CONFIG_SCSI_CONSTANTS=y
# ARCnet devices
#
# CONFIG_ARCNET is not set
+
+#
+# Appletalk devices
+#
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+# CONFIG_IPDDP is not set
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_ADAPTEC_STARFIRE is not set
# CONFIG_AC3200 is not set
# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
CONFIG_DE4X5=y
# CONFIG_TULIP is not set
# CONFIG_DGRS is not set
# CONFIG_SK98LIN is not set
# CONFIG_FDDI is not set
# CONFIG_HIPPI is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_LTPC is not set
-# CONFIG_COPS is not set
-# CONFIG_IPDDP is not set
CONFIG_PPP=y
# CONFIG_PPP_ASYNC is not set
# CONFIG_PPP_SYNC_TTY is not set
# CONFIG_NET_RADIO is not set
#
-# Token Ring driver support
+# Token Ring devices
#
# CONFIG_TR is not set
# CONFIG_NET_FC is not set
CONFIG_FB_MATROX_G100=y
# CONFIG_FB_MATROX_MULTIHEAD is not set
CONFIG_FB_ATY=y
-# CONFIG_FB_ATY128 is not set
+CONFIG_FB_ATY128=y
CONFIG_FB_3DFX=y
# CONFIG_FB_VIRTUAL is not set
# CONFIG_FBCON_ADVANCED is not set
# CONFIG_WATCHDOG is not set
CONFIG_NVRAM=y
# CONFIG_RTC is not set
-# CONFIG_EFI_RTC is not set
#
# Video For Linux
#
# CONFIG_FTAPE is not set
# CONFIG_DRM is not set
+# CONFIG_DRM_TDFX is not set
# CONFIG_AGP is not set
#
# USB support
#
CONFIG_USB=y
-
-#
-# USB Controllers
-#
# CONFIG_USB_UHCI is not set
# CONFIG_USB_UHCI_ALT is not set
CONFIG_USB_OHCI=y
-
-#
-# Miscellaneous USB options
-#
# CONFIG_USB_DEVICEFS is not set
-
-#
-# USB Devices
-#
# CONFIG_USB_PRINTER is not set
# CONFIG_USB_SCANNER is not set
# CONFIG_USB_AUDIO is not set
# CONFIG_USB_OV511 is not set
# CONFIG_USB_DC2XX is not set
# CONFIG_USB_STORAGE is not set
+# CONFIG_USB_USS720 is not set
# CONFIG_USB_DABUSB is not set
# CONFIG_USB_PLUSB is not set
# CONFIG_USB_PEGASUS is not set
# CONFIG_USB_RIO500 is not set
-
-#
-# USB HID
-#
# CONFIG_USB_HID is not set
CONFIG_USB_KBD=y
CONFIG_USB_MOUSE=y
# CONFIG_HPFS_FS is not set
CONFIG_PROC_FS=y
# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_DEBUG is not set
CONFIG_DEVPTS_FS=y
# CONFIG_QNX4FS_FS is not set
# CONFIG_ROMFS_FS is not set
#
# CONFIG_CODA_FS is not set
CONFIG_NFS_FS=y
+# CONFIG_ROOT_NFS is not set
CONFIG_NFSD=y
# CONFIG_NFSD_V3 is not set
CONFIG_SUNRPC=y
# CONFIG_SOUND_MSNDCLAS is not set
# CONFIG_SOUND_MSNDPIN is not set
CONFIG_SOUND_OSS=y
-# CONFIG_SOUND_DMAP is not set
# CONFIG_SOUND_AD1816 is not set
# CONFIG_SOUND_SGALAXY is not set
CONFIG_SOUND_CS4232=m
# CONFIG_SOUND_NM256 is not set
# CONFIG_SOUND_MAD16 is not set
# CONFIG_SOUND_PAS is not set
+# CONFIG_PAS_JOYSTICK is not set
# CONFIG_SOUND_PSS is not set
+# CONFIG_PSS_HAVE_BOOT is not set
# CONFIG_SOUND_SOFTOSS is not set
# CONFIG_SOUND_SB is not set
# CONFIG_SOUND_WAVEFRONT is not set
#
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
#
# CONFIG_UID16 is not set
#
# General setup
#
-# CONFIG_PCI is not set
CONFIG_PCI=y
CONFIG_PCI=y
CONFIG_NET=y
# CONFIG_BINFMT_MISC is not set
# CONFIG_PCI_NAMES is not set
# CONFIG_HOTPLUG is not set
+
+#
+# Parallel port support
+#
# CONFIG_PARPORT is not set
CONFIG_VGA_CONSOLE=y
CONFIG_FB=y
CONFIG_PMAC_PBOOK=y
CONFIG_MAC_FLOPPY=y
CONFIG_MAC_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
CONFIG_ADB=y
CONFIG_ADB_CUDA=y
CONFIG_ADB_MACIO=y
# Plug and Play configuration
#
# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
#
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
# CONFIG_BLK_DEV_HD_IDE is not set
CONFIG_BLK_DEV_IDEDISK=y
# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_BLK_DEV_IDECS is not set
CONFIG_BLK_DEV_IDECD=y
# CONFIG_BLK_DEV_IDETAPE is not set
CONFIG_BLK_DEV_IDEFLOPPY=y
CONFIG_BLK_DEV_IDESCSI=y
-
-#
-# IDE chipset support/bugfixes
-#
# CONFIG_BLK_DEV_CMD640 is not set
# CONFIG_BLK_DEV_RZ1000 is not set
CONFIG_BLK_DEV_IDEPCI=y
CONFIG_IDEDMA_AUTO=y
# CONFIG_IDE_CHIPSETS is not set
# CONFIG_BLK_CPQ_DA is not set
-
-#
-# Additional Block Devices
-#
CONFIG_BLK_DEV_LOOP=y
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
# CONFIG_IP_MROUTE is not set
CONFIG_IP_ALIAS=y
CONFIG_SYN_COOKIES=y
-
-#
-# (it is safe to leave these untouched)
-#
CONFIG_SKB_LARGE=y
# CONFIG_IPV6 is not set
# CONFIG_KHTTPD is not set
# CONFIG_ATM is not set
-
-#
-#
-#
# CONFIG_IPX is not set
CONFIG_ATALK=m
# CONFIG_DECNET is not set
# SCSI support
#
CONFIG_SCSI=y
-
-#
-# SCSI support type (disk, tape, CD-ROM)
-#
CONFIG_BLK_DEV_SD=y
CONFIG_SD_EXTRA_DEVS=40
CONFIG_CHR_DEV_ST=y
CONFIG_BLK_DEV_SR_VENDOR=y
CONFIG_SR_EXTRA_DEVS=2
CONFIG_CHR_DEV_SG=y
-
-#
-# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
-#
# CONFIG_SCSI_DEBUG_QUEUES is not set
# CONFIG_SCSI_MULTI_LUN is not set
CONFIG_SCSI_CONSTANTS=y
# ARCnet devices
#
# CONFIG_ARCNET is not set
+
+#
+# Appletalk devices
+#
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+# CONFIG_IPDDP is not set
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_ADAPTEC_STARFIRE is not set
# CONFIG_AC3200 is not set
# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
CONFIG_DE4X5=y
# CONFIG_TULIP is not set
# CONFIG_DGRS is not set
# CONFIG_SK98LIN is not set
# CONFIG_FDDI is not set
# CONFIG_HIPPI is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_LTPC is not set
-# CONFIG_COPS is not set
-# CONFIG_IPDDP is not set
CONFIG_PPP=y
# CONFIG_PPP_ASYNC is not set
# CONFIG_PPP_SYNC_TTY is not set
# CONFIG_NET_RADIO is not set
#
-# Token Ring driver support
+# Token Ring devices
#
# CONFIG_TR is not set
# CONFIG_NET_FC is not set
CONFIG_FB_MATROX_G100=y
# CONFIG_FB_MATROX_MULTIHEAD is not set
CONFIG_FB_ATY=y
-# CONFIG_FB_ATY128 is not set
+CONFIG_FB_ATY128=y
CONFIG_FB_3DFX=y
# CONFIG_FB_VIRTUAL is not set
# CONFIG_FBCON_ADVANCED is not set
# CONFIG_WATCHDOG is not set
CONFIG_NVRAM=y
# CONFIG_RTC is not set
-# CONFIG_EFI_RTC is not set
#
# Video For Linux
#
# CONFIG_FTAPE is not set
# CONFIG_DRM is not set
+# CONFIG_DRM_TDFX is not set
# CONFIG_AGP is not set
#
# USB support
#
CONFIG_USB=y
-
-#
-# USB Controllers
-#
# CONFIG_USB_UHCI is not set
# CONFIG_USB_UHCI_ALT is not set
CONFIG_USB_OHCI=y
-
-#
-# Miscellaneous USB options
-#
# CONFIG_USB_DEVICEFS is not set
-
-#
-# USB Devices
-#
# CONFIG_USB_PRINTER is not set
# CONFIG_USB_SCANNER is not set
# CONFIG_USB_AUDIO is not set
# CONFIG_USB_OV511 is not set
# CONFIG_USB_DC2XX is not set
# CONFIG_USB_STORAGE is not set
+# CONFIG_USB_USS720 is not set
# CONFIG_USB_DABUSB is not set
# CONFIG_USB_PLUSB is not set
# CONFIG_USB_PEGASUS is not set
# CONFIG_USB_RIO500 is not set
-
-#
-# USB HID
-#
# CONFIG_USB_HID is not set
CONFIG_USB_KBD=y
CONFIG_USB_MOUSE=y
# CONFIG_HPFS_FS is not set
CONFIG_PROC_FS=y
# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_DEBUG is not set
CONFIG_DEVPTS_FS=y
# CONFIG_QNX4FS_FS is not set
# CONFIG_ROMFS_FS is not set
#
# CONFIG_CODA_FS is not set
CONFIG_NFS_FS=y
+# CONFIG_ROOT_NFS is not set
CONFIG_NFSD=y
# CONFIG_NFSD_V3 is not set
CONFIG_SUNRPC=y
# CONFIG_SOUND_MSNDCLAS is not set
# CONFIG_SOUND_MSNDPIN is not set
CONFIG_SOUND_OSS=y
-# CONFIG_SOUND_DMAP is not set
# CONFIG_SOUND_AD1816 is not set
# CONFIG_SOUND_SGALAXY is not set
CONFIG_SOUND_CS4232=m
# CONFIG_SOUND_NM256 is not set
# CONFIG_SOUND_MAD16 is not set
# CONFIG_SOUND_PAS is not set
+# CONFIG_PAS_JOYSTICK is not set
# CONFIG_SOUND_PSS is not set
+# CONFIG_PSS_HAVE_BOOT is not set
# CONFIG_SOUND_SOFTOSS is not set
# CONFIG_SOUND_SB is not set
# CONFIG_SOUND_WAVEFRONT is not set
}
freq *= 30;
divisor = 30;
- printk("time_init: decrementer frequency = %lu/%d (%d MHz)\n", freq,
+ printk("time_init: decrementer frequency = %lu/%d (%ld MHz)\n", freq,
divisor, (freq/divisor)>>20);
decrementer_count = freq / HZ / divisor;
count_period_num = divisor;
err = parse_hex_value(buffer, count, &new_value);
-#if CONFIG_SMP
+#if 0/*CONFIG_SMP*/
/*
* Do not allow disabling IRQs completely - it's a too easy
* way to make the system unusable accidentally :-) At least
#define check_arg_cpu(cpu) do {} while (0)
#endif
-static void no_action(int ir1, void *dev, struct pt_regs *regs)
+void no_action(int ir1, void *dev, struct pt_regs *regs)
{
}
NumSources = 0x10;
#else
/* for non-distributed OpenPIC implementations it's in the IDU -- Cort */
- ISU = OpenPIC->Source;
+ ISU = (OpenPIC_Source *)OpenPIC->Source;
#endif
}
static void clearscreen(void);
static void flushscreen(void);
-void prom_drawchar(char c);
-void prom_drawstring(const char *c);
-void prom_drawhex(unsigned long v);
static void scrollscreen(void);
static void draw_byte(unsigned char c, long locX, long locY);
TFTPIMAGE=/tftpboot/zImage.mbx
ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00100000
-GZIP_FLAGS = -v9
OBJECTS := head.o misc.o ../coffboot/zlib.o m8xx_tty.o
CFLAGS = $(CPPFLAGS) -O2 -DSTDC_HEADERS -fno-builtin -DCONFIG_8xx
-OBJCOPY = $(CROSS_COMPILE)objcopy
OBJCOPY_ARGS = -O elf32-powerpc
ifeq ($(CONFIG_MBX),y)
HOSTCFLAGS = -O -I$(TOPDIR)/include
-CC = $(CROSS_COMPILE)gcc
-LD = $(CROSS_COMPILE)ld
-OBJCOPY = $(CROSS_COMPILE)objcopy
-OBJDUMP = $(CROSS_COMPILE)objdump
-
GZIP = gzip -vf9
RM = rm -f
MKEVIMG = mkevimg -l
#include <linux/sched.h>
#include <asm/ptrace.h>
#include <asm/string.h>
+#include <asm/prom.h>
#include "nonstdio.h"
#include "privinst.h"
static struct bpt *at_breakpoint(unsigned pc);
static void bpt_cmds(void);
static void cacheflush(void);
-static char *pretty_lookup_name(unsigned long addr);
+static char *pretty_print_addr(unsigned long addr);
static char *lookup_name(unsigned long addr);
extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned);
prom_drawstring(" msr="); prom_drawhex(excp->msr);
prom_drawstring(" trap="); prom_drawhex(excp->trap);
prom_drawstring(" sp="); prom_drawhex(excp->gpr[1]);
- sp = &excp->gpr[0];
+ sp = (unsigned *)&excp->gpr[0];
for (i = 0; i < 32; ++i) {
if ((i & 7) == 0)
prom_drawstring("\n");
void
excprint(struct pt_regs *fp)
{
- printf("vector: %x at pc = %x %s",
- fp->trap, fp->nip, pretty_lookup_name(fp->nip));
- printf(", msr = %x, sp = %x [%x]\n",
- fp->msr, fp->gpr[1], fp);
+ printf("vector: %x at pc = %x",
+ fp->trap, fp->nip);
+ printf(", lr = %x, msr = %x, sp = %x [%x]\n",
+ fp->link, fp->msr, fp->gpr[1], fp);
if (fp->trap == 0x300 || fp->trap == 0x600)
printf("dar = %x, dsisr = %x\n", fp->dar, fp->dsisr);
if (current)
lineptr = str;
}
-/*
- * We use this array a lot here. We assume we don't have multiple
- * instances of xmon running and that we don't use the return value of
- * any functions other than printing them.
- * -- Cort
- */
-char last[64];
-static char *pretty_lookup_name(unsigned long addr)
+static char *pretty_print_addr(unsigned long addr)
{
+ printf("%08x", addr);
if ( lookup_name(addr) )
- {
- sprintf(last, " (%s)", lookup_name(addr));
- return last;
- }
- else
- return NULL;
+ printf(" %s", lookup_name(addr) );
+ return NULL;
}
-
static char *lookup_name(unsigned long addr)
{
extern char *sysmap;
if ( !sysmap || !sysmap_size )
return NULL;
-
- /* adjust if addr is relative to kernelbase */
- if ( addr < PAGE_OFFSET )
- addr += PAGE_OFFSET;
-
+return NULL;
+#if 0
cmp = simple_strtoul(c, &c, 8);
strcpy( last, strsep( &c, "\n"));
while ( c < (sysmap+sysmap_size) )
break;
strcpy( last, strsep( &c, "\n"));
}
- return last;
+ return NULLlast;
+#endif
}
source drivers/char/pcmcia/Config.in
fi
-source drivers/misc/Config.in
+#source drivers/misc/Config.in
source fs/Config.in
#
# CONFIG_UNIX98_PTYS is not set
-#
-# Misc devices
-#
-
#
# Filesystems
#
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_MISC=m
CONFIG_SUNOS_EMUL=y
+
+#
+# Parallel port support
+#
# CONFIG_PARPORT is not set
# CONFIG_PRINTER is not set
CONFIG_DECNET=m
CONFIG_DECNET_SIOCGIFCONF=y
# CONFIG_DECNET_ROUTER is not set
-CONFIG_DECNET_RAW=y
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_BRIDGE is not set
-/* $Id: sparc_ksyms.c,v 1.93 2000/02/26 11:02:45 anton Exp $
+/* $Id: sparc_ksyms.c,v 1.94 2000/02/28 04:00:53 anton Exp $
* arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
EXPORT_SYMBOL_PRIVATE(_rw_write_enter);
#endif
#ifdef __SMP__
-#ifdef DEBUG_IRQLOCK
EXPORT_SYMBOL(__global_save_flags);
EXPORT_SYMBOL(__global_restore_flags);
EXPORT_SYMBOL(__global_sti);
EXPORT_SYMBOL(__global_cli);
-#else
-EXPORT_SYMBOL_PRIVATE(_global_restore_flags);
-EXPORT_SYMBOL_PRIVATE(_global_sti);
-EXPORT_SYMBOL_PRIVATE(_global_cli);
-#endif
#endif
/* rw semaphores */
-/* $Id: sys_sunos.c,v 1.113 2000/02/16 07:31:29 davem Exp $
+/* $Id: sys_sunos.c,v 1.114 2000/03/07 22:27:27 davem Exp $
* sys_sunos.c: SunOS specific syscall compatibility support.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
-# $Id: Makefile,v 1.31 1999/12/28 11:50:39 jj Exp $
+# $Id: Makefile,v 1.32 2000/02/28 04:00:48 anton Exp $
# Makefile for Sparc library files..
#
copy_user.o locks.o atomic.o bitops.o debuglocks.o lshrdi3.o \
ashldi3.o rwsem.o
-ifdef CONFIG_SMP
-OBJS += irqlock.o
-endif
-
lib.a: $(OBJS)
$(AR) rcs lib.a $(OBJS)
sync
+++ /dev/null
-/* $Id: irqlock.S,v 1.5 1999/04/20 13:22:37 anton Exp $
- * irqlock.S: High performance IRQ global locking and interrupt entry.
- *
- * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
- */
-
-#include <asm/psr.h>
-#include <asm/smp.h>
-
- .text
- .align 4
-
- /* Weird calling conventions... %g7=flags, %g4=%prev_o7
- * Very clever for the __global_sti case, the inline which
- * gets us here clears %g7 and it just works.
- */
- .globl ___global_restore_flags, ___global_sti, ___global_cli
-___global_restore_flags:
- bne,a ___global_cli
- rd %tbr, %g7
- rd %tbr, %g2
-
-___global_sti:
- sethi %hi(global_irq_holder), %g1
- sethi %hi(global_irq_lock), %g3
- srl %g2, 12, %g2
- ldub [%g1 + %lo(global_irq_holder)], %g5
- and %g2, 3, %g2
- cmp %g5, %g2
- bne 1f
- mov NO_PROC_ID, %g5
- stb %g5, [%g1 + %lo(global_irq_holder)]
- stb %g0, [%g3 + %lo(global_irq_lock)]
-1:
- rd %psr, %g3
- andcc %g7, 2, %g0
- bne,a 1f
- or %g3, PSR_PIL, %g3
- andn %g3, PSR_PIL, %g3
-1:
- wr %g3, 0x0, %psr
- nop
-__global_cli_out: ! All togther now... "fuuunnnnn"
- retl
- mov %g4, %o7
-
-__spin_on_global_irq_lock:
- orcc %g2, 0x0, %g0
- bne,a __spin_on_global_irq_lock
- ldub [%g1], %g2
- b,a 1f
-
- /* This is a royal pain in the ass to make fast... 8-( */
-___global_cli:
- sethi %hi(global_irq_lock), %g5
- srl %g7, 12, %g7
- sethi %hi(global_irq_holder), %g3
- and %g7, 3, %g7
- ldub [%g3 + %lo(global_irq_holder)], %g1
- rd %psr, %g2
- cmp %g1, %g7
- or %g2, PSR_PIL, %g2
- be __global_cli_out
- wr %g2, 0x0, %psr ! XXX some sparcs may choke on this...
- sethi %hi(local_irq_count), %g3
- or %g3, %lo(local_irq_count), %g3
- or %g5, %lo(global_irq_lock), %g1
-1:
- ldstub [%g1], %g2
- orcc %g2, 0x0, %g0
- bne,a __spin_on_global_irq_lock
- ldub [%g1], %g2
-__wait_on_irq:
- sll %g7, 2, %g7
- ld [%g3 + %g7], %g2
- sethi %hi(global_irq_count), %g1
- or %g1, %lo(global_irq_count), %g1
- srl %g7, 2, %g7
- ld [%g1], %g5
- sra %g5, 8, %g5
-__wait_on_irq_loop:
- cmp %g5, %g2
- sethi %hi(global_irq_holder), %g3
- be,a __global_cli_out ! Mamamia, Mamamia, this is the fast path
- stb %g7, [%g3 + %lo(global_irq_holder)]
-1:
- ldstub [%g1 + 3], %g3
- orcc %g3, 0x0, %g0
- bne 1b
- ld [%g1], %g3
- sra %g3, 8, %g3
- sub %g3, %g2, %g3
- sll %g3, 8, %g3
- st %g3, [%g1]
- sethi %hi(global_irq_lock), %g3
- stb %g0, [%g3 + %lo(global_irq_lock)]
-0:
- ld [%g1], %g5
-9:
- ldub [%g3 + %lo(global_irq_lock)], %g3
- sra %g5, 8, %g5
- orcc %g3, %g5, %g0
- bne 0b
- sethi %hi(global_irq_lock), %g3
- ldstub [%g3 + %lo(global_irq_lock)], %g5
- orcc %g5, 0x0, %g0
- bne,a 9b
- ld [%g1], %g5
-1:
- ldstub [%g1 + 3], %g3
- orcc %g3, 0x0, %g0
- bne 1b
- ld [%g1], %g3
- sra %g3, 8, %g3
- add %g3, %g2, %g5
- sll %g5, 8, %g3
- b __wait_on_irq_loop
- st %g3, [%g1]
-
-#if 0 /* XXX I'm not delirious enough to debug this yet. */
- add %o7, (8 + (__wait_on_irq_loop - . - 4)), %o7 ! AIEEEEE
-#endif
-/* $Id: init.c,v 1.81 2000/02/26 11:59:31 anton Exp $
+/* $Id: init.c,v 1.83 2000/03/07 23:12:35 anton Exp $
* linux/arch/sparc/mm/init.c
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
int i;
/* Limit maximum memory until we implement highmem for sparc */
- if (cmdline_memory_size > 0x9000000)
- cmdline_memory_size = 0x9000000;
+ if (!cmdline_memory_size || cmdline_memory_size > 0x0d000000)
+ cmdline_memory_size = 0x0d000000;
/* XXX It is a bit ambiguous here, whether we should
* XXX treat the user specified mem=xxx as total wanted
sp_banks[i].num_bytes;
if (cmdline_memory_size) {
if (end_of_phys_memory > cmdline_memory_size) {
- if (cmdline_memory_size > sp_banks[i].base_addr) {
+ if (cmdline_memory_size < sp_banks[i].base_addr) {
end_of_phys_memory =
sp_banks[i-1].base_addr +
sp_banks[i-1].num_bytes;
CONFIG_BINFMT_MISC=m
# CONFIG_SUNOS_EMUL is not set
CONFIG_SOLARIS_EMUL=m
+
+#
+# Parallel port support
+#
CONFIG_PARPORT=m
CONFIG_PARPORT_PC=m
CONFIG_PARPORT_PC_FIFO=y
CONFIG_DECNET=m
CONFIG_DECNET_SIOCGIFCONF=y
# CONFIG_DECNET_ROUTER is not set
-CONFIG_DECNET_RAW=y
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_BRIDGE is not set
stxa %6, [%0+%8] %3
membar #Sync
stxa %%g0, [%7] %3
+ membar #Sync
mov 0x20, %%g1
ldxa [%%g1] 0x7f, %%g0
membar #Sync"
-/* $Id: sys_sparc32.c,v 1.133 2000/03/01 02:53:33 davem Exp $
+/* $Id: sys_sparc32.c,v 1.134 2000/03/07 22:27:30 davem Exp $
* sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
*
* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
-/* $Id: sys_sunos32.c,v 1.39 2000/02/16 07:31:37 davem Exp $
+/* $Id: sys_sunos32.c,v 1.40 2000/03/07 22:27:31 davem Exp $
* sys_sunos32.c: SunOS binary compatability layer on sparc64.
*
* Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
-/* $Id: init.c,v 1.147 2000/03/03 23:48:44 davem Exp $
+/* $Id: init.c,v 1.148 2000/03/07 07:08:31 anton Exp $
* arch/sparc64/mm/init.c
*
* Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
sp_banks[i].num_bytes;
if (cmdline_memory_size) {
if (end_of_phys_memory > cmdline_memory_size) {
- if (cmdline_memory_size > sp_banks[i].base_addr) {
+ if (cmdline_memory_size < sp_banks[i].base_addr) {
end_of_phys_memory =
sp_banks[i-1].base_addr +
sp_banks[i-1].num_bytes;
static int report_statvfs(struct inode *inode, u32 buf)
{
struct statfs s;
- mm_segment_t old_fs = get_fs();
int error;
struct sol_statvfs *ss = (struct sol_statvfs *)A(buf);
- if (!inode->i_sb)
- return -ENODEV;
- if (!inode->i_sb->s_op->statfs)
- return -ENOSYS;
- set_fs (KERNEL_DS);
- error = inode->i_sb->s_op->statfs(inode->i_sb, &s, sizeof(struct statfs));
- set_fs (old_fs);
+ error = vfs_statfs(inode->i_sb, &s);
if (!error) {
const char *p = inode->i_sb->s_type->name;
int i = 0;
static int report_statvfs64(struct inode *inode, u32 buf)
{
struct statfs s;
- mm_segment_t old_fs = get_fs();
int error;
struct sol_statvfs64 *ss = (struct sol_statvfs64 *)A(buf);
- if (!inode->i_sb)
- return -ENODEV;
- if (!inode->i_sb->s_op->statfs)
- return -ENOSYS;
- set_fs (KERNEL_DS);
- error = inode->i_sb->s_op->statfs(inode->i_sb, &s, sizeof(struct statfs));
- set_fs (old_fs);
+ error = vfs_statfs(inode->i_sb, &s);
if (!error) {
const char *p = inode->i_sb->s_type->name;
int i = 0;
int max_segments;
DAC960_Controller_T * Controller = q->queuedata;
int total_segments = req->nr_segments + next->nr_segments;
+ int same_segment;
max_segments = Controller->MaxSegmentsPerRequest[MINOR(req->rq_dev)];
if (__max_segments < max_segments)
max_segments = __max_segments;
+ same_segment = 0;
if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data)
{
total_segments--;
- q->nr_segments--;
+ same_segment = 1;
}
if (total_segments > max_segments)
return 0;
+ q->nr_segments -= same_segment;
req->nr_segments = total_segments;
return 1;
}
IDE_OBJS += ide-pci.o
endif
+ifeq ($(CONFIG_BLK_DEV_ISAPNP),y)
+IDE_OBJS += ide-pnp.o
+endif
+
ifeq ($(CONFIG_BLK_DEV_IDE_PMAC),y)
IDE_OBJS += ide-pmac.o
endif
#include "ide_modes.h"
-#define ACARD_DEBUG_DRIVE_INFO 1
+#define ACARD_DEBUG_DRIVE_INFO 0
-#define DISPLAY_AEC6210_TIMINGS
+#undef DISPLAY_AEC6210_TIMINGS
#if defined(DISPLAY_AEC6210_TIMINGS) && defined(CONFIG_PROC_FS)
#include <linux/stat.h>
#include <asm/io.h>
#include "ide_modes.h"
+#ifndef SPLIT_BYTE
+#define SPLIT_BYTE(B,H,L) ((H)=(B>>4), (L)=(B-((B>>4)<<4)))
+#endif
+
#define CMD_DEBUG 0
-#undef NO_WRITE
#if CMD_DEBUG
#define cmdprintk(x...) printk(##x)
#define ARTTIM23 0x57
#define ARTTIM23_DIS_RA2 0x04
#define ARTTIM23_DIS_RA3 0x08
+#define ARTTIM2 0x57
+#define ARTTIM3 0x57
#define DRWTIM23 0x58
#define DRWTIM2 0x58
#define BRST 0x59
#define DRWTIM3 0x5b
+#define BMIDECR0 0x70
#define MRDMODE 0x71
+#define BMIDESR0 0x72
+#define UDIDETCR0 0x73
+#define DTPR0 0x74
+#define BMIDECR1 0x78
+#define BMIDECSR 0x79
+#define BMIDESR1 0x7A
+#define UDIDETCR1 0x7B
+#define DTPR1 0x7C
-#define DISPLAY_CMD64X_TIMINGS
+#undef DISPLAY_CMD64X_TIMINGS
#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
#include <linux/stat.h>
static int cmd64x_get_info (char *buffer, char **addr, off_t offset, int count)
{
char *p = buffer;
- u32 bibma = bmide_dev->resource[4].start;
- u8 c0 = 0, c1 = 0;
+ u8 reg53 = 0, reg54 = 0, reg55 = 0, reg56 = 0; /* primary */
+ u8 reg57 = 0, reg58 = 0, reg5b; /* secondary */
+ u8 reg72 = 0, reg73 = 0; /* primary */
+ u8 reg7a = 0, reg7b = 0; /* secondary */
+ u8 hi_byte = 0, lo_byte = 0;
switch(bmide_dev->device) {
case PCI_DEVICE_ID_CMD_648:
p += sprintf(p, "\n CMD64? Chipse.\n");
break;
}
+ (void) pci_read_config_byte(bmide_dev, ARTTIM0, ®53);
+ (void) pci_read_config_byte(bmide_dev, DRWTIM0, ®54);
+ (void) pci_read_config_byte(bmide_dev, ARTTIM1, ®55);
+ (void) pci_read_config_byte(bmide_dev, DRWTIM1, ®56);
+ (void) pci_read_config_byte(bmide_dev, ARTTIM2, ®57);
+ (void) pci_read_config_byte(bmide_dev, DRWTIM2, ®58);
+ (void) pci_read_config_byte(bmide_dev, DRWTIM3, ®5b);
+ (void) pci_read_config_byte(bmide_dev, BMIDESR0, ®72);
+ (void) pci_read_config_byte(bmide_dev, UDIDETCR0, ®73);
+ (void) pci_read_config_byte(bmide_dev, BMIDESR1, ®7a);
+ (void) pci_read_config_byte(bmide_dev, UDIDETCR1, ®7b);
- /*
- * at that point bibma+0x2 et bibma+0xa are byte registers
- * to investigate:
- */
- c0 = inb_p((unsigned short)bibma + 0x02);
- c1 = inb_p((unsigned short)bibma + 0x0a);
p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
p += sprintf(p, " %sabled %sabled\n",
- (c0&0x80) ? "dis" : " en",
- (c1&0x80) ? "dis" : " en");
+ (reg72&0x80) ? "dis" : " en", (reg7a&0x80) ? "dis" : " en");
p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
p += sprintf(p, "DMA enabled: %s %s %s %s\n",
- (c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
- (c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
-
- p += sprintf(p, "UDMA\n");
- p += sprintf(p, "DMA\n");
+ (reg72&0x20) ? "yes" : "no ", (reg72&0x40) ? "yes" : "no ", (reg7a&0x20) ? "yes" : "no ", (reg7a&0x40) ? "yes" : "no " );
+ p += sprintf(p, "UDMA enabled: %s %s %s %s\n",
+ (reg73&0x01) ? "yes" : "no ", (reg73&0x02) ? "yes" : "no ", (reg7b&0x01) ? "yes" : "no ", (reg7b&0x02) ? "yes" : "no " );
+ p += sprintf(p, "UDMA enabled: %s %s %s %s\n",
+ (reg73&0x15) ? "4" : (reg73&0x25) ? "3" : (reg73&0x11) ? "2" : (reg73&0x21) ? "1" : (reg73&0x31) ? "0" : "X",
+ (reg73&0x4A) ? "4" : (reg73&0x8A) ? "3" : (reg73&0x42) ? "2" : (reg73&0x82) ? "1" : (reg73&0xC2) ? "0" : "X",
+ (reg7b&0x15) ? "4" : (reg7b&0x25) ? "3" : (reg7b&0x11) ? "2" : (reg7b&0x21) ? "1" : (reg7b&0x31) ? "0" : "X",
+ (reg7b&0x4A) ? "4" : (reg7b&0x8A) ? "3" : (reg7b&0x42) ? "2" : (reg7b&0x82) ? "1" : (reg7b&0xC2) ? "0" : "X" );
+ p += sprintf(p, "DMA enabled: %s %s %s %s\n",
+ (reg73&0x10) ? "2" : (reg73&0x20) ? "1" : (reg73&0x30) ? "0" : "X",
+ (reg73&0x40) ? "2" : (reg73&0x80) ? "1" : (reg73&0xC0) ? "0" : "X",
+ (reg7b&0x10) ? "2" : (reg7b&0x20) ? "1" : (reg7b&0x30) ? "0" : "X",
+ (reg7b&0x40) ? "2" : (reg7b&0x80) ? "1" : (reg7b&0xC0) ? "0" : "X" );
p += sprintf(p, "PIO\n");
+ SPLIT_BYTE(reg53, hi_byte, lo_byte);
+ p += sprintf(p, "ARTTIM0 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg53, hi_byte, lo_byte);
+ SPLIT_BYTE(reg54, hi_byte, lo_byte);
+ p += sprintf(p, "DRWTIM0 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg54, hi_byte, lo_byte);
+ SPLIT_BYTE(reg55, hi_byte, lo_byte);
+ p += sprintf(p, "ARTTIM1 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg55, hi_byte, lo_byte);
+ SPLIT_BYTE(reg56, hi_byte, lo_byte);
+ p += sprintf(p, "DRWTIM1 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg56, hi_byte, lo_byte);
+ SPLIT_BYTE(reg57, hi_byte, lo_byte);
+ p += sprintf(p, "ARTTIM23 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg57, hi_byte, lo_byte);
+ SPLIT_BYTE(reg58, hi_byte, lo_byte);
+ p += sprintf(p, "DRWTIM2 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg58, hi_byte, lo_byte);
+ SPLIT_BYTE(reg5b, hi_byte, lo_byte);
+ p += sprintf(p, "DRWTIM3 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg5b, hi_byte, lo_byte);
+ SPLIT_BYTE(reg73, hi_byte, lo_byte);
+ p += sprintf(p, "UDIDETCR0 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg73, hi_byte, lo_byte);
+ SPLIT_BYTE(reg7b, hi_byte, lo_byte);
+ p += sprintf(p, "UDIDETCR1 = 0x%02x, HI = 0x%02x, LOW = 0x%02x\n", reg7b, hi_byte, lo_byte);
+
return p-buffer; /* => must be less than 4k! */
}
#endif /* defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) */
* and then the active/recovery counts into the DRWTIM reg
*/
(void) pci_read_config_byte(HWIF(drive)->pci_dev, arttim_regs[channel][slave], &temp_b);
-#ifndef NO_WRITE
(void) pci_write_config_byte(HWIF(drive)->pci_dev, arttim_regs[channel][slave],
((byte) setup_count) | (temp_b & 0x3f));
(void) pci_write_config_byte(HWIF(drive)->pci_dev, drwtim_regs[channel][slave],
(byte) ((active_count << 4) | recovery_count));
-#endif
cmdprintk ("Write %x to %x\n", ((byte) setup_count) | (temp_b & 0x3f), arttim_regs[channel][slave]);
cmdprintk ("Write %x to %x\n", (byte) ((active_count << 4) | recovery_count), drwtim_regs[channel][slave]);
__restore_flags(flags);
return;
}
- (void) ide_get_best_pio_mode (drive, mode_wanted, 5, &d);
+ mode_wanted = ide_get_best_pio_mode (drive, mode_wanted, 5, &d);
pio_mode = d.pio_mode;
cycle_time = d.cycle_time;
*/
program_drive_counts (drive, setup_count, active_count, recovery_count);
- printk ("%s: selected cmd646 PIO mode%d (%dns)%s, clocks=%d/%d/%d\n",
- drive->name, pio_mode, cycle_time,
+ cmdprintk("%s: selected cmd646 PIO mode%d : %d (%dns)%s, clocks=%d/%d/%d\n",
+ drive->name, pio_mode, mode_wanted, cycle_time,
d.overridden ? " (overriding vendor mode)" : "",
setup_count, active_count, recovery_count);
}
+static int tune_chipset_for_dma (ide_drive_t *drive, byte speed)
+{
+#if 0
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned long dma_base = hwif->dma_base;
+ byte unit = (drive->select.b.unit & 0x01);
+
+ u8 reg72 = 0, reg73 = 0; /* primary */
+ u8 reg7a = 0, reg7b = 0; /* secondary */
+ u8 pciU = (hwif->channel) ? UDIDETCR1 : UDIDETCR0;
+ u8 pciD = (hwif->channel) ? BMIDESR1 : BMIDESR0;
+ u8 regU = (hwif->channel) ? 2 : 0;
+ u8 regD = (hwif->channel) ? 2 : 0;
+
+ (void) pci_read_config_byte(dev, BMIDESR0, ®72);
+ (void) pci_read_config_byte(dev, UDIDETCR0, ®73);
+ (void) pci_read_config_byte(dev, BMIDESR1, ®7a);
+ (void) pci_read_config_byte(dev, UDIDETCR1, ®7b);
+
+ switch(speed) {
+ case XFER_UDMA_4:
+ pciU = unit ? 0x4A : 0x15;
+ case XFER_UDMA_3:
+ pciU = unit ? 0x8A : 0x25;
+ case XFER_UDMA_2:
+ pciU = unit ? 0x42 : 0x11;
+ case XFER_UDMA_1:
+ pciU = unit ? 0x82 : 0x21;
+ case XFER_UDMA_0:
+ pciU = unit ? 0xC2 : 0x31
+(reg73&0x15)?"4":(reg73&0x25)?"3":(reg73&0x11)?"2":(reg73&0x21)?"1":(reg73&0x31)?"0":"X",
+(reg73&0x4A)?"4":(reg73&0x8A)?"3":(reg73&0x42)?"2":(reg73&0x82)?"1":(reg73&0xC2)?"0":"X",
+(reg7b&0x15)?"4":(reg7b&0x25)?"3":(reg7b&0x11)?"2":(reg7b&0x21)?"1":(reg7b&0x31)?"0":"X",
+(reg7b&0x4A)?"4":(reg7b&0x8A)?"3":(reg7b&0x42)?"2":(reg7b&0x82)?"1":(reg7b&0xC2)?"0":"X",
+
+ case XFER_MW_DMA_2:
+ pciD = unit ? 0x40 : 0x10;
+ case XFER_MW_DMA_1:
+ pciD = unit ? 0x80 : 0x20;
+ case XFER_MW_DMA_0:
+ pciD = unit ? 0xC0 : 0x30;
+ case XFER_SW_DMA_2:
+ case XFER_SW_DMA_1:
+ case XFER_SW_DMA_0:
+(reg73&0x10)?"2":(reg73&0x20)?"1":(reg73&0x30)?"0":"X",
+(reg73&0x40)?"2":(reg73&0x80)?"1":(reg73&0xC0)?"0":"X",
+(reg7b&0x10)?"2":(reg7b&0x20)?"1":(reg7b&0x30)?"0":"X",
+(reg7b&0x40)?"2":(reg7b&0x80)?"1":(reg7b&0xC0)?"0":"X" );
+
+ default:
+ return 1;
+ }
+
+ (void) ide_config_drive_speed(drive, speed);
+ outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+#endif
+ return 0;
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed)
+{
+ byte speed = 0x00;
+ byte set_pio = ide_get_best_pio_mode(drive, 4, 5, NULL);
+
+ cmd64x_tuneproc(drive, set_pio);
+ speed = XFER_PIO_0 + set_pio;
+ if (set_speed)
+ (void) ide_config_drive_speed(drive, speed);
+}
+
static int config_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ultra_66)
{
struct hd_driveid *id = drive->id;
byte unit = (drive->select.b.unit & 0x01);
byte speed = 0x00;
+ byte set_pio = 0x00;
byte udma_timing_bits = 0x00;
byte udma_33 = ((rev >= 0x05) || (ultra_66)) ? 1 : 0;
byte udma_66 = ((id->hw_config & 0x2000) && (hwif->udma_four)) ? 1 : 0;
} else if (id->dma_1word & 0x0001) {
speed = XFER_SW_DMA_0;
} else {
- return ((int) ide_dma_off_quietly);
+ set_pio = 1;
}
+ config_chipset_for_pio(drive, set_pio);
+
+ if (set_pio)
+ return ((int) ide_dma_off_quietly);
+
+#if 1
+ /*
+ * This the alternate access method. :-(
+ * The correct method is to directly setup the pci-config space.
+ */
(void) ide_config_drive_speed(drive, speed);
outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
udma_ctrl |= (udma_timing_bits << (unit * 2));
outb(udma_ctrl, dma_base+3);
}
+#endif
+
+ if (tune_chipset_for_dma(drive, speed))
+ return ((int) ide_dma_off);
rval = (int)( ((id->dma_ultra >> 11) & 3) ? ide_dma_on :
((id->dma_ultra >> 8) & 7) ? ide_dma_on :
return rval;
}
-static void config_chipset_for_pio (ide_drive_t *drive, unsigned int rev)
-{
- cmd64x_tuneproc(drive, 5);
-}
-
static int cmd64x_config_drive_for_dma (ide_drive_t *drive)
{
struct hd_driveid *id = drive->id;
fast_ata_pio:
dma_func = ide_dma_off_quietly;
no_dma_set:
- config_chipset_for_pio(drive, class_rev);
+ config_chipset_for_pio(drive, 1);
}
return HWIF(drive)->dmaproc(dma_func, drive);
}
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
class_rev &= 0xff;
+#if 0
+ if (dev->resource[PCI_ROM_RESOURCE].start)
+ pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
+#endif
+
switch(dev->device) {
case PCI_DEVICE_ID_CMD_643:
break;
* this point.
*/
(void) pci_write_config_byte(dev, MRDMODE, mrdmode | 0x02);
-#if 0
+
/* Set reasonable active/recovery/address-setup values. */
(void) pci_write_config_byte(dev, ARTTIM0, 0x40);
(void) pci_write_config_byte(dev, DRWTIM0, 0x3f);
(void) pci_write_config_byte(dev, ARTTIM1, 0x40);
(void) pci_write_config_byte(dev, DRWTIM1, 0x3f);
+#ifdef __i386__
+ (void) pci_write_config_byte(dev, ARTTIM23, 0x1c);
+#else
(void) pci_write_config_byte(dev, ARTTIM23, 0x5c);
+#endif
(void) pci_write_config_byte(dev, DRWTIM23, 0x3f);
(void) pci_write_config_byte(dev, DRWTIM3, 0x3f);
-#else
- (void) pci_write_config_byte(dev, ARTTIM23, 0x1c);
-#endif
#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
cmd64x_proc = 1;
byte ata66 = 0;
byte mask = (hwif->channel) ? 0x02 : 0x01;
- pci_read_config_byte(hwif->pci_dev, 0x79, &ata66);
+ pci_read_config_byte(hwif->pci_dev, BMIDECSR, &ata66);
return (ata66 & mask) ? 1 : 0;
}
class_rev &= 0xff;
hwif->tuneproc = &cmd64x_tuneproc;
+ hwif->drives[0].autotune = 1;
+ hwif->drives[0].autotune = 1;
- if (!hwif->dma_base) {
- hwif->drives[0].autotune = 1;
- hwif->drives[1].autotune = 1;
+ if (!hwif->dma_base)
return;
- }
switch(dev->device) {
case PCI_DEVICE_ID_CMD_643:
#define SPLIT_BYTE(B,H,L) ((H)=(B>>4), (L)=(B-((B>>4)<<4)))
#endif
-#define HPT343_DEBUG_DRIVE_INFO 1
+#define HPT343_DEBUG_DRIVE_INFO 0
+
+#undef DISPLAY_HPT34X_TIMINGS
-#define DISPLAY_HPT34X_TIMINGS
#if defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS)
#include <linux/stat.h>
#include <linux/proc_fs.h>
#include "ide_modes.h"
-#define DISPLAY_HPT366_TIMINGS
+#undef DISPLAY_HPT366_TIMINGS
#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
#include <linux/stat.h>
/*
* Disable on-chip PIO FIFO/buffer (to avoid problems handling I/O errors later)
*/
+ if (speed >= XFER_MW_DMA_0) {
+ reg2 = (reg2 & ~0xc0000000) | (reg1 & 0xc0000000);
+ } else {
+ reg2 = (reg2 & ~0x30070000) | (reg1 & 0x30070000);
+ }
reg2 &= ~0x80000000;
pci_write_config_dword(HWIF(drive)->pci_dev, regtime, reg2);
struct hd_driveid *id = drive->id;
byte speed = 0x00;
byte reg51h = 0;
- unsigned int reg40 = 0;
int rval;
if ((id->dma_ultra & 0x0010) &&
if (reg51h & 0x80)
pci_write_config_byte(HWIF(drive)->pci_dev, 0x51, reg51h & ~0x80);
#endif /* CONFIG_HPT366_FIP */
-
- /*
- * Preserve existing PIO settings:
- */
- pci_read_config_dword(HWIF(drive)->pci_dev, 0x40, ®40);
- speed = (speed & ~0xc0000000) | (reg40 & 0xc0000000);
-
#if HPT366_DEBUG_DRIVE_INFO
printk("%s: config_chipset_for_dma: speed=0x%04x\n", drive->name, speed);
#endif /* HPT366_DEBUG_DRIVE_INFO */
unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
unsigned short xfer_pio = drive->id->eide_pio_modes;
byte timing, speed, pio;
- unsigned int reg40 = 0;
#if HPT366_DEBUG_DRIVE_INFO
printk("%s: config_chipset_for_pio\n", drive->name);
speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
break;
}
- /*
- * Preserve existing DMA settings:
- */
- pci_read_config_dword(HWIF(drive)->pci_dev, 0x40, ®40);
- speed = (speed & ~0x30070000) | (reg40 & 0x30070000);
#if HPT366_DEBUG_DRIVE_INFO
printk("%s: config_chipset_for_pio: speed=0x%04x\n", drive->name, speed);
#endif /* HPT366_DEBUG_DRIVE_INFO */
--- /dev/null
+/*
+ * linux/drivers/block/ide-pnp.c
+ *
+ * This file provides autodetection for ISA PnP IDE interfaces.
+ * It was tested with "ESS ES1868 Plug and Play AudioDrive" IDE interface.
+ *
+ * Copyright (C) 2000 Andrey Panin <pazke@orbita.don.sitek.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example /usr/src/linux/COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/ide.h>
+#include <linux/isapnp.h>
+
+#ifndef PREPARE_FUNC
+#define PREPARE_FUNC(dev) (dev->prepare)
+#define ACTIVATE_FUNC(dev) (dev->activate)
+#define DEACTIVATE_FUNC(dev) (dev->deactivate)
+#endif
+
+#define DEV_IO(dev, index) (dev->resource[index].start)
+#define DEV_IRQ(dev, index) (dev->irq_resource[index].start)
+
+#define DEV_NAME(dev) (dev->bus->name ? dev->bus->name : "ISA PnP")
+
+#define GENERIC_HD_DATA 0
+#define GENERIC_HD_ERROR 1
+#define GENERIC_HD_NSECTOR 2
+#define GENERIC_HD_SECTOR 3
+#define GENERIC_HD_LCYL 4
+#define GENERIC_HD_HCYL 5
+#define GENERIC_HD_SELECT 6
+#define GENERIC_HD_STATUS 7
+
+static int generic_ide_offsets[IDE_NR_PORTS] __initdata = {
+ GENERIC_HD_DATA, GENERIC_HD_ERROR, GENERIC_HD_NSECTOR,
+ GENERIC_HD_SECTOR, GENERIC_HD_LCYL, GENERIC_HD_HCYL,
+ GENERIC_HD_SELECT, GENERIC_HD_STATUS, -1, -1
+};
+
+/* ISA PnP device table entry */
+struct pnp_dev_t {
+ unsigned int vendor, device;
+ int (*init_fn)(struct pci_dev *dev, int enable);
+};
+
+/* Generic initialisation function for ISA PnP IDE interface */
+static int __init pnpide_generic_init(struct pci_dev *dev, int enable)
+{
+ hw_regs_t hw;
+ int index;
+
+ if (!enable)
+ return 0;
+
+ if (!(DEV_IO(dev, 0) && DEV_IO(dev, 1) && DEV_IRQ(dev, 0)))
+ return 1;
+
+ ide_setup_ports(&hw, (ide_ioreg_t) DEV_IO(dev, 0),
+ generic_ide_offsets, (ide_ioreg_t) DEV_IO(dev, 1),
+ 0, NULL, DEV_IRQ(dev, 0));
+
+ index = ide_register_hw(&hw, NULL);
+
+ if (index != -1) {
+ printk("ide%d: %s IDE interface\n", index, DEV_NAME(dev));
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Add your devices here :)) */
+struct pnp_dev_t idepnp_devices[] __initdata = {
+ /* Generic ESDI/IDE/ATA compatible hard disk controller\r */
+ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0600),
+ pnpide_generic_init },
+ { 0 }
+};
+
+#ifdef MODULE
+#define NR_PNP_DEVICES 8
+struct pnp_dev_inst {
+ struct pci_dev *dev;
+ struct pnp_dev_t *dev_type;
+};
+static struct pnp_dev_inst devices[NR_PNP_DEVICES];
+static int pnp_ide_dev_idx = 0;
+#endif
+
+/*
+ * Probe for ISA PnP IDE interfaces.
+ */
+void pnpide_init(int enable)
+{
+ struct pci_dev *dev = NULL;
+ struct pnp_dev_t *dev_type;
+
+ if (!isapnp_present())
+ return;
+
+#ifdef MODULE
+ /* Module unload, deactivate all registered devices. */
+ if (!enable) {
+ int i;
+ for (i = 0; i < pnp_ide_dev_idx; i++) {
+ devices[i].dev_type->init_fn(dev, 0);
+
+ if (DEACTIVATE_FUNC(devices[i].dev))
+ DEACTIVATE_FUNC(devices[i].dev)(devices[i].dev);
+ }
+ return;
+ }
+#endif
+ for (dev_type = idepnp_devices; dev_type->vendor; dev_type++) {
+ while ((dev = isapnp_find_dev(NULL, dev_type->vendor,
+ dev_type->device, dev))) {
+
+ if (dev->active)
+ continue;
+
+ if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) {
+ printk("ide: %s prepare failed\n", DEV_NAME(dev));
+ continue;
+ }
+
+ if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) {
+ printk("ide: %s activate failed\n", DEV_NAME(dev));
+ continue;
+ }
+
+ /* Call device initialization function */
+ if (dev_type->init_fn(dev, 1)) {
+ if (DEACTIVATE_FUNC(dev))
+ DEACTIVATE_FUNC(dev)(dev);
+ } else {
+#ifdef MODULE
+ /*
+ * Register device in the array to
+ * deactivate it on a module unload.
+ */
+ if (pnp_ide_dev_idx >= NR_PNP_DEVICES)
+ return;
+ devices[pnp_ide_dev_idx].dev = dev;
+ devices[pnp_ide_dev_idx].dev_type = dev_type;
+ pnp_ide_dev_idx++;
+#endif
+ }
+ }
+ }
+}
{
int region_errors = 0;
+ hwif->straight8 = 0;
region_errors = ide_check_region(hwif->io_ports[IDE_DATA_OFFSET], 1);
region_errors += ide_check_region(hwif->io_ports[IDE_ERROR_OFFSET], 1);
region_errors += ide_check_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1);
region_errors += ide_check_region(hwif->io_ports[IDE_HCYL_OFFSET], 1);
region_errors += ide_check_region(hwif->io_ports[IDE_SELECT_OFFSET], 1);
region_errors += ide_check_region(hwif->io_ports[IDE_STATUS_OFFSET], 1);
+
if (hwif->io_ports[IDE_CONTROL_OFFSET])
region_errors += ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
+
if (hwif->io_ports[IDE_IRQ_OFFSET])
region_errors += ide_check_region(hwif->io_ports[IDE_IRQ_OFFSET], 1);
+ /*
+ * If any errors are return, we drop the hwif interface.
+ */
return(region_errors);
}
static void hwif_register (ide_hwif_t *hwif)
{
+ if ((hwif->io_ports[IDE_DATA_OFFSET] | 7) ==
+ (hwif->io_ports[IDE_STATUS_OFFSET])) {
+ ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 8, hwif->name);
+ hwif->straight8 = 1;
+ goto jump_straight8;
+ }
+
if (hwif->io_ports[IDE_DATA_OFFSET])
ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 1, hwif->name);
if (hwif->io_ports[IDE_ERROR_OFFSET])
ide_request_region(hwif->io_ports[IDE_SELECT_OFFSET], 1, hwif->name);
if (hwif->io_ports[IDE_STATUS_OFFSET])
ide_request_region(hwif->io_ports[IDE_STATUS_OFFSET], 1, hwif->name);
+
+jump_straight8:
if (hwif->io_ports[IDE_CONTROL_OFFSET])
ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name);
if (hwif->io_ports[IDE_IRQ_OFFSET])
extern byte fifoconfig; /* defined in via82cxxx.c used by ide_setup() */
#endif /* CONFIG_BLK_DEV_VIA82CXXX */
-static const byte ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR,
- IDE2_MAJOR, IDE3_MAJOR,
- IDE4_MAJOR, IDE5_MAJOR,
- IDE6_MAJOR, IDE7_MAJOR,
- IDE8_MAJOR, IDE9_MAJOR };
-
-static int idebus_parameter; /* holds the "idebus=" parameter */
-static int system_bus_speed; /* holds what we think is VESA/PCI bus speed */
+static const byte ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, IDE4_MAJOR, IDE5_MAJOR, IDE6_MAJOR, IDE7_MAJOR, IDE8_MAJOR, IDE9_MAJOR };
+
+static int idebus_parameter = 0; /* holds the "idebus=" parameter */
+static int system_bus_speed = 0; /* holds what we think is VESA/PCI bus speed */
static int initializing; /* set while initializing built-in drivers */
#ifdef CONFIG_BLK_DEV_IDEPCI
-static int ide_scan_direction = 0; /* HELLO, comment me!! */
+static int ide_scan_direction = 0; /* THIS was formerly 2.2.x pci=reverse */
#endif /* CONFIG_BLK_DEV_IDEPCI */
#if defined(__mc68000__) || defined(CONFIG_APUS)
/*
* ide_modules keeps track of the available IDE chipset/probe/driver modules.
*/
-ide_module_t *ide_modules = NULL;
-ide_module_t *ide_probe = NULL;
+ide_module_t *ide_modules = NULL;
+ide_module_t *ide_probe = NULL;
/*
* This is declared extern in ide.h, for access by other IDE modules:
*/
void hwif_unregister (ide_hwif_t *hwif)
{
+ if (hwif->straight8) {
+ ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 8);
+ goto jump_eight;
+ }
if (hwif->io_ports[IDE_DATA_OFFSET])
ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 1);
if (hwif->io_ports[IDE_ERROR_OFFSET])
ide_release_region(hwif->io_ports[IDE_SELECT_OFFSET], 1);
if (hwif->io_ports[IDE_STATUS_OFFSET])
ide_release_region(hwif->io_ports[IDE_STATUS_OFFSET], 1);
+jump_eight:
if (hwif->io_ports[IDE_CONTROL_OFFSET])
ide_release_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
if (hwif->io_ports[IDE_IRQ_OFFSET])
hwif->pci_dev = old_hwif.pci_dev;
hwif->pci_devid = old_hwif.pci_devid;
#endif /* CONFIG_BLK_DEV_IDEPCI */
+ hwif->straight8 = old_hwif.straight8;
abort:
restore_flags(flags); /* all CPUs */
err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf);
if (!err && set_transfer(drive, args[0], args[1], args[2])) {
+#if 0
+ /* active-retuning-calls future */
+ if (HWIF(drive)->tune2proc)
+ HWIF(drive)->tune2proc(drive, args[1]);
+#endif
ide_driveid_update(drive);
}
abort:
const char max_hwif = '0' + (MAX_HWIFS - 1);
printk("ide_setup: %s", s);
+ init_ide_data ();
#ifdef CONFIG_BLK_DEV_IDEDOUBLER
if (!strcmp(s, "ide=doubler")) {
}
#endif /* CONFIG_BLK_DEV_IDEPCI */
-#ifndef CONFIG_BLK_DEV_IDEPCI
- init_ide_data ();
-#endif /* CONFIG_BLK_DEV_IDEPCI */
-
/*
* Look for drive options: "hdx="
*/
if (s[3] == 'b' && s[4] == 'u' && s[5] == 's') {
if (match_parm(&s[6], NULL, vals, 1) != 1)
goto bad_option;
- if (vals[0] >= 20 && vals[0] <= 66)
+ if (vals[0] >= 20 && vals[0] <= 66) {
idebus_parameter = vals[0];
- else
+ } else
printk(" -- BAD BUS SPEED! Expected value from 20 to 66");
goto done;
}
buddha_init();
}
#endif /* CONFIG_BLK_DEV_BUDDHA */
+#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP)
+ {
+ extern void pnpide_init(int enable);
+ pnpide_init(1);
+ }
+#endif /* CONFIG_BLK_DEV_ISAPNP */
}
void __init ide_init_builtin_drivers (void)
restore_flags(flags); /* all CPUs */
return 1;
}
+#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) && defined(MODULE)
+ pnpide_init(0);
+#endif /* CONFIG_BLK_DEV_ISAPNP */
#ifdef CONFIG_PROC_FS
ide_remove_proc_entries(drive->proc, DRIVER(drive)->proc);
ide_remove_proc_entries(drive->proc, generic_subdriver_entries);
if (!banner_printed) {
printk(KERN_INFO "Uniform Multi-Platform E-IDE driver " REVISION "\n");
ide_devfs_handle = devfs_mk_dir (NULL, "ide", 3, NULL);
+ (void) ide_system_bus_speed();
banner_printed = 1;
}
if ((next = strchr(line,' ')) != NULL)
*next++ = 0;
if (!strncmp(line,"ide",3) ||
+ !strncmp(line,"idebus",6) ||
#ifdef CONFIG_BLK_DEV_VIA82CXXX
!strncmp(line,"splitfifo",9) ||
#endif /* CONFIG_BLK_DEV_VIA82CXXX */
}
}
+#if 0
+ if (drive->id->major_rev_num & 0x0004) printf("ATA-2 ");
+#endif
+
/*
* Conservative "downgrade" for all pre-ATA2 drives
*/
struct request *next, int max_segments)
{
int total_segments = req->nr_segments + next->nr_segments;
+ int same_segment;
+ same_segment = 0;
if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) {
total_segments--;
- q->nr_segments--;
+ same_segment = 1;
}
if (total_segments > max_segments)
return 0;
+ q->nr_segments -= same_segment;
req->nr_segments = total_segments;
return 1;
}
int sequence = elevator_sequence(&q->elevator, latency);
if (after)
sequence -= req->nr_segments;
- if (elevator_sequence_before(sequence, req->elevator_sequence)) {
- if (!after)
- printk(KERN_WARNING __FUNCTION__
- ": req latency %d req latency %d\n",
- req->elevator_sequence - q->elevator.sequence,
- sequence - q->elevator.sequence);
+ if (elevator_sequence_before(sequence, req->elevator_sequence))
req->elevator_sequence = sequence;
- }
}
static inline void elevator_queue(request_queue_t * q,
#define PDC202XX_DEBUG_DRIVE_INFO 0
#define PDC202XX_DECODE_REGISTER_INFO 0
-#define DISPLAY_PDC202XX_TIMINGS
+#undef DISPLAY_PDC202XX_TIMINGS
+
#if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS)
#include <linux/stat.h>
#include <linux/proc_fs.h>
static int rd_blocksizes[NUM_RAMDISKS]; /* Size of 1024 byte blocks :) */
static int rd_kbsize[NUM_RAMDISKS]; /* Size in blocks of 1024 bytes */
static devfs_handle_t devfs_handle = NULL;
+static struct inode *rd_inode[NUM_RAMDISKS]; /* Protected device inodes */
/*
* Parameters for the boot-loading of the RAM disk. These are set by
return ramdisk_size(str);
}
+static int __init ramdisk_blocksize(char *str)
+{
+ rd_blocksize = simple_strtol(str,NULL,0);
+ return 1;
+}
+
__setup("ramdisk_start=", ramdisk_start_setup);
__setup("load_ramdisk=", load_ramdisk);
__setup("prompt_ramdisk=", prompt_ramdisk);
__setup("ramdisk=", ramdisk_size);
__setup("ramdisk_size=", ramdisk_size2);
+__setup("ramdisk_blocksize=", ramdisk_blocksize);
#endif
goto repeat;
}
- /*
- * This has become somewhat more complicated with the addition of
- * the page cache. The problem is that in some cases the furnished
- * buffer is "real", i.e., part of the existing ramdisk, while in
- * others it is "unreal", e.g., part of a page. In the first case
- * not much needs to be done, while in the second, some kind of
- * transfer is needed.
- *
- * The two cases are distinguished here by checking whether the
- * real buffer is already in the buffer cache, and whether it is
- * the same as the one supplied.
- *
- * There are three cases with read/write to consider:
- *
- * 1. Supplied buffer matched one in the buffer cache:
- * Read - Clear the buffer, as it wasn't already valid.
- * Write - Mark the buffer as "Protected".
- *
- * 2. Supplied buffer mismatched one in the buffer cache:
- * Read - Copy the data from the buffer cache entry.
- * Write - Copy the data to the buffer cache entry.
- *
- * 3 No buffer cache entry existed:
- * Read - Clear the supplied buffer, but do not create a real
- * one.
- * Write - Create a real buffer, copy the data to it, and mark
- * it as "Protected".
- *
- * NOTE: There seems to be some schizophrenia here - the logic
- * using "len" seems to assume arbitrary request lengths, while
- * the "protect" logic assumes a single buffer cache entry.
- * This seems to be left over from the ancient contiguous ramdisk
- * logic.
- */
-
sbh = CURRENT->bh;
- rbh = get_hash_table(sbh->b_dev, sbh->b_blocknr, sbh->b_size);
- if (sbh == rbh) {
- if (CURRENT->cmd == READ)
- memset(CURRENT->buffer, 1, len);
- } else if (rbh) {
- if (CURRENT->cmd == READ)
+ rbh = getblk(sbh->b_dev, sbh->b_blocknr, sbh->b_size);
+ if (CURRENT->cmd == READ) {
+ if (sbh != rbh)
memcpy(CURRENT->buffer, rbh->b_data, rbh->b_size);
- else
+ } else
+ if (sbh != rbh)
memcpy(rbh->b_data, CURRENT->buffer, rbh->b_size);
- } else { /* !rbh */
- if (CURRENT->cmd == READ)
- memset(sbh->b_data, 2, len);
- else {
- rbh = getblk(sbh->b_dev, sbh->b_blocknr, sbh->b_size);
- if (rbh)
- memcpy(rbh->b_data, CURRENT->buffer,
- rbh->b_size);
- else
- BUG(); /* No buffer, what to do here? */
- }
- }
- if (rbh) {
- mark_buffer_protected(rbh);
- brelse(rbh);
- }
+ mark_buffer_protected(rbh);
+ brelse(rbh);
end_request(1);
goto repeat;
if (DEVICE_NR(inode->i_rdev) >= NUM_RAMDISKS)
return -ENXIO;
+ /*
+ * Immunize device against invalidate_buffers() and prune_icache().
+ */
+ if (rd_inode[DEVICE_NR(inode->i_rdev)] == NULL) {
+ if((rd_inode[DEVICE_NR(inode->i_rdev)] = igrab(inode)) != NULL)
+ atomic_inc(&rd_inode[DEVICE_NR(inode->i_rdev)]->i_bdev->bd_openers);
+ }
+
MOD_INC_USE_COUNT;
return 0;
ioctl: rd_ioctl,
};
+#ifdef MODULE
/* Before freeing the module, invalidate all of the protected buffers! */
static void __exit rd_cleanup (void)
{
int i;
for (i = 0 ; i < NUM_RAMDISKS; i++) {
- struct block_device *bdev;
- bdev = bdget(kdev_t_to_nr(MKDEV(MAJOR_NR,i)));
- atomic_dec(&bdev->bd_openers);
+ if (rd_inode[i]) {
+ /* withdraw invalidate_buffers() and prune_icache() immunity */
+ atomic_dec(&rd_inode[i]->i_bdev->bd_openers);
+ /* remove stale pointer to module address space */
+ rd_inode[i]->i_bdev->bd_op = NULL;
+ iput(rd_inode[i]);
+ }
destroy_buffers(MKDEV(MAJOR_NR, i));
}
devfs_unregister (devfs_handle);
unregister_blkdev( MAJOR_NR, "ramdisk" );
blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+ hardsect_size[MAJOR_NR] = NULL;
blksize_size[MAJOR_NR] = NULL;
blk_size[MAJOR_NR] = NULL;
}
+#endif
/* This is the registration and initialization section of the RAM disk driver */
int __init rd_init (void)
S_IFBLK | S_IRUSR | S_IWUSR, 0, 0,
&fd_fops, NULL);
- hardsect_size[MAJOR_NR] = rd_hardsec; /* Size of the RAM disk blocks */
- blksize_size[MAJOR_NR] = rd_blocksizes; /* Avoid set_blocksize() check */
-
- for (i = 0; i < NUM_RAMDISKS; i++) {
- struct block_device *bdev;
+ for (i = 0; i < NUM_RAMDISKS; i++)
register_disk(NULL, MKDEV(MAJOR_NR,i), 1, &fd_fops, rd_size<<1);
- bdev = bdget(kdev_t_to_nr(MKDEV(MAJOR_NR,i)));
- atomic_inc(&bdev->bd_openers); /* avoid invalidate_buffers() */
- }
#ifdef CONFIG_BLK_DEV_INITRD
/* We ought to separate initrd operations here */
register_disk(NULL, MKDEV(MAJOR_NR,INITRD_MINOR), 1, &fd_fops, rd_size<<1);
#endif
+ hardsect_size[MAJOR_NR] = rd_hardsec; /* Size of the RAM disk blocks */
+ blksize_size[MAJOR_NR] = rd_blocksizes; /* Avoid set_blocksize() check */
blk_size[MAJOR_NR] = rd_kbsize; /* Size of the RAM disk in kB */
/* rd_size is given in kB */
#ifdef MODULE
module_init(rd_init);
-#endif
module_exit(rd_cleanup);
+#endif
/* loadable module support */
MODULE_PARM (rd_size, "1i");
* Note that by default (if no command line is provided) and if a channel
* has been disabled in Bios, all the fifo is given to the active channel,
* and its threshold is set to 3/4.
+ *
+ * VT82c586B
+ *
+ * Offset 4B-48 - Drive Timing Control
+ * | pio0 | pio1 | pio2 | pio3 | pio4
+ * 25.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
+ * 33.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
+ * 37.5 MHz | 0xA9 | 0x76 | 0x76 | 0x32 | 0x21
+ *
+ * Offset 53-50 - UltraDMA Extended Timing Control
+ * UDMA | NO | 0 | 1 | 2
+ * | 0x03 | 0x62 | 0x61 | 0x60
+ *
+ * VT82c596B & VT82c686A
+ *
+ * Offset 4B-48 - Drive Timing Control
+ * | pio0 | pio1 | pio2 | pio3 | pio4
+ * 25.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
+ * 33.0 MHz | 0xA8 | 0x65 | 0x65 | 0x31 | 0x20
+ * 37.5 MHz | 0xDB | 0x87 | 0x87 | 0x42 | 0x31
+ * 41.5 MHz | 0xFE | 0xA8 | 0xA8 | 0x53 | 0x32
+ *
+ * Offset 53-50 - UltraDMA Extended Timing Control
+ * UDMA | NO | 0 | 1 | 2
+ * 33.0 MHz | 0x03 | 0xE2 | 0xE1 | 0xE0
+ * 37.5 MHz | 0x03 | 0xE2 | 0xE2 | 0xE1 (1)
+ *
+ * Offset 53-50 - UltraDMA Extended Timing Control
+ * UDMA | NO | 0 | 1 | 2 | 3 | 4
+ * 33.0 MHz | (2) | 0xE6 | 0xE4 | 0xE2 | 0xE1 | 0xE0
+ * 37.5 MHz | (2) | 0xE6 | 0xE6 | 0xE4 | 0xE2 | 0xE1 (1)
+ *
*/
#include <linux/config.h>
{ "VT 82C691 Apollo Pro", PCI_DEVICE_ID_VIA_82C691, },
{ "VT 82C693 Apollo Pro Plus", PCI_DEVICE_ID_VIA_82C693, },
{ "Apollo MVP4", PCI_DEVICE_ID_VIA_8501_0, },
+ { "VT 8371", PCI_DEVICE_ID_VIA_8371_0, },
+ { "VT 8601", PCI_DEVICE_ID_VIA_8601_0, },
};
#define NUM_APOLLO_ISA_CHIP_DEVICES 2
{ PCI_DEVICE_ID_VIA_82C691, PCI_DEVICE_ID_VIA_82C596, 0 },
{ PCI_DEVICE_ID_VIA_82C693, PCI_DEVICE_ID_VIA_82C596, 0 },
{ PCI_DEVICE_ID_VIA_8501_0, PCI_DEVICE_ID_VIA_82C686, VIA_FLAG_ATA_66 },
+ { PCI_DEVICE_ID_VIA_8371_0, PCI_DEVICE_ID_VIA_82C686, VIA_FLAG_ATA_66 },
+ { PCI_DEVICE_ID_VIA_8601_0, PCI_DEVICE_ID_VIA_8231, VIA_FLAG_ATA_66 },
};
#define arraysize(x) (sizeof(x)/sizeof(*(x)))
return 0;
}
+#ifdef CONFIG_VIA82CXXX_TUNING
+
+static int via82cxxx_tune_chipset (ide_drive_t *drive, byte speed)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ unsigned long dma_base = hwif->dma_base;
+ byte unit = (drive->select.b.unit & 0x01);
+ int drive_number = ((hwif->channel ? 2 : 0) + unit);
+
+ byte ata2_pci = 0x00;
+ byte ata3_pci = 0x00;
+ byte timing = 0x00;
+ byte ultra = 0x00;
+ int err;
+
+ int bus_speed = ide_system_bus_speed();
+
+ switch(drive_number) {
+ case 0: ata2_pci = 0x48; ata3_pci = 0x50; break;
+ case 1: ata2_pci = 0x49; ata3_pci = 0x51; break;
+ case 2: ata2_pci = 0x4a; ata3_pci = 0x52; break;
+ case 3: ata2_pci = 0x4b; ata3_pci = 0x53; break;
+ default:
+ return err;
+ }
+
+ pci_read_config_byte(dev, ata2_pci, &timing);
+ pci_read_config_byte(dev, ata3_pci, &ultra);
+
+ switch(speed) {
+ case XFER_UDMA_4:
+ case XFER_UDMA_3:
+ case XFER_UDMA_2:
+ case XFER_UDMA_1:
+ case XFER_UDMA_0:
+ case XFER_MW_DMA_2:
+ case XFER_MW_DMA_1:
+ case XFER_MW_DMA_0:
+ case XFER_SW_DMA_2:
+ case XFER_SW_DMA_1:
+ case XFER_SW_DMA_0:
+ case XFER_PIO_4:
+ case XFER_PIO_3:
+ case XFER_PIO_2:
+ case XFER_PIO_1:
+ case XFER_PIO_0:
+ case XFER_PIO_SLOW:
+ default:
+ break;
+ }
+
+ pci_write_config_byte(dev, ata2_pci, timing);
+ pci_write_config_byte(dev, ata3_pci, ultra);
+
+ err = ide_config_drive_speed(drive, speed);
+
+ return(err);
+}
+
+static int config_chipset_for_dma (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ byte speed = 0x00;
+ int rval;
+
+ if ((id->dma_ultra & 0x0010) && (HWIF(drive)->udma_four)) {
+ speed = XFER_UDMA_4;
+ } else if ((id->dma_ultra & 0x0008) && (HWIF(drive)->udma_four)) {
+ speed = XFER_UDMA_3;
+ } else if (id->dma_ultra & 0x0004) {
+ speed = XFER_UDMA_2;
+ } else if (id->dma_ultra & 0x0002) {
+ speed = XFER_UDMA_1;
+ } else if (id->dma_ultra & 0x0001) {
+ speed = XFER_UDMA_0;
+ } else if (id->dma_mword & 0x0004) {
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_mword & 0x0001) {
+ speed = XFER_MW_DMA_0;
+ } else if (id->dma_1word & 0x0004) {
+ speed = XFER_SW_DMA_2;
+ } else if (id->dma_1word & 0x0002) {
+ speed = XFER_SW_DMA_1;
+ } else if (id->dma_1word & 0x0001) {
+ speed = XFER_SW_DMA_0;
+ } else {
+ return ((int) ide_dma_off_quietly);
+ }
+
+ (void) via82cxxx_tune_chipset(drive, speed);
+
+ rval = (int)( ((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+ ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+ return rval;
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+ unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+ unsigned short xfer_pio = drive->id->eide_pio_modes;
+ byte timing, speed, pio;
+
+ pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+ if (xfer_pio> 4)
+ xfer_pio = 0;
+
+ if (drive->id->eide_pio_iordy > 0) {
+ for (xfer_pio = 5;
+ xfer_pio>0 &&
+ drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+ xfer_pio--);
+ } else {
+ xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+ (drive->id->eide_pio_modes & 2) ? 0x04 :
+ (drive->id->eide_pio_modes & 1) ? 0x03 :
+ (drive->id->tPIO & 2) ? 0x02 :
+ (drive->id->tPIO & 1) ? 0x01 : xfer_pio;
+ }
+
+ timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+ switch(timing) {
+ case 4: speed = XFER_PIO_4;break;
+ case 3: speed = XFER_PIO_3;break;
+ case 2: speed = XFER_PIO_2;break;
+ case 1: speed = XFER_PIO_1;break;
+ default:
+ speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW; break;
+ }
+ (void) via82cxxx_tune_chipset(drive, speed);
+}
+
+static void via82cxxx_tune_drive (ide_drive_t *drive, byte pio)
+{
+ byte speed;
+ switch(pio) {
+ case 4: speed = XFER_PIO_4;break;
+ case 3: speed = XFER_PIO_3;break;
+ case 2: speed = XFER_PIO_2;break;
+ case 1: speed = XFER_PIO_1;break;
+ default: speed = XFER_PIO_0;break;
+ }
+ (void) via82cxxx_tune_chipset(drive, speed);
+}
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_dma_action_t dma_func = ide_dma_on;
+
+ if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ dma_func = ide_dma_off;
+ goto fast_ata_pio;
+ }
+ dma_func = ide_dma_off_quietly;
+ if (id->field_valid & 4) {
+ if (id->dma_ultra & 0x001F) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if ((id->dma_mword & 0x0007) ||
+ (id->dma_1word & 0x0007)) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ }
+ } else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+ if (id->eide_dma_time > 150) {
+ goto no_dma_set;
+ }
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ } else {
+ goto fast_ata_pio;
+ }
+ } else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+ dma_func = ide_dma_off_quietly;
+no_dma_set:
+ config_chipset_for_pio(drive);
+ }
+ return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+int via82cxxx_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ return config_drive_xfer_rate(drive);
+ default:
+ break;
+ }
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+}
+#endif /* CONFIG_VIA82CXXX_TUNING */
+
unsigned int __init pci_init_via82cxxx (struct pci_dev *dev, const char *name)
{
struct pci_dev *host;
continue;
isa = pci_find_device (PCI_VENDOR_ID_VIA,
- ApolloISAChipInfo[i].isa_id,
+ ApolloISAChipInfo[j].isa_id,
NULL);
if (!isa)
continue;
ata33 = 1;
ata66 = 0;
- if (ApolloISAChipInfo[i].flags & VIA_FLAG_CHECK_REV) {
+ if (ApolloISAChipInfo[j].flags & VIA_FLAG_CHECK_REV) {
pci_read_config_byte(isa_dev, 0x0d, &revision);
ata33 = (revision >= 0x20) ? 1 : 0;
- } else if (ApolloISAChipInfo[i].flags & VIA_FLAG_ATA_66) {
+ } else if (ApolloISAChipInfo[j].flags & VIA_FLAG_ATA_66) {
ata33 = 0;
ata66 = 1;
}
void __init ide_init_via82cxxx (ide_hwif_t *hwif)
{
set_via_timings(hwif);
-#if 0
+
+#ifdef CONFIG_VIA82CXXX_TUNING
hwif->tuneproc = &via82cxxx_tune_drive;
if (hwif->dma_base) {
hwif->dmaproc = &via82cxxx_dmaproc;
hwif->drives[0].autotune = 1;
hwif->drives[1].autotune = 1;
}
-#endif
+#endif /* CONFIG_VIA82CXXX_TUNING */
}
/*
tristate '/dev/nvram support' CONFIG_NVRAM
tristate 'Enhanced Real Time Clock Support' CONFIG_RTC
-bool 'EFI Real Time Clock Services' CONFIG_EFI_RTC
+if [ "$CONFIG_IA64" = "y" ]; then
+ bool 'EFI Real Time Clock Services' CONFIG_EFI_RTC
+fi
if [ "$CONFIG_OBSOLETE" = "y" -a "$CONFIG_ALPHA_BOOK1" = "y" ]; then
bool 'Tadpole ANA H8 Support' CONFIG_H8
fi
#endif
#ifdef CONFIG_FB
extern void fbmem_init(void);
+extern void fbconsole_init(void);
#endif
#ifdef CONFIG_PROM_CONSOLE
extern void prom_con_init(void);
#endif
#if defined (CONFIG_FB)
fbmem_init();
+ fbconsole_init();
#endif
#if defined (CONFIG_PROM_CONSOLE)
prom_con_init();
extern int sb1000_probe(struct net_device *dev);
static struct net_device sb1000_dev =
{
- "cm0 __PAD3", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, sb1000_probe
+ "cm0" __PAD3, 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, sb1000_probe
};
register_netdev(&sb1000_dev);
}
obj-n :=
obj-m :=
obj- :=
-export-objs :=
+export-objs := tms380tr.o
obj-$(CONFIG_IBMTR) += ibmtr.o
obj-$(CONFIG_IBMOL) += olympic.o
#endif
#ifdef MODULE
-#define EXPORT_SYMTAB
#include <linux/module.h>
#include <linux/version.h>
#endif
tp->timer.data = (unsigned long)dev;
tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
add_timer(&tp->timer);
-
- netif_device_attach(dev);
}
tulip_up (dev);
+ netif_start_queue (dev);
+
return 0;
}
struct tulip_private *tp = (struct tulip_private *) dev->priv;
unsigned long flags;
- netif_device_detach (dev);
-
del_timer (&tp->timer);
spin_lock_irqsave (&tp->lock, flags);
long ioaddr = dev->base_addr;
struct tulip_private *tp = (struct tulip_private *) dev->priv;
int i;
+
+ netif_stop_queue (dev);
tulip_down (dev);
{
struct net_device *dev = pdev->driver_data;
- if (dev && netif_device_present (dev))
+ if (dev && netif_device_present (dev)) {
+ netif_device_detach (dev);
tulip_down (dev);
+ }
}
{
struct net_device *dev = pdev->driver_data;
- if (dev && !netif_device_present (dev))
+ if (dev && !netif_device_present (dev)) {
tulip_up (dev);
+ netif_device_attach (dev);
+ }
}
-/* $Id: cosa.c,v 1.30 2000/02/21 15:19:49 kas Exp $ */
+/* $Id: cosa.c,v 1.31 2000/03/08 17:47:16 kas Exp $ */
/*
* Copyright (C) 1995-1997 Jan "Yenya" Kasprzak <kas@fi.muni.cz>
{
int i;
- printk(KERN_INFO "cosa v1.07 (c) 1997-2000 Jan Kasprzak <kas@fi.muni.cz>\n");
+ printk(KERN_INFO "cosa v1.08 (c) 1997-2000 Jan Kasprzak <kas@fi.muni.cz>\n");
#ifdef __SMP__
printk(KERN_INFO "cosa: SMP found. Please mail any success/failure reports to the author.\n");
#endif
struct net_device *d;
chan->if_ptr = &chan->pppdev;
chan->pppdev.dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+ memset(chan->pppdev.dev, 0, sizeof(struct net_device));
sppp_attach(&chan->pppdev);
d=chan->pppdev.dev;
d->name = chan->name;
d->get_stats = cosa_net_stats;
d->tx_timeout = cosa_sppp_timeout;
d->watchdog_timeo = TX_TIMEOUT;
- dev_init_buffers(d);
if (register_netdev(d) == -1) {
printk(KERN_WARNING "%s: register_netdev failed.\n", d->name);
sppp_detach(chan->pppdev.dev);
chan->stats.tx_aborted_errors++;
return 1;
}
- dev_kfree_skb(chan->tx_skb);
+ dev_kfree_skb_irq(chan->tx_skb);
chan->tx_skb = 0;
chan->stats.tx_packets++;
chan->stats.tx_bytes += size;
mm_t m;
unsigned long flags;
int ret = 0;
+ int err = 0;
#ifdef DEBUG_IOCTL_TRACE
printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name,
/* Disable interrupts and save flags. */
wv_splhi(lp, &flags);
- /* FIXME: can't copy*user when cli this is broken! */
- /* Note : is it still valid ? Jean II */
/* Look what is the request */
switch (cmd) {
}
/* Copy the key in the driver */
- if (copy_from_user
- (psa.psa_encryption_key,
- wrq->u.encoding.pointer,
- wrq->u.encoding.length)) {
+ wv_splx(lp, &flags);
+ err = copy_from_user(psa.psa_encryption_key,
+ wrq->u.encoding.pointer,
+ wrq->u.encoding.length);
+ wv_splhi(lp, &flags);
+ if (err) {
ret = -EFAULT;
break;
}
/* Copy the key to the user buffer */
wrq->u.encoding.length = 8;
- if (copy_to_user
- (wrq->u.encoding.pointer,
- psa.psa_encryption_key, 8)) ret = -EFAULT;
+ wv_splx(lp, &flags);
+ if (copy_to_user(wrq->u.encoding.pointer,
+ psa.psa_encryption_key, 8))
+ ret = -EFAULT;
+ wv_splhi(lp, &flags);
}
break;
}
/* Copy structure to the user buffer. */
- if (copy_to_user
- (wrq->u.data.pointer, &range,
- sizeof(struct iw_range))) ret = -EFAULT;
+ wv_splx(lp, &flags);
+ if (copy_to_user(wrq->u.data.pointer,
+ &range,
+ sizeof(struct iw_range)))
+ ret = -EFAULT;
+ wv_splhi(lp, &flags);
}
break;
case SIOCGIWPRIV:
/* Basic checking */
if (wrq->u.data.pointer != (caddr_t) 0) {
- struct iw_priv_args priv[] = { /* cmd, set_args, get_args, name */
-
- {SIOCSIPQTHR,
- IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED |
- 1, 0, "setqualthr"},
- {SIOCGIPQTHR, 0,
- IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED |
- 1, "getqualthr"},
-
-
- {SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16,
- 0, "sethisto"},
- {SIOCGIPHISTO, 0, IW_PRIV_TYPE_INT | 16,
- "gethisto"},
+ struct iw_priv_args priv[] = {
+ /* { cmd,
+ set_args,
+ get_args,
+ name } */
+ { SIOCSIPQTHR,
+ IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+ 0,
+ "setqualthr" },
+ { SIOCGIPQTHR,
+ 0,
+ IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+ "getqualthr" },
+ { SIOCSIPHISTO,
+ IW_PRIV_TYPE_BYTE | 16,
+ 0,
+ "sethisto" },
+ { SIOCGIPHISTO,
+ 0,
+ IW_PRIV_TYPE_INT | 16,
+ "gethisto" },
};
/* Set the number of available ioctls. */
wrq->u.data.length = 4;
/* Copy structure to the user buffer. */
- if (copy_to_user
- (wrq->u.data.pointer, (u8 *) priv,
- sizeof(priv))) ret = -EFAULT;
+ wv_splx(lp, &flags);
+ if (copy_to_user(wrq->u.data.pointer,
+ (u8 *) priv,
+ sizeof(priv)))
+ ret = -EFAULT;
+ wv_splhi(lp, &flags);
}
break;
int i;
/* Copy addresses to the driver. */
- if (copy_from_user
- (address, wrq->u.data.pointer,
- sizeof(struct sockaddr) * lp->spy_number)) {
+ wv_splx(lp, &flags);
+ err = copy_from_user(address,
+ wrq->u.data.pointer,
+ sizeof(struct sockaddr)
+ * lp->spy_number);
+ wv_splhi(lp, &flags);
+ if (err) {
ret = -EFAULT;
break;
}
}
/* Copy addresses to the user buffer. */
- if (copy_to_user
- (wrq->u.data.pointer, address,
- sizeof(struct sockaddr) * lp->spy_number)) {
- ret = -EFAULT;
- break;
- }
+ wv_splx(lp, &flags);
+ err = copy_to_user(wrq->u.data.pointer,
+ address,
+ sizeof(struct sockaddr)
+ * lp->spy_number);
/* Copy stats to the user buffer (just after). */
- if (copy_to_user(wrq->u.data.pointer +
- (sizeof(struct sockaddr) *
- lp->spy_number), lp->spy_stat,
- sizeof(iw_qual) * lp->spy_number)) {
+ err |= copy_to_user(wrq->u.data.pointer
+ + (sizeof(struct sockaddr)
+ * lp->spy_number),
+ lp->spy_stat,
+ sizeof(iw_qual) * lp->spy_number);
+ wv_splhi(lp, &flags);
+ if (err) {
ret = -EFAULT;
break;
}
/* Are there addresses to copy? */
if (lp->his_number > 0) {
/* Copy interval ranges to the driver */
- if (copy_from_user
- (lp->his_range, wrq->u.data.pointer,
- sizeof(char) * lp->his_number)) {
+ wv_splx(lp, &flags);
+ err = copy_from_user(lp->his_range,
+ wrq->u.data.pointer,
+ sizeof(char) * lp->his_number);
+ wv_splhi(lp, &flags);
+ if (err) {
ret = -EFAULT;
break;
}
if ((lp->his_number > 0)
&& (wrq->u.data.pointer != (caddr_t) 0)) {
/* Copy data to the user buffer. */
- if (copy_to_user
- (wrq->u.data.pointer, lp->his_sum,
- sizeof(long) * lp->his_number))
+ wv_splx(lp, &flags);
+ if (copy_to_user(wrq->u.data.pointer,
+ lp->his_sum,
+ sizeof(long) * lp->his_number);
ret = -EFAULT;
+ wv_splhi(lp, &flags);
} /* if(pointer != NULL) */
break;
default:
ret = -EOPNOTSUPP;
- }
+ } /* switch (cmd) */
/* Enable interrupts and restore flags. */
wv_splx(lp, &flags);
# Maintained by Martin Mares <pci-ids@ucw.cz>
# If you have any new entries, send them to the maintainer.
#
-# $Id: pci.ids,v 1.50 2000/01/23 05:57:06 mj Exp $
+# $Id: pci.ids,v 1.53 2000/03/09 08:19:20 mj Exp $
#
# Vendors, devices and subsystems. Please keep sorted.
0026 DECchip 21154
0045 DECchip 21553
0046 DECchip 21554
+ 9005 1364 Dell PowerEdge RAID Controller 2
+ 9005 1365 Dell PowerEdge RAID Controller 2
1012 Micronics Computers Inc
1013 Cirrus Logic
0038 GD 7548
1489 M1489
1511 M1511
1513 M1513
- 1531 M1531
- 1533 M1533
+ 1531 M1531 Northbridge [Aladdin IV/IV+]
+ 1533 M1533 PCI-to-ISA Bridge
+ 1535 M1535 PCI Bridge + Super I/O + FIR
+ 1541 M1541 Northbridge [Aladdin V]
+ 1542 M1542 Northbridge [Aladdin V]
+ 1543 M1543 PCI-to-ISA Bridge + Super I/O + FIR
+ 1561 M1561 Northbridge [Aladdin 7]
+ 1621 M1621 Northbridge [Aladdin-Pro II]
+ 1631 M1631 Northbridge+3D Graphics [Aladdin TNT2]
+ 1641 M1641 Northbridge [Aladdin-Pro IV]
3141 M3141
3143 M3143
3145 M3145
3147 M3147
3149 M3149
3151 M3151
+ 3307 M3307 MPEG-I Video Controller
+ 3309 M3309 MPEG-II Video w/ Software Audio Decoder
+ 3321 M3321 MPEG-II Audio/Video Decoder
5217 M5217H
5219 M5219
5225 M5225
5229 M5229
5235 M5235
+ 5251 M5251 P1394 OHCI Controller
1028 Dell Computer Corporation
+ 0001 PowerEdge Expandable RAID Controller 2/Si
+ 0002 PowerEdge Expandable RAID Controller 3/Di
+ 0003 PowerEdge Expandable RAID Controller 3/Si
1029 Siemens Nixdorf IS
102a LSI Logic
0000 HYDRA
10a8 Sierra Semiconductor
0000 STB Horizon 64
10a9 Silicon Graphics, Inc.
- 0003 IOC3
+ 0001 Crosstalk to PCI Bridge
+ 0002 Linc I/O controller
+ 0003 IOC3 I/O controller
+ 0004 O2 MACE
0005 RAD Audio
+ 0006 HPCEX
+ 0007 RPCEX
+ 0008 DiVO VIP
0009 Alteon Gigabit Ethernet
+ 0010 AMP Video I/O
+ 0011 GRIP
+ 0012 SGH PSHAC GSN
+ 1001 Magic Carpet
+ 1002 Lithium
+ 1003 Dual JPEG 1
+ 1004 Dual JPEG 2
+ 1005 Dual JPEG 3
+ 1006 Dual JPEG 4
+ 1007 Dual JPEG 5
+ 1008 Cesium
+ 2001 Fibre Channel
+ 2002 ASDE
+ 8001 O2 1394
+ 8002 G-net NT
10aa ACC Microelectronics
0000 ACCM 2188
10ab Digicom
1077 VScom 400 4 port serial adaptor
9036 9036
9050 PCI <-> IOBus Bridge
+ d84d 4006 EX-4006 1P
+ d84d 4008 EX-4008 1P EPP/ECP
+ d84d 4014 EX-4014 2P
+ d84d 4018 EX-4018 3P EPP/ECP
+ d84d 4025 EX-4025 1S(16C550) RS-232
+ d84d 4027 EX-4027 1S(16C650) RS-232
+ d84d 4028 EX-4028 1S(16C850) RS-232
+ d84d 4036 EX-4036 2S(16C650) RS-232
+ d84d 4037 EX-4037 2S(16C650) RS-232
+ d84d 4038 EX-4038 2S(16C850) RS-232
+ d84d 4052 EX-4052 1S(16C550) RS-422/485
+ d84d 4053 EX-4053 2S(16C550) RS-422/485
+ d84d 4055 EX-4055 4S(16C550) RS-232
+ d84d 4058 EX-4055 4S(16C650) RS-232
+ d84d 4065 EX-4065 8S(16C550) RS-232
+ d84d 4068 EX-4068 8S(16C650) RS-232
+ d84d 4078 EX-4078 2S(16C552) RS-232+1P
9060 9060
906d 9060SD
125c 0640 Aries 16000P
10dc ATT2C15-3 FPGA
10dd Evans & Sutherland
10de nVidia Corporation
- 0008 NV1 EDGE 3D
- 0009 NV1 EDGE 3D
- 0020 Riva TNT 128
+ 0008 EDGE 3D [NV1]
+ 0009 EDGE 3D [NV1]
+ 0010 Mutara V08 [NV2]
+ 0020 Riva TnT 128 [NV04]
1043 0200 V3400 TNT
1092 0550 Viper V550
1092 0552 Viper V550
1092 4904 Viper V550
1092 4914 Viper V550
1092 8225 Viper V550
- 0028 Riva TNT2
+ 0028 Riva TnT2 [NV5]
1043 0200 AGP-V3800 SGRAM
1092 4804 Viper V770
1092 4a00 Viper V770
1092 7a02 Viper V770 Ultra
1102 1020 3D Blaster RIVA TNT2
14af 5810 Maxi Gamer Xentor
- 0029 Riva TNT2 Ultra
+ 0029 Riva TnT2 Ultra [NV5]
1043 0200 AGP-V3800 Deluxe
1102 1021 3D Blaster RIVA TNT2 Ultra
14af 5820 Maxi Gamer Xentor 32
- 002c Vanta
+ 002a Riva TnT2 [NV5]
+ 002b Riva TnT2 [NV5]
+ 002c Vanta [NV6]
1043 0200 AGP-V3800 Combat SDRAM
1092 6820 Viper V730
14af 5008 Maxi Gamer Phoenix 2
- 002d Riva TNT2 Model 64
+ 002d Vanta [NV6]
1043 0200 AGP-V3800M
+ 002e Vanta [NV6]
+ 002f Vanta [NV6]
00a0 Riva TNT2
14af 5810 Maxi Gamer Xentor
10df Emulex Corporation
0101 3GA
8111 Twist3 Frame Grabber
10ec Realtek Semiconductor Co., Ltd.
- 8029 RT8029(AS)
+ 8029 RTL-8029(AS)
10b8 2011 EZ-Card
10ec 8029 RT8029(AS)
1113 1208 EN1208
1186 0300 DE-528
1259 2400 AT-2400
- 8129 RT8129
- 8139 RT8139
+ 8129 RTL-8129
+ 8139 RTL-8139
1025 8920 ALN-325
1025 8921 ALN-325
10bd 0320 EP-320X-R
2646 0001 EtheRx
8e2e 7000 KF-230TX
8e2e 7100 KF-230TX/2
+ a0a0 0007 ALN-325C
10ed Ascii Corporation
7310 V7310
10ee Xilinx, Inc.
+ 3fc0 RME Digi96
+ 3fc1 RME Digi96/8
+ 3fc2 RME Digi96/8 Pro
+ 3fc3 RME Digi96/8 Pad
10ef Racore Computer Products, Inc.
8154 M815x Token Ring Adapter
10f0 Peritek Corporation
10ff NCube
1100 Jazz Multimedia
1101 Initio Corporation
+ 1060 INI-A100U2W
9100 INI-9100/9100W
9400 INI-940
9401 INI-950
1105 Sigma Designs, Inc.
8300 REALmagic Hollywood Plus DVD Decoder
1106 VIA Technologies, Inc.
+ 0391 VT8371 [KX133]
0501 VT8501
+ 0601 VT8601
0505 VT82C505
0561 VT82C561
0571 VT82C586 IDE [Apollo]
3068 VT82C686 [Apollo Super AC97/Modem]
5030 VT82C596 ACPI [Apollo PRO]
6100 VT85C100A [Rhine II]
+ 8231 VT8231 [PCI-to-ISA Bridge]
+ 8391 VT8371 [PCI-PCI Bridge]
8501 VT8501
8596 VT82C596 [Apollo PRO AGP]
8597 VT82C597 [Apollo VP3 AGP]
8598 VT82C598 [Apollo MVP3 AGP]
8691 VT82C691 [Apollo Pro]
+ 8601 VT8601
1107 Stratus Computers
0576 VIA VT82C570MV [Apollo] (Wrong vendor ID!)
1108 Proteon, Inc.
0000 155P-MF1 (FPGA)
0002 155P-MF1 (ASIC)
0003 ENI-25P ATM Adapter
- 0005 ENI-25P ATM Adapter
+ 0005 Speedstream 30xx ATM Adapter
111b Teledyne Electronic Systems
111c Tricord Systems Inc.
0001 Powerbis Bridge
1968 ES1968 Maestro 2
1969 ES1969 Solo-1 Audiodrive
1978 ES1978 Maestro 2E
+ 1988 ES1988 Allegro-1
2808 ES336H Fax Modem (Later Model)
2898 ES2898 Modem
125e Specialvideo Engineering SRL
0100 CM8338A
0101 CM8338B
0111 CM8738
+ 0211 CM8738
13f7 Wildfire Communications
13f8 Ad Lib Multimedia Inc
13f9 NTT Advanced Technology Corp.
145d Gallant Computer Inc
145e Aashima Technology B.V.
145f Baldor Electric Company
+ 0001 NextMove PCI
1460 DYNARC INC
1461 Avermedia Technologies Inc
1462 Micro-star International Co Ltd
14b1 Nextcom K.K.
14b2 ENNOVATE Networks Inc
14b3 XPEED Inc
+ 0000 DSL NIC
14b4 PHILIPS Business Electronics B.V.
14b5 Creamware GmBH
14b6 Quantum Data Corp.
14d9 Alpha Processor Inc
14da National Aerospace Laboratories
14db AFAVLAB Technology Inc
+ 2120 TK9902
14dc Amplicon Liveline Ltd
+ 0000 PCI230
+ 0001 PCI242
+ 0002 PCI244
+ 0003 PCI247
+ 0004 PCI248
+ 0005 PCI249
+ 0006 PCI260
+ 0007 PCI224
+ 0008 PCI234
+ 0009 PCI236
14dd Boulder Design Labs Inc
14de Applied Integration Corporation
14df ASIC Communications Corp
1d44 DPT
a400 PM2x24/PM3224
1de1 Tekram Technology Co.,Ltd.
+ 0391 TRM-S1040
2020 DC-390
690c 690c
dc29 DC290
cafe Chrysalis-ITS
cccc Catapult Communications
d4d4 Dy4 Systems Inc
+d84d Exsys
e000 Winbond
e000 W89C940
e159 Tiger Jet Network Inc.
printk(KERN_INFO "isapnp: AWE32 quirk - adding two ports\n");
}
+static void __init quirk_cmi8330_resources(struct pci_dev *dev)
+{
+ struct isapnp_resources *res = dev->sysdata;
+
+ for ( ; res ; res = res->alt ) {
+
+ struct isapnp_irq *irq;
+ struct isapnp_dma *dma;
+
+ for( irq = res->irq; irq; irq = irq->next ) // Valid irqs are 5, 7, 10
+ irq->map = 0x04A0; // 0000 0100 1010 0000
+
+ for( dma = res->dma; dma; dma = dma->next ) // Valid 8bit dma channels are 1,3
+ if( ( dma->flags & IORESOURCE_DMA_TYPE_MASK ) == IORESOURCE_DMA_8BIT )
+ dma->map = 0x000A;
+ }
+ printk(KERN_INFO "isapnp: CMI8330 quirk - fixing interrupts and dma\n");
+}
+
/*
* ISAPnP Quirks
quirk_awe32_resources },
{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0023),
quirk_awe32_resources },
+ { ISAPNP_VENDOR('@','X','@'), ISAPNP_DEVICE(0x0001), // CMI8330
+ quirk_cmi8330_resources },
{ 0 }
};
static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
/* Globals */
-char *tw_driver_version="1.0.000";
+char *tw_driver_version="1.1.000";
TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
int tw_device_extension_count = 0;
command_packet->status = 0;
command_packet->flags = 0;
- if ((srb->cmnd[0] == WRITE_6) || (srb->cmnd[0] == WRITE_10)) {
+ if (srb->cmnd[0] == WRITE_10) {
if ((srb->cmnd[1] & 0x8) || (srb->cmnd[1] & 0x10))
command_packet->flags = 1;
}
dep_tristate 'DEC SII Scsi Driver' CONFIG_SCSI_DECSII $CONFIG_SCSI
fi
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- dep_tristate '3ware Hardware ATA-RAID support (EXPERIMENTAL)' CONFIG_BLK_DEV_3W_XXXX_RAID $CONFIG_SCSI
+if [ "$CONFIG_PCI" = "y" ]; then
+ dep_tristate '3ware Hardware ATA-RAID support' CONFIG_BLK_DEV_3W_XXXX_RAID $CONFIG_SCSI
fi
dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI
dep_tristate 'ACARD SCSI support' CONFIG_SCSI_ACARD $CONFIG_SCSI
fi
dep_tristate ' Aztech Sound Galaxy (non-PnP) cards' CONFIG_SOUND_SGALAXY $CONFIG_SOUND_OSS
+ dep_tristate ' ACI mixer (miroPCM12)' CONFIG_SOUND_ACI_MIXER $CONFIG_SOUND_OSS
dep_tristate ' Crystal CS4232 based (PnP) cards' CONFIG_SOUND_CS4232 $CONFIG_SOUND_OSS
dep_tristate ' Ensoniq SoundScape support' CONFIG_SOUND_SSCAPE $CONFIG_SOUND_OSS
dep_tristate ' Gravis Ultrasound support' CONFIG_SOUND_GUS $CONFIG_SOUND_OSS
dep_tristate ' PSS (AD1848, ADSP-2115, ESC614) support' CONFIG_SOUND_PSS $CONFIG_SOUND_OSS
if [ "$CONFIG_SOUND_PSS" = "y" -o "$CONFIG_SOUND_PSS" = "m" ]; then
bool ' Enable PSS mixer (Beethoven ADSP-16 and other compatibile)' CONFIG_PSS_MIXER
- fi
- bool ' Have DSPxxx.LD firmware file' CONFIG_PSS_HAVE_BOOT
- if [ "$CONFIG_PSS_HAVE_BOOT" = "y" ]; then
- string ' Full pathname of DSPxxx.LD firmware file' CONFIG_PSS_BOOT_FILE /etc/sound/dsp001.ld
+ bool ' Have DSPxxx.LD firmware file' CONFIG_PSS_HAVE_BOOT
+ if [ "$CONFIG_PSS_HAVE_BOOT" = "y" ]; then
+ string ' Full pathname of DSPxxx.LD firmware file' CONFIG_PSS_BOOT_FILE /etc/sound/dsp001.ld
+ fi
fi
dep_tristate ' SoftOSS software wave table engine' CONFIG_SOUND_SOFTOSS $CONFIG_SOUND_OSS
fi
dep_tristate ' 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SOUND_SB $CONFIG_SOUND_OSS
+ dep_tristate ' AWE32 synth' CONFIG_SOUND_AWE32_SYNTH $CONFIG_SOUND_OSS
dep_tristate ' Full support for Turtle Beach WaveFront (Tropez Plus, Tropez, Maui) synth/soundcards' CONFIG_SOUND_WAVEFRONT $CONFIG_SOUND_OSS m
dep_tristate ' Limited support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_SOUND_MAUI $CONFIG_SOUND_OSS
if [ "$CONFIG_SOUND_MAUI" = "y" ]; then
dep_tristate ' Yamaha OPL3-SA2, SA3, and SAx based PnP cards' CONFIG_SOUND_OPL3SA2 $CONFIG_SOUND_OSS
dep_tristate ' 6850 UART support' CONFIG_SOUND_UART6850 $CONFIG_SOUND_OSS
+ dep_tristate ' Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_SOUND_AEDSP16 $CONFIG_SOUND_OSS
+ if [ "$CONFIG_SOUND_AEDSP16" = "y" -o "$CONFIG_SOUND_AEDSP16" = "m" ]; then
+ bool ' SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600
+ if [ "$CONFIG_SC6600" = "y" ]; then
+ bool ' Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY
+ int ' SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' CONFIG_SC6600_CDROM 4
+ hex ' SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0
+ fi
+ if [ "$CONFIG_SOUND_SB" = "y" -o "$CONFIG_SOUND_SB" = "m" ]; then
+ if [ "$CONFIG_AEDSP16_MSS" != "y" ]; then
+ bool ' Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO
+ fi
+ fi
+ if [ "$CONFIG_SOUND_MSS" = "y" -o "$CONFIG_SOUND_MSS" = "m" ]; then
+ if [ "$CONFIG_AEDSP16_SBPRO" != "y" ]; then
+ bool ' Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS
+ fi
+ fi
+ if [ "$CONFIG_SOUND_MPU401" = "y" -o "$CONFIG_SOUND_MPU401" = "m" ]; then
+ bool ' Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
+ fi
+ fi
+
if [ "$CONFIG_ARM" = "y" ]; then
dep_tristate ' VIDC 16-bit sound' CONFIG_SOUND_VIDC $CONFIG_SOUND_OSS
dep_tristate ' Netwinder WaveArtist' CONFIG_SOUND_WAVEARTIST $CONFIG_SOUND_OSS
fi
-
-
- # Additional low level drivers.
-
- mainmenu_option next_comment
- comment 'Additional low level sound drivers'
- bool 'Additional low level sound drivers' CONFIG_LOWLEVEL_SOUND $CONFIG_SOUND_OSS
- if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
- source drivers/sound/lowlevel/Config.in
- fi
- endmenu
+
fi
SUB_DIRS :=
MOD_SUB_DIRS :=
MOD_IN_SUB_DIRS :=
-ALL_SUB_DIRS := $(SUB_DIRS) lowlevel
-
-ifeq ($(CONFIG_LOWLEVEL_SOUND),y)
- SUB_DIRS += lowlevel
- MOD_IN_SUB_DIRS += lowlevel
-endif
-
+ALL_SUB_DIRS := $(SUB_DIRS)
# All of the (potential) objects that export symbols.
# Please leave it as is, cause the link order is significant !
+obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o
obj-$(CONFIG_SOUND_OPL3SA1) += opl3sa.o ad1848.o uart401.o
obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o
obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o ad1848.o
obj-$(CONFIG_SOUND_AD1816) += ad1816.o
+obj-$(CONFIG_SOUND_ACI_MIXER) += aci.o
+obj-$(CONFIG_SOUND_AWE32_SYNTH) += awe_wave.o
obj-$(CONFIG_SOUND_VIA82CXXX) += via82cxxx_audio.o sb_lib.o uart401.o ac97.o
obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o
# Declare multi-part drivers.
-list-multi := sound.o gus.o pas2.o sb.o softoss2.o vidc_mod.o \
+list-multi := sound.o gus.o pas2.o sb.o sb_lib.o softoss2.o vidc_mod.o \
soundcore.o wavefront.o
sound-objs := \
OX_OBJS := $(filter $(export-objs), $(obj-y))
M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
-#MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m)))
-#MIX_OBJS := $(sort $(filter $(export-objs), $(int-m)))
+MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m)))
+MIX_OBJS := $(sort $(filter $(export-objs), $(int-m)))
ifeq ($(CONFIG_LOWLEVEL_SOUND),y)
O_OBJS += lowlevel/lowlevel.o
--- /dev/null
+/*
+ * Audio Command Interface (ACI) driver (sound/aci.c)
+ *
+ * ACI is a protocol used to communicate with the microcontroller on
+ * some sound cards produced by miro, e.g. the miroSOUND PCM12 and
+ * PCM20. The ACI has been developed for miro by Norberto Pellicci
+ * <pellicci@home.com>. Special thanks to both him and miro for
+ * providing the ACI specification.
+ *
+ * The main function of the ACI is to control the mixer and to get a
+ * product identification. On the PCM20, ACI also controls the radio
+ * tuner on this card, this is supported in the Video for Linux
+ * radio-miropcm20 driver.
+ *
+ * This Voxware ACI driver currently only supports the ACI functions
+ * on the miroSOUND PCM12 and PCM20 card. Support for miro sound cards
+ * with additional ACI functions can easily be added later.
+ *
+ * / NOTE / When compiling as a module, make sure to load the module
+ * after loading the mad16 module. The initialisation code expects the
+ * MAD16 default mixer to be already available.
+ *
+ * Revision history:
+ *
+ * 1995-11-10 Markus Kuhn <mskuhn@cip.informatik.uni-erlangen.de>
+ * First version written.
+ * 1995-12-31 Markus Kuhn
+ * Second revision, general code cleanup.
+ * 1996-05-16 Hannu Savolainen
+ * Integrated with other parts of the driver.
+ * 1996-05-28 Markus Kuhn
+ * Initialize CS4231A mixer, make ACI first mixer,
+ * use new private mixer API for solo mode.
+ * 1998-08-18 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
+ * Small modification to export ACI functions and
+ * complete modularisation.
+ */
+
+/*
+ * Some driver specific information and features:
+ *
+ * This mixer driver identifies itself to applications as "ACI" in
+ * mixer_info.id as retrieved by ioctl(fd, SOUND_MIXER_INFO, &mixer_info).
+ *
+ * Proprietary mixer features that go beyond the standard OSS mixer
+ * interface are:
+ *
+ * Full duplex solo configuration:
+ *
+ * int solo_mode;
+ * ioctl(fd, SOUND_MIXER_PRIVATE1, &solo_mode);
+ *
+ * solo_mode = 0: deactivate solo mode (default)
+ * solo_mode > 0: activate solo mode
+ * With activated solo mode, the PCM input can not any
+ * longer hear the signals produced by the PCM output.
+ * Activating solo mode is important in duplex mode in order
+ * to avoid feedback distortions.
+ * solo_mode < 0: do not change solo mode (just retrieve the status)
+ *
+ * When the ioctl() returns 0, solo_mode contains the previous
+ * status (0 = deactivated, 1 = activated). If solo mode is not
+ * implemented on this card, ioctl() returns -1 and sets errno to
+ * EINVAL.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "sound_config.h"
+
+#undef DEBUG /* if defined, produce a verbose report via syslog */
+
+int aci_port = 0x354; /* as determined by bit 4 in the OPTi 929 MC4 register */
+unsigned char aci_idcode[2] = {0, 0}; /* manufacturer and product ID */
+unsigned char aci_version = 0; /* ACI firmware version */
+int aci_solo; /* status bit of the card that can't be *
+ * checked with ACI versions prior to 0xb0 */
+
+static int aci_present = 0;
+
+#ifdef MODULE /* Whether the aci mixer is to be reset. */
+int aci_reset = 0; /* Default: don't reset if the driver is a */
+MODULE_PARM(aci_reset,"i");
+#else /* module; use "insmod aci.o aci_reset=1" */
+int aci_reset = 1; /* to override. */
+#endif
+
+
+#define COMMAND_REGISTER (aci_port)
+#define STATUS_REGISTER (aci_port + 1)
+#define BUSY_REGISTER (aci_port + 2)
+
+/*
+ * Wait until the ACI microcontroller has set the READYFLAG in the
+ * Busy/IRQ Source Register to 0. This is required to avoid
+ * overrunning the sound card microcontroller. We do a busy wait here,
+ * because the microcontroller is not supposed to signal a busy
+ * condition for more than a few clock cycles. In case of a time-out,
+ * this function returns -1.
+ *
+ * This busy wait code normally requires less than 15 loops and
+ * practically always less than 100 loops on my i486/DX2 66 MHz.
+ *
+ * Warning: Waiting on the general status flag after reseting the MUTE
+ * function can take a VERY long time, because the PCM12 does some kind
+ * of fade-in effect. For this reason, access to the MUTE function has
+ * not been implemented at all.
+ */
+
+static int busy_wait(void)
+{
+ long timeout;
+
+ for (timeout = 0; timeout < 10000000L; timeout++)
+ if ((inb_p(BUSY_REGISTER) & 1) == 0)
+ return 0;
+
+#ifdef DEBUG
+ printk("ACI: READYFLAG timed out.\n");
+#endif
+
+ return -1;
+}
+
+
+/*
+ * Read the GENERAL STATUS register.
+ */
+
+static int read_general_status(void)
+{
+ unsigned long flags;
+ int status;
+
+ save_flags(flags);
+ cli();
+
+ if (busy_wait()) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ status = (unsigned) inb_p(STATUS_REGISTER);
+ restore_flags(flags);
+ return status;
+}
+
+
+/*
+ * The four ACI command types (implied, write, read and indexed) can
+ * be sent to the microcontroller using the following four functions.
+ * If a problem occurred, they return -1.
+ */
+
+int aci_implied_cmd(unsigned char opcode)
+{
+ unsigned long flags;
+
+#ifdef DEBUG
+ printk("ACI: aci_implied_cmd(0x%02x)\n", opcode);
+#endif
+
+ save_flags(flags);
+ cli();
+
+ if (read_general_status() < 0 || busy_wait()) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ outb_p(opcode, COMMAND_REGISTER);
+
+ restore_flags(flags);
+ return 0;
+}
+
+
+int aci_write_cmd(unsigned char opcode, unsigned char parameter)
+{
+ unsigned long flags;
+ int status;
+
+#ifdef DEBUG
+ printk("ACI: aci_write_cmd(0x%02x, 0x%02x)\n", opcode, parameter);
+#endif
+
+ save_flags(flags);
+ cli();
+
+ if (read_general_status() < 0 || busy_wait()) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ outb_p(opcode, COMMAND_REGISTER);
+ if (busy_wait()) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ outb_p(parameter, COMMAND_REGISTER);
+
+ if ((status = read_general_status()) < 0) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ /* polarity of the INVALID flag depends on ACI version */
+ if ((aci_version < 0xb0 && (status & 0x40) != 0) ||
+ (aci_version >= 0xb0 && (status & 0x40) == 0)) {
+ restore_flags(flags);
+ printk("ACI: invalid write command 0x%02x, 0x%02x.\n",
+ opcode, parameter);
+ return -1;
+ }
+
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * This write command send 2 parameters instead of one.
+ * Only used in PCM20 radio frequency tuning control
+ */
+
+int aci_write_cmd_d(unsigned char opcode, unsigned char parameter, unsigned char parameter2)
+{
+ unsigned long flags;
+ int status;
+
+#ifdef DEBUG
+ printk("ACI: aci_write_cmd_d(0x%02x, 0x%02x)\n", opcode, parameter, parameter2);
+#endif
+
+ save_flags(flags);
+ cli();
+
+ if (read_general_status() < 0 || busy_wait()) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ outb_p(opcode, COMMAND_REGISTER);
+ if (busy_wait()) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ outb_p(parameter, COMMAND_REGISTER);
+ if (busy_wait()) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ outb_p(parameter2, COMMAND_REGISTER);
+
+ if ((status = read_general_status()) < 0) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ /* polarity of the INVALID flag depends on ACI version */
+ if ((aci_version < 0xb0 && (status & 0x40) != 0) ||
+ (aci_version >= 0xb0 && (status & 0x40) == 0)) {
+ restore_flags(flags);
+#if 0 /* Frequency tuning works, but the INVALID flag is set ??? */
+ printk("ACI: invalid write (double) command 0x%02x, 0x%02x, 0x%02x.\n",
+ opcode, parameter, parameter2);
+#endif
+ return -1;
+ }
+
+ restore_flags(flags);
+ return 0;
+}
+
+int aci_read_cmd(unsigned char opcode, int length, unsigned char *parameter)
+{
+ unsigned long flags;
+ int i = 0;
+
+ save_flags(flags);
+ cli();
+
+ if (read_general_status() < 0) {
+ restore_flags(flags);
+ return -1;
+ }
+ while (i < length) {
+ if (busy_wait()) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ outb_p(opcode, COMMAND_REGISTER);
+ if (busy_wait()) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ parameter[i++] = inb_p(STATUS_REGISTER);
+#ifdef DEBUG
+ if (i == 1)
+ printk("ACI: aci_read_cmd(0x%02x, %d) = 0x%02x\n",
+ opcode, length, parameter[i-1]);
+ else
+ printk("ACI: aci_read_cmd cont.: 0x%02x\n", parameter[i-1]);
+#endif
+ }
+
+ restore_flags(flags);
+ return 0;
+}
+
+
+int aci_indexed_cmd(unsigned char opcode, unsigned char index,
+ unsigned char *parameter)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if (read_general_status() < 0 || busy_wait()) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ outb_p(opcode, COMMAND_REGISTER);
+ if (busy_wait()) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ outb_p(index, COMMAND_REGISTER);
+ if (busy_wait()) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ *parameter = inb_p(STATUS_REGISTER);
+#ifdef DEBUG
+ printk("ACI: aci_indexed_cmd(0x%02x, 0x%02x) = 0x%02x\n", opcode, index,
+ *parameter);
+#endif
+
+ restore_flags(flags);
+ return 0;
+}
+
+
+/*
+ * The following macro SCALE can be used to scale one integer volume
+ * value into another one using only integer arithmetic. If the input
+ * value x is in the range 0 <= x <= xmax, then the result will be in
+ * the range 0 <= SCALE(xmax,ymax,x) <= ymax.
+ *
+ * This macro has for all xmax, ymax > 0 and all 0 <= x <= xmax the
+ * following nice properties:
+ *
+ * - SCALE(xmax,ymax,xmax) = ymax
+ * - SCALE(xmax,ymax,0) = 0
+ * - SCALE(xmax,ymax,SCALE(ymax,xmax,SCALE(xmax,ymax,x))) = SCALE(xmax,ymax,x)
+ *
+ * In addition, the rounding error is minimal and nicely distributed.
+ * The proofs are left as an exercise to the reader.
+ */
+
+#define SCALE(xmax,ymax,x) (((x)*(ymax)+(xmax)/2)/(xmax))
+
+
+static int getvolume(caddr_t arg,
+ unsigned char left_index, unsigned char right_index)
+{
+ int vol;
+ unsigned char buf;
+
+ /* left channel */
+ if (aci_indexed_cmd(0xf0, left_index, &buf))
+ return -EIO;
+ vol = SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0);
+
+ /* right channel */
+ if (aci_indexed_cmd(0xf0, right_index, &buf))
+ return -EIO;
+ vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8;
+
+ return (*(int *) arg = vol);
+}
+
+
+static int setvolume(caddr_t arg,
+ unsigned char left_index, unsigned char right_index)
+{
+ int vol, ret;
+
+ /* left channel */
+ vol = *(int *)arg & 0xff;
+ if (vol > 100)
+ vol = 100;
+ vol = SCALE(100, 0x20, vol);
+ if (aci_write_cmd(left_index, 0x20 - vol))
+ return -EIO;
+ ret = SCALE(0x20, 100, vol);
+
+
+ /* right channel */
+ vol = (*(int *)arg >> 8) & 0xff;
+ if (vol > 100)
+ vol = 100;
+ vol = SCALE(100, 0x20, vol);
+ if (aci_write_cmd(right_index, 0x20 - vol))
+ return -EIO;
+ ret |= SCALE(0x20, 100, vol) << 8;
+
+ return (*(int *) arg = ret);
+}
+
+
+static int
+aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
+{
+ int status, vol;
+ unsigned char buf;
+
+ /* handle solo mode control */
+ if (cmd == SOUND_MIXER_PRIVATE1) {
+ if (*(int *) arg >= 0) {
+ aci_solo = !!*(int *) arg;
+ if (aci_write_cmd(0xd2, aci_solo))
+ return -EIO;
+ } else if (aci_version >= 0xb0) {
+ if ((status = read_general_status()) < 0)
+ return -EIO;
+ return (*(int *) arg = (status & 0x20) == 0);
+ }
+
+ return (*(int *) arg = aci_solo);
+ }
+
+ if (((cmd >> 8) & 0xff) == 'M') {
+ if (cmd & IOC_IN)
+ /* read and write */
+ switch (cmd & 0xff) {
+ case SOUND_MIXER_VOLUME:
+ return setvolume(arg, 0x01, 0x00);
+ case SOUND_MIXER_CD:
+ return setvolume(arg, 0x3c, 0x34);
+ case SOUND_MIXER_MIC:
+ return setvolume(arg, 0x38, 0x30);
+ case SOUND_MIXER_LINE:
+ return setvolume(arg, 0x39, 0x31);
+ case SOUND_MIXER_SYNTH:
+ return setvolume(arg, 0x3b, 0x33);
+ case SOUND_MIXER_PCM:
+ return setvolume(arg, 0x3a, 0x32);
+ case SOUND_MIXER_LINE1: /* AUX1 */
+ return setvolume(arg, 0x3d, 0x35);
+ case SOUND_MIXER_LINE2: /* AUX2 */
+ return setvolume(arg, 0x3e, 0x36);
+ case SOUND_MIXER_IGAIN: /* MIC pre-amp */
+ vol = *(int *) arg & 0xff;
+ if (vol > 100)
+ vol = 100;
+ vol = SCALE(100, 3, vol);
+ if (aci_write_cmd(0x03, vol))
+ return -EIO;
+ vol = SCALE(3, 100, vol);
+ return (*(int *) arg = vol | (vol << 8));
+ case SOUND_MIXER_RECSRC:
+ return (*(int *) arg = 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+ else
+ /* only read */
+ switch (cmd & 0xff) {
+ case SOUND_MIXER_DEVMASK:
+ return (*(int *) arg =
+ SOUND_MASK_VOLUME | SOUND_MASK_CD |
+ SOUND_MASK_MIC | SOUND_MASK_LINE |
+ SOUND_MASK_SYNTH | SOUND_MASK_PCM |
+#if 0
+ SOUND_MASK_IGAIN |
+#endif
+ SOUND_MASK_LINE1 | SOUND_MASK_LINE2);
+ break;
+ case SOUND_MIXER_STEREODEVS:
+ return (*(int *) arg =
+ SOUND_MASK_VOLUME | SOUND_MASK_CD |
+ SOUND_MASK_MIC | SOUND_MASK_LINE |
+ SOUND_MASK_SYNTH | SOUND_MASK_PCM |
+ SOUND_MASK_LINE1 | SOUND_MASK_LINE2);
+ break;
+ case SOUND_MIXER_RECMASK:
+ return (*(int *) arg = 0);
+ break;
+ case SOUND_MIXER_RECSRC:
+ return (*(int *) arg = 0);
+ break;
+ case SOUND_MIXER_CAPS:
+ return (*(int *) arg = 0);
+ break;
+ case SOUND_MIXER_VOLUME:
+ return getvolume(arg, 0x04, 0x03);
+ case SOUND_MIXER_CD:
+ return getvolume(arg, 0x0a, 0x09);
+ case SOUND_MIXER_MIC:
+ return getvolume(arg, 0x06, 0x05);
+ case SOUND_MIXER_LINE:
+ return getvolume(arg, 0x08, 0x07);
+ case SOUND_MIXER_SYNTH:
+ return getvolume(arg, 0x0c, 0x0b);
+ case SOUND_MIXER_PCM:
+ return getvolume(arg, 0x0e, 0x0d);
+ case SOUND_MIXER_LINE1: /* AUX1 */
+ return getvolume(arg, 0x11, 0x10);
+ case SOUND_MIXER_LINE2: /* AUX2 */
+ return getvolume(arg, 0x13, 0x12);
+ case SOUND_MIXER_IGAIN: /* MIC pre-amp */
+ if (aci_indexed_cmd(0xf0, 0x21, &buf))
+ return -EIO;
+ vol = SCALE(3, 100, buf <= 3 ? buf : 3);
+ vol |= vol << 8;
+ return (*(int *) arg = vol);
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return -EINVAL;
+}
+
+
+static struct mixer_operations aci_mixer_operations =
+{
+ "ACI",
+ "ACI mixer",
+ aci_mixer_ioctl,
+ NULL
+};
+
+static unsigned char
+mad_read (int port)
+{
+ outb (0xE3, 0xf8f); /* Write MAD16 password */
+ return inb (port); /* Read from port */
+}
+
+
+/*
+ * Check, whether there actually is any ACI port operational and if
+ * one was found, then initialize the ACI interface, reserve the I/O
+ * addresses and attach the new mixer to the relevant VoxWare data
+ * structures.
+ *
+ * Returns: 1 ACI mixer detected
+ * 0 nothing there
+ *
+ * There is also an internal mixer in the codec (CS4231A or AD1845),
+ * that deserves no purpose in an ACI based system which uses an
+ * external ACI controlled stereo mixer. Make sure that this codec
+ * mixer has the AUX1 input selected as the recording source, that the
+ * input gain is set near maximum and that the other channels going
+ * from the inputs to the codec output are muted.
+ */
+
+static int __init attach_aci(void)
+{
+ char *boardname = "unknown";
+ int volume;
+
+#define MC4_PORT 0xf90
+
+ aci_port =
+ (mad_read(MC4_PORT) & 0x10) ? 0x344 : 0x354;
+
+ if (check_region(aci_port, 3)) {
+#ifdef DEBUG
+ printk("ACI: I/O area 0x%03x-0x%03x already used.\n",
+ aci_port, aci_port+2);
+#endif
+ return 0;
+ }
+
+ if (aci_read_cmd(0xf2, 2, aci_idcode)) {
+#ifdef DEBUG
+ printk("ACI: Failed to read idcode.\n");
+#endif
+ return 0;
+ }
+
+ if (aci_read_cmd(0xf1, 1, &aci_version)) {
+#ifdef DEBUG
+ printk("ACI: Failed to read version.\n");
+#endif
+ return 0;
+ }
+
+ if (aci_idcode[0] == 0x6d) {
+ /* It looks like a miro sound card. */
+ switch (aci_idcode[1]) {
+ case 0x41:
+ boardname = "PCM1 pro / early PCM12";
+ break;
+ case 0x42:
+ boardname = "PCM12";
+ break;
+ case 0x43:
+ boardname = "PCM20";
+ break;
+ default:
+ boardname = "unknown miro";
+ }
+ } else
+#ifndef DEBUG
+ return 0;
+#endif
+
+ printk("<ACI %02x, id %02x %02x (%s)> at 0x%03x\n",
+ aci_version, aci_idcode[0], aci_idcode[1], boardname, aci_port);
+
+ if (aci_reset) {
+ /* initialize ACI mixer */
+ aci_implied_cmd(0xff);
+ aci_solo = 0;
+ }
+
+ /* attach the mixer */
+ request_region(aci_port, 3, "sound mixer (ACI)");
+ if (num_mixers < MAX_MIXER_DEV) {
+ if (num_mixers > 0 &&
+ !strncmp("MAD16 WSS", mixer_devs[num_mixers-1]->name, 9)) {
+ /*
+ * The previously registered mixer device is the CS4231A which
+ * has no function on an ACI card. Make the ACI mixer the first
+ * of the two mixer devices.
+ */
+ mixer_devs[num_mixers] = mixer_devs[num_mixers-1];
+ mixer_devs[num_mixers-1] = &aci_mixer_operations;
+ /*
+ * Initialize the CS4231A mixer with reasonable values. It is
+ * unlikely that the user ever will want to change these as all
+ * channels can be mixed via ACI.
+ */
+ volume = 0x6464;
+ mixer_devs[num_mixers]->ioctl(num_mixers,
+ SOUND_MIXER_WRITE_PCM, (caddr_t) &volume);
+ volume = 0x6464;
+ mixer_devs[num_mixers]->ioctl(num_mixers,
+ SOUND_MIXER_WRITE_IGAIN, (caddr_t) &volume);
+ volume = 0;
+ mixer_devs[num_mixers]->ioctl(num_mixers,
+ SOUND_MIXER_WRITE_SPEAKER, (caddr_t) &volume);
+ volume = 0;
+ mixer_devs[num_mixers]->ioctl(num_mixers,
+ SOUND_MIXER_WRITE_MIC, (caddr_t) &volume);
+ volume = 0;
+ mixer_devs[num_mixers]->ioctl(num_mixers,
+ SOUND_MIXER_WRITE_IMIX, (caddr_t) &volume);
+ volume = 0;
+ mixer_devs[num_mixers]->ioctl(num_mixers,
+ SOUND_MIXER_WRITE_LINE1, (caddr_t) &volume);
+ volume = 0;
+ mixer_devs[num_mixers]->ioctl(num_mixers,
+ SOUND_MIXER_WRITE_LINE2, (caddr_t) &volume);
+ volume = 0;
+ mixer_devs[num_mixers]->ioctl(num_mixers,
+ SOUND_MIXER_WRITE_LINE3, (caddr_t) &volume);
+ volume = SOUND_MASK_LINE1;
+ mixer_devs[num_mixers]->ioctl(num_mixers,
+ SOUND_MIXER_WRITE_RECSRC, (caddr_t) &volume);
+ num_mixers++;
+ } else
+ mixer_devs[num_mixers++] = &aci_mixer_operations;
+ }
+
+ /* Just do something; otherwise the first write command fails, at
+ * least with my PCM20.
+ */
+ aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_READ_VOLUME, (caddr_t) &volume);
+
+ if (aci_reset) {
+ /* Initialize ACI mixer with reasonable power-up values */
+ volume = 0x3232;
+ aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_VOLUME, (caddr_t) &volume);
+ volume = 0x3232;
+ aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_SYNTH, (caddr_t) &volume);
+ volume = 0x3232;
+ aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_PCM, (caddr_t) &volume);
+ volume = 0x3232;
+ aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE, (caddr_t) &volume);
+ volume = 0x3232;
+ aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_MIC, (caddr_t) &volume);
+ volume = 0x3232;
+ aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_CD, (caddr_t) &volume);
+ volume = 0x3232;
+ aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE1, (caddr_t) &volume);
+ volume = 0x3232;
+ aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE2, (caddr_t) &volume);
+ }
+
+ aci_present = 1;
+
+ return 1;
+}
+
+static void __exit unload_aci(void)
+{
+ if (aci_present)
+ release_region(aci_port, 3);
+}
+
+module_init(attach_aci);
+module_exit(unload_aci);
io = ints[1];
irq = ints[2];
dma = ints[3];
- dma16 = ints[4];
+ dma2 = ints[4];
return 1;
}
--- /dev/null
+/*
+ drivers/sound/lowlevel/aedsp16.c
+
+ Audio Excel DSP 16 software configuration routines
+ Copyright (C) 1995,1996,1997,1998 Riccardo Facchetti (fizban@tin.it)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+/*
+ * Include the main OSS Lite header file. It include all the os, OSS Lite, etc
+ * headers needed by this source.
+ */
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include "sound_config.h"
+#include "soundmodule.h"
+
+/*
+ * Sanity checks
+ */
+
+#if defined(CONFIG_SOUND_AEDSP16_SBPRO) && defined(CONFIG_SOUND_AEDSP16_MSS)
+#error You have to enable only one of the MSS and SBPRO emulations.
+#endif
+
+/*
+
+ READ THIS
+
+ This module started to configure the Audio Excel DSP 16 Sound Card.
+ Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards.
+
+ NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this
+ audio card and want to see the kernel support for it, please contact me.
+
+ Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401
+ compatible card.
+ It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq),
+ so before this module, the only way to configure the DSP under linux was
+ boot the MS-DOS loading the sound.sys device driver (this driver soft-
+ configure the sound board hardware by massaging someone of its registers),
+ and then ctrl-alt-del to boot linux with the DSP configured by the DOS
+ driver.
+
+ This module works configuring your Audio Excel DSP 16's irq, dma and
+ mpu-401-irq. The OSS Lite routines rely on the fact that if the
+ hardware is there, they can detect it. The problem with AEDSP16 is
+ that no hardware can be found by the probe routines if the sound card
+ is not configured properly. Sometimes the kernel probe routines can find
+ an SBPRO even when the card is not configured (this is the standard setup
+ of the card), but the SBPRO emulation don't work well if the card is not
+ properly initialized. For this reason
+
+ aedsp16_init_board()
+
+ routine is called before the OSS Lite probe routines try to detect the
+ hardware.
+
+ NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS)
+
+ NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards
+ have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They
+ have to be configured by software.
+
+ NOTE: The driver is merged with the new OSS Lite sound driver. It works
+ as a lowlevel driver.
+
+ The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS;
+ the OSS Lite sound driver can be configured for SBPRO and MSS cards
+ at the same time, but the aedsp16 can't be two cards!!
+ When we configure it, we have to choose the SBPRO or the MSS emulation
+ for AEDSP16. We also can install a *REAL* card of the other type (see [1]).
+
+ NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO
+ please let me know if it works.
+
+ The MPU-401 support can be compiled in together with one of the other
+ two operating modes.
+
+ NOTE: This is something like plug-and-play: we have only to plug
+ the AEDSP16 board in the socket, and then configure and compile
+ a kernel that uses the AEDSP16 software configuration capability.
+ No jumper setting is needed!
+
+ For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3
+ you have just to make config the OSS Lite package, configuring
+ the AEDSP16 sound card, then activating the SBPro emulation mode
+ and at last configuring IRQ and DMA.
+ Compile the kernel and run it.
+
+ NOTE: This means for SC-6000 cards that you can choose irq and dma,
+ but not the I/O addresses. To change I/O addresses you have to set
+ them with jumpers. For SC-6600 cards you have no jumpers so you have
+ to set up your full card configuration in the make config.
+
+ You can change the irq/dma/mirq settings WITHOUT THE NEED to open
+ your computer and massage the jumpers (there are no irq/dma/mirq
+ jumpers to be configured anyway, only I/O BASE values have to be
+ configured with jumpers)
+
+ For some ununderstandable reason, the card default of irq 7, dma 1,
+ don't work for me. Seems to be an IRQ or DMA conflict. Under heavy
+ HDD work, the kernel start to erupt out a lot of messages like:
+
+ 'Sound: DMA timed out - IRQ/DRQ config error?'
+
+ For what I can say, I have NOT any conflict at irq 7 (under linux I'm
+ using the lp polling driver), and dma line 1 is unused as stated by
+ /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so
+ I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows!
+ Anyway a setting of irq 10, dma 3 works really fine.
+
+ NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know
+ the emulation mode, all the installed hardware and the hardware
+ configuration (irq and dma settings of all the hardware).
+
+ This init module should work with SBPRO+MSS, when one of the two is
+ the AEDSP16 emulation and the other the real card. (see [1])
+ For example:
+
+ AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other
+ AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other
+
+ MPU401 should work. (see [2])
+
+ [1]
+ ---
+ Date: Mon, 29 Jul 1997 08:35:40 +0100
+ From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk>
+
+ [...]
+ Just to let you know got my Audio Excel (emulating a MSS) working
+ with my original SB16, thanks for the driver!
+ [...]
+ ---
+
+ [2] Not tested by me for lack of hardware.
+
+ TODO, WISHES AND TECH
+
+ - About I/O ports allocation -
+
+ Request the 2x0h region (port base) in any case if we are using this card.
+
+ NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16
+ port base region (see code) does not mean necessarily that we are emulating
+ sbpro. Even if this region is the sbpro I/O ports region, we use this
+ region to access the control registers of the card, and if emulating
+ sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro
+ registers are not used, in no way, to emulate an sbpro: they are
+ used only for configuration purposes.
+
+ Started Fri Mar 17 16:13:18 MET 1995
+
+ v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c)
+ - Initial code.
+ v0.2 (ALPHA)
+ - Cleanups.
+ - Integrated with Linux voxware v 2.90-2 kernel sound driver.
+ - SoundBlaster Pro mode configuration.
+ - Microsoft Sound System mode configuration.
+ - MPU-401 mode configuration.
+ v0.3 (ALPHA)
+ - Cleanups.
+ - Rearranged the code to let aedsp16_init_board be more general.
+ - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h
+ inclusion too. We rely on os.h
+ - Used the to get a variable
+ len string (we are not sure about the len of Copyright string).
+ This works with any SB and compatible.
+ - Added the code to request_region at device init (should go in
+ the main body of voxware).
+ v0.4 (BETA)
+ - Better configure.c patch for aedsp16 configuration (better
+ logic of inclusion of AEDSP16 support)
+ - Modified the conditional compilation to better support more than
+ one sound card of the emulated type (read the NOTES above)
+ - Moved the sb init routine from the attach to the very first
+ probe in sb_card.c
+ - Rearrangements and cleanups
+ - Wiped out some unnecessary code and variables: this is kernel
+ code so it is better save some TEXT and DATA
+ - Fixed the request_region code. We must allocate the aedsp16 (sbpro)
+ I/O ports in any case because they are used to access the DSP
+ configuration registers and we can not allow anyone to get them.
+ v0.5
+ - cleanups on comments
+ - prep for diffs against v3.0-proto-950402
+ v0.6
+ - removed the request_region()s when compiling the MODULE sound.o
+ because we are not allowed (by the actual voxware structure) to
+ release_region()
+ v0.7 (pre ALPHA, not distributed)
+ - started porting this module to kernel 1.3.84. Dummy probe/attach
+ routines.
+ v0.8 (ALPHA)
+ - attached all the init routines.
+ v0.9 (BETA)
+ - Integrated with linux-pre2.0.7
+ - Integrated with configuration scripts.
+ - Cleaned up and beautyfied the code.
+ v0.9.9 (BETA)
+ - Thanks to Piercarlo Grandi: corrected the conditonal compilation code.
+ Now only the code configured is compiled in, with some memory saving.
+ v0.9.10
+ - Integration into the sound/lowlevel/ section of the sound driver.
+ - Re-organized the code.
+ v0.9.11 (not distributed)
+ - Rewritten the init interface-routines to initialize the AEDSP16 in
+ one shot.
+ - More cosmetics.
+ - SC-6600 support.
+ - More soft/hard configuration.
+ v0.9.12
+ - Refined the v0.9.11 code with conditional compilation to distinguish
+ between SC-6000 and SC-6600 code.
+ v1.0.0
+ - Prep for merging with OSS Lite and Linux kernel 2.1.13
+ - Corrected a bug in request/check/release region calls (thanks to the
+ new kernel exception handling).
+ v1.1
+ - Revamped for integration with new modularized sound drivers: to enhance
+ the flexibility of modular version, I have removed all the conditional
+ compilation for SBPRO, MPU and MSS code. Now it is all managed with
+ the ae_config structure.
+ v1.2
+ - Module informations added.
+ - Removed aedsp16_delay_10msec(), now using mdelay(10)
+ - All data and funcs moved to .*.init section.
+
+ Known Problems:
+ - Audio Excel DSP 16 III don't work with this driver.
+
+ Credits:
+ Many thanks to Gerald Britton <gbritton@CapAccess.org>. He helped me a
+ lot in testing the 0.9.11 and 0.9.12 versions of this driver.
+
+ */
+
+
+#define VERSION "1.2" /* Version of Audio Excel DSP 16 driver */
+
+#undef AEDSP16_DEBUG 1 /* Define this to enable debug code */
+#undef AEDSP16_DEBUG_MORE 1 /* Define this to enable more debug */
+#undef AEDSP16_INFO 1 /* Define this to enable info code */
+
+#if defined(AEDSP16_DEBUG)
+# define DBG(x) printk x
+# if defined(AEDSP16_DEBUG_MORE)
+# define DBG1(x) printk x
+# else
+# define DBG1(x)
+# endif
+#else
+# define DBG(x)
+# define DBG1(x)
+#endif
+
+/*
+ * Misc definitions
+ */
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Region Size for request/check/release region.
+ */
+#define IOBASE_REGION_SIZE 0x10
+
+/*
+ * Hardware related defaults
+ */
+#define DEF_AEDSP16_IOB 0x220 /* 0x220(default) 0x240 */
+#define DEF_AEDSP16_IRQ 7 /* 5 7(default) 9 10 11 */
+#define DEF_AEDSP16_MRQ 0 /* 5 7 9 10 0(default), 0 means disable */
+#define DEF_AEDSP16_DMA 1 /* 0 1(default) 3 */
+
+/*
+ * Commands of AEDSP16's DSP (SBPRO+special).
+ * Some of them are COMMAND_xx, in the future they may change.
+ */
+#define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */
+#define COMMAND_52 0x52 /* */
+#define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */
+#define COMMAND_5C 0x5c /* */
+#define COMMAND_60 0x60 /* */
+#define COMMAND_66 0x66 /* */
+#define COMMAND_6C 0x6c /* */
+#define COMMAND_6E 0x6e /* */
+#define COMMAND_88 0x88 /* */
+#define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */
+#define COMMAND_C5 0xc5 /* */
+#define GET_DSP_VERSION 0xe1 /* Get DSP Version */
+#define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright */
+
+/*
+ * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port
+ * to have the actual I/O port.
+ * Register permissions are:
+ * (wo) == Write Only
+ * (ro) == Read Only
+ * (w-) == Write
+ * (r-) == Read
+ */
+#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */
+#define DSP_READ 0x0a /* offset of DSP READ (ro) */
+#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */
+#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */
+#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */
+#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */
+
+
+#define RETRY 10 /* Various retry values on I/O opera- */
+#define STATUSRETRY 1000 /* tions. Sometimes we have to */
+#define HARDRETRY 500000 /* wait for previous cmd to complete */
+
+/*
+ * Size of character arrays that store name and version of sound card
+ */
+#define CARDNAMELEN 15 /* Size of the card's name in chars */
+#define CARDVERLEN 2 /* Size of the card's version in chars */
+
+#if defined(CONFIG_SC6600)
+/*
+ * Bitmapped flags of hard configuration
+ */
+/*
+ * Decode macros (xl == low byte, xh = high byte)
+ */
+#define IOBASE(xl) ((xl & 0x01)?0x240:0x220)
+#define JOY(xl) (xl & 0x02)
+#define MPUADDR(xl) ( \
+ (xl & 0x0C)?0x330: \
+ (xl & 0x08)?0x320: \
+ (xl & 0x04)?0x310: \
+ 0x300)
+#define WSSADDR(xl) ((xl & 0x10)?0xE80:0x530)
+#define CDROM(xh) (xh & 0x20)
+#define CDROMADDR(xh) (((xh & 0x1F) << 4) + 0x200)
+/*
+ * Encode macros
+ */
+#define BLDIOBASE(xl, val) { \
+ xl &= ~0x01; \
+ if (val == 0x240) \
+ xl |= 0x01; \
+ }
+#define BLDJOY(xl, val) { \
+ xl &= ~0x02; \
+ if (val == 1) \
+ xl |= 0x02; \
+ }
+#define BLDMPUADDR(xl, val) { \
+ xl &= ~0x0C; \
+ switch (val) { \
+ case 0x330: \
+ xl |= 0x0C; \
+ break; \
+ case 0x320: \
+ xl |= 0x08; \
+ break; \
+ case 0x310: \
+ xl |= 0x04; \
+ break; \
+ case 0x300: \
+ xl |= 0x00; \
+ break; \
+ default: \
+ xl |= 0x00; \
+ break; \
+ } \
+ }
+#define BLDWSSADDR(xl, val) { \
+ xl &= ~0x10; \
+ if (val == 0xE80) \
+ xl |= 0x10; \
+ }
+#define BLDCDROM(xh, val) { \
+ xh &= ~0x20; \
+ if (val == 1) \
+ xh |= 0x20; \
+ }
+#define BLDCDROMADDR(xh, val) { \
+ int tmp = val; \
+ tmp -= 0x200; \
+ tmp >>= 4; \
+ tmp &= 0x1F; \
+ xh |= tmp; \
+ xh &= 0x7F; \
+ xh |= 0x40; \
+ }
+#endif /* CONFIG_SC6600 */
+
+/*
+ * Bit mapped flags for calling aedsp16_init_board(), and saving the current
+ * emulation mode.
+ */
+#define INIT_NONE (0 )
+#define INIT_SBPRO (1<<0)
+#define INIT_MSS (1<<1)
+#define INIT_MPU401 (1<<2)
+
+static int soft_cfg __initdata = 0; /* bitmapped config */
+static int soft_cfg_mss __initdata = 0; /* bitmapped mss config */
+static int ver[CARDVERLEN] __initdata = {0, 0}; /* DSP Ver:
+ hi->ver[0] lo->ver[1] */
+
+#if defined(CONFIG_SC6600)
+static int hard_cfg[2] /* lo<-hard_cfg[0] hi<-hard_cfg[1] */
+ __initdata = { 0, 0};
+#endif /* CONFIG_SC6600 */
+
+#if defined(CONFIG_SC6600)
+/* Decoded hard configuration */
+struct d_hcfg {
+ int iobase;
+ int joystick;
+ int mpubase;
+ int wssbase;
+ int cdrom;
+ int cdrombase;
+};
+
+struct d_hcfg decoded_hcfg __initdata = {0, };
+
+#endif /* CONFIG_SC6600 */
+
+/* orVals contain the values to be or'ed */
+struct orVals {
+ int val; /* irq|mirq|dma */
+ int or; /* soft_cfg |= TheStruct.or */
+};
+
+/* aedsp16_info contain the audio card configuration */
+struct aedsp16_info {
+ int base_io; /* base I/O address for accessing card */
+ int irq; /* irq value for DSP I/O */
+ int mpu_irq; /* irq for mpu401 interface I/O */
+ int dma; /* dma value for DSP I/O */
+ int mss_base; /* base I/O for Microsoft Sound System */
+ int mpu_base; /* base I/O for MPU-401 emulation */
+ int init; /* Initialization status of the card */
+};
+
+/*
+ * Magic values that the DSP will eat when configuring irq/mirq/dma
+ */
+/* DSP IRQ conversion array */
+static struct orVals orIRQ[] __initdata = {
+ {0x05, 0x28},
+ {0x07, 0x08},
+ {0x09, 0x10},
+ {0x0a, 0x18},
+ {0x0b, 0x20},
+ {0x00, 0x00}
+};
+
+/* MPU-401 IRQ conversion array */
+static struct orVals orMIRQ[] __initdata = {
+ {0x05, 0x04},
+ {0x07, 0x44},
+ {0x09, 0x84},
+ {0x0a, 0xc4},
+ {0x00, 0x00}
+};
+
+/* DMA Channels conversion array */
+static struct orVals orDMA[] __initdata = {
+ {0x00, 0x01},
+ {0x01, 0x02},
+ {0x03, 0x03},
+ {0x00, 0x00}
+};
+
+static struct aedsp16_info ae_config __initdata = {
+ DEF_AEDSP16_IOB,
+ DEF_AEDSP16_IRQ,
+ DEF_AEDSP16_MRQ,
+ DEF_AEDSP16_DMA,
+ -1,
+ -1,
+ INIT_NONE
+};
+
+/*
+ * Buffers to store audio card informations
+ */
+static char DSPCopyright[CARDNAMELEN + 1] __initdata = {0, };
+static char DSPVersion[CARDVERLEN + 1] __initdata = {0, };
+
+static int __init aedsp16_wait_data(int port)
+{
+ int loop = STATUSRETRY;
+ unsigned char ret = 0;
+
+ DBG1(("aedsp16_wait_data (0x%x): ", port));
+
+ do {
+ ret = inb(port + DSP_DATAVAIL);
+ /*
+ * Wait for data available (bit 7 of ret == 1)
+ */
+ } while (!(ret & 0x80) && loop--);
+
+ if (ret & 0x80) {
+ DBG1(("success.\n"));
+ return TRUE;
+ }
+
+ DBG1(("failure.\n"));
+ return FALSE;
+}
+
+static int __init aedsp16_read(int port)
+{
+ int inbyte;
+
+ DBG((" Read DSP Byte (0x%x): ", port));
+
+ if (aedsp16_wait_data(port) == FALSE) {
+ DBG(("failure.\n"));
+ return -1;
+ }
+
+ inbyte = inb(port + DSP_READ);
+
+ DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte));
+
+ return inbyte;
+}
+
+static int __init aedsp16_test_dsp(int port)
+{
+ return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE);
+}
+
+static int __init aedsp16_dsp_reset(int port)
+{
+ /*
+ * Reset DSP
+ */
+
+ DBG(("Reset DSP:\n"));
+
+ outb(1, (port + DSP_RESET));
+ udelay(10);
+ outb(0, (port + DSP_RESET));
+ udelay(10);
+ udelay(10);
+ if (aedsp16_test_dsp(port) == TRUE) {
+ DBG(("success.\n"));
+ return TRUE;
+ } else
+ DBG(("failure.\n"));
+ return FALSE;
+}
+
+static int __init aedsp16_write(int port, int cmd)
+{
+ unsigned char ret;
+ int loop = HARDRETRY;
+
+ DBG((" Write DSP Byte (0x%x) [0x%x]: ", port, cmd));
+
+ do {
+ ret = inb(port + DSP_STATUS);
+ /*
+ * DSP ready to receive data if bit 7 of ret == 0
+ */
+ if (!(ret & 0x80)) {
+ outb(cmd, port + DSP_COMMAND);
+ DBG(("success.\n"));
+ return 0;
+ }
+ } while (loop--);
+
+ DBG(("timeout.\n"));
+ printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd);
+
+ return -1;
+}
+
+#if defined(CONFIG_SC6600)
+
+#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+void __init aedsp16_pinfo(void) {
+ DBG(("\n Base address: %x\n", decoded_hcfg.iobase));
+ DBG((" Joystick : %s present\n", decoded_hcfg.joystick?"":" not"));
+ DBG((" WSS addr : %x\n", decoded_hcfg.wssbase));
+ DBG((" MPU-401 addr: %x\n", decoded_hcfg.mpubase));
+ DBG((" CDROM : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not"));
+ DBG((" CDROMADDR : %x\n\n", decoded_hcfg.cdrombase));
+}
+#endif
+
+void __init aedsp16_hard_decode(void) {
+
+ DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+
+/*
+ * Decode Cfg Bytes.
+ */
+ decoded_hcfg.iobase = IOBASE(hard_cfg[0]);
+ decoded_hcfg.joystick = JOY(hard_cfg[0]);
+ decoded_hcfg.wssbase = WSSADDR(hard_cfg[0]);
+ decoded_hcfg.mpubase = MPUADDR(hard_cfg[0]);
+ decoded_hcfg.cdrom = CDROM(hard_cfg[1]);
+ decoded_hcfg.cdrombase = CDROMADDR(hard_cfg[1]);
+
+#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+ printk(" Original sound card configuration:\n");
+ aedsp16_pinfo();
+#endif
+
+/*
+ * Now set up the real kernel configuration.
+ */
+ decoded_hcfg.iobase = ae_config.base_io;
+ decoded_hcfg.wssbase = ae_config.mss_base;
+ decoded_hcfg.mpubase = ae_config.mpu_base;
+
+#if defined(CONFIG_SC6600_JOY)
+ decoded_hcfg.joystick = CONFIG_SC6600_JOY; /* Enable */
+#endif
+#if defined(CONFIG_SC6600_CDROM)
+ decoded_hcfg.cdrom = CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */
+#endif
+#if defined(CONFIG_SC6600_CDROMBASE)
+ decoded_hcfg.cdrombase = CONFIG_SC6600_CDROMBASE; /* 0 Disable */
+#endif
+
+#if defined(AEDSP16_DEBUG)
+ DBG((" New Values:\n"));
+ aedsp16_pinfo();
+#endif
+
+ DBG(("success.\n"));
+}
+
+void __init aedsp16_hard_encode(void) {
+
+ DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+
+ hard_cfg[0] = 0;
+ hard_cfg[1] = 0;
+
+ hard_cfg[0] |= 0x20;
+
+ BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase);
+ BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase);
+ BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase);
+ BLDJOY(hard_cfg[0], decoded_hcfg.joystick);
+ BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom);
+ BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase);
+
+#if defined(AEDSP16_DEBUG)
+ aedsp16_pinfo();
+#endif
+
+ DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+ DBG(("success.\n"));
+
+}
+
+static int __init aedsp16_hard_write(int port) {
+
+ DBG(("aedsp16_hard_write:\n"));
+
+ if (aedsp16_write(port, COMMAND_6C)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+ if (aedsp16_write(port, COMMAND_5C)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+ if (aedsp16_write(port, hard_cfg[0])) {
+ printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+ if (aedsp16_write(port, hard_cfg[1])) {
+ printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+ if (aedsp16_write(port, COMMAND_C5)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+
+ DBG(("success.\n"));
+
+ return TRUE;
+}
+
+static int __init aedsp16_hard_read(int port) {
+
+ DBG(("aedsp16_hard_read:\n"));
+
+ if (aedsp16_write(port, READ_HARD_CFG)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+
+ if ((hard_cfg[0] = aedsp16_read(port)) == -1) {
+ printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+ READ_HARD_CFG);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+ if ((hard_cfg[1] = aedsp16_read(port)) == -1) {
+ printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+ READ_HARD_CFG);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+ if (aedsp16_read(port) == -1) {
+ printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+ READ_HARD_CFG);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+
+ DBG(("success.\n"));
+
+ return TRUE;
+}
+
+static int __init aedsp16_ext_cfg_write(int port) {
+
+ int extcfg, val;
+
+ if (aedsp16_write(port, COMMAND_66)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66);
+ return FALSE;
+ }
+
+ extcfg = 7;
+ if (decoded_hcfg.cdrom != 2)
+ extcfg = 0x0F;
+ if ((decoded_hcfg.cdrom == 4) ||
+ (decoded_hcfg.cdrom == 3))
+ extcfg &= ~2;
+ if (decoded_hcfg.cdrombase == 0)
+ extcfg &= ~2;
+ if (decoded_hcfg.mpubase == 0)
+ extcfg &= ~1;
+
+ if (aedsp16_write(port, extcfg)) {
+ printk("[AEDSP16] Write extcfg: failed!\n");
+ return FALSE;
+ }
+ if (aedsp16_write(port, 0)) {
+ printk("[AEDSP16] Write extcfg: failed!\n");
+ return FALSE;
+ }
+ if (decoded_hcfg.cdrom == 3) {
+ if (aedsp16_write(port, COMMAND_52)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
+ return FALSE;
+ }
+ if ((val = aedsp16_read(port)) == -1) {
+ printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n"
+ , COMMAND_52);
+ return FALSE;
+ }
+ val &= 0x7F;
+ if (aedsp16_write(port, COMMAND_60)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
+ return FALSE;
+ }
+ if (aedsp16_write(port, val)) {
+ printk("[AEDSP16] Write val: failed!\n");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif /* CONFIG_SC6600 */
+
+static int __init aedsp16_cfg_write(int port) {
+ if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
+ return FALSE;
+ }
+ if (aedsp16_write(port, soft_cfg)) {
+ printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int __init aedsp16_init_mss(int port)
+{
+ DBG(("aedsp16_init_mss:\n"));
+
+ mdelay(10);
+
+ if (aedsp16_write(port, DSP_INIT_MSS)) {
+ printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n",
+ DSP_INIT_MSS);
+ DBG(("failure.\n"));
+ return FALSE;
+ }
+
+ mdelay(10);
+
+ if (aedsp16_cfg_write(port) == FALSE)
+ return FALSE;
+
+ outb(soft_cfg_mss, ae_config.mss_base);
+
+ DBG(("success.\n"));
+
+ return TRUE;
+}
+
+static int __init aedsp16_setup_board(int port) {
+ int loop = RETRY;
+
+#if defined(CONFIG_SC6600)
+ int val = 0;
+
+ if (aedsp16_hard_read(port) == FALSE) {
+ printk("[AEDSP16] aedsp16_hard_read: failed!\n");
+ return FALSE;
+ }
+
+ if (aedsp16_write(port, COMMAND_52)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
+ return FALSE;
+ }
+
+ if ((val = aedsp16_read(port)) == -1) {
+ printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+ COMMAND_52);
+ return FALSE;
+ }
+#endif
+
+ do {
+ if (aedsp16_write(port, COMMAND_88)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88);
+ return FALSE;
+ }
+ mdelay(10);
+ } while ((aedsp16_wait_data(port) == FALSE) && loop--);
+
+ if (aedsp16_read(port) == -1) {
+ printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+ COMMAND_88);
+ return FALSE;
+ }
+
+#if !defined(CONFIG_SC6600)
+ if (aedsp16_write(port, COMMAND_5C)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+ return FALSE;
+ }
+#endif
+
+ if (aedsp16_cfg_write(port) == FALSE)
+ return FALSE;
+
+#if defined(CONFIG_SC6600)
+ if (aedsp16_write(port, COMMAND_60)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
+ return FALSE;
+ }
+ if (aedsp16_write(port, val)) {
+ printk("[AEDSP16] DATA 0x%x: failed!\n", val);
+ return FALSE;
+ }
+ if (aedsp16_write(port, COMMAND_6E)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E);
+ return FALSE;
+ }
+ if (aedsp16_write(port, ver[0])) {
+ printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]);
+ return FALSE;
+ }
+ if (aedsp16_write(port, ver[1])) {
+ printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]);
+ return FALSE;
+ }
+
+ if (aedsp16_hard_write(port) == FALSE) {
+ printk("[AEDSP16] aedsp16_hard_write: failed!\n");
+ return FALSE;
+ }
+
+ if (aedsp16_write(port, COMMAND_5C)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+ return FALSE;
+ }
+
+#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET)
+ if (aedsp16_cfg_write(port) == FALSE)
+ return FALSE;
+#endif
+
+#endif
+
+ return TRUE;
+}
+
+static int __init aedsp16_stdcfg(int port) {
+ if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
+ return FALSE;
+ }
+ /*
+ * 0x0A == (IRQ 7, DMA 1, MIRQ 0)
+ */
+ if (aedsp16_write(port, 0x0A)) {
+ printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int __init aedsp16_dsp_version(int port)
+{
+ int len = 0;
+ int ret;
+
+ DBG(("Get DSP Version:\n"));
+
+ if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION);
+ DBG(("failed.\n"));
+ return FALSE;
+ }
+
+ do {
+ if ((ret = aedsp16_read(port)) == -1) {
+ DBG(("failed.\n"));
+ return FALSE;
+ }
+ /*
+ * We already know how many int are stored (2), so we know when the
+ * string is finished.
+ */
+ ver[len++] = ret;
+ } while (len < CARDVERLEN);
+ sprintf(DSPVersion, "%d.%d", ver[0], ver[1]);
+
+ DBG(("success.\n"));
+
+ return TRUE;
+}
+
+static int __init aedsp16_dsp_copyright(int port)
+{
+ int len = 0;
+ int ret;
+
+ DBG(("Get DSP Copyright:\n"));
+
+ if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) {
+ printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT);
+ DBG(("failed.\n"));
+ return FALSE;
+ }
+
+ do {
+ if ((ret = aedsp16_read(port)) == -1) {
+ /*
+ * If no more data available, return to the caller, no error if len>0.
+ * We have no other way to know when the string is finished.
+ */
+ if (len)
+ break;
+ else {
+ DBG(("failed.\n"));
+ return FALSE;
+ }
+ }
+
+ DSPCopyright[len++] = ret;
+
+ } while (len < CARDNAMELEN);
+
+ DBG(("success.\n"));
+
+ return TRUE;
+}
+
+static void __init aedsp16_init_tables(void)
+{
+ int i = 0;
+
+ memset(DSPCopyright, 0, CARDNAMELEN + 1);
+ memset(DSPVersion, 0, CARDVERLEN + 1);
+
+ for (i = 0; orIRQ[i].or; i++)
+ if (orIRQ[i].val == ae_config.irq) {
+ soft_cfg |= orIRQ[i].or;
+ soft_cfg_mss |= orIRQ[i].or;
+ }
+
+ for (i = 0; orMIRQ[i].or; i++)
+ if (orMIRQ[i].or == ae_config.mpu_irq)
+ soft_cfg |= orMIRQ[i].or;
+
+ for (i = 0; orDMA[i].or; i++)
+ if (orDMA[i].val == ae_config.dma) {
+ soft_cfg |= orDMA[i].or;
+ soft_cfg_mss |= orDMA[i].or;
+ }
+}
+
+static int __init aedsp16_init_board(void)
+{
+ aedsp16_init_tables();
+
+ if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_dsp_reset: failed!\n");
+ return FALSE;
+ }
+ if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n");
+ return FALSE;
+ }
+
+ /*
+ * My AEDSP16 card return SC-6000 in DSPCopyright, so
+ * if we have something different, we have to be warned.
+ */
+ if (strcmp("SC-6000", DSPCopyright))
+ printk("[AEDSP16] Warning: non SC-6000 audio card!\n");
+
+ if (aedsp16_dsp_version(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_dsp_version: failed!\n");
+ return FALSE;
+ }
+
+ if (aedsp16_stdcfg(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
+ return FALSE;
+ }
+
+#if defined(CONFIG_SC6600)
+ if (aedsp16_hard_read(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_hard_read: failed!\n");
+ return FALSE;
+ }
+
+ aedsp16_hard_decode();
+
+ aedsp16_hard_encode();
+
+ if (aedsp16_hard_write(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_hard_write: failed!\n");
+ return FALSE;
+ }
+
+ if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n");
+ return FALSE;
+ }
+#endif /* CONFIG_SC6600 */
+
+ if (aedsp16_setup_board(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] aedsp16_setup_board: failed!\n");
+ return FALSE;
+ }
+
+ if (ae_config.mss_base != -1) {
+ if (ae_config.init & INIT_MSS) {
+ if (aedsp16_init_mss(ae_config.base_io) == FALSE) {
+ printk("[AEDSP16] Can not initialize"
+ "Microsoft Sound System mode.\n");
+ return FALSE;
+ }
+ }
+ }
+
+#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+
+ printk("Audio Excel DSP 16 init v%s (%s %s) [",
+ VERSION, DSPCopyright,
+ DSPVersion);
+
+ if (ae_config.mpu_base != -1) {
+ if (ae_config.init & INIT_MPU401) {
+ printk("MPU401");
+ if ((ae_config.init & INIT_MSS) ||
+ (ae_config.init & INIT_SBPRO))
+ printk(" ");
+ }
+ }
+
+ if (ae_config.mss_base == -1) {
+ if (ae_config.init & INIT_SBPRO) {
+ printk("SBPro");
+ if (ae_config.init & INIT_MSS)
+ printk(" ");
+ }
+ }
+
+ if (ae_config.mss_base != -1)
+ if (ae_config.init & INIT_MSS)
+ printk("MSS");
+
+ printk("]\n");
+#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */
+
+ mdelay(10);
+
+ return TRUE;
+}
+
+static int __init init_aedsp16_sb(void)
+{
+ DBG(("init_aedsp16_sb: "));
+
+/*
+ * If the card is already init'ed MSS, we can not init it to SBPRO too
+ * because the board can not emulate simultaneously MSS and SBPRO.
+ */
+ if (ae_config.init & INIT_MSS)
+ return FALSE;
+ if (ae_config.init & INIT_SBPRO)
+ return FALSE;
+
+ ae_config.init |= INIT_SBPRO;
+
+ DBG(("done.\n"));
+
+ return TRUE;
+}
+
+static void __init uninit_aedsp16_sb(void)
+{
+ DBG(("uninit_aedsp16_sb: "));
+
+ ae_config.init &= ~INIT_SBPRO;
+
+ DBG(("done.\n"));
+}
+
+static int __init init_aedsp16_mss(void)
+{
+ DBG(("init_aedsp16_mss: "));
+
+/*
+ * If the card is already init'ed SBPRO, we can not init it to MSS too
+ * because the board can not emulate simultaneously MSS and SBPRO.
+ */
+ if (ae_config.init & INIT_SBPRO)
+ return FALSE;
+ if (ae_config.init & INIT_MSS)
+ return FALSE;
+/*
+ * We must check the CONFIG_AEDSP16_BASE region too because these are the I/O
+ * ports to access card's control registers.
+ */
+ if (!(ae_config.init & INIT_MPU401)) {
+ if (check_region(ae_config.base_io, IOBASE_REGION_SIZE)) {
+ printk(
+ "AEDSP16 BASE I/O port region is already in use.\n");
+ return FALSE;
+ }
+ }
+
+/*
+ * We must allocate the CONFIG_AEDSP16_BASE region too because these are the
+ * I/O ports to access card's control registers.
+ */
+ if (!(ae_config.init & INIT_MPU401))
+ request_region(ae_config.base_io, IOBASE_REGION_SIZE,
+ "aedsp16 (base)");
+
+ ae_config.init |= INIT_MSS;
+
+ DBG(("done.\n"));
+
+ return TRUE;
+}
+
+static void __init uninit_aedsp16_mss(void)
+{
+ DBG(("uninit_aedsp16_mss: "));
+
+ if ((!(ae_config.init & INIT_MPU401)) &&
+ (ae_config.init & INIT_MSS)) {
+ release_region(ae_config.base_io, IOBASE_REGION_SIZE);
+ DBG(("AEDSP16 base region released.\n"));
+ }
+
+ ae_config.init &= ~INIT_MSS;
+ DBG(("done.\n"));
+}
+
+static int __init init_aedsp16_mpu(void)
+{
+ DBG(("init_aedsp16_mpu: "));
+
+ if (ae_config.init & INIT_MPU401)
+ return FALSE;
+
+/*
+ * We must check the CONFIG_AEDSP16_BASE region too because these are the I/O
+ * ports to access card's control registers.
+ */
+ if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) {
+ if (check_region(ae_config.base_io, IOBASE_REGION_SIZE)) {
+ printk(
+ "AEDSP16 BASE I/O port region is already in use.\n");
+ return FALSE;
+ }
+ }
+
+ if (!(ae_config.init & (INIT_MSS | INIT_SBPRO)))
+ request_region(ae_config.base_io, IOBASE_REGION_SIZE,
+ "aedsp16 (base)");
+
+ ae_config.init |= INIT_MPU401;
+
+ DBG(("done.\n"));
+
+ return TRUE;
+}
+
+static void __init uninit_aedsp16_mpu(void)
+{
+ DBG(("uninit_aedsp16_mpu: "));
+
+ if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) &&
+ (ae_config.init & INIT_MPU401)) {
+ release_region(ae_config.base_io, IOBASE_REGION_SIZE);
+ DBG(("AEDSP16 base region released.\n"));
+ }
+
+ ae_config.init &= ~INIT_MPU401;
+
+ DBG(("done.\n"));
+}
+
+int __init init_aedsp16(void)
+{
+ int initialized = FALSE;
+
+ DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n",
+ ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq));
+
+ if (ae_config.mss_base == -1) {
+ if (init_aedsp16_sb() == FALSE) {
+ uninit_aedsp16_sb();
+ } else {
+ initialized = TRUE;
+ }
+ }
+
+ if (ae_config.mpu_base != -1) {
+ if (init_aedsp16_mpu() == FALSE) {
+ uninit_aedsp16_mpu();
+ } else {
+ initialized = TRUE;
+ }
+ }
+
+/*
+ * In the sequence of init routines, the MSS init MUST be the last!
+ * This because of the special register programming the MSS mode needs.
+ * A board reset would disable the MSS mode restoring the default SBPRO
+ * mode.
+ */
+ if (ae_config.mss_base != -1) {
+ if (init_aedsp16_mss() == FALSE) {
+ uninit_aedsp16_mss();
+ } else {
+ initialized = TRUE;
+ }
+ }
+
+ if (initialized)
+ initialized = aedsp16_init_board();
+ return initialized;
+}
+
+void __init uninit_aedsp16(void)
+{
+ if (ae_config.mss_base != -1)
+ uninit_aedsp16_mss();
+ else
+ uninit_aedsp16_sb();
+ if (ae_config.mpu_base != -1)
+ uninit_aedsp16_mpu();
+}
+
+static int __initdata io = -1;
+static int __initdata irq = -1;
+static int __initdata dma = -1;
+static int __initdata mpu_irq = -1;
+static int __initdata mss_base = -1;
+static int __initdata mpu_base = -1;
+
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "I/O base address (0x220 0x240)");
+MODULE_PARM(irq, "i");
+MODULE_PARM_DESC(irq, "IRQ line (5 7 9 10 11)");
+MODULE_PARM(dma, "i");
+MODULE_PARM_DESC(dma, "dma line (0 1 3)");
+MODULE_PARM(mpu_irq, "i");
+MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ line (5 7 9 10 0)");
+MODULE_PARM(mss_base, "i");
+MODULE_PARM_DESC(mss_base, "MSS emulation I/O base address (0x530 0xE80)");
+MODULE_PARM(mpu_base, "i");
+MODULE_PARM_DESC(mpu_base,"MPU-401 I/O base address (0x300 0x310 0x320 0x330)");
+MODULE_AUTHOR("Riccardo Facchetti <fizban@tin.it>");
+MODULE_DESCRIPTION("Audio Excel DSP 16 Driver Version " VERSION);
+
+static int __init do_init_aedsp16(void) {
+ printk("Audio Excel DSP 16 init driver Copyright (C) Riccardo Facchetti 1995-98\n");
+ if (io == -1 || dma == -1 || irq == -1) {
+ printk(KERN_INFO "aedsp16: I/O, IRQ and DMA are mandatory\n");
+ return -EINVAL;
+ }
+
+ ae_config.base_io = io;
+ ae_config.irq = irq;
+ ae_config.dma = dma;
+
+ ae_config.mss_base = mss_base;
+ ae_config.mpu_base = mpu_base;
+ ae_config.mpu_irq = mpu_irq;
+
+ if (init_aedsp16() == FALSE) {
+ printk(KERN_ERR "aedsp16: initialization failed\n");
+ /*
+ * XXX
+ * What error should we return here ?
+ */
+ return -EINVAL;
+ }
+ SOUND_LOCK;
+ return 0;
+}
+
+static void __exit cleanup_aedsp16(void) {
+ uninit_aedsp16();
+ SOUND_LOCK_END;
+}
+
+module_init(do_init_aedsp16);
+module_exit(cleanup_aedsp16);
+
+#ifndef MODULE
+static int __init setup_aedsp16(char *str)
+{
+ /* io, irq, dma, mss_io, mpu_io, mpu_irq */
+ int ints[7];
+
+ str = get_options(str, ARRAY_SIZE(ints), ints);
+
+ io = ints[1];
+ irq = ints[2];
+ dma = ints[3];
+ mss_base = ints[4];
+ mpu_base = ints[5];
+ mpu_irq = ints[6];
+}
+
+__setup("aedsp16=", setup_aedsp16);
+#endif
--- /dev/null
+/*
+ * sound/awe_hw.h
+ *
+ * Access routines and definitions for the low level driver for the
+ * Creative AWE32/SB32/AWE64 wave table synth.
+ * version 0.4.3; Mar. 1, 1998
+ *
+ * Copyright (C) 1996-1998 Takashi Iwai
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef AWE_HW_H_DEF
+#define AWE_HW_H_DEF
+
+/*
+ * Emu-8000 control registers
+ * name(channel) reg, port
+ */
+
+#define awe_cmd_idx(reg,ch) (((reg)<< 5) | (ch))
+
+#define Data0 0 /* 0x620: doubleword r/w */
+#define Data1 1 /* 0xA20: doubleword r/w */
+#define Data2 2 /* 0xA22: word r/w */
+#define Data3 3 /* 0xE20: word r/w */
+#define Pointer 4 /* 0xE22 register pointer r/w */
+
+#define AWE_CPF(ch) awe_cmd_idx(0,ch), Data0 /* DW: current pitch and fractional address */
+#define AWE_PTRX(ch) awe_cmd_idx(1,ch), Data0 /* DW: pitch target and reverb send */
+#define AWE_CVCF(ch) awe_cmd_idx(2,ch), Data0 /* DW: current volume and filter cutoff */
+#define AWE_VTFT(ch) awe_cmd_idx(3,ch), Data0 /* DW: volume and filter cutoff targets */
+#define AWE_0080(ch) awe_cmd_idx(4,ch), Data0 /* DW: ?? */
+#define AWE_00A0(ch) awe_cmd_idx(5,ch), Data0 /* DW: ?? */
+#define AWE_PSST(ch) awe_cmd_idx(6,ch), Data0 /* DW: pan send and loop start address */
+#define AWE_CSL(ch) awe_cmd_idx(7,ch), Data0 /* DW: chorus send and loop end address */
+#define AWE_CCCA(ch) awe_cmd_idx(0,ch), Data1 /* DW: Q, control bits, and current address */
+#define AWE_HWCF4 awe_cmd_idx(1,9), Data1 /* DW: config dw 4 */
+#define AWE_HWCF5 awe_cmd_idx(1,10), Data1 /* DW: config dw 5 */
+#define AWE_HWCF6 awe_cmd_idx(1,13), Data1 /* DW: config dw 6 */
+#define AWE_HWCF7 awe_cmd_idx(1,14), Data1 /* DW: config dw 7? (not documented) */
+#define AWE_SMALR awe_cmd_idx(1,20), Data1 /* DW: sound memory address for left read */
+#define AWE_SMARR awe_cmd_idx(1,21), Data1 /* DW: for right read */
+#define AWE_SMALW awe_cmd_idx(1,22), Data1 /* DW: sound memory address for left write */
+#define AWE_SMARW awe_cmd_idx(1,23), Data1 /* DW: for right write */
+#define AWE_SMLD awe_cmd_idx(1,26), Data1 /* W: sound memory left data */
+#define AWE_SMRD awe_cmd_idx(1,26), Data2 /* W: right data */
+#define AWE_WC awe_cmd_idx(1,27), Data2 /* W: sample counter */
+#define AWE_WC_Cmd awe_cmd_idx(1,27)
+#define AWE_WC_Port Data2
+#define AWE_HWCF1 awe_cmd_idx(1,29), Data1 /* W: config w 1 */
+#define AWE_HWCF2 awe_cmd_idx(1,30), Data1 /* W: config w 2 */
+#define AWE_HWCF3 awe_cmd_idx(1,31), Data1 /* W: config w 3 */
+#define AWE_INIT1(ch) awe_cmd_idx(2,ch), Data1 /* W: init array 1 */
+#define AWE_INIT2(ch) awe_cmd_idx(2,ch), Data2 /* W: init array 2 */
+#define AWE_INIT3(ch) awe_cmd_idx(3,ch), Data1 /* W: init array 3 */
+#define AWE_INIT4(ch) awe_cmd_idx(3,ch), Data2 /* W: init array 4 */
+#define AWE_ENVVOL(ch) awe_cmd_idx(4,ch), Data1 /* W: volume envelope delay */
+#define AWE_DCYSUSV(ch) awe_cmd_idx(5,ch), Data1 /* W: volume envelope sustain and decay */
+#define AWE_ENVVAL(ch) awe_cmd_idx(6,ch), Data1 /* W: modulation envelope delay */
+#define AWE_DCYSUS(ch) awe_cmd_idx(7,ch), Data1 /* W: modulation envelope sustain and decay */
+#define AWE_ATKHLDV(ch) awe_cmd_idx(4,ch), Data2 /* W: volume envelope attack and hold */
+#define AWE_LFO1VAL(ch) awe_cmd_idx(5,ch), Data2 /* W: LFO#1 Delay */
+#define AWE_ATKHLD(ch) awe_cmd_idx(6,ch), Data2 /* W: modulation envelope attack and hold */
+#define AWE_LFO2VAL(ch) awe_cmd_idx(7,ch), Data2 /* W: LFO#2 Delay */
+#define AWE_IP(ch) awe_cmd_idx(0,ch), Data3 /* W: initial pitch */
+#define AWE_IFATN(ch) awe_cmd_idx(1,ch), Data3 /* W: initial filter cutoff and attenuation */
+#define AWE_PEFE(ch) awe_cmd_idx(2,ch), Data3 /* W: pitch and filter envelope heights */
+#define AWE_FMMOD(ch) awe_cmd_idx(3,ch), Data3 /* W: vibrato and filter modulation freq */
+#define AWE_TREMFRQ(ch) awe_cmd_idx(4,ch), Data3 /* W: LFO#1 tremolo amount and freq */
+#define AWE_FM2FRQ2(ch) awe_cmd_idx(5,ch), Data3 /* W: LFO#2 vibrato amount and freq */
+
+/* used during detection (returns ROM version?; not documented in ADIP) */
+#define AWE_U1 0xE0, Data3 /* (R)(W) used in initialization */
+#define AWE_U2(ch) 0xC0+(ch), Data3 /* (W)(W) used in init envelope */
+
+
+#define AWE_MAX_VOICES 32
+#define AWE_NORMAL_VOICES 30 /*30&31 are reserved for DRAM refresh*/
+
+#define AWE_MAX_CHANNELS 32 /* max midi channels (must >= voices) */
+#define AWE_MAX_LAYERS AWE_MAX_VOICES /* maximum number of multiple layers */
+
+#define AWE_DRAM_OFFSET 0x200000
+#define AWE_MAX_DRAM_SIZE (28 * 1024) /* 28 MB is max onboard memory */
+
+#endif
--- /dev/null
+/*
+ * sound/awe_wave.c
+ *
+ * The low level driver for the AWE32/SB32/AWE64 wave table synth.
+ * version 0.4.3; Feb. 1, 1999
+ *
+ * Copyright (C) 1996-1999 Takashi Iwai
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/awe_voice.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#include "sound_config.h"
+#include "soundmodule.h"
+
+#include "awe_wave.h"
+#include "awe_hw.h"
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+#include "tuning.h"
+#include <linux/ultrasound.h>
+#endif
+
+/*
+ * debug message
+ */
+
+/* do not allocate buffer at beginning */
+#define INIT_TABLE(buffer,index,nums,type) {buffer=NULL; index=0;}
+
+#ifdef AWE_DEBUG_ON
+#define DEBUG(LVL,XXX) {if (ctrls[AWE_MD_DEBUG_MODE] > LVL) { XXX; }}
+#define ERRMSG(XXX) {if (ctrls[AWE_MD_DEBUG_MODE]) { XXX; }}
+#define FATALERR(XXX) XXX
+#else
+#define DEBUG(LVL,XXX) /**/
+#define ERRMSG(XXX) XXX
+#define FATALERR(XXX) XXX
+#endif
+
+/*
+ * bank and voice record
+ */
+
+/* soundfont record */
+typedef struct _sf_list {
+ unsigned short sf_id;
+ unsigned short type;
+ int num_info; /* current info table index */
+ int num_sample; /* current sample table index */
+ int mem_ptr; /* current word byte pointer */
+ int infos;
+ int samples;
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+ int shared; /* shared index */
+ unsigned char name[AWE_PATCH_NAME_LEN];
+#endif
+} sf_list;
+
+/* bank record */
+typedef struct _awe_voice_list {
+ int next; /* linked list with same sf_id */
+ unsigned char bank, instr; /* preset number information */
+ char type, disabled; /* type=normal/mapped, disabled=boolean */
+ awe_voice_info v; /* voice information */
+ int next_instr; /* preset table list */
+ int next_bank; /* preset table list */
+} awe_voice_list;
+
+/* voice list type */
+#define V_ST_NORMAL 0
+#define V_ST_MAPPED 1
+
+typedef struct _awe_sample_list {
+ int next; /* linked list with same sf_id */
+ awe_sample_info v; /* sample information */
+} awe_sample_list;
+
+/* sample and information table */
+static int current_sf_id = 0;
+static int locked_sf_id = 0;
+static int max_sfs;
+static sf_list *sflists = NULL;
+
+#define awe_free_mem_ptr() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].mem_ptr)
+#define awe_free_info() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_info)
+#define awe_free_sample() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_sample)
+
+static int max_samples;
+static awe_sample_list *samples = NULL;
+
+static int max_infos;
+static awe_voice_list *infos = NULL;
+
+
+#define AWE_MAX_PRESETS 256
+#define AWE_DEFAULT_PRESET 0
+#define AWE_DEFAULT_BANK 0
+#define AWE_DEFAULT_DRUM 0
+#define AWE_DRUM_BANK 128
+
+#define MAX_LAYERS AWE_MAX_VOICES
+
+/* preset table index */
+static int preset_table[AWE_MAX_PRESETS];
+
+/*
+ * voice table
+ */
+
+/* effects table */
+typedef struct FX_Rec { /* channel effects */
+ unsigned char flags[AWE_FX_END];
+ short val[AWE_FX_END];
+} FX_Rec;
+
+
+/* channel parameters */
+typedef struct _awe_chan_info {
+ int channel; /* channel number */
+ int bank; /* current tone bank */
+ int instr; /* current program */
+ int bender; /* midi pitchbend (-8192 - 8192) */
+ int bender_range; /* midi bender range (x100) */
+ int panning; /* panning (0-127) */
+ int main_vol; /* channel volume (0-127) */
+ int expression_vol; /* midi expression (0-127) */
+ int chan_press; /* channel pressure */
+ int vrec; /* instrument list */
+ int def_vrec; /* default instrument list */
+ int sustained; /* sustain status in MIDI */
+ FX_Rec fx; /* effects */
+ FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */
+} awe_chan_info;
+
+/* voice parameters */
+typedef struct _voice_info {
+ int state;
+#define AWE_ST_OFF (1<<0) /* no sound */
+#define AWE_ST_ON (1<<1) /* playing */
+#define AWE_ST_STANDBY (1<<2) /* stand by for playing */
+#define AWE_ST_SUSTAINED (1<<3) /* sustained */
+#define AWE_ST_MARK (1<<4) /* marked for allocation */
+#define AWE_ST_DRAM (1<<5) /* DRAM read/write */
+#define AWE_ST_FM (1<<6) /* reserved for FM */
+#define AWE_ST_RELEASED (1<<7) /* released */
+
+ int ch; /* midi channel */
+ int key; /* internal key for search */
+ int layer; /* layer number (for channel mode only) */
+ int time; /* allocated time */
+ awe_chan_info *cinfo; /* channel info */
+
+ int note; /* midi key (0-127) */
+ int velocity; /* midi velocity (0-127) */
+ int sostenuto; /* sostenuto on/off */
+ awe_voice_info *sample; /* assigned voice */
+
+ /* EMU8000 parameters */
+ int apitch; /* pitch parameter */
+ int avol; /* volume parameter */
+ int apan; /* panning parameter */
+ int acutoff; /* cutoff parameter */
+ short aaux; /* aux word */
+} voice_info;
+
+/* voice information */
+static voice_info voices[AWE_MAX_VOICES];
+
+#define IS_NO_SOUND(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_RELEASED|AWE_ST_STANDBY|AWE_ST_SUSTAINED))
+#define IS_NO_EFFECT(v) (voices[v].state != AWE_ST_ON)
+#define IS_PLAYING(v) (voices[v].state & (AWE_ST_ON|AWE_ST_SUSTAINED|AWE_ST_RELEASED))
+#define IS_EMPTY(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_MARK|AWE_ST_DRAM|AWE_ST_FM))
+
+
+/* MIDI channel effects information (for hw control) */
+static awe_chan_info channels[AWE_MAX_CHANNELS];
+
+
+/*----------------------------------------------------------------
+ * global variables
+ *----------------------------------------------------------------*/
+
+#ifndef AWE_DEFAULT_BASE_ADDR
+#define AWE_DEFAULT_BASE_ADDR 0 /* autodetect */
+#endif
+
+#ifndef AWE_DEFAULT_MEM_SIZE
+#define AWE_DEFAULT_MEM_SIZE -1 /* autodetect */
+#endif
+
+#define awe_port io
+#define awe_mem_size memsize
+int io = AWE_DEFAULT_BASE_ADDR; /* Emu8000 base address */
+int memsize = AWE_DEFAULT_MEM_SIZE; /* memory size in Kbytes */
+
+MODULE_AUTHOR("Takashi Iwai <iwai@ww.uni-erlangen.de>");
+MODULE_DESCRIPTION("SB AWE32/64 WaveTable driver");
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "base i/o port of Emu8000");
+MODULE_PARM(memsize, "i");
+MODULE_PARM_DESC(memsize, "onboard DRAM size in Kbytes");
+EXPORT_NO_SYMBOLS;
+
+/* DRAM start offset */
+static int awe_mem_start = AWE_DRAM_OFFSET;
+
+/* maximum channels for playing */
+static int awe_max_voices = AWE_MAX_VOICES;
+
+static int patch_opened = 0; /* sample already loaded? */
+
+static char atten_relative = FALSE;
+static short atten_offset = 0;
+
+static int awe_present = FALSE; /* awe device present? */
+static int awe_busy = FALSE; /* awe device opened? */
+
+static int my_dev = -1;
+
+#define DEFAULT_DRUM_FLAGS ((1 << 9) | (1 << 25))
+#define IS_DRUM_CHANNEL(c) (drum_flags & (1 << (c)))
+#define DRUM_CHANNEL_ON(c) (drum_flags |= (1 << (c)))
+#define DRUM_CHANNEL_OFF(c) (drum_flags &= ~(1 << (c)))
+static unsigned int drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */
+
+static int playing_mode = AWE_PLAY_INDIRECT;
+#define SINGLE_LAYER_MODE() (playing_mode == AWE_PLAY_INDIRECT || playing_mode == AWE_PLAY_DIRECT)
+#define MULTI_LAYER_MODE() (playing_mode == AWE_PLAY_MULTI || playing_mode == AWE_PLAY_MULTI2)
+
+static int current_alloc_time = 0; /* voice allocation index for channel mode */
+
+static struct synth_info awe_info = {
+ "AWE32 Synth", /* name */
+ 0, /* device */
+ SYNTH_TYPE_SAMPLE, /* synth_type */
+ SAMPLE_TYPE_AWE32, /* synth_subtype */
+ 0, /* perc_mode (obsolete) */
+ AWE_MAX_VOICES, /* nr_voices */
+ 0, /* nr_drums (obsolete) */
+ AWE_MAX_INFOS /* instr_bank_size */
+};
+
+
+static struct voice_alloc_info *voice_alloc; /* set at initialization */
+
+
+/*
+ * function prototypes
+ */
+
+static int awe_check_port(void);
+static void awe_request_region(void);
+static void awe_release_region(void);
+
+static void awe_reset_samples(void);
+/* emu8000 chip i/o access */
+static void setup_ports(int p1, int p2, int p3);
+static void awe_poke(unsigned short cmd, unsigned short port, unsigned short data);
+static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data);
+static unsigned short awe_peek(unsigned short cmd, unsigned short port);
+static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port);
+static void awe_wait(unsigned short delay);
+
+/* initialize emu8000 chip */
+static int _attach_awe(void);
+static void _unload_awe(void);
+static void awe_initialize(void);
+
+/* set voice parameters */
+static void awe_init_ctrl_parms(int init_all);
+static void awe_init_voice_info(awe_voice_info *vp);
+static void awe_init_voice_parm(awe_voice_parm *pp);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+static int freq_to_note(int freq);
+static int calc_rate_offset(int Hz);
+/*static int calc_parm_delay(int msec);*/
+static int calc_parm_hold(int msec);
+static int calc_parm_attack(int msec);
+static int calc_parm_decay(int msec);
+static int calc_parm_search(int msec, short *table);
+#endif /* gus compat */
+
+/* turn on/off note */
+static void awe_note_on(int voice);
+static void awe_note_off(int voice);
+static void awe_terminate(int voice);
+static void awe_exclusive_off(int voice);
+static void awe_note_off_all(int do_sustain);
+
+/* calculate voice parameters */
+typedef void (*fx_affect_func)(int voice, int forced);
+static void awe_set_pitch(int voice, int forced);
+static void awe_set_voice_pitch(int voice, int forced);
+static void awe_set_volume(int voice, int forced);
+static void awe_set_voice_vol(int voice, int forced);
+static void awe_set_pan(int voice, int forced);
+static void awe_fx_fmmod(int voice, int forced);
+static void awe_fx_tremfrq(int voice, int forced);
+static void awe_fx_fm2frq2(int voice, int forced);
+static void awe_fx_filterQ(int voice, int forced);
+static void awe_calc_pitch(int voice);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+static void awe_calc_pitch_from_freq(int voice, int freq);
+#endif
+static void awe_calc_volume(int voice);
+static void awe_update_volume(void);
+static void awe_change_master_volume(short val);
+static void awe_voice_init(int voice, int init_all);
+static void awe_channel_init(int ch, int init_all);
+static void awe_fx_init(int ch);
+static void awe_send_effect(int voice, int layer, int type, int val);
+static void awe_modwheel_change(int voice, int value);
+
+/* sequencer interface */
+static int awe_open(int dev, int mode);
+static void awe_close(int dev);
+static int awe_ioctl(int dev, unsigned int cmd, caddr_t arg);
+static int awe_kill_note(int dev, int voice, int note, int velocity);
+static int awe_start_note(int dev, int v, int note_num, int volume);
+static int awe_set_instr(int dev, int voice, int instr_no);
+static int awe_set_instr_2(int dev, int voice, int instr_no);
+static void awe_reset(int dev);
+static void awe_hw_control(int dev, unsigned char *event);
+static int awe_load_patch(int dev, int format, const char *addr,
+ int offs, int count, int pmgr_flag);
+static void awe_aftertouch(int dev, int voice, int pressure);
+static void awe_controller(int dev, int voice, int ctrl_num, int value);
+static void awe_panning(int dev, int voice, int value);
+static void awe_volume_method(int dev, int mode);
+static void awe_bender(int dev, int voice, int value);
+static int awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc);
+static void awe_setup_voice(int dev, int voice, int chn);
+
+#define awe_key_pressure(dev,voice,key,press) awe_start_note(dev,voice,(key)+128,press)
+
+/* hardware controls */
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+static void awe_hw_gus_control(int dev, int cmd, unsigned char *event);
+#endif
+static void awe_hw_awe_control(int dev, int cmd, unsigned char *event);
+static void awe_voice_change(int voice, fx_affect_func func);
+static void awe_sostenuto_on(int voice, int forced);
+static void awe_sustain_off(int voice, int forced);
+static void awe_terminate_and_init(int voice, int forced);
+
+/* voice search */
+static int awe_search_instr(int bank, int preset);
+static int awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist);
+static void awe_alloc_multi_voices(int ch, int note, int velocity, int key);
+static void awe_alloc_one_voice(int voice, int note, int velocity);
+static int awe_clear_voice(void);
+
+/* load / remove patches */
+static int awe_open_patch(awe_patch_info *patch, const char *addr, int count);
+static int awe_close_patch(awe_patch_info *patch, const char *addr, int count);
+static int awe_unload_patch(awe_patch_info *patch, const char *addr, int count);
+static int awe_load_info(awe_patch_info *patch, const char *addr, int count);
+static int awe_load_data(awe_patch_info *patch, const char *addr, int count);
+static int awe_replace_data(awe_patch_info *patch, const char *addr, int count);
+static int awe_load_map(awe_patch_info *patch, const char *addr, int count);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+static int awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag);
+#endif
+/*static int awe_probe_info(awe_patch_info *patch, const char *addr, int count);*/
+static int awe_probe_data(awe_patch_info *patch, const char *addr, int count);
+static int check_patch_opened(int type, char *name);
+static int awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels);
+static void add_sf_info(int rec);
+static void add_sf_sample(int rec);
+static void purge_old_list(int rec, int next);
+static void add_info_list(int rec);
+static void awe_remove_samples(int sf_id);
+static void rebuild_preset_list(void);
+static short awe_set_sample(awe_voice_info *vp);
+static int search_sample_index(int sf, int sample, int level);
+
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+static int is_identical_id(int id1, int id2);
+static int is_identical_name(unsigned char *name, int id);
+static int is_shared_sf(unsigned char *name);
+static int info_duplicated(awe_voice_list *rec);
+#endif /* allow sharing */
+
+/* lowlevel functions */
+static void awe_init_audio(void);
+static void awe_init_dma(void);
+static void awe_init_array(void);
+static void awe_send_array(unsigned short *data);
+static void awe_tweak_voice(int voice);
+static void awe_tweak(void);
+static void awe_init_fm(void);
+static int awe_open_dram_for_write(int offset, int channels);
+static void awe_open_dram_for_check(void);
+static void awe_close_dram(void);
+static void awe_write_dram(unsigned short c);
+static int awe_detect_base(int addr);
+static int awe_detect(void);
+static void awe_check_dram(void);
+static int awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count);
+static void awe_set_chorus_mode(int mode);
+static void awe_update_chorus_mode(void);
+static int awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count);
+static void awe_set_reverb_mode(int mode);
+static void awe_update_reverb_mode(void);
+static void awe_equalizer(int bass, int treble);
+static void awe_update_equalizer(void);
+
+#ifdef CONFIG_AWE32_MIXER
+static void attach_mixer(void);
+static void unload_mixer(void);
+#endif
+
+#ifdef CONFIG_AWE32_MIDIEMU
+static void attach_midiemu(void);
+static void unload_midiemu(void);
+#endif
+
+#define limitvalue(x, a, b) if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b)
+
+/*
+ * control parameters
+ */
+
+
+#ifdef AWE_USE_NEW_VOLUME_CALC
+#define DEF_VOLUME_CALC TRUE
+#else
+#define DEF_VOLUME_CALC FALSE
+#endif /* new volume */
+
+#define DEF_ZERO_ATTEN 32 /* 12dB below */
+#define DEF_MOD_SENSE 18
+#define DEF_CHORUS_MODE 2
+#define DEF_REVERB_MODE 4
+#define DEF_BASS_LEVEL 5
+#define DEF_TREBLE_LEVEL 9
+
+static struct CtrlParmsDef {
+ int value;
+ int init_each_time;
+ void (*update)(void);
+} ctrl_parms[AWE_MD_END] = {
+ {0,0, NULL}, {0,0, NULL}, /* <-- not used */
+ {AWE_VERSION_NUMBER, FALSE, NULL},
+ {TRUE, FALSE, NULL}, /* exclusive */
+ {TRUE, FALSE, NULL}, /* realpan */
+ {AWE_DEFAULT_BANK, FALSE, NULL}, /* gusbank */
+ {FALSE, TRUE, NULL}, /* keep effect */
+ {DEF_ZERO_ATTEN, FALSE, awe_update_volume}, /* zero_atten */
+ {FALSE, FALSE, NULL}, /* chn_prior */
+ {DEF_MOD_SENSE, FALSE, NULL}, /* modwheel sense */
+ {AWE_DEFAULT_PRESET, FALSE, NULL}, /* def_preset */
+ {AWE_DEFAULT_BANK, FALSE, NULL}, /* def_bank */
+ {AWE_DEFAULT_DRUM, FALSE, NULL}, /* def_drum */
+ {FALSE, FALSE, NULL}, /* toggle_drum_bank */
+ {DEF_VOLUME_CALC, FALSE, awe_update_volume}, /* new_volume_calc */
+ {DEF_CHORUS_MODE, FALSE, awe_update_chorus_mode}, /* chorus mode */
+ {DEF_REVERB_MODE, FALSE, awe_update_reverb_mode}, /* reverb mode */
+ {DEF_BASS_LEVEL, FALSE, awe_update_equalizer}, /* bass level */
+ {DEF_TREBLE_LEVEL, FALSE, awe_update_equalizer}, /* treble level */
+ {0, FALSE, NULL}, /* debug mode */
+ {FALSE, FALSE, NULL}, /* pan exchange */
+};
+
+static int ctrls[AWE_MD_END];
+
+
+/*----------------------------------------------------------------
+ * synth operation table
+ *----------------------------------------------------------------*/
+
+static struct synth_operations awe_operations =
+{
+ "EMU8K",
+ &awe_info,
+ 0,
+ SYNTH_TYPE_SAMPLE,
+ SAMPLE_TYPE_AWE32,
+ awe_open,
+ awe_close,
+ awe_ioctl,
+ awe_kill_note,
+ awe_start_note,
+ awe_set_instr_2,
+ awe_reset,
+ awe_hw_control,
+ awe_load_patch,
+ awe_aftertouch,
+ awe_controller,
+ awe_panning,
+ awe_volume_method,
+ awe_bender,
+ awe_alloc,
+ awe_setup_voice
+};
+
+
+/*================================================================
+ * General attach / unload interface
+ *================================================================*/
+
+static int _attach_awe(void)
+{
+ if (awe_present) return 0; /* for OSS38.. called twice? */
+
+ /* check presence of AWE32 card */
+ if (! awe_detect()) {
+ printk(KERN_WARNING "AWE32: not detected\n");
+ return 0;
+ }
+
+ /* check AWE32 ports are available */
+ if (awe_check_port()) {
+ printk(KERN_WARNING "AWE32: I/O area already used.\n");
+ return 0;
+ }
+
+ /* set buffers to NULL */
+ sflists = NULL;
+ samples = NULL;
+ infos = NULL;
+
+ /* allocate sample tables */
+ INIT_TABLE(sflists, max_sfs, AWE_MAX_SF_LISTS, sf_list);
+ INIT_TABLE(samples, max_samples, AWE_MAX_SAMPLES, awe_sample_list);
+ INIT_TABLE(infos, max_infos, AWE_MAX_INFOS, awe_voice_list);
+
+ my_dev = sound_alloc_synthdev();
+ if (my_dev == -1) {
+ printk(KERN_WARNING "AWE32 Error: too many synthesizers\n");
+ return 0;
+ }
+
+ voice_alloc = &awe_operations.alloc;
+ voice_alloc->max_voice = awe_max_voices;
+ synth_devs[my_dev] = &awe_operations;
+
+#ifdef CONFIG_AWE32_MIXER
+ attach_mixer();
+#endif
+#ifdef CONFIG_AWE32_MIDIEMU
+ attach_midiemu();
+#endif
+
+ /* reserve I/O ports for awedrv */
+ awe_request_region();
+
+ /* clear all samples */
+ awe_reset_samples();
+
+ /* intialize AWE32 hardware */
+ awe_initialize();
+
+ sprintf(awe_info.name, "AWE32-%s (RAM%dk)",
+ AWEDRV_VERSION, awe_mem_size/1024);
+ printk("<SoundBlaster EMU8000 (RAM%dk)>\n", awe_mem_size/1024);
+
+ awe_present = TRUE;
+
+ SOUND_LOCK;
+
+ return 1;
+}
+
+
+static void free_tables(void)
+{
+ if(sflists)
+ vfree(sflists);
+ sflists = NULL; max_sfs = 0;
+ if (samples)
+ vfree(samples);
+ samples = NULL; max_samples = 0;
+ if (infos)
+ vfree(infos);
+ infos = NULL; max_infos = 0;
+}
+
+static void *realloc_block(void *buf, int oldsize, int size)
+{
+ void *ptr;
+ if (oldsize == size)
+ return buf;
+ if ((ptr = vmalloc(size)) == NULL)
+ return NULL;
+ if (oldsize && size)
+ memcpy(ptr, buf, ((oldsize < size) ? oldsize : size) );
+ if (buf)
+ vfree(buf);
+ return ptr;
+}
+
+
+static void _unload_awe(void)
+{
+ if (awe_present) {
+ awe_reset_samples();
+ awe_release_region();
+ free_tables();
+#ifdef CONFIG_AWE32_MIXER
+ unload_mixer();
+#endif
+#ifdef CONFIG_AWE32_MIDIEMU
+ unload_midiemu();
+#endif
+ sound_unload_synthdev(my_dev);
+ awe_present = FALSE;
+ SOUND_LOCK_END;
+ }
+}
+
+/*
+ * Linux PnP driver support
+ */
+
+#ifdef CONFIG_PNP_DRV
+
+#include <linux/pnp.h>
+
+static int pnp = 1; /* use PnP as default */
+
+#define AWE_NUM_CHIPS 3
+static unsigned int pnp_ids[AWE_NUM_CHIPS] = {
+ PNP_EISAID('C','T','L',0x0021),
+ PNP_EISAID('C','T','L',0x0022),
+ PNP_EISAID('C','T','L',0x0023),
+};
+static struct pnp_driver pnp_awe[AWE_NUM_CHIPS];
+static int awe_pnp_ok = 0;
+
+static void awe_pnp_config(struct pnp_device *d)
+{
+ struct pnp_resource *r;
+ int port[3];
+ int nio = 0;
+
+ port[0] = port[1] = port[2] = 0;
+ for (r = d->res; r != NULL; r = r->next) {
+ if (r->type == PNP_RES_IO) {
+ if (nio >= 0 && nio < 3)
+ port[nio] = r->start;
+ nio++;
+ }
+ }
+ setup_ports(port[0], port[1], port[2]);
+ DEBUG(0,printk("AWE32: PnP setup ports: %x:%x:%x\n", port[0], port[1], port[2]));
+}
+
+static int awe_pnp_event (struct pnp_device *d, struct pnp_drv_event *e)
+{
+ struct pnp_driver *drv = d->l.k.driver;
+
+ switch (e->type) {
+ case PNP_DRV_ALLOC:
+ drv->flags |= PNP_DRV_INUSE;
+ awe_pnp_ok = 1;
+ awe_pnp_config(d);
+ _attach_awe();
+ break;
+
+ case PNP_DRV_DISABLE:
+ case PNP_DRV_EMERGSTOP:
+ drv->flags &= ~PNP_DRV_INUSE;
+ awe_pnp_ok = 0;
+ _unload_awe();
+ break;
+
+ case PNP_DRV_CONFIG:
+ if (awe_busy) return 1; /* used now */
+ awe_release_region();
+ awe_pnp_config(d);
+ awe_request_region();
+ break;
+
+ case PNP_DRV_RECONFIG:
+ break;
+ }
+ return 0;
+}
+
+static int awe_initpnp (void)
+{
+ int i;
+ for (i = 0; i < AWE_NUM_CHIPS; i++) {
+ pnp_awe[i].id.type = PNP_HDL_ISA;
+ pnp_awe[i].id.t.isa.id = pnp_ids[i];
+ pnp_awe[i].id.next = NULL;
+ pnp_awe[i].name = "Soundblaster AWE32/AWE64 PnP";
+ pnp_awe[i].event = awe_pnp_event;
+ pnp_register_driver(&pnp_awe[i], 1);
+ }
+ return 0;
+}
+
+static void awe_unload_pnp (void)
+{
+ int i;
+ for (i = 0; i < AWE_NUM_CHIPS; i++)
+ pnp_unregister_driver(&pnp_awe[i]);
+}
+#endif /* PnP support */
+
+/*
+ * clear sample tables
+ */
+
+static void
+awe_reset_samples(void)
+{
+ int i;
+
+ /* free all bank tables */
+ for (i = 0; i < AWE_MAX_PRESETS; i++)
+ preset_table[i] = -1;
+
+ free_tables();
+
+ current_sf_id = 0;
+ locked_sf_id = 0;
+ patch_opened = 0;
+}
+
+
+/*
+ * EMU register access
+ */
+
+/* select a given AWE32 pointer */
+static int awe_ports[5];
+static int port_setuped = FALSE;
+static int awe_cur_cmd = -1;
+#define awe_set_cmd(cmd) \
+if (awe_cur_cmd != cmd) { outw(cmd, awe_ports[Pointer]); awe_cur_cmd = cmd; }
+
+/* store values to i/o port array */
+static void setup_ports(int port1, int port2, int port3)
+{
+ awe_ports[0] = port1;
+ if (port2 == 0)
+ port2 = port1 + 0x400;
+ awe_ports[1] = port2;
+ awe_ports[2] = port2 + 2;
+ if (port3 == 0)
+ port3 = port1 + 0x800;
+ awe_ports[3] = port3;
+ awe_ports[4] = port3 + 2;
+
+ port_setuped = TRUE;
+}
+
+/* write 16bit data */
+static inline void
+awe_poke(unsigned short cmd, unsigned short port, unsigned short data)
+{
+ awe_set_cmd(cmd);
+ outw(data, awe_ports[port]);
+}
+
+/* write 32bit data */
+static inline void
+awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data)
+{
+ unsigned short addr = awe_ports[port];
+ awe_set_cmd(cmd);
+ outw(data, addr); /* write lower 16 bits */
+ outw(data >> 16, addr + 2); /* write higher 16 bits */
+}
+
+/* read 16bit data */
+static inline unsigned short
+awe_peek(unsigned short cmd, unsigned short port)
+{
+ unsigned short k;
+ awe_set_cmd(cmd);
+ k = inw(awe_ports[port]);
+ return k;
+}
+
+/* read 32bit data */
+static inline unsigned int
+awe_peek_dw(unsigned short cmd, unsigned short port)
+{
+ unsigned int k1, k2;
+ unsigned short addr = awe_ports[port];
+ awe_set_cmd(cmd);
+ k1 = inw(addr);
+ k2 = inw(addr + 2);
+ k1 |= k2 << 16;
+ return k1;
+}
+
+/* wait delay number of AWE32 44100Hz clocks */
+#ifdef WAIT_BY_LOOP /* wait by loop -- that's not good.. */
+static void
+awe_wait(unsigned short delay)
+{
+ unsigned short clock, target;
+ unsigned short port = awe_ports[AWE_WC_Port];
+ int counter;
+
+ /* sample counter */
+ awe_set_cmd(AWE_WC_Cmd);
+ clock = (unsigned short)inw(port);
+ target = clock + delay;
+ counter = 0;
+ if (target < clock) {
+ for (; (unsigned short)inw(port) > target; counter++)
+ if (counter > 65536)
+ break;
+ }
+ for (; (unsigned short)inw(port) < target; counter++)
+ if (counter > 65536)
+ break;
+}
+#else
+
+static void awe_wait(unsigned short delay)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout((HZ*(unsigned long)delay + 44099)/44100);
+}
+#endif /* wait by loop */
+
+/* write a word data */
+static inline void
+awe_write_dram(unsigned short c)
+{
+ awe_poke(AWE_SMLD, c);
+}
+
+
+/*
+ * port check / request
+ * 0x620-623, 0xA20-A23, 0xE20-E23
+ */
+
+static int
+awe_check_port(void)
+{
+ if (! port_setuped) return 0;
+ return (check_region(awe_ports[0], 4) ||
+ check_region(awe_ports[1], 4) ||
+ check_region(awe_ports[3], 4));
+}
+
+static void
+awe_request_region(void)
+{
+ if (! port_setuped) return;
+ request_region(awe_ports[0], 4, "sound driver (AWE32)");
+ request_region(awe_ports[1], 4, "sound driver (AWE32)");
+ request_region(awe_ports[3], 4, "sound driver (AWE32)");
+}
+
+static void
+awe_release_region(void)
+{
+ if (! port_setuped) return;
+ release_region(awe_ports[0], 4);
+ release_region(awe_ports[1], 4);
+ release_region(awe_ports[3], 4);
+}
+
+/*
+ * AWE32 initialization
+ */
+static void
+awe_initialize(void)
+{
+ DEBUG(0,printk("AWE32: initializing..\n"));
+
+ /* initialize hardware configuration */
+ awe_poke(AWE_HWCF1, 0x0059);
+ awe_poke(AWE_HWCF2, 0x0020);
+
+ /* disable audio; this seems to reduce a clicking noise a bit.. */
+ awe_poke(AWE_HWCF3, 0);
+
+ /* initialize audio channels */
+ awe_init_audio();
+
+ /* initialize DMA */
+ awe_init_dma();
+
+ /* initialize init array */
+ awe_init_array();
+
+ /* check DRAM memory size */
+ awe_check_dram();
+
+ /* initialize the FM section of the AWE32 */
+ awe_init_fm();
+
+ /* set up voice envelopes */
+ awe_tweak();
+
+ /* enable audio */
+ awe_poke(AWE_HWCF3, 0x0004);
+
+ /* set default values */
+ awe_init_ctrl_parms(TRUE);
+
+ /* set equalizer */
+ awe_update_equalizer();
+
+ /* set reverb & chorus modes */
+ awe_update_reverb_mode();
+ awe_update_chorus_mode();
+}
+
+
+/*
+ * AWE32 voice parameters
+ */
+
+/* initialize voice_info record */
+static void
+awe_init_voice_info(awe_voice_info *vp)
+{
+ vp->sf_id = 0; /* normal mode */
+ vp->sample = 0;
+ vp->rate_offset = 0;
+
+ vp->start = 0;
+ vp->end = 0;
+ vp->loopstart = 0;
+ vp->loopend = 0;
+ vp->mode = 0;
+ vp->root = 60;
+ vp->tune = 0;
+ vp->low = 0;
+ vp->high = 127;
+ vp->vellow = 0;
+ vp->velhigh = 127;
+
+ vp->fixkey = -1;
+ vp->fixvel = -1;
+ vp->fixpan = -1;
+ vp->pan = -1;
+
+ vp->exclusiveClass = 0;
+ vp->amplitude = 127;
+ vp->attenuation = 0;
+ vp->scaleTuning = 100;
+
+ awe_init_voice_parm(&vp->parm);
+}
+
+/* initialize voice_parm record:
+ * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0.
+ * Vibrato and Tremolo effects are zero.
+ * Cutoff is maximum.
+ * Chorus and Reverb effects are zero.
+ */
+static void
+awe_init_voice_parm(awe_voice_parm *pp)
+{
+ pp->moddelay = 0x8000;
+ pp->modatkhld = 0x7f7f;
+ pp->moddcysus = 0x7f7f;
+ pp->modrelease = 0x807f;
+ pp->modkeyhold = 0;
+ pp->modkeydecay = 0;
+
+ pp->voldelay = 0x8000;
+ pp->volatkhld = 0x7f7f;
+ pp->voldcysus = 0x7f7f;
+ pp->volrelease = 0x807f;
+ pp->volkeyhold = 0;
+ pp->volkeydecay = 0;
+
+ pp->lfo1delay = 0x8000;
+ pp->lfo2delay = 0x8000;
+ pp->pefe = 0;
+
+ pp->fmmod = 0;
+ pp->tremfrq = 0;
+ pp->fm2frq2 = 0;
+
+ pp->cutoff = 0xff;
+ pp->filterQ = 0;
+
+ pp->chorus = 0;
+ pp->reverb = 0;
+}
+
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
+/* convert frequency mHz to abstract cents (= midi key * 100) */
+static int
+freq_to_note(int mHz)
+{
+ /* abscents = log(mHz/8176) / log(2) * 1200 */
+ unsigned int max_val = (unsigned int)0xffffffff / 10000;
+ int i, times;
+ unsigned int base;
+ unsigned int freq;
+ int note, tune;
+
+ if (mHz == 0)
+ return 0;
+ if (mHz < 0)
+ return 12799; /* maximum */
+
+ freq = mHz;
+ note = 0;
+ for (base = 8176 * 2; freq >= base; base *= 2) {
+ note += 12;
+ if (note >= 128) /* over maximum */
+ return 12799;
+ }
+ base /= 2;
+
+ /* to avoid overflow... */
+ times = 10000;
+ while (freq > max_val) {
+ max_val *= 10;
+ times /= 10;
+ base /= 10;
+ }
+
+ freq = freq * times / base;
+ for (i = 0; i < 12; i++) {
+ if (freq < semitone_tuning[i+1])
+ break;
+ note++;
+ }
+
+ tune = 0;
+ freq = freq * 10000 / semitone_tuning[i];
+ for (i = 0; i < 100; i++) {
+ if (freq < cent_tuning[i+1])
+ break;
+ tune++;
+ }
+
+ return note * 100 + tune;
+}
+
+
+/* convert Hz to AWE32 rate offset:
+ * sample pitch offset for the specified sample rate
+ * rate=44100 is no offset, each 4096 is 1 octave (twice).
+ * eg, when rate is 22050, this offset becomes -4096.
+ */
+static int
+calc_rate_offset(int Hz)
+{
+ /* offset = log(Hz / 44100) / log(2) * 4096 */
+ int freq, base, i;
+
+ /* maybe smaller than max (44100Hz) */
+ if (Hz <= 0 || Hz >= 44100) return 0;
+
+ base = 0;
+ for (freq = Hz * 2; freq < 44100; freq *= 2)
+ base++;
+ base *= 1200;
+
+ freq = 44100 * 10000 / (freq/2);
+ for (i = 0; i < 12; i++) {
+ if (freq < semitone_tuning[i+1])
+ break;
+ base += 100;
+ }
+ freq = freq * 10000 / semitone_tuning[i];
+ for (i = 0; i < 100; i++) {
+ if (freq < cent_tuning[i+1])
+ break;
+ base++;
+ }
+ return -base * 4096 / 1200;
+}
+
+
+/*
+ * convert envelope time parameter to AWE32 raw parameter
+ */
+
+/* attack & decay/release time table (msec) */
+static short attack_time_tbl[128] = {
+32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816,
+707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377,
+361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188,
+180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94,
+90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47,
+45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23,
+22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12,
+11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0,
+};
+
+static short decay_time_tbl[128] = {
+32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082,
+2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507,
+1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722,
+691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361,
+345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180,
+172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90,
+86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45,
+43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22,
+};
+
+#define calc_parm_delay(msec) (0x8000 - (msec) * 1000 / 725);
+
+/* delay time = 0x8000 - msec/92 */
+static int
+calc_parm_hold(int msec)
+{
+ int val = (0x7f * 92 - msec) / 92;
+ if (val < 1) val = 1;
+ if (val > 127) val = 127;
+ return val;
+}
+
+/* attack time: search from time table */
+static int
+calc_parm_attack(int msec)
+{
+ return calc_parm_search(msec, attack_time_tbl);
+}
+
+/* decay/release time: search from time table */
+static int
+calc_parm_decay(int msec)
+{
+ return calc_parm_search(msec, decay_time_tbl);
+}
+
+/* search an index for specified time from given time table */
+static int
+calc_parm_search(int msec, short *table)
+{
+ int left = 1, right = 127, mid;
+ while (left < right) {
+ mid = (left + right) / 2;
+ if (msec < (int)table[mid])
+ left = mid + 1;
+ else
+ right = mid;
+ }
+ return left;
+}
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
+
+
+/*
+ * effects table
+ */
+
+/* set an effect value */
+#define FX_FLAG_OFF 0
+#define FX_FLAG_SET 1
+#define FX_FLAG_ADD 2
+
+#define FX_SET(rec,type,value) \
+ ((rec)->flags[type] = FX_FLAG_SET, (rec)->val[type] = (value))
+#define FX_ADD(rec,type,value) \
+ ((rec)->flags[type] = FX_FLAG_ADD, (rec)->val[type] = (value))
+#define FX_UNSET(rec,type) \
+ ((rec)->flags[type] = FX_FLAG_OFF, (rec)->val[type] = 0)
+
+/* check the effect value is set */
+#define FX_ON(rec,type) ((rec)->flags[type])
+
+#define PARM_BYTE 0
+#define PARM_WORD 1
+#define PARM_SIGN 2
+
+static struct PARM_DEFS {
+ int type; /* byte or word */
+ int low, high; /* value range */
+ fx_affect_func realtime; /* realtime paramater change */
+} parm_defs[] = {
+ {PARM_WORD, 0, 0x8000, NULL}, /* env1 delay */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env1 attack */
+ {PARM_BYTE, 0, 0x7e, NULL}, /* env1 hold */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env1 decay */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env1 release */
+ {PARM_BYTE, 0, 0x7f, NULL}, /* env1 sustain */
+ {PARM_BYTE, 0, 0xff, NULL}, /* env1 pitch */
+ {PARM_BYTE, 0, 0xff, NULL}, /* env1 cutoff */
+
+ {PARM_WORD, 0, 0x8000, NULL}, /* env2 delay */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env2 attack */
+ {PARM_BYTE, 0, 0x7e, NULL}, /* env2 hold */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env2 decay */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env2 release */
+ {PARM_BYTE, 0, 0x7f, NULL}, /* env2 sustain */
+
+ {PARM_WORD, 0, 0x8000, NULL}, /* lfo1 delay */
+ {PARM_BYTE, 0, 0xff, awe_fx_tremfrq}, /* lfo1 freq */
+ {PARM_SIGN, -128, 127, awe_fx_tremfrq}, /* lfo1 volume */
+ {PARM_SIGN, -128, 127, awe_fx_fmmod}, /* lfo1 pitch */
+ {PARM_BYTE, 0, 0xff, awe_fx_fmmod}, /* lfo1 cutoff */
+
+ {PARM_WORD, 0, 0x8000, NULL}, /* lfo2 delay */
+ {PARM_BYTE, 0, 0xff, awe_fx_fm2frq2}, /* lfo2 freq */
+ {PARM_SIGN, -128, 127, awe_fx_fm2frq2}, /* lfo2 pitch */
+
+ {PARM_WORD, 0, 0xffff, awe_set_voice_pitch}, /* initial pitch */
+ {PARM_BYTE, 0, 0xff, NULL}, /* chorus */
+ {PARM_BYTE, 0, 0xff, NULL}, /* reverb */
+ {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial cutoff */
+ {PARM_BYTE, 0, 15, awe_fx_filterQ}, /* initial resonance */
+
+ {PARM_WORD, 0, 0xffff, NULL}, /* sample start */
+ {PARM_WORD, 0, 0xffff, NULL}, /* loop start */
+ {PARM_WORD, 0, 0xffff, NULL}, /* loop end */
+ {PARM_WORD, 0, 0xffff, NULL}, /* coarse sample start */
+ {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop start */
+ {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop end */
+ {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial attenuation */
+};
+
+
+static unsigned char
+FX_BYTE(FX_Rec *rec, FX_Rec *lay, int type, unsigned char value)
+{
+ int effect = 0;
+ int on = 0;
+ if (lay && (on = FX_ON(lay, type)) != 0)
+ effect = lay->val[type];
+ if (!on && (on = FX_ON(rec, type)) != 0)
+ effect = rec->val[type];
+ if (on == FX_FLAG_ADD) {
+ if (parm_defs[type].type == PARM_SIGN) {
+ if (value > 0x7f)
+ effect += (int)value - 0x100;
+ else
+ effect += (int)value;
+ } else {
+ effect += (int)value;
+ }
+ }
+ if (on) {
+ if (effect < parm_defs[type].low)
+ effect = parm_defs[type].low;
+ else if (effect > parm_defs[type].high)
+ effect = parm_defs[type].high;
+ return (unsigned char)effect;
+ }
+ return value;
+}
+
+/* get word effect value */
+static unsigned short
+FX_WORD(FX_Rec *rec, FX_Rec *lay, int type, unsigned short value)
+{
+ int effect = 0;
+ int on = 0;
+ if (lay && (on = FX_ON(lay, type)) != 0)
+ effect = lay->val[type];
+ if (!on && (on = FX_ON(rec, type)) != 0)
+ effect = rec->val[type];
+ if (on == FX_FLAG_ADD)
+ effect += (int)value;
+ if (on) {
+ if (effect < parm_defs[type].low)
+ effect = parm_defs[type].low;
+ else if (effect > parm_defs[type].high)
+ effect = parm_defs[type].high;
+ return (unsigned short)effect;
+ }
+ return value;
+}
+
+/* get word (upper=type1/lower=type2) effect value */
+static unsigned short
+FX_COMB(FX_Rec *rec, FX_Rec *lay, int type1, int type2, unsigned short value)
+{
+ unsigned short tmp;
+ tmp = FX_BYTE(rec, lay, type1, (unsigned char)(value >> 8));
+ tmp <<= 8;
+ tmp |= FX_BYTE(rec, lay, type2, (unsigned char)(value & 0xff));
+ return tmp;
+}
+
+/* address offset */
+static int
+FX_OFFSET(FX_Rec *rec, FX_Rec *lay, int lo, int hi, int mode)
+{
+ int addr = 0;
+ if (lay && FX_ON(lay, hi))
+ addr = (short)lay->val[hi];
+ else if (FX_ON(rec, hi))
+ addr = (short)rec->val[hi];
+ addr = addr << 15;
+ if (lay && FX_ON(lay, lo))
+ addr += (short)lay->val[lo];
+ else if (FX_ON(rec, lo))
+ addr += (short)rec->val[lo];
+ if (!(mode & AWE_SAMPLE_8BITS))
+ addr /= 2;
+ return addr;
+}
+
+
+/*
+ * turn on/off sample
+ */
+
+/* table for volume target calculation */
+static unsigned short voltarget[16] = {
+ 0xEAC0, 0XE0C8, 0XD740, 0XCE20, 0XC560, 0XBD08, 0XB500, 0XAD58,
+ 0XA5F8, 0X9EF0, 0X9830, 0X91C0, 0X8B90, 0X85A8, 0X8000, 0X7A90
+};
+
+static void
+awe_note_on(int voice)
+{
+ unsigned int temp;
+ int addr;
+ int vtarget, ftarget, ptarget, pitch;
+ awe_voice_info *vp;
+ awe_voice_parm_block *parm;
+ FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+ /* A voice sample must assigned before calling */
+ if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+ return;
+
+ parm = (awe_voice_parm_block*)&vp->parm;
+
+ /* channel to be silent and idle */
+ awe_poke(AWE_DCYSUSV(voice), 0x0080);
+ awe_poke(AWE_VTFT(voice), 0x0000FFFF);
+ awe_poke(AWE_CVCF(voice), 0x0000FFFF);
+ awe_poke(AWE_PTRX(voice), 0);
+ awe_poke(AWE_CPF(voice), 0);
+
+ /* set pitch offset */
+ awe_set_pitch(voice, TRUE);
+
+ /* modulation & volume envelope */
+ if (parm->modatk >= 0x80 && parm->moddelay >= 0x8000) {
+ awe_poke(AWE_ENVVAL(voice), 0xBFFF);
+ pitch = (parm->env1pit<<4) + voices[voice].apitch;
+ if (pitch > 0xffff) pitch = 0xffff;
+ /* calculate filter target */
+ ftarget = parm->cutoff + parm->env1fc;
+ limitvalue(ftarget, 0, 255);
+ ftarget <<= 8;
+ } else {
+ awe_poke(AWE_ENVVAL(voice),
+ FX_WORD(fx, fx_lay, AWE_FX_ENV1_DELAY, parm->moddelay));
+ ftarget = parm->cutoff;
+ ftarget <<= 8;
+ pitch = voices[voice].apitch;
+ }
+
+ /* calcualte pitch target */
+ if (pitch != 0xffff) {
+ ptarget = 1 << (pitch >> 12);
+ if (pitch & 0x800) ptarget += (ptarget*0x102e)/0x2710;
+ if (pitch & 0x400) ptarget += (ptarget*0x764)/0x2710;
+ if (pitch & 0x200) ptarget += (ptarget*0x389)/0x2710;
+ ptarget += (ptarget>>1);
+ if (ptarget > 0xffff) ptarget = 0xffff;
+
+ } else ptarget = 0xffff;
+ if (parm->modatk >= 0x80)
+ awe_poke(AWE_ATKHLD(voice),
+ FX_BYTE(fx, fx_lay, AWE_FX_ENV1_HOLD, parm->modhld) << 8 | 0x7f);
+ else
+ awe_poke(AWE_ATKHLD(voice),
+ FX_COMB(fx, fx_lay, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK,
+ vp->parm.modatkhld));
+ awe_poke(AWE_DCYSUS(voice),
+ FX_COMB(fx, fx_lay, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
+ vp->parm.moddcysus));
+
+ if (parm->volatk >= 0x80 && parm->voldelay >= 0x8000) {
+ awe_poke(AWE_ENVVOL(voice), 0xBFFF);
+ vtarget = voltarget[voices[voice].avol%0x10]>>(voices[voice].avol>>4);
+ } else {
+ awe_poke(AWE_ENVVOL(voice),
+ FX_WORD(fx, fx_lay, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
+ vtarget = 0;
+ }
+ if (parm->volatk >= 0x80)
+ awe_poke(AWE_ATKHLDV(voice),
+ FX_BYTE(fx, fx_lay, AWE_FX_ENV2_HOLD, parm->volhld) << 8 | 0x7f);
+ else
+ awe_poke(AWE_ATKHLDV(voice),
+ FX_COMB(fx, fx_lay, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK,
+ vp->parm.volatkhld));
+ /* decay/sustain parameter for volume envelope must be set at last */
+
+ /* cutoff and volume */
+ awe_set_volume(voice, TRUE);
+
+ /* modulation envelope heights */
+ awe_poke(AWE_PEFE(voice),
+ FX_COMB(fx, fx_lay, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
+ vp->parm.pefe));
+
+ /* lfo1/2 delay */
+ awe_poke(AWE_LFO1VAL(voice),
+ FX_WORD(fx, fx_lay, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay));
+ awe_poke(AWE_LFO2VAL(voice),
+ FX_WORD(fx, fx_lay, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay));
+
+ /* lfo1 pitch & cutoff shift */
+ awe_fx_fmmod(voice, TRUE);
+ /* lfo1 volume & freq */
+ awe_fx_tremfrq(voice, TRUE);
+ /* lfo2 pitch & freq */
+ awe_fx_fm2frq2(voice, TRUE);
+ /* pan & loop start */
+ awe_set_pan(voice, TRUE);
+
+ /* chorus & loop end (chorus 8bit, MSB) */
+ addr = vp->loopend - 1;
+ addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_END,
+ AWE_FX_COARSE_LOOP_END, vp->mode);
+ temp = FX_BYTE(fx, fx_lay, AWE_FX_CHORUS, vp->parm.chorus);
+ temp = (temp <<24) | (unsigned int)addr;
+ awe_poke_dw(AWE_CSL(voice), temp);
+ DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n", vp->loopend, addr));
+
+ /* Q & current address (Q 4bit value, MSB) */
+ addr = vp->start - 1;
+ addr += FX_OFFSET(fx, fx_lay, AWE_FX_SAMPLE_START,
+ AWE_FX_COARSE_SAMPLE_START, vp->mode);
+ temp = FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ);
+ temp = (temp<<28) | (unsigned int)addr;
+ awe_poke_dw(AWE_CCCA(voice), temp);
+ DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n", vp->start, addr));
+
+ /* clear unknown registers */
+ awe_poke_dw(AWE_00A0(voice), 0);
+ awe_poke_dw(AWE_0080(voice), 0);
+
+ /* reset volume */
+ awe_poke_dw(AWE_VTFT(voice), (vtarget<<16)|ftarget);
+ awe_poke_dw(AWE_CVCF(voice), (vtarget<<16)|ftarget);
+
+ /* turn on envelope */
+ awe_poke(AWE_DCYSUSV(voice),
+ FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
+ vp->parm.voldcysus));
+ /* set reverb */
+ temp = FX_BYTE(fx, fx_lay, AWE_FX_REVERB, vp->parm.reverb);
+ temp = (temp << 8) | (ptarget << 16) | voices[voice].aaux;
+ awe_poke_dw(AWE_PTRX(voice), temp);
+ awe_poke_dw(AWE_CPF(voice), ptarget << 16);
+
+ voices[voice].state = AWE_ST_ON;
+
+ /* clear voice position for the next note on this channel */
+ if (SINGLE_LAYER_MODE()) {
+ FX_UNSET(fx, AWE_FX_SAMPLE_START);
+ FX_UNSET(fx, AWE_FX_COARSE_SAMPLE_START);
+ }
+}
+
+
+/* turn off the voice */
+static void
+awe_note_off(int voice)
+{
+ awe_voice_info *vp;
+ unsigned short tmp;
+ FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+ if ((vp = voices[voice].sample) == NULL) {
+ voices[voice].state = AWE_ST_OFF;
+ return;
+ }
+
+ tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV1_RELEASE,
+ (unsigned char)vp->parm.modrelease);
+ awe_poke(AWE_DCYSUS(voice), tmp);
+ tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV2_RELEASE,
+ (unsigned char)vp->parm.volrelease);
+ awe_poke(AWE_DCYSUSV(voice), tmp);
+ voices[voice].state = AWE_ST_RELEASED;
+}
+
+/* force to terminate the voice (no releasing echo) */
+static void
+awe_terminate(int voice)
+{
+ awe_poke(AWE_DCYSUSV(voice), 0x807F);
+ awe_tweak_voice(voice);
+ voices[voice].state = AWE_ST_OFF;
+}
+
+/* turn off other voices with the same exclusive class (for drums) */
+static void
+awe_exclusive_off(int voice)
+{
+ int i, exclass;
+
+ if (voices[voice].sample == NULL)
+ return;
+ if ((exclass = voices[voice].sample->exclusiveClass) == 0)
+ return; /* not exclusive */
+
+ /* turn off voices with the same class */
+ for (i = 0; i < awe_max_voices; i++) {
+ if (i != voice && IS_PLAYING(i) &&
+ voices[i].sample && voices[i].ch == voices[voice].ch &&
+ voices[i].sample->exclusiveClass == exclass) {
+ DEBUG(4,printk("AWE32: [exoff(%d)]\n", i));
+ awe_terminate(i);
+ awe_voice_init(i, TRUE);
+ }
+ }
+}
+
+
+/*
+ * change the parameters of an audible voice
+ */
+
+/* change pitch */
+static void
+awe_set_pitch(int voice, int forced)
+{
+ if (IS_NO_EFFECT(voice) && !forced) return;
+ awe_poke(AWE_IP(voice), voices[voice].apitch);
+ DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch));
+}
+
+/* calculate & change pitch */
+static void
+awe_set_voice_pitch(int voice, int forced)
+{
+ awe_calc_pitch(voice);
+ awe_set_pitch(voice, forced);
+}
+
+/* change volume & cutoff */
+static void
+awe_set_volume(int voice, int forced)
+{
+ awe_voice_info *vp;
+ unsigned short tmp2;
+ FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+ if (!IS_PLAYING(voice) && !forced) return;
+ if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+ return;
+
+ tmp2 = FX_BYTE(fx, fx_lay, AWE_FX_CUTOFF,
+ (unsigned char)voices[voice].acutoff);
+ tmp2 = (tmp2 << 8);
+ tmp2 |= FX_BYTE(fx, fx_lay, AWE_FX_ATTEN,
+ (unsigned char)voices[voice].avol);
+ awe_poke(AWE_IFATN(voice), tmp2);
+}
+
+/* calculate & change volume */
+static void
+awe_set_voice_vol(int voice, int forced)
+{
+ if (IS_EMPTY(voice))
+ return;
+ awe_calc_volume(voice);
+ awe_set_volume(voice, forced);
+}
+
+
+/* change pan; this could make a click noise.. */
+static void
+awe_set_pan(int voice, int forced)
+{
+ unsigned int temp;
+ int addr;
+ awe_voice_info *vp;
+ FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+ if (IS_NO_EFFECT(voice) && !forced) return;
+ if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+ return;
+
+ /* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */
+ if (vp->fixpan > 0) /* 0-127 */
+ temp = 255 - (int)vp->fixpan * 2;
+ else {
+ int pos = 0;
+ if (vp->pan >= 0) /* 0-127 */
+ pos = (int)vp->pan * 2 - 128;
+ pos += voices[voice].cinfo->panning; /* -128 - 127 */
+ temp = 127 - pos;
+ }
+ limitvalue(temp, 0, 255);
+ if (ctrls[AWE_MD_PAN_EXCHANGE]) {
+ temp = 255 - temp;
+ }
+ if (forced || temp != voices[voice].apan) {
+ voices[voice].apan = temp;
+ addr = vp->loopstart - 1;
+ addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_START,
+ AWE_FX_COARSE_LOOP_START, vp->mode);
+ temp = (temp<<24) | (unsigned int)addr;
+ awe_poke_dw(AWE_PSST(voice), temp);
+ DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n", vp->loopstart, addr));
+ if (temp == 0) voices[voice].aaux = 0xff;
+ else voices[voice].aaux = (-temp)&0xff;
+ }
+}
+
+/* effects change during playing */
+static void
+awe_fx_fmmod(int voice, int forced)
+{
+ awe_voice_info *vp;
+ FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+ if (IS_NO_EFFECT(voice) && !forced) return;
+ if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+ return;
+ awe_poke(AWE_FMMOD(voice),
+ FX_COMB(fx, fx_lay, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
+ vp->parm.fmmod));
+}
+
+/* set tremolo (lfo1) volume & frequency */
+static void
+awe_fx_tremfrq(int voice, int forced)
+{
+ awe_voice_info *vp;
+ FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+ if (IS_NO_EFFECT(voice) && !forced) return;
+ if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+ return;
+ awe_poke(AWE_TREMFRQ(voice),
+ FX_COMB(fx, fx_lay, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
+ vp->parm.tremfrq));
+}
+
+/* set lfo2 pitch & frequency */
+static void
+awe_fx_fm2frq2(int voice, int forced)
+{
+ awe_voice_info *vp;
+ FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+ if (IS_NO_EFFECT(voice) && !forced) return;
+ if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+ return;
+ awe_poke(AWE_FM2FRQ2(voice),
+ FX_COMB(fx, fx_lay, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
+ vp->parm.fm2frq2));
+}
+
+
+/* Q & current address (Q 4bit value, MSB) */
+static void
+awe_fx_filterQ(int voice, int forced)
+{
+ unsigned int addr;
+ awe_voice_info *vp;
+ FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+ if (IS_NO_EFFECT(voice) && !forced) return;
+ if ((vp = voices[voice].sample) == NULL || vp->index < 0)
+ return;
+
+ addr = awe_peek_dw(AWE_CCCA(voice)) & 0xffffff;
+ addr |= (FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ) << 28);
+ awe_poke_dw(AWE_CCCA(voice), addr);
+}
+
+/*================================================================
+ * calculate pitch offset
+ *----------------------------------------------------------------
+ * 0xE000 is no pitch offset at 44100Hz sample.
+ * Every 4096 is one octave.
+ *================================================================*/
+
+static void
+awe_calc_pitch(int voice)
+{
+ voice_info *vp = &voices[voice];
+ awe_voice_info *ap;
+ awe_chan_info *cp = voices[voice].cinfo;
+ int offset;
+
+ /* search voice information */
+ if ((ap = vp->sample) == NULL)
+ return;
+ if (ap->index < 0) {
+ DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
+ if (awe_set_sample(ap) < 0)
+ return;
+ }
+
+ /* calculate offset */
+ if (ap->fixkey >= 0) {
+ DEBUG(3,printk("AWE32: p-> fixkey(%d) tune(%d)\n", ap->fixkey, ap->tune));
+ offset = (ap->fixkey - ap->root) * 4096 / 12;
+ } else {
+ DEBUG(3,printk("AWE32: p(%d)-> root(%d) tune(%d)\n", vp->note, ap->root, ap->tune));
+ offset = (vp->note - ap->root) * 4096 / 12;
+ DEBUG(4,printk("AWE32: p-> ofs=%d\n", offset));
+ }
+ offset = (offset * ap->scaleTuning) / 100;
+ DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset));
+ offset += ap->tune * 4096 / 1200;
+ DEBUG(4,printk("AWE32: p-> tune+ ofs=%d\n", offset));
+ if (cp->bender != 0) {
+ DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, cp->bender));
+ /* (819200: 1 semitone) ==> (4096: 12 semitones) */
+ offset += cp->bender * cp->bender_range / 2400;
+ }
+
+ /* add initial pitch correction */
+ if (FX_ON(&cp->fx_layer[vp->layer], AWE_FX_INIT_PITCH))
+ offset += cp->fx_layer[vp->layer].val[AWE_FX_INIT_PITCH];
+ else if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH))
+ offset += cp->fx.val[AWE_FX_INIT_PITCH];
+
+ /* 0xe000: root pitch */
+ vp->apitch = 0xe000 + ap->rate_offset + offset;
+ DEBUG(4,printk("AWE32: p-> sum aofs=%x, rate_ofs=%d\n", vp->apitch, ap->rate_offset));
+ if (vp->apitch > 0xffff)
+ vp->apitch = 0xffff;
+ if (vp->apitch < 0)
+ vp->apitch = 0;
+}
+
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+/* calculate MIDI key and semitone from the specified frequency */
+static void
+awe_calc_pitch_from_freq(int voice, int freq)
+{
+ voice_info *vp = &voices[voice];
+ awe_voice_info *ap;
+ FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ int offset;
+ int note;
+
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+ /* search voice information */
+ if ((ap = vp->sample) == NULL)
+ return;
+ if (ap->index < 0) {
+ DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
+ if (awe_set_sample(ap) < 0)
+ return;
+ }
+ note = freq_to_note(freq);
+ offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200;
+ offset = (offset * ap->scaleTuning) / 100;
+ if (fx_lay && FX_ON(fx_lay, AWE_FX_INIT_PITCH))
+ offset += fx_lay->val[AWE_FX_INIT_PITCH];
+ else if (FX_ON(fx, AWE_FX_INIT_PITCH))
+ offset += fx->val[AWE_FX_INIT_PITCH];
+ vp->apitch = 0xe000 + ap->rate_offset + offset;
+ if (vp->apitch > 0xffff)
+ vp->apitch = 0xffff;
+ if (vp->apitch < 0)
+ vp->apitch = 0;
+}
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
+
+
+/*================================================================
+ * calculate volume attenuation
+ *----------------------------------------------------------------
+ * Voice volume is controlled by volume attenuation parameter.
+ * So volume becomes maximum when avol is 0 (no attenuation), and
+ * minimum when 255 (-96dB or silence).
+ *================================================================*/
+
+static int vol_table[128] = {
+ 255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49,
+ 47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32,
+ 31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22,
+ 22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16,
+ 15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10,
+ 10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6,
+ 6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3,
+ 2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0,
+};
+
+/* tables for volume->attenuation calculation */
+static unsigned char voltab1[128] = {
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22,
+ 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a,
+ 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14,
+ 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10,
+ 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d,
+ 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char voltab2[128] = {
+ 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a,
+ 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21,
+ 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a,
+ 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15,
+ 0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10,
+ 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d,
+ 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char expressiontab[128] = {
+ 0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42,
+ 0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30,
+ 0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25,
+ 0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e,
+ 0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18,
+ 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13,
+ 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f,
+ 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c,
+ 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09,
+ 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
+ 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03,
+ 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static void
+awe_calc_volume(int voice)
+{
+ voice_info *vp = &voices[voice];
+ awe_voice_info *ap;
+ awe_chan_info *cp = voices[voice].cinfo;
+ int vol;
+
+ /* search voice information */
+ if ((ap = vp->sample) == NULL)
+ return;
+
+ ap = vp->sample;
+ if (ap->index < 0) {
+ DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
+ if (awe_set_sample(ap) < 0)
+ return;
+ }
+
+ if (ctrls[AWE_MD_NEW_VOLUME_CALC]) {
+ int main_vol = cp->main_vol * ap->amplitude / 127;
+ limitvalue(vp->velocity, 0, 127);
+ limitvalue(main_vol, 0, 127);
+ limitvalue(cp->expression_vol, 0, 127);
+
+ vol = voltab1[main_vol] + voltab2[vp->velocity];
+ vol = (vol * 8) / 3;
+ vol += ap->attenuation;
+ if (cp->expression_vol < 127)
+ vol += ((0x100 - vol) * expressiontab[cp->expression_vol])/128;
+ vol += atten_offset;
+ if (atten_relative)
+ vol += ctrls[AWE_MD_ZERO_ATTEN];
+ limitvalue(vol, 0, 255);
+ vp->avol = vol;
+
+ } else {
+ /* 0 - 127 */
+ vol = (vp->velocity * cp->main_vol * cp->expression_vol) / (127*127);
+ vol = vol * ap->amplitude / 127;
+
+ if (vol < 0) vol = 0;
+ if (vol > 127) vol = 127;
+
+ /* calc to attenuation */
+ vol = vol_table[vol];
+ vol += (int)ap->attenuation;
+ vol += atten_offset;
+ if (atten_relative)
+ vol += ctrls[AWE_MD_ZERO_ATTEN];
+ if (vol > 255) vol = 255;
+
+ vp->avol = vol;
+ }
+ if (cp->bank != AWE_DRUM_BANK && ((awe_voice_parm_block*)(&ap->parm))->volatk < 0x7d) {
+ int atten;
+ if (vp->velocity < 70) atten = 70;
+ else atten = vp->velocity;
+ vp->acutoff = (atten * ap->parm.cutoff + 0xa0) >> 7;
+ } else {
+ vp->acutoff = ap->parm.cutoff;
+ }
+ DEBUG(3,printk("AWE32: [-- voice(%d) vol=%x]\n", voice, vol));
+}
+
+/* change master volume */
+static void
+awe_change_master_volume(short val)
+{
+ limitvalue(val, 0, 127);
+ atten_offset = vol_table[val];
+ atten_relative = TRUE;
+ awe_update_volume();
+}
+
+/* update volumes of all available channels */
+static void awe_update_volume(void)
+{
+ int i;
+ for (i = 0; i < awe_max_voices; i++)
+ awe_set_voice_vol(i, TRUE);
+}
+
+/* set sostenuto on */
+static void awe_sostenuto_on(int voice, int forced)
+{
+ if (IS_NO_EFFECT(voice) && !forced) return;
+ voices[voice].sostenuto = 127;
+}
+
+
+/* drop sustain */
+static void awe_sustain_off(int voice, int forced)
+{
+ if (voices[voice].state == AWE_ST_SUSTAINED) {
+ awe_note_off(voice);
+ awe_fx_init(voices[voice].ch);
+ awe_voice_init(voice, FALSE);
+ }
+}
+
+
+/* terminate and initialize voice */
+static void awe_terminate_and_init(int voice, int forced)
+{
+ awe_terminate(voice);
+ awe_fx_init(voices[voice].ch);
+ awe_voice_init(voice, TRUE);
+}
+
+
+/*
+ * synth operation routines
+ */
+
+#define AWE_VOICE_KEY(v) (0x8000 | (v))
+#define AWE_CHAN_KEY(c,n) (((c) << 8) | ((n) + 1))
+#define KEY_CHAN_MATCH(key,c) (((key) >> 8) == (c))
+
+/* initialize the voice */
+static void
+awe_voice_init(int voice, int init_all)
+{
+ voice_info *vp = &voices[voice];
+
+ /* reset voice search key */
+ if (playing_mode == AWE_PLAY_DIRECT)
+ vp->key = AWE_VOICE_KEY(voice);
+ else
+ vp->key = 0;
+
+ /* clear voice mapping */
+ voice_alloc->map[voice] = 0;
+
+ /* touch the timing flag */
+ vp->time = current_alloc_time;
+
+ /* initialize other parameters if necessary */
+ if (init_all) {
+ vp->note = -1;
+ vp->velocity = 0;
+ vp->sostenuto = 0;
+
+ vp->sample = NULL;
+ vp->cinfo = &channels[voice];
+ vp->ch = voice;
+ vp->state = AWE_ST_OFF;
+
+ /* emu8000 parameters */
+ vp->apitch = 0;
+ vp->avol = 255;
+ vp->apan = -1;
+ }
+}
+
+/* clear effects */
+static void awe_fx_init(int ch)
+{
+ if (SINGLE_LAYER_MODE() && !ctrls[AWE_MD_KEEP_EFFECT]) {
+ memset(&channels[ch].fx, 0, sizeof(channels[ch].fx));
+ memset(&channels[ch].fx_layer, 0, sizeof(&channels[ch].fx_layer));
+ }
+}
+
+/* initialize channel info */
+static void awe_channel_init(int ch, int init_all)
+{
+ awe_chan_info *cp = &channels[ch];
+ cp->channel = ch;
+ if (init_all) {
+ cp->panning = 0; /* zero center */
+ cp->bender_range = 200; /* sense * 100 */
+ cp->main_vol = 127;
+ if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) {
+ cp->instr = ctrls[AWE_MD_DEF_DRUM];
+ cp->bank = AWE_DRUM_BANK;
+ } else {
+ cp->instr = ctrls[AWE_MD_DEF_PRESET];
+ cp->bank = ctrls[AWE_MD_DEF_BANK];
+ }
+ cp->vrec = -1;
+ cp->def_vrec = -1;
+ }
+
+ cp->bender = 0; /* zero tune skew */
+ cp->expression_vol = 127;
+ cp->chan_press = 0;
+ cp->sustained = 0;
+
+ if (! ctrls[AWE_MD_KEEP_EFFECT]) {
+ memset(&cp->fx, 0, sizeof(cp->fx));
+ memset(&cp->fx_layer, 0, sizeof(cp->fx_layer));
+ }
+}
+
+
+/* change the voice parameters; voice = channel */
+static void awe_voice_change(int voice, fx_affect_func func)
+{
+ int i;
+ switch (playing_mode) {
+ case AWE_PLAY_DIRECT:
+ func(voice, FALSE);
+ break;
+ case AWE_PLAY_INDIRECT:
+ for (i = 0; i < awe_max_voices; i++)
+ if (voices[i].key == AWE_VOICE_KEY(voice))
+ func(i, FALSE);
+ break;
+ default:
+ for (i = 0; i < awe_max_voices; i++)
+ if (KEY_CHAN_MATCH(voices[i].key, voice))
+ func(i, FALSE);
+ break;
+ }
+}
+
+
+/*----------------------------------------------------------------
+ * device open / close
+ *----------------------------------------------------------------*/
+
+/* open device:
+ * reset status of all voices, and clear sample position flag
+ */
+static int
+awe_open(int dev, int mode)
+{
+ if (awe_busy)
+ return -EBUSY;
+
+ awe_busy = TRUE;
+
+ /* set default mode */
+ awe_init_ctrl_parms(FALSE);
+ atten_relative = TRUE;
+ atten_offset = 0;
+ drum_flags = DEFAULT_DRUM_FLAGS;
+ playing_mode = AWE_PLAY_INDIRECT;
+
+ /* reset voices & channels */
+ awe_reset(dev);
+
+ patch_opened = 0;
+
+ return 0;
+}
+
+
+/* close device:
+ * reset all voices again (terminate sounds)
+ */
+static void
+awe_close(int dev)
+{
+ awe_reset(dev);
+ awe_busy = FALSE;
+}
+
+
+/* set miscellaneous mode parameters
+ */
+static void
+awe_init_ctrl_parms(int init_all)
+{
+ int i;
+ for (i = 0; i < AWE_MD_END; i++) {
+ if (init_all || ctrl_parms[i].init_each_time)
+ ctrls[i] = ctrl_parms[i].value;
+ }
+}
+
+
+/* sequencer I/O control:
+ */
+static int
+awe_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+ switch (cmd) {
+ case SNDCTL_SYNTH_INFO:
+ if (playing_mode == AWE_PLAY_DIRECT)
+ awe_info.nr_voices = awe_max_voices;
+ else
+ awe_info.nr_voices = AWE_MAX_CHANNELS;
+ memcpy((char*)arg, &awe_info + 0, sizeof(awe_info));
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_RESETSAMPLES:
+ awe_reset_samples();
+ awe_reset(dev);
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_PERCMODE:
+ /* what's this? */
+ return 0;
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return awe_mem_size - awe_free_mem_ptr() * 2;
+
+ default:
+ printk("AWE32: unsupported ioctl %d\n", cmd);
+ return -EINVAL;
+ }
+}
+
+
+static int voice_in_range(int voice)
+{
+ if (playing_mode == AWE_PLAY_DIRECT) {
+ if (voice < 0 || voice >= awe_max_voices)
+ return FALSE;
+ } else {
+ if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void release_voice(int voice, int do_sustain)
+{
+ if (IS_NO_SOUND(voice))
+ return;
+ if (do_sustain && (voices[voice].cinfo->sustained == 127 ||
+ voices[voice].sostenuto == 127))
+ voices[voice].state = AWE_ST_SUSTAINED;
+ else {
+ awe_note_off(voice);
+ awe_fx_init(voices[voice].ch);
+ awe_voice_init(voice, FALSE);
+ }
+}
+
+/* release all notes */
+static void awe_note_off_all(int do_sustain)
+{
+ int i;
+ for (i = 0; i < awe_max_voices; i++)
+ release_voice(i, do_sustain);
+}
+
+/* kill a voice:
+ * not terminate, just release the voice.
+ */
+static int
+awe_kill_note(int dev, int voice, int note, int velocity)
+{
+ int i, v2, key;
+
+ DEBUG(2,printk("AWE32: [off(%d) nt=%d vl=%d]\n", voice, note, velocity));
+ if (! voice_in_range(voice))
+ return -EINVAL;
+
+ switch (playing_mode) {
+ case AWE_PLAY_DIRECT:
+ case AWE_PLAY_INDIRECT:
+ key = AWE_VOICE_KEY(voice);
+ break;
+
+ case AWE_PLAY_MULTI2:
+ v2 = voice_alloc->map[voice] >> 8;
+ voice_alloc->map[voice] = 0;
+ voice = v2;
+ if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+ return -EINVAL;
+ /* continue to below */
+ default:
+ key = AWE_CHAN_KEY(voice, note);
+ break;
+ }
+
+ for (i = 0; i < awe_max_voices; i++) {
+ if (voices[i].key == key)
+ release_voice(i, TRUE);
+ }
+ return 0;
+}
+
+
+static void start_or_volume_change(int voice, int velocity)
+{
+ voices[voice].velocity = velocity;
+ awe_calc_volume(voice);
+ if (voices[voice].state == AWE_ST_STANDBY)
+ awe_note_on(voice);
+ else if (voices[voice].state == AWE_ST_ON)
+ awe_set_volume(voice, FALSE);
+}
+
+static void set_and_start_voice(int voice, int state)
+{
+ /* calculate pitch & volume parameters */
+ voices[voice].state = state;
+ awe_calc_pitch(voice);
+ awe_calc_volume(voice);
+ if (state == AWE_ST_ON)
+ awe_note_on(voice);
+}
+
+/* start a voice:
+ * if note is 255, identical with aftertouch function.
+ * Otherwise, start a voice with specified not and volume.
+ */
+static int
+awe_start_note(int dev, int voice, int note, int velocity)
+{
+ int i, key, state, volonly;
+
+ DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", voice, note, velocity));
+ if (! voice_in_range(voice))
+ return -EINVAL;
+
+ if (velocity == 0)
+ state = AWE_ST_STANDBY; /* stand by for playing */
+ else
+ state = AWE_ST_ON; /* really play */
+ volonly = FALSE;
+
+ switch (playing_mode) {
+ case AWE_PLAY_DIRECT:
+ case AWE_PLAY_INDIRECT:
+ key = AWE_VOICE_KEY(voice);
+ if (note == 255)
+ volonly = TRUE;
+ break;
+
+ case AWE_PLAY_MULTI2:
+ voice = voice_alloc->map[voice] >> 8;
+ if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+ return -EINVAL;
+ /* continue to below */
+ default:
+ if (note >= 128) { /* key volume mode */
+ note -= 128;
+ volonly = TRUE;
+ }
+ key = AWE_CHAN_KEY(voice, note);
+ break;
+ }
+
+ /* dynamic volume change */
+ if (volonly) {
+ for (i = 0; i < awe_max_voices; i++) {
+ if (voices[i].key == key)
+ start_or_volume_change(i, velocity);
+ }
+ return 0;
+ }
+
+ /* if the same note still playing, stop it */
+ if (playing_mode != AWE_PLAY_DIRECT || ctrls[AWE_MD_EXCLUSIVE_SOUND]) {
+ for (i = 0; i < awe_max_voices; i++)
+ if (voices[i].key == key) {
+ if (voices[i].state == AWE_ST_ON) {
+ awe_note_off(i);
+ awe_voice_init(i, FALSE);
+ } else if (voices[i].state == AWE_ST_STANDBY)
+ awe_voice_init(i, TRUE);
+ }
+ }
+
+ /* allocate voices */
+ if (playing_mode == AWE_PLAY_DIRECT)
+ awe_alloc_one_voice(voice, note, velocity);
+ else
+ awe_alloc_multi_voices(voice, note, velocity, key);
+
+ /* turn off other voices exlusively (for drums) */
+ for (i = 0; i < awe_max_voices; i++)
+ if (voices[i].key == key)
+ awe_exclusive_off(i);
+
+ /* set up pitch and volume parameters */
+ for (i = 0; i < awe_max_voices; i++) {
+ if (voices[i].key == key && voices[i].state == AWE_ST_OFF)
+ set_and_start_voice(i, state);
+ }
+
+ return 0;
+}
+
+
+/* search instrument from preset table with the specified bank */
+static int
+awe_search_instr(int bank, int preset)
+{
+ int i;
+
+ limitvalue(preset, 0, AWE_MAX_PRESETS-1);
+ for (i = preset_table[preset]; i >= 0; i = infos[i].next_bank) {
+ if (infos[i].bank == bank)
+ return i;
+ }
+ return -1;
+}
+
+
+/* assign the instrument to a voice */
+static int
+awe_set_instr_2(int dev, int voice, int instr_no)
+{
+ if (playing_mode == AWE_PLAY_MULTI2) {
+ voice = voice_alloc->map[voice] >> 8;
+ if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+ return -EINVAL;
+ }
+ return awe_set_instr(dev, voice, instr_no);
+}
+
+/* assign the instrument to a channel; voice is the channel number */
+static int
+awe_set_instr(int dev, int voice, int instr_no)
+{
+ awe_chan_info *cinfo;
+ int def_bank;
+
+ if (! voice_in_range(voice))
+ return -EINVAL;
+
+ if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS)
+ return -EINVAL;
+
+ cinfo = &channels[voice];
+
+ if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice))
+ def_bank = AWE_DRUM_BANK; /* always search drumset */
+ else
+ def_bank = cinfo->bank;
+
+ cinfo->vrec = -1;
+ cinfo->def_vrec = -1;
+ cinfo->vrec = awe_search_instr(def_bank, instr_no);
+ if (def_bank == AWE_DRUM_BANK) /* search default drumset */
+ cinfo->def_vrec = awe_search_instr(def_bank, ctrls[AWE_MD_DEF_DRUM]);
+ else /* search default preset */
+ cinfo->def_vrec = awe_search_instr(ctrls[AWE_MD_DEF_BANK], instr_no);
+
+ if (cinfo->vrec < 0 && cinfo->def_vrec < 0) {
+ DEBUG(1,printk("AWE32 Warning: can't find instrument %d\n", instr_no));
+ }
+
+ cinfo->instr = instr_no;
+ DEBUG(2,printk("AWE32: [program(%d) %d/%d]\n", voice, instr_no, def_bank));
+
+ return 0;
+}
+
+
+/* reset all voices; terminate sounds and initialize parameters */
+static void
+awe_reset(int dev)
+{
+ int i;
+ current_alloc_time = 0;
+ /* don't turn off voice 31 and 32. they are used also for FM voices */
+ for (i = 0; i < awe_max_voices; i++) {
+ awe_terminate(i);
+ awe_voice_init(i, TRUE);
+ }
+ for (i = 0; i < AWE_MAX_CHANNELS; i++)
+ awe_channel_init(i, TRUE);
+ for (i = 0; i < 16; i++) {
+ awe_operations.chn_info[i].controllers[CTL_MAIN_VOLUME] = 127;
+ awe_operations.chn_info[i].controllers[CTL_EXPRESSION] = 127;
+ }
+ awe_init_fm();
+ awe_tweak();
+}
+
+
+/* hardware specific control:
+ * GUS specific and AWE32 specific controls are available.
+ */
+static void
+awe_hw_control(int dev, unsigned char *event)
+{
+ int cmd = event[2];
+ if (cmd & _AWE_MODE_FLAG)
+ awe_hw_awe_control(dev, cmd & _AWE_MODE_VALUE_MASK, event);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+ else
+ awe_hw_gus_control(dev, cmd & _AWE_MODE_VALUE_MASK, event);
+#endif
+}
+
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
+/* GUS compatible controls */
+static void
+awe_hw_gus_control(int dev, int cmd, unsigned char *event)
+{
+ int voice, i, key;
+ unsigned short p1;
+ short p2;
+ int plong;
+
+ if (MULTI_LAYER_MODE())
+ return;
+ if (cmd == _GUS_NUMVOICES)
+ return;
+
+ voice = event[3];
+ if (! voice_in_range(voice))
+ return;
+
+ p1 = *(unsigned short *) &event[4];
+ p2 = *(short *) &event[6];
+ plong = *(int*) &event[4];
+
+ switch (cmd) {
+ case _GUS_VOICESAMPLE:
+ awe_set_instr(dev, voice, p1);
+ return;
+
+ case _GUS_VOICEBALA:
+ /* 0 to 15 --> -128 to 127 */
+ awe_panning(dev, voice, ((int)p1 << 4) - 128);
+ return;
+
+ case _GUS_VOICEVOL:
+ case _GUS_VOICEVOL2:
+ /* not supported yet */
+ return;
+
+ case _GUS_RAMPRANGE:
+ case _GUS_RAMPRATE:
+ case _GUS_RAMPMODE:
+ case _GUS_RAMPON:
+ case _GUS_RAMPOFF:
+ /* volume ramping not supported */
+ return;
+
+ case _GUS_VOLUME_SCALE:
+ return;
+
+ case _GUS_VOICE_POS:
+ FX_SET(&channels[voice].fx, AWE_FX_SAMPLE_START,
+ (short)(plong & 0x7fff));
+ FX_SET(&channels[voice].fx, AWE_FX_COARSE_SAMPLE_START,
+ (plong >> 15) & 0xffff);
+ return;
+ }
+
+ key = AWE_VOICE_KEY(voice);
+ for (i = 0; i < awe_max_voices; i++) {
+ if (voices[i].key == key) {
+ switch (cmd) {
+ case _GUS_VOICEON:
+ awe_note_on(i);
+ break;
+
+ case _GUS_VOICEOFF:
+ awe_terminate(i);
+ awe_fx_init(voices[i].ch);
+ awe_voice_init(i, TRUE);
+ break;
+
+ case _GUS_VOICEFADE:
+ awe_note_off(i);
+ awe_fx_init(voices[i].ch);
+ awe_voice_init(i, FALSE);
+ break;
+
+ case _GUS_VOICEFREQ:
+ awe_calc_pitch_from_freq(i, plong);
+ break;
+ }
+ }
+ }
+}
+
+#endif /* gus_compat */
+
+
+/* AWE32 specific controls */
+static void
+awe_hw_awe_control(int dev, int cmd, unsigned char *event)
+{
+ int voice;
+ unsigned short p1;
+ short p2;
+ int i;
+
+ voice = event[3];
+ if (! voice_in_range(voice))
+ return;
+
+ if (playing_mode == AWE_PLAY_MULTI2) {
+ voice = voice_alloc->map[voice] >> 8;
+ if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+ return;
+ }
+
+ p1 = *(unsigned short *) &event[4];
+ p2 = *(short *) &event[6];
+
+ switch (cmd) {
+ case _AWE_DEBUG_MODE:
+ ctrls[AWE_MD_DEBUG_MODE] = p1;
+ printk("AWE32: debug mode = %d\n", ctrls[AWE_MD_DEBUG_MODE]);
+ break;
+ case _AWE_REVERB_MODE:
+ ctrls[AWE_MD_REVERB_MODE] = p1;
+ awe_update_reverb_mode();
+ break;
+
+ case _AWE_CHORUS_MODE:
+ ctrls[AWE_MD_CHORUS_MODE] = p1;
+ awe_update_chorus_mode();
+ break;
+
+ case _AWE_REMOVE_LAST_SAMPLES:
+ DEBUG(0,printk("AWE32: remove last samples\n"));
+ if (locked_sf_id > 0)
+ awe_remove_samples(locked_sf_id);
+ break;
+
+ case _AWE_INITIALIZE_CHIP:
+ awe_initialize();
+ break;
+
+ case _AWE_SEND_EFFECT:
+ i = -1;
+ if (p1 >= 0x100) {
+ i = (p1 >> 8);
+ if (i < 0 || i >= MAX_LAYERS)
+ break;
+ }
+ awe_send_effect(voice, i, p1, p2);
+ break;
+
+ case _AWE_RESET_CHANNEL:
+ awe_channel_init(voice, !p1);
+ break;
+
+ case _AWE_TERMINATE_ALL:
+ awe_reset(0);
+ break;
+
+ case _AWE_TERMINATE_CHANNEL:
+ awe_voice_change(voice, awe_terminate_and_init);
+ break;
+
+ case _AWE_RELEASE_ALL:
+ awe_note_off_all(FALSE);
+ break;
+ case _AWE_NOTEOFF_ALL:
+ awe_note_off_all(TRUE);
+ break;
+
+ case _AWE_INITIAL_VOLUME:
+ DEBUG(0,printk("AWE32: init attenuation %d\n", p1));
+ atten_relative = (char)p2;
+ atten_offset = (short)p1;
+ awe_update_volume();
+ break;
+
+ case _AWE_CHN_PRESSURE:
+ channels[voice].chan_press = p1;
+ awe_modwheel_change(voice, p1);
+ break;
+
+ case _AWE_CHANNEL_MODE:
+ DEBUG(0,printk("AWE32: channel mode = %d\n", p1));
+ playing_mode = p1;
+ awe_reset(0);
+ break;
+
+ case _AWE_DRUM_CHANNELS:
+ DEBUG(0,printk("AWE32: drum flags = %x\n", p1));
+ drum_flags = *(unsigned int*)&event[4];
+ break;
+
+ case _AWE_MISC_MODE:
+ DEBUG(0,printk("AWE32: ctrl parms = %d %d\n", p1, p2));
+ if (p1 > AWE_MD_VERSION && p1 < AWE_MD_END) {
+ ctrls[p1] = p2;
+ if (ctrl_parms[p1].update)
+ ctrl_parms[p1].update();
+ }
+ break;
+
+ case _AWE_EQUALIZER:
+ ctrls[AWE_MD_BASS_LEVEL] = p1;
+ ctrls[AWE_MD_TREBLE_LEVEL] = p2;
+ awe_update_equalizer();
+ break;
+
+ default:
+ DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice));
+ break;
+ }
+}
+
+
+/* change effects */
+static void
+awe_send_effect(int voice, int layer, int type, int val)
+{
+ awe_chan_info *cinfo;
+ FX_Rec *fx;
+ int mode;
+
+ cinfo = &channels[voice];
+ if (layer >= 0 && layer < MAX_LAYERS)
+ fx = &cinfo->fx_layer[layer];
+ else
+ fx = &cinfo->fx;
+
+ if (type & 0x40)
+ mode = FX_FLAG_OFF;
+ else if (type & 0x80)
+ mode = FX_FLAG_ADD;
+ else
+ mode = FX_FLAG_SET;
+ type &= 0x3f;
+
+ if (type >= 0 && type < AWE_FX_END) {
+ DEBUG(2,printk("AWE32: effects (%d) %d %d\n", voice, type, val));
+ if (mode == FX_FLAG_SET)
+ FX_SET(fx, type, val);
+ else if (mode == FX_FLAG_ADD)
+ FX_ADD(fx, type, val);
+ else
+ FX_UNSET(fx, type);
+ if (mode != FX_FLAG_OFF && parm_defs[type].realtime) {
+ DEBUG(2,printk("AWE32: fx_realtime (%d)\n", voice));
+ awe_voice_change(voice, parm_defs[type].realtime);
+ }
+ }
+}
+
+
+/* change modulation wheel; voice is already mapped on multi2 mode */
+static void
+awe_modwheel_change(int voice, int value)
+{
+ int i;
+ awe_chan_info *cinfo;
+
+ cinfo = &channels[voice];
+ i = value * ctrls[AWE_MD_MOD_SENSE] / 1200;
+ FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, i);
+ awe_voice_change(voice, awe_fx_fmmod);
+ FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, i);
+ awe_voice_change(voice, awe_fx_fm2frq2);
+}
+
+
+/* voice pressure change */
+static void
+awe_aftertouch(int dev, int voice, int pressure)
+{
+ int note;
+
+ DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure));
+ if (! voice_in_range(voice))
+ return;
+
+ switch (playing_mode) {
+ case AWE_PLAY_DIRECT:
+ case AWE_PLAY_INDIRECT:
+ awe_start_note(dev, voice, 255, pressure);
+ break;
+ case AWE_PLAY_MULTI2:
+ note = (voice_alloc->map[voice] & 0xff) - 1;
+ awe_key_pressure(dev, voice, note + 0x80, pressure);
+ break;
+ }
+}
+
+
+/* voice control change */
+static void
+awe_controller(int dev, int voice, int ctrl_num, int value)
+{
+ awe_chan_info *cinfo;
+
+ if (! voice_in_range(voice))
+ return;
+
+ if (playing_mode == AWE_PLAY_MULTI2) {
+ voice = voice_alloc->map[voice] >> 8;
+ if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+ return;
+ }
+
+ cinfo = &channels[voice];
+
+ switch (ctrl_num) {
+ case CTL_BANK_SELECT: /* MIDI control #0 */
+ DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value));
+ if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice) &&
+ !ctrls[AWE_MD_TOGGLE_DRUM_BANK])
+ break;
+ cinfo->bank = value;
+ if (cinfo->bank == AWE_DRUM_BANK)
+ DRUM_CHANNEL_ON(cinfo->channel);
+ else
+ DRUM_CHANNEL_OFF(cinfo->channel);
+ awe_set_instr(dev, voice, cinfo->instr);
+ break;
+
+ case CTL_MODWHEEL: /* MIDI control #1 */
+ DEBUG(2,printk("AWE32: [modwheel(%d) %d]\n", voice, value));
+ awe_modwheel_change(voice, value);
+ break;
+
+ case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */
+ DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value));
+ /* zero centered */
+ cinfo->bender = value;
+ awe_voice_change(voice, awe_set_voice_pitch);
+ break;
+
+ case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */
+ DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value));
+ /* value = sense x 100 */
+ cinfo->bender_range = value;
+ /* no audible pitch change yet.. */
+ break;
+
+ case CTL_EXPRESSION: /* MIDI control #11 */
+ if (SINGLE_LAYER_MODE())
+ value /= 128;
+ case CTRL_EXPRESSION: /* SEQ1 V2 control */
+ DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value));
+ /* 0 - 127 */
+ cinfo->expression_vol = value;
+ awe_voice_change(voice, awe_set_voice_vol);
+ break;
+
+ case CTL_PAN: /* MIDI control #10 */
+ DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value));
+ /* (0-127) -> signed 8bit */
+ cinfo->panning = value * 2 - 128;
+ if (ctrls[AWE_MD_REALTIME_PAN])
+ awe_voice_change(voice, awe_set_pan);
+ break;
+
+ case CTL_MAIN_VOLUME: /* MIDI control #7 */
+ if (SINGLE_LAYER_MODE())
+ value = (value * 100) / 16383;
+ case CTRL_MAIN_VOLUME: /* SEQ1 V2 control */
+ DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value));
+ /* 0 - 127 */
+ cinfo->main_vol = value;
+ awe_voice_change(voice, awe_set_voice_vol);
+ break;
+
+ case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */
+ DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value));
+ FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2);
+ break;
+
+ case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */
+ DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value));
+ FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2);
+ break;
+
+ case 120: /* all sounds off */
+ awe_note_off_all(FALSE);
+ break;
+ case 123: /* all notes off */
+ awe_note_off_all(TRUE);
+ break;
+
+ case CTL_SUSTAIN: /* MIDI control #64 */
+ cinfo->sustained = value;
+ if (value != 127)
+ awe_voice_change(voice, awe_sustain_off);
+ break;
+
+ case CTL_SOSTENUTO: /* MIDI control #66 */
+ if (value == 127)
+ awe_voice_change(voice, awe_sostenuto_on);
+ else
+ awe_voice_change(voice, awe_sustain_off);
+ break;
+
+ default:
+ DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n",
+ voice, ctrl_num, value));
+ break;
+ }
+}
+
+
+/* voice pan change (value = -128 - 127) */
+static void
+awe_panning(int dev, int voice, int value)
+{
+ awe_chan_info *cinfo;
+
+ if (! voice_in_range(voice))
+ return;
+
+ if (playing_mode == AWE_PLAY_MULTI2) {
+ voice = voice_alloc->map[voice] >> 8;
+ if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+ return;
+ }
+
+ cinfo = &channels[voice];
+ cinfo->panning = value;
+ DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning));
+ if (ctrls[AWE_MD_REALTIME_PAN])
+ awe_voice_change(voice, awe_set_pan);
+}
+
+
+/* volume mode change */
+static void
+awe_volume_method(int dev, int mode)
+{
+ /* not impremented */
+ DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode));
+}
+
+
+/* pitch wheel change: 0-16384 */
+static void
+awe_bender(int dev, int voice, int value)
+{
+ awe_chan_info *cinfo;
+
+ if (! voice_in_range(voice))
+ return;
+
+ if (playing_mode == AWE_PLAY_MULTI2) {
+ voice = voice_alloc->map[voice] >> 8;
+ if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+ return;
+ }
+
+ /* convert to zero centered value */
+ cinfo = &channels[voice];
+ cinfo->bender = value - 8192;
+ DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender));
+ awe_voice_change(voice, awe_set_voice_pitch);
+}
+
+
+/*----------------------------------------------------------------
+ * load a sound patch:
+ * three types of patches are accepted: AWE, GUS, and SYSEX.
+ *----------------------------------------------------------------*/
+
+static int
+awe_load_patch(int dev, int format, const char *addr,
+ int offs, int count, int pmgr_flag)
+{
+ awe_patch_info patch;
+ int rc = 0;
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+ if (format == GUS_PATCH) {
+ return awe_load_guspatch(addr, offs, count, pmgr_flag);
+ } else
+#endif
+ if (format == SYSEX_PATCH) {
+ /* no system exclusive message supported yet */
+ return 0;
+ } else if (format != AWE_PATCH) {
+ printk("AWE32 Error: Invalid patch format (key) 0x%x\n", format);
+ return -EINVAL;
+ }
+
+ if (count < AWE_PATCH_INFO_SIZE) {
+ printk("AWE32 Error: Patch header too short\n");
+ return -EINVAL;
+ }
+ copy_from_user(((char*)&patch) + offs, addr + offs,
+ AWE_PATCH_INFO_SIZE - offs);
+
+ count -= AWE_PATCH_INFO_SIZE;
+ if (count < patch.len) {
+ printk("AWE32: sample: Patch record too short (%d<%d)\n",
+ count, patch.len);
+ return -EINVAL;
+ }
+
+ switch (patch.type) {
+ case AWE_LOAD_INFO:
+ rc = awe_load_info(&patch, addr, count);
+ break;
+ case AWE_LOAD_DATA:
+ rc = awe_load_data(&patch, addr, count);
+ break;
+ case AWE_OPEN_PATCH:
+ rc = awe_open_patch(&patch, addr, count);
+ break;
+ case AWE_CLOSE_PATCH:
+ rc = awe_close_patch(&patch, addr, count);
+ break;
+ case AWE_UNLOAD_PATCH:
+ rc = awe_unload_patch(&patch, addr, count);
+ break;
+ case AWE_REPLACE_DATA:
+ rc = awe_replace_data(&patch, addr, count);
+ break;
+ case AWE_MAP_PRESET:
+ rc = awe_load_map(&patch, addr, count);
+ break;
+ /* case AWE_PROBE_INFO:
+ rc = awe_probe_info(&patch, addr, count);
+ break;*/
+ case AWE_PROBE_DATA:
+ rc = awe_probe_data(&patch, addr, count);
+ break;
+ case AWE_LOAD_CHORUS_FX:
+ rc = awe_load_chorus_fx(&patch, addr, count);
+ break;
+ case AWE_LOAD_REVERB_FX:
+ rc = awe_load_reverb_fx(&patch, addr, count);
+ break;
+
+ default:
+ printk("AWE32 Error: unknown patch format type %d\n",
+ patch.type);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+
+/* create an sflist record */
+static int
+awe_create_sf(int type, char *name)
+{
+ sf_list *rec;
+
+ /* terminate sounds */
+ awe_reset(0);
+ if (current_sf_id >= max_sfs) {
+ int newsize = max_sfs + AWE_MAX_SF_LISTS;
+ sf_list *newlist = realloc_block(sflists, sizeof(sf_list)*max_sfs,
+ sizeof(sf_list)*newsize);
+ if (newlist == NULL)
+ return 1;
+ sflists = newlist;
+ max_sfs = newsize;
+ }
+ rec = &sflists[current_sf_id];
+ rec->sf_id = current_sf_id + 1;
+ rec->type = type;
+ if (current_sf_id == 0 || (type & AWE_PAT_LOCKED) != 0)
+ locked_sf_id = current_sf_id + 1;
+ rec->num_info = awe_free_info();
+ rec->num_sample = awe_free_sample();
+ rec->mem_ptr = awe_free_mem_ptr();
+ rec->infos = -1;
+ rec->samples = -1;
+
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+ rec->shared = 0;
+ if (name)
+ memcpy(rec->name, name, AWE_PATCH_NAME_LEN);
+ else
+ strcpy(rec->name, "*TEMPORARY*");
+ if (current_sf_id > 0 && name && (type & AWE_PAT_SHARED) != 0) {
+ /* is the current font really a shared font? */
+ if (is_shared_sf(rec->name)) {
+ /* check if the shared font is already installed */
+ int i;
+ for (i = current_sf_id; i > 0; i--) {
+ if (is_identical_name(rec->name, i)) {
+ rec->shared = i;
+ break;
+ }
+ }
+ }
+ }
+#endif /* allow sharing */
+
+ current_sf_id++;
+
+ return 0;
+}
+
+
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+
+/* check if the given name is a valid shared name */
+#define ASC_TO_KEY(c) ((c) - 'A' + 1)
+static int is_shared_sf(unsigned char *name)
+{
+ static unsigned char id_head[6] = {
+ ASC_TO_KEY('A'), ASC_TO_KEY('W'), ASC_TO_KEY('E'),
+ AWE_MAJOR_VERSION,
+ AWE_MINOR_VERSION,
+ AWE_TINY_VERSION,
+ };
+ if (memcmp(name, id_head, 6) == 0)
+ return TRUE;
+ return FALSE;
+}
+
+/* check if the given name matches to the existing list */
+static int is_identical_name(unsigned char *name, int sf)
+{
+ char *id = sflists[sf-1].name;
+ if (is_shared_sf(id) && memcmp(id, name, AWE_PATCH_NAME_LEN) == 0)
+ return TRUE;
+ return FALSE;
+}
+
+/* check if the given voice info exists */
+static int info_duplicated(awe_voice_list *rec)
+{
+ int j, sf_id;
+ sf_list *sf;
+
+ /* search for all sharing lists */
+ for (sf_id = rec->v.sf_id; sf_id > 0 && sf_id <= current_sf_id; sf_id = sf->shared) {
+ sf = &sflists[sf_id - 1];
+ for (j = sf->infos; j >= 0; j = infos[j].next) {
+ awe_voice_list *p = &infos[j];
+ if (p->type == V_ST_NORMAL &&
+ p->bank == rec->bank &&
+ p->instr == rec->instr &&
+ p->v.low == rec->v.low &&
+ p->v.high == rec->v.high &&
+ p->v.sample == rec->v.sample)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+#endif /* AWE_ALLOW_SAMPLE_SHARING */
+
+
+/* open patch; create sf list and set opened flag */
+static int
+awe_open_patch(awe_patch_info *patch, const char *addr, int count)
+{
+ awe_open_parm parm;
+ int shared;
+
+ copy_from_user(&parm, addr + AWE_PATCH_INFO_SIZE, sizeof(parm));
+ shared = FALSE;
+
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+ if (current_sf_id > 0 && (parm.type & AWE_PAT_SHARED) != 0) {
+ /* is the previous font the same font? */
+ if (is_identical_name(parm.name, current_sf_id)) {
+ /* then append to the previous */
+ shared = TRUE;
+ awe_reset(0);
+ if (parm.type & AWE_PAT_LOCKED)
+ locked_sf_id = current_sf_id;
+ }
+ }
+#endif /* allow sharing */
+ if (! shared) {
+ if (awe_create_sf(parm.type, parm.name)) {
+ printk("AWE32: can't open: failed to alloc new list\n");
+ return -ENOSPC;
+ }
+ }
+ patch_opened = TRUE;
+ return current_sf_id;
+}
+
+/* check if the patch is already opened */
+static int
+check_patch_opened(int type, char *name)
+{
+ if (! patch_opened) {
+ if (awe_create_sf(type, name)) {
+ printk("AWE32: failed to alloc new list\n");
+ return -ENOSPC;
+ }
+ patch_opened = TRUE;
+ return current_sf_id;
+ }
+ return current_sf_id;
+}
+
+/* close the patch; if no voice is loaded, remove the patch */
+static int
+awe_close_patch(awe_patch_info *patch, const char *addr, int count)
+{
+ if (patch_opened && current_sf_id > 0) {
+ /* if no voice is loaded, release the current patch */
+ if (sflists[current_sf_id-1].infos == -1)
+ awe_remove_samples(current_sf_id - 1);
+ }
+ patch_opened = 0;
+ return 0;
+}
+
+
+/* remove the latest patch */
+static int
+awe_unload_patch(awe_patch_info *patch, const char *addr, int count)
+{
+ if (current_sf_id > 0 && current_sf_id > locked_sf_id)
+ awe_remove_samples(current_sf_id - 1);
+ return 0;
+}
+
+/* allocate voice info list records */
+static int alloc_new_info(int nvoices)
+{
+ int newsize, free_info;
+ awe_voice_list *newlist;
+ free_info = awe_free_info();
+ if (free_info + nvoices >= max_infos) {
+ do {
+ newsize = max_infos + AWE_MAX_INFOS;
+ } while (free_info + nvoices >= newsize);
+ newlist = realloc_block(infos, sizeof(awe_voice_list)*max_infos,
+ sizeof(awe_voice_list)*newsize);
+ if (newlist == NULL) {
+ printk("AWE32: can't alloc info table\n");
+ return -ENOSPC;
+ }
+ infos = newlist;
+ max_infos = newsize;
+ }
+ return 0;
+}
+
+/* allocate sample info list records */
+static int alloc_new_sample(void)
+{
+ int newsize, free_sample;
+ awe_sample_list *newlist;
+ free_sample = awe_free_sample();
+ if (free_sample >= max_samples) {
+ newsize = max_samples + AWE_MAX_SAMPLES;
+ newlist = realloc_block(samples,
+ sizeof(awe_sample_list)*max_samples,
+ sizeof(awe_sample_list)*newsize);
+ if (newlist == NULL) {
+ printk("AWE32: can't alloc sample table\n");
+ return -ENOSPC;
+ }
+ samples = newlist;
+ max_samples = newsize;
+ }
+ return 0;
+}
+
+/* load voice map */
+static int
+awe_load_map(awe_patch_info *patch, const char *addr, int count)
+{
+ awe_voice_map map;
+ awe_voice_list *rec;
+ int p, free_info;
+
+ /* get the link info */
+ if (count < sizeof(map)) {
+ printk("AWE32 Error: invalid patch info length\n");
+ return -EINVAL;
+ }
+ copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map));
+
+ /* check if the identical mapping already exists */
+ p = awe_search_instr(map.map_bank, map.map_instr);
+ for (; p >= 0; p = infos[p].next_instr) {
+ if (p >= 0 && infos[p].type == V_ST_MAPPED &&
+ infos[p].v.low == map.map_key &&
+ infos[p].v.start == map.src_instr &&
+ infos[p].v.end == map.src_bank &&
+ infos[p].v.fixkey == map.src_key)
+ return 0; /* already present! */
+ }
+
+ if (check_patch_opened(AWE_PAT_TYPE_MAP, NULL) < 0)
+ return -ENOSPC;
+
+ if (alloc_new_info(1) < 0)
+ return -ENOSPC;
+
+ free_info = awe_free_info();
+ rec = &infos[free_info];
+ rec->bank = map.map_bank;
+ rec->instr = map.map_instr;
+ rec->type = V_ST_MAPPED;
+ rec->disabled = FALSE;
+ awe_init_voice_info(&rec->v);
+ if (map.map_key >= 0) {
+ rec->v.low = map.map_key;
+ rec->v.high = map.map_key;
+ }
+ rec->v.start = map.src_instr;
+ rec->v.end = map.src_bank;
+ rec->v.fixkey = map.src_key;
+ rec->v.sf_id = current_sf_id;
+ add_info_list(free_info);
+ add_sf_info(free_info);
+
+ return 0;
+}
+
+#if 0
+/* probe preset in the current list -- nothing to be loaded */
+static int
+awe_probe_info(awe_patch_info *patch, const char *addr, int count)
+{
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+ awe_voice_map map;
+ int p;
+
+ if (! patch_opened)
+ return -EINVAL;
+
+ /* get the link info */
+ if (count < sizeof(map)) {
+ printk("AWE32 Error: invalid patch info length\n");
+ return -EINVAL;
+ }
+ copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map));
+
+ /* check if the identical mapping already exists */
+ p = awe_search_instr(map.src_bank, map.src_instr);
+ for (; p >= 0; p = infos[p].next_instr) {
+ if (p >= 0 && infos[p].type == V_ST_NORMAL &&
+ is_identical_id(infos[p].v.sf_id, current_sf_id) &&
+ infos[p].v.low <= map.src_key &&
+ infos[p].v.high >= map.src_key)
+ return 0; /* already present! */
+ }
+#endif /* allow sharing */
+ return -EINVAL;
+}
+#endif
+
+/* probe sample in the current list -- nothing to be loaded */
+static int
+awe_probe_data(awe_patch_info *patch, const char *addr, int count)
+{
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+ if (! patch_opened)
+ return -EINVAL;
+
+ /* search the specified sample by optarg */
+ if (search_sample_index(current_sf_id, patch->optarg, 0) >= 0)
+ return 0;
+#endif /* allow sharing */
+ return -EINVAL;
+}
+
+/* load voice information data */
+static int
+awe_load_info(awe_patch_info *patch, const char *addr, int count)
+{
+ int offset;
+ awe_voice_rec_hdr hdr;
+ int i;
+ int total_size;
+
+ if (count < AWE_VOICE_REC_SIZE) {
+ printk("AWE32 Error: invalid patch info length\n");
+ return -EINVAL;
+ }
+
+ offset = AWE_PATCH_INFO_SIZE;
+ copy_from_user((char*)&hdr, addr + offset, AWE_VOICE_REC_SIZE);
+ offset += AWE_VOICE_REC_SIZE;
+
+ if (hdr.nvoices <= 0 || hdr.nvoices >= 100) {
+ printk("AWE32 Error: Illegal voice number %d\n", hdr.nvoices);
+ return -EINVAL;
+ }
+ total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * hdr.nvoices;
+ if (count < total_size) {
+ printk("AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n",
+ count, hdr.nvoices);
+ return -EINVAL;
+ }
+
+ if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0)
+ return -ENOSPC;
+
+#if 0 /* it looks like not so useful.. */
+ /* check if the same preset already exists in the info list */
+ for (i = sflists[current_sf_id-1].infos; i >= 0; i = infos[i].next) {
+ if (infos[i].disabled) continue;
+ if (infos[i].bank == hdr.bank && infos[i].instr == hdr.instr) {
+ /* in exclusive mode, do skip loading this */
+ if (hdr.write_mode == AWE_WR_EXCLUSIVE)
+ return 0;
+ /* in replace mode, disable the old data */
+ else if (hdr.write_mode == AWE_WR_REPLACE)
+ infos[i].disabled = TRUE;
+ }
+ }
+ if (hdr.write_mode == AWE_WR_REPLACE)
+ rebuild_preset_list();
+#endif
+
+ if (alloc_new_info(hdr.nvoices) < 0)
+ return -ENOSPC;
+
+ for (i = 0; i < hdr.nvoices; i++) {
+ int rec = awe_free_info();
+
+ infos[rec].bank = hdr.bank;
+ infos[rec].instr = hdr.instr;
+ infos[rec].type = V_ST_NORMAL;
+ infos[rec].disabled = FALSE;
+
+ /* copy awe_voice_info parameters */
+ copy_from_user(&infos[rec].v, addr + offset, AWE_VOICE_INFO_SIZE);
+ offset += AWE_VOICE_INFO_SIZE;
+ infos[rec].v.sf_id = current_sf_id;
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+ if (sflists[current_sf_id-1].shared) {
+ if (info_duplicated(&infos[rec]))
+ continue;
+ }
+#endif /* allow sharing */
+ if (infos[rec].v.mode & AWE_MODE_INIT_PARM)
+ awe_init_voice_parm(&infos[rec].v.parm);
+ awe_set_sample(&infos[rec].v);
+ add_info_list(rec);
+ add_sf_info(rec);
+ }
+
+ return 0;
+}
+
+
+/* load wave sample data */
+static int
+awe_load_data(awe_patch_info *patch, const char *addr, int count)
+{
+ int offset, size;
+ int rc, free_sample;
+ awe_sample_info tmprec, *rec;
+
+ if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0)
+ return -ENOSPC;
+
+ size = (count - AWE_SAMPLE_INFO_SIZE) / 2;
+ offset = AWE_PATCH_INFO_SIZE;
+ copy_from_user(&tmprec, addr + offset, AWE_SAMPLE_INFO_SIZE);
+ offset += AWE_SAMPLE_INFO_SIZE;
+ if (size != tmprec.size) {
+ printk("AWE32: load: sample size differed (%d != %d)\n",
+ tmprec.size, size);
+ return -EINVAL;
+ }
+
+ if (search_sample_index(current_sf_id, tmprec.sample, 0) >= 0) {
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+ /* if shared sample, skip this data */
+ if (sflists[current_sf_id-1].type & AWE_PAT_SHARED)
+ return 0;
+#endif /* allow sharing */
+ DEBUG(1,printk("AWE32: sample data %d already present\n", tmprec.sample));
+ return -EINVAL;
+ }
+
+ if (alloc_new_sample() < 0)
+ return -ENOSPC;
+
+ free_sample = awe_free_sample();
+ rec = &samples[free_sample].v;
+ *rec = tmprec;
+
+ if (rec->size > 0)
+ if ((rc = awe_write_wave_data(addr, offset, rec, -1)) != 0)
+ return rc;
+
+ rec->sf_id = current_sf_id;
+
+ add_sf_sample(free_sample);
+
+ return 0;
+}
+
+
+/* replace wave sample data */
+static int
+awe_replace_data(awe_patch_info *patch, const char *addr, int count)
+{
+ int offset;
+ int size;
+ int rc, i;
+ int channels;
+ awe_sample_info cursmp;
+ int save_mem_ptr;
+
+ if (! patch_opened) {
+ printk("AWE32: replace: patch not opened\n");
+ return -EINVAL;
+ }
+
+ size = (count - AWE_SAMPLE_INFO_SIZE) / 2;
+ offset = AWE_PATCH_INFO_SIZE;
+ copy_from_user(&cursmp, addr + offset, AWE_SAMPLE_INFO_SIZE);
+ offset += AWE_SAMPLE_INFO_SIZE;
+ if (cursmp.size == 0 || size != cursmp.size) {
+ printk("AWE32: replace: illegal sample size (%d!=%d)\n",
+ cursmp.size, size);
+ return -EINVAL;
+ }
+ channels = patch->optarg;
+ if (channels <= 0 || channels > AWE_NORMAL_VOICES) {
+ printk("AWE32: replace: illegal channels %d\n", channels);
+ return -EINVAL;
+ }
+
+ for (i = sflists[current_sf_id-1].samples;
+ i >= 0; i = samples[i].next) {
+ if (samples[i].v.sample == cursmp.sample)
+ break;
+ }
+ if (i < 0) {
+ printk("AWE32: replace: cannot find existing sample data %d\n",
+ cursmp.sample);
+ return -EINVAL;
+ }
+
+ if (samples[i].v.size != cursmp.size) {
+ printk("AWE32: replace: exiting size differed (%d!=%d)\n",
+ samples[i].v.size, cursmp.size);
+ return -EINVAL;
+ }
+
+ save_mem_ptr = awe_free_mem_ptr();
+ sflists[current_sf_id-1].mem_ptr = samples[i].v.start - awe_mem_start;
+ memcpy(&samples[i].v, &cursmp, sizeof(cursmp));
+ if ((rc = awe_write_wave_data(addr, offset, &samples[i].v, channels)) != 0)
+ return rc;
+ sflists[current_sf_id-1].mem_ptr = save_mem_ptr;
+ samples[i].v.sf_id = current_sf_id;
+
+ return 0;
+}
+
+
+/*----------------------------------------------------------------*/
+
+static const char *readbuf_addr;
+static int readbuf_offs;
+static int readbuf_flags;
+#ifdef MALLOC_LOOP_DATA
+static unsigned short *readbuf_loop;
+static int readbuf_loopstart, readbuf_loopend;
+#endif
+
+/* initialize read buffer */
+static int
+readbuf_init(const char *addr, int offset, awe_sample_info *sp)
+{
+#ifdef MALLOC_LOOP_DATA
+ readbuf_loop = NULL;
+ readbuf_loopstart = sp->loopstart;
+ readbuf_loopend = sp->loopend;
+ if (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) {
+ int looplen = sp->loopend - sp->loopstart;
+ readbuf_loop = vmalloc(looplen * 2);
+ if (readbuf_loop == NULL) {
+ printk("AWE32: can't malloc temp buffer\n");
+ return -ENOSPC;
+ }
+ }
+#endif
+ readbuf_addr = addr;
+ readbuf_offs = offset;
+ readbuf_flags = sp->mode_flags;
+ return 0;
+}
+
+/* read directly from user buffer */
+static unsigned short
+readbuf_word(int pos)
+{
+ unsigned short c;
+ /* read from user buffer */
+ if (readbuf_flags & AWE_SAMPLE_8BITS) {
+ unsigned char cc;
+ get_user(cc, (unsigned char*)&(readbuf_addr)[readbuf_offs + pos]);
+ c = cc << 8; /* convert 8bit -> 16bit */
+ } else {
+ get_user(c, (unsigned short*)&(readbuf_addr)[readbuf_offs + pos * 2]);
+ }
+ if (readbuf_flags & AWE_SAMPLE_UNSIGNED)
+ c ^= 0x8000; /* unsigned -> signed */
+#ifdef MALLOC_LOOP_DATA
+ /* write on cache for reverse loop */
+ if (readbuf_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) {
+ if (pos >= readbuf_loopstart && pos < readbuf_loopend)
+ readbuf_loop[pos - readbuf_loopstart] = c;
+ }
+#endif
+ return c;
+}
+
+#ifdef MALLOC_LOOP_DATA
+/* read from cache */
+static unsigned short
+readbuf_word_cache(int pos)
+{
+ if (pos >= readbuf_loopstart && pos < readbuf_loopend)
+ return readbuf_loop[pos - readbuf_loopstart];
+ return 0;
+}
+
+static void
+readbuf_end(void)
+{
+ if (readbuf_loop)
+ vfree(readbuf_loop);
+ readbuf_loop = NULL;
+}
+
+#else
+
+#define readbuf_word_cache readbuf_word
+#define readbuf_end() /**/
+
+#endif
+
+/*----------------------------------------------------------------*/
+
+#define BLANK_LOOP_START 8
+#define BLANK_LOOP_END 40
+#define BLANK_LOOP_SIZE 48
+
+/* loading onto memory */
+static int
+awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels)
+{
+ int i, truesize, dram_offset;
+ int rc;
+
+ /* be sure loop points start < end */
+ if (sp->loopstart > sp->loopend) {
+ int tmp = sp->loopstart;
+ sp->loopstart = sp->loopend;
+ sp->loopend = tmp;
+ }
+
+ /* compute true data size to be loaded */
+ truesize = sp->size;
+ if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP)
+ truesize += sp->loopend - sp->loopstart;
+ if (sp->mode_flags & AWE_SAMPLE_NO_BLANK)
+ truesize += BLANK_LOOP_SIZE;
+ if (awe_free_mem_ptr() + truesize >= awe_mem_size/2) {
+ DEBUG(-1,printk("AWE32 Error: Sample memory full\n"));
+ return -ENOSPC;
+ }
+
+ /* recalculate address offset */
+ sp->end -= sp->start;
+ sp->loopstart -= sp->start;
+ sp->loopend -= sp->start;
+
+ dram_offset = awe_free_mem_ptr() + awe_mem_start;
+ sp->start = dram_offset;
+ sp->end += dram_offset;
+ sp->loopstart += dram_offset;
+ sp->loopend += dram_offset;
+
+ /* set the total size (store onto obsolete checksum value) */
+ if (sp->size == 0)
+ sp->checksum = 0;
+ else
+ sp->checksum = truesize;
+
+ if ((rc = awe_open_dram_for_write(dram_offset, channels)) != 0)
+ return rc;
+
+ if (readbuf_init(addr, offset, sp) < 0)
+ return -ENOSPC;
+
+ for (i = 0; i < sp->size; i++) {
+ unsigned short c;
+ c = readbuf_word(i);
+ awe_write_dram(c);
+ if (i == sp->loopend &&
+ (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))) {
+ int looplen = sp->loopend - sp->loopstart;
+ /* copy reverse loop */
+ int k;
+ for (k = 1; k <= looplen; k++) {
+ c = readbuf_word_cache(i - k);
+ awe_write_dram(c);
+ }
+ if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) {
+ sp->end += looplen;
+ } else {
+ sp->start += looplen;
+ sp->end += looplen;
+ }
+ }
+ }
+ readbuf_end();
+
+ /* if no blank loop is attached in the sample, add it */
+ if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) {
+ for (i = 0; i < BLANK_LOOP_SIZE; i++)
+ awe_write_dram(0);
+ if (sp->mode_flags & AWE_SAMPLE_SINGLESHOT) {
+ sp->loopstart = sp->end + BLANK_LOOP_START;
+ sp->loopend = sp->end + BLANK_LOOP_END;
+ }
+ }
+
+ sflists[current_sf_id-1].mem_ptr += truesize;
+ awe_close_dram();
+
+ /* initialize FM */
+ awe_init_fm();
+
+ return 0;
+}
+
+
+/*----------------------------------------------------------------*/
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
+/* calculate GUS envelope time:
+ * is this correct? i have no idea..
+ */
+static int
+calc_gus_envelope_time(int rate, int start, int end)
+{
+ int r, p, t;
+ r = (3 - ((rate >> 6) & 3)) * 3;
+ p = rate & 0x3f;
+ t = end - start;
+ if (t < 0) t = -t;
+ if (13 > r)
+ t = t << (13 - r);
+ else
+ t = t >> (r - 13);
+ return (t * 10) / (p * 441);
+}
+
+#define calc_gus_sustain(val) (0x7f - vol_table[(val)/2])
+#define calc_gus_attenuation(val) vol_table[(val)/2]
+
+/* load GUS patch */
+static int
+awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag)
+{
+ struct patch_info patch;
+ awe_voice_info *rec;
+ awe_sample_info *smp;
+ int sizeof_patch;
+ int note, free_sample, free_info;
+ int rc;
+
+ sizeof_patch = (int)((long)&patch.data[0] - (long)&patch); /* header size */
+ if (size < sizeof_patch) {
+ printk("AWE32 Error: Patch header too short\n");
+ return -EINVAL;
+ }
+ copy_from_user(((char*)&patch) + offs, addr + offs, sizeof_patch - offs);
+ size -= sizeof_patch;
+ if (size < patch.len) {
+ printk("AWE32 Warning: Patch record too short (%d<%d)\n",
+ size, patch.len);
+ return -EINVAL;
+ }
+ if (check_patch_opened(AWE_PAT_TYPE_GUS, NULL) < 0)
+ return -ENOSPC;
+ if (alloc_new_sample() < 0)
+ return -ENOSPC;
+ if (alloc_new_info(1))
+ return -ENOSPC;
+
+ free_sample = awe_free_sample();
+ smp = &samples[free_sample].v;
+
+ smp->sample = free_sample;
+ smp->start = 0;
+ smp->end = patch.len;
+ smp->loopstart = patch.loop_start;
+ smp->loopend = patch.loop_end;
+ smp->size = patch.len;
+
+ /* set up mode flags */
+ smp->mode_flags = 0;
+ if (!(patch.mode & WAVE_16_BITS))
+ smp->mode_flags |= AWE_SAMPLE_8BITS;
+ if (patch.mode & WAVE_UNSIGNED)
+ smp->mode_flags |= AWE_SAMPLE_UNSIGNED;
+ smp->mode_flags |= AWE_SAMPLE_NO_BLANK;
+ if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK)))
+ smp->mode_flags |= AWE_SAMPLE_SINGLESHOT;
+ if (patch.mode & WAVE_BIDIR_LOOP)
+ smp->mode_flags |= AWE_SAMPLE_BIDIR_LOOP;
+ if (patch.mode & WAVE_LOOP_BACK)
+ smp->mode_flags |= AWE_SAMPLE_REVERSE_LOOP;
+
+ DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no, smp->mode_flags));
+ if (patch.mode & WAVE_16_BITS) {
+ /* convert to word offsets */
+ smp->size /= 2;
+ smp->end /= 2;
+ smp->loopstart /= 2;
+ smp->loopend /= 2;
+ }
+ smp->checksum_flag = 0;
+ smp->checksum = 0;
+
+ if ((rc = awe_write_wave_data(addr, sizeof_patch, smp, -1)) != 0)
+ return rc;
+
+ smp->sf_id = current_sf_id;
+ add_sf_sample(free_sample);
+
+ /* set up voice info */
+ free_info = awe_free_info();
+ rec = &infos[free_info].v;
+ awe_init_voice_info(rec);
+ rec->sample = free_sample; /* the last sample */
+ rec->rate_offset = calc_rate_offset(patch.base_freq);
+ note = freq_to_note(patch.base_note);
+ rec->root = note / 100;
+ rec->tune = -(note % 100);
+ rec->low = freq_to_note(patch.low_note) / 100;
+ rec->high = freq_to_note(patch.high_note) / 100;
+ DEBUG(1,printk("AWE32: [gus base offset=%d, note=%d, range=%d-%d(%d-%d)]\n",
+ rec->rate_offset, note,
+ rec->low, rec->high,
+ patch.low_note, patch.high_note));
+ /* panning position; -128 - 127 => 0-127 */
+ rec->pan = (patch.panning + 128) / 2;
+
+ /* detuning is ignored */
+ /* 6points volume envelope */
+ if (patch.mode & WAVE_ENVELOPES) {
+ int attack, hold, decay, release;
+ attack = calc_gus_envelope_time
+ (patch.env_rate[0], 0, patch.env_offset[0]);
+ hold = calc_gus_envelope_time
+ (patch.env_rate[1], patch.env_offset[0],
+ patch.env_offset[1]);
+ decay = calc_gus_envelope_time
+ (patch.env_rate[2], patch.env_offset[1],
+ patch.env_offset[2]);
+ release = calc_gus_envelope_time
+ (patch.env_rate[3], patch.env_offset[1],
+ patch.env_offset[4]);
+ release += calc_gus_envelope_time
+ (patch.env_rate[4], patch.env_offset[3],
+ patch.env_offset[4]);
+ release += calc_gus_envelope_time
+ (patch.env_rate[5], patch.env_offset[4],
+ patch.env_offset[5]);
+ rec->parm.volatkhld = (calc_parm_attack(attack) << 8) |
+ calc_parm_hold(hold);
+ rec->parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) |
+ calc_parm_decay(decay);
+ rec->parm.volrelease = 0x8000 | calc_parm_decay(release);
+ DEBUG(2,printk("AWE32: [gusenv atk=%d, hld=%d, dcy=%d, rel=%d]\n", attack, hold, decay, release));
+ rec->attenuation = calc_gus_attenuation(patch.env_offset[0]);
+ }
+
+ /* tremolo effect */
+ if (patch.mode & WAVE_TREMOLO) {
+ int rate = (patch.tremolo_rate * 1000 / 38) / 42;
+ rec->parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate;
+ DEBUG(2,printk("AWE32: [gusenv tremolo rate=%d, dep=%d, tremfrq=%x]\n",
+ patch.tremolo_rate, patch.tremolo_depth,
+ rec->parm.tremfrq));
+ }
+ /* vibrato effect */
+ if (patch.mode & WAVE_VIBRATO) {
+ int rate = (patch.vibrato_rate * 1000 / 38) / 42;
+ rec->parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate;
+ DEBUG(2,printk("AWE32: [gusenv vibrato rate=%d, dep=%d, tremfrq=%x]\n",
+ patch.tremolo_rate, patch.tremolo_depth,
+ rec->parm.tremfrq));
+ }
+
+ /* scale_freq, scale_factor, volume, and fractions not implemented */
+
+ /* append to the tail of the list */
+ infos[free_info].bank = ctrls[AWE_MD_GUS_BANK];
+ infos[free_info].instr = patch.instr_no;
+ infos[free_info].disabled = FALSE;
+ infos[free_info].type = V_ST_NORMAL;
+ infos[free_info].v.sf_id = current_sf_id;
+
+ add_info_list(free_info);
+ add_sf_info(free_info);
+
+ /* set the voice index */
+ awe_set_sample(rec);
+
+ return 0;
+}
+
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
+
+/*
+ * sample and voice list handlers
+ */
+
+/* append this to the sf list */
+static void add_sf_info(int rec)
+{
+ int sf_id = infos[rec].v.sf_id;
+ if (sf_id <= 0) return;
+ sf_id--;
+ if (sflists[sf_id].infos < 0)
+ sflists[sf_id].infos = rec;
+ else {
+ int i, prev;
+ prev = sflists[sf_id].infos;
+ while ((i = infos[prev].next) >= 0)
+ prev = i;
+ infos[prev].next = rec;
+ }
+ infos[rec].next = -1;
+ sflists[sf_id].num_info++;
+}
+
+/* prepend this sample to sf list */
+static void add_sf_sample(int rec)
+{
+ int sf_id = samples[rec].v.sf_id;
+ if (sf_id <= 0) return;
+ sf_id--;
+ samples[rec].next = sflists[sf_id].samples;
+ sflists[sf_id].samples = rec;
+ sflists[sf_id].num_sample++;
+}
+
+/* purge the old records which don't belong with the same file id */
+static void purge_old_list(int rec, int next)
+{
+ infos[rec].next_instr = next;
+ if (infos[rec].bank == AWE_DRUM_BANK) {
+ /* remove samples with the same note range */
+ int cur, *prevp = &infos[rec].next_instr;
+ int low = infos[rec].v.low;
+ int high = infos[rec].v.high;
+ for (cur = next; cur >= 0; cur = infos[cur].next_instr) {
+ if (infos[cur].v.low == low &&
+ infos[cur].v.high == high &&
+ ! is_identical_id(infos[cur].v.sf_id, infos[rec].v.sf_id))
+ *prevp = infos[cur].next_instr;
+ prevp = &infos[cur].next_instr;
+ }
+ } else {
+ if (! is_identical_id(infos[next].v.sf_id, infos[rec].v.sf_id))
+ infos[rec].next_instr = -1;
+ }
+}
+
+/* prepend to top of the preset table */
+static void add_info_list(int rec)
+{
+ int *prevp, cur;
+ int instr;
+ int bank;
+
+ if (infos[rec].disabled)
+ return;
+
+ instr = infos[rec].instr;
+ bank = infos[rec].bank;
+ limitvalue(instr, 0, AWE_MAX_PRESETS-1);
+ prevp = &preset_table[instr];
+ cur = *prevp;
+ while (cur >= 0) {
+ /* search the first record with the same bank number */
+ if (infos[cur].bank == bank) {
+ /* replace the list with the new record */
+ infos[rec].next_bank = infos[cur].next_bank;
+ *prevp = rec;
+ purge_old_list(rec, cur);
+ return;
+ }
+ prevp = &infos[cur].next_bank;
+ cur = infos[cur].next_bank;
+ }
+
+ /* this is the first bank record.. just add this */
+ infos[rec].next_instr = -1;
+ infos[rec].next_bank = preset_table[instr];
+ preset_table[instr] = rec;
+}
+
+/* remove samples later than the specified sf_id */
+static void
+awe_remove_samples(int sf_id)
+{
+ if (sf_id <= 0) {
+ awe_reset_samples();
+ return;
+ }
+ /* already removed? */
+ if (current_sf_id <= sf_id)
+ return;
+
+ current_sf_id = sf_id;
+ if (locked_sf_id > sf_id)
+ locked_sf_id = sf_id;
+
+ rebuild_preset_list();
+}
+
+/* rebuild preset search list */
+static void rebuild_preset_list(void)
+{
+ int i, j;
+
+ for (i = 0; i < AWE_MAX_PRESETS; i++)
+ preset_table[i] = -1;
+
+ for (i = 0; i < current_sf_id; i++) {
+ for (j = sflists[i].infos; j >= 0; j = infos[j].next)
+ add_info_list(j);
+ }
+}
+
+/* compare the given sf_id pair */
+static int is_identical_id(int id1, int id2)
+{
+ if (id1 == id2)
+ return TRUE;
+ if (id1 <= 0 || id2 <= 0) /* this must not happen.. */
+ return FALSE;
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+ {
+ /* compare with the sharing id */
+ int i;
+ if (id1 < id2) { /* make sure id1 > id2 */
+ int tmp; tmp = id1; id1 = id2; id2 = tmp;
+ }
+ for (i = sflists[id1-1].shared; i > 0 && i <= current_sf_id; i = sflists[i-1].shared) {
+ if (i == id2)
+ return TRUE;
+ }
+ }
+#endif /* allow sharing */
+ return FALSE;
+}
+
+/* search the sample index matching with the given sample id */
+static int search_sample_index(int sf, int sample, int level)
+{
+ int i;
+
+ if (sf <= 0 || sf > current_sf_id)
+ return -1; /* this must not happen */
+
+ for (i = sflists[sf-1].samples; i >= 0; i = samples[i].next) {
+ if (samples[i].v.sample == sample)
+ return i;
+ }
+#ifdef AWE_ALLOW_SAMPLE_SHARING
+ if ((i = sflists[sf-1].shared) > 0 && i <= current_sf_id) { /* search recursively */
+ if (level > current_sf_id)
+ return -1; /* strange sharing loop.. quit */
+ return search_sample_index(i, sample, level + 1);
+ }
+#endif
+ return -1;
+}
+
+/* search the specified sample */
+static short
+awe_set_sample(awe_voice_info *vp)
+{
+ int i;
+
+ vp->index = -1;
+ if ((i = search_sample_index(vp->sf_id, vp->sample, 0)) < 0)
+ return -1;
+
+ /* set the actual sample offsets */
+ vp->start += samples[i].v.start;
+ vp->end += samples[i].v.end;
+ vp->loopstart += samples[i].v.loopstart;
+ vp->loopend += samples[i].v.loopend;
+ /* copy mode flags */
+ vp->mode = samples[i].v.mode_flags;
+ /* set index */
+ vp->index = i;
+
+ return i;
+}
+
+
+/*----------------------------------------------------------------
+ * voice allocation
+ *----------------------------------------------------------------*/
+
+/* look for all voices associated with the specified note & velocity */
+static int
+awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist)
+{
+ int nvoices;
+
+ nvoices = 0;
+ for (; rec >= 0; rec = infos[rec].next_instr) {
+ if (note >= infos[rec].v.low &&
+ note <= infos[rec].v.high &&
+ velocity >= infos[rec].v.vellow &&
+ velocity <= infos[rec].v.velhigh) {
+ if (infos[rec].type == V_ST_MAPPED) {
+ /* mapper */
+ vlist[0] = &infos[rec].v;
+ return -1;
+ }
+ vlist[nvoices++] = &infos[rec].v;
+ if (nvoices >= AWE_MAX_VOICES)
+ break;
+ }
+ }
+ return nvoices;
+}
+
+/* store the voice list from the specified note and velocity.
+ if the preset is mapped, seek for the destination preset, and rewrite
+ the note number if necessary.
+ */
+static int
+really_alloc_voices(int vrec, int def_vrec, int *note, int velocity, awe_voice_info **vlist, int level)
+{
+ int nvoices;
+
+ nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist);
+ if (nvoices == 0)
+ nvoices = awe_search_multi_voices(def_vrec, *note, velocity, vlist);
+ if (nvoices < 0) { /* mapping */
+ int preset = vlist[0]->start;
+ int bank = vlist[0]->end;
+ int key = vlist[0]->fixkey;
+ if (level > 5) {
+ printk("AWE32: too deep mapping level\n");
+ return 0;
+ }
+ vrec = awe_search_instr(bank, preset);
+ if (bank == AWE_DRUM_BANK)
+ def_vrec = awe_search_instr(bank, 0);
+ else
+ def_vrec = awe_search_instr(0, preset);
+ if (key >= 0)
+ *note = key;
+ return really_alloc_voices(vrec, def_vrec, note, velocity, vlist, level+1);
+ }
+
+ return nvoices;
+}
+
+/* allocate voices corresponding note and velocity; supports multiple insts. */
+static void
+awe_alloc_multi_voices(int ch, int note, int velocity, int key)
+{
+ int i, v, nvoices;
+ awe_voice_info *vlist[AWE_MAX_VOICES];
+
+ if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0)
+ awe_set_instr(0, ch, channels[ch].instr);
+
+ /* check the possible voices; note may be changeable if mapped */
+ nvoices = really_alloc_voices(channels[ch].vrec, channels[ch].def_vrec,
+ ¬e, velocity, vlist, 0);
+
+ /* set the voices */
+ current_alloc_time++;
+ for (i = 0; i < nvoices; i++) {
+ v = awe_clear_voice();
+ voices[v].key = key;
+ voices[v].ch = ch;
+ voices[v].note = note;
+ voices[v].velocity = velocity;
+ voices[v].time = current_alloc_time;
+ voices[v].cinfo = &channels[ch];
+ voices[v].sample = vlist[i];
+ voices[v].state = AWE_ST_MARK;
+ voices[v].layer = nvoices - i - 1; /* in reverse order */
+ }
+
+ /* clear the mark in allocated voices */
+ for (i = 0; i < awe_max_voices; i++) {
+ if (voices[i].state == AWE_ST_MARK)
+ voices[i].state = AWE_ST_OFF;
+
+ }
+}
+
+
+/* search the best voice from the specified status condition */
+static int
+search_best_voice(int condition)
+{
+ int i, time, best;
+ int vtarget = 0xffff, min_vtarget = 0xffff;
+
+ best = -1;
+ time = current_alloc_time + 1;
+ for (i = 0; i < awe_max_voices; i++) {
+ if (! (voices[i].state & condition))
+ continue;
+#ifdef AWE_CHECK_VTARGET
+ /* get current volume */
+ vtarget = (awe_peek_dw(AWE_VTFT(i)) >> 16) & 0xffff;
+#endif
+ if (best < 0 || vtarget < min_vtarget ||
+ (vtarget == min_vtarget && voices[i].time < time)) {
+ best = i;
+ time = voices[i].time;
+ min_vtarget = vtarget;
+ }
+ }
+ /* clear voice */
+ if (best >= 0) {
+ if (voices[best].state != AWE_ST_OFF)
+ awe_terminate(best);
+ awe_voice_init(best, TRUE);
+ }
+
+ return best;
+}
+
+/* search an empty voice.
+ if no empty voice is found, at least terminate a voice
+ */
+static int
+awe_clear_voice(void)
+{
+ int best;
+
+ /* looking for the oldest empty voice */
+ if ((best = search_best_voice(AWE_ST_OFF)) >= 0)
+ return best;
+ if ((best = search_best_voice(AWE_ST_RELEASED)) >= 0)
+ return best;
+ /* looking for the oldest sustained voice */
+ if ((best = search_best_voice(AWE_ST_SUSTAINED)) >= 0)
+ return best;
+
+ if (MULTI_LAYER_MODE() && ctrls[AWE_MD_CHN_PRIOR]) {
+ int ch = -1;
+ int time = current_alloc_time + 1;
+ int i;
+ /* looking for the voices from high channel (except drum ch) */
+ for (i = 0; i < awe_max_voices; i++) {
+ if (IS_DRUM_CHANNEL(voices[i].ch)) continue;
+ if (voices[i].ch < ch) continue;
+ if (voices[i].state != AWE_ST_MARK &&
+ (voices[i].ch > ch || voices[i].time < time)) {
+ best = i;
+ time = voices[i].time;
+ ch = voices[i].ch;
+ }
+ }
+ }
+ if (best < 0)
+ best = search_best_voice(~AWE_ST_MARK);
+
+ if (best >= 0)
+ return best;
+
+ return 0;
+}
+
+
+/* search sample for the specified note & velocity and set it on the voice;
+ * note that voice is the voice index (not channel index)
+ */
+static void
+awe_alloc_one_voice(int voice, int note, int velocity)
+{
+ int ch, nvoices;
+ awe_voice_info *vlist[AWE_MAX_VOICES];
+
+ ch = voices[voice].ch;
+ if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0)
+ awe_set_instr(0, ch, channels[ch].instr);
+
+ nvoices = really_alloc_voices(voices[voice].cinfo->vrec,
+ voices[voice].cinfo->def_vrec,
+ ¬e, velocity, vlist, 0);
+ if (nvoices > 0) {
+ voices[voice].time = ++current_alloc_time;
+ voices[voice].sample = vlist[0]; /* use the first one */
+ voices[voice].layer = 0;
+ voices[voice].note = note;
+ voices[voice].velocity = velocity;
+ }
+}
+
+
+/*----------------------------------------------------------------
+ * sequencer2 functions
+ *----------------------------------------------------------------*/
+
+/* search an empty voice; used by sequencer2 */
+static int
+awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+ playing_mode = AWE_PLAY_MULTI2;
+ awe_info.nr_voices = AWE_MAX_CHANNELS;
+ return awe_clear_voice();
+}
+
+
+/* set up voice; used by sequencer2 */
+static void
+awe_setup_voice(int dev, int voice, int chn)
+{
+ struct channel_info *info;
+ if (synth_devs[dev] == NULL ||
+ (info = &synth_devs[dev]->chn_info[chn]) == NULL)
+ return;
+
+ if (voice < 0 || voice >= awe_max_voices)
+ return;
+
+ DEBUG(2,printk("AWE32: [setup(%d) ch=%d]\n", voice, chn));
+ channels[chn].expression_vol = info->controllers[CTL_EXPRESSION];
+ channels[chn].main_vol = info->controllers[CTL_MAIN_VOLUME];
+ channels[chn].panning =
+ info->controllers[CTL_PAN] * 2 - 128; /* signed 8bit */
+ channels[chn].bender = info->bender_value; /* zero center */
+ channels[chn].bank = info->controllers[CTL_BANK_SELECT];
+ channels[chn].sustained = info->controllers[CTL_SUSTAIN];
+ if (info->controllers[CTL_EXT_EFF_DEPTH]) {
+ FX_SET(&channels[chn].fx, AWE_FX_REVERB,
+ info->controllers[CTL_EXT_EFF_DEPTH] * 2);
+ }
+ if (info->controllers[CTL_CHORUS_DEPTH]) {
+ FX_SET(&channels[chn].fx, AWE_FX_CHORUS,
+ info->controllers[CTL_CHORUS_DEPTH] * 2);
+ }
+ awe_set_instr(dev, chn, info->pgm_num);
+}
+
+
+#ifdef CONFIG_AWE32_MIXER
+/*
+ * AWE32 mixer device control
+ */
+
+static int awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg);
+
+static int my_mixerdev = -1;
+
+static struct mixer_operations awe_mixer_operations = {
+ "AWE32 Equalizer",
+ awe_mixer_ioctl,
+};
+
+static void attach_mixer(void)
+{
+ if ((my_mixerdev = sound_alloc_mixerdev()) >= 0) {
+ mixer_devs[my_mixerdev] = &awe_mixer_operations;
+ }
+}
+
+static void unload_mixer(void)
+{
+ if (my_mixerdev >= 0)
+ sound_unload_mixerdev(my_mixerdev);
+}
+
+static int
+awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+ int i, level, value;
+
+ if (((cmd >> 8) & 0xff) != 'M')
+ return -EINVAL;
+
+ level = (int) *(int *)arg;
+ level = ((level & 0xff) + (level >> 8)) / 2;
+ DEBUG(0,printk("AWEMix: cmd=%x val=%d\n", cmd & 0xff, level));
+
+ if (_SIOC_DIR(cmd) & _IOC_WRITE) {
+ switch (cmd & 0xff) {
+ case SOUND_MIXER_BASS:
+ value = level * 12 / 100;
+ if (value >= 12)
+ value = 11;
+ ctrls[AWE_MD_BASS_LEVEL] = value;
+ awe_update_equalizer();
+ break;
+ case SOUND_MIXER_TREBLE:
+ value = level * 12 / 100;
+ if (value >= 12)
+ value = 11;
+ ctrls[AWE_MD_TREBLE_LEVEL] = value;
+ awe_update_equalizer();
+ break;
+ case SOUND_MIXER_VOLUME:
+ level = level * 127 / 100;
+ if (level >= 128) level = 127;
+ atten_relative = FALSE;
+ atten_offset = vol_table[level];
+ awe_update_volume();
+ break;
+ }
+ }
+ switch (cmd & 0xff) {
+ case SOUND_MIXER_BASS:
+ level = ctrls[AWE_MD_BASS_LEVEL] * 100 / 24;
+ level = (level << 8) | level;
+ break;
+ case SOUND_MIXER_TREBLE:
+ level = ctrls[AWE_MD_TREBLE_LEVEL] * 100 / 24;
+ level = (level << 8) | level;
+ break;
+ case SOUND_MIXER_VOLUME:
+ value = atten_offset;
+ if (atten_relative)
+ value += ctrls[AWE_MD_ZERO_ATTEN];
+ for (i = 127; i > 0; i--) {
+ if (value <= vol_table[i])
+ break;
+ }
+ level = i * 100 / 127;
+ level = (level << 8) | level;
+ break;
+ case SOUND_MIXER_DEVMASK:
+ level = SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_VOLUME;
+ break;
+ default:
+ level = 0;
+ break;
+ }
+ return *(int *)arg = level;
+}
+#endif /* CONFIG_AWE32_MIXER */
+
+
+/*
+ * initialization of AWE32
+ */
+
+/* intiailize audio channels */
+static void
+awe_init_audio(void)
+{
+ int ch;
+
+ /* turn off envelope engines */
+ for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
+ awe_poke(AWE_DCYSUSV(ch), 0x80);
+ }
+
+ /* reset all other parameters to zero */
+ for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
+ awe_poke(AWE_ENVVOL(ch), 0);
+ awe_poke(AWE_ENVVAL(ch), 0);
+ awe_poke(AWE_DCYSUS(ch), 0);
+ awe_poke(AWE_ATKHLDV(ch), 0);
+ awe_poke(AWE_LFO1VAL(ch), 0);
+ awe_poke(AWE_ATKHLD(ch), 0);
+ awe_poke(AWE_LFO2VAL(ch), 0);
+ awe_poke(AWE_IP(ch), 0);
+ awe_poke(AWE_IFATN(ch), 0);
+ awe_poke(AWE_PEFE(ch), 0);
+ awe_poke(AWE_FMMOD(ch), 0);
+ awe_poke(AWE_TREMFRQ(ch), 0);
+ awe_poke(AWE_FM2FRQ2(ch), 0);
+ awe_poke_dw(AWE_PTRX(ch), 0);
+ awe_poke_dw(AWE_VTFT(ch), 0);
+ awe_poke_dw(AWE_PSST(ch), 0);
+ awe_poke_dw(AWE_CSL(ch), 0);
+ awe_poke_dw(AWE_CCCA(ch), 0);
+ }
+
+ for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
+ awe_poke_dw(AWE_CPF(ch), 0);
+ awe_poke_dw(AWE_CVCF(ch), 0);
+ }
+}
+
+
+/* initialize DMA address */
+static void
+awe_init_dma(void)
+{
+ awe_poke_dw(AWE_SMALR, 0);
+ awe_poke_dw(AWE_SMARR, 0);
+ awe_poke_dw(AWE_SMALW, 0);
+ awe_poke_dw(AWE_SMARW, 0);
+}
+
+
+/* initialization arrays; from ADIP */
+
+static unsigned short init1[128] = {
+ 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330,
+ 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730,
+ 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30,
+ 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30,
+
+ 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330,
+ 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730,
+ 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30,
+ 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30,
+
+ 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330,
+ 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730,
+ 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30,
+ 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30,
+
+ 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330,
+ 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730,
+ 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30,
+ 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30,
+};
+
+static unsigned short init2[128] = {
+ 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330,
+ 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730,
+ 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30,
+ 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30,
+
+ 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330,
+ 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730,
+ 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30,
+ 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30,
+
+ 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330,
+ 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730,
+ 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30,
+ 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30,
+
+ 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330,
+ 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730,
+ 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30,
+ 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,
+};
+
+static unsigned short init3[128] = {
+ 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
+ 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254,
+ 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234,
+ 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224,
+
+ 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254,
+ 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264,
+ 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294,
+ 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3,
+
+ 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287,
+ 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7,
+ 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386,
+ 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55,
+
+ 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308,
+ 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F,
+ 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319,
+ 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,
+};
+
+static unsigned short init4[128] = {
+ 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
+ 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254,
+ 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234,
+ 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224,
+
+ 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254,
+ 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264,
+ 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294,
+ 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3,
+
+ 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287,
+ 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7,
+ 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386,
+ 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55,
+
+ 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308,
+ 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F,
+ 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319,
+ 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570,
+};
+
+
+/* send initialization arrays to start up */
+static void
+awe_init_array(void)
+{
+ awe_send_array(init1);
+ awe_wait(1024);
+ awe_send_array(init2);
+ awe_send_array(init3);
+ awe_poke_dw(AWE_HWCF4, 0);
+ awe_poke_dw(AWE_HWCF5, 0x83);
+ awe_poke_dw(AWE_HWCF6, 0x8000);
+ awe_send_array(init4);
+}
+
+/* send an initialization array */
+static void
+awe_send_array(unsigned short *data)
+{
+ int i;
+ unsigned short *p;
+
+ p = data;
+ for (i = 0; i < AWE_MAX_VOICES; i++, p++)
+ awe_poke(AWE_INIT1(i), *p);
+ for (i = 0; i < AWE_MAX_VOICES; i++, p++)
+ awe_poke(AWE_INIT2(i), *p);
+ for (i = 0; i < AWE_MAX_VOICES; i++, p++)
+ awe_poke(AWE_INIT3(i), *p);
+ for (i = 0; i < AWE_MAX_VOICES; i++, p++)
+ awe_poke(AWE_INIT4(i), *p);
+}
+
+
+/*
+ * set up awe32 channels to some known state.
+ */
+
+/* set the envelope & LFO parameters to the default values; see ADIP */
+static void
+awe_tweak_voice(int i)
+{
+ /* set all mod/vol envelope shape to minimum */
+ awe_poke(AWE_ENVVOL(i), 0x8000);
+ awe_poke(AWE_ENVVAL(i), 0x8000);
+ awe_poke(AWE_DCYSUS(i), 0x7F7F);
+ awe_poke(AWE_ATKHLDV(i), 0x7F7F);
+ awe_poke(AWE_ATKHLD(i), 0x7F7F);
+ awe_poke(AWE_PEFE(i), 0); /* mod envelope height to zero */
+ awe_poke(AWE_LFO1VAL(i), 0x8000); /* no delay for LFO1 */
+ awe_poke(AWE_LFO2VAL(i), 0x8000);
+ awe_poke(AWE_IP(i), 0xE000); /* no pitch shift */
+ awe_poke(AWE_IFATN(i), 0xFF00); /* volume to minimum */
+ awe_poke(AWE_FMMOD(i), 0);
+ awe_poke(AWE_TREMFRQ(i), 0);
+ awe_poke(AWE_FM2FRQ2(i), 0);
+}
+
+static void
+awe_tweak(void)
+{
+ int i;
+ /* reset all channels */
+ for (i = 0; i < awe_max_voices; i++)
+ awe_tweak_voice(i);
+}
+
+
+/*
+ * initializes the FM section of AWE32;
+ * see Vince Vu's unofficial AWE32 programming guide
+ */
+
+static void
+awe_init_fm(void)
+{
+#ifndef AWE_ALWAYS_INIT_FM
+ /* if no extended memory is on board.. */
+ if (awe_mem_size <= 0)
+ return;
+#endif
+ DEBUG(3,printk("AWE32: initializing FM\n"));
+
+ /* Initialize the last two channels for DRAM refresh and producing
+ the reverb and chorus effects for Yamaha OPL-3 synthesizer */
+
+ /* 31: FM left channel, 0xffffe0-0xffffe8 */
+ awe_poke(AWE_DCYSUSV(30), 0x80);
+ awe_poke_dw(AWE_PSST(30), 0xFFFFFFE0); /* full left */
+ awe_poke_dw(AWE_CSL(30), 0x00FFFFE8 |
+ (DEF_FM_CHORUS_DEPTH << 24));
+ awe_poke_dw(AWE_PTRX(30), (DEF_FM_REVERB_DEPTH << 8));
+ awe_poke_dw(AWE_CPF(30), 0);
+ awe_poke_dw(AWE_CCCA(30), 0x00FFFFE3);
+
+ /* 32: FM right channel, 0xfffff0-0xfffff8 */
+ awe_poke(AWE_DCYSUSV(31), 0x80);
+ awe_poke_dw(AWE_PSST(31), 0x00FFFFF0); /* full right */
+ awe_poke_dw(AWE_CSL(31), 0x00FFFFF8 |
+ (DEF_FM_CHORUS_DEPTH << 24));
+ awe_poke_dw(AWE_PTRX(31), (DEF_FM_REVERB_DEPTH << 8));
+ awe_poke_dw(AWE_CPF(31), 0x8000);
+ awe_poke_dw(AWE_CCCA(31), 0x00FFFFF3);
+
+ /* skew volume & cutoff */
+ awe_poke_dw(AWE_VTFT(30), 0x8000FFFF);
+ awe_poke_dw(AWE_VTFT(31), 0x8000FFFF);
+
+ voices[30].state = AWE_ST_FM;
+ voices[31].state = AWE_ST_FM;
+
+ /* change maximum channels to 30 */
+ awe_max_voices = AWE_NORMAL_VOICES;
+ if (playing_mode == AWE_PLAY_DIRECT)
+ awe_info.nr_voices = awe_max_voices;
+ else
+ awe_info.nr_voices = AWE_MAX_CHANNELS;
+ voice_alloc->max_voice = awe_max_voices;
+}
+
+/*
+ * AWE32 DRAM access routines
+ */
+
+/* open DRAM write accessing mode */
+static int
+awe_open_dram_for_write(int offset, int channels)
+{
+ int vidx[AWE_NORMAL_VOICES];
+ int i;
+
+ if (channels < 0 || channels >= AWE_NORMAL_VOICES) {
+ channels = AWE_NORMAL_VOICES;
+ for (i = 0; i < AWE_NORMAL_VOICES; i++)
+ vidx[i] = i;
+ } else {
+ for (i = 0; i < channels; i++) {
+ vidx[i] = awe_clear_voice();
+ voices[vidx[i]].state = AWE_ST_MARK;
+ }
+ }
+
+ /* use all channels for DMA transfer */
+ for (i = 0; i < channels; i++) {
+ if (vidx[i] < 0) continue;
+ awe_poke(AWE_DCYSUSV(vidx[i]), 0x80);
+ awe_poke_dw(AWE_VTFT(vidx[i]), 0);
+ awe_poke_dw(AWE_CVCF(vidx[i]), 0);
+ awe_poke_dw(AWE_PTRX(vidx[i]), 0x40000000);
+ awe_poke_dw(AWE_CPF(vidx[i]), 0x40000000);
+ awe_poke_dw(AWE_PSST(vidx[i]), 0);
+ awe_poke_dw(AWE_CSL(vidx[i]), 0);
+ awe_poke_dw(AWE_CCCA(vidx[i]), 0x06000000);
+ voices[vidx[i]].state = AWE_ST_DRAM;
+ }
+ /* point channels 31 & 32 to ROM samples for DRAM refresh */
+ awe_poke_dw(AWE_VTFT(30), 0);
+ awe_poke_dw(AWE_PSST(30), 0x1d8);
+ awe_poke_dw(AWE_CSL(30), 0x1e0);
+ awe_poke_dw(AWE_CCCA(30), 0x1d8);
+ awe_poke_dw(AWE_VTFT(31), 0);
+ awe_poke_dw(AWE_PSST(31), 0x1d8);
+ awe_poke_dw(AWE_CSL(31), 0x1e0);
+ awe_poke_dw(AWE_CCCA(31), 0x1d8);
+ voices[30].state = AWE_ST_FM;
+ voices[31].state = AWE_ST_FM;
+
+ /* if full bit is on, not ready to write on */
+ if (awe_peek_dw(AWE_SMALW) & 0x80000000) {
+ for (i = 0; i < channels; i++) {
+ awe_poke_dw(AWE_CCCA(vidx[i]), 0);
+ voices[vidx[i]].state = AWE_ST_OFF;
+ }
+ return -ENOSPC;
+ }
+
+ /* set address to write */
+ awe_poke_dw(AWE_SMALW, offset);
+
+ return 0;
+}
+
+/* open DRAM for RAM size detection */
+static void
+awe_open_dram_for_check(void)
+{
+ int i;
+ for (i = 0; i < AWE_NORMAL_VOICES; i++) {
+ awe_poke(AWE_DCYSUSV(i), 0x80);
+ awe_poke_dw(AWE_VTFT(i), 0);
+ awe_poke_dw(AWE_CVCF(i), 0);
+ awe_poke_dw(AWE_PTRX(i), 0x40000000);
+ awe_poke_dw(AWE_CPF(i), 0x40000000);
+ awe_poke_dw(AWE_PSST(i), 0);
+ awe_poke_dw(AWE_CSL(i), 0);
+ if (i & 1) /* DMA write */
+ awe_poke_dw(AWE_CCCA(i), 0x06000000);
+ else /* DMA read */
+ awe_poke_dw(AWE_CCCA(i), 0x04000000);
+ voices[i].state = AWE_ST_DRAM;
+ }
+}
+
+
+/* close dram access */
+static void
+awe_close_dram(void)
+{
+ int i;
+ /* wait until FULL bit in SMAxW register be false */
+ for (i = 0; i < 10000; i++) {
+ if (!(awe_peek_dw(AWE_SMALW) & 0x80000000))
+ break;
+ awe_wait(10);
+ }
+
+ for (i = 0; i < AWE_NORMAL_VOICES; i++) {
+ if (voices[i].state == AWE_ST_DRAM) {
+ awe_poke_dw(AWE_CCCA(i), 0);
+ awe_poke(AWE_DCYSUSV(i), 0x807F);
+ voices[i].state = AWE_ST_OFF;
+ }
+ }
+}
+
+
+/*================================================================
+ * detect presence of AWE32 and check memory size
+ *================================================================*/
+
+/* detect emu8000 chip on the specified address; from VV's guide */
+
+static int
+awe_detect_base(int addr)
+{
+ setup_ports(addr, 0, 0);
+ if ((awe_peek(AWE_U1) & 0x000F) != 0x000C)
+ return 0;
+ if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058)
+ return 0;
+ if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003)
+ return 0;
+ DEBUG(0,printk("AWE32 found at %x\n", addr));
+ return 1;
+}
+
+static int
+awe_detect(void)
+{
+ int base;
+
+ if (port_setuped) /* already initialized by PnP */
+ return 1;
+
+ if (awe_port) /* use default i/o port value */
+ setup_ports(awe_port, 0, 0);
+ else { /* probe it */
+ for (base = 0x620; base <= 0x680; base += 0x20)
+ if (awe_detect_base(base))
+ return 1;
+ DEBUG(0,printk("AWE32 not found\n"));
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*================================================================
+ * check dram size on AWE board
+ *================================================================*/
+
+/* any three numbers you like */
+#define UNIQUE_ID1 0x1234
+#define UNIQUE_ID2 0x4321
+#define UNIQUE_ID3 0xFFFF
+
+static void
+awe_check_dram(void)
+{
+ if (awe_present) /* already initialized */
+ return;
+
+ if (awe_mem_size >= 0) { /* given by config file or module option */
+ awe_mem_size *= 1024; /* convert to Kbytes */
+ return;
+ }
+
+ awe_open_dram_for_check();
+
+ awe_mem_size = 0;
+
+ /* set up unique two id numbers */
+ awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET);
+ awe_poke(AWE_SMLD, UNIQUE_ID1);
+ awe_poke(AWE_SMLD, UNIQUE_ID2);
+
+ while (awe_mem_size < AWE_MAX_DRAM_SIZE) {
+ awe_wait(5);
+ /* read a data on the DRAM start address */
+ awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET);
+ awe_peek(AWE_SMLD); /* discard stale data */
+ if (awe_peek(AWE_SMLD) != UNIQUE_ID1)
+ break;
+ if (awe_peek(AWE_SMLD) != UNIQUE_ID2)
+ break;
+ awe_mem_size += 512; /* increment 512kbytes */
+ /* Write a unique data on the test address;
+ * if the address is out of range, the data is written on
+ * 0x200000(=AWE_DRAM_OFFSET). Then the two id words are
+ * broken by this data.
+ */
+ awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + awe_mem_size*512L);
+ awe_poke(AWE_SMLD, UNIQUE_ID3);
+ awe_wait(5);
+ /* read a data on the just written DRAM address */
+ awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + awe_mem_size*512L);
+ awe_peek(AWE_SMLD); /* discard stale data */
+ if (awe_peek(AWE_SMLD) != UNIQUE_ID3)
+ break;
+ }
+ awe_close_dram();
+
+ DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", awe_mem_size));
+
+ /* convert to Kbytes */
+ awe_mem_size *= 1024;
+}
+
+
+/*================================================================
+ * chorus and reverb controls; from VV's guide
+ *================================================================*/
+
+/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
+static char chorus_defined[AWE_CHORUS_NUMBERS];
+static awe_chorus_fx_rec chorus_parm[AWE_CHORUS_NUMBERS] = {
+ {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */
+ {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */
+ {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */
+ {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */
+ {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */
+ {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */
+ {0xE600, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay */
+ {0xE6C0, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay + feedback */
+};
+
+static int
+awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count)
+{
+ if (patch->optarg < AWE_CHORUS_PREDEFINED || patch->optarg >= AWE_CHORUS_NUMBERS) {
+ printk("AWE32 Error: illegal chorus mode %d for uploading\n", patch->optarg);
+ return -EINVAL;
+ }
+ if (count < sizeof(awe_chorus_fx_rec)) {
+ printk("AWE32 Error: too short chorus fx parameters\n");
+ return -EINVAL;
+ }
+ copy_from_user(&chorus_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE,
+ sizeof(awe_chorus_fx_rec));
+ chorus_defined[patch->optarg] = TRUE;
+ return 0;
+}
+
+static void
+awe_set_chorus_mode(int effect)
+{
+ if (effect < 0 || effect >= AWE_CHORUS_NUMBERS ||
+ (effect >= AWE_CHORUS_PREDEFINED && !chorus_defined[effect]))
+ return;
+ awe_poke(AWE_INIT3(9), chorus_parm[effect].feedback);
+ awe_poke(AWE_INIT3(12), chorus_parm[effect].delay_offset);
+ awe_poke(AWE_INIT4(3), chorus_parm[effect].lfo_depth);
+ awe_poke_dw(AWE_HWCF4, chorus_parm[effect].delay);
+ awe_poke_dw(AWE_HWCF5, chorus_parm[effect].lfo_freq);
+ awe_poke_dw(AWE_HWCF6, 0x8000);
+ awe_poke_dw(AWE_HWCF7, 0x0000);
+}
+
+static void
+awe_update_chorus_mode(void)
+{
+ awe_set_chorus_mode(ctrls[AWE_MD_CHORUS_MODE]);
+}
+
+/*----------------------------------------------------------------*/
+
+/* reverb mode settings; write the following 28 data of 16 bit length
+ * on the corresponding ports in the reverb_cmds array
+ */
+static char reverb_defined[AWE_CHORUS_NUMBERS];
+static awe_reverb_fx_rec reverb_parm[AWE_REVERB_NUMBERS] = {
+{{ /* room 1 */
+ 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4,
+ 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516,
+ 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+ 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{ /* room 2 */
+ 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+ 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+ 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+ 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{ /* room 3 */
+ 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+ 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516,
+ 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B,
+ 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A,
+}},
+{{ /* hall 1 */
+ 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+ 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+ 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A,
+ 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529,
+}},
+{{ /* hall 2 */
+ 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254,
+ 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3,
+ 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+ 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{ /* plate */
+ 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234,
+ 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548,
+ 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+ 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{ /* delay */
+ 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204,
+ 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+ 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+ 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+}},
+{{ /* panning delay */
+ 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204,
+ 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+ 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+ 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+}},
+};
+
+static struct ReverbCmdPair {
+ unsigned short cmd, port;
+} reverb_cmds[28] = {
+ {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)},
+ {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)},
+ {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)},
+ {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)},
+ {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)},
+ {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)},
+ {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
+};
+
+static int
+awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count)
+{
+ if (patch->optarg < AWE_REVERB_PREDEFINED || patch->optarg >= AWE_REVERB_NUMBERS) {
+ printk("AWE32 Error: illegal reverb mode %d for uploading\n", patch->optarg);
+ return -EINVAL;
+ }
+ if (count < sizeof(awe_reverb_fx_rec)) {
+ printk("AWE32 Error: too short reverb fx parameters\n");
+ return -EINVAL;
+ }
+ copy_from_user(&reverb_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE,
+ sizeof(awe_reverb_fx_rec));
+ reverb_defined[patch->optarg] = TRUE;
+ return 0;
+}
+
+static void
+awe_set_reverb_mode(int effect)
+{
+ int i;
+ if (effect < 0 || effect >= AWE_REVERB_NUMBERS ||
+ (effect >= AWE_REVERB_PREDEFINED && !reverb_defined[effect]))
+ return;
+ for (i = 0; i < 28; i++)
+ awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port,
+ reverb_parm[effect].parms[i]);
+}
+
+static void
+awe_update_reverb_mode(void)
+{
+ awe_set_reverb_mode(ctrls[AWE_MD_REVERB_MODE]);
+}
+
+/*================================================================
+ * treble/bass equalizer control
+ *================================================================*/
+
+static unsigned short bass_parm[12][3] = {
+ {0xD26A, 0xD36A, 0x0000}, /* -12 dB */
+ {0xD25B, 0xD35B, 0x0000}, /* -8 */
+ {0xD24C, 0xD34C, 0x0000}, /* -6 */
+ {0xD23D, 0xD33D, 0x0000}, /* -4 */
+ {0xD21F, 0xD31F, 0x0000}, /* -2 */
+ {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */
+ {0xC219, 0xC319, 0x0001}, /* +2 */
+ {0xC22A, 0xC32A, 0x0001}, /* +4 */
+ {0xC24C, 0xC34C, 0x0001}, /* +6 */
+ {0xC26E, 0xC36E, 0x0001}, /* +8 */
+ {0xC248, 0xC348, 0x0002}, /* +10 */
+ {0xC26A, 0xC36A, 0x0002}, /* +12 dB */
+};
+
+static unsigned short treble_parm[12][9] = {
+ {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */
+ {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+ {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+ {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+ {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */
+ {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002},
+ {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +12 dB */
+};
+
+
+/*
+ * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB]
+ */
+static void
+awe_equalizer(int bass, int treble)
+{
+ unsigned short w;
+
+ if (bass < 0 || bass > 11 || treble < 0 || treble > 11)
+ return;
+ awe_poke(AWE_INIT4(0x01), bass_parm[bass][0]);
+ awe_poke(AWE_INIT4(0x11), bass_parm[bass][1]);
+ awe_poke(AWE_INIT3(0x11), treble_parm[treble][0]);
+ awe_poke(AWE_INIT3(0x13), treble_parm[treble][1]);
+ awe_poke(AWE_INIT3(0x1B), treble_parm[treble][2]);
+ awe_poke(AWE_INIT4(0x07), treble_parm[treble][3]);
+ awe_poke(AWE_INIT4(0x0B), treble_parm[treble][4]);
+ awe_poke(AWE_INIT4(0x0D), treble_parm[treble][5]);
+ awe_poke(AWE_INIT4(0x17), treble_parm[treble][6]);
+ awe_poke(AWE_INIT4(0x19), treble_parm[treble][7]);
+ w = bass_parm[bass][2] + treble_parm[treble][8];
+ awe_poke(AWE_INIT4(0x15), (unsigned short)(w + 0x0262));
+ awe_poke(AWE_INIT4(0x1D), (unsigned short)(w + 0x8362));
+}
+
+static void awe_update_equalizer(void)
+{
+ awe_equalizer(ctrls[AWE_MD_BASS_LEVEL], ctrls[AWE_MD_TREBLE_LEVEL]);
+}
+
+
+#ifdef CONFIG_AWE32_MIDIEMU
+
+/*================================================================
+ * Emu8000 MIDI Emulation
+ *================================================================*/
+
+/*================================================================
+ * midi queue record
+ *================================================================*/
+
+/* queue type */
+enum { Q_NONE, Q_VARLEN, Q_READ, Q_SYSEX, };
+
+#define MAX_MIDIBUF 64
+
+/* midi status */
+typedef struct MidiStatus {
+ int queue; /* queue type */
+ int qlen; /* queue length */
+ int read; /* chars read */
+ int status; /* current status */
+ int chan; /* current channel */
+ unsigned char buf[MAX_MIDIBUF];
+} MidiStatus;
+
+/* MIDI mode type */
+enum { MODE_GM, MODE_GS, MODE_XG, };
+
+/* NRPN / CC -> Emu8000 parameter converter */
+typedef struct {
+ int control;
+ int awe_effect;
+ unsigned short (*convert)(int val);
+} ConvTable;
+
+
+/*================================================================
+ * prototypes
+ *================================================================*/
+
+static int awe_midi_open(int dev, int mode, void (*input)(int,unsigned char), void (*output)(int));
+static void awe_midi_close(int dev);
+static int awe_midi_ioctl(int dev, unsigned cmd, caddr_t arg);
+static int awe_midi_outputc(int dev, unsigned char midi_byte);
+
+static void init_midi_status(MidiStatus *st);
+static void clear_rpn(void);
+static void get_midi_char(MidiStatus *st, int c);
+/*static void queue_varlen(MidiStatus *st, int c);*/
+static void special_event(MidiStatus *st, int c);
+static void queue_read(MidiStatus *st, int c);
+static void midi_note_on(MidiStatus *st);
+static void midi_note_off(MidiStatus *st);
+static void midi_key_pressure(MidiStatus *st);
+static void midi_channel_pressure(MidiStatus *st);
+static void midi_pitch_wheel(MidiStatus *st);
+static void midi_program_change(MidiStatus *st);
+static void midi_control_change(MidiStatus *st);
+static void midi_select_bank(MidiStatus *st, int val);
+static void midi_nrpn_event(MidiStatus *st);
+static void midi_rpn_event(MidiStatus *st);
+static void midi_detune(int chan, int coarse, int fine);
+static void midi_system_exclusive(MidiStatus *st);
+static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val);
+static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val);
+static int xg_control_change(MidiStatus *st, int cmd, int val);
+
+#define numberof(ary) (sizeof(ary)/sizeof(ary[0]))
+
+
+/*================================================================
+ * OSS Midi device record
+ *================================================================*/
+
+static struct midi_operations awe_midi_operations =
+{
+ {"AWE Midi Emu", 0, 0, SNDCARD_SB},
+ NULL /*&std_midi_synth*/,
+ {0}, /* input_info */
+ awe_midi_open, /*open*/
+ awe_midi_close, /*close*/
+ awe_midi_ioctl, /*ioctl*/
+ awe_midi_outputc, /*outputc*/
+ NULL /*start_read*/,
+ NULL /*end_read*/,
+ NULL, /* kick */
+ NULL, /* command */
+};
+
+static int my_mididev = -1;
+
+static void attach_midiemu(void)
+{
+ if ((my_mididev = sound_alloc_mididev()) < 0)
+ printk ("Sound: Too many midi devices detected\n");
+ else
+ midi_devs[my_mididev] = &awe_midi_operations;
+}
+
+static void unload_midiemu(void)
+{
+ if (my_mididev >= 0)
+ sound_unload_mididev(my_mididev);
+}
+
+
+/*
+ * open/close midi device
+ */
+
+static int midi_opened = FALSE;
+
+static int midi_mode;
+static int coarsetune = 0, finetune = 0;
+
+static int xg_mapping = TRUE;
+static int xg_bankmode = 0;
+
+/* effect sensitivity */
+
+#define FX_CUTOFF 0
+#define FX_RESONANCE 1
+#define FX_ATTACK 2
+#define FX_RELEASE 3
+#define FX_VIBRATE 4
+#define FX_VIBDEPTH 5
+#define FX_VIBDELAY 6
+#define FX_NUMS 7
+
+#define DEF_FX_CUTOFF 170
+#define DEF_FX_RESONANCE 6
+#define DEF_FX_ATTACK 50
+#define DEF_FX_RELEASE 50
+#define DEF_FX_VIBRATE 30
+#define DEF_FX_VIBDEPTH 4
+#define DEF_FX_VIBDELAY 1500
+
+/* effect sense: */
+static int gs_sense[] =
+{
+ DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE,
+ DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY
+};
+static int xg_sense[] =
+{
+ DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE,
+ DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY
+};
+
+
+/* current status */
+static MidiStatus curst;
+
+
+static int
+awe_midi_open (int dev, int mode,
+ void (*input)(int,unsigned char),
+ void (*output)(int))
+{
+ if (midi_opened)
+ return -EBUSY;
+
+ midi_opened = TRUE;
+
+ midi_mode = MODE_GM;
+
+ curst.queue = Q_NONE;
+ curst.qlen = 0;
+ curst.read = 0;
+ curst.status = 0;
+ curst.chan = 0;
+ memset(curst.buf, 0, sizeof(curst.buf));
+
+ init_midi_status(&curst);
+
+ return 0;
+}
+
+static void
+awe_midi_close (int dev)
+{
+ midi_opened = FALSE;
+}
+
+
+static int
+awe_midi_ioctl (int dev, unsigned cmd, caddr_t arg)
+{
+ return -EPERM;
+}
+
+static int
+awe_midi_outputc (int dev, unsigned char midi_byte)
+{
+ if (! midi_opened)
+ return 1;
+
+ /* force to change playing mode */
+ playing_mode = AWE_PLAY_MULTI;
+
+ get_midi_char(&curst, midi_byte);
+ return 1;
+}
+
+
+/*
+ * initialize
+ */
+
+static void init_midi_status(MidiStatus *st)
+{
+ clear_rpn();
+ coarsetune = 0;
+ finetune = 0;
+}
+
+
+/*
+ * RPN & NRPN
+ */
+
+#define MAX_MIDI_CHANNELS 16
+
+/* RPN & NRPN */
+static unsigned char nrpn[MAX_MIDI_CHANNELS]; /* current event is NRPN? */
+static int msb_bit; /* current event is msb for RPN/NRPN */
+/* RPN & NRPN indeces */
+static unsigned char rpn_msb[MAX_MIDI_CHANNELS], rpn_lsb[MAX_MIDI_CHANNELS];
+/* RPN & NRPN values */
+static int rpn_val[MAX_MIDI_CHANNELS];
+
+static void clear_rpn(void)
+{
+ int i;
+ for (i = 0; i < MAX_MIDI_CHANNELS; i++) {
+ nrpn[i] = 0;
+ rpn_msb[i] = 127;
+ rpn_lsb[i] = 127;
+ rpn_val[i] = 0;
+ }
+ msb_bit = 0;
+}
+
+
+/*
+ * process midi queue
+ */
+
+/* status event types */
+typedef void (*StatusEvent)(MidiStatus *st);
+static struct StatusEventList {
+ StatusEvent process;
+ int qlen;
+} status_event[8] = {
+ {midi_note_off, 2},
+ {midi_note_on, 2},
+ {midi_key_pressure, 2},
+ {midi_control_change, 2},
+ {midi_program_change, 1},
+ {midi_channel_pressure, 1},
+ {midi_pitch_wheel, 2},
+ {NULL, 0},
+};
+
+
+/* read a char from fifo and process it */
+static void get_midi_char(MidiStatus *st, int c)
+{
+ if (c == 0xfe) {
+ /* ignore active sense */
+ st->queue = Q_NONE;
+ return;
+ }
+
+ switch (st->queue) {
+ /* case Q_VARLEN: queue_varlen(st, c); break;*/
+ case Q_READ:
+ case Q_SYSEX:
+ queue_read(st, c);
+ break;
+ case Q_NONE:
+ st->read = 0;
+ if ((c & 0xf0) == 0xf0) {
+ special_event(st, c);
+ } else if (c & 0x80) { /* status change */
+ st->status = (c >> 4) & 0x07;
+ st->chan = c & 0x0f;
+ st->queue = Q_READ;
+ st->qlen = status_event[st->status].qlen;
+ if (st->qlen == 0)
+ st->queue = Q_NONE;
+ }
+ break;
+ }
+}
+
+/* 0xfx events */
+static void special_event(MidiStatus *st, int c)
+{
+ switch (c) {
+ case 0xf0: /* system exclusive */
+ st->queue = Q_SYSEX;
+ st->qlen = 0;
+ break;
+ case 0xf1: /* MTC quarter frame */
+ case 0xf3: /* song select */
+ st->queue = Q_READ;
+ st->qlen = 1;
+ break;
+ case 0xf2: /* song position */
+ st->queue = Q_READ;
+ st->qlen = 2;
+ break;
+ }
+}
+
+#if 0
+/* read variable length value */
+static void queue_varlen(MidiStatus *st, int c)
+{
+ st->qlen += (c & 0x7f);
+ if (c & 0x80) {
+ st->qlen <<= 7;
+ return;
+ }
+ if (st->qlen <= 0) {
+ st->qlen = 0;
+ st->queue = Q_NONE;
+ }
+ st->queue = Q_READ;
+ st->read = 0;
+}
+#endif
+
+
+/* read a char */
+static void queue_read(MidiStatus *st, int c)
+{
+ if (st->read < MAX_MIDIBUF) {
+ if (st->queue != Q_SYSEX)
+ c &= 0x7f;
+ st->buf[st->read] = (unsigned char)c;
+ }
+ st->read++;
+ if (st->queue == Q_SYSEX && c == 0xf7) {
+ midi_system_exclusive(st);
+ st->queue = Q_NONE;
+ } else if (st->queue == Q_READ && st->read >= st->qlen) {
+ if (status_event[st->status].process)
+ status_event[st->status].process(st);
+ st->queue = Q_NONE;
+ }
+}
+
+
+/*
+ * status events
+ */
+
+/* note on */
+static void midi_note_on(MidiStatus *st)
+{
+ DEBUG(2,printk("midi: note_on (%d) %d %d\n", st->chan, st->buf[0], st->buf[1]));
+ if (st->buf[1] == 0)
+ midi_note_off(st);
+ else
+ awe_start_note(0, st->chan, st->buf[0], st->buf[1]);
+}
+
+/* note off */
+static void midi_note_off(MidiStatus *st)
+{
+ DEBUG(2,printk("midi: note_off (%d) %d %d\n", st->chan, st->buf[0], st->buf[1]));
+ awe_kill_note(0, st->chan, st->buf[0], st->buf[1]);
+}
+
+/* key pressure change */
+static void midi_key_pressure(MidiStatus *st)
+{
+ awe_key_pressure(0, st->chan, st->buf[0], st->buf[1]);
+}
+
+/* channel pressure change */
+static void midi_channel_pressure(MidiStatus *st)
+{
+ channels[st->chan].chan_press = st->buf[0];
+ awe_modwheel_change(st->chan, st->buf[0]);
+}
+
+/* pitch wheel change */
+static void midi_pitch_wheel(MidiStatus *st)
+{
+ int val = (int)st->buf[1] * 128 + st->buf[0];
+ awe_bender(0, st->chan, val);
+}
+
+/* program change */
+static void midi_program_change(MidiStatus *st)
+{
+ int preset;
+ preset = st->buf[0];
+ if (midi_mode == MODE_GS && IS_DRUM_CHANNEL(st->chan) && preset == 127)
+ preset = 0;
+ else if (midi_mode == MODE_XG && xg_mapping && IS_DRUM_CHANNEL(st->chan))
+ preset += 64;
+
+ awe_set_instr(0, st->chan, preset);
+}
+
+#define send_effect(chan,type,val) awe_send_effect(chan,-1,type,val)
+#define add_effect(chan,type,val) awe_send_effect(chan,-1,(type)|0x80,val)
+#define unset_effect(chan,type) awe_send_effect(chan,-1,(type)|0x40,0)
+
+/* midi control change */
+static void midi_control_change(MidiStatus *st)
+{
+ int cmd = st->buf[0];
+ int val = st->buf[1];
+
+ DEBUG(2,printk("midi: control (%d) %d %d\n", st->chan, cmd, val));
+ if (midi_mode == MODE_XG) {
+ if (xg_control_change(st, cmd, val))
+ return;
+ }
+
+ /* controls #31 - #64 are LSB of #0 - #31 */
+ msb_bit = 1;
+ if (cmd >= 0x20 && cmd < 0x40) {
+ msb_bit = 0;
+ cmd -= 0x20;
+ }
+
+ switch (cmd) {
+ case CTL_SOFT_PEDAL:
+ if (val == 127)
+ add_effect(st->chan, AWE_FX_CUTOFF, -160);
+ else
+ unset_effect(st->chan, AWE_FX_CUTOFF);
+ break;
+
+ case CTL_BANK_SELECT:
+ midi_select_bank(st, val);
+ break;
+
+ /* set RPN/NRPN parameter */
+ case CTL_REGIST_PARM_NUM_MSB:
+ nrpn[st->chan]=0; rpn_msb[st->chan]=val;
+ break;
+ case CTL_REGIST_PARM_NUM_LSB:
+ nrpn[st->chan]=0; rpn_lsb[st->chan]=val;
+ break;
+ case CTL_NONREG_PARM_NUM_MSB:
+ nrpn[st->chan]=1; rpn_msb[st->chan]=val;
+ break;
+ case CTL_NONREG_PARM_NUM_LSB:
+ nrpn[st->chan]=1; rpn_lsb[st->chan]=val;
+ break;
+
+ /* send RPN/NRPN entry */
+ case CTL_DATA_ENTRY:
+ if (msb_bit)
+ rpn_val[st->chan] = val * 128;
+ else
+ rpn_val[st->chan] |= val;
+ if (nrpn[st->chan])
+ midi_nrpn_event(st);
+ else
+ midi_rpn_event(st);
+ break;
+
+ /* increase/decrease data entry */
+ case CTL_DATA_INCREMENT:
+ rpn_val[st->chan]++;
+ midi_rpn_event(st);
+ break;
+ case CTL_DATA_DECREMENT:
+ rpn_val[st->chan]--;
+ midi_rpn_event(st);
+ break;
+
+ /* default */
+ default:
+ awe_controller(0, st->chan, cmd, val);
+ break;
+ }
+}
+
+/* tone bank change */
+static void midi_select_bank(MidiStatus *st, int val)
+{
+ if (midi_mode == MODE_XG && msb_bit) {
+ xg_bankmode = val;
+ /* XG MSB value; not normal bank selection */
+ switch (val) {
+ case 127: /* remap to drum channel */
+ awe_controller(0, st->chan, CTL_BANK_SELECT, 128);
+ break;
+ default: /* remap to normal channel */
+ awe_controller(0, st->chan, CTL_BANK_SELECT, val);
+ break;
+ }
+ return;
+ } else if (midi_mode == MODE_GS && !msb_bit)
+ /* ignore LSB bank in GS mode (used for mapping) */
+ return;
+
+ /* normal bank controls; accept both MSB and LSB */
+ if (! IS_DRUM_CHANNEL(st->chan)) {
+ if (midi_mode == MODE_XG) {
+ if (xg_bankmode) return;
+ if (val == 64 || val == 126)
+ val = 0;
+ } else if (midi_mode == MODE_GS && val == 127)
+ val = 0;
+ awe_controller(0, st->chan, CTL_BANK_SELECT, val);
+ }
+}
+
+
+/*================================================================
+ * RPN events
+ *================================================================*/
+
+static void midi_rpn_event(MidiStatus *st)
+{
+ int type;
+ type = (rpn_msb[st->chan]<<8) | rpn_lsb[st->chan];
+ switch (type) {
+ case 0x0000: /* Pitch bend sensitivity */
+ /* MSB only / 1 semitone per 128 */
+ if (msb_bit) {
+ channels[st->chan].bender_range =
+ rpn_val[st->chan] * 100 / 128;
+ }
+ break;
+
+ case 0x0001: /* fine tuning: */
+ /* MSB/LSB, 8192=center, 100/8192 cent step */
+ finetune = rpn_val[st->chan] - 8192;
+ midi_detune(st->chan, coarsetune, finetune);
+ break;
+
+ case 0x0002: /* coarse tuning */
+ /* MSB only / 8192=center, 1 semitone per 128 */
+ if (msb_bit) {
+ coarsetune = rpn_val[st->chan] - 8192;
+ midi_detune(st->chan, coarsetune, finetune);
+ }
+ break;
+
+ case 0x7F7F: /* "lock-in" RPN */
+ break;
+ }
+}
+
+
+/* tuning:
+ * coarse = -8192 to 8192 (100 cent per 128)
+ * fine = -8192 to 8192 (max=100cent)
+ */
+static void midi_detune(int chan, int coarse, int fine)
+{
+ /* 4096 = 1200 cents in AWE parameter */
+ int val;
+ val = coarse * 4096 / (12 * 128);
+ val += fine / 24;
+ if (val)
+ send_effect(chan, AWE_FX_INIT_PITCH, val);
+ else
+ unset_effect(chan, AWE_FX_INIT_PITCH);
+}
+
+
+/*================================================================
+ * system exclusive message
+ * GM/GS/XG macros are accepted
+ *================================================================*/
+
+static void midi_system_exclusive(MidiStatus *st)
+{
+ /* GM on */
+ static unsigned char gm_on_macro[] = {
+ 0x7e,0x7f,0x09,0x01,
+ };
+ /* XG on */
+ static unsigned char xg_on_macro[] = {
+ 0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,
+ };
+ /* GS prefix
+ * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off
+ * reverb mode: XX=0x01, YY=0x30, ZZ=0-7
+ * chorus mode: XX=0x01, YY=0x38, ZZ=0-7
+ */
+ static unsigned char gs_pfx_macro[] = {
+ 0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/
+ };
+
+#if 0
+ /* SC88 system mode set
+ * single module mode: XX=1
+ * double module mode: XX=0
+ */
+ static unsigned char gs_mode_macro[] = {
+ 0x41,0x10,0x42,0x12,0x00,0x00,0x7F,/*ZZ*/
+ };
+ /* SC88 display macro: XX=01:bitmap, 00:text
+ */
+ static unsigned char gs_disp_macro[] = {
+ 0x41,0x10,0x45,0x12,0x10,/*XX,00*/
+ };
+#endif
+
+ /* GM on */
+ if (memcmp(st->buf, gm_on_macro, sizeof(gm_on_macro)) == 0) {
+ if (midi_mode != MODE_GS && midi_mode != MODE_XG)
+ midi_mode = MODE_GM;
+ init_midi_status(st);
+ }
+
+ /* GS macros */
+ else if (memcmp(st->buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) {
+ if (midi_mode != MODE_GS && midi_mode != MODE_XG)
+ midi_mode = MODE_GS;
+
+ if (st->buf[5] == 0x00 && st->buf[6] == 0x7f && st->buf[7] == 0x00) {
+ /* GS reset */
+ init_midi_status(st);
+ }
+
+ else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x15) {
+ /* drum pattern */
+ int p = st->buf[5] & 0x0f;
+ if (p == 0) p = 9;
+ else if (p < 10) p--;
+ if (st->buf[7] == 0)
+ DRUM_CHANNEL_OFF(p);
+ else
+ DRUM_CHANNEL_ON(p);
+
+ } else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x21) {
+ /* program */
+ int p = st->buf[5] & 0x0f;
+ if (p == 0) p = 9;
+ else if (p < 10) p--;
+ if (! IS_DRUM_CHANNEL(p))
+ awe_set_instr(0, p, st->buf[7]);
+
+ } else if (st->buf[5] == 0x01 && st->buf[6] == 0x30) {
+ /* reverb mode */
+ awe_set_reverb_mode(st->buf[7]);
+
+ } else if (st->buf[5] == 0x01 && st->buf[6] == 0x38) {
+ /* chorus mode */
+ awe_set_chorus_mode(st->buf[7]);
+
+ } else if (st->buf[5] == 0x00 && st->buf[6] == 0x04) {
+ /* master volume */
+ awe_change_master_volume(st->buf[7]);
+
+ }
+ }
+
+ /* XG on */
+ else if (memcmp(st->buf, xg_on_macro, sizeof(xg_on_macro)) == 0) {
+ midi_mode = MODE_XG;
+ xg_mapping = TRUE;
+ xg_bankmode = 0;
+ }
+}
+
+
+/*
+ * convert NRPN/control values
+ */
+
+static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val)
+{
+ int i, cval;
+ for (i = 0; i < num_tables; i++) {
+ if (table[i].control == type) {
+ cval = table[i].convert(val);
+ send_effect(st->chan, table[i].awe_effect, cval);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val)
+{
+ int i, cval;
+ for (i = 0; i < num_tables; i++) {
+ if (table[i].control == type) {
+ cval = table[i].convert(val);
+ add_effect(st->chan, table[i].awe_effect|0x80, cval);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/*
+ * AWE32 NRPN effects
+ */
+
+static unsigned short fx_delay(int val);
+static unsigned short fx_attack(int val);
+static unsigned short fx_hold(int val);
+static unsigned short fx_decay(int val);
+static unsigned short fx_the_value(int val);
+static unsigned short fx_twice_value(int val);
+static unsigned short fx_conv_pitch(int val);
+static unsigned short fx_conv_Q(int val);
+
+/* function for each NRPN */ /* [range] units */
+#define fx_env1_delay fx_delay /* [0,5900] 4msec */
+#define fx_env1_attack fx_attack /* [0,5940] 1msec */
+#define fx_env1_hold fx_hold /* [0,8191] 1msec */
+#define fx_env1_decay fx_decay /* [0,5940] 4msec */
+#define fx_env1_release fx_decay /* [0,5940] 4msec */
+#define fx_env1_sustain fx_the_value /* [0,127] 0.75dB */
+#define fx_env1_pitch fx_the_value /* [-127,127] 9.375cents */
+#define fx_env1_cutoff fx_the_value /* [-127,127] 56.25cents */
+
+#define fx_env2_delay fx_delay /* [0,5900] 4msec */
+#define fx_env2_attack fx_attack /* [0,5940] 1msec */
+#define fx_env2_hold fx_hold /* [0,8191] 1msec */
+#define fx_env2_decay fx_decay /* [0,5940] 4msec */
+#define fx_env2_release fx_decay /* [0,5940] 4msec */
+#define fx_env2_sustain fx_the_value /* [0,127] 0.75dB */
+
+#define fx_lfo1_delay fx_delay /* [0,5900] 4msec */
+#define fx_lfo1_freq fx_twice_value /* [0,127] 84mHz */
+#define fx_lfo1_volume fx_twice_value /* [0,127] 0.1875dB */
+#define fx_lfo1_pitch fx_the_value /* [-127,127] 9.375cents */
+#define fx_lfo1_cutoff fx_twice_value /* [-64,63] 56.25cents */
+
+#define fx_lfo2_delay fx_delay /* [0,5900] 4msec */
+#define fx_lfo2_freq fx_twice_value /* [0,127] 84mHz */
+#define fx_lfo2_pitch fx_the_value /* [-127,127] 9.375cents */
+
+#define fx_init_pitch fx_conv_pitch /* [-8192,8192] cents */
+#define fx_chorus fx_the_value /* [0,255] -- */
+#define fx_reverb fx_the_value /* [0,255] -- */
+#define fx_cutoff fx_twice_value /* [0,127] 62Hz */
+#define fx_filterQ fx_conv_Q /* [0,127] -- */
+
+static unsigned short fx_delay(int val)
+{
+ return (unsigned short)calc_parm_delay(val);
+}
+
+static unsigned short fx_attack(int val)
+{
+ return (unsigned short)calc_parm_attack(val);
+}
+
+static unsigned short fx_hold(int val)
+{
+ return (unsigned short)calc_parm_hold(val);
+}
+
+static unsigned short fx_decay(int val)
+{
+ return (unsigned short)calc_parm_decay(val);
+}
+
+static unsigned short fx_the_value(int val)
+{
+ return (unsigned short)(val & 0xff);
+}
+
+static unsigned short fx_twice_value(int val)
+{
+ return (unsigned short)((val * 2) & 0xff);
+}
+
+static unsigned short fx_conv_pitch(int val)
+{
+ return (short)(val * 4096 / 1200);
+}
+
+static unsigned short fx_conv_Q(int val)
+{
+ return (unsigned short)((val / 8) & 0xff);
+}
+
+
+static ConvTable awe_effects[] =
+{
+ { 0, AWE_FX_LFO1_DELAY, fx_lfo1_delay},
+ { 1, AWE_FX_LFO1_FREQ, fx_lfo1_freq},
+ { 2, AWE_FX_LFO2_DELAY, fx_lfo2_delay},
+ { 3, AWE_FX_LFO2_FREQ, fx_lfo2_freq},
+
+ { 4, AWE_FX_ENV1_DELAY, fx_env1_delay},
+ { 5, AWE_FX_ENV1_ATTACK,fx_env1_attack},
+ { 6, AWE_FX_ENV1_HOLD, fx_env1_hold},
+ { 7, AWE_FX_ENV1_DECAY, fx_env1_decay},
+ { 8, AWE_FX_ENV1_SUSTAIN, fx_env1_sustain},
+ { 9, AWE_FX_ENV1_RELEASE, fx_env1_release},
+
+ {10, AWE_FX_ENV2_DELAY, fx_env2_delay},
+ {11, AWE_FX_ENV2_ATTACK, fx_env2_attack},
+ {12, AWE_FX_ENV2_HOLD, fx_env2_hold},
+ {13, AWE_FX_ENV2_DECAY, fx_env2_decay},
+ {14, AWE_FX_ENV2_SUSTAIN, fx_env2_sustain},
+ {15, AWE_FX_ENV2_RELEASE, fx_env2_release},
+
+ {16, AWE_FX_INIT_PITCH, fx_init_pitch},
+ {17, AWE_FX_LFO1_PITCH, fx_lfo1_pitch},
+ {18, AWE_FX_LFO2_PITCH, fx_lfo2_pitch},
+ {19, AWE_FX_ENV1_PITCH, fx_env1_pitch},
+ {20, AWE_FX_LFO1_VOLUME, fx_lfo1_volume},
+ {21, AWE_FX_CUTOFF, fx_cutoff},
+ {22, AWE_FX_FILTERQ, fx_filterQ},
+ {23, AWE_FX_LFO1_CUTOFF, fx_lfo1_cutoff},
+ {24, AWE_FX_ENV1_CUTOFF, fx_env1_cutoff},
+ {25, AWE_FX_CHORUS, fx_chorus},
+ {26, AWE_FX_REVERB, fx_reverb},
+};
+
+static int num_awe_effects = numberof(awe_effects);
+
+
+/*
+ * GS(SC88) NRPN effects; still experimental
+ */
+
+/* cutoff: quarter semitone step, max=255 */
+static unsigned short gs_cutoff(int val)
+{
+ return (val - 64) * gs_sense[FX_CUTOFF] / 50;
+}
+
+/* resonance: 0 to 15(max) */
+static unsigned short gs_filterQ(int val)
+{
+ return (val - 64) * gs_sense[FX_RESONANCE] / 50;
+}
+
+/* attack: */
+static unsigned short gs_attack(int val)
+{
+ return -(val - 64) * gs_sense[FX_ATTACK] / 50;
+}
+
+/* decay: */
+static unsigned short gs_decay(int val)
+{
+ return -(val - 64) * gs_sense[FX_RELEASE] / 50;
+}
+
+/* release: */
+static unsigned short gs_release(int val)
+{
+ return -(val - 64) * gs_sense[FX_RELEASE] / 50;
+}
+
+/* vibrato freq: 0.042Hz step, max=255 */
+static unsigned short gs_vib_rate(int val)
+{
+ return (val - 64) * gs_sense[FX_VIBRATE] / 50;
+}
+
+/* vibrato depth: max=127, 1 octave */
+static unsigned short gs_vib_depth(int val)
+{
+ return (val - 64) * gs_sense[FX_VIBDEPTH] / 50;
+}
+
+/* vibrato delay: -0.725msec step */
+static unsigned short gs_vib_delay(int val)
+{
+ return -(val - 64) * gs_sense[FX_VIBDELAY] / 50;
+}
+
+static ConvTable gs_effects[] =
+{
+ {32, AWE_FX_CUTOFF, gs_cutoff},
+ {33, AWE_FX_FILTERQ, gs_filterQ},
+ {99, AWE_FX_ENV2_ATTACK, gs_attack},
+ {100, AWE_FX_ENV2_DECAY, gs_decay},
+ {102, AWE_FX_ENV2_RELEASE, gs_release},
+ {8, AWE_FX_LFO1_FREQ, gs_vib_rate},
+ {9, AWE_FX_LFO1_VOLUME, gs_vib_depth},
+ {10, AWE_FX_LFO1_DELAY, gs_vib_delay},
+};
+
+static int num_gs_effects = numberof(gs_effects);
+
+
+/*================================================================
+ * NRPN events: accept as AWE32/SC88 specific controls
+ *================================================================*/
+
+static void midi_nrpn_event(MidiStatus *st)
+{
+ if (rpn_msb[st->chan] == 127 && rpn_lsb[st->chan] <= 26) {
+ if (! msb_bit) /* both MSB/LSB necessary */
+ send_converted_effect(awe_effects, num_awe_effects,
+ st, rpn_lsb[st->chan],
+ rpn_val[st->chan] - 8192);
+ } else if (rpn_msb[st->chan] == 1) {
+ if (msb_bit) /* only MSB is valid */
+ add_converted_effect(gs_effects, num_gs_effects,
+ st, rpn_lsb[st->chan],
+ rpn_val[st->chan] / 128);
+ }
+}
+
+
+/*----------------------------------------------------------------
+ * XG control effects; still experimental
+ *----------------------------------------------------------------*/
+
+/* cutoff: quarter semitone step, max=255 */
+static unsigned short xg_cutoff(int val)
+{
+ return (val - 64) * xg_sense[FX_CUTOFF] / 64;
+}
+
+/* resonance: 0(open) to 15(most nasal) */
+static unsigned short xg_filterQ(int val)
+{
+ return (val - 64) * xg_sense[FX_RESONANCE] / 64;
+}
+
+/* attack: */
+static unsigned short xg_attack(int val)
+{
+ return -(val - 64) * xg_sense[FX_ATTACK] / 64;
+}
+
+/* release: */
+static unsigned short xg_release(int val)
+{
+ return -(val - 64) * xg_sense[FX_RELEASE] / 64;
+}
+
+static ConvTable xg_effects[] =
+{
+ {71, AWE_FX_CUTOFF, xg_cutoff},
+ {74, AWE_FX_FILTERQ, xg_filterQ},
+ {72, AWE_FX_ENV2_RELEASE, xg_release},
+ {73, AWE_FX_ENV2_ATTACK, xg_attack},
+};
+
+static int num_xg_effects = numberof(xg_effects);
+
+static int xg_control_change(MidiStatus *st, int cmd, int val)
+{
+ return add_converted_effect(xg_effects, num_xg_effects, st, cmd, val);
+}
+
+#endif /* CONFIG_AWE32_MIDIEMU */
+
+/* new type interface */
+static int __init attach_awe(void)
+{
+#ifdef CONFIG_PNP_DRV
+ if (pnp) {
+ awe_initpnp();
+ if (awe_pnp_ok)
+ return 0;
+ }
+#endif /* pnp */
+
+ _attach_awe();
+
+ return 0;
+}
+
+static void __exit unload_awe(void)
+{
+#ifdef CONFIG_PNP_DRV
+ if (pnp)
+ awe_unload_pnp();
+#endif
+
+ _unload_awe();
+}
+
+module_init(attach_awe);
+module_exit(unload_awe);
+
+#ifndef MODULE
+static int __init setup_awe(char *str)
+{
+ /* io, memsize */
+ int ints[3];
+
+ str = get_options(str, ARRAY_SIZE(ints), ints);
+
+ io = ints[1];
+ memsize = ints[2];
+
+ return 1;
+}
+
+__setup("awe=", setup_awe);
+#endif
--- /dev/null
+/*
+ * sound/awe_config.h
+ *
+ * Configuration of AWE32/SB32/AWE64 wave table synth driver.
+ * version 0.4.3; Mar. 1, 1998
+ *
+ * Copyright (C) 1996-1998 Takashi Iwai
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * chorus & reverb effects send for FM chip: from 0 to 0xff
+ * larger numbers often cause weird sounds.
+ */
+
+#define DEF_FM_CHORUS_DEPTH 0x10
+#define DEF_FM_REVERB_DEPTH 0x10
+
+
+/*
+ * other compile conditions
+ */
+
+/* initialize FM passthrough even without extended RAM */
+#undef AWE_ALWAYS_INIT_FM
+
+/* debug on */
+#define AWE_DEBUG_ON
+
+/* GUS compatible mode */
+#define AWE_HAS_GUS_COMPATIBILITY
+
+/* add MIDI emulation by wavetable */
+#define CONFIG_AWE32_MIDIEMU
+
+/* add mixer control of emu8000 equalizer */
+#undef CONFIG_AWE32_MIXER
+
+/* use new volume calculation method as default */
+#define AWE_USE_NEW_VOLUME_CALC
+
+/* check current volume target for searching empty voices */
+#define AWE_CHECK_VTARGET
+
+/* allow sample sharing */
+#define AWE_ALLOW_SAMPLE_SHARING
+
+/*
+ * AWE32 card configuration:
+ * uncomment the following lines *ONLY* when auto detection doesn't
+ * work properly on your machine.
+ */
+
+/*#define AWE_DEFAULT_BASE_ADDR 0x620*/ /* base port address */
+/*#define AWE_DEFAULT_MEM_SIZE 512*/ /* kbytes */
+
+/*
+ * maximum size of soundfont list table
+ */
+
+#define AWE_MAX_SF_LISTS 16
+
+/*
+ * chunk size of sample and voice tables
+ */
+
+#define AWE_MAX_SAMPLES 400
+#define AWE_MAX_INFOS 800
+
+#define AWE_MAJOR_VERSION 0
+#define AWE_MINOR_VERSION 4
+#define AWE_TINY_VERSION 3
+#define AWE_VERSION_NUMBER ((AWE_MAJOR_VERSION<<16)|(AWE_MINOR_VERSION<<8)|AWE_TINY_VERSION)
+#define AWEDRV_VERSION "0.4.3"
+++ /dev/null
-dep_tristate ' ACI mixer (miroPCM12)' CONFIG_ACI_MIXER $CONFIG_SOUND_OSS
-
-dep_tristate ' AWE32 synth' CONFIG_AWE32_SYNTH $CONFIG_SOUND_OSS
-
-if [ "$CONFIG_AWE32_SYNTH" = "y" -o "$CONFIG_AWE32_SYNTH" = "m" ]; then
- comment 'AWE32 PnP-ISA Cards are not always setup correctly'
- bool 'Configure AWE32 synth Base Address and Default Memory Size' CONFIG_AWE32_SYNTH_DEFAULTS
- if [ "$CONFIG_AWE32_SYNTH_DEFAULTS" = "y" ]; then
- hex 'AWE32 synth Base Address 620' AWE_DEFAULT_BASE_ADDR 620
- int 'AWE32 synth Default Memory Size 512 1024 or 4096' AWE_DEFAULT_MEM_SIZE 512
- fi
-fi
-
-if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND" = "m" ]; then
- dep_tristate ' Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16 $CONFIG_SOUND_OSS
- if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then
- hex 'I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220
- hex 'I/O base for MPU401 Check from manual of the card' CONFIG_MPU_BASE 330
- fi
-
- if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then
- comment 'SC-6600 Audio Cards have no jumper switches at all'
- bool ' SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600
- if [ "$CONFIG_SC6600" = "y" ]; then
- comment 'SC-6600 specific configuration'
- bool ' Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY
- int 'SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' CONFIG_SC6600_CDROM 4
- hex 'SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0
- fi
-
- if [ "$CONFIG_SOUND_SB" = "y" -o "$CONFIG_SOUND_SB" = "m" ]; then
- if [ "$CONFIG_AEDSP16_MSS" != "y" ]; then
- bool 'Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO
- if [ "$CONFIG_AEDSP16_SBPRO" = "y" ]; then
- comment 'Audio Excel DSP 16 [Sound Blaster Pro]'
- hex 'I/O base for Audio Excel DSP 16 220, 240' CONFIG_AEDSP16_BASE $CONFIG_SB_BASE 220
- int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' CONFIG_AEDSP16_SB_IRQ $CONFIG_SB_IRQ 5
- int 'Audio Excel DSP 16 DMA 0, 1 or 3' CONFIG_AEDSP16_SB_DMA $CONFIG_SB_DMA 0
- fi
- fi
- fi
-
- if [ "$CONFIG_SOUND_MSS" = "y" -o "$CONFIG_SOUND_MSS" = "m" ]; then
- if [ "$CONFIG_AEDSP16_SBPRO" != "y" ]; then
- bool ' Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS
- if [ "$CONFIG_AEDSP16_MSS" = "y" ]; then
- comment 'Audio Excel DSP 16 [Microsoft Sound System]'
- hex 'I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220
- int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' CONFIG_AEDSP16_MSS_IRQ $CONFIG_MSS_IRQ 5
- int 'Audio Excel DSP 16 DMA 0, 1 or 3' CONFIG_AEDSP16_MSS_DMA $CONFIG_MSS_DMA 1
- fi
- fi
- fi
-
- if [ "$CONFIG_SOUND_MPU401" = "y" -o "$CONFIG_SOUND_MPU401" = "m" ]; then
- bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
- if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then
- comment 'Audio Excel DSP 16 [MPU-401]'
- if [ "$CONFIG_AEDSP16_SBPRO" != "y" -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
- hex 'I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220
- fi
- int 'MPU401 IRQ for Audio Excel DSP 16 5, 7, 9, 10 or 0 (disable)' CONFIG_AEDSP16_MPU_IRQ $CONFIG_MPU_IRQ
- fi
- fi
- fi
-fi
+++ /dev/null
-# Makefile for the Linux low-level sound card drivers.
-#
-# 11 Feb 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
-# Rewritten to use lists instead of if statements.
-
-export-objs := soundlow.o
-
-list-y :=
-list-m :=
-list-n :=
-list- :=
-
-obj-$(CONFIG_SOUND_OSS) += soundlow.o
-obj-$(CONFIG_ACI_MIXER) += aci.o
-obj-$(CONFIG_AEDSP16) += aedsp16.o
-obj-$(CONFIG_AWE32_SYNTH) += awe_wave.o
-
-O_TARGET := lowlevel.o
-O_OBJS := $(sort $(filter-out $(export-objs), $(obj-y)))
-OX_OBJS := $(sort $(filter $(export-objs), $(obj-y)))
-M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
-MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
-
-include $(TOPDIR)/Rules.make
+++ /dev/null
-Additional low level sound drivers for Linux
-============================================
-
-This directory contains some low level sound drivers.
-These drivers are used to be (when Linux sound drivers was OSS/Lite) external
-drivers, not maintained by Hannu Savolainen and not touched by him.
-Now things are changed: the new Linux sound driver code maintained by Alan Cox
-include these lowlevel drivers and they are no more neglected (thanks Alan).
-
-The following low level drivers are included:
-
-- ACI MIXER for miroPCM12 by Markus Kuhn
- (mskuhn@cip.informatik.uni-erlangen.de).
-- Audio Excel DSP 16 initialization driver by Riccardo Facchetti
- (fizban@tin.it)
-- SB32/AWE synthesizer driver (Emu8000) by Takashi Iwai
- (iwai@dragon.mm.t.u-tokyo.ac.jp).
-
-You can find documentation for these drivers in the Documentation/sound
-directory.
-
-[ File edited 17.01.1999 - Riccardo Facchetti ]
+++ /dev/null
-/*
- * Audio Command Interface (ACI) driver (sound/aci.c)
- *
- * ACI is a protocol used to communicate with the microcontroller on
- * some sound cards produced by miro, e.g. the miroSOUND PCM12 and
- * PCM20. The ACI has been developed for miro by Norberto Pellicci
- * <pellicci@home.com>. Special thanks to both him and miro for
- * providing the ACI specification.
- *
- * The main function of the ACI is to control the mixer and to get a
- * product identification. On the PCM20, ACI also controls the radio
- * tuner on this card, this is supported in the Video for Linux
- * radio-miropcm20 driver.
- *
- * This Voxware ACI driver currently only supports the ACI functions
- * on the miroSOUND PCM12 and PCM20 card. Support for miro sound cards
- * with additional ACI functions can easily be added later.
- *
- * / NOTE / When compiling as a module, make sure to load the module
- * after loading the mad16 module. The initialisation code expects the
- * MAD16 default mixer to be already available.
- *
- * / NOTE / When compiling as a module, make sure to load the module
- * after loading the mad16 module. The initialisation code expects the
- * MAD16 default mixer to be already available.
- *
- * Revision history:
- *
- * 1995-11-10 Markus Kuhn <mskuhn@cip.informatik.uni-erlangen.de>
- * First version written.
- * 1995-12-31 Markus Kuhn
- * Second revision, general code cleanup.
- * 1996-05-16 Hannu Savolainen
- * Integrated with other parts of the driver.
- * 1996-05-28 Markus Kuhn
- * Initialize CS4231A mixer, make ACI first mixer,
- * use new private mixer API for solo mode.
- * 1998-08-18 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
- * Small modification to export ACI functions and
- * complete modularisation.
- */
-
-/*
- * Some driver specific information and features:
- *
- * This mixer driver identifies itself to applications as "ACI" in
- * mixer_info.id as retrieved by ioctl(fd, SOUND_MIXER_INFO, &mixer_info).
- *
- * Proprietary mixer features that go beyond the standard OSS mixer
- * interface are:
- *
- * Full duplex solo configuration:
- *
- * int solo_mode;
- * ioctl(fd, SOUND_MIXER_PRIVATE1, &solo_mode);
- *
- * solo_mode = 0: deactivate solo mode (default)
- * solo_mode > 0: activate solo mode
- * With activated solo mode, the PCM input can not any
- * longer hear the signals produced by the PCM output.
- * Activating solo mode is important in duplex mode in order
- * to avoid feedback distortions.
- * solo_mode < 0: do not change solo mode (just retrieve the status)
- *
- * When the ioctl() returns 0, solo_mode contains the previous
- * status (0 = deactivated, 1 = activated). If solo mode is not
- * implemented on this card, ioctl() returns -1 and sets errno to
- * EINVAL.
- *
- */
-
-#include <linux/config.h> /* for CONFIG_ACI_MIXER */
-#include <linux/module.h>
-#include "lowlevel.h"
-#include "../sound_config.h"
-
-#if defined(CONFIG_ACI_MIXER) || defined(CONFIG_ACI_MIXER_MODULE)
-
-#undef DEBUG /* if defined, produce a verbose report via syslog */
-
-int aci_port = 0x354; /* as determined by bit 4 in the OPTi 929 MC4 register */
-unsigned char aci_idcode[2] = {0, 0}; /* manufacturer and product ID */
-unsigned char aci_version = 0; /* ACI firmware version */
-int aci_solo; /* status bit of the card that can't be *
- * checked with ACI versions prior to 0xb0 */
-
-static int aci_present = 0;
-
-#ifdef MODULE /* Whether the aci mixer is to be reset. */
-int aci_reset = 0; /* Default: don't reset if the driver is a */
-MODULE_PARM(aci_reset,"i");
-#else /* module; use "insmod aci.o aci_reset=1" */
-int aci_reset = 1; /* to override. */
-#endif
-
-
-#define COMMAND_REGISTER (aci_port)
-#define STATUS_REGISTER (aci_port + 1)
-#define BUSY_REGISTER (aci_port + 2)
-
-/*
- * Wait until the ACI microcontroller has set the READYFLAG in the
- * Busy/IRQ Source Register to 0. This is required to avoid
- * overrunning the sound card microcontroller. We do a busy wait here,
- * because the microcontroller is not supposed to signal a busy
- * condition for more than a few clock cycles. In case of a time-out,
- * this function returns -1.
- *
- * This busy wait code normally requires less than 15 loops and
- * practically always less than 100 loops on my i486/DX2 66 MHz.
- *
- * Warning: Waiting on the general status flag after reseting the MUTE
- * function can take a VERY long time, because the PCM12 does some kind
- * of fade-in effect. For this reason, access to the MUTE function has
- * not been implemented at all.
- */
-
-static int busy_wait(void)
-{
- long timeout;
-
- for (timeout = 0; timeout < 10000000L; timeout++)
- if ((inb_p(BUSY_REGISTER) & 1) == 0)
- return 0;
-
-#ifdef DEBUG
- printk("ACI: READYFLAG timed out.\n");
-#endif
-
- return -1;
-}
-
-
-/*
- * Read the GENERAL STATUS register.
- */
-
-static int read_general_status(void)
-{
- unsigned long flags;
- int status;
-
- save_flags(flags);
- cli();
- if (busy_wait()) { restore_flags(flags); return -1; }
- status = (unsigned) inb_p(STATUS_REGISTER);
- restore_flags(flags);
- return status;
-}
-
-
-/*
- * The four ACI command types (implied, write, read and indexed) can
- * be sent to the microcontroller using the following four functions.
- * If a problem occurred, they return -1.
- */
-
-int aci_implied_cmd(unsigned char opcode)
-{
- unsigned long flags;
-
-#ifdef DEBUG
- printk("ACI: aci_implied_cmd(0x%02x)\n", opcode);
-#endif
-
- save_flags(flags);
- cli();
-
- if (read_general_status() < 0 || busy_wait()) {
- restore_flags(flags);
- return -1;
- }
- outb_p(opcode, COMMAND_REGISTER);
-
- restore_flags(flags);
- return 0;
-}
-
-
-int aci_write_cmd(unsigned char opcode, unsigned char parameter)
-{
- unsigned long flags;
- int status;
-
-#ifdef DEBUG
- printk("ACI: aci_write_cmd(0x%02x, 0x%02x)\n", opcode, parameter);
-#endif
-
- save_flags(flags);
- cli();
-
- if (read_general_status() < 0 || busy_wait()) {
- restore_flags(flags);
- return -1;
- }
- outb_p(opcode, COMMAND_REGISTER);
- if (busy_wait()) { restore_flags(flags); return -1; }
- outb_p(parameter, COMMAND_REGISTER);
-
- if ((status = read_general_status()) < 0) {
- restore_flags(flags);
- return -1;
- }
- /* polarity of the INVALID flag depends on ACI version */
- if ((aci_version < 0xb0 && (status & 0x40) != 0) ||
- (aci_version >= 0xb0 && (status & 0x40) == 0)) {
- restore_flags(flags);
- printk("ACI: invalid write command 0x%02x, 0x%02x.\n",
- opcode, parameter);
- return -1;
- }
-
- restore_flags(flags);
- return 0;
-}
-
-/*
- * This write command send 2 parameters instead of one.
- * Only used in PCM20 radio frequency tuning control
- */
-
-int aci_write_cmd_d(unsigned char opcode, unsigned char parameter, unsigned char parameter2)
-{
- unsigned long flags;
- int status;
-
-#ifdef DEBUG
- printk("ACI: aci_write_cmd_d(0x%02x, 0x%02x)\n", opcode, parameter, parameter2);
-#endif
-
- save_flags(flags);
- cli();
-
- if (read_general_status() < 0 || busy_wait()) {
- restore_flags(flags);
- return -1;
- }
- outb_p(opcode, COMMAND_REGISTER);
- if (busy_wait()) { restore_flags(flags); return -1; }
- outb_p(parameter, COMMAND_REGISTER);
- if (busy_wait()) { restore_flags(flags); return -1; }
- outb_p(parameter2, COMMAND_REGISTER);
-
- if ((status = read_general_status()) < 0) {
- restore_flags(flags);
- return -1;
- }
- /* polarity of the INVALID flag depends on ACI version */
- if ((aci_version < 0xb0 && (status & 0x40) != 0) ||
- (aci_version >= 0xb0 && (status & 0x40) == 0)) {
- restore_flags(flags);
-#if 0 /* Frequency tuning works, but the INVALID flag is set ??? */
- printk("ACI: invalid write (double) command 0x%02x, 0x%02x, 0x%02x.\n",
- opcode, parameter, parameter2);
-#endif
- return -1;
- }
-
- restore_flags(flags);
- return 0;
-}
-
-int aci_read_cmd(unsigned char opcode, int length, unsigned char *parameter)
-{
- unsigned long flags;
- int i = 0;
-
- save_flags(flags);
- cli();
-
- if (read_general_status() < 0) { restore_flags(flags); return -1; }
- while (i < length) {
- if (busy_wait()) { restore_flags(flags); return -1; }
- outb_p(opcode, COMMAND_REGISTER);
- if (busy_wait()) { restore_flags(flags); return -1; }
- parameter[i++] = inb_p(STATUS_REGISTER);
-#ifdef DEBUG
- if (i == 1)
- printk("ACI: aci_read_cmd(0x%02x, %d) = 0x%02x\n", opcode, length,
- parameter[i-1]);
- else
- printk("ACI: aci_read_cmd cont.: 0x%02x\n", parameter[i-1]);
-#endif
- }
-
- restore_flags(flags);
- return 0;
-}
-
-
-int aci_indexed_cmd(unsigned char opcode, unsigned char index,
- unsigned char *parameter)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- if (read_general_status() < 0 || busy_wait()) {
- restore_flags(flags);
- return -1;
- }
- outb_p(opcode, COMMAND_REGISTER);
- if (busy_wait()) { restore_flags(flags); return -1; }
- outb_p(index, COMMAND_REGISTER);
- if (busy_wait()) { restore_flags(flags); return -1; }
- *parameter = inb_p(STATUS_REGISTER);
-#ifdef DEBUG
- printk("ACI: aci_indexed_cmd(0x%02x, 0x%02x) = 0x%02x\n", opcode, index,
- *parameter);
-#endif
-
- restore_flags(flags);
- return 0;
-}
-
-
-/*
- * The following macro SCALE can be used to scale one integer volume
- * value into another one using only integer arithmetic. If the input
- * value x is in the range 0 <= x <= xmax, then the result will be in
- * the range 0 <= SCALE(xmax,ymax,x) <= ymax.
- *
- * This macro has for all xmax, ymax > 0 and all 0 <= x <= xmax the
- * following nice properties:
- *
- * - SCALE(xmax,ymax,xmax) = ymax
- * - SCALE(xmax,ymax,0) = 0
- * - SCALE(xmax,ymax,SCALE(ymax,xmax,SCALE(xmax,ymax,x))) = SCALE(xmax,ymax,x)
- *
- * In addition, the rounding error is minimal and nicely distributed.
- * The proofs are left as an exercise to the reader.
- */
-
-#define SCALE(xmax,ymax,x) (((x)*(ymax)+(xmax)/2)/(xmax))
-
-
-static int getvolume(caddr_t arg,
- unsigned char left_index, unsigned char right_index)
-{
- int vol;
- unsigned char buf;
-
- /* left channel */
- if (aci_indexed_cmd(0xf0, left_index, &buf)) return -EIO;
- vol = SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0);
- /* right channel */
- if (aci_indexed_cmd(0xf0, right_index, &buf)) return -EIO;
- vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8;
-
- return (*(int *) arg = vol);
-}
-
-
-static int setvolume(caddr_t arg,
- unsigned char left_index, unsigned char right_index)
-{
- int vol, ret;
-
- /* left channel */
- vol = *(int *)arg & 0xff;
- if (vol > 100) vol = 100;
- vol = SCALE(100, 0x20, vol);
- if (aci_write_cmd(left_index, 0x20 - vol)) return -EIO;
- ret = SCALE(0x20, 100, vol);
- /* right channel */
- vol = (*(int *)arg >> 8) & 0xff;
- if (vol > 100) vol = 100;
- vol = SCALE(100, 0x20, vol);
- if (aci_write_cmd(right_index, 0x20 - vol)) return -EIO;
- ret |= SCALE(0x20, 100, vol) << 8;
-
- return (*(int *) arg = ret);
-}
-
-
-static int
-aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
-{
- int status, vol;
- unsigned char buf;
-
- /* handle solo mode control */
- if (cmd == SOUND_MIXER_PRIVATE1) {
- if (*(int *) arg >= 0) {
- aci_solo = !!*(int *) arg;
- if (aci_write_cmd(0xd2, aci_solo)) return -EIO;
- } else if (aci_version >= 0xb0) {
- if ((status = read_general_status()) < 0) return -EIO;
- return (*(int *) arg = (status & 0x20) == 0);
- }
- return (*(int *) arg = aci_solo);
- }
-
- if (((cmd >> 8) & 0xff) == 'M') {
- if (cmd & IOC_IN)
- /* read and write */
- switch (cmd & 0xff) {
- case SOUND_MIXER_VOLUME:
- return setvolume(arg, 0x01, 0x00);
- case SOUND_MIXER_CD:
- return setvolume(arg, 0x3c, 0x34);
- case SOUND_MIXER_MIC:
- return setvolume(arg, 0x38, 0x30);
- case SOUND_MIXER_LINE:
- return setvolume(arg, 0x39, 0x31);
- case SOUND_MIXER_SYNTH:
- return setvolume(arg, 0x3b, 0x33);
- case SOUND_MIXER_PCM:
- return setvolume(arg, 0x3a, 0x32);
- case SOUND_MIXER_LINE1: /* AUX1 */
- return setvolume(arg, 0x3d, 0x35);
- case SOUND_MIXER_LINE2: /* AUX2 */
- return setvolume(arg, 0x3e, 0x36);
- case SOUND_MIXER_IGAIN: /* MIC pre-amp */
- vol = *(int *) arg & 0xff;
- if (vol > 100) vol = 100;
- vol = SCALE(100, 3, vol);
- if (aci_write_cmd(0x03, vol)) return -EIO;
- vol = SCALE(3, 100, vol);
- return (*(int *) arg = vol | (vol << 8));
- case SOUND_MIXER_RECSRC:
- return (*(int *) arg = 0);
- break;
- default:
- return -EINVAL;
- }
- else
- /* only read */
- switch (cmd & 0xff) {
- case SOUND_MIXER_DEVMASK:
- return (*(int *) arg =
- SOUND_MASK_VOLUME | SOUND_MASK_CD |
- SOUND_MASK_MIC | SOUND_MASK_LINE |
- SOUND_MASK_SYNTH | SOUND_MASK_PCM |
-#if 0
- SOUND_MASK_IGAIN |
-#endif
- SOUND_MASK_LINE1 | SOUND_MASK_LINE2);
- break;
- case SOUND_MIXER_STEREODEVS:
- return (*(int *) arg =
- SOUND_MASK_VOLUME | SOUND_MASK_CD |
- SOUND_MASK_MIC | SOUND_MASK_LINE |
- SOUND_MASK_SYNTH | SOUND_MASK_PCM |
- SOUND_MASK_LINE1 | SOUND_MASK_LINE2);
- break;
- case SOUND_MIXER_RECMASK:
- return (*(int *) arg = 0);
- break;
- case SOUND_MIXER_RECSRC:
- return (*(int *) arg = 0);
- break;
- case SOUND_MIXER_CAPS:
- return (*(int *) arg = 0);
- break;
- case SOUND_MIXER_VOLUME:
- return getvolume(arg, 0x04, 0x03);
- case SOUND_MIXER_CD:
- return getvolume(arg, 0x0a, 0x09);
- case SOUND_MIXER_MIC:
- return getvolume(arg, 0x06, 0x05);
- case SOUND_MIXER_LINE:
- return getvolume(arg, 0x08, 0x07);
- case SOUND_MIXER_SYNTH:
- return getvolume(arg, 0x0c, 0x0b);
- case SOUND_MIXER_PCM:
- return getvolume(arg, 0x0e, 0x0d);
- case SOUND_MIXER_LINE1: /* AUX1 */
- return getvolume(arg, 0x11, 0x10);
- case SOUND_MIXER_LINE2: /* AUX2 */
- return getvolume(arg, 0x13, 0x12);
- case SOUND_MIXER_IGAIN: /* MIC pre-amp */
- if (aci_indexed_cmd(0xf0, 0x21, &buf)) return -EIO;
- vol = SCALE(3, 100, buf <= 3 ? buf : 3);
- vol |= vol << 8;
- return (*(int *) arg = vol);
- default:
- return -EINVAL;
- }
- }
-
- return -EINVAL;
-}
-
-
-static struct mixer_operations aci_mixer_operations =
-{
- "ACI",
- "ACI mixer",
- aci_mixer_ioctl,
- NULL
-};
-
-static unsigned char
-mad_read (int port)
-{
- outb (0xE3, 0xf8f); /* Write MAD16 password */
- return inb (port); /* Read from port */
-}
-
-
-/*
- * Check, whether there actually is any ACI port operational and if
- * one was found, then initialize the ACI interface, reserve the I/O
- * addresses and attach the new mixer to the relevant VoxWare data
- * structures.
- *
- * Returns: 1 ACI mixer detected
- * 0 nothing there
- *
- * There is also an internal mixer in the codec (CS4231A or AD1845),
- * that deserves no purpose in an ACI based system which uses an
- * external ACI controlled stereo mixer. Make sure that this codec
- * mixer has the AUX1 input selected as the recording source, that the
- * input gain is set near maximum and that the other channels going
- * from the inputs to the codec output are muted.
- */
-
-int attach_aci(void)
-{
- char *boardname = "unknown";
- int volume;
-
-#define MC4_PORT 0xf90
-
- aci_port =
- (mad_read(MC4_PORT) & 0x10) ? 0x344 : 0x354;
-
- if (check_region(aci_port, 3)) {
-#ifdef DEBUG
- printk("ACI: I/O area 0x%03x-0x%03x already used.\n",
- aci_port, aci_port+2);
-#endif
- return 0;
- }
-
- if (aci_read_cmd(0xf2, 2, aci_idcode)) {
-#ifdef DEBUG
- printk("ACI: Failed to read idcode.\n");
-#endif
- return 0;
- }
- if (aci_read_cmd(0xf1, 1, &aci_version)) {
-#ifdef DEBUG
- printk("ACI: Failed to read version.\n");
-#endif
- return 0;
- }
-
- if (aci_idcode[0] == 0x6d) {
- /* It looks like a miro sound card. */
- switch (aci_idcode[1]) {
- case 0x41:
- boardname = "PCM1 pro / early PCM12";
- break;
- case 0x42:
- boardname = "PCM12";
- break;
- case 0x43:
- boardname = "PCM20";
- break;
- default:
- boardname = "unknown miro";
- }
- } else
-#ifndef DEBUG
- return 0;
-#endif
-
- printk("<ACI %02x, id %02x %02x (%s)> at 0x%03x\n",
- aci_version, aci_idcode[0], aci_idcode[1], boardname, aci_port);
-
- if (aci_reset) {
- /* initialize ACI mixer */
- aci_implied_cmd(0xff);
- aci_solo = 0;
- }
-
- /* attach the mixer */
- request_region(aci_port, 3, "sound mixer (ACI)");
- if (num_mixers < MAX_MIXER_DEV) {
- if (num_mixers > 0 &&
- !strncmp("MAD16 WSS", mixer_devs[num_mixers-1]->name, 9)) {
- /*
- * The previously registered mixer device is the CS4231A which
- * has no function on an ACI card. Make the ACI mixer the first
- * of the two mixer devices.
- */
- mixer_devs[num_mixers] = mixer_devs[num_mixers-1];
- mixer_devs[num_mixers-1] = &aci_mixer_operations;
- /*
- * Initialize the CS4231A mixer with reasonable values. It is
- * unlikely that the user ever will want to change these as all
- * channels can be mixed via ACI.
- */
- volume = 0x6464;
- mixer_devs[num_mixers]->
- ioctl(num_mixers, SOUND_MIXER_WRITE_PCM, (caddr_t) &volume);
- volume = 0x6464;
- mixer_devs[num_mixers]->
- ioctl(num_mixers, SOUND_MIXER_WRITE_IGAIN, (caddr_t) &volume);
- volume = 0;
- mixer_devs[num_mixers]->
- ioctl(num_mixers, SOUND_MIXER_WRITE_SPEAKER, (caddr_t) &volume);
- volume = 0;
- mixer_devs[num_mixers]->
- ioctl(num_mixers, SOUND_MIXER_WRITE_MIC, (caddr_t) &volume);
- volume = 0;
- mixer_devs[num_mixers]->
- ioctl(num_mixers, SOUND_MIXER_WRITE_IMIX, (caddr_t) &volume);
- volume = 0;
- mixer_devs[num_mixers]->
- ioctl(num_mixers, SOUND_MIXER_WRITE_LINE1, (caddr_t) &volume);
- volume = 0;
- mixer_devs[num_mixers]->
- ioctl(num_mixers, SOUND_MIXER_WRITE_LINE2, (caddr_t) &volume);
- volume = 0;
- mixer_devs[num_mixers]->
- ioctl(num_mixers, SOUND_MIXER_WRITE_LINE3, (caddr_t) &volume);
- volume = SOUND_MASK_LINE1;
- mixer_devs[num_mixers]->
- ioctl(num_mixers, SOUND_MIXER_WRITE_RECSRC, (caddr_t) &volume);
- num_mixers++;
- } else
- mixer_devs[num_mixers++] = &aci_mixer_operations;
- }
-
- /* Just do something; otherwise the first write command fails, at
- * least with my PCM20.
- */
- aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_READ_VOLUME, (caddr_t) &volume);
-
- if (aci_reset) {
- /* Initialize ACI mixer with reasonable power-up values */
- volume = 0x3232;
- aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_VOLUME, (caddr_t) &volume);
- volume = 0x3232;
- aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_SYNTH, (caddr_t) &volume);
- volume = 0x3232;
- aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_PCM, (caddr_t) &volume);
- volume = 0x3232;
- aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE, (caddr_t) &volume);
- volume = 0x3232;
- aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_MIC, (caddr_t) &volume);
- volume = 0x3232;
- aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_CD, (caddr_t) &volume);
- volume = 0x3232;
- aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE1, (caddr_t) &volume);
- volume = 0x3232;
- aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE2, (caddr_t) &volume);
- }
-
- aci_present = 1;
-
- return 1;
-}
-
-void unload_aci(void)
-{
- if (aci_present)
- release_region(aci_port, 3);
-}
-
-#endif
-
-#if defined(MODULE)
-
-int init_module(void) {
- attach_aci();
- return(0);
-}
-
-void cleanup_module(void) {
- unload_aci();
-}
-
-#endif /* MODULE */
-
\ No newline at end of file
+++ /dev/null
-/*
- drivers/sound/lowlevel/aedsp16.c
-
- Audio Excel DSP 16 software configuration routines
- Copyright (C) 1995,1996,1997,1998 Riccardo Facchetti (fizban@tin.it)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- */
-/*
- * Include the main OSS Lite header file. It include all the os, OSS Lite, etc
- * headers needed by this source.
- */
-#include <linux/config.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include "../sound_config.h"
-#include "../soundmodule.h"
-
-/*
- * Sanity checks
- */
-
-#if !defined(CONFIG_AEDSP16_BASE)
-# undef CONFIG_AEDSP16
-#else
-# if defined(MODULE) && defined(CONFIG_AEDSP16_MODULE)
-# define CONFIG_AEDSP16 1
-# endif
-#endif
-
-
-#if defined(CONFIG_AEDSP16)
-
-#if defined(CONFIG_AEDSP16_SBPRO) && defined(CONFIG_AEDSP16_MSS)
-#error You have to enable only one of the MSS and SBPRO emulations.
-#endif
-
-/*
-
- READ THIS
-
- This module started to configure the Audio Excel DSP 16 Sound Card.
- Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards.
-
- NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this
- audio card and want to see the kernel support for it, please contact me.
-
- Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401
- compatible card.
- It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq),
- so before this module, the only way to configure the DSP under linux was
- boot the MS-DOS loading the sound.sys device driver (this driver soft-
- configure the sound board hardware by massaging someone of its registers),
- and then ctrl-alt-del to boot linux with the DSP configured by the DOS
- driver.
-
- This module works configuring your Audio Excel DSP 16's irq, dma and
- mpu-401-irq. The OSS Lite routines rely on the fact that if the
- hardware is there, they can detect it. The problem with AEDSP16 is
- that no hardware can be found by the probe routines if the sound card
- is not configured properly. Sometimes the kernel probe routines can find
- an SBPRO even when the card is not configured (this is the standard setup
- of the card), but the SBPRO emulation don't work well if the card is not
- properly initialized. For this reason
-
- aedsp16_init_board()
-
- routine is called before the OSS Lite probe routines try to detect the
- hardware.
-
- NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS)
-
- NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards
- have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They
- have to be configured by software.
-
- NOTE: The driver is merged with the new OSS Lite sound driver. It works
- as a lowlevel driver.
-
- The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS;
- the OSS Lite sound driver can be configured for SBPRO and MSS cards
- at the same time, but the aedsp16 can't be two cards!!
- When we configure it, we have to choose the SBPRO or the MSS emulation
- for AEDSP16. We also can install a *REAL* card of the other type (see [1]).
-
- NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO
- please let me know if it works.
-
- The MPU-401 support can be compiled in together with one of the other
- two operating modes.
-
- NOTE: This is something like plug-and-play: we have only to plug
- the AEDSP16 board in the socket, and then configure and compile
- a kernel that uses the AEDSP16 software configuration capability.
- No jumper setting is needed!
-
- For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3
- you have just to make config the OSS Lite package, configuring
- the AEDSP16 sound card, then activating the SBPro emulation mode
- and at last configuring IRQ and DMA.
- Compile the kernel and run it.
-
- NOTE: This means for SC-6000 cards that you can choose irq and dma,
- but not the I/O addresses. To change I/O addresses you have to set
- them with jumpers. For SC-6600 cards you have no jumpers so you have
- to set up your full card configuration in the make config.
-
- You can change the irq/dma/mirq settings WITHOUT THE NEED to open
- your computer and massage the jumpers (there are no irq/dma/mirq
- jumpers to be configured anyway, only I/O BASE values have to be
- configured with jumpers)
-
- For some ununderstandable reason, the card default of irq 7, dma 1,
- don't work for me. Seems to be an IRQ or DMA conflict. Under heavy
- HDD work, the kernel start to erupt out a lot of messages like:
-
- 'Sound: DMA timed out - IRQ/DRQ config error?'
-
- For what I can say, I have NOT any conflict at irq 7 (under linux I'm
- using the lp polling driver), and dma line 1 is unused as stated by
- /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so
- I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows!
- Anyway a setting of irq 10, dma 3 works really fine.
-
- NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know
- the emulation mode, all the installed hardware and the hardware
- configuration (irq and dma settings of all the hardware).
-
- This init module should work with SBPRO+MSS, when one of the two is
- the AEDSP16 emulation and the other the real card. (see [1])
- For example:
-
- AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other
- AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other
-
- MPU401 should work. (see [2])
-
- [1]
- ---
- Date: Mon, 29 Jul 1997 08:35:40 +0100
- From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk>
-
- [...]
- Just to let you know got my Audio Excel (emulating a MSS) working
- with my original SB16, thanks for the driver!
- [...]
- ---
-
- [2] Not tested by me for lack of hardware.
-
- TODO, WISHES AND TECH
-
- - About I/O ports allocation -
-
- Request the 2x0h region (port base) in any case if we are using this card.
-
- NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16
- port base region (see code) does not mean necessarily that we are emulating
- sbpro. Even if this region is the sbpro I/O ports region, we use this
- region to access the control registers of the card, and if emulating
- sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro
- registers are not used, in no way, to emulate an sbpro: they are
- used only for configuration purposes.
-
- Started Fri Mar 17 16:13:18 MET 1995
-
- v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c)
- - Initial code.
- v0.2 (ALPHA)
- - Cleanups.
- - Integrated with Linux voxware v 2.90-2 kernel sound driver.
- - SoundBlaster Pro mode configuration.
- - Microsoft Sound System mode configuration.
- - MPU-401 mode configuration.
- v0.3 (ALPHA)
- - Cleanups.
- - Rearranged the code to let aedsp16_init_board be more general.
- - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h
- inclusion too. We rely on os.h
- - Used the to get a variable
- len string (we are not sure about the len of Copyright string).
- This works with any SB and compatible.
- - Added the code to request_region at device init (should go in
- the main body of voxware).
- v0.4 (BETA)
- - Better configure.c patch for aedsp16 configuration (better
- logic of inclusion of AEDSP16 support)
- - Modified the conditional compilation to better support more than
- one sound card of the emulated type (read the NOTES above)
- - Moved the sb init routine from the attach to the very first
- probe in sb_card.c
- - Rearrangements and cleanups
- - Wiped out some unnecessary code and variables: this is kernel
- code so it is better save some TEXT and DATA
- - Fixed the request_region code. We must allocate the aedsp16 (sbpro)
- I/O ports in any case because they are used to access the DSP
- configuration registers and we can not allow anyone to get them.
- v0.5
- - cleanups on comments
- - prep for diffs against v3.0-proto-950402
- v0.6
- - removed the request_region()s when compiling the MODULE sound.o
- because we are not allowed (by the actual voxware structure) to
- release_region()
- v0.7 (pre ALPHA, not distributed)
- - started porting this module to kernel 1.3.84. Dummy probe/attach
- routines.
- v0.8 (ALPHA)
- - attached all the init routines.
- v0.9 (BETA)
- - Integrated with linux-pre2.0.7
- - Integrated with configuration scripts.
- - Cleaned up and beautyfied the code.
- v0.9.9 (BETA)
- - Thanks to Piercarlo Grandi: corrected the conditonal compilation code.
- Now only the code configured is compiled in, with some memory saving.
- v0.9.10
- - Integration into the sound/lowlevel/ section of the sound driver.
- - Re-organized the code.
- v0.9.11 (not distributed)
- - Rewritten the init interface-routines to initialize the AEDSP16 in
- one shot.
- - More cosmetics.
- - SC-6600 support.
- - More soft/hard configuration.
- v0.9.12
- - Refined the v0.9.11 code with conditional compilation to distinguish
- between SC-6000 and SC-6600 code.
- v1.0.0
- - Prep for merging with OSS Lite and Linux kernel 2.1.13
- - Corrected a bug in request/check/release region calls (thanks to the
- new kernel exception handling).
- v1.1
- - Revamped for integration with new modularized sound drivers: to enhance
- the flexibility of modular version, I have removed all the conditional
- compilation for SBPRO, MPU and MSS code. Now it is all managed with
- the ae_config structure.
- v1.2
- - Module informations added.
- - Removed aedsp16_delay_10msec(), now using mdelay(10)
- - All data and funcs moved to .*.init section.
-
- Known Problems:
- - Audio Excel DSP 16 III don't work with this driver.
-
- Credits:
- Many thanks to Gerald Britton <gbritton@CapAccess.org>. He helped me a
- lot in testing the 0.9.11 and 0.9.12 versions of this driver.
-
- */
-
-
-#define VERSION "1.2" /* Version of Audio Excel DSP 16 driver */
-
-#undef AEDSP16_DEBUG 1 /* Define this to enable debug code */
-#undef AEDSP16_DEBUG_MORE 1 /* Define this to enable more debug */
-#undef AEDSP16_INFO 1 /* Define this to enable info code */
-
-#if defined(AEDSP16_DEBUG)
-# define DBG(x) printk x
-# if defined(AEDSP16_DEBUG_MORE)
-# define DBG1(x) printk x
-# else
-# define DBG1(x)
-# endif
-#else
-# define DBG(x)
-# define DBG1(x)
-#endif
-
-/*
- * Misc definitions
- */
-#define TRUE 1
-#define FALSE 0
-
-/*
- * Region Size for request/check/release region.
- */
-#define IOBASE_REGION_SIZE 0x10
-
-/*
- * Hardware related defaults
- */
-#define DEF_AEDSP16_IOB 0x220 /* 0x220(default) 0x240 */
-#define DEF_AEDSP16_IRQ 7 /* 5 7(default) 9 10 11 */
-#define DEF_AEDSP16_MRQ 0 /* 5 7 9 10 0(default), 0 means disable */
-#define DEF_AEDSP16_DMA 1 /* 0 1(default) 3 */
-
-/*
- * Commands of AEDSP16's DSP (SBPRO+special).
- * Some of them are COMMAND_xx, in the future they may change.
- */
-#define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */
-#define COMMAND_52 0x52 /* */
-#define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */
-#define COMMAND_5C 0x5c /* */
-#define COMMAND_60 0x60 /* */
-#define COMMAND_66 0x66 /* */
-#define COMMAND_6C 0x6c /* */
-#define COMMAND_6E 0x6e /* */
-#define COMMAND_88 0x88 /* */
-#define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */
-#define COMMAND_C5 0xc5 /* */
-#define GET_DSP_VERSION 0xe1 /* Get DSP Version */
-#define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright */
-
-/*
- * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port
- * to have the actual I/O port.
- * Register permissions are:
- * (wo) == Write Only
- * (ro) == Read Only
- * (w-) == Write
- * (r-) == Read
- */
-#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */
-#define DSP_READ 0x0a /* offset of DSP READ (ro) */
-#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */
-#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */
-#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */
-#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */
-
-
-#define RETRY 10 /* Various retry values on I/O opera- */
-#define STATUSRETRY 1000 /* tions. Sometimes we have to */
-#define HARDRETRY 500000 /* wait for previous cmd to complete */
-
-/*
- * Size of character arrays that store name and version of sound card
- */
-#define CARDNAMELEN 15 /* Size of the card's name in chars */
-#define CARDVERLEN 2 /* Size of the card's version in chars */
-
-#if defined(CONFIG_SC6600)
-/*
- * Bitmapped flags of hard configuration
- */
-/*
- * Decode macros (xl == low byte, xh = high byte)
- */
-#define IOBASE(xl) ((xl & 0x01)?0x240:0x220)
-#define JOY(xl) (xl & 0x02)
-#define MPUADDR(xl) ( \
- (xl & 0x0C)?0x330: \
- (xl & 0x08)?0x320: \
- (xl & 0x04)?0x310: \
- 0x300)
-#define WSSADDR(xl) ((xl & 0x10)?0xE80:0x530)
-#define CDROM(xh) (xh & 0x20)
-#define CDROMADDR(xh) (((xh & 0x1F) << 4) + 0x200)
-/*
- * Encode macros
- */
-#define BLDIOBASE(xl, val) { \
- xl &= ~0x01; \
- if (val == 0x240) \
- xl |= 0x01; \
- }
-#define BLDJOY(xl, val) { \
- xl &= ~0x02; \
- if (val == 1) \
- xl |= 0x02; \
- }
-#define BLDMPUADDR(xl, val) { \
- xl &= ~0x0C; \
- switch (val) { \
- case 0x330: \
- xl |= 0x0C; \
- break; \
- case 0x320: \
- xl |= 0x08; \
- break; \
- case 0x310: \
- xl |= 0x04; \
- break; \
- case 0x300: \
- xl |= 0x00; \
- break; \
- default: \
- xl |= 0x00; \
- break; \
- } \
- }
-#define BLDWSSADDR(xl, val) { \
- xl &= ~0x10; \
- if (val == 0xE80) \
- xl |= 0x10; \
- }
-#define BLDCDROM(xh, val) { \
- xh &= ~0x20; \
- if (val == 1) \
- xh |= 0x20; \
- }
-#define BLDCDROMADDR(xh, val) { \
- int tmp = val; \
- tmp -= 0x200; \
- tmp >>= 4; \
- tmp &= 0x1F; \
- xh |= tmp; \
- xh &= 0x7F; \
- xh |= 0x40; \
- }
-#endif /* CONFIG_SC6600 */
-
-/*
- * Bit mapped flags for calling aedsp16_init_board(), and saving the current
- * emulation mode.
- */
-#define INIT_NONE (0 )
-#define INIT_SBPRO (1<<0)
-#define INIT_MSS (1<<1)
-#define INIT_MPU401 (1<<2)
-
-static int soft_cfg __initdata = 0; /* bitmapped config */
-static int soft_cfg_mss __initdata = 0; /* bitmapped mss config */
-static int ver[CARDVERLEN] __initdata = {0, 0}; /* DSP Ver:
- hi->ver[0] lo->ver[1] */
-
-#if defined(CONFIG_SC6600)
-static int hard_cfg[2] /* lo<-hard_cfg[0] hi<-hard_cfg[1] */
- __initdata = { 0, 0};
-#endif /* CONFIG_SC6600 */
-
-#if defined(CONFIG_SC6600)
-/* Decoded hard configuration */
-struct d_hcfg {
- int iobase;
- int joystick;
- int mpubase;
- int wssbase;
- int cdrom;
- int cdrombase;
-};
-
-struct d_hcfg decoded_hcfg __initdata = {0, };
-
-#endif /* CONFIG_SC6600 */
-
-/* orVals contain the values to be or'ed */
-struct orVals {
- int val; /* irq|mirq|dma */
- int or; /* soft_cfg |= TheStruct.or */
-};
-
-/* aedsp16_info contain the audio card configuration */
-struct aedsp16_info {
- int base_io; /* base I/O address for accessing card */
- int irq; /* irq value for DSP I/O */
- int mpu_irq; /* irq for mpu401 interface I/O */
- int dma; /* dma value for DSP I/O */
- int mss_base; /* base I/O for Microsoft Sound System */
- int mpu_base; /* base I/O for MPU-401 emulation */
- int init; /* Initialization status of the card */
-};
-
-/*
- * Magic values that the DSP will eat when configuring irq/mirq/dma
- */
-/* DSP IRQ conversion array */
-static struct orVals orIRQ[] __initdata = {
- {0x05, 0x28},
- {0x07, 0x08},
- {0x09, 0x10},
- {0x0a, 0x18},
- {0x0b, 0x20},
- {0x00, 0x00}
-};
-
-/* MPU-401 IRQ conversion array */
-static struct orVals orMIRQ[] __initdata = {
- {0x05, 0x04},
- {0x07, 0x44},
- {0x09, 0x84},
- {0x0a, 0xc4},
- {0x00, 0x00}
-};
-
-/* DMA Channels conversion array */
-static struct orVals orDMA[] __initdata = {
- {0x00, 0x01},
- {0x01, 0x02},
- {0x03, 0x03},
- {0x00, 0x00}
-};
-
-static struct aedsp16_info ae_config __initdata = {
- DEF_AEDSP16_IOB,
- DEF_AEDSP16_IRQ,
- DEF_AEDSP16_MRQ,
- DEF_AEDSP16_DMA,
- -1,
- -1,
- INIT_NONE
-};
-
-/*
- * Buffers to store audio card informations
- */
-static char DSPCopyright[CARDNAMELEN + 1] __initdata = {0, };
-static char DSPVersion[CARDVERLEN + 1] __initdata = {0, };
-
-static int __init aedsp16_wait_data(int port)
-{
- int loop = STATUSRETRY;
- unsigned char ret = 0;
-
- DBG1(("aedsp16_wait_data (0x%x): ", port));
-
- do {
- ret = inb(port + DSP_DATAVAIL);
- /*
- * Wait for data available (bit 7 of ret == 1)
- */
- } while (!(ret & 0x80) && loop--);
-
- if (ret & 0x80) {
- DBG1(("success.\n"));
- return TRUE;
- }
-
- DBG1(("failure.\n"));
- return FALSE;
-}
-
-static int __init aedsp16_read(int port)
-{
- int inbyte;
-
- DBG((" Read DSP Byte (0x%x): ", port));
-
- if (aedsp16_wait_data(port) == FALSE) {
- DBG(("failure.\n"));
- return -1;
- }
-
- inbyte = inb(port + DSP_READ);
-
- DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte));
-
- return inbyte;
-}
-
-static int __init aedsp16_test_dsp(int port)
-{
- return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE);
-}
-
-static int __init aedsp16_dsp_reset(int port)
-{
- /*
- * Reset DSP
- */
-
- DBG(("Reset DSP:\n"));
-
- outb(1, (port + DSP_RESET));
- udelay(10);
- outb(0, (port + DSP_RESET));
- udelay(10);
- udelay(10);
- if (aedsp16_test_dsp(port) == TRUE) {
- DBG(("success.\n"));
- return TRUE;
- } else
- DBG(("failure.\n"));
- return FALSE;
-}
-
-static int __init aedsp16_write(int port, int cmd)
-{
- unsigned char ret;
- int loop = HARDRETRY;
-
- DBG((" Write DSP Byte (0x%x) [0x%x]: ", port, cmd));
-
- do {
- ret = inb(port + DSP_STATUS);
- /*
- * DSP ready to receive data if bit 7 of ret == 0
- */
- if (!(ret & 0x80)) {
- outb(cmd, port + DSP_COMMAND);
- DBG(("success.\n"));
- return 0;
- }
- } while (loop--);
-
- DBG(("timeout.\n"));
- printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd);
-
- return -1;
-}
-
-#if defined(CONFIG_SC6600)
-
-#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
-void __init aedsp16_pinfo(void) {
- DBG(("\n Base address: %x\n", decoded_hcfg.iobase));
- DBG((" Joystick : %s present\n", decoded_hcfg.joystick?"":" not"));
- DBG((" WSS addr : %x\n", decoded_hcfg.wssbase));
- DBG((" MPU-401 addr: %x\n", decoded_hcfg.mpubase));
- DBG((" CDROM : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not"));
- DBG((" CDROMADDR : %x\n\n", decoded_hcfg.cdrombase));
-}
-#endif
-
-void __init aedsp16_hard_decode(void) {
-
- DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
-
-/*
- * Decode Cfg Bytes.
- */
- decoded_hcfg.iobase = IOBASE(hard_cfg[0]);
- decoded_hcfg.joystick = JOY(hard_cfg[0]);
- decoded_hcfg.wssbase = WSSADDR(hard_cfg[0]);
- decoded_hcfg.mpubase = MPUADDR(hard_cfg[0]);
- decoded_hcfg.cdrom = CDROM(hard_cfg[1]);
- decoded_hcfg.cdrombase = CDROMADDR(hard_cfg[1]);
-
-#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
- printk(" Original sound card configuration:\n");
- aedsp16_pinfo();
-#endif
-
-/*
- * Now set up the real kernel configuration.
- */
- decoded_hcfg.iobase = ae_config.base_io;
- decoded_hcfg.wssbase = ae_config.mss_base;
- decoded_hcfg.mpubase = ae_config.mpu_base;
-
-#if defined(CONFIG_SC6600_JOY)
- decoded_hcfg.joystick = CONFIG_SC6600_JOY; /* Enable */
-#endif
-#if defined(CONFIG_SC6600_CDROM)
- decoded_hcfg.cdrom = CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */
-#endif
-#if defined(CONFIG_SC6600_CDROMBASE)
- decoded_hcfg.cdrombase = CONFIG_SC6600_CDROMBASE; /* 0 Disable */
-#endif
-
-#if defined(AEDSP16_DEBUG)
- DBG((" New Values:\n"));
- aedsp16_pinfo();
-#endif
-
- DBG(("success.\n"));
-}
-
-void __init aedsp16_hard_encode(void) {
-
- DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
-
- hard_cfg[0] = 0;
- hard_cfg[1] = 0;
-
- hard_cfg[0] |= 0x20;
-
- BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase);
- BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase);
- BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase);
- BLDJOY(hard_cfg[0], decoded_hcfg.joystick);
- BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom);
- BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase);
-
-#if defined(AEDSP16_DEBUG)
- aedsp16_pinfo();
-#endif
-
- DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
- DBG(("success.\n"));
-
-}
-
-static int __init aedsp16_hard_write(int port) {
-
- DBG(("aedsp16_hard_write:\n"));
-
- if (aedsp16_write(port, COMMAND_6C)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_write(port, COMMAND_5C)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_write(port, hard_cfg[0])) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_write(port, hard_cfg[1])) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_write(port, COMMAND_C5)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5);
- DBG(("failure.\n"));
- return FALSE;
- }
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static int __init aedsp16_hard_read(int port) {
-
- DBG(("aedsp16_hard_read:\n"));
-
- if (aedsp16_write(port, READ_HARD_CFG)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG);
- DBG(("failure.\n"));
- return FALSE;
- }
-
- if ((hard_cfg[0] = aedsp16_read(port)) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- READ_HARD_CFG);
- DBG(("failure.\n"));
- return FALSE;
- }
- if ((hard_cfg[1] = aedsp16_read(port)) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- READ_HARD_CFG);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_read(port) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- READ_HARD_CFG);
- DBG(("failure.\n"));
- return FALSE;
- }
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static int __init aedsp16_ext_cfg_write(int port) {
-
- int extcfg, val;
-
- if (aedsp16_write(port, COMMAND_66)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66);
- return FALSE;
- }
-
- extcfg = 7;
- if (decoded_hcfg.cdrom != 2)
- extcfg = 0x0F;
- if ((decoded_hcfg.cdrom == 4) ||
- (decoded_hcfg.cdrom == 3))
- extcfg &= ~2;
- if (decoded_hcfg.cdrombase == 0)
- extcfg &= ~2;
- if (decoded_hcfg.mpubase == 0)
- extcfg &= ~1;
-
- if (aedsp16_write(port, extcfg)) {
- printk("[AEDSP16] Write extcfg: failed!\n");
- return FALSE;
- }
- if (aedsp16_write(port, 0)) {
- printk("[AEDSP16] Write extcfg: failed!\n");
- return FALSE;
- }
- if (decoded_hcfg.cdrom == 3) {
- if (aedsp16_write(port, COMMAND_52)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
- return FALSE;
- }
- if ((val = aedsp16_read(port)) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n"
- , COMMAND_52);
- return FALSE;
- }
- val &= 0x7F;
- if (aedsp16_write(port, COMMAND_60)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
- return FALSE;
- }
- if (aedsp16_write(port, val)) {
- printk("[AEDSP16] Write val: failed!\n");
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-#endif /* CONFIG_SC6600 */
-
-static int __init aedsp16_cfg_write(int port) {
- if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
- return FALSE;
- }
- if (aedsp16_write(port, soft_cfg)) {
- printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n");
- return FALSE;
- }
- return TRUE;
-}
-
-static int __init aedsp16_init_mss(int port)
-{
- DBG(("aedsp16_init_mss:\n"));
-
- mdelay(10);
-
- if (aedsp16_write(port, DSP_INIT_MSS)) {
- printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n",
- DSP_INIT_MSS);
- DBG(("failure.\n"));
- return FALSE;
- }
-
- mdelay(10);
-
- if (aedsp16_cfg_write(port) == FALSE)
- return FALSE;
-
- outb(soft_cfg_mss, ae_config.mss_base);
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static int __init aedsp16_setup_board(int port) {
- int loop = RETRY;
-
-#if defined(CONFIG_SC6600)
- int val = 0;
-
- if (aedsp16_hard_read(port) == FALSE) {
- printk("[AEDSP16] aedsp16_hard_read: failed!\n");
- return FALSE;
- }
-
- if (aedsp16_write(port, COMMAND_52)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
- return FALSE;
- }
-
- if ((val = aedsp16_read(port)) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- COMMAND_52);
- return FALSE;
- }
-#endif
-
- do {
- if (aedsp16_write(port, COMMAND_88)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88);
- return FALSE;
- }
- mdelay(10);
- } while ((aedsp16_wait_data(port) == FALSE) && loop--);
-
- if (aedsp16_read(port) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- COMMAND_88);
- return FALSE;
- }
-
-#if !defined(CONFIG_SC6600)
- if (aedsp16_write(port, COMMAND_5C)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
- return FALSE;
- }
-#endif
-
- if (aedsp16_cfg_write(port) == FALSE)
- return FALSE;
-
-#if defined(CONFIG_SC6600)
- if (aedsp16_write(port, COMMAND_60)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
- return FALSE;
- }
- if (aedsp16_write(port, val)) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", val);
- return FALSE;
- }
- if (aedsp16_write(port, COMMAND_6E)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E);
- return FALSE;
- }
- if (aedsp16_write(port, ver[0])) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]);
- return FALSE;
- }
- if (aedsp16_write(port, ver[1])) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]);
- return FALSE;
- }
-
- if (aedsp16_hard_write(port) == FALSE) {
- printk("[AEDSP16] aedsp16_hard_write: failed!\n");
- return FALSE;
- }
-
- if (aedsp16_write(port, COMMAND_5C)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
- return FALSE;
- }
-
-#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET)
- if (aedsp16_cfg_write(port) == FALSE)
- return FALSE;
-#endif
-
-#endif
-
- return TRUE;
-}
-
-static int __init aedsp16_stdcfg(int port) {
- if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
- return FALSE;
- }
- /*
- * 0x0A == (IRQ 7, DMA 1, MIRQ 0)
- */
- if (aedsp16_write(port, 0x0A)) {
- printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
- return FALSE;
- }
- return TRUE;
-}
-
-static int __init aedsp16_dsp_version(int port)
-{
- int len = 0;
- int ret;
-
- DBG(("Get DSP Version:\n"));
-
- if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION);
- DBG(("failed.\n"));
- return FALSE;
- }
-
- do {
- if ((ret = aedsp16_read(port)) == -1) {
- DBG(("failed.\n"));
- return FALSE;
- }
- /*
- * We already know how many int are stored (2), so we know when the
- * string is finished.
- */
- ver[len++] = ret;
- } while (len < CARDVERLEN);
- sprintf(DSPVersion, "%d.%d", ver[0], ver[1]);
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static int __init aedsp16_dsp_copyright(int port)
-{
- int len = 0;
- int ret;
-
- DBG(("Get DSP Copyright:\n"));
-
- if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT);
- DBG(("failed.\n"));
- return FALSE;
- }
-
- do {
- if ((ret = aedsp16_read(port)) == -1) {
- /*
- * If no more data available, return to the caller, no error if len>0.
- * We have no other way to know when the string is finished.
- */
- if (len)
- break;
- else {
- DBG(("failed.\n"));
- return FALSE;
- }
- }
-
- DSPCopyright[len++] = ret;
-
- } while (len < CARDNAMELEN);
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static void __init aedsp16_init_tables(void)
-{
- int i = 0;
-
- memset(DSPCopyright, 0, CARDNAMELEN + 1);
- memset(DSPVersion, 0, CARDVERLEN + 1);
-
- for (i = 0; orIRQ[i].or; i++)
- if (orIRQ[i].val == ae_config.irq) {
- soft_cfg |= orIRQ[i].or;
- soft_cfg_mss |= orIRQ[i].or;
- }
-
- for (i = 0; orMIRQ[i].or; i++)
- if (orMIRQ[i].or == ae_config.mpu_irq)
- soft_cfg |= orMIRQ[i].or;
-
- for (i = 0; orDMA[i].or; i++)
- if (orDMA[i].val == ae_config.dma) {
- soft_cfg |= orDMA[i].or;
- soft_cfg_mss |= orDMA[i].or;
- }
-}
-
-static int __init aedsp16_init_board(void)
-{
- aedsp16_init_tables();
-
- if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_dsp_reset: failed!\n");
- return FALSE;
- }
- if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n");
- return FALSE;
- }
-
- /*
- * My AEDSP16 card return SC-6000 in DSPCopyright, so
- * if we have something different, we have to be warned.
- */
- if (strcmp("SC-6000", DSPCopyright))
- printk("[AEDSP16] Warning: non SC-6000 audio card!\n");
-
- if (aedsp16_dsp_version(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_dsp_version: failed!\n");
- return FALSE;
- }
-
- if (aedsp16_stdcfg(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
- return FALSE;
- }
-
-#if defined(CONFIG_SC6600)
- if (aedsp16_hard_read(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_hard_read: failed!\n");
- return FALSE;
- }
-
- aedsp16_hard_decode();
-
- aedsp16_hard_encode();
-
- if (aedsp16_hard_write(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_hard_write: failed!\n");
- return FALSE;
- }
-
- if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n");
- return FALSE;
- }
-#endif /* CONFIG_SC6600 */
-
- if (aedsp16_setup_board(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_setup_board: failed!\n");
- return FALSE;
- }
-
- if (ae_config.mss_base != -1) {
- if (ae_config.init & INIT_MSS) {
- if (aedsp16_init_mss(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] Can not initialize"
- "Microsoft Sound System mode.\n");
- return FALSE;
- }
- }
- }
-
-#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
-
- printk("Audio Excel DSP 16 init v%s (%s %s) [",
- VERSION, DSPCopyright,
- DSPVersion);
-
- if (ae_config.mpu_base != -1) {
- if (ae_config.init & INIT_MPU401) {
- printk("MPU401");
- if ((ae_config.init & INIT_MSS) ||
- (ae_config.init & INIT_SBPRO))
- printk(" ");
- }
- }
-
- if (ae_config.mss_base == -1) {
- if (ae_config.init & INIT_SBPRO) {
- printk("SBPro");
- if (ae_config.init & INIT_MSS)
- printk(" ");
- }
- }
-
- if (ae_config.mss_base != -1)
- if (ae_config.init & INIT_MSS)
- printk("MSS");
-
- printk("]\n");
-#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */
-
- mdelay(10);
-
- return TRUE;
-}
-
-static int __init init_aedsp16_sb(void)
-{
- DBG(("init_aedsp16_sb: "));
-
-/*
- * If the card is already init'ed MSS, we can not init it to SBPRO too
- * because the board can not emulate simultaneously MSS and SBPRO.
- */
- if (ae_config.init & INIT_MSS)
- return FALSE;
- if (ae_config.init & INIT_SBPRO)
- return FALSE;
-
- ae_config.init |= INIT_SBPRO;
-
- DBG(("done.\n"));
-
- return TRUE;
-}
-
-static void __init uninit_aedsp16_sb(void)
-{
- DBG(("uninit_aedsp16_sb: "));
-
- ae_config.init &= ~INIT_SBPRO;
-
- DBG(("done.\n"));
-}
-
-static int __init init_aedsp16_mss(void)
-{
- DBG(("init_aedsp16_mss: "));
-
-/*
- * If the card is already init'ed SBPRO, we can not init it to MSS too
- * because the board can not emulate simultaneously MSS and SBPRO.
- */
- if (ae_config.init & INIT_SBPRO)
- return FALSE;
- if (ae_config.init & INIT_MSS)
- return FALSE;
-/*
- * We must check the CONFIG_AEDSP16_BASE region too because these are the I/O
- * ports to access card's control registers.
- */
- if (!(ae_config.init & INIT_MPU401)) {
- if (check_region(ae_config.base_io, IOBASE_REGION_SIZE)) {
- printk(
- "AEDSP16 BASE I/O port region is already in use.\n");
- return FALSE;
- }
- }
-
-/*
- * We must allocate the CONFIG_AEDSP16_BASE region too because these are the
- * I/O ports to access card's control registers.
- */
- if (!(ae_config.init & INIT_MPU401))
- request_region(ae_config.base_io, IOBASE_REGION_SIZE,
- "aedsp16 (base)");
-
- ae_config.init |= INIT_MSS;
-
- DBG(("done.\n"));
-
- return TRUE;
-}
-
-static void __init uninit_aedsp16_mss(void)
-{
- DBG(("uninit_aedsp16_mss: "));
-
- if ((!(ae_config.init & INIT_MPU401)) &&
- (ae_config.init & INIT_MSS)) {
- release_region(ae_config.base_io, IOBASE_REGION_SIZE);
- DBG(("AEDSP16 base region released.\n"));
- }
-
- ae_config.init &= ~INIT_MSS;
- DBG(("done.\n"));
-}
-
-static int __init init_aedsp16_mpu(void)
-{
- DBG(("init_aedsp16_mpu: "));
-
- if (ae_config.init & INIT_MPU401)
- return FALSE;
-
-/*
- * We must check the CONFIG_AEDSP16_BASE region too because these are the I/O
- * ports to access card's control registers.
- */
- if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) {
- if (check_region(ae_config.base_io, IOBASE_REGION_SIZE)) {
- printk(
- "AEDSP16 BASE I/O port region is already in use.\n");
- return FALSE;
- }
- }
-
- if (!(ae_config.init & (INIT_MSS | INIT_SBPRO)))
- request_region(ae_config.base_io, IOBASE_REGION_SIZE,
- "aedsp16 (base)");
-
- ae_config.init |= INIT_MPU401;
-
- DBG(("done.\n"));
-
- return TRUE;
-}
-
-static void __init uninit_aedsp16_mpu(void)
-{
- DBG(("uninit_aedsp16_mpu: "));
-
- if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) &&
- (ae_config.init & INIT_MPU401)) {
- release_region(ae_config.base_io, IOBASE_REGION_SIZE);
- DBG(("AEDSP16 base region released.\n"));
- }
-
- ae_config.init &= ~INIT_MPU401;
-
- DBG(("done.\n"));
-}
-
-int __init init_aedsp16(void)
-{
- int initialized = FALSE;
-
-#if !defined(MODULE)
- ae_config.base_io = CONFIG_AEDSP16_BASE;
-#if defined(CONFIG_AEDSP16_SBPRO)
- ae_config.irq = CONFIG_AEDSP16_SB_IRQ;
- ae_config.dma = CONFIG_AEDSP16_SB_DMA;
-#endif
-#if defined(CONFIG_AEDSP16_MSS)
- ae_config.mss_base = CONFIG_MSS_BASE;
- ae_config.irq = CONFIG_AEDSP16_MSS_IRQ;
- ae_config.dma = CONFIG_AEDSP16_MSS_DMA;
-#endif
-#if defined(CONFIG_AEDSP16_MPU401)
- ae_config.mpu_base = CONFIG_MPU_BASE;
- ae_config.mpu_irq = CONFIG_AEDSP16_MPU_IRQ;
-#endif
-#endif /* !MODULE */
- DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n",
- ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq));
-
- if (ae_config.mss_base == -1) {
- if (init_aedsp16_sb() == FALSE) {
- uninit_aedsp16_sb();
- } else {
- initialized = TRUE;
- }
- }
-
- if (ae_config.mpu_base != -1) {
- if (init_aedsp16_mpu() == FALSE) {
- uninit_aedsp16_mpu();
- } else {
- initialized = TRUE;
- }
- }
-
-/*
- * In the sequence of init routines, the MSS init MUST be the last!
- * This because of the special register programming the MSS mode needs.
- * A board reset would disable the MSS mode restoring the default SBPRO
- * mode.
- */
- if (ae_config.mss_base != -1) {
- if (init_aedsp16_mss() == FALSE) {
- uninit_aedsp16_mss();
- } else {
- initialized = TRUE;
- }
- }
-
- if (initialized)
- initialized = aedsp16_init_board();
- return initialized;
-}
-
-void __init uninit_aedsp16(void)
-{
- if (ae_config.mss_base != -1)
- uninit_aedsp16_mss();
- else
- uninit_aedsp16_sb();
- if (ae_config.mpu_base != -1)
- uninit_aedsp16_mpu();
-}
-
-#if defined(MODULE)
-
-int io = -1;
-int irq = -1;
-int dma = -1;
-int mpu_irq = -1;
-int mss_base = -1;
-int mpu_base = -1;
-
-
-MODULE_PARM(io, "i");
-MODULE_PARM_DESC(io, "I/O base address (0x220 0x240)");
-MODULE_PARM(irq, "i");
-MODULE_PARM_DESC(irq, "IRQ line (5 7 9 10 11)");
-MODULE_PARM(dma, "i");
-MODULE_PARM_DESC(dma, "dma line (0 1 3)");
-MODULE_PARM(mpu_irq, "i");
-MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ line (5 7 9 10 0)");
-MODULE_PARM(mss_base, "i");
-MODULE_PARM_DESC(mss_base, "MSS emulation I/O base address (0x530 0xE80)");
-MODULE_PARM(mpu_base, "i");
-MODULE_PARM_DESC(mpu_base,"MPU-401 I/O base address (0x300 0x310 0x320 0x330)");
-MODULE_AUTHOR("Riccardo Facchetti <fizban@tin.it>");
-MODULE_DESCRIPTION("Audio Excel DSP 16 Driver Version " VERSION);
-
-int init_module(void) {
- printk("Audio Excel DSP 16 init driver Copyright (C) Riccardo Facchetti 1995-98\n");
- if (io == -1 || dma == -1 || irq == -1) {
- printk(KERN_INFO "aedsp16: I/O, IRQ and DMA are mandatory\n");
- return -EINVAL;
- }
-
- ae_config.base_io = io;
- ae_config.irq = irq;
- ae_config.dma = dma;
-
- ae_config.mss_base = mss_base;
- ae_config.mpu_base = mpu_base;
- ae_config.mpu_irq = mpu_irq;
-
- if (init_aedsp16() == FALSE) {
- printk(KERN_ERR "aedsp16: initialization failed\n");
- /*
- * XXX
- * What error should we return here ?
- */
- return -EINVAL;
- }
- SOUND_LOCK;
- return 0;
-}
-
-void cleanup_module(void) {
- uninit_aedsp16();
- SOUND_LOCK_END;
-}
-#endif /* MODULE */
-
-#endif /* CONFIG_AEDSP16 */
+++ /dev/null
-/*
- * sound/awe_compat.h
- *
- * Compat defines for the AWE32/SB32/AWE64 wave table synth driver.
- * version 0.4.3; Oct. 1, 1998
- *
- * Copyright (C) 1996-1998 Takashi Iwai
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef AWE_COMPAT_H_DEF
-#define AWE_COMPAT_H_DEF
-
-/*================================================================
- * version check
- *================================================================*/
-
-#include "awe_config.h"
-
-#define ASC_LINUX_VERSION(V,P,S) (((V) * 65536) + ((P) * 256) + (S))
-
-#ifndef LINUX_VERSION_CODE
-#include <linux/version.h>
-#endif
-
-/* linux version check */
-#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0)
-#define AWE_OBSOLETE_VOXWARE
-#endif
-
-#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0)
-#define AWE_NEW_KERNEL_INTERFACE
-#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,80)
-#define AWE_MODULE_SUPPORT
-#endif
-#endif
-
-#ifdef AWE_OBSOLETE_VOXWARE
-#include "soundvers.h"
-#else
-#include "../soundvers.h"
-#endif
-
-#if defined(SOUND_INTERNAL_VERSION) && SOUND_INTERNAL_VERSION >= 0x30803
-/* OSS/Free-3.8 */
-#define AWE_NO_PATCHMGR
-#define AWE_OSS38
-#define HAS_LOWLEVEL_H
-#endif
-
-/*================================================================
- * INCLUDE OTHER HEADER FILES
- *================================================================*/
-
-/* set up module */
-
-#if defined(AWE_MODULE_SUPPORT) && defined(MODULE)
-#include <linux/config.h>
-#include <linux/string.h>
-#include <linux/module.h>
-#include "../soundmodule.h"
-#endif
-
-
-/* reading configuration of sound driver */
-
-#ifdef AWE_OBSOLETE_VOXWARE
-
-#include "sound_config.h"
-#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AWE32)
-#define CONFIG_AWE32_SYNTH
-#endif
-
-#else /* AWE_OBSOLETE_VOXWARE */
-
-#ifdef HAS_LOWLEVEL_H
-#include "lowlevel.h"
-#endif
-
-#include "../sound_config.h"
-
-#endif /* AWE_OBSOLETE_VOXWARE */
-
-
-/*================================================================
- * include AWE header files
- *================================================================*/
-
-#if defined(CONFIG_AWE32_SYNTH) || defined(CONFIG_AWE32_SYNTH_MODULE)
-
-#include "awe_hw.h"
-#include "awe_version.h"
-#include <linux/awe_voice.h>
-
-#ifdef AWE_HAS_GUS_COMPATIBILITY
-/* include finetune table */
-#ifdef AWE_OBSOLETE_VOXWARE
-# include "tuning.h"
-#else
-# include "../tuning.h"
-#endif
-#include <linux/ultrasound.h>
-#endif /* AWE_HAS_GUS_COMPATIBILITY */
-
-
-/*----------------------------------------------------------------
- * compatibility macros for AWE32 driver
- *----------------------------------------------------------------*/
-
-/* redefine following macros */
-#undef IOCTL_IN
-#undef IOCTL_OUT
-#undef OUTW
-#undef COPY_FROM_USER
-#undef COPY_TO_USER
-#undef GET_BYTE_FROM_USER
-#undef GET_SHORT_FROM_USER
-#undef IOCTL_TO_USER
-
-/* use inline prefix */
-#define INLINE /*inline*/
-
-/*----------------------------------------------------------------
- * memory management for linux
- *----------------------------------------------------------------*/
-
-#ifdef AWE_OBSOLETE_VOXWARE
-/* old type linux system */
-
-/* i/o requests; nothing */
-#define awe_check_port() 0 /* always false */
-#define awe_request_region() /* nothing */
-#define awe_release_region() /* nothing */
-
-static int _mem_start; /* memory pointer for permanent buffers */
-
-#define my_malloc_init(memptr) _mem_start = (memptr)
-#define my_malloc_memptr() _mem_start
-#define my_free(ptr) /* do nothing */
-
-/* allocate buffer only once */
-#define INIT_TABLE(buffer,index,nums,type) {\
-PERMANENT_MALLOC(buffer, char*, size, _mem_start); index = (nums);\
-}
-
-#else
-
-#define AWE_DYNAMIC_BUFFER
-
-#define my_malloc_init(ptr) /* nothing */
-#define my_malloc_memptr() 0
-#define my_malloc(size) vmalloc(size)
-#define my_free(ptr) if (ptr) {vfree(ptr);}
-
-/* do not allocate buffer at beginning */
-#define INIT_TABLE(buffer,index,nums,type) {buffer=NULL; index=0;}
-
-/* old type macro */
-#define RET_ERROR(err) -err
-
-#endif
-
-/*----------------------------------------------------------------
- * i/o interfaces for linux
- *----------------------------------------------------------------*/
-
-#define OUTW(data,addr) outw(data, addr)
-
-#ifdef AWE_NEW_KERNEL_INTERFACE
-#define COPY_FROM_USER(target,source,offs,count) \
- copy_from_user(target, (source)+(offs), count)
-#define GET_BYTE_FROM_USER(target,addr,offs) \
- get_user(target, (unsigned char*)&((addr)[offs]))
-#define GET_SHORT_FROM_USER(target,addr,offs) \
- get_user(target, (unsigned short*)&((addr)[offs]))
-#ifdef AWE_OSS38
-#define IOCTL_TO_USER(target,offs,source,count) \
- memcpy(target, (source)+(offs), count)
-#define IO_WRITE_CHECK(cmd) (_SIOC_DIR(cmd) & _IOC_WRITE)
-#else
-#define IOCTL_TO_USER(target,offs,source,count) \
- copy_to_user(target, (source)+(offs), count)
-#define IO_WRITE_CHECK(cmd) (_IOC_DIR(cmd) & _IOC_WRITE)
-#endif /* AWE_OSS38 */
-#define COPY_TO_USER IOCTL_TO_USER
-#define IOCTL_IN(arg) (*(int*)(arg))
-#define IOCTL_OUT(arg,val) (*(int*)(arg) = (val))
-
-#else /* old type i/o */
-#define COPY_FROM_USER(target,source,offs,count) \
- memcpy_fromfs(target, (source)+(offs), (count))
-#define GET_BYTE_FROM_USER(target,addr,offs) \
- *((char *)&(target)) = get_fs_byte((addr)+(offs))
-#define GET_SHORT_FROM_USER(target,addr,offs) \
- *((short *)&(target)) = get_fs_word((addr)+(offs))
-#ifdef AWE_OSS38
-#define IOCTL_TO_USER(target,offs,source,count) \
- memcpy(target, (source)+(offs), count)
-#define COPY_TO_USER(target,offs,source,count) \
- memcpy_tofs(target, (source)+(offs), (count))
-#define IOCTL_IN(arg) (*(int*)(arg))
-#define IOCTL_OUT(arg,val) (*(int*)(arg) = (val))
-#define IO_WRITE_CHECK(cmd) (_SIOC_DIR(cmd) & _IOC_WRITE)
-#else /* AWE_OSS38 */
-#define IOCTL_TO_USER(target,offs,source,count) \
- memcpy_tofs(target, (source)+(offs), (count))
-#define COPY_TO_USER IOCTL_TO_USER
-#define IOCTL_IN(arg) get_fs_long((long *)(arg))
-#define IOCTL_OUT(arg,ret) snd_ioctl_return((int *)arg, ret)
-#define IO_WRITE_CHECK(cmd) (cmd & IOC_IN)
-#endif /* AWE_OSS38 */
-
-#endif /* AWE_NEW_KERNEL_INTERFACE */
-
-#define BZERO(target,len) memset(target, 0, len)
-#define MEMCPY(dst,src,len) memcpy(dst, src, len)
-#define MEMCMP(p1,p2,len) memcmp(p1, p2, len)
-
-/* old style device tables (not modulized) */
-#ifndef AWE_MODULE_SUPPORT
-
-#define sound_alloc_synthdev() \
- (num_synths >= MAX_SYNTH_DEV ? -1 : num_synths++)
-#define sound_alloc_mixerdev() \
- (num_mixers >= MAX_MIXER_DEV ? -1 : num_mixers++)
-#define sound_alloc_mididev() \
- (num_midis >= MAX_MIXER_DEV ? -1 : num_midis++)
-#define sound_unload_synthdev(dev) /**/
-#define sound_unload_mixerdev(dev) /**/
-#define sound_unload_mididev(dev) /**/
-
-#endif /* AWE_MODULE_SUPPORT */
-
-#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,0)
-inline static void interruptible_sleep_on_timeout(wait_queue_head_t *q, unsigned long timeout)
-{
- current->timeout = jiffies + timeout;
- interruptible_sleep_on(q);
-}
-#endif
-
-#endif /* CONFIG_AWE32_SYNTH */
-
-#endif /* AWE_COMPAT_H_DEF */
+++ /dev/null
-/*
- * sound/awe_config.h
- *
- * Configuration of AWE32/SB32/AWE64 wave table synth driver.
- * version 0.4.3; Mar. 1, 1998
- *
- * Copyright (C) 1996-1998 Takashi Iwai
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef AWE_CONFIG_H_DEF
-#define AWE_CONFIG_H_DEF
-
-/*----------------------------------------------------------------
- * system configuration
- *----------------------------------------------------------------*/
-
-/* if your kernel support module for each soundcard, define this.
- * NOTE: it will be automatically set on linux-2.1.x kernels.
- * only define here if you have moduler sound system on
- * 2.0.x kernel (like RedHat).
- */
-#undef AWE_MODULE_SUPPORT
-
-
-/*----------------------------------------------------------------
- * chorus & reverb effects send for FM chip: from 0 to 0xff
- * larger numbers often cause weird sounds.
- *----------------------------------------------------------------*/
-
-#define DEF_FM_CHORUS_DEPTH 0x10
-#define DEF_FM_REVERB_DEPTH 0x10
-
-
-/*----------------------------------------------------------------*
- * other compile conditions
- *----------------------------------------------------------------*/
-
-/* initialize FM passthrough even without extended RAM */
-#undef AWE_ALWAYS_INIT_FM
-
-/* debug on */
-#define AWE_DEBUG_ON
-
-/* GUS compatible mode */
-#define AWE_HAS_GUS_COMPATIBILITY
-
-/* add MIDI emulation by wavetable */
-#define CONFIG_AWE32_MIDIEMU
-
-/* add mixer control of emu8000 equalizer */
-#undef CONFIG_AWE32_MIXER
-
-/* use new volume calculation method as default */
-#define AWE_USE_NEW_VOLUME_CALC
-
-/* check current volume target for searching empty voices */
-#define AWE_CHECK_VTARGET
-
-/* allow sample sharing */
-#define AWE_ALLOW_SAMPLE_SHARING
-
-/*================================================================
- * Usually, you don't have to touch the following options.
- *================================================================*/
-
-/*----------------------------------------------------------------
- * AWE32 card configuration:
- * uncomment the following lines *ONLY* when auto detection doesn't
- * work properly on your machine.
- *----------------------------------------------------------------*/
-
-/*#define AWE_DEFAULT_BASE_ADDR 0x620*/ /* base port address */
-/*#define AWE_DEFAULT_MEM_SIZE 512*/ /* kbytes */
-
-/*----------------------------------------------------------------
- * maximum size of soundfont list table
- *----------------------------------------------------------------*/
-
-#define AWE_MAX_SF_LISTS 16
-
-/*----------------------------------------------------------------
- * chunk size of sample and voice tables
- *----------------------------------------------------------------*/
-
-#define AWE_MAX_SAMPLES 400
-#define AWE_MAX_INFOS 800
-
-#endif /* AWE_CONFIG_H_DEF */
+++ /dev/null
-/*
- * sound/awe_hw.h
- *
- * Access routines and definitions for the low level driver for the
- * Creative AWE32/SB32/AWE64 wave table synth.
- * version 0.4.3; Mar. 1, 1998
- *
- * Copyright (C) 1996-1998 Takashi Iwai
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef AWE_HW_H_DEF
-#define AWE_HW_H_DEF
-
-/*
- * Emu-8000 control registers
- * name(channel) reg, port
- */
-
-#define awe_cmd_idx(reg,ch) (((reg)<< 5) | (ch))
-
-#define Data0 0 /* 0x620: doubleword r/w */
-#define Data1 1 /* 0xA20: doubleword r/w */
-#define Data2 2 /* 0xA22: word r/w */
-#define Data3 3 /* 0xE20: word r/w */
-#define Pointer 4 /* 0xE22 register pointer r/w */
-
-#define AWE_CPF(ch) awe_cmd_idx(0,ch), Data0 /* DW: current pitch and fractional address */
-#define AWE_PTRX(ch) awe_cmd_idx(1,ch), Data0 /* DW: pitch target and reverb send */
-#define AWE_CVCF(ch) awe_cmd_idx(2,ch), Data0 /* DW: current volume and filter cutoff */
-#define AWE_VTFT(ch) awe_cmd_idx(3,ch), Data0 /* DW: volume and filter cutoff targets */
-#define AWE_0080(ch) awe_cmd_idx(4,ch), Data0 /* DW: ?? */
-#define AWE_00A0(ch) awe_cmd_idx(5,ch), Data0 /* DW: ?? */
-#define AWE_PSST(ch) awe_cmd_idx(6,ch), Data0 /* DW: pan send and loop start address */
-#define AWE_CSL(ch) awe_cmd_idx(7,ch), Data0 /* DW: chorus send and loop end address */
-#define AWE_CCCA(ch) awe_cmd_idx(0,ch), Data1 /* DW: Q, control bits, and current address */
-#define AWE_HWCF4 awe_cmd_idx(1,9), Data1 /* DW: config dw 4 */
-#define AWE_HWCF5 awe_cmd_idx(1,10), Data1 /* DW: config dw 5 */
-#define AWE_HWCF6 awe_cmd_idx(1,13), Data1 /* DW: config dw 6 */
-#define AWE_HWCF7 awe_cmd_idx(1,14), Data1 /* DW: config dw 7? (not documented) */
-#define AWE_SMALR awe_cmd_idx(1,20), Data1 /* DW: sound memory address for left read */
-#define AWE_SMARR awe_cmd_idx(1,21), Data1 /* DW: for right read */
-#define AWE_SMALW awe_cmd_idx(1,22), Data1 /* DW: sound memory address for left write */
-#define AWE_SMARW awe_cmd_idx(1,23), Data1 /* DW: for right write */
-#define AWE_SMLD awe_cmd_idx(1,26), Data1 /* W: sound memory left data */
-#define AWE_SMRD awe_cmd_idx(1,26), Data2 /* W: right data */
-#define AWE_WC awe_cmd_idx(1,27), Data2 /* W: sample counter */
-#define AWE_WC_Cmd awe_cmd_idx(1,27)
-#define AWE_WC_Port Data2
-#define AWE_HWCF1 awe_cmd_idx(1,29), Data1 /* W: config w 1 */
-#define AWE_HWCF2 awe_cmd_idx(1,30), Data1 /* W: config w 2 */
-#define AWE_HWCF3 awe_cmd_idx(1,31), Data1 /* W: config w 3 */
-#define AWE_INIT1(ch) awe_cmd_idx(2,ch), Data1 /* W: init array 1 */
-#define AWE_INIT2(ch) awe_cmd_idx(2,ch), Data2 /* W: init array 2 */
-#define AWE_INIT3(ch) awe_cmd_idx(3,ch), Data1 /* W: init array 3 */
-#define AWE_INIT4(ch) awe_cmd_idx(3,ch), Data2 /* W: init array 4 */
-#define AWE_ENVVOL(ch) awe_cmd_idx(4,ch), Data1 /* W: volume envelope delay */
-#define AWE_DCYSUSV(ch) awe_cmd_idx(5,ch), Data1 /* W: volume envelope sustain and decay */
-#define AWE_ENVVAL(ch) awe_cmd_idx(6,ch), Data1 /* W: modulation envelope delay */
-#define AWE_DCYSUS(ch) awe_cmd_idx(7,ch), Data1 /* W: modulation envelope sustain and decay */
-#define AWE_ATKHLDV(ch) awe_cmd_idx(4,ch), Data2 /* W: volume envelope attack and hold */
-#define AWE_LFO1VAL(ch) awe_cmd_idx(5,ch), Data2 /* W: LFO#1 Delay */
-#define AWE_ATKHLD(ch) awe_cmd_idx(6,ch), Data2 /* W: modulation envelope attack and hold */
-#define AWE_LFO2VAL(ch) awe_cmd_idx(7,ch), Data2 /* W: LFO#2 Delay */
-#define AWE_IP(ch) awe_cmd_idx(0,ch), Data3 /* W: initial pitch */
-#define AWE_IFATN(ch) awe_cmd_idx(1,ch), Data3 /* W: initial filter cutoff and attenuation */
-#define AWE_PEFE(ch) awe_cmd_idx(2,ch), Data3 /* W: pitch and filter envelope heights */
-#define AWE_FMMOD(ch) awe_cmd_idx(3,ch), Data3 /* W: vibrato and filter modulation freq */
-#define AWE_TREMFRQ(ch) awe_cmd_idx(4,ch), Data3 /* W: LFO#1 tremolo amount and freq */
-#define AWE_FM2FRQ2(ch) awe_cmd_idx(5,ch), Data3 /* W: LFO#2 vibrato amount and freq */
-
-/* used during detection (returns ROM version?; not documented in ADIP) */
-#define AWE_U1 0xE0, Data3 /* (R)(W) used in initialization */
-#define AWE_U2(ch) 0xC0+(ch), Data3 /* (W)(W) used in init envelope */
-
-
-#define AWE_MAX_VOICES 32
-#define AWE_NORMAL_VOICES 30 /*30&31 are reserved for DRAM refresh*/
-
-#define AWE_MAX_CHANNELS 32 /* max midi channels (must >= voices) */
-#define AWE_MAX_LAYERS AWE_MAX_VOICES /* maximum number of multiple layers */
-
-#define AWE_DRAM_OFFSET 0x200000
-#define AWE_MAX_DRAM_SIZE (28 * 1024) /* 28 MB is max onboard memory */
-
-#endif
+++ /dev/null
-/*
- * sound/awe_version.h
- *
- * Version defines for the AWE32/SB32/AWE64 wave table synth driver.
- * version 0.4.3; Mar. 1, 1998
- *
- * Copyright (C) 1996-1998 Takashi Iwai
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* AWE32 driver version number */
-
-#ifndef AWE_VERSION_H_DEF
-#define AWE_VERSION_H_DEF
-
-#define AWE_MAJOR_VERSION 0
-#define AWE_MINOR_VERSION 4
-#define AWE_TINY_VERSION 3
-#define AWE_VERSION_NUMBER ((AWE_MAJOR_VERSION<<16)|(AWE_MINOR_VERSION<<8)|AWE_TINY_VERSION)
-#define AWEDRV_VERSION "0.4.3"
-
-#endif
+++ /dev/null
-/*
- * sound/awe_wave.c
- *
- * The low level driver for the AWE32/SB32/AWE64 wave table synth.
- * version 0.4.3; Feb. 1, 1999
- *
- * Copyright (C) 1996-1999 Takashi Iwai
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* include initial header files and compatibility macros */
-#ifdef __FreeBSD__
-# include <i386/isa/sound/awe_compat.h>
-#else
-# include "awe_compat.h"
-#endif /* FreeBSD */
-#ifdef __linux__
-# include <linux/config.h>
-#endif
-
-/*----------------------------------------------------------------*/
-
-#if defined(CONFIG_AWE32_SYNTH) || defined(CONFIG_AWE32_SYNTH_MODULE)
-
-/*----------------------------------------------------------------
- * debug message
- *----------------------------------------------------------------*/
-
-#ifdef AWE_DEBUG_ON
-#define DEBUG(LVL,XXX) {if (ctrls[AWE_MD_DEBUG_MODE] > LVL) { XXX; }}
-#define ERRMSG(XXX) {if (ctrls[AWE_MD_DEBUG_MODE]) { XXX; }}
-#define FATALERR(XXX) XXX
-#else
-#define DEBUG(LVL,XXX) /**/
-#define ERRMSG(XXX) XXX
-#define FATALERR(XXX) XXX
-#endif
-
-/*----------------------------------------------------------------
- * bank and voice record
- *----------------------------------------------------------------*/
-
-/* soundfont record */
-typedef struct _sf_list {
- unsigned short sf_id;
- unsigned short type;
- int num_info; /* current info table index */
- int num_sample; /* current sample table index */
- int mem_ptr; /* current word byte pointer */
- int infos;
- int samples;
-#ifdef AWE_ALLOW_SAMPLE_SHARING
- int shared; /* shared index */
- unsigned char name[AWE_PATCH_NAME_LEN];
-#endif
-} sf_list;
-
-/* bank record */
-typedef struct _awe_voice_list {
- int next; /* linked list with same sf_id */
- unsigned char bank, instr; /* preset number information */
- char type, disabled; /* type=normal/mapped, disabled=boolean */
- awe_voice_info v; /* voice information */
- int next_instr; /* preset table list */
- int next_bank; /* preset table list */
-} awe_voice_list;
-
-/* voice list type */
-#define V_ST_NORMAL 0
-#define V_ST_MAPPED 1
-
-typedef struct _awe_sample_list {
- int next; /* linked list with same sf_id */
- awe_sample_info v; /* sample information */
-} awe_sample_list;
-
-/* sample and information table */
-static int current_sf_id = 0;
-static int locked_sf_id = 0;
-static int max_sfs;
-static sf_list *sflists = NULL;
-
-#define awe_free_mem_ptr() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].mem_ptr)
-#define awe_free_info() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_info)
-#define awe_free_sample() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_sample)
-
-static int max_samples;
-static awe_sample_list *samples = NULL;
-
-static int max_infos;
-static awe_voice_list *infos = NULL;
-
-
-#define AWE_MAX_PRESETS 256
-#define AWE_DEFAULT_PRESET 0
-#define AWE_DEFAULT_BANK 0
-#define AWE_DEFAULT_DRUM 0
-#define AWE_DRUM_BANK 128
-
-#define MAX_LAYERS AWE_MAX_VOICES
-
-/* preset table index */
-static int preset_table[AWE_MAX_PRESETS];
-
-/*----------------------------------------------------------------
- * voice table
- *----------------------------------------------------------------*/
-
-/* effects table */
-typedef struct FX_Rec { /* channel effects */
- unsigned char flags[AWE_FX_END];
- short val[AWE_FX_END];
-} FX_Rec;
-
-
-/* channel parameters */
-typedef struct _awe_chan_info {
- int channel; /* channel number */
- int bank; /* current tone bank */
- int instr; /* current program */
- int bender; /* midi pitchbend (-8192 - 8192) */
- int bender_range; /* midi bender range (x100) */
- int panning; /* panning (0-127) */
- int main_vol; /* channel volume (0-127) */
- int expression_vol; /* midi expression (0-127) */
- int chan_press; /* channel pressure */
- int vrec; /* instrument list */
- int def_vrec; /* default instrument list */
- int sustained; /* sustain status in MIDI */
- FX_Rec fx; /* effects */
- FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */
-} awe_chan_info;
-
-/* voice parameters */
-typedef struct _voice_info {
- int state;
-#define AWE_ST_OFF (1<<0) /* no sound */
-#define AWE_ST_ON (1<<1) /* playing */
-#define AWE_ST_STANDBY (1<<2) /* stand by for playing */
-#define AWE_ST_SUSTAINED (1<<3) /* sustained */
-#define AWE_ST_MARK (1<<4) /* marked for allocation */
-#define AWE_ST_DRAM (1<<5) /* DRAM read/write */
-#define AWE_ST_FM (1<<6) /* reserved for FM */
-#define AWE_ST_RELEASED (1<<7) /* released */
-
- int ch; /* midi channel */
- int key; /* internal key for search */
- int layer; /* layer number (for channel mode only) */
- int time; /* allocated time */
- awe_chan_info *cinfo; /* channel info */
-
- int note; /* midi key (0-127) */
- int velocity; /* midi velocity (0-127) */
- int sostenuto; /* sostenuto on/off */
- awe_voice_info *sample; /* assigned voice */
-
- /* EMU8000 parameters */
- int apitch; /* pitch parameter */
- int avol; /* volume parameter */
- int apan; /* panning parameter */
- int acutoff; /* cutoff parameter */
- short aaux; /* aux word */
-} voice_info;
-
-/* voice information */
-static voice_info voices[AWE_MAX_VOICES];
-
-#define IS_NO_SOUND(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_RELEASED|AWE_ST_STANDBY|AWE_ST_SUSTAINED))
-#define IS_NO_EFFECT(v) (voices[v].state != AWE_ST_ON)
-#define IS_PLAYING(v) (voices[v].state & (AWE_ST_ON|AWE_ST_SUSTAINED|AWE_ST_RELEASED))
-#define IS_EMPTY(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_MARK|AWE_ST_DRAM|AWE_ST_FM))
-
-
-/* MIDI channel effects information (for hw control) */
-static awe_chan_info channels[AWE_MAX_CHANNELS];
-
-
-/*----------------------------------------------------------------
- * global variables
- *----------------------------------------------------------------*/
-
-#ifndef AWE_DEFAULT_BASE_ADDR
-#define AWE_DEFAULT_BASE_ADDR 0 /* autodetect */
-#endif
-
-#ifndef AWE_DEFAULT_MEM_SIZE
-#define AWE_DEFAULT_MEM_SIZE -1 /* autodetect */
-#endif
-
-/* set variables */
-#if defined(AWE_MODULE_SUPPORT) && defined(MODULE)
-/* replace awe_port variable with exported variable */
-#define awe_port io
-#define awe_mem_size memsize
-int io = AWE_DEFAULT_BASE_ADDR; /* Emu8000 base address */
-int memsize = AWE_DEFAULT_MEM_SIZE; /* memory size in Kbytes */
-#ifdef MODULE_PARM
-MODULE_PARM(io, "i");
-MODULE_PARM_DESC(io, "base i/o port of Emu8000");
-MODULE_PARM(memsize, "i");
-MODULE_PARM_DESC(memsize, "onboard DRAM size in Kbytes");
-#endif
-#else
-static int awe_port = AWE_DEFAULT_BASE_ADDR;
-static int awe_mem_size = AWE_DEFAULT_MEM_SIZE;
-#endif /* module */
-
-/* DRAM start offset */
-static int awe_mem_start = AWE_DRAM_OFFSET;
-
-/* maximum channels for playing */
-static int awe_max_voices = AWE_MAX_VOICES;
-
-static int patch_opened = 0; /* sample already loaded? */
-
-static char atten_relative = FALSE;
-static short atten_offset = 0;
-
-static int awe_present = FALSE; /* awe device present? */
-static int awe_busy = FALSE; /* awe device opened? */
-
-static int my_dev = -1;
-
-#define DEFAULT_DRUM_FLAGS ((1 << 9) | (1 << 25))
-#define IS_DRUM_CHANNEL(c) (drum_flags & (1 << (c)))
-#define DRUM_CHANNEL_ON(c) (drum_flags |= (1 << (c)))
-#define DRUM_CHANNEL_OFF(c) (drum_flags &= ~(1 << (c)))
-static unsigned int drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */
-
-static int playing_mode = AWE_PLAY_INDIRECT;
-#define SINGLE_LAYER_MODE() (playing_mode == AWE_PLAY_INDIRECT || playing_mode == AWE_PLAY_DIRECT)
-#define MULTI_LAYER_MODE() (playing_mode == AWE_PLAY_MULTI || playing_mode == AWE_PLAY_MULTI2)
-
-static int current_alloc_time = 0; /* voice allocation index for channel mode */
-
-static struct synth_info awe_info = {
- "AWE32 Synth", /* name */
- 0, /* device */
- SYNTH_TYPE_SAMPLE, /* synth_type */
- SAMPLE_TYPE_AWE32, /* synth_subtype */
- 0, /* perc_mode (obsolete) */
- AWE_MAX_VOICES, /* nr_voices */
- 0, /* nr_drums (obsolete) */
- AWE_MAX_INFOS /* instr_bank_size */
-};
-
-
-static struct voice_alloc_info *voice_alloc; /* set at initialization */
-
-
-/*----------------------------------------------------------------
- * function prototypes
- *----------------------------------------------------------------*/
-
-#if defined(linux) && !defined(AWE_OBSOLETE_VOXWARE)
-static int awe_check_port(void);
-static void awe_request_region(void);
-static void awe_release_region(void);
-#endif /* linux & obsolete */
-
-static void awe_reset_samples(void);
-/* emu8000 chip i/o access */
-static void setup_ports(int p1, int p2, int p3);
-static void awe_poke(unsigned short cmd, unsigned short port, unsigned short data);
-static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data);
-static unsigned short awe_peek(unsigned short cmd, unsigned short port);
-static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port);
-static void awe_wait(unsigned short delay);
-
-/* initialize emu8000 chip */
-static int _attach_awe(void);
-static void _unload_awe(void);
-static void awe_initialize(void);
-
-/* set voice parameters */
-static void awe_init_ctrl_parms(int init_all);
-static void awe_init_voice_info(awe_voice_info *vp);
-static void awe_init_voice_parm(awe_voice_parm *pp);
-#ifdef AWE_HAS_GUS_COMPATIBILITY
-static int freq_to_note(int freq);
-static int calc_rate_offset(int Hz);
-/*static int calc_parm_delay(int msec);*/
-static int calc_parm_hold(int msec);
-static int calc_parm_attack(int msec);
-static int calc_parm_decay(int msec);
-static int calc_parm_search(int msec, short *table);
-#endif /* gus compat */
-
-/* turn on/off note */
-static void awe_note_on(int voice);
-static void awe_note_off(int voice);
-static void awe_terminate(int voice);
-static void awe_exclusive_off(int voice);
-static void awe_note_off_all(int do_sustain);
-
-/* calculate voice parameters */
-typedef void (*fx_affect_func)(int voice, int forced);
-static void awe_set_pitch(int voice, int forced);
-static void awe_set_voice_pitch(int voice, int forced);
-static void awe_set_volume(int voice, int forced);
-static void awe_set_voice_vol(int voice, int forced);
-static void awe_set_pan(int voice, int forced);
-static void awe_fx_fmmod(int voice, int forced);
-static void awe_fx_tremfrq(int voice, int forced);
-static void awe_fx_fm2frq2(int voice, int forced);
-static void awe_fx_filterQ(int voice, int forced);
-static void awe_calc_pitch(int voice);
-#ifdef AWE_HAS_GUS_COMPATIBILITY
-static void awe_calc_pitch_from_freq(int voice, int freq);
-#endif
-static void awe_calc_volume(int voice);
-static void awe_update_volume(void);
-static void awe_change_master_volume(short val);
-static void awe_voice_init(int voice, int init_all);
-static void awe_channel_init(int ch, int init_all);
-static void awe_fx_init(int ch);
-static void awe_send_effect(int voice, int layer, int type, int val);
-static void awe_modwheel_change(int voice, int value);
-
-/* sequencer interface */
-static int awe_open(int dev, int mode);
-static void awe_close(int dev);
-static int awe_ioctl(int dev, unsigned int cmd, caddr_t arg);
-static int awe_kill_note(int dev, int voice, int note, int velocity);
-static int awe_start_note(int dev, int v, int note_num, int volume);
-static int awe_set_instr(int dev, int voice, int instr_no);
-static int awe_set_instr_2(int dev, int voice, int instr_no);
-static void awe_reset(int dev);
-static void awe_hw_control(int dev, unsigned char *event);
-static int awe_load_patch(int dev, int format, const char *addr,
- int offs, int count, int pmgr_flag);
-static void awe_aftertouch(int dev, int voice, int pressure);
-static void awe_controller(int dev, int voice, int ctrl_num, int value);
-static void awe_panning(int dev, int voice, int value);
-static void awe_volume_method(int dev, int mode);
-#ifndef AWE_NO_PATCHMGR
-static int awe_patchmgr(int dev, struct patmgr_info *rec);
-#endif
-static void awe_bender(int dev, int voice, int value);
-static int awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc);
-static void awe_setup_voice(int dev, int voice, int chn);
-
-#define awe_key_pressure(dev,voice,key,press) awe_start_note(dev,voice,(key)+128,press)
-
-/* hardware controls */
-#ifdef AWE_HAS_GUS_COMPATIBILITY
-static void awe_hw_gus_control(int dev, int cmd, unsigned char *event);
-#endif
-static void awe_hw_awe_control(int dev, int cmd, unsigned char *event);
-static void awe_voice_change(int voice, fx_affect_func func);
-static void awe_sostenuto_on(int voice, int forced);
-static void awe_sustain_off(int voice, int forced);
-static void awe_terminate_and_init(int voice, int forced);
-
-/* voice search */
-static int awe_search_instr(int bank, int preset);
-static int awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist);
-static void awe_alloc_multi_voices(int ch, int note, int velocity, int key);
-static void awe_alloc_one_voice(int voice, int note, int velocity);
-static int awe_clear_voice(void);
-
-/* load / remove patches */
-static int awe_open_patch(awe_patch_info *patch, const char *addr, int count);
-static int awe_close_patch(awe_patch_info *patch, const char *addr, int count);
-static int awe_unload_patch(awe_patch_info *patch, const char *addr, int count);
-static int awe_load_info(awe_patch_info *patch, const char *addr, int count);
-static int awe_load_data(awe_patch_info *patch, const char *addr, int count);
-static int awe_replace_data(awe_patch_info *patch, const char *addr, int count);
-static int awe_load_map(awe_patch_info *patch, const char *addr, int count);
-#ifdef AWE_HAS_GUS_COMPATIBILITY
-static int awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag);
-#endif
-/*static int awe_probe_info(awe_patch_info *patch, const char *addr, int count);*/
-static int awe_probe_data(awe_patch_info *patch, const char *addr, int count);
-static int check_patch_opened(int type, char *name);
-static int awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels);
-static void add_sf_info(int rec);
-static void add_sf_sample(int rec);
-static void purge_old_list(int rec, int next);
-static void add_info_list(int rec);
-static void awe_remove_samples(int sf_id);
-static void rebuild_preset_list(void);
-static short awe_set_sample(awe_voice_info *vp);
-static int search_sample_index(int sf, int sample, int level);
-
-#ifdef AWE_ALLOW_SAMPLE_SHARING
-static int is_identical_id(int id1, int id2);
-static int is_identical_name(unsigned char *name, int id);
-static int is_shared_sf(unsigned char *name);
-static int info_duplicated(awe_voice_list *rec);
-#endif /* allow sharing */
-
-/* lowlevel functions */
-static void awe_init_audio(void);
-static void awe_init_dma(void);
-static void awe_init_array(void);
-static void awe_send_array(unsigned short *data);
-static void awe_tweak_voice(int voice);
-static void awe_tweak(void);
-static void awe_init_fm(void);
-static int awe_open_dram_for_write(int offset, int channels);
-static void awe_open_dram_for_check(void);
-static void awe_close_dram(void);
-static void awe_write_dram(unsigned short c);
-static int awe_detect_base(int addr);
-static int awe_detect(void);
-static void awe_check_dram(void);
-static int awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count);
-static void awe_set_chorus_mode(int mode);
-static void awe_update_chorus_mode(void);
-static int awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count);
-static void awe_set_reverb_mode(int mode);
-static void awe_update_reverb_mode(void);
-static void awe_equalizer(int bass, int treble);
-static void awe_update_equalizer(void);
-
-#ifdef CONFIG_AWE32_MIXER
-static void attach_mixer(void);
-static void unload_mixer(void);
-#endif
-
-#ifdef CONFIG_AWE32_MIDIEMU
-static void attach_midiemu(void);
-static void unload_midiemu(void);
-#endif
-
-#define limitvalue(x, a, b) if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b)
-
-#ifdef __FreeBSD__
-/* FIXME */
-#define MALLOC_LOOP_DATA
-#define WAIT_BY_LOOP
-#endif
-
-/*----------------------------------------------------------------
- * control parameters
- *----------------------------------------------------------------*/
-
-
-#ifdef AWE_USE_NEW_VOLUME_CALC
-#define DEF_VOLUME_CALC TRUE
-#else
-#define DEF_VOLUME_CALC FALSE
-#endif /* new volume */
-
-#define DEF_ZERO_ATTEN 32 /* 12dB below */
-#define DEF_MOD_SENSE 18
-#define DEF_CHORUS_MODE 2
-#define DEF_REVERB_MODE 4
-#define DEF_BASS_LEVEL 5
-#define DEF_TREBLE_LEVEL 9
-
-static struct CtrlParmsDef {
- int value;
- int init_each_time;
- void (*update)(void);
-} ctrl_parms[AWE_MD_END] = {
- {0,0, NULL}, {0,0, NULL}, /* <-- not used */
- {AWE_VERSION_NUMBER, FALSE, NULL},
- {TRUE, FALSE, NULL}, /* exclusive */
- {TRUE, FALSE, NULL}, /* realpan */
- {AWE_DEFAULT_BANK, FALSE, NULL}, /* gusbank */
- {FALSE, TRUE, NULL}, /* keep effect */
- {DEF_ZERO_ATTEN, FALSE, awe_update_volume}, /* zero_atten */
- {FALSE, FALSE, NULL}, /* chn_prior */
- {DEF_MOD_SENSE, FALSE, NULL}, /* modwheel sense */
- {AWE_DEFAULT_PRESET, FALSE, NULL}, /* def_preset */
- {AWE_DEFAULT_BANK, FALSE, NULL}, /* def_bank */
- {AWE_DEFAULT_DRUM, FALSE, NULL}, /* def_drum */
- {FALSE, FALSE, NULL}, /* toggle_drum_bank */
- {DEF_VOLUME_CALC, FALSE, awe_update_volume}, /* new_volume_calc */
- {DEF_CHORUS_MODE, FALSE, awe_update_chorus_mode}, /* chorus mode */
- {DEF_REVERB_MODE, FALSE, awe_update_reverb_mode}, /* reverb mode */
- {DEF_BASS_LEVEL, FALSE, awe_update_equalizer}, /* bass level */
- {DEF_TREBLE_LEVEL, FALSE, awe_update_equalizer}, /* treble level */
- {0, FALSE, NULL}, /* debug mode */
- {FALSE, FALSE, NULL}, /* pan exchange */
-};
-
-static int ctrls[AWE_MD_END];
-
-
-/*----------------------------------------------------------------
- * synth operation table
- *----------------------------------------------------------------*/
-
-static struct synth_operations awe_operations =
-{
-#ifdef AWE_OSS38
- "EMU8K",
-#endif
- &awe_info,
- 0,
- SYNTH_TYPE_SAMPLE,
- SAMPLE_TYPE_AWE32,
- awe_open,
- awe_close,
- awe_ioctl,
- awe_kill_note,
- awe_start_note,
- awe_set_instr_2,
- awe_reset,
- awe_hw_control,
- awe_load_patch,
- awe_aftertouch,
- awe_controller,
- awe_panning,
- awe_volume_method,
-#ifndef AWE_NO_PATCHMGR
- awe_patchmgr,
-#endif
- awe_bender,
- awe_alloc,
- awe_setup_voice
-};
-
-
-/*================================================================
- * General attach / unload interface
- *================================================================*/
-
-static int _attach_awe(void)
-{
- if (awe_present) return 0; /* for OSS38.. called twice? */
-
- /* check presence of AWE32 card */
- if (! awe_detect()) {
- printk(KERN_WARNING "AWE32: not detected\n");
- return 0;
- }
-
- /* check AWE32 ports are available */
-#if defined(linux) && !defined(AWE_OBSOLETE_VOXWARE)
- if (awe_check_port()) {
- printk(KERN_WARNING "AWE32: I/O area already used.\n");
- return 0;
- }
-#endif
-
- /* set buffers to NULL */
- sflists = NULL;
- samples = NULL;
- infos = NULL;
-
- /* allocate sample tables */
- INIT_TABLE(sflists, max_sfs, AWE_MAX_SF_LISTS, sf_list);
- INIT_TABLE(samples, max_samples, AWE_MAX_SAMPLES, awe_sample_list);
- INIT_TABLE(infos, max_infos, AWE_MAX_INFOS, awe_voice_list);
-
- my_dev = sound_alloc_synthdev();
- if (my_dev == -1) {
- printk(KERN_WARNING "AWE32 Error: too many synthesizers\n");
- return 0;
- }
-
- voice_alloc = &awe_operations.alloc;
- voice_alloc->max_voice = awe_max_voices;
- synth_devs[my_dev] = &awe_operations;
-
-#ifdef CONFIG_AWE32_MIXER
- attach_mixer();
-#endif
-#ifdef CONFIG_AWE32_MIDIEMU
- attach_midiemu();
-#endif
-
-#if defined(linux) && !defined(AWE_OBSOLETE_VOXWARE)
- /* reserve I/O ports for awedrv */
- awe_request_region();
-#endif
-
- /* clear all samples */
- awe_reset_samples();
-
- /* intialize AWE32 hardware */
- awe_initialize();
-
- sprintf(awe_info.name, "AWE32-%s (RAM%dk)",
- AWEDRV_VERSION, awe_mem_size/1024);
- printk("<SoundBlaster EMU8000 (RAM%dk)>\n", awe_mem_size/1024);
-
- awe_present = TRUE;
-
-#if defined(AWE_MODULE_SUPPORT) && defined(MODULE)
- SOUND_LOCK;
-#endif
-
- return 1;
-}
-
-
-#ifdef AWE_DYNAMIC_BUFFER
-static void free_tables(void)
-{
- if (sflists)
- my_free(sflists);
- sflists = NULL; max_sfs = 0;
- if (samples)
- my_free(samples);
- samples = NULL; max_samples = 0;
- if (infos)
- my_free(infos);
- infos = NULL; max_infos = 0;
-}
-
-static void *realloc_block(void *buf, int oldsize, int size)
-{
- void *ptr;
- if (oldsize == size)
- return buf;
- if ((ptr = my_malloc(size)) == NULL)
- return NULL;
- if (oldsize && size)
- MEMCPY(ptr, buf, ((oldsize < size) ? oldsize : size) );
- if (buf)
- my_free(buf);
- return ptr;
-}
-
-
-#else /* dynamic buffer */
-
-#define free_buffers() /**/
-
-#endif /* dynamic_buffer */
-
-
-static void _unload_awe(void)
-{
- if (awe_present) {
- awe_reset_samples();
- awe_release_region();
- free_tables();
-#ifdef CONFIG_AWE32_MIXER
- unload_mixer();
-#endif
-#ifdef CONFIG_AWE32_MIDIEMU
- unload_midiemu();
-#endif
- sound_unload_synthdev(my_dev);
- awe_present = FALSE;
-#if defined(AWE_MODULE_SUPPORT) && defined(MODULE)
- SOUND_LOCK_END;
-#endif
- }
-}
-
-
-/*================================================================
- * Linux interface
- *================================================================*/
-
-#ifdef linux
-
-/*----------------------------------------------------------------
- * Linux PnP driver support
- *----------------------------------------------------------------*/
-
-#ifdef CONFIG_PNP_DRV
-
-#include <linux/pnp.h>
-
-static int pnp = 1; /* use PnP as default */
-
-#define AWE_NUM_CHIPS 3
-static unsigned int pnp_ids[AWE_NUM_CHIPS] = {
- PNP_EISAID('C','T','L',0x0021),
- PNP_EISAID('C','T','L',0x0022),
- PNP_EISAID('C','T','L',0x0023),
-};
-static struct pnp_driver pnp_awe[AWE_NUM_CHIPS];
-static int awe_pnp_ok = 0;
-
-static void awe_pnp_config(struct pnp_device *d)
-{
- struct pnp_resource *r;
- int port[3];
- int nio = 0;
-
- port[0] = port[1] = port[2] = 0;
- for (r = d->res; r != NULL; r = r->next) {
- if (r->type == PNP_RES_IO) {
- if (nio >= 0 && nio < 3)
- port[nio] = r->start;
- nio++;
- }
- }
- setup_ports(port[0], port[1], port[2]);
- DEBUG(0,printk("AWE32: PnP setup ports: %x:%x:%x\n", port[0], port[1], port[2]));
-}
-
-static int awe_pnp_event (struct pnp_device *d, struct pnp_drv_event *e)
-{
- struct pnp_driver *drv = d->l.k.driver;
-
- switch (e->type) {
- case PNP_DRV_ALLOC:
- drv->flags |= PNP_DRV_INUSE;
- awe_pnp_ok = 1;
- awe_pnp_config(d);
- _attach_awe();
- break;
-
- case PNP_DRV_DISABLE:
- case PNP_DRV_EMERGSTOP:
- drv->flags &= ~PNP_DRV_INUSE;
- awe_pnp_ok = 0;
- _unload_awe();
- break;
-
- case PNP_DRV_CONFIG:
- if (awe_busy) return 1; /* used now */
- awe_release_region();
- awe_pnp_config(d);
- awe_request_region();
- break;
-
- case PNP_DRV_RECONFIG:
- break;
- }
- return 0;
-}
-
-static int awe_initpnp (void)
-{
- int i;
- for (i = 0; i < AWE_NUM_CHIPS; i++) {
- pnp_awe[i].id.type = PNP_HDL_ISA;
- pnp_awe[i].id.t.isa.id = pnp_ids[i];
- pnp_awe[i].id.next = NULL;
- pnp_awe[i].name = "Soundblaster AWE32/AWE64 PnP";
- pnp_awe[i].event = awe_pnp_event;
- pnp_register_driver(&pnp_awe[i], 1);
- }
- return 0;
-}
-
-static void awe_unload_pnp (void)
-{
- int i;
- for (i = 0; i < AWE_NUM_CHIPS; i++)
- pnp_unregister_driver(&pnp_awe[i]);
-}
-#endif /* PnP support */
-
-/*----------------------------------------------------------------
- * device / lowlevel (module) interface
- *----------------------------------------------------------------*/
-
-#ifdef AWE_OBSOLETEL_VOXWARE
-
-/* old type interface */
-int attach_awe_obsolete(int mem_start, struct address_info *hw_config)
-{
- my_malloc_init(mem_start);
- if (! _attach_awe())
- return 0;
- return my_malloc_memptr();
-}
-
-int probe_awe_obsolete(struct address_info *hw_config)
-{
- return 1;
- /*return awe_detect();*/
-}
-
-#else /* !obsolete */
-
-/* new type interface */
-int attach_awe(void)
-{
-#ifdef CONFIG_PNP_DRV
- if (pnp) {
- awe_initpnp();
- if (awe_pnp_ok)
- return 0;
- }
-#endif /* pnp */
- _attach_awe();
- return 0;
-}
-
-void unload_awe(void)
-{
-#ifdef CONFIG_PNP_DRV
- if (pnp)
- awe_unload_pnp();
-#endif /* pnp */
- _unload_awe();
-}
-
-/* module interface */
-
-#if defined(AWE_MODULE_SUPPORT) && defined(MODULE)
-int init_module(void)
-{
- return attach_awe();
-}
-
-void cleanup_module(void)
-{
- unload_awe();
-}
-
-#ifdef MODULE_PARM
-EXPORT_NO_SYMBOLS;
-MODULE_AUTHOR("Takashi Iwai <iwai@ww.uni-erlangen.de>");
-MODULE_DESCRIPTION("SB AWE32/64 WaveTable driver");
-MODULE_SUPPORTED_DEVICE("sound");
-#endif
-
-#endif /* module */
-
-#endif /* AWE_OBSOLETE_VOXWARE */
-
-#endif /* linux */
-
-
-/*================================================================
- * FreeBSD interface
- *================================================================*/
-
-#ifdef __FreeBSD__
-
-#ifdef AWE_OBSOLETE_VOXWARE
-long attach_awe_obsolete(long mem_start, struct address_info *hw_config)
-{
- _attach_awe();
- return 0;
-}
-
-int probe_awe_obsolete(struct address_info *hw_config)
-{
- return 1;
-}
-
-#else /* !obsolete */
-
-/* new type interface */
-void attach_awe(struct address_info *hw_config)
-{
- _attach_awe();
-}
-
-int probe_awe(struct address_info *hw_config)
-{
- return 1;
-}
-
-#endif /* obsolete */
-
-#endif /* FreeBSD */
-
-
-/*================================================================
- * clear sample tables
- *================================================================*/
-
-static void
-awe_reset_samples(void)
-{
- int i;
-
- /* free all bank tables */
- for (i = 0; i < AWE_MAX_PRESETS; i++)
- preset_table[i] = -1;
-
- free_tables();
-
- current_sf_id = 0;
- locked_sf_id = 0;
- patch_opened = 0;
-}
-
-
-/*================================================================
- * EMU register access
- *================================================================*/
-
-/* select a given AWE32 pointer */
-static int awe_ports[5];
-static int port_setuped = FALSE;
-static int awe_cur_cmd = -1;
-#define awe_set_cmd(cmd) \
-if (awe_cur_cmd != cmd) { OUTW(cmd, awe_ports[Pointer]); awe_cur_cmd = cmd; }
-
-/* store values to i/o port array */
-static void setup_ports(int port1, int port2, int port3)
-{
- awe_ports[0] = port1;
- if (port2 == 0)
- port2 = port1 + 0x400;
- awe_ports[1] = port2;
- awe_ports[2] = port2 + 2;
- if (port3 == 0)
- port3 = port1 + 0x800;
- awe_ports[3] = port3;
- awe_ports[4] = port3 + 2;
-
- port_setuped = TRUE;
-}
-
-/* write 16bit data */
-INLINE static void
-awe_poke(unsigned short cmd, unsigned short port, unsigned short data)
-{
- awe_set_cmd(cmd);
- OUTW(data, awe_ports[port]);
-}
-
-/* write 32bit data */
-INLINE static void
-awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data)
-{
- unsigned short addr = awe_ports[port];
- awe_set_cmd(cmd);
- OUTW(data, addr); /* write lower 16 bits */
- OUTW(data >> 16, addr + 2); /* write higher 16 bits */
-}
-
-/* read 16bit data */
-INLINE static unsigned short
-awe_peek(unsigned short cmd, unsigned short port)
-{
- unsigned short k;
- awe_set_cmd(cmd);
- k = inw(awe_ports[port]);
- return k;
-}
-
-/* read 32bit data */
-INLINE static unsigned int
-awe_peek_dw(unsigned short cmd, unsigned short port)
-{
- unsigned int k1, k2;
- unsigned short addr = awe_ports[port];
- awe_set_cmd(cmd);
- k1 = inw(addr);
- k2 = inw(addr + 2);
- k1 |= k2 << 16;
- return k1;
-}
-
-/* wait delay number of AWE32 44100Hz clocks */
-#ifdef WAIT_BY_LOOP /* wait by loop -- that's not good.. */
-static void
-awe_wait(unsigned short delay)
-{
- unsigned short clock, target;
- unsigned short port = awe_ports[AWE_WC_Port];
- int counter;
-
- /* sample counter */
- awe_set_cmd(AWE_WC_Cmd);
- clock = (unsigned short)inw(port);
- target = clock + delay;
- counter = 0;
- if (target < clock) {
- for (; (unsigned short)inw(port) > target; counter++)
- if (counter > 65536)
- break;
- }
- for (; (unsigned short)inw(port) < target; counter++)
- if (counter > 65536)
- break;
-}
-#else
-
-static void awe_wait(unsigned short delay)
-{
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout((HZ*(unsigned long)delay + 44099)/44100);
-}
-#endif /* wait by loop */
-
-/* write a word data */
-INLINE static void
-awe_write_dram(unsigned short c)
-{
- awe_poke(AWE_SMLD, c);
-}
-
-
-#if defined(linux) && !defined(AWE_OBSOLETE_VOXWARE)
-
-/*================================================================
- * port check / request
- * 0x620-623, 0xA20-A23, 0xE20-E23
- *================================================================*/
-
-static int
-awe_check_port(void)
-{
- if (! port_setuped) return 0;
- return (check_region(awe_ports[0], 4) ||
- check_region(awe_ports[1], 4) ||
- check_region(awe_ports[3], 4));
-}
-
-static void
-awe_request_region(void)
-{
- if (! port_setuped) return;
- request_region(awe_ports[0], 4, "sound driver (AWE32)");
- request_region(awe_ports[1], 4, "sound driver (AWE32)");
- request_region(awe_ports[3], 4, "sound driver (AWE32)");
-}
-
-static void
-awe_release_region(void)
-{
- if (! port_setuped) return;
- release_region(awe_ports[0], 4);
- release_region(awe_ports[1], 4);
- release_region(awe_ports[3], 4);
-}
-
-#endif /* linux && !AWE_OBSOLETE_VOXWARE */
-
-
-/*================================================================
- * AWE32 initialization
- *================================================================*/
-static void
-awe_initialize(void)
-{
- DEBUG(0,printk("AWE32: initializing..\n"));
-
- /* initialize hardware configuration */
- awe_poke(AWE_HWCF1, 0x0059);
- awe_poke(AWE_HWCF2, 0x0020);
-
- /* disable audio; this seems to reduce a clicking noise a bit.. */
- awe_poke(AWE_HWCF3, 0);
-
- /* initialize audio channels */
- awe_init_audio();
-
- /* initialize DMA */
- awe_init_dma();
-
- /* initialize init array */
- awe_init_array();
-
- /* check DRAM memory size */
- awe_check_dram();
-
- /* initialize the FM section of the AWE32 */
- awe_init_fm();
-
- /* set up voice envelopes */
- awe_tweak();
-
- /* enable audio */
- awe_poke(AWE_HWCF3, 0x0004);
-
- /* set default values */
- awe_init_ctrl_parms(TRUE);
-
- /* set equalizer */
- awe_update_equalizer();
-
- /* set reverb & chorus modes */
- awe_update_reverb_mode();
- awe_update_chorus_mode();
-}
-
-
-/*================================================================
- * AWE32 voice parameters
- *================================================================*/
-
-/* initialize voice_info record */
-static void
-awe_init_voice_info(awe_voice_info *vp)
-{
- vp->sf_id = 0; /* normal mode */
- vp->sample = 0;
- vp->rate_offset = 0;
-
- vp->start = 0;
- vp->end = 0;
- vp->loopstart = 0;
- vp->loopend = 0;
- vp->mode = 0;
- vp->root = 60;
- vp->tune = 0;
- vp->low = 0;
- vp->high = 127;
- vp->vellow = 0;
- vp->velhigh = 127;
-
- vp->fixkey = -1;
- vp->fixvel = -1;
- vp->fixpan = -1;
- vp->pan = -1;
-
- vp->exclusiveClass = 0;
- vp->amplitude = 127;
- vp->attenuation = 0;
- vp->scaleTuning = 100;
-
- awe_init_voice_parm(&vp->parm);
-}
-
-/* initialize voice_parm record:
- * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0.
- * Vibrato and Tremolo effects are zero.
- * Cutoff is maximum.
- * Chorus and Reverb effects are zero.
- */
-static void
-awe_init_voice_parm(awe_voice_parm *pp)
-{
- pp->moddelay = 0x8000;
- pp->modatkhld = 0x7f7f;
- pp->moddcysus = 0x7f7f;
- pp->modrelease = 0x807f;
- pp->modkeyhold = 0;
- pp->modkeydecay = 0;
-
- pp->voldelay = 0x8000;
- pp->volatkhld = 0x7f7f;
- pp->voldcysus = 0x7f7f;
- pp->volrelease = 0x807f;
- pp->volkeyhold = 0;
- pp->volkeydecay = 0;
-
- pp->lfo1delay = 0x8000;
- pp->lfo2delay = 0x8000;
- pp->pefe = 0;
-
- pp->fmmod = 0;
- pp->tremfrq = 0;
- pp->fm2frq2 = 0;
-
- pp->cutoff = 0xff;
- pp->filterQ = 0;
-
- pp->chorus = 0;
- pp->reverb = 0;
-}
-
-
-#ifdef AWE_HAS_GUS_COMPATIBILITY
-
-/* convert frequency mHz to abstract cents (= midi key * 100) */
-static int
-freq_to_note(int mHz)
-{
- /* abscents = log(mHz/8176) / log(2) * 1200 */
- unsigned int max_val = (unsigned int)0xffffffff / 10000;
- int i, times;
- unsigned int base;
- unsigned int freq;
- int note, tune;
-
- if (mHz == 0)
- return 0;
- if (mHz < 0)
- return 12799; /* maximum */
-
- freq = mHz;
- note = 0;
- for (base = 8176 * 2; freq >= base; base *= 2) {
- note += 12;
- if (note >= 128) /* over maximum */
- return 12799;
- }
- base /= 2;
-
- /* to avoid overflow... */
- times = 10000;
- while (freq > max_val) {
- max_val *= 10;
- times /= 10;
- base /= 10;
- }
-
- freq = freq * times / base;
- for (i = 0; i < 12; i++) {
- if (freq < semitone_tuning[i+1])
- break;
- note++;
- }
-
- tune = 0;
- freq = freq * 10000 / semitone_tuning[i];
- for (i = 0; i < 100; i++) {
- if (freq < cent_tuning[i+1])
- break;
- tune++;
- }
-
- return note * 100 + tune;
-}
-
-
-/* convert Hz to AWE32 rate offset:
- * sample pitch offset for the specified sample rate
- * rate=44100 is no offset, each 4096 is 1 octave (twice).
- * eg, when rate is 22050, this offset becomes -4096.
- */
-static int
-calc_rate_offset(int Hz)
-{
- /* offset = log(Hz / 44100) / log(2) * 4096 */
- int freq, base, i;
-
- /* maybe smaller than max (44100Hz) */
- if (Hz <= 0 || Hz >= 44100) return 0;
-
- base = 0;
- for (freq = Hz * 2; freq < 44100; freq *= 2)
- base++;
- base *= 1200;
-
- freq = 44100 * 10000 / (freq/2);
- for (i = 0; i < 12; i++) {
- if (freq < semitone_tuning[i+1])
- break;
- base += 100;
- }
- freq = freq * 10000 / semitone_tuning[i];
- for (i = 0; i < 100; i++) {
- if (freq < cent_tuning[i+1])
- break;
- base++;
- }
- return -base * 4096 / 1200;
-}
-
-
-/*----------------------------------------------------------------
- * convert envelope time parameter to AWE32 raw parameter
- *----------------------------------------------------------------*/
-
-/* attack & decay/release time table (msec) */
-static short attack_time_tbl[128] = {
-32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816,
-707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377,
-361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188,
-180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94,
-90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47,
-45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23,
-22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12,
-11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0,
-};
-
-static short decay_time_tbl[128] = {
-32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082,
-2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507,
-1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722,
-691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361,
-345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180,
-172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90,
-86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45,
-43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22,
-};
-
-#define calc_parm_delay(msec) (0x8000 - (msec) * 1000 / 725);
-
-/* delay time = 0x8000 - msec/92 */
-static int
-calc_parm_hold(int msec)
-{
- int val = (0x7f * 92 - msec) / 92;
- if (val < 1) val = 1;
- if (val > 127) val = 127;
- return val;
-}
-
-/* attack time: search from time table */
-static int
-calc_parm_attack(int msec)
-{
- return calc_parm_search(msec, attack_time_tbl);
-}
-
-/* decay/release time: search from time table */
-static int
-calc_parm_decay(int msec)
-{
- return calc_parm_search(msec, decay_time_tbl);
-}
-
-/* search an index for specified time from given time table */
-static int
-calc_parm_search(int msec, short *table)
-{
- int left = 1, right = 127, mid;
- while (left < right) {
- mid = (left + right) / 2;
- if (msec < (int)table[mid])
- left = mid + 1;
- else
- right = mid;
- }
- return left;
-}
-#endif /* AWE_HAS_GUS_COMPATIBILITY */
-
-
-/*================================================================
- * effects table
- *================================================================*/
-
-/* set an effect value */
-#define FX_FLAG_OFF 0
-#define FX_FLAG_SET 1
-#define FX_FLAG_ADD 2
-
-#define FX_SET(rec,type,value) \
- ((rec)->flags[type] = FX_FLAG_SET, (rec)->val[type] = (value))
-#define FX_ADD(rec,type,value) \
- ((rec)->flags[type] = FX_FLAG_ADD, (rec)->val[type] = (value))
-#define FX_UNSET(rec,type) \
- ((rec)->flags[type] = FX_FLAG_OFF, (rec)->val[type] = 0)
-
-/* check the effect value is set */
-#define FX_ON(rec,type) ((rec)->flags[type])
-
-#define PARM_BYTE 0
-#define PARM_WORD 1
-#define PARM_SIGN 2
-
-static struct PARM_DEFS {
- int type; /* byte or word */
- int low, high; /* value range */
- fx_affect_func realtime; /* realtime paramater change */
-} parm_defs[] = {
- {PARM_WORD, 0, 0x8000, NULL}, /* env1 delay */
- {PARM_BYTE, 1, 0x7f, NULL}, /* env1 attack */
- {PARM_BYTE, 0, 0x7e, NULL}, /* env1 hold */
- {PARM_BYTE, 1, 0x7f, NULL}, /* env1 decay */
- {PARM_BYTE, 1, 0x7f, NULL}, /* env1 release */
- {PARM_BYTE, 0, 0x7f, NULL}, /* env1 sustain */
- {PARM_BYTE, 0, 0xff, NULL}, /* env1 pitch */
- {PARM_BYTE, 0, 0xff, NULL}, /* env1 cutoff */
-
- {PARM_WORD, 0, 0x8000, NULL}, /* env2 delay */
- {PARM_BYTE, 1, 0x7f, NULL}, /* env2 attack */
- {PARM_BYTE, 0, 0x7e, NULL}, /* env2 hold */
- {PARM_BYTE, 1, 0x7f, NULL}, /* env2 decay */
- {PARM_BYTE, 1, 0x7f, NULL}, /* env2 release */
- {PARM_BYTE, 0, 0x7f, NULL}, /* env2 sustain */
-
- {PARM_WORD, 0, 0x8000, NULL}, /* lfo1 delay */
- {PARM_BYTE, 0, 0xff, awe_fx_tremfrq}, /* lfo1 freq */
- {PARM_SIGN, -128, 127, awe_fx_tremfrq}, /* lfo1 volume */
- {PARM_SIGN, -128, 127, awe_fx_fmmod}, /* lfo1 pitch */
- {PARM_BYTE, 0, 0xff, awe_fx_fmmod}, /* lfo1 cutoff */
-
- {PARM_WORD, 0, 0x8000, NULL}, /* lfo2 delay */
- {PARM_BYTE, 0, 0xff, awe_fx_fm2frq2}, /* lfo2 freq */
- {PARM_SIGN, -128, 127, awe_fx_fm2frq2}, /* lfo2 pitch */
-
- {PARM_WORD, 0, 0xffff, awe_set_voice_pitch}, /* initial pitch */
- {PARM_BYTE, 0, 0xff, NULL}, /* chorus */
- {PARM_BYTE, 0, 0xff, NULL}, /* reverb */
- {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial cutoff */
- {PARM_BYTE, 0, 15, awe_fx_filterQ}, /* initial resonance */
-
- {PARM_WORD, 0, 0xffff, NULL}, /* sample start */
- {PARM_WORD, 0, 0xffff, NULL}, /* loop start */
- {PARM_WORD, 0, 0xffff, NULL}, /* loop end */
- {PARM_WORD, 0, 0xffff, NULL}, /* coarse sample start */
- {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop start */
- {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop end */
- {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial attenuation */
-};
-
-
-static unsigned char
-FX_BYTE(FX_Rec *rec, FX_Rec *lay, int type, unsigned char value)
-{
- int effect = 0;
- int on = 0;
- if (lay && (on = FX_ON(lay, type)) != 0)
- effect = lay->val[type];
- if (!on && (on = FX_ON(rec, type)) != 0)
- effect = rec->val[type];
- if (on == FX_FLAG_ADD) {
- if (parm_defs[type].type == PARM_SIGN) {
- if (value > 0x7f)
- effect += (int)value - 0x100;
- else
- effect += (int)value;
- } else {
- effect += (int)value;
- }
- }
- if (on) {
- if (effect < parm_defs[type].low)
- effect = parm_defs[type].low;
- else if (effect > parm_defs[type].high)
- effect = parm_defs[type].high;
- return (unsigned char)effect;
- }
- return value;
-}
-
-/* get word effect value */
-static unsigned short
-FX_WORD(FX_Rec *rec, FX_Rec *lay, int type, unsigned short value)
-{
- int effect = 0;
- int on = 0;
- if (lay && (on = FX_ON(lay, type)) != 0)
- effect = lay->val[type];
- if (!on && (on = FX_ON(rec, type)) != 0)
- effect = rec->val[type];
- if (on == FX_FLAG_ADD)
- effect += (int)value;
- if (on) {
- if (effect < parm_defs[type].low)
- effect = parm_defs[type].low;
- else if (effect > parm_defs[type].high)
- effect = parm_defs[type].high;
- return (unsigned short)effect;
- }
- return value;
-}
-
-/* get word (upper=type1/lower=type2) effect value */
-static unsigned short
-FX_COMB(FX_Rec *rec, FX_Rec *lay, int type1, int type2, unsigned short value)
-{
- unsigned short tmp;
- tmp = FX_BYTE(rec, lay, type1, (unsigned char)(value >> 8));
- tmp <<= 8;
- tmp |= FX_BYTE(rec, lay, type2, (unsigned char)(value & 0xff));
- return tmp;
-}
-
-/* address offset */
-static int
-FX_OFFSET(FX_Rec *rec, FX_Rec *lay, int lo, int hi, int mode)
-{
- int addr = 0;
- if (lay && FX_ON(lay, hi))
- addr = (short)lay->val[hi];
- else if (FX_ON(rec, hi))
- addr = (short)rec->val[hi];
- addr = addr << 15;
- if (lay && FX_ON(lay, lo))
- addr += (short)lay->val[lo];
- else if (FX_ON(rec, lo))
- addr += (short)rec->val[lo];
- if (!(mode & AWE_SAMPLE_8BITS))
- addr /= 2;
- return addr;
-}
-
-
-/*================================================================
- * turn on/off sample
- *================================================================*/
-
-/* table for volume target calculation */
-static unsigned short voltarget[16] = {
- 0xEAC0, 0XE0C8, 0XD740, 0XCE20, 0XC560, 0XBD08, 0XB500, 0XAD58,
- 0XA5F8, 0X9EF0, 0X9830, 0X91C0, 0X8B90, 0X85A8, 0X8000, 0X7A90
-};
-
-static void
-awe_note_on(int voice)
-{
- unsigned int temp;
- int addr;
- int vtarget, ftarget, ptarget, pitch;
- awe_voice_info *vp;
- awe_voice_parm_block *parm;
- FX_Rec *fx = &voices[voice].cinfo->fx;
- FX_Rec *fx_lay = NULL;
- if (voices[voice].layer < MAX_LAYERS)
- fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
-
- /* A voice sample must assigned before calling */
- if ((vp = voices[voice].sample) == NULL || vp->index < 0)
- return;
-
- parm = (awe_voice_parm_block*)&vp->parm;
-
- /* channel to be silent and idle */
- awe_poke(AWE_DCYSUSV(voice), 0x0080);
- awe_poke(AWE_VTFT(voice), 0x0000FFFF);
- awe_poke(AWE_CVCF(voice), 0x0000FFFF);
- awe_poke(AWE_PTRX(voice), 0);
- awe_poke(AWE_CPF(voice), 0);
-
- /* set pitch offset */
- awe_set_pitch(voice, TRUE);
-
- /* modulation & volume envelope */
- if (parm->modatk >= 0x80 && parm->moddelay >= 0x8000) {
- awe_poke(AWE_ENVVAL(voice), 0xBFFF);
- pitch = (parm->env1pit<<4) + voices[voice].apitch;
- if (pitch > 0xffff) pitch = 0xffff;
- /* calculate filter target */
- ftarget = parm->cutoff + parm->env1fc;
- limitvalue(ftarget, 0, 255);
- ftarget <<= 8;
- } else {
- awe_poke(AWE_ENVVAL(voice),
- FX_WORD(fx, fx_lay, AWE_FX_ENV1_DELAY, parm->moddelay));
- ftarget = parm->cutoff;
- ftarget <<= 8;
- pitch = voices[voice].apitch;
- }
-
- /* calcualte pitch target */
- if (pitch != 0xffff) {
- ptarget = 1 << (pitch >> 12);
- if (pitch & 0x800) ptarget += (ptarget*0x102e)/0x2710;
- if (pitch & 0x400) ptarget += (ptarget*0x764)/0x2710;
- if (pitch & 0x200) ptarget += (ptarget*0x389)/0x2710;
- ptarget += (ptarget>>1);
- if (ptarget > 0xffff) ptarget = 0xffff;
-
- } else ptarget = 0xffff;
- if (parm->modatk >= 0x80)
- awe_poke(AWE_ATKHLD(voice),
- FX_BYTE(fx, fx_lay, AWE_FX_ENV1_HOLD, parm->modhld) << 8 | 0x7f);
- else
- awe_poke(AWE_ATKHLD(voice),
- FX_COMB(fx, fx_lay, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK,
- vp->parm.modatkhld));
- awe_poke(AWE_DCYSUS(voice),
- FX_COMB(fx, fx_lay, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
- vp->parm.moddcysus));
-
- if (parm->volatk >= 0x80 && parm->voldelay >= 0x8000) {
- awe_poke(AWE_ENVVOL(voice), 0xBFFF);
- vtarget = voltarget[voices[voice].avol%0x10]>>(voices[voice].avol>>4);
- } else {
- awe_poke(AWE_ENVVOL(voice),
- FX_WORD(fx, fx_lay, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
- vtarget = 0;
- }
- if (parm->volatk >= 0x80)
- awe_poke(AWE_ATKHLDV(voice),
- FX_BYTE(fx, fx_lay, AWE_FX_ENV2_HOLD, parm->volhld) << 8 | 0x7f);
- else
- awe_poke(AWE_ATKHLDV(voice),
- FX_COMB(fx, fx_lay, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK,
- vp->parm.volatkhld));
- /* decay/sustain parameter for volume envelope must be set at last */
-
- /* cutoff and volume */
- awe_set_volume(voice, TRUE);
-
- /* modulation envelope heights */
- awe_poke(AWE_PEFE(voice),
- FX_COMB(fx, fx_lay, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
- vp->parm.pefe));
-
- /* lfo1/2 delay */
- awe_poke(AWE_LFO1VAL(voice),
- FX_WORD(fx, fx_lay, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay));
- awe_poke(AWE_LFO2VAL(voice),
- FX_WORD(fx, fx_lay, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay));
-
- /* lfo1 pitch & cutoff shift */
- awe_fx_fmmod(voice, TRUE);
- /* lfo1 volume & freq */
- awe_fx_tremfrq(voice, TRUE);
- /* lfo2 pitch & freq */
- awe_fx_fm2frq2(voice, TRUE);
- /* pan & loop start */
- awe_set_pan(voice, TRUE);
-
- /* chorus & loop end (chorus 8bit, MSB) */
- addr = vp->loopend - 1;
- addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_END,
- AWE_FX_COARSE_LOOP_END, vp->mode);
- temp = FX_BYTE(fx, fx_lay, AWE_FX_CHORUS, vp->parm.chorus);
- temp = (temp <<24) | (unsigned int)addr;
- awe_poke_dw(AWE_CSL(voice), temp);
- DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n", vp->loopend, addr));
-
- /* Q & current address (Q 4bit value, MSB) */
- addr = vp->start - 1;
- addr += FX_OFFSET(fx, fx_lay, AWE_FX_SAMPLE_START,
- AWE_FX_COARSE_SAMPLE_START, vp->mode);
- temp = FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ);
- temp = (temp<<28) | (unsigned int)addr;
- awe_poke_dw(AWE_CCCA(voice), temp);
- DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n", vp->start, addr));
-
- /* clear unknown registers */
- awe_poke_dw(AWE_00A0(voice), 0);
- awe_poke_dw(AWE_0080(voice), 0);
-
- /* reset volume */
- awe_poke_dw(AWE_VTFT(voice), (vtarget<<16)|ftarget);
- awe_poke_dw(AWE_CVCF(voice), (vtarget<<16)|ftarget);
-
- /* turn on envelope */
- awe_poke(AWE_DCYSUSV(voice),
- FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
- vp->parm.voldcysus));
- /* set reverb */
- temp = FX_BYTE(fx, fx_lay, AWE_FX_REVERB, vp->parm.reverb);
- temp = (temp << 8) | (ptarget << 16) | voices[voice].aaux;
- awe_poke_dw(AWE_PTRX(voice), temp);
- awe_poke_dw(AWE_CPF(voice), ptarget << 16);
-
- voices[voice].state = AWE_ST_ON;
-
- /* clear voice position for the next note on this channel */
- if (SINGLE_LAYER_MODE()) {
- FX_UNSET(fx, AWE_FX_SAMPLE_START);
- FX_UNSET(fx, AWE_FX_COARSE_SAMPLE_START);
- }
-}
-
-
-/* turn off the voice */
-static void
-awe_note_off(int voice)
-{
- awe_voice_info *vp;
- unsigned short tmp;
- FX_Rec *fx = &voices[voice].cinfo->fx;
- FX_Rec *fx_lay = NULL;
- if (voices[voice].layer < MAX_LAYERS)
- fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
-
- if ((vp = voices[voice].sample) == NULL) {
- voices[voice].state = AWE_ST_OFF;
- return;
- }
-
- tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV1_RELEASE,
- (unsigned char)vp->parm.modrelease);
- awe_poke(AWE_DCYSUS(voice), tmp);
- tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV2_RELEASE,
- (unsigned char)vp->parm.volrelease);
- awe_poke(AWE_DCYSUSV(voice), tmp);
- voices[voice].state = AWE_ST_RELEASED;
-}
-
-/* force to terminate the voice (no releasing echo) */
-static void
-awe_terminate(int voice)
-{
- awe_poke(AWE_DCYSUSV(voice), 0x807F);
- awe_tweak_voice(voice);
- voices[voice].state = AWE_ST_OFF;
-}
-
-/* turn off other voices with the same exclusive class (for drums) */
-static void
-awe_exclusive_off(int voice)
-{
- int i, exclass;
-
- if (voices[voice].sample == NULL)
- return;
- if ((exclass = voices[voice].sample->exclusiveClass) == 0)
- return; /* not exclusive */
-
- /* turn off voices with the same class */
- for (i = 0; i < awe_max_voices; i++) {
- if (i != voice && IS_PLAYING(i) &&
- voices[i].sample && voices[i].ch == voices[voice].ch &&
- voices[i].sample->exclusiveClass == exclass) {
- DEBUG(4,printk("AWE32: [exoff(%d)]\n", i));
- awe_terminate(i);
- awe_voice_init(i, TRUE);
- }
- }
-}
-
-
-/*================================================================
- * change the parameters of an audible voice
- *================================================================*/
-
-/* change pitch */
-static void
-awe_set_pitch(int voice, int forced)
-{
- if (IS_NO_EFFECT(voice) && !forced) return;
- awe_poke(AWE_IP(voice), voices[voice].apitch);
- DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch));
-}
-
-/* calculate & change pitch */
-static void
-awe_set_voice_pitch(int voice, int forced)
-{
- awe_calc_pitch(voice);
- awe_set_pitch(voice, forced);
-}
-
-/* change volume & cutoff */
-static void
-awe_set_volume(int voice, int forced)
-{
- awe_voice_info *vp;
- unsigned short tmp2;
- FX_Rec *fx = &voices[voice].cinfo->fx;
- FX_Rec *fx_lay = NULL;
- if (voices[voice].layer < MAX_LAYERS)
- fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
-
- if (!IS_PLAYING(voice) && !forced) return;
- if ((vp = voices[voice].sample) == NULL || vp->index < 0)
- return;
-
- tmp2 = FX_BYTE(fx, fx_lay, AWE_FX_CUTOFF,
- (unsigned char)voices[voice].acutoff);
- tmp2 = (tmp2 << 8);
- tmp2 |= FX_BYTE(fx, fx_lay, AWE_FX_ATTEN,
- (unsigned char)voices[voice].avol);
- awe_poke(AWE_IFATN(voice), tmp2);
-}
-
-/* calculate & change volume */
-static void
-awe_set_voice_vol(int voice, int forced)
-{
- if (IS_EMPTY(voice))
- return;
- awe_calc_volume(voice);
- awe_set_volume(voice, forced);
-}
-
-
-/* change pan; this could make a click noise.. */
-static void
-awe_set_pan(int voice, int forced)
-{
- unsigned int temp;
- int addr;
- awe_voice_info *vp;
- FX_Rec *fx = &voices[voice].cinfo->fx;
- FX_Rec *fx_lay = NULL;
- if (voices[voice].layer < MAX_LAYERS)
- fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
-
- if (IS_NO_EFFECT(voice) && !forced) return;
- if ((vp = voices[voice].sample) == NULL || vp->index < 0)
- return;
-
- /* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */
- if (vp->fixpan > 0) /* 0-127 */
- temp = 255 - (int)vp->fixpan * 2;
- else {
- int pos = 0;
- if (vp->pan >= 0) /* 0-127 */
- pos = (int)vp->pan * 2 - 128;
- pos += voices[voice].cinfo->panning; /* -128 - 127 */
- temp = 127 - pos;
- }
- limitvalue(temp, 0, 255);
- if (ctrls[AWE_MD_PAN_EXCHANGE]) {
- temp = 255 - temp;
- }
- if (forced || temp != voices[voice].apan) {
- voices[voice].apan = temp;
- addr = vp->loopstart - 1;
- addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_START,
- AWE_FX_COARSE_LOOP_START, vp->mode);
- temp = (temp<<24) | (unsigned int)addr;
- awe_poke_dw(AWE_PSST(voice), temp);
- DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n", vp->loopstart, addr));
- if (temp == 0) voices[voice].aaux = 0xff;
- else voices[voice].aaux = (-temp)&0xff;
- }
-}
-
-/* effects change during playing */
-static void
-awe_fx_fmmod(int voice, int forced)
-{
- awe_voice_info *vp;
- FX_Rec *fx = &voices[voice].cinfo->fx;
- FX_Rec *fx_lay = NULL;
- if (voices[voice].layer < MAX_LAYERS)
- fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
-
- if (IS_NO_EFFECT(voice) && !forced) return;
- if ((vp = voices[voice].sample) == NULL || vp->index < 0)
- return;
- awe_poke(AWE_FMMOD(voice),
- FX_COMB(fx, fx_lay, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
- vp->parm.fmmod));
-}
-
-/* set tremolo (lfo1) volume & frequency */
-static void
-awe_fx_tremfrq(int voice, int forced)
-{
- awe_voice_info *vp;
- FX_Rec *fx = &voices[voice].cinfo->fx;
- FX_Rec *fx_lay = NULL;
- if (voices[voice].layer < MAX_LAYERS)
- fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
-
- if (IS_NO_EFFECT(voice) && !forced) return;
- if ((vp = voices[voice].sample) == NULL || vp->index < 0)
- return;
- awe_poke(AWE_TREMFRQ(voice),
- FX_COMB(fx, fx_lay, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
- vp->parm.tremfrq));
-}
-
-/* set lfo2 pitch & frequency */
-static void
-awe_fx_fm2frq2(int voice, int forced)
-{
- awe_voice_info *vp;
- FX_Rec *fx = &voices[voice].cinfo->fx;
- FX_Rec *fx_lay = NULL;
- if (voices[voice].layer < MAX_LAYERS)
- fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
-
- if (IS_NO_EFFECT(voice) && !forced) return;
- if ((vp = voices[voice].sample) == NULL || vp->index < 0)
- return;
- awe_poke(AWE_FM2FRQ2(voice),
- FX_COMB(fx, fx_lay, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
- vp->parm.fm2frq2));
-}
-
-
-/* Q & current address (Q 4bit value, MSB) */
-static void
-awe_fx_filterQ(int voice, int forced)
-{
- unsigned int addr;
- awe_voice_info *vp;
- FX_Rec *fx = &voices[voice].cinfo->fx;
- FX_Rec *fx_lay = NULL;
- if (voices[voice].layer < MAX_LAYERS)
- fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
-
- if (IS_NO_EFFECT(voice) && !forced) return;
- if ((vp = voices[voice].sample) == NULL || vp->index < 0)
- return;
-
- addr = awe_peek_dw(AWE_CCCA(voice)) & 0xffffff;
- addr |= (FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ) << 28);
- awe_poke_dw(AWE_CCCA(voice), addr);
-}
-
-/*================================================================
- * calculate pitch offset
- *----------------------------------------------------------------
- * 0xE000 is no pitch offset at 44100Hz sample.
- * Every 4096 is one octave.
- *================================================================*/
-
-static void
-awe_calc_pitch(int voice)
-{
- voice_info *vp = &voices[voice];
- awe_voice_info *ap;
- awe_chan_info *cp = voices[voice].cinfo;
- int offset;
-
- /* search voice information */
- if ((ap = vp->sample) == NULL)
- return;
- if (ap->index < 0) {
- DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
- if (awe_set_sample(ap) < 0)
- return;
- }
-
- /* calculate offset */
- if (ap->fixkey >= 0) {
- DEBUG(3,printk("AWE32: p-> fixkey(%d) tune(%d)\n", ap->fixkey, ap->tune));
- offset = (ap->fixkey - ap->root) * 4096 / 12;
- } else {
- DEBUG(3,printk("AWE32: p(%d)-> root(%d) tune(%d)\n", vp->note, ap->root, ap->tune));
- offset = (vp->note - ap->root) * 4096 / 12;
- DEBUG(4,printk("AWE32: p-> ofs=%d\n", offset));
- }
- offset = (offset * ap->scaleTuning) / 100;
- DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset));
- offset += ap->tune * 4096 / 1200;
- DEBUG(4,printk("AWE32: p-> tune+ ofs=%d\n", offset));
- if (cp->bender != 0) {
- DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, cp->bender));
- /* (819200: 1 semitone) ==> (4096: 12 semitones) */
- offset += cp->bender * cp->bender_range / 2400;
- }
-
- /* add initial pitch correction */
- if (FX_ON(&cp->fx_layer[vp->layer], AWE_FX_INIT_PITCH))
- offset += cp->fx_layer[vp->layer].val[AWE_FX_INIT_PITCH];
- else if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH))
- offset += cp->fx.val[AWE_FX_INIT_PITCH];
-
- /* 0xe000: root pitch */
- vp->apitch = 0xe000 + ap->rate_offset + offset;
- DEBUG(4,printk("AWE32: p-> sum aofs=%x, rate_ofs=%d\n", vp->apitch, ap->rate_offset));
- if (vp->apitch > 0xffff)
- vp->apitch = 0xffff;
- if (vp->apitch < 0)
- vp->apitch = 0;
-}
-
-
-#ifdef AWE_HAS_GUS_COMPATIBILITY
-/* calculate MIDI key and semitone from the specified frequency */
-static void
-awe_calc_pitch_from_freq(int voice, int freq)
-{
- voice_info *vp = &voices[voice];
- awe_voice_info *ap;
- FX_Rec *fx = &voices[voice].cinfo->fx;
- FX_Rec *fx_lay = NULL;
- int offset;
- int note;
-
- if (voices[voice].layer < MAX_LAYERS)
- fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
-
- /* search voice information */
- if ((ap = vp->sample) == NULL)
- return;
- if (ap->index < 0) {
- DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
- if (awe_set_sample(ap) < 0)
- return;
- }
- note = freq_to_note(freq);
- offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200;
- offset = (offset * ap->scaleTuning) / 100;
- if (fx_lay && FX_ON(fx_lay, AWE_FX_INIT_PITCH))
- offset += fx_lay->val[AWE_FX_INIT_PITCH];
- else if (FX_ON(fx, AWE_FX_INIT_PITCH))
- offset += fx->val[AWE_FX_INIT_PITCH];
- vp->apitch = 0xe000 + ap->rate_offset + offset;
- if (vp->apitch > 0xffff)
- vp->apitch = 0xffff;
- if (vp->apitch < 0)
- vp->apitch = 0;
-}
-#endif /* AWE_HAS_GUS_COMPATIBILITY */
-
-
-/*================================================================
- * calculate volume attenuation
- *----------------------------------------------------------------
- * Voice volume is controlled by volume attenuation parameter.
- * So volume becomes maximum when avol is 0 (no attenuation), and
- * minimum when 255 (-96dB or silence).
- *================================================================*/
-
-static int vol_table[128] = {
- 255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49,
- 47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32,
- 31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22,
- 22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16,
- 15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10,
- 10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6,
- 6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3,
- 2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0,
-};
-
-/* tables for volume->attenuation calculation */
-static unsigned char voltab1[128] = {
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22,
- 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a,
- 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14,
- 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10,
- 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d,
- 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-static unsigned char voltab2[128] = {
- 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a,
- 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21,
- 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a,
- 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15,
- 0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10,
- 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d,
- 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-static unsigned char expressiontab[128] = {
- 0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42,
- 0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30,
- 0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25,
- 0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e,
- 0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18,
- 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13,
- 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f,
- 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c,
- 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09,
- 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
- 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03,
- 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-static void
-awe_calc_volume(int voice)
-{
- voice_info *vp = &voices[voice];
- awe_voice_info *ap;
- awe_chan_info *cp = voices[voice].cinfo;
- int vol;
-
- /* search voice information */
- if ((ap = vp->sample) == NULL)
- return;
-
- ap = vp->sample;
- if (ap->index < 0) {
- DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
- if (awe_set_sample(ap) < 0)
- return;
- }
-
- if (ctrls[AWE_MD_NEW_VOLUME_CALC]) {
- int main_vol = cp->main_vol * ap->amplitude / 127;
- limitvalue(vp->velocity, 0, 127);
- limitvalue(main_vol, 0, 127);
- limitvalue(cp->expression_vol, 0, 127);
-
- vol = voltab1[main_vol] + voltab2[vp->velocity];
- vol = (vol * 8) / 3;
- vol += ap->attenuation;
- if (cp->expression_vol < 127)
- vol += ((0x100 - vol) * expressiontab[cp->expression_vol])/128;
- vol += atten_offset;
- if (atten_relative)
- vol += ctrls[AWE_MD_ZERO_ATTEN];
- limitvalue(vol, 0, 255);
- vp->avol = vol;
-
- } else {
- /* 0 - 127 */
- vol = (vp->velocity * cp->main_vol * cp->expression_vol) / (127*127);
- vol = vol * ap->amplitude / 127;
-
- if (vol < 0) vol = 0;
- if (vol > 127) vol = 127;
-
- /* calc to attenuation */
- vol = vol_table[vol];
- vol += (int)ap->attenuation;
- vol += atten_offset;
- if (atten_relative)
- vol += ctrls[AWE_MD_ZERO_ATTEN];
- if (vol > 255) vol = 255;
-
- vp->avol = vol;
- }
- if (cp->bank != AWE_DRUM_BANK && ((awe_voice_parm_block*)(&ap->parm))->volatk < 0x7d) {
- int atten;
- if (vp->velocity < 70) atten = 70;
- else atten = vp->velocity;
- vp->acutoff = (atten * ap->parm.cutoff + 0xa0) >> 7;
- } else {
- vp->acutoff = ap->parm.cutoff;
- }
- DEBUG(3,printk("AWE32: [-- voice(%d) vol=%x]\n", voice, vol));
-}
-
-/* change master volume */
-static void
-awe_change_master_volume(short val)
-{
- limitvalue(val, 0, 127);
- atten_offset = vol_table[val];
- atten_relative = TRUE;
- awe_update_volume();
-}
-
-/* update volumes of all available channels */
-static void awe_update_volume(void)
-{
- int i;
- for (i = 0; i < awe_max_voices; i++)
- awe_set_voice_vol(i, TRUE);
-}
-
-/* set sostenuto on */
-static void awe_sostenuto_on(int voice, int forced)
-{
- if (IS_NO_EFFECT(voice) && !forced) return;
- voices[voice].sostenuto = 127;
-}
-
-
-/* drop sustain */
-static void awe_sustain_off(int voice, int forced)
-{
- if (voices[voice].state == AWE_ST_SUSTAINED) {
- awe_note_off(voice);
- awe_fx_init(voices[voice].ch);
- awe_voice_init(voice, FALSE);
- }
-}
-
-
-/* terminate and initialize voice */
-static void awe_terminate_and_init(int voice, int forced)
-{
- awe_terminate(voice);
- awe_fx_init(voices[voice].ch);
- awe_voice_init(voice, TRUE);
-}
-
-
-/*================================================================
- * synth operation routines
- *================================================================*/
-
-#define AWE_VOICE_KEY(v) (0x8000 | (v))
-#define AWE_CHAN_KEY(c,n) (((c) << 8) | ((n) + 1))
-#define KEY_CHAN_MATCH(key,c) (((key) >> 8) == (c))
-
-/* initialize the voice */
-static void
-awe_voice_init(int voice, int init_all)
-{
- voice_info *vp = &voices[voice];
-
- /* reset voice search key */
- if (playing_mode == AWE_PLAY_DIRECT)
- vp->key = AWE_VOICE_KEY(voice);
- else
- vp->key = 0;
-
- /* clear voice mapping */
- voice_alloc->map[voice] = 0;
-
- /* touch the timing flag */
- vp->time = current_alloc_time;
-
- /* initialize other parameters if necessary */
- if (init_all) {
- vp->note = -1;
- vp->velocity = 0;
- vp->sostenuto = 0;
-
- vp->sample = NULL;
- vp->cinfo = &channels[voice];
- vp->ch = voice;
- vp->state = AWE_ST_OFF;
-
- /* emu8000 parameters */
- vp->apitch = 0;
- vp->avol = 255;
- vp->apan = -1;
- }
-}
-
-/* clear effects */
-static void awe_fx_init(int ch)
-{
- if (SINGLE_LAYER_MODE() && !ctrls[AWE_MD_KEEP_EFFECT]) {
- BZERO(&channels[ch].fx, sizeof(channels[ch].fx));
- BZERO(&channels[ch].fx_layer, sizeof(&channels[ch].fx_layer));
- }
-}
-
-/* initialize channel info */
-static void awe_channel_init(int ch, int init_all)
-{
- awe_chan_info *cp = &channels[ch];
- cp->channel = ch;
- if (init_all) {
- cp->panning = 0; /* zero center */
- cp->bender_range = 200; /* sense * 100 */
- cp->main_vol = 127;
- if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) {
- cp->instr = ctrls[AWE_MD_DEF_DRUM];
- cp->bank = AWE_DRUM_BANK;
- } else {
- cp->instr = ctrls[AWE_MD_DEF_PRESET];
- cp->bank = ctrls[AWE_MD_DEF_BANK];
- }
- cp->vrec = -1;
- cp->def_vrec = -1;
- }
-
- cp->bender = 0; /* zero tune skew */
- cp->expression_vol = 127;
- cp->chan_press = 0;
- cp->sustained = 0;
-
- if (! ctrls[AWE_MD_KEEP_EFFECT]) {
- BZERO(&cp->fx, sizeof(cp->fx));
- BZERO(&cp->fx_layer, sizeof(cp->fx_layer));
- }
-}
-
-
-/* change the voice parameters; voice = channel */
-static void awe_voice_change(int voice, fx_affect_func func)
-{
- int i;
- switch (playing_mode) {
- case AWE_PLAY_DIRECT:
- func(voice, FALSE);
- break;
- case AWE_PLAY_INDIRECT:
- for (i = 0; i < awe_max_voices; i++)
- if (voices[i].key == AWE_VOICE_KEY(voice))
- func(i, FALSE);
- break;
- default:
- for (i = 0; i < awe_max_voices; i++)
- if (KEY_CHAN_MATCH(voices[i].key, voice))
- func(i, FALSE);
- break;
- }
-}
-
-
-/*----------------------------------------------------------------
- * device open / close
- *----------------------------------------------------------------*/
-
-/* open device:
- * reset status of all voices, and clear sample position flag
- */
-static int
-awe_open(int dev, int mode)
-{
- if (awe_busy)
- return RET_ERROR(EBUSY);
-
- awe_busy = TRUE;
-
- /* set default mode */
- awe_init_ctrl_parms(FALSE);
- atten_relative = TRUE;
- atten_offset = 0;
- drum_flags = DEFAULT_DRUM_FLAGS;
- playing_mode = AWE_PLAY_INDIRECT;
-
- /* reset voices & channels */
- awe_reset(dev);
-
- patch_opened = 0;
-
- return 0;
-}
-
-
-/* close device:
- * reset all voices again (terminate sounds)
- */
-static void
-awe_close(int dev)
-{
- awe_reset(dev);
- awe_busy = FALSE;
-}
-
-
-/* set miscellaneous mode parameters
- */
-static void
-awe_init_ctrl_parms(int init_all)
-{
- int i;
- for (i = 0; i < AWE_MD_END; i++) {
- if (init_all || ctrl_parms[i].init_each_time)
- ctrls[i] = ctrl_parms[i].value;
- }
-}
-
-
-/* sequencer I/O control:
- */
-static int
-awe_ioctl(int dev, unsigned int cmd, caddr_t arg)
-{
- switch (cmd) {
- case SNDCTL_SYNTH_INFO:
- if (playing_mode == AWE_PLAY_DIRECT)
- awe_info.nr_voices = awe_max_voices;
- else
- awe_info.nr_voices = AWE_MAX_CHANNELS;
- IOCTL_TO_USER((char*)arg, 0, &awe_info, sizeof(awe_info));
- return 0;
- break;
-
- case SNDCTL_SEQ_RESETSAMPLES:
- awe_reset_samples();
- awe_reset(dev);
- return 0;
- break;
-
- case SNDCTL_SEQ_PERCMODE:
- /* what's this? */
- return 0;
- break;
-
- case SNDCTL_SYNTH_MEMAVL:
- return awe_mem_size - awe_free_mem_ptr() * 2;
-
- default:
- printk("AWE32: unsupported ioctl %d\n", cmd);
- return RET_ERROR(EINVAL);
- }
-}
-
-
-static int voice_in_range(int voice)
-{
- if (playing_mode == AWE_PLAY_DIRECT) {
- if (voice < 0 || voice >= awe_max_voices)
- return FALSE;
- } else {
- if (voice < 0 || voice >= AWE_MAX_CHANNELS)
- return FALSE;
- }
- return TRUE;
-}
-
-static void release_voice(int voice, int do_sustain)
-{
- if (IS_NO_SOUND(voice))
- return;
- if (do_sustain && (voices[voice].cinfo->sustained == 127 ||
- voices[voice].sostenuto == 127))
- voices[voice].state = AWE_ST_SUSTAINED;
- else {
- awe_note_off(voice);
- awe_fx_init(voices[voice].ch);
- awe_voice_init(voice, FALSE);
- }
-}
-
-/* release all notes */
-static void awe_note_off_all(int do_sustain)
-{
- int i;
- for (i = 0; i < awe_max_voices; i++)
- release_voice(i, do_sustain);
-}
-
-/* kill a voice:
- * not terminate, just release the voice.
- */
-static int
-awe_kill_note(int dev, int voice, int note, int velocity)
-{
- int i, v2, key;
-
- DEBUG(2,printk("AWE32: [off(%d) nt=%d vl=%d]\n", voice, note, velocity));
- if (! voice_in_range(voice))
- return RET_ERROR(EINVAL);
-
- switch (playing_mode) {
- case AWE_PLAY_DIRECT:
- case AWE_PLAY_INDIRECT:
- key = AWE_VOICE_KEY(voice);
- break;
-
- case AWE_PLAY_MULTI2:
- v2 = voice_alloc->map[voice] >> 8;
- voice_alloc->map[voice] = 0;
- voice = v2;
- if (voice < 0 || voice >= AWE_MAX_CHANNELS)
- return RET_ERROR(EINVAL);
- /* continue to below */
- default:
- key = AWE_CHAN_KEY(voice, note);
- break;
- }
-
- for (i = 0; i < awe_max_voices; i++) {
- if (voices[i].key == key)
- release_voice(i, TRUE);
- }
- return 0;
-}
-
-
-static void start_or_volume_change(int voice, int velocity)
-{
- voices[voice].velocity = velocity;
- awe_calc_volume(voice);
- if (voices[voice].state == AWE_ST_STANDBY)
- awe_note_on(voice);
- else if (voices[voice].state == AWE_ST_ON)
- awe_set_volume(voice, FALSE);
-}
-
-static void set_and_start_voice(int voice, int state)
-{
- /* calculate pitch & volume parameters */
- voices[voice].state = state;
- awe_calc_pitch(voice);
- awe_calc_volume(voice);
- if (state == AWE_ST_ON)
- awe_note_on(voice);
-}
-
-/* start a voice:
- * if note is 255, identical with aftertouch function.
- * Otherwise, start a voice with specified not and volume.
- */
-static int
-awe_start_note(int dev, int voice, int note, int velocity)
-{
- int i, key, state, volonly;
-
- DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", voice, note, velocity));
- if (! voice_in_range(voice))
- return RET_ERROR(EINVAL);
-
- if (velocity == 0)
- state = AWE_ST_STANDBY; /* stand by for playing */
- else
- state = AWE_ST_ON; /* really play */
- volonly = FALSE;
-
- switch (playing_mode) {
- case AWE_PLAY_DIRECT:
- case AWE_PLAY_INDIRECT:
- key = AWE_VOICE_KEY(voice);
- if (note == 255)
- volonly = TRUE;
- break;
-
- case AWE_PLAY_MULTI2:
- voice = voice_alloc->map[voice] >> 8;
- if (voice < 0 || voice >= AWE_MAX_CHANNELS)
- return RET_ERROR(EINVAL);
- /* continue to below */
- default:
- if (note >= 128) { /* key volume mode */
- note -= 128;
- volonly = TRUE;
- }
- key = AWE_CHAN_KEY(voice, note);
- break;
- }
-
- /* dynamic volume change */
- if (volonly) {
- for (i = 0; i < awe_max_voices; i++) {
- if (voices[i].key == key)
- start_or_volume_change(i, velocity);
- }
- return 0;
- }
-
- /* if the same note still playing, stop it */
- if (playing_mode != AWE_PLAY_DIRECT || ctrls[AWE_MD_EXCLUSIVE_SOUND]) {
- for (i = 0; i < awe_max_voices; i++)
- if (voices[i].key == key) {
- if (voices[i].state == AWE_ST_ON) {
- awe_note_off(i);
- awe_voice_init(i, FALSE);
- } else if (voices[i].state == AWE_ST_STANDBY)
- awe_voice_init(i, TRUE);
- }
- }
-
- /* allocate voices */
- if (playing_mode == AWE_PLAY_DIRECT)
- awe_alloc_one_voice(voice, note, velocity);
- else
- awe_alloc_multi_voices(voice, note, velocity, key);
-
- /* turn off other voices exlusively (for drums) */
- for (i = 0; i < awe_max_voices; i++)
- if (voices[i].key == key)
- awe_exclusive_off(i);
-
- /* set up pitch and volume parameters */
- for (i = 0; i < awe_max_voices; i++) {
- if (voices[i].key == key && voices[i].state == AWE_ST_OFF)
- set_and_start_voice(i, state);
- }
-
- return 0;
-}
-
-
-/* search instrument from preset table with the specified bank */
-static int
-awe_search_instr(int bank, int preset)
-{
- int i;
-
- limitvalue(preset, 0, AWE_MAX_PRESETS-1);
- for (i = preset_table[preset]; i >= 0; i = infos[i].next_bank) {
- if (infos[i].bank == bank)
- return i;
- }
- return -1;
-}
-
-
-/* assign the instrument to a voice */
-static int
-awe_set_instr_2(int dev, int voice, int instr_no)
-{
- if (playing_mode == AWE_PLAY_MULTI2) {
- voice = voice_alloc->map[voice] >> 8;
- if (voice < 0 || voice >= AWE_MAX_CHANNELS)
- return RET_ERROR(EINVAL);
- }
- return awe_set_instr(dev, voice, instr_no);
-}
-
-/* assign the instrument to a channel; voice is the channel number */
-static int
-awe_set_instr(int dev, int voice, int instr_no)
-{
- awe_chan_info *cinfo;
- int def_bank;
-
- if (! voice_in_range(voice))
- return RET_ERROR(EINVAL);
-
- if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS)
- return RET_ERROR(EINVAL);
-
- cinfo = &channels[voice];
-
- if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice))
- def_bank = AWE_DRUM_BANK; /* always search drumset */
- else
- def_bank = cinfo->bank;
-
- cinfo->vrec = -1;
- cinfo->def_vrec = -1;
- cinfo->vrec = awe_search_instr(def_bank, instr_no);
- if (def_bank == AWE_DRUM_BANK) /* search default drumset */
- cinfo->def_vrec = awe_search_instr(def_bank, ctrls[AWE_MD_DEF_DRUM]);
- else /* search default preset */
- cinfo->def_vrec = awe_search_instr(ctrls[AWE_MD_DEF_BANK], instr_no);
-
- if (cinfo->vrec < 0 && cinfo->def_vrec < 0) {
- DEBUG(1,printk("AWE32 Warning: can't find instrument %d\n", instr_no));
- }
-
- cinfo->instr = instr_no;
- DEBUG(2,printk("AWE32: [program(%d) %d/%d]\n", voice, instr_no, def_bank));
-
- return 0;
-}
-
-
-/* reset all voices; terminate sounds and initialize parameters */
-static void
-awe_reset(int dev)
-{
- int i;
- current_alloc_time = 0;
- /* don't turn off voice 31 and 32. they are used also for FM voices */
- for (i = 0; i < awe_max_voices; i++) {
- awe_terminate(i);
- awe_voice_init(i, TRUE);
- }
- for (i = 0; i < AWE_MAX_CHANNELS; i++)
- awe_channel_init(i, TRUE);
- for (i = 0; i < 16; i++) {
- awe_operations.chn_info[i].controllers[CTL_MAIN_VOLUME] = 127;
- awe_operations.chn_info[i].controllers[CTL_EXPRESSION] = 127;
- }
- awe_init_fm();
- awe_tweak();
-}
-
-
-/* hardware specific control:
- * GUS specific and AWE32 specific controls are available.
- */
-static void
-awe_hw_control(int dev, unsigned char *event)
-{
- int cmd = event[2];
- if (cmd & _AWE_MODE_FLAG)
- awe_hw_awe_control(dev, cmd & _AWE_MODE_VALUE_MASK, event);
-#ifdef AWE_HAS_GUS_COMPATIBILITY
- else
- awe_hw_gus_control(dev, cmd & _AWE_MODE_VALUE_MASK, event);
-#endif
-}
-
-
-#ifdef AWE_HAS_GUS_COMPATIBILITY
-
-/* GUS compatible controls */
-static void
-awe_hw_gus_control(int dev, int cmd, unsigned char *event)
-{
- int voice, i, key;
- unsigned short p1;
- short p2;
- int plong;
-
- if (MULTI_LAYER_MODE())
- return;
- if (cmd == _GUS_NUMVOICES)
- return;
-
- voice = event[3];
- if (! voice_in_range(voice))
- return;
-
- p1 = *(unsigned short *) &event[4];
- p2 = *(short *) &event[6];
- plong = *(int*) &event[4];
-
- switch (cmd) {
- case _GUS_VOICESAMPLE:
- awe_set_instr(dev, voice, p1);
- return;
-
- case _GUS_VOICEBALA:
- /* 0 to 15 --> -128 to 127 */
- awe_panning(dev, voice, ((int)p1 << 4) - 128);
- return;
-
- case _GUS_VOICEVOL:
- case _GUS_VOICEVOL2:
- /* not supported yet */
- return;
-
- case _GUS_RAMPRANGE:
- case _GUS_RAMPRATE:
- case _GUS_RAMPMODE:
- case _GUS_RAMPON:
- case _GUS_RAMPOFF:
- /* volume ramping not supported */
- return;
-
- case _GUS_VOLUME_SCALE:
- return;
-
- case _GUS_VOICE_POS:
- FX_SET(&channels[voice].fx, AWE_FX_SAMPLE_START,
- (short)(plong & 0x7fff));
- FX_SET(&channels[voice].fx, AWE_FX_COARSE_SAMPLE_START,
- (plong >> 15) & 0xffff);
- return;
- }
-
- key = AWE_VOICE_KEY(voice);
- for (i = 0; i < awe_max_voices; i++) {
- if (voices[i].key == key) {
- switch (cmd) {
- case _GUS_VOICEON:
- awe_note_on(i);
- break;
-
- case _GUS_VOICEOFF:
- awe_terminate(i);
- awe_fx_init(voices[i].ch);
- awe_voice_init(i, TRUE);
- break;
-
- case _GUS_VOICEFADE:
- awe_note_off(i);
- awe_fx_init(voices[i].ch);
- awe_voice_init(i, FALSE);
- break;
-
- case _GUS_VOICEFREQ:
- awe_calc_pitch_from_freq(i, plong);
- break;
- }
- }
- }
-}
-
-#endif /* gus_compat */
-
-
-/* AWE32 specific controls */
-static void
-awe_hw_awe_control(int dev, int cmd, unsigned char *event)
-{
- int voice;
- unsigned short p1;
- short p2;
- int i;
-
- voice = event[3];
- if (! voice_in_range(voice))
- return;
-
- if (playing_mode == AWE_PLAY_MULTI2) {
- voice = voice_alloc->map[voice] >> 8;
- if (voice < 0 || voice >= AWE_MAX_CHANNELS)
- return;
- }
-
- p1 = *(unsigned short *) &event[4];
- p2 = *(short *) &event[6];
-
- switch (cmd) {
- case _AWE_DEBUG_MODE:
- ctrls[AWE_MD_DEBUG_MODE] = p1;
- printk("AWE32: debug mode = %d\n", ctrls[AWE_MD_DEBUG_MODE]);
- break;
- case _AWE_REVERB_MODE:
- ctrls[AWE_MD_REVERB_MODE] = p1;
- awe_update_reverb_mode();
- break;
-
- case _AWE_CHORUS_MODE:
- ctrls[AWE_MD_CHORUS_MODE] = p1;
- awe_update_chorus_mode();
- break;
-
- case _AWE_REMOVE_LAST_SAMPLES:
- DEBUG(0,printk("AWE32: remove last samples\n"));
- if (locked_sf_id > 0)
- awe_remove_samples(locked_sf_id);
- break;
-
- case _AWE_INITIALIZE_CHIP:
- awe_initialize();
- break;
-
- case _AWE_SEND_EFFECT:
- i = -1;
- if (p1 >= 0x100) {
- i = (p1 >> 8);
- if (i < 0 || i >= MAX_LAYERS)
- break;
- }
- awe_send_effect(voice, i, p1, p2);
- break;
-
- case _AWE_RESET_CHANNEL:
- awe_channel_init(voice, !p1);
- break;
-
- case _AWE_TERMINATE_ALL:
- awe_reset(0);
- break;
-
- case _AWE_TERMINATE_CHANNEL:
- awe_voice_change(voice, awe_terminate_and_init);
- break;
-
- case _AWE_RELEASE_ALL:
- awe_note_off_all(FALSE);
- break;
- case _AWE_NOTEOFF_ALL:
- awe_note_off_all(TRUE);
- break;
-
- case _AWE_INITIAL_VOLUME:
- DEBUG(0,printk("AWE32: init attenuation %d\n", p1));
- atten_relative = (char)p2;
- atten_offset = (short)p1;
- awe_update_volume();
- break;
-
- case _AWE_CHN_PRESSURE:
- channels[voice].chan_press = p1;
- awe_modwheel_change(voice, p1);
- break;
-
- case _AWE_CHANNEL_MODE:
- DEBUG(0,printk("AWE32: channel mode = %d\n", p1));
- playing_mode = p1;
- awe_reset(0);
- break;
-
- case _AWE_DRUM_CHANNELS:
- DEBUG(0,printk("AWE32: drum flags = %x\n", p1));
- drum_flags = *(unsigned int*)&event[4];
- break;
-
- case _AWE_MISC_MODE:
- DEBUG(0,printk("AWE32: ctrl parms = %d %d\n", p1, p2));
- if (p1 > AWE_MD_VERSION && p1 < AWE_MD_END) {
- ctrls[p1] = p2;
- if (ctrl_parms[p1].update)
- ctrl_parms[p1].update();
- }
- break;
-
- case _AWE_EQUALIZER:
- ctrls[AWE_MD_BASS_LEVEL] = p1;
- ctrls[AWE_MD_TREBLE_LEVEL] = p2;
- awe_update_equalizer();
- break;
-
- default:
- DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice));
- break;
- }
-}
-
-
-/* change effects */
-static void
-awe_send_effect(int voice, int layer, int type, int val)
-{
- awe_chan_info *cinfo;
- FX_Rec *fx;
- int mode;
-
- cinfo = &channels[voice];
- if (layer >= 0 && layer < MAX_LAYERS)
- fx = &cinfo->fx_layer[layer];
- else
- fx = &cinfo->fx;
-
- if (type & 0x40)
- mode = FX_FLAG_OFF;
- else if (type & 0x80)
- mode = FX_FLAG_ADD;
- else
- mode = FX_FLAG_SET;
- type &= 0x3f;
-
- if (type >= 0 && type < AWE_FX_END) {
- DEBUG(2,printk("AWE32: effects (%d) %d %d\n", voice, type, val));
- if (mode == FX_FLAG_SET)
- FX_SET(fx, type, val);
- else if (mode == FX_FLAG_ADD)
- FX_ADD(fx, type, val);
- else
- FX_UNSET(fx, type);
- if (mode != FX_FLAG_OFF && parm_defs[type].realtime) {
- DEBUG(2,printk("AWE32: fx_realtime (%d)\n", voice));
- awe_voice_change(voice, parm_defs[type].realtime);
- }
- }
-}
-
-
-/* change modulation wheel; voice is already mapped on multi2 mode */
-static void
-awe_modwheel_change(int voice, int value)
-{
- int i;
- awe_chan_info *cinfo;
-
- cinfo = &channels[voice];
- i = value * ctrls[AWE_MD_MOD_SENSE] / 1200;
- FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, i);
- awe_voice_change(voice, awe_fx_fmmod);
- FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, i);
- awe_voice_change(voice, awe_fx_fm2frq2);
-}
-
-
-/* voice pressure change */
-static void
-awe_aftertouch(int dev, int voice, int pressure)
-{
- int note;
-
- DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure));
- if (! voice_in_range(voice))
- return;
-
- switch (playing_mode) {
- case AWE_PLAY_DIRECT:
- case AWE_PLAY_INDIRECT:
- awe_start_note(dev, voice, 255, pressure);
- break;
- case AWE_PLAY_MULTI2:
- note = (voice_alloc->map[voice] & 0xff) - 1;
- awe_key_pressure(dev, voice, note + 0x80, pressure);
- break;
- }
-}
-
-
-/* voice control change */
-static void
-awe_controller(int dev, int voice, int ctrl_num, int value)
-{
- awe_chan_info *cinfo;
-
- if (! voice_in_range(voice))
- return;
-
- if (playing_mode == AWE_PLAY_MULTI2) {
- voice = voice_alloc->map[voice] >> 8;
- if (voice < 0 || voice >= AWE_MAX_CHANNELS)
- return;
- }
-
- cinfo = &channels[voice];
-
- switch (ctrl_num) {
- case CTL_BANK_SELECT: /* MIDI control #0 */
- DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value));
- if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice) &&
- !ctrls[AWE_MD_TOGGLE_DRUM_BANK])
- break;
- cinfo->bank = value;
- if (cinfo->bank == AWE_DRUM_BANK)
- DRUM_CHANNEL_ON(cinfo->channel);
- else
- DRUM_CHANNEL_OFF(cinfo->channel);
- awe_set_instr(dev, voice, cinfo->instr);
- break;
-
- case CTL_MODWHEEL: /* MIDI control #1 */
- DEBUG(2,printk("AWE32: [modwheel(%d) %d]\n", voice, value));
- awe_modwheel_change(voice, value);
- break;
-
- case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */
- DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value));
- /* zero centered */
- cinfo->bender = value;
- awe_voice_change(voice, awe_set_voice_pitch);
- break;
-
- case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */
- DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value));
- /* value = sense x 100 */
- cinfo->bender_range = value;
- /* no audible pitch change yet.. */
- break;
-
- case CTL_EXPRESSION: /* MIDI control #11 */
- if (SINGLE_LAYER_MODE())
- value /= 128;
- case CTRL_EXPRESSION: /* SEQ1 V2 control */
- DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value));
- /* 0 - 127 */
- cinfo->expression_vol = value;
- awe_voice_change(voice, awe_set_voice_vol);
- break;
-
- case CTL_PAN: /* MIDI control #10 */
- DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value));
- /* (0-127) -> signed 8bit */
- cinfo->panning = value * 2 - 128;
- if (ctrls[AWE_MD_REALTIME_PAN])
- awe_voice_change(voice, awe_set_pan);
- break;
-
- case CTL_MAIN_VOLUME: /* MIDI control #7 */
- if (SINGLE_LAYER_MODE())
- value = (value * 100) / 16383;
- case CTRL_MAIN_VOLUME: /* SEQ1 V2 control */
- DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value));
- /* 0 - 127 */
- cinfo->main_vol = value;
- awe_voice_change(voice, awe_set_voice_vol);
- break;
-
- case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */
- DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value));
- FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2);
- break;
-
- case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */
- DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value));
- FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2);
- break;
-
- case 120: /* all sounds off */
- awe_note_off_all(FALSE);
- break;
- case 123: /* all notes off */
- awe_note_off_all(TRUE);
- break;
-
- case CTL_SUSTAIN: /* MIDI control #64 */
- cinfo->sustained = value;
- if (value != 127)
- awe_voice_change(voice, awe_sustain_off);
- break;
-
- case CTL_SOSTENUTO: /* MIDI control #66 */
- if (value == 127)
- awe_voice_change(voice, awe_sostenuto_on);
- else
- awe_voice_change(voice, awe_sustain_off);
- break;
-
- default:
- DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n",
- voice, ctrl_num, value));
- break;
- }
-}
-
-
-/* voice pan change (value = -128 - 127) */
-static void
-awe_panning(int dev, int voice, int value)
-{
- awe_chan_info *cinfo;
-
- if (! voice_in_range(voice))
- return;
-
- if (playing_mode == AWE_PLAY_MULTI2) {
- voice = voice_alloc->map[voice] >> 8;
- if (voice < 0 || voice >= AWE_MAX_CHANNELS)
- return;
- }
-
- cinfo = &channels[voice];
- cinfo->panning = value;
- DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning));
- if (ctrls[AWE_MD_REALTIME_PAN])
- awe_voice_change(voice, awe_set_pan);
-}
-
-
-/* volume mode change */
-static void
-awe_volume_method(int dev, int mode)
-{
- /* not impremented */
- DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode));
-}
-
-
-#ifndef AWE_NO_PATCHMGR
-/* patch manager */
-static int
-awe_patchmgr(int dev, struct patmgr_info *rec)
-{
- printk("AWE32 Warning: patch manager control not supported\n");
- return 0;
-}
-#endif
-
-
-/* pitch wheel change: 0-16384 */
-static void
-awe_bender(int dev, int voice, int value)
-{
- awe_chan_info *cinfo;
-
- if (! voice_in_range(voice))
- return;
-
- if (playing_mode == AWE_PLAY_MULTI2) {
- voice = voice_alloc->map[voice] >> 8;
- if (voice < 0 || voice >= AWE_MAX_CHANNELS)
- return;
- }
-
- /* convert to zero centered value */
- cinfo = &channels[voice];
- cinfo->bender = value - 8192;
- DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender));
- awe_voice_change(voice, awe_set_voice_pitch);
-}
-
-
-/*----------------------------------------------------------------
- * load a sound patch:
- * three types of patches are accepted: AWE, GUS, and SYSEX.
- *----------------------------------------------------------------*/
-
-static int
-awe_load_patch(int dev, int format, const char *addr,
- int offs, int count, int pmgr_flag)
-{
- awe_patch_info patch;
- int rc = 0;
-
-#ifdef AWE_HAS_GUS_COMPATIBILITY
- if (format == GUS_PATCH) {
- return awe_load_guspatch(addr, offs, count, pmgr_flag);
- } else
-#endif
- if (format == SYSEX_PATCH) {
- /* no system exclusive message supported yet */
- return 0;
- } else if (format != AWE_PATCH) {
- printk("AWE32 Error: Invalid patch format (key) 0x%x\n", format);
- return RET_ERROR(EINVAL);
- }
-
- if (count < AWE_PATCH_INFO_SIZE) {
- printk("AWE32 Error: Patch header too short\n");
- return RET_ERROR(EINVAL);
- }
- COPY_FROM_USER(((char*)&patch) + offs, addr, offs,
- AWE_PATCH_INFO_SIZE - offs);
-
- count -= AWE_PATCH_INFO_SIZE;
- if (count < patch.len) {
- printk("AWE32: sample: Patch record too short (%d<%d)\n",
- count, patch.len);
- return RET_ERROR(EINVAL);
- }
-
- switch (patch.type) {
- case AWE_LOAD_INFO:
- rc = awe_load_info(&patch, addr, count);
- break;
- case AWE_LOAD_DATA:
- rc = awe_load_data(&patch, addr, count);
- break;
- case AWE_OPEN_PATCH:
- rc = awe_open_patch(&patch, addr, count);
- break;
- case AWE_CLOSE_PATCH:
- rc = awe_close_patch(&patch, addr, count);
- break;
- case AWE_UNLOAD_PATCH:
- rc = awe_unload_patch(&patch, addr, count);
- break;
- case AWE_REPLACE_DATA:
- rc = awe_replace_data(&patch, addr, count);
- break;
- case AWE_MAP_PRESET:
- rc = awe_load_map(&patch, addr, count);
- break;
- /* case AWE_PROBE_INFO:
- rc = awe_probe_info(&patch, addr, count);
- break;*/
- case AWE_PROBE_DATA:
- rc = awe_probe_data(&patch, addr, count);
- break;
- case AWE_LOAD_CHORUS_FX:
- rc = awe_load_chorus_fx(&patch, addr, count);
- break;
- case AWE_LOAD_REVERB_FX:
- rc = awe_load_reverb_fx(&patch, addr, count);
- break;
-
- default:
- printk("AWE32 Error: unknown patch format type %d\n",
- patch.type);
- rc = RET_ERROR(EINVAL);
- }
-
- return rc;
-}
-
-
-/* create an sflist record */
-static int
-awe_create_sf(int type, char *name)
-{
- sf_list *rec;
-
- /* terminate sounds */
- awe_reset(0);
- if (current_sf_id >= max_sfs) {
-#ifndef AWE_DYNAMIC_BUFFER
- return 1;
-#else
- int newsize = max_sfs + AWE_MAX_SF_LISTS;
- sf_list *newlist = realloc_block(sflists, sizeof(sf_list)*max_sfs,
- sizeof(sf_list)*newsize);
- if (newlist == NULL)
- return 1;
- sflists = newlist;
- max_sfs = newsize;
-#endif /* dynamic buffer */
- }
- rec = &sflists[current_sf_id];
- rec->sf_id = current_sf_id + 1;
- rec->type = type;
- if (current_sf_id == 0 || (type & AWE_PAT_LOCKED) != 0)
- locked_sf_id = current_sf_id + 1;
- rec->num_info = awe_free_info();
- rec->num_sample = awe_free_sample();
- rec->mem_ptr = awe_free_mem_ptr();
- rec->infos = -1;
- rec->samples = -1;
-
-#ifdef AWE_ALLOW_SAMPLE_SHARING
- rec->shared = 0;
- if (name)
- memcpy(rec->name, name, AWE_PATCH_NAME_LEN);
- else
- strcpy(rec->name, "*TEMPORARY*");
- if (current_sf_id > 0 && name && (type & AWE_PAT_SHARED) != 0) {
- /* is the current font really a shared font? */
- if (is_shared_sf(rec->name)) {
- /* check if the shared font is already installed */
- int i;
- for (i = current_sf_id; i > 0; i--) {
- if (is_identical_name(rec->name, i)) {
- rec->shared = i;
- break;
- }
- }
- }
- }
-#endif /* allow sharing */
-
- current_sf_id++;
-
- return 0;
-}
-
-
-#ifdef AWE_ALLOW_SAMPLE_SHARING
-
-/* check if the given name is a valid shared name */
-#define ASC_TO_KEY(c) ((c) - 'A' + 1)
-static int is_shared_sf(unsigned char *name)
-{
- static unsigned char id_head[6] = {
- ASC_TO_KEY('A'), ASC_TO_KEY('W'), ASC_TO_KEY('E'),
- AWE_MAJOR_VERSION,
- AWE_MINOR_VERSION,
- AWE_TINY_VERSION,
- };
- if (MEMCMP(name, id_head, 6) == 0)
- return TRUE;
- return FALSE;
-}
-
-/* check if the given name matches to the existing list */
-static int is_identical_name(unsigned char *name, int sf)
-{
- char *id = sflists[sf-1].name;
- if (is_shared_sf(id) && MEMCMP(id, name, AWE_PATCH_NAME_LEN) == 0)
- return TRUE;
- return FALSE;
-}
-
-/* check if the given voice info exists */
-static int info_duplicated(awe_voice_list *rec)
-{
- int j, sf_id;
- sf_list *sf;
-
- /* search for all sharing lists */
- for (sf_id = rec->v.sf_id; sf_id > 0 && sf_id <= current_sf_id; sf_id = sf->shared) {
- sf = &sflists[sf_id - 1];
- for (j = sf->infos; j >= 0; j = infos[j].next) {
- awe_voice_list *p = &infos[j];
- if (p->type == V_ST_NORMAL &&
- p->bank == rec->bank &&
- p->instr == rec->instr &&
- p->v.low == rec->v.low &&
- p->v.high == rec->v.high &&
- p->v.sample == rec->v.sample)
- return TRUE;
- }
- }
- return FALSE;
-}
-
-#endif /* AWE_ALLOW_SAMPLE_SHARING */
-
-
-/* open patch; create sf list and set opened flag */
-static int
-awe_open_patch(awe_patch_info *patch, const char *addr, int count)
-{
- awe_open_parm parm;
- int shared;
-
- COPY_FROM_USER(&parm, addr, AWE_PATCH_INFO_SIZE, sizeof(parm));
- shared = FALSE;
-
-#ifdef AWE_ALLOW_SAMPLE_SHARING
- if (current_sf_id > 0 && (parm.type & AWE_PAT_SHARED) != 0) {
- /* is the previous font the same font? */
- if (is_identical_name(parm.name, current_sf_id)) {
- /* then append to the previous */
- shared = TRUE;
- awe_reset(0);
- if (parm.type & AWE_PAT_LOCKED)
- locked_sf_id = current_sf_id;
- }
- }
-#endif /* allow sharing */
- if (! shared) {
- if (awe_create_sf(parm.type, parm.name)) {
- printk("AWE32: can't open: failed to alloc new list\n");
- return RET_ERROR(ENOSPC);
- }
- }
- patch_opened = TRUE;
- return current_sf_id;
-}
-
-/* check if the patch is already opened */
-static int
-check_patch_opened(int type, char *name)
-{
- if (! patch_opened) {
- if (awe_create_sf(type, name)) {
- printk("AWE32: failed to alloc new list\n");
- return RET_ERROR(ENOSPC);
- }
- patch_opened = TRUE;
- return current_sf_id;
- }
- return current_sf_id;
-}
-
-/* close the patch; if no voice is loaded, remove the patch */
-static int
-awe_close_patch(awe_patch_info *patch, const char *addr, int count)
-{
- if (patch_opened && current_sf_id > 0) {
- /* if no voice is loaded, release the current patch */
- if (sflists[current_sf_id-1].infos == -1)
- awe_remove_samples(current_sf_id - 1);
- }
- patch_opened = 0;
- return 0;
-}
-
-
-/* remove the latest patch */
-static int
-awe_unload_patch(awe_patch_info *patch, const char *addr, int count)
-{
- if (current_sf_id > 0 && current_sf_id > locked_sf_id)
- awe_remove_samples(current_sf_id - 1);
- return 0;
-}
-
-/* allocate voice info list records */
-static int alloc_new_info(int nvoices)
-{
- int newsize, free_info;
- awe_voice_list *newlist;
- free_info = awe_free_info();
- if (free_info + nvoices >= max_infos) {
-#ifndef AWE_DYNAMIC_BUFFER
- printk("AWE32: can't alloc info table\n");
- return RET_ERROR(ENOSPC);
-#else
- do {
- newsize = max_infos + AWE_MAX_INFOS;
- } while (free_info + nvoices >= newsize);
- newlist = realloc_block(infos, sizeof(awe_voice_list)*max_infos,
- sizeof(awe_voice_list)*newsize);
- if (newlist == NULL) {
- printk("AWE32: can't alloc info table\n");
- return RET_ERROR(ENOSPC);
- }
- infos = newlist;
- max_infos = newsize;
-#endif
- }
- return 0;
-}
-
-/* allocate sample info list records */
-static int alloc_new_sample(void)
-{
- int newsize, free_sample;
- awe_sample_list *newlist;
- free_sample = awe_free_sample();
- if (free_sample >= max_samples) {
-#ifndef AWE_DYNAMIC_BUFFER
- printk("AWE32: can't alloc sample table\n");
- return RET_ERROR(ENOSPC);
-#else
- newsize = max_samples + AWE_MAX_SAMPLES;
- newlist = realloc_block(samples,
- sizeof(awe_sample_list)*max_samples,
- sizeof(awe_sample_list)*newsize);
- if (newlist == NULL) {
- printk("AWE32: can't alloc sample table\n");
- return RET_ERROR(ENOSPC);
- }
- samples = newlist;
- max_samples = newsize;
-#endif
- }
- return 0;
-}
-
-/* load voice map */
-static int
-awe_load_map(awe_patch_info *patch, const char *addr, int count)
-{
- awe_voice_map map;
- awe_voice_list *rec;
- int p, free_info;
-
- /* get the link info */
- if (count < sizeof(map)) {
- printk("AWE32 Error: invalid patch info length\n");
- return RET_ERROR(EINVAL);
- }
- COPY_FROM_USER(&map, addr, AWE_PATCH_INFO_SIZE, sizeof(map));
-
- /* check if the identical mapping already exists */
- p = awe_search_instr(map.map_bank, map.map_instr);
- for (; p >= 0; p = infos[p].next_instr) {
- if (p >= 0 && infos[p].type == V_ST_MAPPED &&
- infos[p].v.low == map.map_key &&
- infos[p].v.start == map.src_instr &&
- infos[p].v.end == map.src_bank &&
- infos[p].v.fixkey == map.src_key)
- return 0; /* already present! */
- }
-
- if (check_patch_opened(AWE_PAT_TYPE_MAP, NULL) < 0)
- return RET_ERROR(ENOSPC);
-
- if (alloc_new_info(1) < 0)
- return RET_ERROR(ENOSPC);
-
- free_info = awe_free_info();
- rec = &infos[free_info];
- rec->bank = map.map_bank;
- rec->instr = map.map_instr;
- rec->type = V_ST_MAPPED;
- rec->disabled = FALSE;
- awe_init_voice_info(&rec->v);
- if (map.map_key >= 0) {
- rec->v.low = map.map_key;
- rec->v.high = map.map_key;
- }
- rec->v.start = map.src_instr;
- rec->v.end = map.src_bank;
- rec->v.fixkey = map.src_key;
- rec->v.sf_id = current_sf_id;
- add_info_list(free_info);
- add_sf_info(free_info);
-
- return 0;
-}
-
-#if 0
-/* probe preset in the current list -- nothing to be loaded */
-static int
-awe_probe_info(awe_patch_info *patch, const char *addr, int count)
-{
-#ifdef AWE_ALLOW_SAMPLE_SHARING
- awe_voice_map map;
- int p;
-
- if (! patch_opened)
- return RET_ERROR(EINVAL);
-
- /* get the link info */
- if (count < sizeof(map)) {
- printk("AWE32 Error: invalid patch info length\n");
- return RET_ERROR(EINVAL);
- }
- COPY_FROM_USER(&map, addr, AWE_PATCH_INFO_SIZE, sizeof(map));
-
- /* check if the identical mapping already exists */
- p = awe_search_instr(map.src_bank, map.src_instr);
- for (; p >= 0; p = infos[p].next_instr) {
- if (p >= 0 && infos[p].type == V_ST_NORMAL &&
- is_identical_id(infos[p].v.sf_id, current_sf_id) &&
- infos[p].v.low <= map.src_key &&
- infos[p].v.high >= map.src_key)
- return 0; /* already present! */
- }
-#endif /* allow sharing */
- return RET_ERROR(EINVAL);
-}
-#endif
-
-/* probe sample in the current list -- nothing to be loaded */
-static int
-awe_probe_data(awe_patch_info *patch, const char *addr, int count)
-{
-#ifdef AWE_ALLOW_SAMPLE_SHARING
- if (! patch_opened)
- return RET_ERROR(EINVAL);
-
- /* search the specified sample by optarg */
- if (search_sample_index(current_sf_id, patch->optarg, 0) >= 0)
- return 0;
-#endif /* allow sharing */
- return RET_ERROR(EINVAL);
-}
-
-/* load voice information data */
-static int
-awe_load_info(awe_patch_info *patch, const char *addr, int count)
-{
- int offset;
- awe_voice_rec_hdr hdr;
- int i;
- int total_size;
-
- if (count < AWE_VOICE_REC_SIZE) {
- printk("AWE32 Error: invalid patch info length\n");
- return RET_ERROR(EINVAL);
- }
-
- offset = AWE_PATCH_INFO_SIZE;
- COPY_FROM_USER((char*)&hdr, addr, offset, AWE_VOICE_REC_SIZE);
- offset += AWE_VOICE_REC_SIZE;
-
- if (hdr.nvoices <= 0 || hdr.nvoices >= 100) {
- printk("AWE32 Error: Illegal voice number %d\n", hdr.nvoices);
- return RET_ERROR(EINVAL);
- }
- total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * hdr.nvoices;
- if (count < total_size) {
- printk("AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n",
- count, hdr.nvoices);
- return RET_ERROR(EINVAL);
- }
-
- if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0)
- return RET_ERROR(ENOSPC);
-
-#if 0 /* it looks like not so useful.. */
- /* check if the same preset already exists in the info list */
- for (i = sflists[current_sf_id-1].infos; i >= 0; i = infos[i].next) {
- if (infos[i].disabled) continue;
- if (infos[i].bank == hdr.bank && infos[i].instr == hdr.instr) {
- /* in exclusive mode, do skip loading this */
- if (hdr.write_mode == AWE_WR_EXCLUSIVE)
- return 0;
- /* in replace mode, disable the old data */
- else if (hdr.write_mode == AWE_WR_REPLACE)
- infos[i].disabled = TRUE;
- }
- }
- if (hdr.write_mode == AWE_WR_REPLACE)
- rebuild_preset_list();
-#endif
-
- if (alloc_new_info(hdr.nvoices) < 0)
- return RET_ERROR(ENOSPC);
-
- for (i = 0; i < hdr.nvoices; i++) {
- int rec = awe_free_info();
-
- infos[rec].bank = hdr.bank;
- infos[rec].instr = hdr.instr;
- infos[rec].type = V_ST_NORMAL;
- infos[rec].disabled = FALSE;
-
- /* copy awe_voice_info parameters */
- COPY_FROM_USER(&infos[rec].v, addr, offset, AWE_VOICE_INFO_SIZE);
- offset += AWE_VOICE_INFO_SIZE;
- infos[rec].v.sf_id = current_sf_id;
-#ifdef AWE_ALLOW_SAMPLE_SHARING
- if (sflists[current_sf_id-1].shared) {
- if (info_duplicated(&infos[rec]))
- continue;
- }
-#endif /* allow sharing */
- if (infos[rec].v.mode & AWE_MODE_INIT_PARM)
- awe_init_voice_parm(&infos[rec].v.parm);
- awe_set_sample(&infos[rec].v);
- add_info_list(rec);
- add_sf_info(rec);
- }
-
- return 0;
-}
-
-
-/* load wave sample data */
-static int
-awe_load_data(awe_patch_info *patch, const char *addr, int count)
-{
- int offset, size;
- int rc, free_sample;
- awe_sample_info tmprec, *rec;
-
- if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0)
- return RET_ERROR(ENOSPC);
-
- size = (count - AWE_SAMPLE_INFO_SIZE) / 2;
- offset = AWE_PATCH_INFO_SIZE;
- COPY_FROM_USER(&tmprec, addr, offset, AWE_SAMPLE_INFO_SIZE);
- offset += AWE_SAMPLE_INFO_SIZE;
- if (size != tmprec.size) {
- printk("AWE32: load: sample size differed (%d != %d)\n",
- tmprec.size, size);
- return RET_ERROR(EINVAL);
- }
-
- if (search_sample_index(current_sf_id, tmprec.sample, 0) >= 0) {
-#ifdef AWE_ALLOW_SAMPLE_SHARING
- /* if shared sample, skip this data */
- if (sflists[current_sf_id-1].type & AWE_PAT_SHARED)
- return 0;
-#endif /* allow sharing */
- DEBUG(1,printk("AWE32: sample data %d already present\n", tmprec.sample));
- return RET_ERROR(EINVAL);
- }
-
- if (alloc_new_sample() < 0)
- return RET_ERROR(ENOSPC);
-
- free_sample = awe_free_sample();
- rec = &samples[free_sample].v;
- *rec = tmprec;
-
- if (rec->size > 0)
- if ((rc = awe_write_wave_data(addr, offset, rec, -1)) != 0)
- return rc;
-
- rec->sf_id = current_sf_id;
-
- add_sf_sample(free_sample);
-
- return 0;
-}
-
-
-/* replace wave sample data */
-static int
-awe_replace_data(awe_patch_info *patch, const char *addr, int count)
-{
- int offset;
- int size;
- int rc, i;
- int channels;
- awe_sample_info cursmp;
- int save_mem_ptr;
-
- if (! patch_opened) {
- printk("AWE32: replace: patch not opened\n");
- return RET_ERROR(EINVAL);
- }
-
- size = (count - AWE_SAMPLE_INFO_SIZE) / 2;
- offset = AWE_PATCH_INFO_SIZE;
- COPY_FROM_USER(&cursmp, addr, offset, AWE_SAMPLE_INFO_SIZE);
- offset += AWE_SAMPLE_INFO_SIZE;
- if (cursmp.size == 0 || size != cursmp.size) {
- printk("AWE32: replace: illegal sample size (%d!=%d)\n",
- cursmp.size, size);
- return RET_ERROR(EINVAL);
- }
- channels = patch->optarg;
- if (channels <= 0 || channels > AWE_NORMAL_VOICES) {
- printk("AWE32: replace: illegal channels %d\n", channels);
- return RET_ERROR(EINVAL);
- }
-
- for (i = sflists[current_sf_id-1].samples;
- i >= 0; i = samples[i].next) {
- if (samples[i].v.sample == cursmp.sample)
- break;
- }
- if (i < 0) {
- printk("AWE32: replace: cannot find existing sample data %d\n",
- cursmp.sample);
- return RET_ERROR(EINVAL);
- }
-
- if (samples[i].v.size != cursmp.size) {
- printk("AWE32: replace: exiting size differed (%d!=%d)\n",
- samples[i].v.size, cursmp.size);
- return RET_ERROR(EINVAL);
- }
-
- save_mem_ptr = awe_free_mem_ptr();
- sflists[current_sf_id-1].mem_ptr = samples[i].v.start - awe_mem_start;
- MEMCPY(&samples[i].v, &cursmp, sizeof(cursmp));
- if ((rc = awe_write_wave_data(addr, offset, &samples[i].v, channels)) != 0)
- return rc;
- sflists[current_sf_id-1].mem_ptr = save_mem_ptr;
- samples[i].v.sf_id = current_sf_id;
-
- return 0;
-}
-
-
-/*----------------------------------------------------------------*/
-
-static const char *readbuf_addr;
-static int readbuf_offs;
-static int readbuf_flags;
-#ifdef MALLOC_LOOP_DATA
-static unsigned short *readbuf_loop;
-static int readbuf_loopstart, readbuf_loopend;
-#endif
-
-/* initialize read buffer */
-static int
-readbuf_init(const char *addr, int offset, awe_sample_info *sp)
-{
-#ifdef MALLOC_LOOP_DATA
- readbuf_loop = NULL;
- readbuf_loopstart = sp->loopstart;
- readbuf_loopend = sp->loopend;
- if (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) {
- int looplen = sp->loopend - sp->loopstart;
- readbuf_loop = my_malloc(looplen * 2);
- if (readbuf_loop == NULL) {
- printk("AWE32: can't malloc temp buffer\n");
- return RET_ERROR(ENOSPC);
- }
- }
-#endif
- readbuf_addr = addr;
- readbuf_offs = offset;
- readbuf_flags = sp->mode_flags;
- return 0;
-}
-
-/* read directly from user buffer */
-static unsigned short
-readbuf_word(int pos)
-{
- unsigned short c;
- /* read from user buffer */
- if (readbuf_flags & AWE_SAMPLE_8BITS) {
- unsigned char cc;
- GET_BYTE_FROM_USER(cc, readbuf_addr, readbuf_offs + pos);
- c = cc << 8; /* convert 8bit -> 16bit */
- } else {
- GET_SHORT_FROM_USER(c, readbuf_addr, readbuf_offs + pos * 2);
- }
- if (readbuf_flags & AWE_SAMPLE_UNSIGNED)
- c ^= 0x8000; /* unsigned -> signed */
-#ifdef MALLOC_LOOP_DATA
- /* write on cache for reverse loop */
- if (readbuf_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) {
- if (pos >= readbuf_loopstart && pos < readbuf_loopend)
- readbuf_loop[pos - readbuf_loopstart] = c;
- }
-#endif
- return c;
-}
-
-#ifdef MALLOC_LOOP_DATA
-/* read from cache */
-static unsigned short
-readbuf_word_cache(int pos)
-{
- if (pos >= readbuf_loopstart && pos < readbuf_loopend)
- return readbuf_loop[pos - readbuf_loopstart];
- return 0;
-}
-
-static void
-readbuf_end(void)
-{
- if (readbuf_loop) {
- my_free(readbuf_loop);
- }
- readbuf_loop = NULL;
-}
-
-#else
-
-#define readbuf_word_cache readbuf_word
-#define readbuf_end() /**/
-
-#endif
-
-/*----------------------------------------------------------------*/
-
-#define BLANK_LOOP_START 8
-#define BLANK_LOOP_END 40
-#define BLANK_LOOP_SIZE 48
-
-/* loading onto memory */
-static int
-awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels)
-{
- int i, truesize, dram_offset;
- int rc;
-
- /* be sure loop points start < end */
- if (sp->loopstart > sp->loopend) {
- int tmp = sp->loopstart;
- sp->loopstart = sp->loopend;
- sp->loopend = tmp;
- }
-
- /* compute true data size to be loaded */
- truesize = sp->size;
- if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP)
- truesize += sp->loopend - sp->loopstart;
- if (sp->mode_flags & AWE_SAMPLE_NO_BLANK)
- truesize += BLANK_LOOP_SIZE;
- if (awe_free_mem_ptr() + truesize >= awe_mem_size/2) {
- DEBUG(-1,printk("AWE32 Error: Sample memory full\n"));
- return RET_ERROR(ENOSPC);
- }
-
- /* recalculate address offset */
- sp->end -= sp->start;
- sp->loopstart -= sp->start;
- sp->loopend -= sp->start;
-
- dram_offset = awe_free_mem_ptr() + awe_mem_start;
- sp->start = dram_offset;
- sp->end += dram_offset;
- sp->loopstart += dram_offset;
- sp->loopend += dram_offset;
-
- /* set the total size (store onto obsolete checksum value) */
- if (sp->size == 0)
- sp->checksum = 0;
- else
- sp->checksum = truesize;
-
- if ((rc = awe_open_dram_for_write(dram_offset, channels)) != 0)
- return rc;
-
- if (readbuf_init(addr, offset, sp) < 0)
- return RET_ERROR(ENOSPC);
-
- for (i = 0; i < sp->size; i++) {
- unsigned short c;
- c = readbuf_word(i);
- awe_write_dram(c);
- if (i == sp->loopend &&
- (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))) {
- int looplen = sp->loopend - sp->loopstart;
- /* copy reverse loop */
- int k;
- for (k = 1; k <= looplen; k++) {
- c = readbuf_word_cache(i - k);
- awe_write_dram(c);
- }
- if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) {
- sp->end += looplen;
- } else {
- sp->start += looplen;
- sp->end += looplen;
- }
- }
- }
- readbuf_end();
-
- /* if no blank loop is attached in the sample, add it */
- if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) {
- for (i = 0; i < BLANK_LOOP_SIZE; i++)
- awe_write_dram(0);
- if (sp->mode_flags & AWE_SAMPLE_SINGLESHOT) {
- sp->loopstart = sp->end + BLANK_LOOP_START;
- sp->loopend = sp->end + BLANK_LOOP_END;
- }
- }
-
- sflists[current_sf_id-1].mem_ptr += truesize;
- awe_close_dram();
-
- /* initialize FM */
- awe_init_fm();
-
- return 0;
-}
-
-
-/*----------------------------------------------------------------*/
-
-#ifdef AWE_HAS_GUS_COMPATIBILITY
-
-/* calculate GUS envelope time:
- * is this correct? i have no idea..
- */
-static int
-calc_gus_envelope_time(int rate, int start, int end)
-{
- int r, p, t;
- r = (3 - ((rate >> 6) & 3)) * 3;
- p = rate & 0x3f;
- t = end - start;
- if (t < 0) t = -t;
- if (13 > r)
- t = t << (13 - r);
- else
- t = t >> (r - 13);
- return (t * 10) / (p * 441);
-}
-
-#define calc_gus_sustain(val) (0x7f - vol_table[(val)/2])
-#define calc_gus_attenuation(val) vol_table[(val)/2]
-
-/* load GUS patch */
-static int
-awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag)
-{
- struct patch_info patch;
- awe_voice_info *rec;
- awe_sample_info *smp;
- int sizeof_patch;
- int note, free_sample, free_info;
- int rc;
-
- sizeof_patch = (int)((long)&patch.data[0] - (long)&patch); /* header size */
- if (size < sizeof_patch) {
- printk("AWE32 Error: Patch header too short\n");
- return RET_ERROR(EINVAL);
- }
- COPY_FROM_USER(((char*)&patch) + offs, addr, offs, sizeof_patch - offs);
- size -= sizeof_patch;
- if (size < patch.len) {
- printk("AWE32 Warning: Patch record too short (%d<%d)\n",
- size, patch.len);
- return RET_ERROR(EINVAL);
- }
- if (check_patch_opened(AWE_PAT_TYPE_GUS, NULL) < 0)
- return RET_ERROR(ENOSPC);
- if (alloc_new_sample() < 0)
- return RET_ERROR(ENOSPC);
- if (alloc_new_info(1))
- return RET_ERROR(ENOSPC);
-
- free_sample = awe_free_sample();
- smp = &samples[free_sample].v;
-
- smp->sample = free_sample;
- smp->start = 0;
- smp->end = patch.len;
- smp->loopstart = patch.loop_start;
- smp->loopend = patch.loop_end;
- smp->size = patch.len;
-
- /* set up mode flags */
- smp->mode_flags = 0;
- if (!(patch.mode & WAVE_16_BITS))
- smp->mode_flags |= AWE_SAMPLE_8BITS;
- if (patch.mode & WAVE_UNSIGNED)
- smp->mode_flags |= AWE_SAMPLE_UNSIGNED;
- smp->mode_flags |= AWE_SAMPLE_NO_BLANK;
- if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK)))
- smp->mode_flags |= AWE_SAMPLE_SINGLESHOT;
- if (patch.mode & WAVE_BIDIR_LOOP)
- smp->mode_flags |= AWE_SAMPLE_BIDIR_LOOP;
- if (patch.mode & WAVE_LOOP_BACK)
- smp->mode_flags |= AWE_SAMPLE_REVERSE_LOOP;
-
- DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no, smp->mode_flags));
- if (patch.mode & WAVE_16_BITS) {
- /* convert to word offsets */
- smp->size /= 2;
- smp->end /= 2;
- smp->loopstart /= 2;
- smp->loopend /= 2;
- }
- smp->checksum_flag = 0;
- smp->checksum = 0;
-
- if ((rc = awe_write_wave_data(addr, sizeof_patch, smp, -1)) != 0)
- return rc;
-
- smp->sf_id = current_sf_id;
- add_sf_sample(free_sample);
-
- /* set up voice info */
- free_info = awe_free_info();
- rec = &infos[free_info].v;
- awe_init_voice_info(rec);
- rec->sample = free_sample; /* the last sample */
- rec->rate_offset = calc_rate_offset(patch.base_freq);
- note = freq_to_note(patch.base_note);
- rec->root = note / 100;
- rec->tune = -(note % 100);
- rec->low = freq_to_note(patch.low_note) / 100;
- rec->high = freq_to_note(patch.high_note) / 100;
- DEBUG(1,printk("AWE32: [gus base offset=%d, note=%d, range=%d-%d(%d-%d)]\n",
- rec->rate_offset, note,
- rec->low, rec->high,
- patch.low_note, patch.high_note));
- /* panning position; -128 - 127 => 0-127 */
- rec->pan = (patch.panning + 128) / 2;
-
- /* detuning is ignored */
- /* 6points volume envelope */
- if (patch.mode & WAVE_ENVELOPES) {
- int attack, hold, decay, release;
- attack = calc_gus_envelope_time
- (patch.env_rate[0], 0, patch.env_offset[0]);
- hold = calc_gus_envelope_time
- (patch.env_rate[1], patch.env_offset[0],
- patch.env_offset[1]);
- decay = calc_gus_envelope_time
- (patch.env_rate[2], patch.env_offset[1],
- patch.env_offset[2]);
- release = calc_gus_envelope_time
- (patch.env_rate[3], patch.env_offset[1],
- patch.env_offset[4]);
- release += calc_gus_envelope_time
- (patch.env_rate[4], patch.env_offset[3],
- patch.env_offset[4]);
- release += calc_gus_envelope_time
- (patch.env_rate[5], patch.env_offset[4],
- patch.env_offset[5]);
- rec->parm.volatkhld = (calc_parm_attack(attack) << 8) |
- calc_parm_hold(hold);
- rec->parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) |
- calc_parm_decay(decay);
- rec->parm.volrelease = 0x8000 | calc_parm_decay(release);
- DEBUG(2,printk("AWE32: [gusenv atk=%d, hld=%d, dcy=%d, rel=%d]\n", attack, hold, decay, release));
- rec->attenuation = calc_gus_attenuation(patch.env_offset[0]);
- }
-
- /* tremolo effect */
- if (patch.mode & WAVE_TREMOLO) {
- int rate = (patch.tremolo_rate * 1000 / 38) / 42;
- rec->parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate;
- DEBUG(2,printk("AWE32: [gusenv tremolo rate=%d, dep=%d, tremfrq=%x]\n",
- patch.tremolo_rate, patch.tremolo_depth,
- rec->parm.tremfrq));
- }
- /* vibrato effect */
- if (patch.mode & WAVE_VIBRATO) {
- int rate = (patch.vibrato_rate * 1000 / 38) / 42;
- rec->parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate;
- DEBUG(2,printk("AWE32: [gusenv vibrato rate=%d, dep=%d, tremfrq=%x]\n",
- patch.tremolo_rate, patch.tremolo_depth,
- rec->parm.tremfrq));
- }
-
- /* scale_freq, scale_factor, volume, and fractions not implemented */
-
- /* append to the tail of the list */
- infos[free_info].bank = ctrls[AWE_MD_GUS_BANK];
- infos[free_info].instr = patch.instr_no;
- infos[free_info].disabled = FALSE;
- infos[free_info].type = V_ST_NORMAL;
- infos[free_info].v.sf_id = current_sf_id;
-
- add_info_list(free_info);
- add_sf_info(free_info);
-
- /* set the voice index */
- awe_set_sample(rec);
-
- return 0;
-}
-
-#endif /* AWE_HAS_GUS_COMPATIBILITY */
-
-/*----------------------------------------------------------------
- * sample and voice list handlers
- *----------------------------------------------------------------*/
-
-/* append this to the sf list */
-static void add_sf_info(int rec)
-{
- int sf_id = infos[rec].v.sf_id;
- if (sf_id <= 0) return;
- sf_id--;
- if (sflists[sf_id].infos < 0)
- sflists[sf_id].infos = rec;
- else {
- int i, prev;
- prev = sflists[sf_id].infos;
- while ((i = infos[prev].next) >= 0)
- prev = i;
- infos[prev].next = rec;
- }
- infos[rec].next = -1;
- sflists[sf_id].num_info++;
-}
-
-/* prepend this sample to sf list */
-static void add_sf_sample(int rec)
-{
- int sf_id = samples[rec].v.sf_id;
- if (sf_id <= 0) return;
- sf_id--;
- samples[rec].next = sflists[sf_id].samples;
- sflists[sf_id].samples = rec;
- sflists[sf_id].num_sample++;
-}
-
-/* purge the old records which don't belong with the same file id */
-static void purge_old_list(int rec, int next)
-{
- infos[rec].next_instr = next;
- if (infos[rec].bank == AWE_DRUM_BANK) {
- /* remove samples with the same note range */
- int cur, *prevp = &infos[rec].next_instr;
- int low = infos[rec].v.low;
- int high = infos[rec].v.high;
- for (cur = next; cur >= 0; cur = infos[cur].next_instr) {
- if (infos[cur].v.low == low &&
- infos[cur].v.high == high &&
- ! is_identical_id(infos[cur].v.sf_id, infos[rec].v.sf_id))
- *prevp = infos[cur].next_instr;
- prevp = &infos[cur].next_instr;
- }
- } else {
- if (! is_identical_id(infos[next].v.sf_id, infos[rec].v.sf_id))
- infos[rec].next_instr = -1;
- }
-}
-
-/* prepend to top of the preset table */
-static void add_info_list(int rec)
-{
- int *prevp, cur;
- int instr;
- int bank;
-
- if (infos[rec].disabled)
- return;
-
- instr = infos[rec].instr;
- bank = infos[rec].bank;
- limitvalue(instr, 0, AWE_MAX_PRESETS-1);
- prevp = &preset_table[instr];
- cur = *prevp;
- while (cur >= 0) {
- /* search the first record with the same bank number */
- if (infos[cur].bank == bank) {
- /* replace the list with the new record */
- infos[rec].next_bank = infos[cur].next_bank;
- *prevp = rec;
- purge_old_list(rec, cur);
- return;
- }
- prevp = &infos[cur].next_bank;
- cur = infos[cur].next_bank;
- }
-
- /* this is the first bank record.. just add this */
- infos[rec].next_instr = -1;
- infos[rec].next_bank = preset_table[instr];
- preset_table[instr] = rec;
-}
-
-/* remove samples later than the specified sf_id */
-static void
-awe_remove_samples(int sf_id)
-{
- if (sf_id <= 0) {
- awe_reset_samples();
- return;
- }
- /* already removed? */
- if (current_sf_id <= sf_id)
- return;
-
- current_sf_id = sf_id;
- if (locked_sf_id > sf_id)
- locked_sf_id = sf_id;
-
- rebuild_preset_list();
-}
-
-/* rebuild preset search list */
-static void rebuild_preset_list(void)
-{
- int i, j;
-
- for (i = 0; i < AWE_MAX_PRESETS; i++)
- preset_table[i] = -1;
-
- for (i = 0; i < current_sf_id; i++) {
- for (j = sflists[i].infos; j >= 0; j = infos[j].next)
- add_info_list(j);
- }
-}
-
-/* compare the given sf_id pair */
-static int is_identical_id(int id1, int id2)
-{
- if (id1 == id2)
- return TRUE;
- if (id1 <= 0 || id2 <= 0) /* this must not happen.. */
- return FALSE;
-#ifdef AWE_ALLOW_SAMPLE_SHARING
- {
- /* compare with the sharing id */
- int i;
- if (id1 < id2) { /* make sure id1 > id2 */
- int tmp; tmp = id1; id1 = id2; id2 = tmp;
- }
- for (i = sflists[id1-1].shared; i > 0 && i <= current_sf_id; i = sflists[i-1].shared) {
- if (i == id2)
- return TRUE;
- }
- }
-#endif /* allow sharing */
- return FALSE;
-}
-
-/* search the sample index matching with the given sample id */
-static int search_sample_index(int sf, int sample, int level)
-{
- int i;
-
- if (sf <= 0 || sf > current_sf_id)
- return -1; /* this must not happen */
-
- for (i = sflists[sf-1].samples; i >= 0; i = samples[i].next) {
- if (samples[i].v.sample == sample)
- return i;
- }
-#ifdef AWE_ALLOW_SAMPLE_SHARING
- if ((i = sflists[sf-1].shared) > 0 && i <= current_sf_id) { /* search recursively */
- if (level > current_sf_id)
- return -1; /* strange sharing loop.. quit */
- return search_sample_index(i, sample, level + 1);
- }
-#endif
- return -1;
-}
-
-/* search the specified sample */
-static short
-awe_set_sample(awe_voice_info *vp)
-{
- int i;
-
- vp->index = -1;
- if ((i = search_sample_index(vp->sf_id, vp->sample, 0)) < 0)
- return -1;
-
- /* set the actual sample offsets */
- vp->start += samples[i].v.start;
- vp->end += samples[i].v.end;
- vp->loopstart += samples[i].v.loopstart;
- vp->loopend += samples[i].v.loopend;
- /* copy mode flags */
- vp->mode = samples[i].v.mode_flags;
- /* set index */
- vp->index = i;
-
- return i;
-}
-
-
-/*----------------------------------------------------------------
- * voice allocation
- *----------------------------------------------------------------*/
-
-/* look for all voices associated with the specified note & velocity */
-static int
-awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist)
-{
- int nvoices;
-
- nvoices = 0;
- for (; rec >= 0; rec = infos[rec].next_instr) {
- if (note >= infos[rec].v.low &&
- note <= infos[rec].v.high &&
- velocity >= infos[rec].v.vellow &&
- velocity <= infos[rec].v.velhigh) {
- if (infos[rec].type == V_ST_MAPPED) {
- /* mapper */
- vlist[0] = &infos[rec].v;
- return -1;
- }
- vlist[nvoices++] = &infos[rec].v;
- if (nvoices >= AWE_MAX_VOICES)
- break;
- }
- }
- return nvoices;
-}
-
-/* store the voice list from the specified note and velocity.
- if the preset is mapped, seek for the destination preset, and rewrite
- the note number if necessary.
- */
-static int
-really_alloc_voices(int vrec, int def_vrec, int *note, int velocity, awe_voice_info **vlist, int level)
-{
- int nvoices;
-
- nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist);
- if (nvoices == 0)
- nvoices = awe_search_multi_voices(def_vrec, *note, velocity, vlist);
- if (nvoices < 0) { /* mapping */
- int preset = vlist[0]->start;
- int bank = vlist[0]->end;
- int key = vlist[0]->fixkey;
- if (level > 5) {
- printk("AWE32: too deep mapping level\n");
- return 0;
- }
- vrec = awe_search_instr(bank, preset);
- if (bank == AWE_DRUM_BANK)
- def_vrec = awe_search_instr(bank, 0);
- else
- def_vrec = awe_search_instr(0, preset);
- if (key >= 0)
- *note = key;
- return really_alloc_voices(vrec, def_vrec, note, velocity, vlist, level+1);
- }
-
- return nvoices;
-}
-
-/* allocate voices corresponding note and velocity; supports multiple insts. */
-static void
-awe_alloc_multi_voices(int ch, int note, int velocity, int key)
-{
- int i, v, nvoices;
- awe_voice_info *vlist[AWE_MAX_VOICES];
-
- if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0)
- awe_set_instr(0, ch, channels[ch].instr);
-
- /* check the possible voices; note may be changeable if mapped */
- nvoices = really_alloc_voices(channels[ch].vrec, channels[ch].def_vrec,
- ¬e, velocity, vlist, 0);
-
- /* set the voices */
- current_alloc_time++;
- for (i = 0; i < nvoices; i++) {
- v = awe_clear_voice();
- voices[v].key = key;
- voices[v].ch = ch;
- voices[v].note = note;
- voices[v].velocity = velocity;
- voices[v].time = current_alloc_time;
- voices[v].cinfo = &channels[ch];
- voices[v].sample = vlist[i];
- voices[v].state = AWE_ST_MARK;
- voices[v].layer = nvoices - i - 1; /* in reverse order */
- }
-
- /* clear the mark in allocated voices */
- for (i = 0; i < awe_max_voices; i++) {
- if (voices[i].state == AWE_ST_MARK)
- voices[i].state = AWE_ST_OFF;
-
- }
-}
-
-
-/* search the best voice from the specified status condition */
-static int
-search_best_voice(int condition)
-{
- int i, time, best;
- int vtarget = 0xffff, min_vtarget = 0xffff;
-
- best = -1;
- time = current_alloc_time + 1;
- for (i = 0; i < awe_max_voices; i++) {
- if (! (voices[i].state & condition))
- continue;
-#ifdef AWE_CHECK_VTARGET
- /* get current volume */
- vtarget = (awe_peek_dw(AWE_VTFT(i)) >> 16) & 0xffff;
-#endif
- if (best < 0 || vtarget < min_vtarget ||
- (vtarget == min_vtarget && voices[i].time < time)) {
- best = i;
- time = voices[i].time;
- min_vtarget = vtarget;
- }
- }
- /* clear voice */
- if (best >= 0) {
- if (voices[best].state != AWE_ST_OFF)
- awe_terminate(best);
- awe_voice_init(best, TRUE);
- }
-
- return best;
-}
-
-/* search an empty voice.
- if no empty voice is found, at least terminate a voice
- */
-static int
-awe_clear_voice(void)
-{
- int best;
-
- /* looking for the oldest empty voice */
- if ((best = search_best_voice(AWE_ST_OFF)) >= 0)
- return best;
- if ((best = search_best_voice(AWE_ST_RELEASED)) >= 0)
- return best;
- /* looking for the oldest sustained voice */
- if ((best = search_best_voice(AWE_ST_SUSTAINED)) >= 0)
- return best;
-
- if (MULTI_LAYER_MODE() && ctrls[AWE_MD_CHN_PRIOR]) {
- int ch = -1;
- int time = current_alloc_time + 1;
- int i;
- /* looking for the voices from high channel (except drum ch) */
- for (i = 0; i < awe_max_voices; i++) {
- if (IS_DRUM_CHANNEL(voices[i].ch)) continue;
- if (voices[i].ch < ch) continue;
- if (voices[i].state != AWE_ST_MARK &&
- (voices[i].ch > ch || voices[i].time < time)) {
- best = i;
- time = voices[i].time;
- ch = voices[i].ch;
- }
- }
- }
- if (best < 0)
- best = search_best_voice(~AWE_ST_MARK);
-
- if (best >= 0)
- return best;
-
- return 0;
-}
-
-
-/* search sample for the specified note & velocity and set it on the voice;
- * note that voice is the voice index (not channel index)
- */
-static void
-awe_alloc_one_voice(int voice, int note, int velocity)
-{
- int ch, nvoices;
- awe_voice_info *vlist[AWE_MAX_VOICES];
-
- ch = voices[voice].ch;
- if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0)
- awe_set_instr(0, ch, channels[ch].instr);
-
- nvoices = really_alloc_voices(voices[voice].cinfo->vrec,
- voices[voice].cinfo->def_vrec,
- ¬e, velocity, vlist, 0);
- if (nvoices > 0) {
- voices[voice].time = ++current_alloc_time;
- voices[voice].sample = vlist[0]; /* use the first one */
- voices[voice].layer = 0;
- voices[voice].note = note;
- voices[voice].velocity = velocity;
- }
-}
-
-
-/*----------------------------------------------------------------
- * sequencer2 functions
- *----------------------------------------------------------------*/
-
-/* search an empty voice; used by sequencer2 */
-static int
-awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc)
-{
- playing_mode = AWE_PLAY_MULTI2;
- awe_info.nr_voices = AWE_MAX_CHANNELS;
- return awe_clear_voice();
-}
-
-
-/* set up voice; used by sequencer2 */
-static void
-awe_setup_voice(int dev, int voice, int chn)
-{
- struct channel_info *info;
- if (synth_devs[dev] == NULL ||
- (info = &synth_devs[dev]->chn_info[chn]) == NULL)
- return;
-
- if (voice < 0 || voice >= awe_max_voices)
- return;
-
- DEBUG(2,printk("AWE32: [setup(%d) ch=%d]\n", voice, chn));
- channels[chn].expression_vol = info->controllers[CTL_EXPRESSION];
- channels[chn].main_vol = info->controllers[CTL_MAIN_VOLUME];
- channels[chn].panning =
- info->controllers[CTL_PAN] * 2 - 128; /* signed 8bit */
- channels[chn].bender = info->bender_value; /* zero center */
- channels[chn].bank = info->controllers[CTL_BANK_SELECT];
- channels[chn].sustained = info->controllers[CTL_SUSTAIN];
- if (info->controllers[CTL_EXT_EFF_DEPTH]) {
- FX_SET(&channels[chn].fx, AWE_FX_REVERB,
- info->controllers[CTL_EXT_EFF_DEPTH] * 2);
- }
- if (info->controllers[CTL_CHORUS_DEPTH]) {
- FX_SET(&channels[chn].fx, AWE_FX_CHORUS,
- info->controllers[CTL_CHORUS_DEPTH] * 2);
- }
- awe_set_instr(dev, chn, info->pgm_num);
-}
-
-
-#ifdef CONFIG_AWE32_MIXER
-/*================================================================
- * AWE32 mixer device control
- *================================================================*/
-
-static int awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg);
-
-static int my_mixerdev = -1;
-
-static struct mixer_operations awe_mixer_operations = {
-#ifndef __FreeBSD__
- "AWE32",
-#endif
- "AWE32 Equalizer",
- awe_mixer_ioctl,
-};
-
-static void attach_mixer(void)
-{
- if ((my_mixerdev = sound_alloc_mixerdev()) >= 0) {
- mixer_devs[my_mixerdev] = &awe_mixer_operations;
- }
-}
-
-static void unload_mixer(void)
-{
- if (my_mixerdev >= 0)
- sound_unload_mixerdev(my_mixerdev);
-}
-
-static int
-awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
-{
- int i, level, value;
-
- if (((cmd >> 8) & 0xff) != 'M')
- return RET_ERROR(EINVAL);
-
- level = (int)IOCTL_IN(arg);
- level = ((level & 0xff) + (level >> 8)) / 2;
- DEBUG(0,printk("AWEMix: cmd=%x val=%d\n", cmd & 0xff, level));
-
- if (IO_WRITE_CHECK(cmd)) {
- switch (cmd & 0xff) {
- case SOUND_MIXER_BASS:
- value = level * 12 / 100;
- if (value >= 12)
- value = 11;
- ctrls[AWE_MD_BASS_LEVEL] = value;
- awe_update_equalizer();
- break;
- case SOUND_MIXER_TREBLE:
- value = level * 12 / 100;
- if (value >= 12)
- value = 11;
- ctrls[AWE_MD_TREBLE_LEVEL] = value;
- awe_update_equalizer();
- break;
- case SOUND_MIXER_VOLUME:
- level = level * 127 / 100;
- if (level >= 128) level = 127;
- atten_relative = FALSE;
- atten_offset = vol_table[level];
- awe_update_volume();
- break;
- }
- }
- switch (cmd & 0xff) {
- case SOUND_MIXER_BASS:
- level = ctrls[AWE_MD_BASS_LEVEL] * 100 / 24;
- level = (level << 8) | level;
- break;
- case SOUND_MIXER_TREBLE:
- level = ctrls[AWE_MD_TREBLE_LEVEL] * 100 / 24;
- level = (level << 8) | level;
- break;
- case SOUND_MIXER_VOLUME:
- value = atten_offset;
- if (atten_relative)
- value += ctrls[AWE_MD_ZERO_ATTEN];
- for (i = 127; i > 0; i--) {
- if (value <= vol_table[i])
- break;
- }
- level = i * 100 / 127;
- level = (level << 8) | level;
- break;
- case SOUND_MIXER_DEVMASK:
- level = SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_VOLUME;
- break;
- default:
- level = 0;
- break;
- }
- return IOCTL_OUT(arg, level);
-}
-#endif /* CONFIG_AWE32_MIXER */
-
-
-/*================================================================
- * initialization of AWE32
- *================================================================*/
-
-/* intiailize audio channels */
-static void
-awe_init_audio(void)
-{
- int ch;
-
- /* turn off envelope engines */
- for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
- awe_poke(AWE_DCYSUSV(ch), 0x80);
- }
-
- /* reset all other parameters to zero */
- for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
- awe_poke(AWE_ENVVOL(ch), 0);
- awe_poke(AWE_ENVVAL(ch), 0);
- awe_poke(AWE_DCYSUS(ch), 0);
- awe_poke(AWE_ATKHLDV(ch), 0);
- awe_poke(AWE_LFO1VAL(ch), 0);
- awe_poke(AWE_ATKHLD(ch), 0);
- awe_poke(AWE_LFO2VAL(ch), 0);
- awe_poke(AWE_IP(ch), 0);
- awe_poke(AWE_IFATN(ch), 0);
- awe_poke(AWE_PEFE(ch), 0);
- awe_poke(AWE_FMMOD(ch), 0);
- awe_poke(AWE_TREMFRQ(ch), 0);
- awe_poke(AWE_FM2FRQ2(ch), 0);
- awe_poke_dw(AWE_PTRX(ch), 0);
- awe_poke_dw(AWE_VTFT(ch), 0);
- awe_poke_dw(AWE_PSST(ch), 0);
- awe_poke_dw(AWE_CSL(ch), 0);
- awe_poke_dw(AWE_CCCA(ch), 0);
- }
-
- for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
- awe_poke_dw(AWE_CPF(ch), 0);
- awe_poke_dw(AWE_CVCF(ch), 0);
- }
-}
-
-
-/* initialize DMA address */
-static void
-awe_init_dma(void)
-{
- awe_poke_dw(AWE_SMALR, 0);
- awe_poke_dw(AWE_SMARR, 0);
- awe_poke_dw(AWE_SMALW, 0);
- awe_poke_dw(AWE_SMARW, 0);
-}
-
-
-/* initialization arrays; from ADIP */
-
-static unsigned short init1[128] = {
- 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330,
- 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730,
- 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30,
- 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30,
-
- 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330,
- 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730,
- 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30,
- 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30,
-
- 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330,
- 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730,
- 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30,
- 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30,
-
- 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330,
- 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730,
- 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30,
- 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30,
-};
-
-static unsigned short init2[128] = {
- 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330,
- 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730,
- 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30,
- 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30,
-
- 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330,
- 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730,
- 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30,
- 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30,
-
- 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330,
- 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730,
- 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30,
- 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30,
-
- 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330,
- 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730,
- 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30,
- 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,
-};
-
-static unsigned short init3[128] = {
- 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
- 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254,
- 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234,
- 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224,
-
- 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254,
- 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264,
- 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294,
- 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3,
-
- 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287,
- 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7,
- 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386,
- 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55,
-
- 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308,
- 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F,
- 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319,
- 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,
-};
-
-static unsigned short init4[128] = {
- 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
- 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254,
- 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234,
- 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224,
-
- 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254,
- 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264,
- 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294,
- 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3,
-
- 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287,
- 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7,
- 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386,
- 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55,
-
- 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308,
- 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F,
- 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319,
- 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570,
-};
-
-
-/* send initialization arrays to start up */
-static void
-awe_init_array(void)
-{
- awe_send_array(init1);
- awe_wait(1024);
- awe_send_array(init2);
- awe_send_array(init3);
- awe_poke_dw(AWE_HWCF4, 0);
- awe_poke_dw(AWE_HWCF5, 0x83);
- awe_poke_dw(AWE_HWCF6, 0x8000);
- awe_send_array(init4);
-}
-
-/* send an initialization array */
-static void
-awe_send_array(unsigned short *data)
-{
- int i;
- unsigned short *p;
-
- p = data;
- for (i = 0; i < AWE_MAX_VOICES; i++, p++)
- awe_poke(AWE_INIT1(i), *p);
- for (i = 0; i < AWE_MAX_VOICES; i++, p++)
- awe_poke(AWE_INIT2(i), *p);
- for (i = 0; i < AWE_MAX_VOICES; i++, p++)
- awe_poke(AWE_INIT3(i), *p);
- for (i = 0; i < AWE_MAX_VOICES; i++, p++)
- awe_poke(AWE_INIT4(i), *p);
-}
-
-
-/*
- * set up awe32 channels to some known state.
- */
-
-/* set the envelope & LFO parameters to the default values; see ADIP */
-static void
-awe_tweak_voice(int i)
-{
- /* set all mod/vol envelope shape to minimum */
- awe_poke(AWE_ENVVOL(i), 0x8000);
- awe_poke(AWE_ENVVAL(i), 0x8000);
- awe_poke(AWE_DCYSUS(i), 0x7F7F);
- awe_poke(AWE_ATKHLDV(i), 0x7F7F);
- awe_poke(AWE_ATKHLD(i), 0x7F7F);
- awe_poke(AWE_PEFE(i), 0); /* mod envelope height to zero */
- awe_poke(AWE_LFO1VAL(i), 0x8000); /* no delay for LFO1 */
- awe_poke(AWE_LFO2VAL(i), 0x8000);
- awe_poke(AWE_IP(i), 0xE000); /* no pitch shift */
- awe_poke(AWE_IFATN(i), 0xFF00); /* volume to minimum */
- awe_poke(AWE_FMMOD(i), 0);
- awe_poke(AWE_TREMFRQ(i), 0);
- awe_poke(AWE_FM2FRQ2(i), 0);
-}
-
-static void
-awe_tweak(void)
-{
- int i;
- /* reset all channels */
- for (i = 0; i < awe_max_voices; i++)
- awe_tweak_voice(i);
-}
-
-
-/*
- * initializes the FM section of AWE32;
- * see Vince Vu's unofficial AWE32 programming guide
- */
-
-static void
-awe_init_fm(void)
-{
-#ifndef AWE_ALWAYS_INIT_FM
- /* if no extended memory is on board.. */
- if (awe_mem_size <= 0)
- return;
-#endif
- DEBUG(3,printk("AWE32: initializing FM\n"));
-
- /* Initialize the last two channels for DRAM refresh and producing
- the reverb and chorus effects for Yamaha OPL-3 synthesizer */
-
- /* 31: FM left channel, 0xffffe0-0xffffe8 */
- awe_poke(AWE_DCYSUSV(30), 0x80);
- awe_poke_dw(AWE_PSST(30), 0xFFFFFFE0); /* full left */
- awe_poke_dw(AWE_CSL(30), 0x00FFFFE8 |
- (DEF_FM_CHORUS_DEPTH << 24));
- awe_poke_dw(AWE_PTRX(30), (DEF_FM_REVERB_DEPTH << 8));
- awe_poke_dw(AWE_CPF(30), 0);
- awe_poke_dw(AWE_CCCA(30), 0x00FFFFE3);
-
- /* 32: FM right channel, 0xfffff0-0xfffff8 */
- awe_poke(AWE_DCYSUSV(31), 0x80);
- awe_poke_dw(AWE_PSST(31), 0x00FFFFF0); /* full right */
- awe_poke_dw(AWE_CSL(31), 0x00FFFFF8 |
- (DEF_FM_CHORUS_DEPTH << 24));
- awe_poke_dw(AWE_PTRX(31), (DEF_FM_REVERB_DEPTH << 8));
- awe_poke_dw(AWE_CPF(31), 0x8000);
- awe_poke_dw(AWE_CCCA(31), 0x00FFFFF3);
-
- /* skew volume & cutoff */
- awe_poke_dw(AWE_VTFT(30), 0x8000FFFF);
- awe_poke_dw(AWE_VTFT(31), 0x8000FFFF);
-
- voices[30].state = AWE_ST_FM;
- voices[31].state = AWE_ST_FM;
-
- /* change maximum channels to 30 */
- awe_max_voices = AWE_NORMAL_VOICES;
- if (playing_mode == AWE_PLAY_DIRECT)
- awe_info.nr_voices = awe_max_voices;
- else
- awe_info.nr_voices = AWE_MAX_CHANNELS;
- voice_alloc->max_voice = awe_max_voices;
-}
-
-/*
- * AWE32 DRAM access routines
- */
-
-/* open DRAM write accessing mode */
-static int
-awe_open_dram_for_write(int offset, int channels)
-{
- int vidx[AWE_NORMAL_VOICES];
- int i;
-
- if (channels < 0 || channels >= AWE_NORMAL_VOICES) {
- channels = AWE_NORMAL_VOICES;
- for (i = 0; i < AWE_NORMAL_VOICES; i++)
- vidx[i] = i;
- } else {
- for (i = 0; i < channels; i++) {
- vidx[i] = awe_clear_voice();
- voices[vidx[i]].state = AWE_ST_MARK;
- }
- }
-
- /* use all channels for DMA transfer */
- for (i = 0; i < channels; i++) {
- if (vidx[i] < 0) continue;
- awe_poke(AWE_DCYSUSV(vidx[i]), 0x80);
- awe_poke_dw(AWE_VTFT(vidx[i]), 0);
- awe_poke_dw(AWE_CVCF(vidx[i]), 0);
- awe_poke_dw(AWE_PTRX(vidx[i]), 0x40000000);
- awe_poke_dw(AWE_CPF(vidx[i]), 0x40000000);
- awe_poke_dw(AWE_PSST(vidx[i]), 0);
- awe_poke_dw(AWE_CSL(vidx[i]), 0);
- awe_poke_dw(AWE_CCCA(vidx[i]), 0x06000000);
- voices[vidx[i]].state = AWE_ST_DRAM;
- }
- /* point channels 31 & 32 to ROM samples for DRAM refresh */
- awe_poke_dw(AWE_VTFT(30), 0);
- awe_poke_dw(AWE_PSST(30), 0x1d8);
- awe_poke_dw(AWE_CSL(30), 0x1e0);
- awe_poke_dw(AWE_CCCA(30), 0x1d8);
- awe_poke_dw(AWE_VTFT(31), 0);
- awe_poke_dw(AWE_PSST(31), 0x1d8);
- awe_poke_dw(AWE_CSL(31), 0x1e0);
- awe_poke_dw(AWE_CCCA(31), 0x1d8);
- voices[30].state = AWE_ST_FM;
- voices[31].state = AWE_ST_FM;
-
- /* if full bit is on, not ready to write on */
- if (awe_peek_dw(AWE_SMALW) & 0x80000000) {
- for (i = 0; i < channels; i++) {
- awe_poke_dw(AWE_CCCA(vidx[i]), 0);
- voices[vidx[i]].state = AWE_ST_OFF;
- }
- return RET_ERROR(ENOSPC);
- }
-
- /* set address to write */
- awe_poke_dw(AWE_SMALW, offset);
-
- return 0;
-}
-
-/* open DRAM for RAM size detection */
-static void
-awe_open_dram_for_check(void)
-{
- int i;
- for (i = 0; i < AWE_NORMAL_VOICES; i++) {
- awe_poke(AWE_DCYSUSV(i), 0x80);
- awe_poke_dw(AWE_VTFT(i), 0);
- awe_poke_dw(AWE_CVCF(i), 0);
- awe_poke_dw(AWE_PTRX(i), 0x40000000);
- awe_poke_dw(AWE_CPF(i), 0x40000000);
- awe_poke_dw(AWE_PSST(i), 0);
- awe_poke_dw(AWE_CSL(i), 0);
- if (i & 1) /* DMA write */
- awe_poke_dw(AWE_CCCA(i), 0x06000000);
- else /* DMA read */
- awe_poke_dw(AWE_CCCA(i), 0x04000000);
- voices[i].state = AWE_ST_DRAM;
- }
-}
-
-
-/* close dram access */
-static void
-awe_close_dram(void)
-{
- int i;
- /* wait until FULL bit in SMAxW register be false */
- for (i = 0; i < 10000; i++) {
- if (!(awe_peek_dw(AWE_SMALW) & 0x80000000))
- break;
- awe_wait(10);
- }
-
- for (i = 0; i < AWE_NORMAL_VOICES; i++) {
- if (voices[i].state == AWE_ST_DRAM) {
- awe_poke_dw(AWE_CCCA(i), 0);
- awe_poke(AWE_DCYSUSV(i), 0x807F);
- voices[i].state = AWE_ST_OFF;
- }
- }
-}
-
-
-/*================================================================
- * detect presence of AWE32 and check memory size
- *================================================================*/
-
-/* detect emu8000 chip on the specified address; from VV's guide */
-
-static int
-awe_detect_base(int addr)
-{
- setup_ports(addr, 0, 0);
- if ((awe_peek(AWE_U1) & 0x000F) != 0x000C)
- return 0;
- if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058)
- return 0;
- if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003)
- return 0;
- DEBUG(0,printk("AWE32 found at %x\n", addr));
- return 1;
-}
-
-static int
-awe_detect(void)
-{
- int base;
-
- if (port_setuped) /* already initialized by PnP */
- return 1;
-
- if (awe_port) /* use default i/o port value */
- setup_ports(awe_port, 0, 0);
- else { /* probe it */
- for (base = 0x620; base <= 0x680; base += 0x20)
- if (awe_detect_base(base))
- return 1;
- DEBUG(0,printk("AWE32 not found\n"));
- return 0;
- }
-
- return 1;
-}
-
-
-/*================================================================
- * check dram size on AWE board
- *================================================================*/
-
-/* any three numbers you like */
-#define UNIQUE_ID1 0x1234
-#define UNIQUE_ID2 0x4321
-#define UNIQUE_ID3 0xFFFF
-
-static void
-awe_check_dram(void)
-{
- if (awe_present) /* already initialized */
- return;
-
- if (awe_mem_size >= 0) { /* given by config file or module option */
- awe_mem_size *= 1024; /* convert to Kbytes */
- return;
- }
-
- awe_open_dram_for_check();
-
- awe_mem_size = 0;
-
- /* set up unique two id numbers */
- awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET);
- awe_poke(AWE_SMLD, UNIQUE_ID1);
- awe_poke(AWE_SMLD, UNIQUE_ID2);
-
- while (awe_mem_size < AWE_MAX_DRAM_SIZE) {
- awe_wait(5);
- /* read a data on the DRAM start address */
- awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET);
- awe_peek(AWE_SMLD); /* discard stale data */
- if (awe_peek(AWE_SMLD) != UNIQUE_ID1)
- break;
- if (awe_peek(AWE_SMLD) != UNIQUE_ID2)
- break;
- awe_mem_size += 512; /* increment 512kbytes */
- /* Write a unique data on the test address;
- * if the address is out of range, the data is written on
- * 0x200000(=AWE_DRAM_OFFSET). Then the two id words are
- * broken by this data.
- */
- awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + awe_mem_size*512L);
- awe_poke(AWE_SMLD, UNIQUE_ID3);
- awe_wait(5);
- /* read a data on the just written DRAM address */
- awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + awe_mem_size*512L);
- awe_peek(AWE_SMLD); /* discard stale data */
- if (awe_peek(AWE_SMLD) != UNIQUE_ID3)
- break;
- }
- awe_close_dram();
-
- DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", awe_mem_size));
-
- /* convert to Kbytes */
- awe_mem_size *= 1024;
-}
-
-
-/*================================================================
- * chorus and reverb controls; from VV's guide
- *================================================================*/
-
-/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
-static char chorus_defined[AWE_CHORUS_NUMBERS];
-static awe_chorus_fx_rec chorus_parm[AWE_CHORUS_NUMBERS] = {
- {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */
- {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */
- {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */
- {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */
- {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */
- {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */
- {0xE600, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay */
- {0xE6C0, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay + feedback */
-};
-
-static int
-awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count)
-{
- if (patch->optarg < AWE_CHORUS_PREDEFINED || patch->optarg >= AWE_CHORUS_NUMBERS) {
- printk("AWE32 Error: illegal chorus mode %d for uploading\n", patch->optarg);
- return RET_ERROR(EINVAL);
- }
- if (count < sizeof(awe_chorus_fx_rec)) {
- printk("AWE32 Error: too short chorus fx parameters\n");
- return RET_ERROR(EINVAL);
- }
- COPY_FROM_USER(&chorus_parm[patch->optarg], addr, AWE_PATCH_INFO_SIZE,
- sizeof(awe_chorus_fx_rec));
- chorus_defined[patch->optarg] = TRUE;
- return 0;
-}
-
-static void
-awe_set_chorus_mode(int effect)
-{
- if (effect < 0 || effect >= AWE_CHORUS_NUMBERS ||
- (effect >= AWE_CHORUS_PREDEFINED && !chorus_defined[effect]))
- return;
- awe_poke(AWE_INIT3(9), chorus_parm[effect].feedback);
- awe_poke(AWE_INIT3(12), chorus_parm[effect].delay_offset);
- awe_poke(AWE_INIT4(3), chorus_parm[effect].lfo_depth);
- awe_poke_dw(AWE_HWCF4, chorus_parm[effect].delay);
- awe_poke_dw(AWE_HWCF5, chorus_parm[effect].lfo_freq);
- awe_poke_dw(AWE_HWCF6, 0x8000);
- awe_poke_dw(AWE_HWCF7, 0x0000);
-}
-
-static void
-awe_update_chorus_mode(void)
-{
- awe_set_chorus_mode(ctrls[AWE_MD_CHORUS_MODE]);
-}
-
-/*----------------------------------------------------------------*/
-
-/* reverb mode settings; write the following 28 data of 16 bit length
- * on the corresponding ports in the reverb_cmds array
- */
-static char reverb_defined[AWE_CHORUS_NUMBERS];
-static awe_reverb_fx_rec reverb_parm[AWE_REVERB_NUMBERS] = {
-{{ /* room 1 */
- 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4,
- 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516,
- 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
- 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
-}},
-{{ /* room 2 */
- 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284,
- 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
- 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
- 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
-}},
-{{ /* room 3 */
- 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284,
- 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516,
- 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B,
- 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A,
-}},
-{{ /* hall 1 */
- 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284,
- 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
- 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A,
- 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529,
-}},
-{{ /* hall 2 */
- 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254,
- 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3,
- 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
- 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
-}},
-{{ /* plate */
- 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234,
- 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548,
- 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
- 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
-}},
-{{ /* delay */
- 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204,
- 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
- 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
- 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
-}},
-{{ /* panning delay */
- 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204,
- 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
- 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
- 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
-}},
-};
-
-static struct ReverbCmdPair {
- unsigned short cmd, port;
-} reverb_cmds[28] = {
- {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)},
- {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)},
- {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)},
- {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)},
- {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)},
- {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)},
- {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
-};
-
-static int
-awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count)
-{
- if (patch->optarg < AWE_REVERB_PREDEFINED || patch->optarg >= AWE_REVERB_NUMBERS) {
- printk("AWE32 Error: illegal reverb mode %d for uploading\n", patch->optarg);
- return RET_ERROR(EINVAL);
- }
- if (count < sizeof(awe_reverb_fx_rec)) {
- printk("AWE32 Error: too short reverb fx parameters\n");
- return RET_ERROR(EINVAL);
- }
- COPY_FROM_USER(&reverb_parm[patch->optarg], addr, AWE_PATCH_INFO_SIZE,
- sizeof(awe_reverb_fx_rec));
- reverb_defined[patch->optarg] = TRUE;
- return 0;
-}
-
-static void
-awe_set_reverb_mode(int effect)
-{
- int i;
- if (effect < 0 || effect >= AWE_REVERB_NUMBERS ||
- (effect >= AWE_REVERB_PREDEFINED && !reverb_defined[effect]))
- return;
- for (i = 0; i < 28; i++)
- awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port,
- reverb_parm[effect].parms[i]);
-}
-
-static void
-awe_update_reverb_mode(void)
-{
- awe_set_reverb_mode(ctrls[AWE_MD_REVERB_MODE]);
-}
-
-/*================================================================
- * treble/bass equalizer control
- *================================================================*/
-
-static unsigned short bass_parm[12][3] = {
- {0xD26A, 0xD36A, 0x0000}, /* -12 dB */
- {0xD25B, 0xD35B, 0x0000}, /* -8 */
- {0xD24C, 0xD34C, 0x0000}, /* -6 */
- {0xD23D, 0xD33D, 0x0000}, /* -4 */
- {0xD21F, 0xD31F, 0x0000}, /* -2 */
- {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */
- {0xC219, 0xC319, 0x0001}, /* +2 */
- {0xC22A, 0xC32A, 0x0001}, /* +4 */
- {0xC24C, 0xC34C, 0x0001}, /* +6 */
- {0xC26E, 0xC36E, 0x0001}, /* +8 */
- {0xC248, 0xC348, 0x0002}, /* +10 */
- {0xC26A, 0xC36A, 0x0002}, /* +12 dB */
-};
-
-static unsigned short treble_parm[12][9] = {
- {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */
- {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
- {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
- {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
- {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
- {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002},
- {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002},
- {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002},
- {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002},
- {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */
- {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002},
- {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +12 dB */
-};
-
-
-/*
- * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB]
- */
-static void
-awe_equalizer(int bass, int treble)
-{
- unsigned short w;
-
- if (bass < 0 || bass > 11 || treble < 0 || treble > 11)
- return;
- awe_poke(AWE_INIT4(0x01), bass_parm[bass][0]);
- awe_poke(AWE_INIT4(0x11), bass_parm[bass][1]);
- awe_poke(AWE_INIT3(0x11), treble_parm[treble][0]);
- awe_poke(AWE_INIT3(0x13), treble_parm[treble][1]);
- awe_poke(AWE_INIT3(0x1B), treble_parm[treble][2]);
- awe_poke(AWE_INIT4(0x07), treble_parm[treble][3]);
- awe_poke(AWE_INIT4(0x0B), treble_parm[treble][4]);
- awe_poke(AWE_INIT4(0x0D), treble_parm[treble][5]);
- awe_poke(AWE_INIT4(0x17), treble_parm[treble][6]);
- awe_poke(AWE_INIT4(0x19), treble_parm[treble][7]);
- w = bass_parm[bass][2] + treble_parm[treble][8];
- awe_poke(AWE_INIT4(0x15), (unsigned short)(w + 0x0262));
- awe_poke(AWE_INIT4(0x1D), (unsigned short)(w + 0x8362));
-}
-
-static void awe_update_equalizer(void)
-{
- awe_equalizer(ctrls[AWE_MD_BASS_LEVEL], ctrls[AWE_MD_TREBLE_LEVEL]);
-}
-
-
-#ifdef CONFIG_AWE32_MIDIEMU
-
-/*================================================================
- * Emu8000 MIDI Emulation
- *================================================================*/
-
-/*================================================================
- * midi queue record
- *================================================================*/
-
-/* queue type */
-enum { Q_NONE, Q_VARLEN, Q_READ, Q_SYSEX, };
-
-#define MAX_MIDIBUF 64
-
-/* midi status */
-typedef struct MidiStatus {
- int queue; /* queue type */
- int qlen; /* queue length */
- int read; /* chars read */
- int status; /* current status */
- int chan; /* current channel */
- unsigned char buf[MAX_MIDIBUF];
-} MidiStatus;
-
-/* MIDI mode type */
-enum { MODE_GM, MODE_GS, MODE_XG, };
-
-/* NRPN / CC -> Emu8000 parameter converter */
-typedef struct {
- int control;
- int awe_effect;
- unsigned short (*convert)(int val);
-} ConvTable;
-
-
-/*================================================================
- * prototypes
- *================================================================*/
-
-static int awe_midi_open(int dev, int mode, void (*input)(int,unsigned char), void (*output)(int));
-static void awe_midi_close(int dev);
-static int awe_midi_ioctl(int dev, unsigned cmd, caddr_t arg);
-static int awe_midi_outputc(int dev, unsigned char midi_byte);
-
-static void init_midi_status(MidiStatus *st);
-static void clear_rpn(void);
-static void get_midi_char(MidiStatus *st, int c);
-/*static void queue_varlen(MidiStatus *st, int c);*/
-static void special_event(MidiStatus *st, int c);
-static void queue_read(MidiStatus *st, int c);
-static void midi_note_on(MidiStatus *st);
-static void midi_note_off(MidiStatus *st);
-static void midi_key_pressure(MidiStatus *st);
-static void midi_channel_pressure(MidiStatus *st);
-static void midi_pitch_wheel(MidiStatus *st);
-static void midi_program_change(MidiStatus *st);
-static void midi_control_change(MidiStatus *st);
-static void midi_select_bank(MidiStatus *st, int val);
-static void midi_nrpn_event(MidiStatus *st);
-static void midi_rpn_event(MidiStatus *st);
-static void midi_detune(int chan, int coarse, int fine);
-static void midi_system_exclusive(MidiStatus *st);
-static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val);
-static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val);
-static int xg_control_change(MidiStatus *st, int cmd, int val);
-
-#define numberof(ary) (sizeof(ary)/sizeof(ary[0]))
-
-
-/*================================================================
- * OSS Midi device record
- *================================================================*/
-
-static struct midi_operations awe_midi_operations =
-{
- {"AWE Midi Emu", 0, 0, SNDCARD_SB},
- NULL /*&std_midi_synth*/,
- {0}, /* input_info */
- awe_midi_open, /*open*/
- awe_midi_close, /*close*/
- awe_midi_ioctl, /*ioctl*/
- awe_midi_outputc, /*outputc*/
- NULL /*start_read*/,
- NULL /*end_read*/,
- NULL, /* kick */
- NULL, /* command */
-};
-
-static int my_mididev = -1;
-
-static void attach_midiemu(void)
-{
- if ((my_mididev = sound_alloc_mididev()) < 0)
- printk ("Sound: Too many midi devices detected\n");
- else
- midi_devs[my_mididev] = &awe_midi_operations;
-}
-
-static void unload_midiemu(void)
-{
- if (my_mididev >= 0)
- sound_unload_mididev(my_mididev);
-}
-
-
-/*================================================================
- * open/close midi device
- *================================================================*/
-
-static int midi_opened = FALSE;
-
-static int midi_mode;
-static int coarsetune = 0, finetune = 0;
-
-static int xg_mapping = TRUE;
-static int xg_bankmode = 0;
-
-/* effect sensitivity */
-
-#define FX_CUTOFF 0
-#define FX_RESONANCE 1
-#define FX_ATTACK 2
-#define FX_RELEASE 3
-#define FX_VIBRATE 4
-#define FX_VIBDEPTH 5
-#define FX_VIBDELAY 6
-#define FX_NUMS 7
-
-#define DEF_FX_CUTOFF 170
-#define DEF_FX_RESONANCE 6
-#define DEF_FX_ATTACK 50
-#define DEF_FX_RELEASE 50
-#define DEF_FX_VIBRATE 30
-#define DEF_FX_VIBDEPTH 4
-#define DEF_FX_VIBDELAY 1500
-
-/* effect sense: */
-static int gs_sense[] =
-{
- DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE,
- DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY
-};
-static int xg_sense[] =
-{
- DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE,
- DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY
-};
-
-
-/* current status */
-static MidiStatus curst;
-
-
-static int
-awe_midi_open (int dev, int mode,
- void (*input)(int,unsigned char),
- void (*output)(int))
-{
- if (midi_opened)
- return -EBUSY;
-
- midi_opened = TRUE;
-
- midi_mode = MODE_GM;
-
- curst.queue = Q_NONE;
- curst.qlen = 0;
- curst.read = 0;
- curst.status = 0;
- curst.chan = 0;
- BZERO(curst.buf, sizeof(curst.buf));
-
- init_midi_status(&curst);
-
- return 0;
-}
-
-static void
-awe_midi_close (int dev)
-{
- midi_opened = FALSE;
-}
-
-
-static int
-awe_midi_ioctl (int dev, unsigned cmd, caddr_t arg)
-{
- return -EPERM;
-}
-
-static int
-awe_midi_outputc (int dev, unsigned char midi_byte)
-{
- if (! midi_opened)
- return 1;
-
- /* force to change playing mode */
- playing_mode = AWE_PLAY_MULTI;
-
- get_midi_char(&curst, midi_byte);
- return 1;
-}
-
-
-/*================================================================
- * initialize
- *================================================================*/
-
-static void init_midi_status(MidiStatus *st)
-{
- clear_rpn();
- coarsetune = 0;
- finetune = 0;
-}
-
-
-/*================================================================
- * RPN & NRPN
- *================================================================*/
-
-#define MAX_MIDI_CHANNELS 16
-
-/* RPN & NRPN */
-static unsigned char nrpn[MAX_MIDI_CHANNELS]; /* current event is NRPN? */
-static int msb_bit; /* current event is msb for RPN/NRPN */
-/* RPN & NRPN indeces */
-static unsigned char rpn_msb[MAX_MIDI_CHANNELS], rpn_lsb[MAX_MIDI_CHANNELS];
-/* RPN & NRPN values */
-static int rpn_val[MAX_MIDI_CHANNELS];
-
-static void clear_rpn(void)
-{
- int i;
- for (i = 0; i < MAX_MIDI_CHANNELS; i++) {
- nrpn[i] = 0;
- rpn_msb[i] = 127;
- rpn_lsb[i] = 127;
- rpn_val[i] = 0;
- }
- msb_bit = 0;
-}
-
-
-/*================================================================
- * process midi queue
- *================================================================*/
-
-/* status event types */
-typedef void (*StatusEvent)(MidiStatus *st);
-static struct StatusEventList {
- StatusEvent process;
- int qlen;
-} status_event[8] = {
- {midi_note_off, 2},
- {midi_note_on, 2},
- {midi_key_pressure, 2},
- {midi_control_change, 2},
- {midi_program_change, 1},
- {midi_channel_pressure, 1},
- {midi_pitch_wheel, 2},
- {NULL, 0},
-};
-
-
-/* read a char from fifo and process it */
-static void get_midi_char(MidiStatus *st, int c)
-{
- if (c == 0xfe) {
- /* ignore active sense */
- st->queue = Q_NONE;
- return;
- }
-
- switch (st->queue) {
- /* case Q_VARLEN: queue_varlen(st, c); break;*/
- case Q_READ:
- case Q_SYSEX:
- queue_read(st, c);
- break;
- case Q_NONE:
- st->read = 0;
- if ((c & 0xf0) == 0xf0) {
- special_event(st, c);
- } else if (c & 0x80) { /* status change */
- st->status = (c >> 4) & 0x07;
- st->chan = c & 0x0f;
- st->queue = Q_READ;
- st->qlen = status_event[st->status].qlen;
- if (st->qlen == 0)
- st->queue = Q_NONE;
- }
- break;
- }
-}
-
-/* 0xfx events */
-static void special_event(MidiStatus *st, int c)
-{
- switch (c) {
- case 0xf0: /* system exclusive */
- st->queue = Q_SYSEX;
- st->qlen = 0;
- break;
- case 0xf1: /* MTC quarter frame */
- case 0xf3: /* song select */
- st->queue = Q_READ;
- st->qlen = 1;
- break;
- case 0xf2: /* song position */
- st->queue = Q_READ;
- st->qlen = 2;
- break;
- }
-}
-
-#if 0
-/* read variable length value */
-static void queue_varlen(MidiStatus *st, int c)
-{
- st->qlen += (c & 0x7f);
- if (c & 0x80) {
- st->qlen <<= 7;
- return;
- }
- if (st->qlen <= 0) {
- st->qlen = 0;
- st->queue = Q_NONE;
- }
- st->queue = Q_READ;
- st->read = 0;
-}
-#endif
-
-
-/* read a char */
-static void queue_read(MidiStatus *st, int c)
-{
- if (st->read < MAX_MIDIBUF) {
- if (st->queue != Q_SYSEX)
- c &= 0x7f;
- st->buf[st->read] = (unsigned char)c;
- }
- st->read++;
- if (st->queue == Q_SYSEX && c == 0xf7) {
- midi_system_exclusive(st);
- st->queue = Q_NONE;
- } else if (st->queue == Q_READ && st->read >= st->qlen) {
- if (status_event[st->status].process)
- status_event[st->status].process(st);
- st->queue = Q_NONE;
- }
-}
-
-
-/*================================================================
- * status events
- *================================================================*/
-
-/* note on */
-static void midi_note_on(MidiStatus *st)
-{
- DEBUG(2,printk("midi: note_on (%d) %d %d\n", st->chan, st->buf[0], st->buf[1]));
- if (st->buf[1] == 0)
- midi_note_off(st);
- else
- awe_start_note(0, st->chan, st->buf[0], st->buf[1]);
-}
-
-/* note off */
-static void midi_note_off(MidiStatus *st)
-{
- DEBUG(2,printk("midi: note_off (%d) %d %d\n", st->chan, st->buf[0], st->buf[1]));
- awe_kill_note(0, st->chan, st->buf[0], st->buf[1]);
-}
-
-/* key pressure change */
-static void midi_key_pressure(MidiStatus *st)
-{
- awe_key_pressure(0, st->chan, st->buf[0], st->buf[1]);
-}
-
-/* channel pressure change */
-static void midi_channel_pressure(MidiStatus *st)
-{
- channels[st->chan].chan_press = st->buf[0];
- awe_modwheel_change(st->chan, st->buf[0]);
-}
-
-/* pitch wheel change */
-static void midi_pitch_wheel(MidiStatus *st)
-{
- int val = (int)st->buf[1] * 128 + st->buf[0];
- awe_bender(0, st->chan, val);
-}
-
-/* program change */
-static void midi_program_change(MidiStatus *st)
-{
- int preset;
- preset = st->buf[0];
- if (midi_mode == MODE_GS && IS_DRUM_CHANNEL(st->chan) && preset == 127)
- preset = 0;
- else if (midi_mode == MODE_XG && xg_mapping && IS_DRUM_CHANNEL(st->chan))
- preset += 64;
-
- awe_set_instr(0, st->chan, preset);
-}
-
-#define send_effect(chan,type,val) awe_send_effect(chan,-1,type,val)
-#define add_effect(chan,type,val) awe_send_effect(chan,-1,(type)|0x80,val)
-#define unset_effect(chan,type) awe_send_effect(chan,-1,(type)|0x40,0)
-
-/* midi control change */
-static void midi_control_change(MidiStatus *st)
-{
- int cmd = st->buf[0];
- int val = st->buf[1];
-
- DEBUG(2,printk("midi: control (%d) %d %d\n", st->chan, cmd, val));
- if (midi_mode == MODE_XG) {
- if (xg_control_change(st, cmd, val))
- return;
- }
-
- /* controls #31 - #64 are LSB of #0 - #31 */
- msb_bit = 1;
- if (cmd >= 0x20 && cmd < 0x40) {
- msb_bit = 0;
- cmd -= 0x20;
- }
-
- switch (cmd) {
- case CTL_SOFT_PEDAL:
- if (val == 127)
- add_effect(st->chan, AWE_FX_CUTOFF, -160);
- else
- unset_effect(st->chan, AWE_FX_CUTOFF);
- break;
-
- case CTL_BANK_SELECT:
- midi_select_bank(st, val);
- break;
-
- /* set RPN/NRPN parameter */
- case CTL_REGIST_PARM_NUM_MSB:
- nrpn[st->chan]=0; rpn_msb[st->chan]=val;
- break;
- case CTL_REGIST_PARM_NUM_LSB:
- nrpn[st->chan]=0; rpn_lsb[st->chan]=val;
- break;
- case CTL_NONREG_PARM_NUM_MSB:
- nrpn[st->chan]=1; rpn_msb[st->chan]=val;
- break;
- case CTL_NONREG_PARM_NUM_LSB:
- nrpn[st->chan]=1; rpn_lsb[st->chan]=val;
- break;
-
- /* send RPN/NRPN entry */
- case CTL_DATA_ENTRY:
- if (msb_bit)
- rpn_val[st->chan] = val * 128;
- else
- rpn_val[st->chan] |= val;
- if (nrpn[st->chan])
- midi_nrpn_event(st);
- else
- midi_rpn_event(st);
- break;
-
- /* increase/decrease data entry */
- case CTL_DATA_INCREMENT:
- rpn_val[st->chan]++;
- midi_rpn_event(st);
- break;
- case CTL_DATA_DECREMENT:
- rpn_val[st->chan]--;
- midi_rpn_event(st);
- break;
-
- /* default */
- default:
- awe_controller(0, st->chan, cmd, val);
- break;
- }
-}
-
-/* tone bank change */
-static void midi_select_bank(MidiStatus *st, int val)
-{
- if (midi_mode == MODE_XG && msb_bit) {
- xg_bankmode = val;
- /* XG MSB value; not normal bank selection */
- switch (val) {
- case 127: /* remap to drum channel */
- awe_controller(0, st->chan, CTL_BANK_SELECT, 128);
- break;
- default: /* remap to normal channel */
- awe_controller(0, st->chan, CTL_BANK_SELECT, val);
- break;
- }
- return;
- } else if (midi_mode == MODE_GS && !msb_bit)
- /* ignore LSB bank in GS mode (used for mapping) */
- return;
-
- /* normal bank controls; accept both MSB and LSB */
- if (! IS_DRUM_CHANNEL(st->chan)) {
- if (midi_mode == MODE_XG) {
- if (xg_bankmode) return;
- if (val == 64 || val == 126)
- val = 0;
- } else if (midi_mode == MODE_GS && val == 127)
- val = 0;
- awe_controller(0, st->chan, CTL_BANK_SELECT, val);
- }
-}
-
-
-/*================================================================
- * RPN events
- *================================================================*/
-
-static void midi_rpn_event(MidiStatus *st)
-{
- int type;
- type = (rpn_msb[st->chan]<<8) | rpn_lsb[st->chan];
- switch (type) {
- case 0x0000: /* Pitch bend sensitivity */
- /* MSB only / 1 semitone per 128 */
- if (msb_bit) {
- channels[st->chan].bender_range =
- rpn_val[st->chan] * 100 / 128;
- }
- break;
-
- case 0x0001: /* fine tuning: */
- /* MSB/LSB, 8192=center, 100/8192 cent step */
- finetune = rpn_val[st->chan] - 8192;
- midi_detune(st->chan, coarsetune, finetune);
- break;
-
- case 0x0002: /* coarse tuning */
- /* MSB only / 8192=center, 1 semitone per 128 */
- if (msb_bit) {
- coarsetune = rpn_val[st->chan] - 8192;
- midi_detune(st->chan, coarsetune, finetune);
- }
- break;
-
- case 0x7F7F: /* "lock-in" RPN */
- break;
- }
-}
-
-
-/* tuning:
- * coarse = -8192 to 8192 (100 cent per 128)
- * fine = -8192 to 8192 (max=100cent)
- */
-static void midi_detune(int chan, int coarse, int fine)
-{
- /* 4096 = 1200 cents in AWE parameter */
- int val;
- val = coarse * 4096 / (12 * 128);
- val += fine / 24;
- if (val)
- send_effect(chan, AWE_FX_INIT_PITCH, val);
- else
- unset_effect(chan, AWE_FX_INIT_PITCH);
-}
-
-
-/*================================================================
- * system exclusive message
- * GM/GS/XG macros are accepted
- *================================================================*/
-
-static void midi_system_exclusive(MidiStatus *st)
-{
- /* GM on */
- static unsigned char gm_on_macro[] = {
- 0x7e,0x7f,0x09,0x01,
- };
- /* XG on */
- static unsigned char xg_on_macro[] = {
- 0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,
- };
- /* GS prefix
- * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off
- * reverb mode: XX=0x01, YY=0x30, ZZ=0-7
- * chorus mode: XX=0x01, YY=0x38, ZZ=0-7
- */
- static unsigned char gs_pfx_macro[] = {
- 0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/
- };
-
-#if 0
- /* SC88 system mode set
- * single module mode: XX=1
- * double module mode: XX=0
- */
- static unsigned char gs_mode_macro[] = {
- 0x41,0x10,0x42,0x12,0x00,0x00,0x7F,/*ZZ*/
- };
- /* SC88 display macro: XX=01:bitmap, 00:text
- */
- static unsigned char gs_disp_macro[] = {
- 0x41,0x10,0x45,0x12,0x10,/*XX,00*/
- };
-#endif
-
- /* GM on */
- if (MEMCMP(st->buf, gm_on_macro, sizeof(gm_on_macro)) == 0) {
- if (midi_mode != MODE_GS && midi_mode != MODE_XG)
- midi_mode = MODE_GM;
- init_midi_status(st);
- }
-
- /* GS macros */
- else if (MEMCMP(st->buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) {
- if (midi_mode != MODE_GS && midi_mode != MODE_XG)
- midi_mode = MODE_GS;
-
- if (st->buf[5] == 0x00 && st->buf[6] == 0x7f && st->buf[7] == 0x00) {
- /* GS reset */
- init_midi_status(st);
- }
-
- else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x15) {
- /* drum pattern */
- int p = st->buf[5] & 0x0f;
- if (p == 0) p = 9;
- else if (p < 10) p--;
- if (st->buf[7] == 0)
- DRUM_CHANNEL_OFF(p);
- else
- DRUM_CHANNEL_ON(p);
-
- } else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x21) {
- /* program */
- int p = st->buf[5] & 0x0f;
- if (p == 0) p = 9;
- else if (p < 10) p--;
- if (! IS_DRUM_CHANNEL(p))
- awe_set_instr(0, p, st->buf[7]);
-
- } else if (st->buf[5] == 0x01 && st->buf[6] == 0x30) {
- /* reverb mode */
- awe_set_reverb_mode(st->buf[7]);
-
- } else if (st->buf[5] == 0x01 && st->buf[6] == 0x38) {
- /* chorus mode */
- awe_set_chorus_mode(st->buf[7]);
-
- } else if (st->buf[5] == 0x00 && st->buf[6] == 0x04) {
- /* master volume */
- awe_change_master_volume(st->buf[7]);
-
- }
- }
-
- /* XG on */
- else if (MEMCMP(st->buf, xg_on_macro, sizeof(xg_on_macro)) == 0) {
- midi_mode = MODE_XG;
- xg_mapping = TRUE;
- xg_bankmode = 0;
- }
-}
-
-
-/*================================================================
- * convert NRPN/control values
- *================================================================*/
-
-static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val)
-{
- int i, cval;
- for (i = 0; i < num_tables; i++) {
- if (table[i].control == type) {
- cval = table[i].convert(val);
- send_effect(st->chan, table[i].awe_effect, cval);
- return TRUE;
- }
- }
- return FALSE;
-}
-
-static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val)
-{
- int i, cval;
- for (i = 0; i < num_tables; i++) {
- if (table[i].control == type) {
- cval = table[i].convert(val);
- add_effect(st->chan, table[i].awe_effect|0x80, cval);
- return TRUE;
- }
- }
- return FALSE;
-}
-
-
-/*----------------------------------------------------------------
- * AWE32 NRPN effects
- *----------------------------------------------------------------*/
-
-static unsigned short fx_delay(int val);
-static unsigned short fx_attack(int val);
-static unsigned short fx_hold(int val);
-static unsigned short fx_decay(int val);
-static unsigned short fx_the_value(int val);
-static unsigned short fx_twice_value(int val);
-static unsigned short fx_conv_pitch(int val);
-static unsigned short fx_conv_Q(int val);
-
-/* function for each NRPN */ /* [range] units */
-#define fx_env1_delay fx_delay /* [0,5900] 4msec */
-#define fx_env1_attack fx_attack /* [0,5940] 1msec */
-#define fx_env1_hold fx_hold /* [0,8191] 1msec */
-#define fx_env1_decay fx_decay /* [0,5940] 4msec */
-#define fx_env1_release fx_decay /* [0,5940] 4msec */
-#define fx_env1_sustain fx_the_value /* [0,127] 0.75dB */
-#define fx_env1_pitch fx_the_value /* [-127,127] 9.375cents */
-#define fx_env1_cutoff fx_the_value /* [-127,127] 56.25cents */
-
-#define fx_env2_delay fx_delay /* [0,5900] 4msec */
-#define fx_env2_attack fx_attack /* [0,5940] 1msec */
-#define fx_env2_hold fx_hold /* [0,8191] 1msec */
-#define fx_env2_decay fx_decay /* [0,5940] 4msec */
-#define fx_env2_release fx_decay /* [0,5940] 4msec */
-#define fx_env2_sustain fx_the_value /* [0,127] 0.75dB */
-
-#define fx_lfo1_delay fx_delay /* [0,5900] 4msec */
-#define fx_lfo1_freq fx_twice_value /* [0,127] 84mHz */
-#define fx_lfo1_volume fx_twice_value /* [0,127] 0.1875dB */
-#define fx_lfo1_pitch fx_the_value /* [-127,127] 9.375cents */
-#define fx_lfo1_cutoff fx_twice_value /* [-64,63] 56.25cents */
-
-#define fx_lfo2_delay fx_delay /* [0,5900] 4msec */
-#define fx_lfo2_freq fx_twice_value /* [0,127] 84mHz */
-#define fx_lfo2_pitch fx_the_value /* [-127,127] 9.375cents */
-
-#define fx_init_pitch fx_conv_pitch /* [-8192,8192] cents */
-#define fx_chorus fx_the_value /* [0,255] -- */
-#define fx_reverb fx_the_value /* [0,255] -- */
-#define fx_cutoff fx_twice_value /* [0,127] 62Hz */
-#define fx_filterQ fx_conv_Q /* [0,127] -- */
-
-static unsigned short fx_delay(int val)
-{
- return (unsigned short)calc_parm_delay(val);
-}
-
-static unsigned short fx_attack(int val)
-{
- return (unsigned short)calc_parm_attack(val);
-}
-
-static unsigned short fx_hold(int val)
-{
- return (unsigned short)calc_parm_hold(val);
-}
-
-static unsigned short fx_decay(int val)
-{
- return (unsigned short)calc_parm_decay(val);
-}
-
-static unsigned short fx_the_value(int val)
-{
- return (unsigned short)(val & 0xff);
-}
-
-static unsigned short fx_twice_value(int val)
-{
- return (unsigned short)((val * 2) & 0xff);
-}
-
-static unsigned short fx_conv_pitch(int val)
-{
- return (short)(val * 4096 / 1200);
-}
-
-static unsigned short fx_conv_Q(int val)
-{
- return (unsigned short)((val / 8) & 0xff);
-}
-
-
-static ConvTable awe_effects[] =
-{
- { 0, AWE_FX_LFO1_DELAY, fx_lfo1_delay},
- { 1, AWE_FX_LFO1_FREQ, fx_lfo1_freq},
- { 2, AWE_FX_LFO2_DELAY, fx_lfo2_delay},
- { 3, AWE_FX_LFO2_FREQ, fx_lfo2_freq},
-
- { 4, AWE_FX_ENV1_DELAY, fx_env1_delay},
- { 5, AWE_FX_ENV1_ATTACK,fx_env1_attack},
- { 6, AWE_FX_ENV1_HOLD, fx_env1_hold},
- { 7, AWE_FX_ENV1_DECAY, fx_env1_decay},
- { 8, AWE_FX_ENV1_SUSTAIN, fx_env1_sustain},
- { 9, AWE_FX_ENV1_RELEASE, fx_env1_release},
-
- {10, AWE_FX_ENV2_DELAY, fx_env2_delay},
- {11, AWE_FX_ENV2_ATTACK, fx_env2_attack},
- {12, AWE_FX_ENV2_HOLD, fx_env2_hold},
- {13, AWE_FX_ENV2_DECAY, fx_env2_decay},
- {14, AWE_FX_ENV2_SUSTAIN, fx_env2_sustain},
- {15, AWE_FX_ENV2_RELEASE, fx_env2_release},
-
- {16, AWE_FX_INIT_PITCH, fx_init_pitch},
- {17, AWE_FX_LFO1_PITCH, fx_lfo1_pitch},
- {18, AWE_FX_LFO2_PITCH, fx_lfo2_pitch},
- {19, AWE_FX_ENV1_PITCH, fx_env1_pitch},
- {20, AWE_FX_LFO1_VOLUME, fx_lfo1_volume},
- {21, AWE_FX_CUTOFF, fx_cutoff},
- {22, AWE_FX_FILTERQ, fx_filterQ},
- {23, AWE_FX_LFO1_CUTOFF, fx_lfo1_cutoff},
- {24, AWE_FX_ENV1_CUTOFF, fx_env1_cutoff},
- {25, AWE_FX_CHORUS, fx_chorus},
- {26, AWE_FX_REVERB, fx_reverb},
-};
-
-static int num_awe_effects = numberof(awe_effects);
-
-
-/*----------------------------------------------------------------
- * GS(SC88) NRPN effects; still experimental
- *----------------------------------------------------------------*/
-
-/* cutoff: quarter semitone step, max=255 */
-static unsigned short gs_cutoff(int val)
-{
- return (val - 64) * gs_sense[FX_CUTOFF] / 50;
-}
-
-/* resonance: 0 to 15(max) */
-static unsigned short gs_filterQ(int val)
-{
- return (val - 64) * gs_sense[FX_RESONANCE] / 50;
-}
-
-/* attack: */
-static unsigned short gs_attack(int val)
-{
- return -(val - 64) * gs_sense[FX_ATTACK] / 50;
-}
-
-/* decay: */
-static unsigned short gs_decay(int val)
-{
- return -(val - 64) * gs_sense[FX_RELEASE] / 50;
-}
-
-/* release: */
-static unsigned short gs_release(int val)
-{
- return -(val - 64) * gs_sense[FX_RELEASE] / 50;
-}
-
-/* vibrato freq: 0.042Hz step, max=255 */
-static unsigned short gs_vib_rate(int val)
-{
- return (val - 64) * gs_sense[FX_VIBRATE] / 50;
-}
-
-/* vibrato depth: max=127, 1 octave */
-static unsigned short gs_vib_depth(int val)
-{
- return (val - 64) * gs_sense[FX_VIBDEPTH] / 50;
-}
-
-/* vibrato delay: -0.725msec step */
-static unsigned short gs_vib_delay(int val)
-{
- return -(val - 64) * gs_sense[FX_VIBDELAY] / 50;
-}
-
-static ConvTable gs_effects[] =
-{
- {32, AWE_FX_CUTOFF, gs_cutoff},
- {33, AWE_FX_FILTERQ, gs_filterQ},
- {99, AWE_FX_ENV2_ATTACK, gs_attack},
- {100, AWE_FX_ENV2_DECAY, gs_decay},
- {102, AWE_FX_ENV2_RELEASE, gs_release},
- {8, AWE_FX_LFO1_FREQ, gs_vib_rate},
- {9, AWE_FX_LFO1_VOLUME, gs_vib_depth},
- {10, AWE_FX_LFO1_DELAY, gs_vib_delay},
-};
-
-static int num_gs_effects = numberof(gs_effects);
-
-
-/*================================================================
- * NRPN events: accept as AWE32/SC88 specific controls
- *================================================================*/
-
-static void midi_nrpn_event(MidiStatus *st)
-{
- if (rpn_msb[st->chan] == 127 && rpn_lsb[st->chan] <= 26) {
- if (! msb_bit) /* both MSB/LSB necessary */
- send_converted_effect(awe_effects, num_awe_effects,
- st, rpn_lsb[st->chan],
- rpn_val[st->chan] - 8192);
- } else if (rpn_msb[st->chan] == 1) {
- if (msb_bit) /* only MSB is valid */
- add_converted_effect(gs_effects, num_gs_effects,
- st, rpn_lsb[st->chan],
- rpn_val[st->chan] / 128);
- }
-}
-
-
-/*----------------------------------------------------------------
- * XG control effects; still experimental
- *----------------------------------------------------------------*/
-
-/* cutoff: quarter semitone step, max=255 */
-static unsigned short xg_cutoff(int val)
-{
- return (val - 64) * xg_sense[FX_CUTOFF] / 64;
-}
-
-/* resonance: 0(open) to 15(most nasal) */
-static unsigned short xg_filterQ(int val)
-{
- return (val - 64) * xg_sense[FX_RESONANCE] / 64;
-}
-
-/* attack: */
-static unsigned short xg_attack(int val)
-{
- return -(val - 64) * xg_sense[FX_ATTACK] / 64;
-}
-
-/* release: */
-static unsigned short xg_release(int val)
-{
- return -(val - 64) * xg_sense[FX_RELEASE] / 64;
-}
-
-static ConvTable xg_effects[] =
-{
- {71, AWE_FX_CUTOFF, xg_cutoff},
- {74, AWE_FX_FILTERQ, xg_filterQ},
- {72, AWE_FX_ENV2_RELEASE, xg_release},
- {73, AWE_FX_ENV2_ATTACK, xg_attack},
-};
-
-static int num_xg_effects = numberof(xg_effects);
-
-static int xg_control_change(MidiStatus *st, int cmd, int val)
-{
- return add_converted_effect(xg_effects, num_xg_effects, st, cmd, val);
-}
-
-#endif /* CONFIG_AWE32_MIDIEMU */
-
-#endif /* CONFIG_AWE32_SYNTH */
+++ /dev/null
-#ifdef LOWLEVEL_MODULE
-#define MODVERSIONS
-#include <linux/modversions.h>
-#include "manual_config.h"
-#endif
+++ /dev/null
-#include <linux/config.h>
-#if defined(CONFIG_ACI_MIXER) || defined(CONFIG_ACI_MIXER_MODULE)
-extern int aci_implied_cmd(unsigned char opcode);
-extern int aci_write_cmd(unsigned char opcode, unsigned char parameter);
-extern int aci_write_cmd_d(unsigned char opcode, unsigned char parameter, unsigned char parameter2);
-extern int aci_read_cmd(unsigned char opcode, int length, unsigned char *parameter);
-extern int aci_indexed_cmd(unsigned char opcode, unsigned char index, unsigned char *parameter);
-#else
-
-
-#error Compiling a driver that needs the ACI-mixer but ACI-mixer support is not configured
-
-#endif
+++ /dev/null
-/*
- * lowlevel/init.c - Calls initialization code for configured drivers.
- */
-
-#include "lowlevel.h"
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include "../soundvers.h"
-
-#ifdef LOWLEVEL_MODULE
-char *lowlevel_version = SOUND_VERSION_STRING;
-#endif
-
-extern int attach_aci(void);
-extern void unload_aci(void);
-extern int attach_awe(void);
-extern void unload_awe(void);
-extern int init_aedsp16(void) __init;
-extern void uninit_aedsp16(void) __init;
-
-/*
- * There are two places where you can insert initialization calls of
- * low level drivers. sound_init_lowlevel_drivers() is called after
- * the sound driver has been initialized (the normal case)
- * while sound_preinit_lowlevel_drivers() is called before that.
- */
-void
-sound_preinit_lowlevel_drivers(void)
-{
-#if defined(CONFIG_AEDSP16) && !defined(MODULE)
- init_aedsp16();
-#endif
-}
-
-void
-sound_init_lowlevel_drivers(void)
-{
-#ifdef CONFIG_ACI_MIXER
- attach_aci();
-#endif
-
-#ifdef CONFIG_AWE32_SYNTH
- attach_awe();
-#endif
-}
-
-void
-sound_unload_lowlevel_drivers(void)
-{
-#ifdef CONFIG_ACI_MIXER
- unload_aci();
-#endif
-
-#ifdef CONFIG_AWE32_SYNTH
- unload_awe();
-#endif
-
-#ifdef CONFIG_AEDSP16
- uninit_aedsp16();
-#endif
-
-}
-
-EXPORT_SYMBOL(sound_init_lowlevel_drivers);
-EXPORT_SYMBOL(sound_unload_lowlevel_drivers);
-EXPORT_SYMBOL(sound_preinit_lowlevel_drivers);
if((sb_dev = isapnp_find_dev(bus,
ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL)))
{
-#ifdef CMI8330_DMA0BAD
- int dmahack = 0;
-#endif
sb_dev->prepare(sb_dev);
- /* This device doesn't work with DMA 0, so we must allocate
- * it to prevent PnP routines to assign it to the card.
- *
- * I know i could have inlined the following lines, but it's cleaner
- * this way.
- */
-
-#ifdef CMI8330_DMA0BAD
- if(sb_dev->dma_resource[0].start == 0)
- {
- if(!request_dma(0, "cmi8330 dma hack"))
- {
- /* DMA was free, we now have it */
- dmahack = 1;
- }
- }
-#endif
-
if((sb_dev = activate_dev("CMI8330", "sb", sb_dev)))
{
hw_config->io_base = sb_dev->resource[0].start;
show_base("CMI8330", "sb", &sb_dev->resource[0]);
}
-#ifdef CMI8330_DMA0BAD
- if(dmahack) free_dma(0);
-#endif
if(!sb_dev) return(NULL);
}
else
*
*/
-/* FIXME: *grr* why can't the f**in Makefile do this for me ? */
-#define EXPORT_SYMTAB
-
#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
MOD_DEC_USE_COUNT;
}
-static int usbdevfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int usbdevfs_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
-
- tmp.f_type = USBDEVICE_SUPER_MAGIC;
- tmp.f_bsize = PAGE_SIZE/sizeof(long); /* ??? */
- tmp.f_blocks = 0;
- tmp.f_bfree = 0;
- tmp.f_bavail = 0;
- tmp.f_files = 0;
- tmp.f_ffree = 0;
- tmp.f_namelen = NAME_MAX;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = USBDEVICE_SUPER_MAGIC;
+ buf->f_bsize = PAGE_SIZE/sizeof(long); /* ??? */
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = NAME_MAX;
+ return 0;
}
static struct super_operations usbdevfs_sops = {
define_bool CONFIG_FB_Q40 y
fi
if [ "$CONFIG_AMIGA" = "y" ]; then
- bool ' Amiga native chipset support' CONFIG_FB_AMIGA
+ tristate ' Amiga native chipset support' CONFIG_FB_AMIGA
if [ "$CONFIG_FB_AMIGA" != "n" ]; then
bool ' Amiga OCS chipset support' CONFIG_FB_AMIGA_OCS
bool ' Amiga ECS chipset support' CONFIG_FB_AMIGA_ECS
# All of the (potential) objects that export symbols.
# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
-export-objs := fbmem.o fbcmap.o fbcon.o fbcon-afb.o fbcon-ilbm.o \
+export-objs := fbmem.o fbcmap.o fbcon.o fbmon.o fbcon-afb.o fbcon-ilbm.o \
fbcon-vga.o fbcon-iplan2p2.o fbcon-iplan2p4.o \
fbcon-iplan2p8.o fbcon-vga-planes.o fbcon-cfb16.o \
fbcon-cfb2.o fbcon-cfb24.o fbcon-cfb32.o fbcon-cfb4.o \
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/console.h>
+#include <linux/ioport.h>
#include <asm/uaccess.h>
#include <asm/system.h>
*/
int amifb_init(void);
+static void amifb_deinit(void);
static int amifbcon_switch(int con, struct fb_info *info);
static int amifbcon_updatevar(int con, struct fb_info *info);
static void amifbcon_blank(int blank, struct fb_info *info);
static int flash_cursor(void);
static void amifb_interrupt(int irq, void *dev_id, struct pt_regs *fp);
static u_long chipalloc(u_long size);
+static void chipfree(void);
static char *strtoke(char *s,const char *ct);
/*
static void ami_rebuild_copper(void);
- /*
- * External references
- */
-
-extern unsigned short ami_intena_vals[];
-
-
static struct fb_ops amifb_ops = {
amifb_open, amifb_release, amifb_get_fix, amifb_get_var,
amifb_set_var, amifb_get_cmap, amifb_set_cmap,
}
+ /*
+ * Allocate, Clear and Align a Block of Chip Memory
+ */
+
+static u_long unaligned_chipptr = 0;
+
+static inline u_long __init chipalloc(u_long size)
+{
+ size += PAGE_SIZE-1;
+ if (!(unaligned_chipptr = (u_long)amiga_chip_alloc(size,
+ "amifb [RAM]")))
+ panic("No Chip RAM for frame buffer");
+ memset((void *)unaligned_chipptr, 0, size);
+ return PAGE_ALIGN(unaligned_chipptr);
+}
+
+static inline void chipfree(void)
+{
+ if (unaligned_chipptr)
+ amiga_chip_free((void *)unaligned_chipptr);
+}
+
+
/*
* Initialisation
*/
int __init amifb_init(void)
{
- int tag, i;
+ int tag, i, err = 0;
u_long chipptr;
u_int defmode;
struct fb_var_screeninfo var;
}
#endif
+ /*
+ * We request all registers starting from bplpt[0]
+ */
+ if (!request_mem_region(CUSTOM_PHYSADDR+0xe0, 0x120,
+ "amifb [Denise/Lisa]"))
+ return -EBUSY;
+
custom.dmacon = DMAF_ALL | DMAF_MASTER;
switch (amiga_chipset) {
strcat(amifb_name, "Unknown");
goto default_chipset;
#else /* CONFIG_FB_AMIGA_OCS */
- return -ENXIO;
+ err = -ENXIO;
+ goto amifb_error;
#endif /* CONFIG_FB_AMIGA_OCS */
break;
}
fb_info.flags = FBINFO_FLAG_DEFAULT;
memset(&var, 0, sizeof(var));
+#ifdef MODULE
+ var.xres = ami_modedb[defmode].xres;
+ var.yres = ami_modedb[defmode].yres;
+ var.xres_virtual = ami_modedb[defmode].xres;
+ var.yres_virtual = ami_modedb[defmode].yres;
+ var.xoffset = 0;
+ var.yoffset = 0;
+ var.bits_per_pixel = 4;
+ var.activate |= FB_ACTIVATE_TEST;
+ var.pixclock = ami_modedb[defmode].pixclock;
+ var.left_margin = ami_modedb[defmode].left_margin;
+ var.right_margin = ami_modedb[defmode].right_margin;
+ var.upper_margin = ami_modedb[defmode].upper_margin;
+ var.lower_margin = ami_modedb[defmode].lower_margin;
+ var.hsync_len = ami_modedb[defmode].hsync_len;
+ var.vsync_len = ami_modedb[defmode].vsync_len;
+ var.sync = ami_modedb[defmode].sync;
+ var.vmode = ami_modedb[defmode].vmode;
+ err = fb_info.fbops->fb_set_var(&var, -1, &fb_info);
+ var.activate &= ~FB_ACTIVATE_TEST;
+ if (err) {
+ err = -EINVAL;
+ goto amifb_error;
+ }
+#else
if (!fb_find_mode(&var, &fb_info, mode_option, ami_modedb,
- NUM_TOTAL_MODES, &ami_modedb[defmode], 4))
- panic("Can't find any usable video mode");
+ NUM_TOTAL_MODES, &ami_modedb[defmode], 4)) {
+ err = -EINVAL;
+ goto amifb_error;
+ }
+#endif
round_down_bpp = 0;
chipptr = chipalloc(videomemorysize+
* access the videomem with writethrough cache
*/
videomemory_phys = (u_long)ZTWO_PADDR(videomemory);
-#if 1
videomemory = (u_long)ioremap_writethrough(videomemory_phys, videomemorysize);
-#endif
if (!videomemory) {
printk("amifb: WARNING! unable to map videomem cached writethrough\n");
videomemory = ZTWO_VADDR(videomemory_phys);
ami_init_copper();
if (request_irq(IRQ_AMIGA_AUTO_3, amifb_interrupt, 0,
- "fb vertb handler", NULL))
- panic("Couldn't add vblank interrupt\n");
- ami_intena_vals[IRQ_AMIGA_VERTB] = IF_COPER;
- ami_intena_vals[IRQ_AMIGA_COPPER] = 0;
+ "fb vertb handler", NULL)) {
+ err = -EBUSY;
+ goto amifb_error;
+ }
+ amiga_intena_vals[IRQ_AMIGA_VERTB] = IF_COPER;
+ amiga_intena_vals[IRQ_AMIGA_COPPER] = 0;
custom.intena = IF_VERTB;
custom.intena = IF_SETCLR | IF_COPER;
amifb_set_var(&var, -1, &fb_info);
- if (register_framebuffer(&fb_info) < 0)
- return -EINVAL;
+ if (register_framebuffer(&fb_info) < 0) {
+ err = -EINVAL;
+ goto amifb_error;
+ }
printk("fb%d: %s frame buffer device, using %ldK of video memory\n",
GET_FB_IDX(fb_info.node), fb_info.modename,
videomemorysize>>10);
- /* TODO: This driver cannot be unloaded yet */
- MOD_INC_USE_COUNT;
-
return 0;
+
+amifb_error:
+ amifb_deinit();
+ return err;
+}
+
+static void amifb_deinit(void)
+{
+ chipfree();
+ release_mem_region(CUSTOM_PHYSADDR+0xe0, 0x120);
+ custom.dmacon = DMAF_ALL | DMAF_MASTER;
}
static int amifbcon_switch(int con, struct fb_info *info)
}
}
- /*
- * Allocate, Clear and Align a Block of Chip Memory
- */
-
-static u_long __init chipalloc(u_long size)
-{
- u_long ptr;
-
- size += PAGE_SIZE-1;
- if (!(ptr = (u_long)amiga_chip_alloc(size, "amifb")))
- panic("No Chip RAM for frame buffer");
- memset((void *)ptr, 0, size);
- ptr = PAGE_ALIGN(ptr);
-
- return ptr;
-}
-
/*
* A strtok which returns empty strings, too
*/
void cleanup_module(void)
{
- /* Not reached because the usecount will never
- be decremented to zero */
unregister_framebuffer(&fb_info);
- /* TODO: clean up ... */
+ amifb_deinit();
}
#endif /* MODULE */
}
#ifdef CONFIG_FB_COMPAT_XPMAC
- if (console_fb_info == &info->fb_info) {
+ if (!console_fb_info || console_fb_info == &info->fb_info) {
int vmode, cmode;
display_info.width = var->xres;
strcpy(fix->id, aty128fb_name);
-#ifdef CONFIG_PPC /* why? I don't know */
- *fix->smem_start = (long)info->frame_buffer_phys;
- *fix->mmio_start = (long)info->regbase_phys;
-#else
fix->smem_start = (long)info->frame_buffer_phys;
fix->mmio_start = (long)info->regbase_phys;
-#endif
fix->smem_len = (u32)info->vram_size;
fix->mmio_len = 0x1fff;
chip_rev = (aty_ld_le32(CONFIG_CNTL) >> 16) & 0x1F;
/* put a name with the face */
- while (info->pdev->device != aci->device) { aci++; }
+ while (aci->name && info->pdev->device != aci->device) { aci++; }
video_card = (char *)aci->name;
printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] [card rev %x] ",
if (default_cmode == CMODE_NVRAM)
default_cmode = nvram_read_byte(NV_CMODE);
#endif
- }
+ } else if (_machine == _MACH_Pmac)
+ if (mac_vmode_to_var(default_vmode, default_cmode, &var))
+ var = default_var;
+
#endif
#endif /* MODULE */
#define LOGO_W 80
#define LOGO_LINE (LOGO_W/8)
+static int first_fb_vc = 0;
+static int last_fb_vc = MAX_NR_CONSOLES-1;
+static int fbcon_is_default = 1;
+
struct display fb_display[MAX_NR_CONSOLES];
char con2fb_map[MAX_NR_CONSOLES];
static int logo_lines;
#define FNTSUM(fd) (((int *)(fd))[-4])
#define FONT_EXTRA_WORDS 4
+static char fontname[40] __initdata = { 0 }; /* default font name */
+
#define CM_SOFTBACK (8)
#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * conp->vc_size_row)
* Internal routines
*/
-static void fbcon_setup(int con, int init, int logo);
+static int __init fbcon_setup(char *options);
+static void fbcon_set_disp(int con, int init, int logo);
static __inline__ int real_y(struct display *p, int ypos);
static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp);
static __inline__ void updatescrollmode(struct display *p);
newfb = registered_fb[newidx];
if (newfb->fbops->fb_open(newfb,0))
return;
+ newfb->count++;
+ oldfb->count--;
oldfb->fbops->fb_release(oldfb,0);
conp = fb_display[unit].conp;
fontdata = fb_display[unit].fontdata;
}
}
+static int __init fbcon_setup(char *options)
+{
+ int i, j;
+
+ if (!options || !*options)
+ return 0;
+
+ if (!strncmp(options, "font:", 5))
+ strcpy(fontname, options+5);
+
+ if (!strncmp(options, "scrollback:", 11)) {
+ options += 11;
+ if (*options) {
+ fbcon_softback_size = simple_strtoul(options, &options, 0);
+ if (*options == 'k' || *options == 'K') {
+ fbcon_softback_size *= 1024;
+ options++;
+ }
+ if (*options != ',')
+ return 0;
+ options++;
+ } else
+ return 0;
+ }
+
+ if (!strncmp(options, "map:", 4)) {
+ options += 4;
+ if (*options)
+ for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
+ if (!options[j])
+ j = 0;
+ con2fb_map[i] = (options[j++]-'0') % FB_MAX;
+ }
+ return 0;
+ }
+
+ if (!strncmp(options, "vc:", 3)) {
+ options += 3;
+ if (*options)
+ first_fb_vc = simple_strtoul(options, &options, 10) - 1;
+ if (first_fb_vc < 0)
+ first_fb_vc = 0;
+ if (*options++ == '-')
+ last_fb_vc = simple_strtoul(options, &options, 10) - 1;
+ fbcon_is_default = 0;
+ }
+ return 0;
+}
+
+__setup("fbcon=", fbcon_setup);
+
/*
* Low Level Operations
*/
return display_desc;
}
-
static void fbcon_init(struct vc_data *conp, int init)
{
- int unit = conp->vc_num;
+ int j, unit = conp->vc_num;
struct fb_info *info;
-
+
/* on which frame buffer will we open this console? */
info = registered_fb[(int)con2fb_map[unit]];
+ /*
+ * We assume initial frame buffer devices can be opened this
+ * many times
+ */
+ for (j = 0; j < (last_fb_vc - first_fb_vc + 1); j++) {
+ info->fbops->fb_open(info,0);
+ info->count++;
+ }
+
info->changevar = &fbcon_changevar;
fb_display[unit] = *(info->disp); /* copy from default */
DPRINTK("mode: %s\n",info->modename);
fb_display[unit].cmap.green = 0;
fb_display[unit].cmap.blue = 0;
fb_display[unit].cmap.transp = 0;
- fbcon_setup(unit, init, !init);
- /* Must be done after fbcon_setup to prevent excess updates */
+ fbcon_set_disp(unit, init, !init);
+ /* Must be done after fbcon_set_disp to prevent excess updates */
conp->vc_display_fg = &info->display_fg;
if (!info->display_fg)
info->display_fg = conp;
fbcon_free_font(p);
p->dispsw = &fbcon_dummy;
+ p->fb_info->count = 0;
p->conp = 0;
}
static int fbcon_changevar(int con)
{
if (fb_display[con].conp)
- fbcon_setup(con, 0, 0);
+ fbcon_set_disp(con, 0, 0);
return 0;
}
#define fontwidthvalid(p,w) ((p)->dispsw->fontwidthmask & FONTWIDTH(w))
-static void fbcon_setup(int con, int init, int logo)
+static void fbcon_set_disp(int con, int init, int logo)
{
struct display *p = &fb_display[con];
struct vc_data *conp = p->conp;
}
if (!p->fontdata) {
- if (!p->fb_info->fontname[0] ||
- !(font = fbcon_find_font(p->fb_info->fontname)))
- font = fbcon_get_default_font(p->var.xres, p->var.yres);
+ if (!fontname[0] || !(font = fbcon_find_font(fontname)))
+ font = fbcon_get_default_font(p->var.xres, p->var.yres);
p->_fontwidth = font->width;
p->_fontheight = font->height;
p->fontdata = font->data;
#endif
{
/* ++Geert: changed from panic() to `correct and continue' */
- printk(KERN_ERR "fbcon_setup: No support for fontwidth %d\n", fontwidth(p));
+ printk(KERN_ERR "fbcon_set_disp: No support for fontwidth %d\n", fontwidth(p));
p->dispsw = &fbcon_dummy;
}
}
}
if (p->dispsw == &fbcon_dummy)
- printk(KERN_WARNING "fbcon_setup: type %d (aux %d, depth %d) not "
+ printk(KERN_WARNING "fbcon_set_disp: type %d (aux %d, depth %d) not "
"supported\n", p->type, p->type_aux, p->var.bits_per_pixel);
p->dispsw->setup(p);
con_getxy: fbcon_getxy,
};
+void __init fbconsole_init(void)
+{
+ if (!num_registered_fb || (first_fb_vc > last_fb_vc))
+ return;
+
+ take_over_console(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default);
+}
/*
* Dummy Low Level Operations
static initcall_t pref_init_funcs[FB_MAX];
static int num_pref_init_funcs __initdata = 0;
-
struct fb_info *registered_fb[FB_MAX];
int num_registered_fb = 0;
-extern int fbcon_softback_size;
-
-static int first_fb_vc = 0;
-static int last_fb_vc = MAX_NR_CONSOLES-1;
-static int fbcon_is_default = 1;
static int fbmem_read_proc(char *buf, char **start, off_t offset,
int len, int *eof, void *private)
#endif /* CONFIG_KMOD */
if (!(info = registered_fb[fbidx]))
return -ENODEV;
+ if (info->flags & FBINFO_FLAG_OPEN) return -EBUSY;
+ info->flags |= FBINFO_FLAG_OPEN;
return info->fbops->fb_open(info,1);
}
struct fb_info *info = registered_fb[fbidx];
info->fbops->fb_release(info,1);
+ info->flags &= ~FBINFO_FLAG_OPEN;
return 0;
}
int
register_framebuffer(struct fb_info *fb_info)
{
- int i, j;
char name_buf[8];
- static int fb_ever_opened[FB_MAX];
- static int first = 1;
+ int i;
if (num_registered_fb == FB_MAX)
return -ENXIO;
if (!registered_fb[i])
break;
fb_info->node = MKDEV(FB_MAJOR, i);
+ fb_info->flags &= ~FBINFO_FLAG_OPEN;
+ fb_info->count = 0;
registered_fb[i] = fb_info;
- if (!fb_ever_opened[i]) {
- /*
- * We assume initial frame buffer devices can be opened this
- * many times
- */
- for (j = 0; j < MAX_NR_CONSOLES; j++)
- if (con2fb_map[j] == i)
- fb_info->fbops->fb_open(fb_info,0);
- fb_ever_opened[i] = 1;
- }
-
- if (first) {
- first = 0;
- take_over_console(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default);
- }
sprintf (name_buf, "%d", i);
fb_info->devfs_handle =
devfs_register (devfs_handle, name_buf, 0, DEVFS_FL_NONE,
int
unregister_framebuffer(struct fb_info *fb_info)
{
- int i, j;
+ int i;
i = GET_FB_IDX(fb_info->node);
- for (j = 0; j < MAX_NR_CONSOLES; j++)
- if (con2fb_map[j] == i)
- return -EBUSY;
+
+ if (fb_info->count || (fb_info->flags & FBINFO_FLAG_OPEN))
+ return -EBUSY;
if (!registered_fb[i])
return -EINVAL;
devfs_unregister (fb_info->devfs_handle);
if (!options || !*options)
return 0;
-
- if (!strncmp(options, "scrollback:", 11)) {
- options += 11;
- if (*options) {
- fbcon_softback_size = simple_strtoul(options, &options, 0);
- if (*options == 'k' || *options == 'K') {
- fbcon_softback_size *= 1024;
- options++;
- }
- if (*options != ',')
- return 0;
- options++;
- } else
- return 0;
- }
-
- if (!strncmp(options, "map:", 4)) {
- options += 4;
- if (*options)
- for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
- if (!options[j])
- j = 0;
- con2fb_map[i] = (options[j++]-'0') % FB_MAX;
- }
- return 0;
- }
-
- if (!strncmp(options, "vc:", 3)) {
- options += 3;
- if (*options)
- first_fb_vc = simple_strtoul(options, &options, 10) - 1;
- if (first_fb_vc < 0)
- first_fb_vc = 0;
- if (*options++ == '-')
- last_fb_vc = simple_strtoul(options, &options, 10) - 1;
- fbcon_is_default = 0;
- }
if (num_pref_init_funcs == FB_MAX)
return 0;
*/
#include <linux/tty.h>
#include <linux/fb.h>
+#include <linux/module.h>
int fbmon_valid_timings(u_int pixclock, u_int htotal, u_int vtotal,
const struct fb_info *fb_info)
{
return fb_info->monspecs.dpms;
}
+
+EXPORT_SYMBOL(fbmon_valid_timings);
}
-#ifdef CONFIG_FB_ATY128
-extern void aty128fb_of_init(struct device_node *dp);
-#endif /* CONFIG_FB_ATY */
#ifdef CONFIG_FB_S3TRIO
extern void s3triofb_init_of(struct device_node *dp);
#endif /* CONFIG_FB_S3TRIO */
static int __init offb_init_driver(struct device_node *dp)
{
-#ifdef CONFIG_FB_ATY128
- if (!strncmp(dp->name, "ATY,Rage128", 11)) {
- aty128fb_of_init(dp);
- return 1;
- }
-#endif
#ifdef CONFIG_FB_S3TRIO
if (!strncmp(dp->name, "S3Trio", 6)) {
s3triofb_init_of(dp);
static void vgacon_invert_region(struct vc_data *c, u16 *p, int count);
static unsigned long vgacon_uni_pagedir[2];
-void clear_status_line( void );
-
/* Description of the hardware situation */
static unsigned long vga_vram_base; /* Base of video memory */
static inline void vga_set_mem_top(struct vc_data *c)
{
write_vga(12, (c->vc_visible_origin-vga_vram_base)/2);
- clear_status_line();
}
static void vgacon_deinit(struct vc_data *c)
vgacon_build_attr,
vgacon_invert_region
};
-
-
-int inited = 0;
-
-void
-clear_status_line( void )
-{
-#if CONFIG_COMMENT_INT==3
- u16 *org;
- int i;
- int currcons = fg_console;
- struct vc_data *c = vc_cons[fg_console].d;
- if (!inited) return;
- if (vga_is_gfx) return;
- if (c->vc_origin != c->vc_visible_origin) return;
- org = screen_pos( fg_console, video_num_lines*video_num_columns, 1 );
- for (i=0; i<video_num_columns; i++)
- scr_writew( (0x0f << 8) + ' ', org++ );
-#endif
-}
-
-#if CONFIG_COMMENT_INT==3
-void
-paint_status_line( int timer )
-{
- u16 *org;
- int i,j;
- int currcons = fg_console;
- struct vc_data *c = vc_cons[fg_console].d;
-
- if (!inited) return;
- if (vga_is_gfx) /* We don't play origin tricks in graphic modes */
- return;
- if (c->vc_origin != c->vc_visible_origin) return;
-
- org = screen_pos( fg_console, video_num_lines*video_num_columns, 1 );
-
- /* Are we busy? */
- {
- i = current->pid;
- j = 0;
- if (i)
- j = (i>16) ? ((current->priority < DEF_PRIORITY) ? 0xA0:0xE0) : 0xC0;
- scr_writew( (j<<8) + ' ', org++ );
-
-#if 0
- org++;
-
-#define DISP( x ) scr_writew( (((i&x) ? 0x90:0) << 8) + ' ', org++ );
- DISP( 0x80 );
- DISP( 0x40 );
- DISP( 0x20 );
- DISP( 0x10 );
- DISP( 0x08 );
- DISP( 0x04 );
- DISP( 0x02 );
- DISP( 0x01 );
-#endif
- }
-
- if (!timer) return;
-
- org++;
- /* Serial? */
- {
- j = (ledflags & 0x10) ? 0x90 : 0x00;
- scr_writew( (j<<8) + 'S', org++ );
- }
-
- org++;
- /* NE2000? */
- {
- j = (ledflags & 0x20) ? 0x90 : 0x00;
- scr_writew( (j<<8) + 'N', org++ );
- }
-}
-#endif
return parse_options(sb, data);
}
-static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int adfs_statfs(struct super_block *sb, struct statfs *buf)
{
struct adfs_sb_info *asb = &sb->u.adfs_sb;
- struct statfs tmp;
-
- tmp.f_type = ADFS_SUPER_MAGIC;
- tmp.f_namelen = asb->s_namelen;
- tmp.f_bsize = sb->s_blocksize;
- tmp.f_blocks = asb->s_size;
- tmp.f_files = asb->s_ids_per_zone * asb->s_map_size;
- tmp.f_bavail =
- tmp.f_bfree = adfs_map_free(sb);
- tmp.f_ffree = tmp.f_bfree * tmp.f_files / tmp.f_blocks;
-
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+
+ buf->f_type = ADFS_SUPER_MAGIC;
+ buf->f_namelen = asb->s_namelen;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = asb->s_size;
+ buf->f_files = asb->s_ids_per_zone * asb->s_map_size;
+ buf->f_bavail =
+ buf->f_bfree = adfs_map_free(sb);
+ buf->f_ffree = buf->f_bfree * buf->f_files / buf->f_blocks;
+
+ return 0;
}
static struct super_operations adfs_sops = {
if (!empty_dir(bh,AFFS_I2HSIZE(inode)))
goto rmdir_done;
retval = -EBUSY;
- if (!list_empty(&dentry->d_hash))
+ if (!d_unhashed(dentry))
goto rmdir_done;
if ((retval = affs_remove_header(bh,inode)) < 0)
}
if (S_ISDIR(old_inode->i_mode)) {
if (new_inode) {
+ retval = -EBUSY;
+ if (!d_unhashed(new_dentry))
+ goto end_rename;
retval = -ENOTEMPTY;
if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
goto end_rename;
#define MIN(a,b) (((a)<(b))?(a):(b))
-static int affs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static int affs_statfs(struct super_block *sb, struct statfs *buf);
static int affs_remount (struct super_block *sb, int *flags, char *data);
static void
}
static int
-affs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+affs_statfs(struct super_block *sb, struct statfs *buf)
{
int free;
- struct statfs tmp;
pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",sb->u.affs_sb.s_partition_size,
sb->u.affs_sb.s_reserved);
free = affs_count_free_blocks(sb);
- tmp.f_type = AFFS_SUPER_MAGIC;
- tmp.f_bsize = sb->s_blocksize;
- tmp.f_blocks = sb->u.affs_sb.s_partition_size - sb->u.affs_sb.s_reserved;
- tmp.f_bfree = free;
- tmp.f_bavail = free;
- tmp.f_files = 0;
- tmp.f_ffree = 0;
- return copy_to_user(buf,&tmp,bufsiz) ? -EFAULT : 0;
+ buf->f_type = AFFS_SUPER_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = sb->u.affs_sb.s_partition_size - sb->u.affs_sb.s_reserved;
+ buf->f_bfree = free;
+ buf->f_bavail = free;
+ return 0;
}
static struct file_system_type affs_fs_type = {
#endif
}
-static int autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static int autofs_statfs(struct super_block *sb, struct statfs *buf);
static void autofs_read_inode(struct inode *inode);
static void autofs_write_inode(struct inode *inode);
return NULL;
}
-static int autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int autofs_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
-
- tmp.f_type = AUTOFS_SUPER_MAGIC;
- tmp.f_bsize = 1024;
- tmp.f_blocks = 0;
- tmp.f_bfree = 0;
- tmp.f_bavail = 0;
- tmp.f_files = 0;
- tmp.f_ffree = 0;
- tmp.f_namelen = NAME_MAX;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = AUTOFS_SUPER_MAGIC;
+ buf->f_bsize = 1024;
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = NAME_MAX;
+ return 0;
}
static void autofs_read_inode(struct inode *inode)
* doesn't do the right thing for all system calls, but it should
* be OK for the operations we permit from an autofs.
*/
- if ( dentry->d_inode && list_empty(&dentry->d_hash) )
+ if ( dentry->d_inode && d_unhashed(dentry) )
return ERR_PTR(-ENOENT);
return NULL;
autofs4_catatonic_mode(sbi);
}
-static int autofs4_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static int autofs4_statfs(struct super_block *sb, struct statfs *buf);
static void autofs4_read_inode(struct inode *inode);
static void autofs4_write_inode(struct inode *inode);
return NULL;
}
-static int autofs4_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int autofs4_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
-
- tmp.f_type = AUTOFS_SUPER_MAGIC;
- tmp.f_bsize = 1024;
- tmp.f_blocks = 0;
- tmp.f_bfree = 0;
- tmp.f_bavail = 0;
- tmp.f_files = 0;
- tmp.f_ffree = 0;
- tmp.f_namelen = NAME_MAX;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = AUTOFS_SUPER_MAGIC;
+ buf->f_bsize = 1024;
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = NAME_MAX;
+ return 0;
}
static void autofs4_read_inode(struct inode *inode)
* doesn't do the right thing for all system calls, but it should
* be OK for the operations we permit from an autofs.
*/
- if ( dentry->d_inode && list_empty(&dentry->d_hash) )
+ if ( dentry->d_inode && d_unhashed(dentry) )
return ERR_PTR(-ENOENT);
return NULL;
MOD_DEC_USE_COUNT;
}
-static int bfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
+static int bfs_statfs(struct super_block *s, struct statfs *buf)
{
- struct statfs tmp;
-
- tmp.f_type = BFS_MAGIC;
- tmp.f_bsize = s->s_blocksize;
- tmp.f_blocks = s->su_blocks;
- tmp.f_bfree = tmp.f_bavail = s->su_freeb;
- tmp.f_files = s->su_lasti + 1 - BFS_ROOT_INO;
- tmp.f_ffree = s->su_freei;
- tmp.f_fsid.val[0] = s->s_dev;
- tmp.f_namelen = BFS_NAMELEN;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = BFS_MAGIC;
+ buf->f_bsize = s->s_blocksize;
+ buf->f_blocks = s->su_blocks;
+ buf->f_bfree = buf->f_bavail = s->su_freeb;
+ buf->f_files = s->su_lasti + 1 - BFS_ROOT_INO;
+ buf->f_ffree = s->su_freei;
+ buf->f_fsid.val[0] = s->s_dev;
+ buf->f_namelen = BFS_NAMELEN;
+ return 0;
}
static void bfs_write_super(struct super_block *s)
dircnp = ITOC(dir);
- if (!list_empty(&de->d_hash))
+ if (!d_unhashed(de))
return -EBUSY;
error = venus_rmdir(dir->i_sb, &(dircnp->c_fid), name, len);
static void coda_put_inode(struct inode *);
static void coda_delete_inode(struct inode *);
static void coda_put_super(struct super_block *);
-static int coda_statfs(struct super_block *sb, struct statfs *buf,
- int bufsiz);
+static int coda_statfs(struct super_block *sb, struct statfs *buf);
/* exported operations */
struct super_operations coda_super_operations =
return error;
}
-static int coda_statfs(struct super_block *sb, struct statfs *buf,
- int bufsiz)
+static int coda_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
int error;
- memset(&tmp, 0, sizeof(struct statfs));
-
- error = venus_statfs(sb, &tmp);
+ error = venus_statfs(sb, buf);
if (error) {
/* fake something like AFS does */
- tmp.f_blocks = 9000000;
- tmp.f_bfree = 9000000;
- tmp.f_bavail = 9000000;
- tmp.f_files = 9000000;
- tmp.f_ffree = 9000000;
+ buf->f_blocks = 9000000;
+ buf->f_bfree = 9000000;
+ buf->f_bavail = 9000000;
+ buf->f_files = 9000000;
+ buf->f_ffree = 9000000;
}
/* and fill in the rest */
- tmp.f_type = CODA_SUPER_MAGIC;
- tmp.f_bsize = 1024;
- tmp.f_namelen = CODA_MAXNAMLEN;
-
- copy_to_user(buf, &tmp, bufsiz);
+ buf->f_type = CODA_SUPER_MAGIC;
+ buf->f_bsize = 1024;
+ buf->f_namelen = CODA_MAXNAMLEN;
return 0;
}
return;
}
-static int cramfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
+static int cramfs_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
-
- /* Unsupported fields set to -1 as per man page. */
- memset(&tmp, 0xff, sizeof(tmp));
-
- tmp.f_type = CRAMFS_MAGIC;
- tmp.f_bsize = PAGE_CACHE_SIZE;
- tmp.f_bfree = 0;
- tmp.f_bavail = 0;
- tmp.f_ffree = 0;
- tmp.f_namelen = 255;
- return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0;
+ buf->f_type = CRAMFS_MAGIC;
+ buf->f_bsize = PAGE_CACHE_SIZE;
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = 255;
+ return 0;
}
/*
MOD_DEC_USE_COUNT;
} /* End Function devfs_put_super */
-static int devfs_statfs (struct super_block *sb, struct statfs *buf,int bufsiz)
-{
- struct statfs tmp;
-
- tmp.f_type = DEVFS_SUPER_MAGIC;
- tmp.f_bsize = PAGE_SIZE / sizeof (long);
- tmp.f_blocks = 0;
- tmp.f_bfree = 0;
- tmp.f_bavail = 0;
- tmp.f_files = 0;
- tmp.f_ffree = 0;
- tmp.f_namelen = NAME_MAX;
- return copy_to_user (buf, &tmp, bufsiz) ? -EFAULT : 0;
+static int devfs_statfs (struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = DEVFS_SUPER_MAGIC;
+ buf->f_bsize = PAGE_SIZE / sizeof (long);
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = NAME_MAX;
+ return 0;
} /* End Function devfs_statfs */
static struct super_operations devfs_sops =
#endif
}
-static int devpts_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static int devpts_statfs(struct super_block *sb, struct statfs *buf);
static void devpts_read_inode(struct inode *inode);
static void devpts_write_inode(struct inode *inode);
return NULL;
}
-static int devpts_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int devpts_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
-
- tmp.f_type = DEVPTS_SUPER_MAGIC;
- tmp.f_bsize = 1024;
- tmp.f_blocks = 0;
- tmp.f_bfree = 0;
- tmp.f_bavail = 0;
- tmp.f_files = 0;
- tmp.f_ffree = 0;
- tmp.f_namelen = NAME_MAX;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = DEVPTS_SUPER_MAGIC;
+ buf->f_bsize = 1024;
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = NAME_MAX;
+ return 0;
}
static void devpts_read_inode(struct inode *inode)
MOD_DEC_USE_COUNT;
}
-int efs_statfs(struct super_block *s, struct statfs *buf, int bufsiz) {
- struct statfs ret;
+int efs_statfs(struct super_block *s, struct statfs *buf) {
struct efs_sb_info *sb = SUPER_INFO(s);
- ret.f_type = EFS_SUPER_MAGIC; /* efs magic number */
- ret.f_bsize = EFS_BLOCKSIZE; /* blocksize */
- ret.f_blocks = sb->total_groups * /* total data blocks */
+ buf->f_type = EFS_SUPER_MAGIC; /* efs magic number */
+ buf->f_bsize = EFS_BLOCKSIZE; /* blocksize */
+ buf->f_blocks = sb->total_groups * /* total data blocks */
(sb->group_size - sb->inode_blocks);
- ret.f_bfree = sb->data_free; /* free data blocks */
- ret.f_bavail = sb->data_free; /* free blocks for non-root */
- ret.f_files = sb->total_groups * /* total inodes */
+ buf->f_bfree = sb->data_free; /* free data blocks */
+ buf->f_bavail = sb->data_free; /* free blocks for non-root */
+ buf->f_files = sb->total_groups * /* total inodes */
sb->inode_blocks *
(EFS_BLOCKSIZE / sizeof(struct efs_dinode));
- ret.f_ffree = sb->inode_free; /* free inodes */
- ret.f_fsid.val[0] = (sb->fs_magic >> 16) & 0xffff; /* fs ID */
- ret.f_fsid.val[1] = sb->fs_magic & 0xffff; /* fs ID */
- ret.f_namelen = EFS_MAXNAMELEN; /* max filename length */
+ buf->f_ffree = sb->inode_free; /* free inodes */
+ buf->f_fsid.val[0] = (sb->fs_magic >> 16) & 0xffff; /* fs ID */
+ buf->f_fsid.val[1] = sb->fs_magic & 0xffff; /* fs ID */
+ buf->f_namelen = EFS_MAXNAMELEN; /* max filename length */
- return copy_to_user(buf, &ret, bufsiz) ? -EFAULT : 0;
+ return 0;
}
return 0;
}
-int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
+int ext2_statfs (struct super_block * sb, struct statfs * buf)
{
unsigned long overhead;
- struct statfs tmp;
int ngroups, i;
if (test_opt (sb, MINIX_DF))
(2 + sb->u.ext2_sb.s_itb_per_group));
}
- tmp.f_type = EXT2_SUPER_MAGIC;
- tmp.f_bsize = sb->s_blocksize;
- tmp.f_blocks = le32_to_cpu(sb->u.ext2_sb.s_es->s_blocks_count) - overhead;
- tmp.f_bfree = ext2_count_free_blocks (sb);
- tmp.f_bavail = tmp.f_bfree - le32_to_cpu(sb->u.ext2_sb.s_es->s_r_blocks_count);
- if (tmp.f_bfree < le32_to_cpu(sb->u.ext2_sb.s_es->s_r_blocks_count))
- tmp.f_bavail = 0;
- tmp.f_files = le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count);
- tmp.f_ffree = ext2_count_free_inodes (sb);
- tmp.f_namelen = EXT2_NAME_LEN;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = EXT2_SUPER_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = le32_to_cpu(sb->u.ext2_sb.s_es->s_blocks_count) - overhead;
+ buf->f_bfree = ext2_count_free_blocks (sb);
+ buf->f_bavail = buf->f_bfree - le32_to_cpu(sb->u.ext2_sb.s_es->s_r_blocks_count);
+ if (buf->f_bfree < le32_to_cpu(sb->u.ext2_sb.s_es->s_r_blocks_count))
+ buf->f_bavail = 0;
+ buf->f_files = le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count);
+ buf->f_ffree = ext2_count_free_inodes (sb);
+ buf->f_namelen = EXT2_NAME_LEN;
+ return 0;
}
static struct file_system_type ext2_fs_type = {
return NULL;
}
-int fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
+int fat_statfs(struct super_block *sb,struct statfs *buf)
{
int free,nr;
- struct statfs tmp;
if (MSDOS_SB(sb)->cvf_format &&
MSDOS_SB(sb)->cvf_format->cvf_statfs)
- return MSDOS_SB(sb)->cvf_format->cvf_statfs(sb,buf,bufsiz);
+ return MSDOS_SB(sb)->cvf_format->cvf_statfs(sb,buf,
+ sizeof(struct statfs));
lock_fat(sb);
if (MSDOS_SB(sb)->free_clusters != -1)
MSDOS_SB(sb)->free_clusters = free;
}
unlock_fat(sb);
- tmp.f_type = sb->s_magic;
- tmp.f_bsize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE;
- tmp.f_blocks = MSDOS_SB(sb)->clusters;
- tmp.f_bfree = free;
- tmp.f_bavail = free;
- tmp.f_files = 0;
- tmp.f_ffree = 0;
- tmp.f_namelen = MSDOS_SB(sb)->options.isvfat ? 260 : 12;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = sb->s_magic;
+ buf->f_bsize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE;
+ buf->f_blocks = MSDOS_SB(sb)->clusters;
+ buf->f_bfree = free;
+ buf->f_bavail = free;
+ buf->f_namelen = MSDOS_SB(sb)->options.isvfat ? 260 : 12;
+ return 0;
}
static int is_exec(char *extension)
init_devfs_fs(); /* Header file may make this empty */
-#ifdef CONFIG_LOCKD
- nlmxdr_init();
-#endif
-
#ifdef CONFIG_NFS_FS
init_nfs_fs();
#endif
goto hfs_rmdir_put;
error = -EBUSY;
- if (!list_empty(&dentry->d_hash))
+ if (!d_unhashed(dentry))
goto hfs_rmdir_put;
if (/* we only have to worry about 2 and 3 for mount points */
static void hfs_read_inode(struct inode *);
static void hfs_put_super(struct super_block *);
-static int hfs_statfs(struct super_block *, struct statfs *, int);
+static int hfs_statfs(struct super_block *, struct statfs *);
static void hfs_write_super(struct super_block *);
/*================ Global variables ================*/
*
* changed f_files/f_ffree to reflect the fs_ablock/free_ablocks.
*/
-static int hfs_statfs(struct super_block *sb, struct statfs *buf, int len)
+static int hfs_statfs(struct super_block *sb, struct statfs *buf)
{
struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb;
- struct statfs tmp;
-
- tmp.f_type = HFS_SUPER_MAGIC;
- tmp.f_bsize = HFS_SECTOR_SIZE;
- tmp.f_blocks = mdb->alloc_blksz * mdb->fs_ablocks;
- tmp.f_bfree = mdb->alloc_blksz * mdb->free_ablocks;
- tmp.f_bavail = tmp.f_bfree;
- tmp.f_files = mdb->fs_ablocks;
- tmp.f_ffree = mdb->free_ablocks;
- tmp.f_namelen = HFS_NAMELEN;
-
- return copy_to_user(buf, &tmp, len) ? -EFAULT : 0;
+
+ buf->f_type = HFS_SUPER_MAGIC;
+ buf->f_bsize = HFS_SECTOR_SIZE;
+ buf->f_blocks = mdb->alloc_blksz * mdb->fs_ablocks;
+ buf->f_bfree = mdb->alloc_blksz * mdb->free_ablocks;
+ buf->f_bavail = buf->f_bfree;
+ buf->f_files = mdb->fs_ablocks;
+ buf->f_ffree = mdb->free_ablocks;
+ buf->f_namelen = HFS_NAMELEN;
+
+ return 0;
}
/*
int hpfs_remount_fs(struct super_block *, int *, char *);
void hpfs_put_super(struct super_block *);
unsigned hpfs_count_one_bitmap(struct super_block *, secno);
-int hpfs_statfs(struct super_block *, struct statfs *, int);
+int hpfs_statfs(struct super_block *, struct statfs *);
struct super_block *hpfs_read_super(struct super_block *, void *, int);
extern struct address_space_operations hpfs_aops;
hpfs_unlock_2inodes(dir, inode);
return -ENOTDIR;
}
- if (!list_empty(&dentry->d_hash)) {
+ if (!d_unhashed(dentry)) {
hpfs_brelse4(&qbh);
hpfs_unlock_2inodes(dir, inode);
return -EBUSY;
return count;
}
-int hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
+int hpfs_statfs(struct super_block *s, struct statfs *buf)
{
- struct statfs tmp;
/*if (s->s_hpfs_n_free == -1) {*/
s->s_hpfs_n_free = count_bitmaps(s);
s->s_hpfs_n_free_dnodes = hpfs_count_one_bitmap(s, s->s_hpfs_dmap);
/*}*/
- tmp.f_type = s->s_magic;
- tmp.f_bsize = 512;
- tmp.f_blocks = s->s_hpfs_fs_size;
- tmp.f_bfree = s->s_hpfs_n_free;
- tmp.f_bavail = s->s_hpfs_n_free;
- tmp.f_files = s->s_hpfs_dirband_size / 4;
- tmp.f_ffree = s->s_hpfs_n_free_dnodes;
- tmp.f_namelen = 254;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = s->s_magic;
+ buf->f_bsize = 512;
+ buf->f_blocks = s->s_hpfs_fs_size;
+ buf->f_bfree = s->s_hpfs_n_free;
+ buf->f_bavail = s->s_hpfs_n_free;
+ buf->f_files = s->s_hpfs_dirband_size / 4;
+ buf->f_ffree = s->s_hpfs_n_free_dnodes;
+ buf->f_namelen = 254;
+ return 0;
}
/* Super operations */
INIT_LIST_HEAD(&inode->i_data.pages);
INIT_LIST_HEAD(&inode->i_dentry);
sema_init(&inode->i_sem, 1);
+ sema_init(&inode->i_zombie, 1);
spin_lock_init(&inode->i_data.i_shared_lock);
}
}
}
static void isofs_read_inode(struct inode *);
-static int isofs_statfs (struct super_block *, struct statfs *, int);
+static int isofs_statfs (struct super_block *, struct statfs *);
static struct super_operations isofs_sops = {
read_inode: isofs_read_inode,
return NULL;
}
-static int isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz)
+static int isofs_statfs (struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
-
- tmp.f_type = ISOFS_SUPER_MAGIC;
- tmp.f_bsize = sb->s_blocksize;
- tmp.f_blocks = (sb->u.isofs_sb.s_nzones
+ buf->f_type = ISOFS_SUPER_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = (sb->u.isofs_sb.s_nzones
<< (sb->u.isofs_sb.s_log_zone_size - sb->s_blocksize_bits));
- tmp.f_bfree = 0;
- tmp.f_bavail = 0;
- tmp.f_files = sb->u.isofs_sb.s_ninodes;
- tmp.f_ffree = 0;
- tmp.f_namelen = NAME_MAX;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_files = sb->u.isofs_sb.s_ninodes;
+ buf->f_ffree = 0;
+ buf->f_namelen = NAME_MAX;
+ return 0;
}
/* Life is simpler than for other filesystem since we never
EXPORT_SYMBOL(nlmsvc_grace_period);
EXPORT_SYMBOL(nlmsvc_timeout);
-#ifdef CONFIG_LOCKD_V4
-
-/* NLM4 exported symbols */
-EXPORT_SYMBOL(nlm4_rofs);
-EXPORT_SYMBOL(nlm4_stale_fh);
-EXPORT_SYMBOL(nlm4_deadlock);
-EXPORT_SYMBOL(nlm4_failed);
-EXPORT_SYMBOL(nlm4_fbig);
-
-#endif
-
#endif /* CONFIG_MODULES */
init_MUTEX(&nlmsvc_sema);
nlmsvc_users = 0;
nlmsvc_pid = 0;
- nlmxdr_init();
return 0;
}
#include <linux/lockd/sm_inter.h>
#define NLMDBG_FACILITY NLMDBG_XDR
-#define NLM_MAXSTRLEN 1024
-#define QUADLEN(len) (((len) + 3) >> 2)
-
-
-u32 nlm_granted, nlm_lck_denied, nlm_lck_denied_nolocks,
- nlm_lck_blocked, nlm_lck_denied_grace_period;
-
-
-typedef struct nlm_args nlm_args;
-
-/*
- * Initialization of NFS status variables
- */
-void
-nlmxdr_init(void)
-{
- static int inited = 0;
-
- if (inited)
- return;
-
- nlm_granted = htonl(NLM_LCK_GRANTED);
- nlm_lck_denied = htonl(NLM_LCK_DENIED);
- nlm_lck_denied_nolocks = htonl(NLM_LCK_DENIED_NOLOCKS);
- nlm_lck_blocked = htonl(NLM_LCK_BLOCKED);
- nlm_lck_denied_grace_period = htonl(NLM_LCK_DENIED_GRACE_PERIOD);
-
-#ifdef CONFIG_LOCKD_V4
- nlm4_deadlock = htonl(NLM_DEADLCK);
- nlm4_rofs = htonl(NLM_ROFS);
- nlm4_stale_fh = htonl(NLM_STALE_FH);
- nlm4_fbig = htonl(NLM_FBIG);
- nlm4_failed = htonl(NLM_FAILED);
-#endif
-
- inited = 1;
-}
/*
* XDR functions for basic NLM types
#include <linux/lockd/sm_inter.h>
#define NLMDBG_FACILITY NLMDBG_XDR
-#define NLM_MAXSTRLEN 1024
#define OFFSET_MAX ((off_t)LONG_MAX)
-#define QUADLEN(len) (((len) + 3) >> 2)
-
-u32 nlm4_deadlock, nlm4_rofs, nlm4_stale_fh, nlm4_fbig,
- nlm4_failed;
-
-
-typedef struct nlm_args nlm_args;
static inline off_t
size_to_off_t(__s64 size)
#include <linux/highuid.h>
#include <asm/system.h>
-#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/minix_fs.h>
static void minix_read_inode(struct inode * inode);
static void minix_write_inode(struct inode * inode);
-static int minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static int minix_statfs(struct super_block *sb, struct statfs *buf);
static int minix_remount (struct super_block * sb, int * flags, char * data);
static void minix_delete_inode(struct inode *inode)
return NULL;
}
-static int minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int minix_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
-
- tmp.f_type = sb->s_magic;
- tmp.f_bsize = sb->s_blocksize;
- tmp.f_blocks = (sb->u.minix_sb.s_nzones - sb->u.minix_sb.s_firstdatazone) << sb->u.minix_sb.s_log_zone_size;
- tmp.f_bfree = minix_count_free_blocks(sb);
- tmp.f_bavail = tmp.f_bfree;
- tmp.f_files = sb->u.minix_sb.s_ninodes;
- tmp.f_ffree = minix_count_free_inodes(sb);
- tmp.f_namelen = sb->u.minix_sb.s_namelen;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = sb->s_magic;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = (sb->u.minix_sb.s_nzones - sb->u.minix_sb.s_firstdatazone) << sb->u.minix_sb.s_log_zone_size;
+ buf->f_bfree = minix_count_free_blocks(sb);
+ buf->f_bavail = buf->f_bfree;
+ buf->f_files = sb->u.minix_sb.s_ninodes;
+ buf->f_ffree = minix_count_free_inodes(sb);
+ buf->f_namelen = sb->u.minix_sb.s_namelen;
+ return 0;
}
/*
retval = -ENOENT;
goto end_rmdir;
}
- if (!list_empty(&dentry->d_hash)) {
+ if (!d_unhashed(dentry)) {
retval = -EBUSY;
goto end_rmdir;
}
}
if (S_ISDIR(old_inode->i_mode)) {
if (new_inode) {
+ retval = -EBUSY;
+ if (!d_unhashed(new_dentry))
+ goto end_rename;
retval = -ENOTEMPTY;
if (!empty_dir(new_inode))
goto end_rename;
* whether it is empty.
*/
res = -EBUSY;
- if (!list_empty(&dentry->d_hash))
+ if (!d_unhashed(dentry))
goto rmdir_done;
res = fat_dir_empty(inode);
if (res)
goto degenerate_case;
if (is_dir) {
if (new_inode) {
+ error = -EBUSY;
+ if (!d_unhashed(new_dentry))
+ goto out;
error = fat_dir_empty(new_inode);
if (error)
goto out;
mode &= S_IALLUGO & ~current->fs->umask;
mode |= S_IFREG;
+ down(&dir->i_zombie);
error = may_create(dir, dentry);
if (error)
goto exit_lock;
DQUOT_INIT(dir);
error = dir->i_op->create(dir, dentry, mode);
exit_lock:
+ up(&dir->i_zombie);
return error;
}
mode &= ~current->fs->umask;
- if (!S_ISFIFO(mode) && !capable(CAP_MKNOD))
+ down(&dir->i_zombie);
+ if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
goto exit_lock;
error = may_create(dir, dentry);
DQUOT_INIT(dir);
error = dir->i_op->mknod(dir, dentry, mode, dev);
exit_lock:
+ up(&dir->i_zombie);
return error;
}
{
int error;
+ down(&dir->i_zombie);
error = may_create(dir, dentry);
if (error)
goto exit_lock;
error = dir->i_op->mkdir(dir, dentry, mode);
exit_lock:
+ up(&dir->i_zombie);
return error;
}
return error;
}
+/*
+ * We try to drop the dentry early: we should have
+ * a usage count of 2 if we're the only user of this
+ * dentry, and if that is true (possibly after pruning
+ * the dcache), then we drop the dentry now.
+ *
+ * A low-level filesystem can, if it choses, legally
+ * do a
+ *
+ * if (!d_unhashed(dentry))
+ * return -EBUSY;
+ *
+ * if it cannot handle the case of removing a directory
+ * that is still in use by something else..
+ */
+static void d_unhash(struct dentry *dentry)
+{
+ dget(dentry);
+ switch (dentry->d_count) {
+ default:
+ shrink_dcache_parent(dentry);
+ if (dentry->d_count != 2)
+ break;
+ case 2:
+ d_drop(dentry);
+ }
+}
+
int vfs_rmdir(struct inode *dir, struct dentry *dentry)
{
int error;
DQUOT_INIT(dir);
- /*
- * We try to drop the dentry early: we should have
- * a usage count of 2 if we're the only user of this
- * dentry, and if that is true (possibly after pruning
- * the dcache), then we drop the dentry now.
- *
- * A low-level filesystem can, if it choses, legally
- * do a
- *
- * if (!list_empty(&dentry->d_hash))
- * return -EBUSY;
- *
- * if it cannot handle the case of removing a directory
- * that is still in use by something else..
- */
- switch (dentry->d_count) {
- default:
- shrink_dcache_parent(dentry);
- if (dentry->d_count != 2)
- break;
- case 2:
- d_drop(dentry);
- }
-
+ double_down(&dir->i_zombie, &dentry->d_inode->i_zombie);
+ d_unhash(dentry);
error = dir->i_op->rmdir(dir, dentry);
+ double_up(&dir->i_zombie, &dentry->d_inode->i_zombie);
+ dput(dentry);
return error;
}
if (IS_ERR(dentry))
goto exit;
- error = -ENOENT;
- if (!dentry->d_inode)
- goto exit_dput;
-
- dir = dget(dentry->d_parent);
-
- /*
- * The dentry->d_count stuff confuses d_delete() enough to
- * not kill the inode from under us while it is locked. This
- * wouldn't be needed, except the dentry semaphore is really
- * in the inode, not in the dentry..
- */
- dentry->d_count++;
- double_lock(dir, dentry);
-
+ dir = lock_parent(dentry);
error = -ENOENT;
if (check_parent(dir, dentry))
error = vfs_rmdir(dir->d_inode, dentry);
-
- double_unlock(dentry, dir);
-exit_dput:
+ unlock_dir(dir);
dput(dentry);
exit:
return error;
{
int error;
+ down(&dir->i_zombie);
error = may_delete(dir, dentry, 0);
if (!error) {
error = -EPERM;
error = dir->i_op->unlink(dir, dentry);
}
}
+ up(&dir->i_zombie);
return error;
}
-static inline int do_unlink(const char * name)
+int do_unlink(const char * name)
{
int error;
struct dentry *dir;
{
int error;
+ down(&dir->i_zombie);
error = may_create(dir, dentry);
if (error)
goto exit_lock;
error = dir->i_op->symlink(dir, dentry, oldname);
exit_lock:
+ up(&dir->i_zombie);
return error;
}
struct inode *inode;
int error;
+ down(&dir->i_zombie);
error = -ENOENT;
inode = old_dentry->d_inode;
if (!inode)
error = dir->i_op->link(old_dentry, dir, new_dentry);
exit_lock:
+ up(&dir->i_zombie);
return error;
}
return error;
}
+/*
+ * The worst of all namespace operations - renaming directory. "Perverted"
+ * doesn't even start to describe it. Somebody in UCB had a heck of a trip...
+ * Problems:
+ * a) we can get into loop creation. Check is done in is_subdir().
+ * b) race potential - two innocent renames can create a loop together.
+ * That's where 4.4 screws up. Current fix: serialization on
+ * sb->s_vfs_rename_sem. We might be more accurate, but that's another
+ * story.
+ * c) we have to lock _three_ objects - parents and victim (if it exists).
+ * And that - after we got ->i_sem on parents (until then we don't know
+ * whether the target exists at all, let alone whether it is a directory
+ * or not). Solution: ->i_zombie. Taken only after ->i_sem. Always taken
+ * on link creation/removal of any kind. And taken (without ->i_sem) on
+ * directory that will be removed (both in rmdir() and here).
+ * d) some filesystems don't support opened-but-unlinked directories,
+ * either because of layout or because they are not ready to deal with
+ * all cases correctly. The latter will be fixed (taking this sort of
+ * stuff into VFS), but the former is not going away. Solution: the same
+ * trick as in rmdir().
+ * e) conversion from fhandle to dentry may come in the wrong moment - when
+ * we are removing the target. Solution: we will have to grab ->i_zombie
+ * in the fhandle_to_dentry code. [FIXME - current nfsfh.c relies on
+ * ->i_sem on parents, which works but leads to some truely excessive
+ * locking].
+ */
int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
int error;
- int need_rehash = 0;
+ struct inode *target;
if (old_dentry->d_inode == new_dentry->d_inode)
return 0;
error = -EINVAL;
if (is_subdir(new_dentry, old_dentry))
goto out_unlock;
- if (new_dentry->d_inode) {
- error = -EBUSY;
- if (d_invalidate(new_dentry)<0)
- goto out_unlock;
- need_rehash = 1;
- }
+ target = new_dentry->d_inode;
+ if (target) { /* Hastur! Hastur! Hastur! */
+ triple_down(&old_dir->i_zombie,
+ &new_dir->i_zombie,
+ &target->i_zombie);
+ d_unhash(new_dentry);
+ } else
+ double_down(&old_dir->i_zombie,
+ &new_dir->i_zombie);
error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
- if (need_rehash)
+ if (target) {
+ triple_up(&old_dir->i_zombie,
+ &new_dir->i_zombie,
+ &target->i_zombie);
d_rehash(new_dentry);
+ dput(new_dentry);
+ } else
+ double_up(&old_dir->i_zombie,
+ &new_dir->i_zombie);
+
if (!error)
d_move(old_dentry,new_dentry);
out_unlock:
DQUOT_INIT(old_dir);
DQUOT_INIT(new_dir);
+ double_down(&old_dir->i_zombie, &new_dir->i_zombie);
error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+ double_up(&old_dir->i_zombie, &new_dir->i_zombie);
if (error)
return error;
/* The following d_move() should become unconditional */
goto out;
error = -EBUSY;
- if (!list_empty(&dentry->d_hash))
+ if (!d_unhashed(dentry))
goto out;
error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
case 0x00:
DPRINTK("ncp renamed %s -> %s.\n",
old_dentry->d_name.name,new_dentry->d_name.name);
- /* d_move(old_dentry, new_dentry); */
break;
case 0x9E:
error = -ENAMETOOLONG;
static void ncp_put_inode(struct inode *);
static void ncp_delete_inode(struct inode *);
static void ncp_put_super(struct super_block *);
-static int ncp_statfs(struct super_block *, struct statfs *, int);
+static int ncp_statfs(struct super_block *, struct statfs *);
static struct super_operations ncp_sops =
{
MOD_DEC_USE_COUNT;
}
-static int ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int ncp_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
-
/* We cannot say how much disk space is left on a mounted
NetWare Server, because free space is distributed over
volumes, and the current user might have disk quotas. So
free space is not that simple to determine. Our decision
here is to err conservatively. */
- tmp.f_type = NCP_SUPER_MAGIC;
- tmp.f_bsize = NCP_BLOCK_SIZE;
- tmp.f_blocks = 0;
- tmp.f_bfree = 0;
- tmp.f_bavail = 0;
- tmp.f_files = -1;
- tmp.f_ffree = -1;
- tmp.f_namelen = 12;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = NCP_SUPER_MAGIC;
+ buf->f_bsize = NCP_BLOCK_SIZE;
+ buf->f_blocks = 0;
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_namelen = 12;
+ return 0;
}
int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
struct dentry * dentry = list_entry(tmp, struct dentry, d_alias);
const char * unhashed = "";
- if (list_empty(&dentry->d_hash))
+ if (d_unhashed(dentry))
unhashed = "(unhashed)";
printk("show_dentry: %s/%s, d_count=%d%s\n",
/*
* Unhash the dentry while we remove the file ...
*/
- if (!list_empty(&dentry->d_hash)) {
+ if (!d_unhashed(dentry)) {
d_drop(dentry);
rehash = 1;
}
* For files, make a copy of the dentry and then do a
* silly-rename. If the silly-rename succeeds, the
* copied dentry is hashed and becomes the new target.
- *
- * With directories check is done in VFS.
*/
+ if (!new_inode)
+ goto go_ahead;
error = -EBUSY;
- if (new_dentry->d_count > 1 && new_inode) {
+ if (S_ISDIR(new_inode->i_mode))
+ goto out;
+ else if (new_dentry->d_count > 1) {
int err;
/* copy the target dentry's name */
dentry = d_alloc(new_dentry->d_parent,
}
}
+go_ahead:
/*
* ... prune child dentries and writebacks if needed.
*/
shrink_dcache_parent(old_dentry);
}
- if (new_dentry->d_count > 1 && new_inode) {
-#ifdef NFS_PARANOIA
- printk("nfs_rename: new dentry %s/%s busy, d_count=%d\n",
- new_dentry->d_parent->d_name.name,
- new_dentry->d_name.name,
- new_dentry->d_count);
-#endif
- goto out;
- }
-
/*
* To prevent any new references to the target during the rename,
* we unhash the dentry and free the inode in advance.
*/
- if (!list_empty(&new_dentry->d_hash)) {
+ if (!d_unhashed(new_dentry)) {
d_drop(new_dentry);
rehash = 1;
}
static void nfs_delete_inode(struct inode *);
static void nfs_put_super(struct super_block *);
static void nfs_umount_begin(struct super_block *);
-static int nfs_statfs(struct super_block *, struct statfs *, int);
+static int nfs_statfs(struct super_block *, struct statfs *);
static struct super_operations nfs_sops = {
read_inode: nfs_read_inode,
}
static int
-nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+nfs_statfs(struct super_block *sb, struct statfs *buf)
{
int error;
struct nfs_fsinfo res;
- struct statfs tmp;
error = nfs_proc_statfs(&sb->u.nfs_sb.s_server, &sb->u.nfs_sb.s_root,
&res);
if (error) {
printk("nfs_statfs: statfs error = %d\n", -error);
- res.bsize = res.blocks = res.bfree = res.bavail = 0;
+ res.bsize = res.blocks = res.bfree = res.bavail = -1;
}
- tmp.f_type = NFS_SUPER_MAGIC;
- tmp.f_bsize = res.bsize;
- tmp.f_blocks = res.blocks;
- tmp.f_bfree = res.bfree;
- tmp.f_bavail = res.bavail;
- tmp.f_files = 0;
- tmp.f_ffree = 0;
- tmp.f_namelen = NAME_MAX;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = NFS_SUPER_MAGIC;
+ buf->f_bsize = res.bsize;
+ buf->f_blocks = res.blocks;
+ buf->f_bfree = res.bfree;
+ buf->f_bavail = res.bavail;
+ buf->f_namelen = NAME_MAX;
+ return 0;
}
/*
struct dentry *dentry = list_entry(tmp, struct dentry, d_alias);
dprintk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
- dentry->d_count, !list_empty(&dentry->d_hash));
+ dentry->d_count, !d_unhashed(dentry));
if (!list_empty(&dentry->d_subdirs))
shrink_dcache_parent(dentry);
if (!dentry->d_count) {
dput(dentry);
goto restart;
}
- if (list_empty(&dentry->d_hash))
+ if (d_unhashed(dentry))
unhashed++;
}
return unhashed;
static int hash_count = 0;
static DECLARE_WAIT_QUEUE_HEAD( hash_wait );
-#define READLOCK 0
-#define WRITELOCK 1
/*
* Find a client's export for a device.
* 1 = stale file handle
* 2 = other error
*/
- if (nfserr == 0)
+ switch (nfserr) {
+ case nfs_ok:
return 0;
- else if (nfserr == nfserr_stale)
+ case nfserr_stale:
return 1;
- else return 2;
+ default:
+ return 2;
+ }
}
static void
#define NFSDDBG_FACILITY NFSDDBG_PROC
-#define RETURN(st) { resp->status = (st); return (st); }
+#define RETURN_STATUS(st) { resp->status = (st); return (st); }
static int nfs3_ftypes[] = {
0, /* NF3NON */
fh_copy(&resp->fh, &argp->fh);
nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
fh_copy(&resp->fh, &argp->fh);
nfserr = nfsd_setattr(rqstp, &resp->fh, &argp->attrs);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
argp->name,
argp->len,
&resp->fh);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
fh_copy(&resp->fh, &argp->fh);
resp->access = argp->access;
nfserr = nfsd_access(rqstp, &resp->fh, &resp->access);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
fh_copy(&resp->fh, &argp->fh);
resp->len = NFS3_MAXPATHLEN;
nfserr = nfsd_readlink(rqstp, &resp->fh, (char *) path, &resp->len);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
resp->eof = (argp->offset + resp->count) >= inode->i_size;
}
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
argp->stable? " stable" : "");
fh_copy(&resp->fh, &argp->fh);
+ resp->committed = argp->stable;
nfserr = nfsd_write(rqstp, &resp->fh,
argp->offset,
argp->data,
argp->len,
- argp->stable);
- resp->committed = argp->stable;
+ &resp->committed);
resp->count = argp->count;
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
/* Get the directory inode */
nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_CREATE);
if (nfserr)
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
/* Unfudge the mode bits */
attr->ia_mode &= ~S_IFMT;
attr, newfhp,
argp->createmode, argp->verf);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
&argp->attrs, S_IFDIR, 0, &resp->fh);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
static int
nfserr = nfsd_symlink(rqstp, &resp->dirfh, argp->fname, argp->flen,
argp->tname, argp->tlen,
&resp->fh, &argp->attrs);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
fh_init(&resp->fh);
if (argp->ftype == 0 || argp->ftype >= NF3BAD)
- return nfserr_inval;
+ RETURN_STATUS(nfserr_inval);
if (argp->ftype == NF3CHR || argp->ftype == NF3BLK) {
if ((argp->ftype == NF3CHR && argp->major >= MAX_CHRDEV)
|| (argp->ftype == NF3BLK && argp->major >= MAX_BLKDEV)
|| argp->minor > 0xFF)
- return nfserr_inval;
+ RETURN_STATUS(nfserr_inval);
rdev = ((argp->major) << 8) | (argp->minor);
} else
if (argp->ftype != NF3SOCK && argp->ftype != NF3FIFO)
- return nfserr_inval;
+ RETURN_STATUS(nfserr_inval);
type = nfs3_ftypes[argp->ftype];
nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
&argp->attrs, type, rdev, &resp->fh);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
/* Unlink. -S_IFDIR means file must not be a directory */
fh_copy(&resp->fh, &argp->fh);
nfserr = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, argp->name, argp->len);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
fh_copy(&resp->fh, &argp->fh);
nfserr = nfsd_unlink(rqstp, &resp->fh, S_IFDIR, argp->name, argp->len);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
static int
fh_copy(&resp->tfh, &argp->tfh);
nfserr = nfsd_rename(rqstp, &resp->ffh, argp->fname, argp->flen,
&resp->tfh, argp->tname, argp->tlen);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
static int
fh_copy(&resp->tfh, &argp->tfh);
nfserr = nfsd_link(rqstp, &resp->tfh, argp->tname, argp->tlen,
&resp->fh);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
memcpy(resp->verf, argp->verf, 8);
resp->count = count;
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
memcpy(resp->verf, argp->verf, 8);
resp->count = count;
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
fh_put(&argp->fh);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
}
fh_put(&argp->fh);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
/*
}
fh_put(&argp->fh);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
(unsigned long) argp->offset);
if (argp->offset > NFS_OFFSET_MAX)
- return nfserr_inval;
+ RETURN_STATUS(nfserr_inval);
fh_copy(&resp->fh, &argp->fh);
nfserr = nfsd_commit(rqstp, &resp->fh, argp->offset, argp->count);
- RETURN(nfserr);
+ RETURN_STATUS(nfserr);
}
static void
nfsd_init(void)
{
- nfsd_xdr_init(); /* XDR */
nfsd_stat_init(); /* Statistics */
nfsd_cache_init(); /* RPC reply cache */
nfsd_export_init(); /* Exports table */
!(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
if (IS_ERR(dentry)) {
- error = nfserrno(-PTR_ERR(dentry));
+ error = nfserrno(PTR_ERR(dentry));
goto out;
}
#ifdef NFSD_PARANOIA
#define NFSDDBG_FACILITY NFSDDBG_PROC
-#define RETURN(st) return st
static void
svcbuf_reserve(struct svc_buf *buf, u32 **ptr, int *len, int nr)
static int
nfsd_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
{
- RETURN(nfs_ok);
+ return nfs_ok;
}
/*
SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh));
fh_copy(&resp->fh, &argp->fh);
- RETURN(fh_verify(rqstp, &resp->fh, 0, MAY_NOP));
+ return fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
}
/*
argp->attrs.ia_valid, (long) argp->attrs.ia_size);
fh_copy(&resp->fh, &argp->fh);
- RETURN(nfsd_setattr(rqstp, &resp->fh, &argp->attrs));
+ return nfsd_setattr(rqstp, &resp->fh, &argp->attrs);
}
/*
&resp->fh);
fh_put(&argp->fh);
- RETURN(nfserr);
+ return nfserr;
}
/*
nfserr = nfsd_readlink(rqstp, &argp->fh, (char *) path, &resp->len);
fh_put(&argp->fh);
- RETURN(nfserr);
+ return nfserr;
}
/*
(char *) buffer,
&resp->count);
- RETURN(nfserr);
+ return nfserr;
}
/*
struct nfsd_attrstat *resp)
{
int nfserr;
+ int stable = 1;
dprintk("nfsd: WRITE %d/%d %d bytes at %d\n",
SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
argp->offset,
argp->data,
argp->len,
- 0);
- RETURN(nfserr);
+ &stable);
+ return nfserr;
}
/*
done:
fh_put(dirfhp);
- RETURN(nfserr);
+ return nfserr;
}
static int
/* Unlink. -SIFDIR means file must not be a directory */
nfserr = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR, argp->name, argp->len);
fh_put(&argp->fh);
- RETURN(nfserr);
+ return nfserr;
}
static int
&argp->tfh, argp->tname, argp->tlen);
fh_put(&argp->ffh);
fh_put(&argp->tfh);
- RETURN(nfserr);
+ return nfserr;
}
static int
&argp->ffh);
fh_put(&argp->ffh);
fh_put(&argp->tfh);
- RETURN(nfserr);
+ return nfserr;
}
static int
fh_put(&argp->ffh);
fh_put(&newfh);
- RETURN(nfserr);
+ return nfserr;
}
/*
nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
&argp->attrs, S_IFDIR, 0, &resp->fh);
fh_put(&argp->fh);
- RETURN(nfserr);
+ return nfserr;
}
/*
nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len);
fh_put(&argp->fh);
- RETURN(nfserr);
+ return nfserr;
}
/*
resp->count = count;
fh_put(&argp->fh);
- RETURN(nfserr);
+ return nfserr;
}
/*
nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
fh_put(&argp->fh);
- RETURN(nfserr);
+ return nfserr;
}
/*
int nfserr;
int syserr;
} nfs_errtbl[] = {
- { NFS_OK, 0 },
- { NFSERR_PERM, EPERM },
- { NFSERR_NOENT, ENOENT },
- { NFSERR_IO, EIO },
- { NFSERR_NXIO, ENXIO },
- { NFSERR_ACCES, EACCES },
- { NFSERR_EXIST, EEXIST },
- { NFSERR_XDEV, EXDEV },
- { NFSERR_MLINK, EMLINK },
- { NFSERR_NODEV, ENODEV },
- { NFSERR_NOTDIR, ENOTDIR },
- { NFSERR_ISDIR, EISDIR },
- { NFSERR_INVAL, EINVAL },
- { NFSERR_FBIG, EFBIG },
- { NFSERR_NOSPC, ENOSPC },
- { NFSERR_ROFS, EROFS },
- { NFSERR_MLINK, EMLINK },
- { NFSERR_NAMETOOLONG, ENAMETOOLONG },
- { NFSERR_NOTEMPTY, ENOTEMPTY },
+ { nfs_ok, 0 },
+ { nfserr_perm, -EPERM },
+ { nfserr_noent, -ENOENT },
+ { nfserr_io, -EIO },
+ { nfserr_nxio, -ENXIO },
+ { nfserr_acces, -EACCES },
+ { nfserr_exist, -EEXIST },
+ { nfserr_xdev, -EXDEV },
+ { nfserr_mlink, -EMLINK },
+ { nfserr_nodev, -ENODEV },
+ { nfserr_notdir, -ENOTDIR },
+ { nfserr_isdir, -EISDIR },
+ { nfserr_inval, -EINVAL },
+ { nfserr_fbig, -EFBIG },
+ { nfserr_nospc, -ENOSPC },
+ { nfserr_rofs, -EROFS },
+ { nfserr_mlink, -EMLINK },
+ { nfserr_nametoolong, -ENAMETOOLONG },
+ { nfserr_notempty, -ENOTEMPTY },
#ifdef EDQUOT
- { NFSERR_DQUOT, EDQUOT },
+ { nfserr_dquot, -EDQUOT },
#endif
- { NFSERR_STALE, ESTALE },
- { -1, EIO }
+ { nfserr_stale, -ESTALE },
+ { -1, -EIO }
};
int i;
for (i = 0; nfs_errtbl[i].nfserr != -1; i++) {
if (nfs_errtbl[i].syserr == errno)
- return htonl(nfs_errtbl[i].nfserr);
+ return nfs_errtbl[i].nfserr;
}
printk (KERN_INFO "nfsd: non-standard errno: %d\n", errno);
return nfserr_io;
#define NFSDDBG_FACILITY NFSDDBG_XDR
-u32 nfs_ok, nfserr_perm, nfserr_noent, nfserr_io, nfserr_nxio,
- nfserr_acces, nfserr_exist, nfserr_xdev, nfserr_nodev,
- nfserr_notdir, nfserr_isdir, nfserr_inval, nfserr_fbig,
- nfserr_nospc, nfserr_rofs, nfserr_mlink,
- nfserr_nametoolong, nfserr_notempty, nfserr_dquot, nfserr_stale,
- nfserr_remote, nfserr_badhandle, nfserr_notsync,
- nfserr_badcookie, nfserr_notsupp, nfserr_toosmall,
- nfserr_serverfault, nfserr_badtype, nfserr_jukebox;
-
#ifdef NFSD_OPTIMIZE_SPACE
# define inline
NFSOCK, NFBAD, NFLNK, NFBAD,
};
-/*
- * Initialization of NFS status variables
- */
-void
-nfsd_xdr_init(void)
-{
- static int inited = 0;
-
- if (inited)
- return;
-
- nfs_ok = htonl(NFS_OK);
- nfserr_perm = htonl(NFSERR_PERM);
- nfserr_noent = htonl(NFSERR_NOENT);
- nfserr_io = htonl(NFSERR_IO);
- nfserr_inval = htonl(NFSERR_INVAL);
- nfserr_nxio = htonl(NFSERR_NXIO);
- nfserr_acces = htonl(NFSERR_ACCES);
- nfserr_exist = htonl(NFSERR_EXIST);
- nfserr_xdev = htonl(NFSERR_XDEV);
- nfserr_nodev = htonl(NFSERR_NODEV);
- nfserr_notdir = htonl(NFSERR_NOTDIR);
- nfserr_isdir = htonl(NFSERR_ISDIR);
- nfserr_inval = htonl(NFSERR_INVAL);
- nfserr_fbig = htonl(NFSERR_FBIG);
- nfserr_nospc = htonl(NFSERR_NOSPC);
- nfserr_rofs = htonl(NFSERR_ROFS);
- nfserr_mlink = htonl(NFSERR_MLINK);
- nfserr_nametoolong = htonl(NFSERR_NAMETOOLONG);
- nfserr_notempty = htonl(NFSERR_NOTEMPTY);
- nfserr_dquot = htonl(NFSERR_DQUOT);
- nfserr_stale = htonl(NFSERR_STALE);
- nfserr_remote = htonl(NFSERR_REMOTE);
- nfserr_badhandle = htonl(NFSERR_BADHANDLE);
- nfserr_notsync = htonl(NFSERR_NOT_SYNC);
- nfserr_badcookie = htonl(NFSERR_BAD_COOKIE);
- nfserr_notsupp = htonl(NFSERR_NOTSUPP);
- nfserr_toosmall = htonl(NFSERR_TOOSMALL);
- nfserr_serverfault = htonl(NFSERR_SERVERFAULT);
- nfserr_badtype = htonl(NFSERR_BADTYPE);
- nfserr_jukebox = htonl(NFSERR_JUKEBOX);
-
-
- inited = 1;
-}
/*
* XDR functions for basic NFS types
#include <linux/config.h>
#include <linux/version.h>
+#include <linux/string.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/locks.h>
* the check_parent in linux/fs/namei.c.
*/
#define nfsd_check_parent(dir, dentry) \
- ((dir) == (dentry)->d_parent && !list_empty(&dentry->d_hash))
+ ((dir) == (dentry)->d_parent && !d_unhashed(dentry))
/*
* Lock a parent directory following the VFS locking protocol.
return err;
out_nfserr:
- err = nfserrno(-PTR_ERR(dchild));
+ err = nfserrno(PTR_ERR(dchild));
goto out;
}
/* The size case is special. It changes the file as well as the attributes. */
if (iap->ia_valid & ATTR_SIZE) {
-if (!S_ISREG(inode->i_mode))
-printk("nfsd_setattr: size change??\n");
if (iap->ia_size < inode->i_size) {
err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE);
if (err)
return err;
out_nfserr:
- err = nfserrno(-err);
+ err = nfserrno(err);
goto out;
}
if (map->access & query) {
unsigned int err2;
err2 = nfsd_permission(export, dentry, map->how);
- /* cannot use a "switch" as nfserr_* are variables, even though they are constant :-( */
- if (err2 == 0)
+ switch (err2) {
+ case nfs_ok:
result |= map->access;
+ break;
+
/* the following error codes just mean the access was not allowed,
* rather than an error occurred */
- else if (err2 == nfserr_rofs ||
- err2 == nfserr_acces ||
- err2 == nfserr_perm
- )
+ case nfserr_rofs:
+ case nfserr_acces:
+ case nfserr_perm:
/* simply don't "or" in the access bit. */
- ;
- else {
+ break;
+ default:
error = err2;
goto out;
}
}
out_nfserr:
if (err)
- err = nfserrno(-err);
+ err = nfserrno(err);
out:
return err;
}
*count = err;
err = 0;
} else
- err = nfserrno(-err);
+ err = nfserrno(err);
out_close:
nfsd_close(&file);
out:
*/
int
nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
- char *buf, unsigned long cnt, int stable)
+ char *buf, unsigned long cnt, int *stablep)
{
struct svc_export *exp;
struct file file;
struct inode *inode;
mm_segment_t oldfs;
int err = 0;
+ int stable = *stablep;
#ifdef CONFIG_QUOTA
uid_t saved_euid;
#endif
* flushing the data to disk is handled separately below.
*/
#ifdef CONFIG_NFSD_V3
- if (rqstp->rq_vers == 2)
- stable = EX_ISSYNC(exp);
- else if (file.f_op->fsync == 0)
- stable = 1;
+ if (file.f_op->fsync == 0) {/* COMMIT3 cannot work */
+ stable = 2;
+ *stablep = 2; /* FILE_SYNC */
+ }
+ if (!EX_ISSYNC(exp))
+ stable = 0;
if (stable && !EX_WGATHER(exp))
file.f_flags |= O_SYNC;
#else
if (err >= 0)
err = 0;
else
- err = nfserrno(-err);
+ err = nfserrno(err);
out_close:
nfsd_close(&file);
out:
if ((err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file)) != 0)
return err;
-
- if (file.f_op && file.f_op->fsync) {
- nfsd_sync(&file);
- } else {
- err = nfserr_notsupp;
+ if (EX_ISSYNC(fhp->fh_export)) {
+ if (file.f_op && file.f_op->fsync) {
+ nfsd_sync(&file);
+ } else {
+ err = nfserr_notsupp;
+ }
}
nfsd_close(&file);
return err;
out_nfserr:
- err = nfserrno(-err);
+ err = nfserrno(err);
goto out;
}
return err;
out_nfserr:
- err = nfserrno(-err);
+ err = nfserrno(err);
goto out;
}
#endif /* CONFIG_NFSD_V3 */
return err;
out_nfserr:
- err = nfserrno(-err);
+ err = nfserrno(err);
goto out;
}
}
}
} else
- err = nfserrno(-err);
+ err = nfserrno(err);
fh_unlock(fhp);
/* Compose the fh so the dentry will be freed ... */
return err;
out_nfserr:
- err = nfserrno(-err);
+ err = nfserrno(err);
goto out;
}
if (err == -EXDEV && rqstp->rq_vers == 2)
err = nfserr_acces;
else
- err = nfserrno(-err);
+ err = nfserrno(err);
}
fh_unlock(ffhp);
return err;
out_nfserr:
- err = nfserrno(-err);
+ err = nfserrno(err);
goto out;
}
return err;
out_nfserr:
- err = nfserrno(-err);
+ err = nfserrno(err);
goto out;
}
goto out;
}
+ err = fh_lock_parent(fhp, rdentry);
+ if (err)
+ goto out;
- if (type != S_IFDIR) {
- /* It's UNLINK */
-
- err = fh_lock_parent(fhp, rdentry);
- if (err)
- goto out;
-
+ if (type != S_IFDIR) { /* It's UNLINK */
err = vfs_unlink(dirp, rdentry);
+ } else { /* It's RMDIR */
+ err = vfs_rmdir(dirp, rdentry);
+ }
- fh_unlock(fhp);
-
- dput(rdentry);
-
- } else {
- /* It's RMDIR */
- /* See comments in fs/namei.c:do_rmdir */
-
- rdentry->d_count++;
- nfsd_double_down(&dirp->i_sem, &rdentry->d_inode->i_sem);
-
-#ifdef CONFIG_NFSD_V3
- fill_pre_wcc(fhp);
-#else
- fhp->fh_locked = 1;
-#endif /* CONFIG_NFSD_V3 */
-
- err = -ENOENT;
- if (nfsd_check_parent(dentry, rdentry))
- err = vfs_rmdir(dirp, rdentry);
-
- rdentry->d_count--;
-#ifdef CONFIG_NFSD_V3
- fill_post_wcc(fhp);
-#else
- fhp->fh_locked = 0;
-#endif /* CONFIG_NFSD_V3 */
- nfsd_double_up(&dirp->i_sem, &rdentry->d_inode->i_sem);
+ fh_unlock(fhp);
- dput(rdentry);
- }
+ dput(rdentry);
if (err)
goto out_nfserr;
return err;
out_nfserr:
- err = nfserrno(-err);
+ err = nfserrno(err);
goto out;
}
return err;
out_nfserr:
- err = nfserrno(-err);
+ err = nfserrno(err);
goto out_close;
}
int
nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat)
{
- struct dentry *dentry;
- struct inode *inode;
- struct super_block *sb;
- mm_segment_t oldfs;
- int err;
-
- err = fh_verify(rqstp, fhp, 0, MAY_NOP);
- if (err)
- goto out;
- dentry = fhp->fh_dentry;
- inode = dentry->d_inode;
-
- err = nfserr_io;
- if (!(sb = inode->i_sb) || !sb->s_op->statfs)
- goto out;
-
- oldfs = get_fs();
- set_fs (KERNEL_DS);
- sb->s_op->statfs(sb, stat, sizeof(*stat));
- set_fs (oldfs);
- err = 0;
-
-out:
+ int err = fh_verify(rqstp, fhp, 0, MAY_NOP);
+ if (!err && vfs_statfs(fhp->fh_dentry->d_inode->i_sb,stat))
+ err = nfserr_io;
return err;
}
if (current->fsuid != 0)
current->cap_effective = saved_cap;
- return err? nfserrno(-err) : 0;
+ return err? nfserrno(err) : 0;
}
void
}
/* Called by the kernel when asking for stats */
-static int ntfs_statfs(struct super_block *sb, struct statfs *sf, int bufsize)
+static int ntfs_statfs(struct super_block *sb, struct statfs *sf)
{
- struct statfs fs;
struct inode *mft;
ntfs_volume *vol;
ntfs_u64 size;
ntfs_debug(DEBUG_OTHER, "ntfs_statfs\n");
vol=NTFS_SB2VOL(sb);
- memset(&fs,0,sizeof(fs));
- fs.f_type=NTFS_SUPER_MAGIC;
- fs.f_bsize=vol->clustersize;
+ sf->f_type=NTFS_SUPER_MAGIC;
+ sf->f_bsize=vol->clustersize;
error = ntfs_get_volumesize( NTFS_SB2VOL( sb ), &size );
if( error )
return -error;
- fs.f_blocks = size; /* volumesize is in clusters */
- fs.f_bfree=ntfs_get_free_cluster_count(vol->bitmap);
- fs.f_bavail=fs.f_bfree;
+ sf->f_blocks = size; /* volumesize is in clusters */
+ sf->f_bfree=ntfs_get_free_cluster_count(vol->bitmap);
+ sf->f_bavail=sf->f_bfree;
- /* Number of files is limited by free space only, so we lie here */
- fs.f_ffree=0;
mft=iget(sb,FILE_MFT);
if (!mft)
return -EIO;
/* So ... we lie... thus this following cast of loff_t value
is ok here.. */
- fs.f_files = (unsigned long)mft->i_size / vol->mft_recordsize;
+ sf->f_files = (unsigned long)mft->i_size / vol->mft_recordsize;
iput(mft);
/* should be read from volume */
- fs.f_namelen=255;
- copy_to_user(sf,&fs,bufsize);
+ sf->f_namelen=255;
return 0;
}
* Copyright (C) 1991, 1992 Linus Torvalds
*/
+#include <linux/string.h>
#include <linux/mm.h>
#include <linux/utime.h>
#include <linux/file.h>
dentry = namei(path);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
- struct inode * inode = dentry->d_inode;
- struct super_block * sb = inode->i_sb;
-
- error = -ENODEV;
- if (sb && sb->s_op && sb->s_op->statfs)
- error = sb->s_op->statfs(sb, buf, sizeof(struct statfs));
-
+ struct statfs tmp;
+ error = vfs_statfs(dentry->d_inode->i_sb, &tmp);
+ if (!error && copy_to_user(buf, &tmp, sizeof(struct statfs)))
+ error = -EFAULT;
dput(dentry);
}
unlock_kernel();
asmlinkage long sys_fstatfs(unsigned int fd, struct statfs * buf)
{
struct file * file;
- struct super_block * sb;
+ struct statfs tmp;
int error;
error = -EBADF;
file = fget(fd);
if (!file)
goto out;
- error = -ENODEV;
- sb = file->f_dentry->d_inode->i_sb;
lock_kernel();
- if (sb && sb->s_op && sb->s_op->statfs)
- error = sb->s_op->statfs(sb, buf, sizeof(struct statfs));
+ error = vfs_statfs(file->f_dentry->d_inode->i_sb, &tmp);
+ if (!error && copy_to_user(buf, &tmp, sizeof(struct statfs)))
+ error = -EFAULT;
unlock_kernel();
fput(file);
out:
MOD_DEC_USE_COUNT;
}
-static int openprom_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int openprom_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
-
- tmp.f_type = OPENPROM_SUPER_MAGIC;
- tmp.f_bsize = PAGE_SIZE/sizeof(long); /* ??? */
- tmp.f_blocks = 0;
- tmp.f_bfree = 0;
- tmp.f_bavail = 0;
- tmp.f_files = 0;
- tmp.f_ffree = 0;
- tmp.f_namelen = NAME_MAX;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = OPENPROM_SUPER_MAGIC;
+ buf->f_bsize = PAGE_SIZE/sizeof(long); /* ??? */
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = NAME_MAX;
+ return 0;
}
static struct super_operations openprom_sops = {
"State:\t%s\n"
"Pid:\t%d\n"
"PPid:\t%d\n"
+ "TracerPid:\t%d\n"
"Uid:\t%d\t%d\t%d\t%d\n"
"Gid:\t%d\t%d\t%d\t%d\n"
"FDSize:\t%d\n"
"Groups:\t",
get_task_state(p),
- p->pid, p->p_pptr->pid,
+ p->pid, p->p_opptr->pid, p->p_pptr->pid != p->p_opptr->pid ? p->p_opptr->pid : 0,
p->uid, p->euid, p->suid, p->fsuid,
p->gid, p->egid, p->sgid, p->fsgid,
p->files ? p->files->max_fds : 0);
task->pid,
task->comm,
state,
- task->p_pptr->pid,
+ task->p_opptr->pid,
task->pgrp,
task->session,
task->tty ? kdev_t_to_nr(task->tty->device) : 0,
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
}
-static int proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int proc_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
-
- tmp.f_type = PROC_SUPER_MAGIC;
- tmp.f_bsize = PAGE_SIZE/sizeof(long);
- tmp.f_blocks = 0;
- tmp.f_bfree = 0;
- tmp.f_bavail = 0;
- tmp.f_files = 0;
- tmp.f_ffree = 0;
- tmp.f_namelen = NAME_MAX;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = PROC_SUPER_MAGIC;
+ buf->f_bsize = PAGE_SIZE/sizeof(long);
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = NAME_MAX;
+ return 0;
}
static struct super_operations proc_sops = {
static void qnx4_put_super(struct super_block *sb);
static void qnx4_read_inode(struct inode *);
static int qnx4_remount(struct super_block *sb, int *flags, char *data);
-static int qnx4_statfs(struct super_block *, struct statfs *, int);
+static int qnx4_statfs(struct super_block *, struct statfs *);
static struct super_operations qnx4_sops =
{
return block;
}
-static int qnx4_statfs(struct super_block *sb,
- struct statfs *buf, int bufsize)
+static int qnx4_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
-
- memset(&tmp, 0, sizeof tmp);
- tmp.f_type = sb->s_magic;
- tmp.f_bsize = sb->s_blocksize;
- tmp.f_blocks = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_size) * 8;
- tmp.f_bfree = qnx4_count_free_blocks(sb);
- tmp.f_bavail = tmp.f_bfree;
- tmp.f_files = -1; /* we don't count files */
- tmp.f_ffree = -1; /* inodes are allocated dynamically */
- tmp.f_namelen = QNX4_NAME_MAX;
-
- return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0;
+ buf->f_type = sb->s_magic;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_size) * 8;
+ buf->f_bfree = qnx4_count_free_blocks(sb);
+ buf->f_bavail = buf->f_bfree;
+ buf->f_namelen = QNX4_NAME_MAX;
+
+ return 0;
}
/*
goto end_rmdir;
}
#endif
- if (!list_empty(&dentry->d_hash)) {
+ if (!d_unhashed(dentry)) {
retval = -EBUSY;
goto end_rmdir;
}
/* That's simple too. */
static int
-romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
+romfs_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
-
- memset(&tmp, 0, sizeof(tmp));
- tmp.f_type = ROMFS_MAGIC;
- tmp.f_bsize = ROMBSIZE;
- tmp.f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS;
- tmp.f_namelen = ROMFS_MAXFN;
- return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0;
+ buf->f_type = ROMFS_MAGIC;
+ buf->f_bsize = ROMBSIZE;
+ buf->f_bfree = buf->f_bavail = buf->f_ffree;
+ buf->f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS;
+ buf->f_namelen = ROMFS_MAXFN;
+ return 0;
}
/* some helper routines */
* Check that nobody else is using the directory..
*/
error = -EBUSY;
- if (!list_empty(&dentry->d_hash))
+ if (!d_unhashed(dentry))
goto out;
smb_invalid_dir_cache(dir);
static void smb_put_inode(struct inode *);
static void smb_delete_inode(struct inode *);
static void smb_put_super(struct super_block *);
-static int smb_statfs(struct super_block *, struct statfs *, int);
+static int smb_statfs(struct super_block *, struct statfs *);
static void smb_set_inode_attr(struct inode *, struct smb_fattr *);
static struct super_operations smb_sops =
}
static int
-smb_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+smb_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs attr;
+ smb_proc_dskattr(sb, buf);
- memset(&attr, 0, sizeof(attr));
-
- smb_proc_dskattr(sb, &attr);
-
- attr.f_type = SMB_SUPER_MAGIC;
- attr.f_files = -1;
- attr.f_ffree = -1;
- attr.f_namelen = SMB_MAXPATHLEN;
- return copy_to_user(buf, &attr, bufsiz) ? -EFAULT : 0;
+ buf->f_type = SMB_SUPER_MAGIC;
+ buf->f_namelen = SMB_MAXPATHLEN;
+ return 0;
}
int
*/
#include <linux/config.h>
+#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/locks.h>
#include <linux/smp_lock.h>
struct super_block *s;
struct ustat tmp;
struct statfs sbuf;
- mm_segment_t old_fs;
int err = -EINVAL;
lock_kernel();
s = get_super(to_kdev_t(dev));
if (s == NULL)
goto out;
- err = -ENOSYS;
- if (!(s->s_op->statfs))
- goto out;
-
- old_fs = get_fs();
- set_fs(get_ds());
- s->s_op->statfs(s,&sbuf,sizeof(struct statfs));
- set_fs(old_fs);
+ err = vfs_statfs(s, &sbuf);
+ if (err)
+ goto out;
memset(&tmp,0,sizeof(struct ustat));
tmp.f_tfree = sbuf.f_bfree;
static void sysv_put_super(struct super_block *);
static void sysv_write_super(struct super_block *);
static void sysv_read_inode(struct inode *);
-static int sysv_statfs(struct super_block *, struct statfs *, int);
+static int sysv_statfs(struct super_block *, struct statfs *);
static struct super_operations sysv_sops = {
read_inode: sysv_read_inode,
MOD_DEC_USE_COUNT;
}
-static int sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int sysv_statfs(struct super_block *sb, struct statfs *buf)
{
- struct statfs tmp;
-
- tmp.f_type = sb->s_magic; /* type of filesystem */
- tmp.f_bsize = sb->sv_block_size; /* block size */
- tmp.f_blocks = sb->sv_ndatazones; /* total data blocks in file system */
- tmp.f_bfree = sysv_count_free_blocks(sb); /* free blocks in fs */
- tmp.f_bavail = tmp.f_bfree; /* free blocks available to non-superuser */
- tmp.f_files = sb->sv_ninodes; /* total file nodes in file system */
- tmp.f_ffree = sysv_count_free_inodes(sb); /* free file nodes in fs */
- tmp.f_namelen = SYSV_NAMELEN;
- /* Don't know what value to put in tmp.f_fsid */ /* file system id */
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_type = sb->s_magic; /* type of filesystem */
+ buf->f_bsize = sb->sv_block_size; /* block size */
+ buf->f_blocks = sb->sv_ndatazones; /* total data blocks in file system */
+ buf->f_bfree = sysv_count_free_blocks(sb); /* free blocks in fs */
+ buf->f_bavail = buf->f_bfree; /* free blocks available to non-superuser */
+ buf->f_files = sb->sv_ninodes; /* total file nodes in file system */
+ buf->f_ffree = sysv_count_free_inodes(sb); /* free file nodes in fs */
+ buf->f_namelen = SYSV_NAMELEN;
+ /* Don't know what value to put in buf->f_fsid */ /* file system id */
+ return 0;
}
retval = -ENOTEMPTY;
goto end_rmdir;
}
- if (!list_empty(&dentry->d_hash)) {
+ if (!d_unhashed(dentry)) {
retval = -EBUSY;
goto end_rmdir;
}
}
if (S_ISDIR(old_inode->i_mode)) {
if (new_inode) {
+ retval = -EBUSY;
+ if (!d_unhashed(new_dentry))
+ goto end_rename;
retval = -ENOTEMPTY;
if (!empty_dir(new_inode))
goto end_rename;
if (new_inode)
{
+ retval = -EBUSY;
+ if (!d_unhashed(new_dentry))
+ goto end_rename;
retval = -ENOTEMPTY;
if (!empty_dir(new_inode))
goto end_rename;
static void udf_open_lvid(struct super_block *);
static void udf_close_lvid(struct super_block *);
static unsigned int udf_count_free(struct super_block *);
-static int udf_statfs(struct super_block *, struct statfs *, int);
+static int udf_statfs(struct super_block *, struct statfs *);
/* UDF filesystem type */
static struct file_system_type udf_fstype = {
* Written, tested, and released.
*/
static int
-udf_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
+udf_statfs(struct super_block *sb, struct statfs *buf)
{
- int size;
- struct statfs tmp;
- int rc;
-
- size = (bufsize < sizeof(tmp)) ? bufsize: sizeof(tmp);
-
- memset(&tmp, 0, sizeof(tmp));
- tmp.f_type = UDF_SUPER_MAGIC;
- tmp.f_bsize = sb->s_blocksize;
- tmp.f_blocks = UDF_SB_PARTLEN(sb, UDF_SB_PARTITION(sb));
- tmp.f_bfree = udf_count_free(sb);
- tmp.f_bavail = tmp.f_bfree;
- tmp.f_files = (UDF_SB_LVIDBH(sb) ?
+ buf->f_type = UDF_SUPER_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = UDF_SB_PARTLEN(sb, UDF_SB_PARTITION(sb));
+ buf->f_bfree = udf_count_free(sb);
+ buf->f_bavail = buf->f_bfree;
+ buf->f_files = (UDF_SB_LVIDBH(sb) ?
(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) +
- le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs)) : 0) + tmp.f_bfree;
- tmp.f_ffree = tmp.f_bfree;
+ le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs)) : 0) + buf->f_bfree;
+ buf->f_ffree = buf->f_bfree;
/* __kernel_fsid_t f_fsid */
- tmp.f_namelen = UDF_NAME_LEN;
+ buf->f_namelen = UDF_NAME_LEN;
- rc= copy_to_user(buf, &tmp, size) ? -EFAULT: 0;
- return rc;
+ return 0;
}
static unsigned char udf_bitmap_lookup[16] = {
return 0;
}
-int ufs_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
+int ufs_statfs (struct super_block * sb, struct statfs * buf)
{
struct ufs_sb_private_info * uspi;
struct ufs_super_block_first * usb1;
- struct statfs tmp;
unsigned swab;
swab = sb->u.ufs_sb.s_swab;
uspi = sb->u.ufs_sb.s_uspi;
usb1 = ubh_get_usb_first (USPI_UBH);
- tmp.f_type = UFS_MAGIC;
- tmp.f_bsize = sb->s_blocksize;
- tmp.f_blocks = uspi->s_dsize;
- tmp.f_bfree = ufs_blkstofrags(SWAB32(usb1->fs_cstotal.cs_nbfree)) +
+ buf->f_type = UFS_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = uspi->s_dsize;
+ buf->f_bfree = ufs_blkstofrags(SWAB32(usb1->fs_cstotal.cs_nbfree)) +
SWAB32(usb1->fs_cstotal.cs_nffree);
- tmp.f_bavail = (tmp.f_bfree > ((tmp.f_blocks / 100) * uspi->s_minfree))
- ? (tmp.f_bfree - ((tmp.f_blocks / 100) * uspi->s_minfree)) : 0;
- tmp.f_files = uspi->s_ncg * uspi->s_ipg;
- tmp.f_ffree = SWAB32(usb1->fs_cstotal.cs_nifree);
- tmp.f_namelen = UFS_MAXNAMLEN;
- return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+ buf->f_bavail = (buf->f_bfree > ((buf->f_blocks / 100) * uspi->s_minfree))
+ ? (buf->f_bfree - ((buf->f_blocks / 100) * uspi->s_minfree)) : 0;
+ buf->f_files = uspi->s_ncg * uspi->s_ipg;
+ buf->f_ffree = SWAB32(usb1->fs_cstotal.cs_nifree);
+ buf->f_namelen = UFS_MAXNAMLEN;
+ return 0;
}
static struct super_operations ufs_super_ops = {
goto out;
ret = -EBUSY;
- if (!list_empty(&dentry->d_hash))
+ if (!d_unhashed(dentry))
goto out;
/* check whether the EMD is empty */
goto out;
ret = -EBUSY;
- if (!list_empty(&dentry->d_hash))
+ if (!d_unhashed(dentry))
goto out;
ret = msdos_rmdir (dir, dentry);
tmp = next;
next = tmp->next;
alias = list_entry(tmp, struct dentry, d_alias);
- if (!list_empty(&alias->d_hash))
+ if (!d_unhashed(alias))
return dget(alias);
}
return NULL;
struct buffer_head *bh = NULL;
struct msdos_dir_entry *de;
- if (!list_empty(&dentry->d_hash))
+ if (!d_unhashed(dentry))
return -EBUSY;
res = fat_dir_empty(dentry->d_inode);
}
if (is_dir) {
+ res =-EBUSY;
+ if (!d_unhashed(new_dentry))
+ goto rename_done;
res = fat_dir_empty(new_inode);
if (res)
goto rename_done;
#include <linux/config.h>
#ifndef MAX_HWIFS
+# ifdef CONFIG_BLK_DEV_IDEPCI
#define MAX_HWIFS 10
+# else
+#define MAX_HWIFS 6
+# endif
#endif
#define ide__sti() __sti()
#include <linux/config.h>
#ifndef MAX_HWIFS
+# ifdef CONFIG_BLK_DEV_IDEPCI
#define MAX_HWIFS 10
+# else
+#define MAX_HWIFS 6
+# endif
#endif
#define ide__sti() __sti()
*/
static inline void ide_init_default_hwifs(void)
{
-#ifdef __DO_I_NEED_THIS
+#ifndef CONFIG_BLK_DEV_IDEPCI
hw_regs_t hw;
int index;
hw.irq = ide_default_irq(ide_default_io_base(index));
ide_register_hw(&hw, NULL);
}
-#endif /* __DO_I_NEED_THIS */
+#endif /* CONFIG_BLK_DEV_IDEPCI */
}
typedef union {
extern void print_properties(struct device_node *node);
extern int call_rtas(const char *service, int nargs, int nret,
unsigned long *outputs, ...);
+extern void prom_drawstring(const char *c);
+extern void prom_drawhex(unsigned long v);
+extern void prom_drawchar(char c);
*/
static __inline__ void ide_init_default_hwifs(void)
{
-#ifdef __DO_I_NEED_THIS
+#ifndef CONFIG_BLK_DEV_IDEPCI
hw_regs_t hw;
int index;
hw.irq = ide_default_irq(ide_default_io_base(index));
ide_register_hw(&hw, NULL);
}
-#endif /* __DO_I_NEED_THIS */
+#endif /* CONFIG_BLK_DEV_IDEPCI */
}
typedef union {
-/* $Id: system.h,v 1.80 1999/12/16 12:58:31 anton Exp $ */
+/* $Id: system.h,v 1.81 2000/02/28 04:00:44 anton Exp $ */
#include <linux/config.h>
#ifndef __SPARC_SYSTEM_H
#ifdef __SMP__
-/* This goes away after lockups have been found... */
-#ifndef DEBUG_IRQLOCK
-#define DEBUG_IRQLOCK
-#endif
-
extern unsigned char global_irq_holder;
#define save_and_cli(flags) do { save_flags(flags); cli(); } while(0)
-#ifdef DEBUG_IRQLOCK
extern void __global_cli(void);
extern void __global_sti(void);
extern unsigned long __global_save_flags(void);
#define sti() __global_sti()
#define save_flags(flags) ((flags)=__global_save_flags())
#define restore_flags(flags) __global_restore_flags(flags)
-#else
-
-#error For combined sun4[md] smp, we need to get rid of the rdtbr.
-
-/* Visit arch/sparc/lib/irqlock.S for all the fun details... */
-#define cli() __asm__ __volatile__("mov %%o7, %%g4\n\t" \
- "call ___f_global_cli\n\t" \
- " rd %%tbr, %%g7" : : \
- : "g1", "g2", "g3", "g4", "g5", "g7", \
- "memory", "cc")
-
-#define sti() \
-do { register unsigned long bits asm("g7"); \
- bits = 0; \
- __asm__ __volatile__("mov %%o7, %%g4\n\t" \
- "call ___f_global_sti\n\t" \
- " rd %%tbr, %%g2" \
- : /* no outputs */ \
- : "r" (bits) \
- : "g1", "g2", "g3", "g4", "g5", \
- "memory", "cc"); \
-} while(0)
-
-#define restore_flags(flags) \
-do { register unsigned long bits asm("g7"); \
- bits = flags; \
- __asm__ __volatile__("mov %%o7, %%g4\n\t" \
- "call ___f_global_restore_flags\n\t" \
- " andcc %%g7, 0x1, %%g0" \
- : "=&r" (bits) \
- : "0" (bits) \
- : "g1", "g2", "g3", "g4", "g5", \
- "memory", "cc"); \
-} while(0)
-
-#endif /* DEBUG_IRQLOCK */
#else
return dentry;
}
+static __inline__ int d_unhashed(struct dentry *dentry)
+{
+ return list_empty(&dentry->d_hash);
+}
+
extern void dput(struct dentry *);
#endif /* __KERNEL__ */
extern void cleanup_module(void);
extern struct super_block *efs_read_super(struct super_block *, void *, int);
extern void efs_put_super(struct super_block *);
-extern int efs_statfs(struct super_block *, struct statfs *, int);
+extern int efs_statfs(struct super_block *, struct statfs *);
extern void efs_read_inode(struct inode *);
extern efs_block_t efs_map_block(struct inode *, efs_block_t);
extern void ext2_write_super (struct super_block *);
extern int ext2_remount (struct super_block *, int *, char *);
extern struct super_block * ext2_read_super (struct super_block *,void *,int);
-extern int ext2_statfs (struct super_block *, struct statfs *, int);
+extern int ext2_statfs (struct super_block *, struct statfs *);
/* truncate.c */
extern void ext2_truncate (struct inode *);
int (*fb_rasterimg)(struct fb_info *info, int start);
};
+/* fb_info flags */
+#define FBINFO_FLAG_MODULE 1 /* Low-level driver is a module */
+#define FBINFO_FLAG_OPEN 2 /* Has this been open already ? */
+
struct fb_info {
char modename[40]; /* default video mode */
kdev_t node;
int flags;
- int open; /* Has this been open already ? */
-#define FBINFO_FLAG_MODULE 1 /* Low-level driver is a module */
+ int count; /* How many using the hardware */
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
struct fb_monspecs monspecs; /* Current Monitor specs */
unsigned long i_blocks;
unsigned long i_version;
struct semaphore i_sem;
+ struct semaphore i_zombie;
struct inode_operations *i_op;
struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct super_block *i_sb;
void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *);
void (*write_super) (struct super_block *);
- int (*statfs) (struct super_block *, struct statfs *, int);
+ int (*statfs) (struct super_block *, struct statfs *);
int (*remount_fs) (struct super_block *, int *, char *);
void (*clear_inode) (struct inode *);
void (*umount_begin) (struct super_block *);
extern int register_filesystem(struct file_system_type *);
extern int unregister_filesystem(struct file_system_type *);
+static inline int vfs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ if (!sb)
+ return -ENODEV;
+ if (!sb->s_op || !sb->s_op->statfs)
+ return -ENOSYS;
+ memset(buf, 0xff, sizeof(struct statfs));
+ return sb->s_op->statfs(sb, buf);
+}
+
/* Return value for VFS lock functions - tells locks.c to lock conventionally
* REALLY kosha for root NFS and nfs_lock
*/
extern struct dentry * open_namei(const char *, int, int);
extern struct dentry * do_mknod(const char *, int, dev_t);
extern int do_pipe(int *);
+extern int do_unlink(const char * name);
/* fs/dcache.c -- generic fs support functions */
extern int is_subdir(struct dentry *, struct dentry *);
* other process will be too late..
*/
#define check_parent(dir, dentry) \
- ((dir) == (dentry)->d_parent && !list_empty(&dentry->d_hash))
+ ((dir) == (dentry)->d_parent && !d_unhashed(dentry))
/*
* Locking the parent is needed to:
* Whee.. Deadlock country. Happily there are only two VFS
* operations that does this..
*/
-static inline void double_lock(struct dentry *d1, struct dentry *d2)
+static inline void double_down(struct semaphore *s1, struct semaphore *s2)
{
- struct semaphore *s1 = &d1->d_inode->i_sem;
- struct semaphore *s2 = &d2->d_inode->i_sem;
-
if (s1 != s2) {
if ((unsigned long) s1 < (unsigned long) s2) {
struct semaphore *tmp = s2;
down(s2);
}
-static inline void double_unlock(struct dentry *d1, struct dentry *d2)
+/*
+ * Ewwwwwwww... _triple_ lock. We are guaranteed that the 3rd argument is
+ * not equal to 1st and not equal to 2nd - the first case (target is parent of
+ * source) would be already caught, the second is plain impossible (target is
+ * its own parent and that case would be caught even earlier). Very messy.
+ * I _think_ that it works, but no warranties - please, look it through.
+ * Pox on bloody lusers who mandated overwriting rename() for directories...
+ */
+
+static inline void triple_down(struct semaphore *s1,
+ struct semaphore *s2,
+ struct semaphore *s3)
{
- struct semaphore *s1 = &d1->d_inode->i_sem;
- struct semaphore *s2 = &d2->d_inode->i_sem;
+ if (s1 != s2) {
+ if ((unsigned long) s1 < (unsigned long) s2) {
+ if ((unsigned long) s1 < (unsigned long) s3) {
+ struct semaphore *tmp = s3;
+ s3 = s1; s1 = tmp;
+ }
+ if ((unsigned long) s1 < (unsigned long) s2) {
+ struct semaphore *tmp = s2;
+ s2 = s1; s1 = tmp;
+ }
+ } else {
+ if ((unsigned long) s1 < (unsigned long) s3) {
+ struct semaphore *tmp = s3;
+ s3 = s1; s1 = tmp;
+ }
+ if ((unsigned long) s2 < (unsigned long) s3) {
+ struct semaphore *tmp = s3;
+ s3 = s2; s2 = tmp;
+ }
+ }
+ down(s1);
+ } else if ((unsigned long) s2 < (unsigned long) s3) {
+ struct semaphore *tmp = s3;
+ s3 = s2; s2 = tmp;
+ }
+ down(s2);
+ down(s3);
+}
+static inline void double_up(struct semaphore *s1, struct semaphore *s2)
+{
up(s1);
if (s1 != s2)
up(s2);
- dput(d1);
- dput(d2);
}
+static inline void triple_up(struct semaphore *s1,
+ struct semaphore *s2,
+ struct semaphore *s3)
+{
+ up(s1);
+ if (s1 != s2)
+ up(s2);
+ up(s3);
+}
+
+static inline void double_lock(struct dentry *d1, struct dentry *d2)
+{
+ double_down(&d1->d_inode->i_sem, &d2->d_inode->i_sem);
+}
+static inline void double_unlock(struct dentry *d1, struct dentry *d2)
+{
+ double_up(&d1->d_inode->i_sem,&d2->d_inode->i_sem);
+ dput(d1);
+ dput(d2);
+}
#endif /* __KERNEL__ */
#if (DISK_RECOVERY_TIME > 0)
unsigned long last_time; /* time when previous rq was done */
#endif
+ byte straight8; /* Alan's straight 8 check */
} ide_hwif_t;
/*
#ifdef __KERNEL__
+#define EC_HLEN 6
+
+/* This is what an Econet frame looks like on the wire. */
+struct ec_framehdr
+{
+ unsigned char dst_stn;
+ unsigned char dst_net;
+ unsigned char src_stn;
+ unsigned char src_net;
+ unsigned char cb;
+ unsigned char port;
+};
+
struct econet_opt
{
unsigned char cb;
unsigned char net;
};
+struct ec_device
+{
+ unsigned char station, net; /* Econet protocol address */
+};
+
+extern struct sock *ec_listening_socket(unsigned char port, unsigned char
+ station, unsigned char net);
+
#endif
#endif
#include <linux/nfs.h>
#include <linux/sunrpc/xdr.h>
-extern u32 nlm_granted, nlm_lck_denied, nlm_lck_denied_nolocks,
- nlm_lck_blocked, nlm_lck_denied_grace_period;
+#define NLM_MAXSTRLEN 1024
+
+#define QUADLEN(len) (((len) + 3) >> 2)
+
+#define nlm_granted __constant_htonl(NLM_LCK_GRANTED)
+#define nlm_lck_denied __constant_htonl(NLM_LCK_DENIED)
+#define nlm_lck_denied_nolocks __constant_htonl(NLM_LCK_DENIED_NOLOCKS)
+#define nlm_lck_blocked __constant_htonl(NLM_LCK_BLOCKED)
+#define nlm_lck_denied_grace_period __constant_htonl(NLM_LCK_DENIED_GRACE_PERIOD)
/* Lock info passed via NLM */
struct nlm_lock {
u32 fsm_mode;
};
+typedef struct nlm_args nlm_args;
+
/*
* Generic lockd result
*/
#include <linux/lockd/xdr.h>
/* error codes new to NLMv4 */
-extern u32 nlm4_deadlock, nlm4_rofs, nlm4_stale_fh, nlm4_fbig, nlm4_failed;
+#define nlm4_deadlock __constant_htonl(NLM_DEADLCK)
+#define nlm4_rofs __constant_htonl(NLM_ROFS)
+#define nlm4_stale_fh __constant_htonl(NLM_STALE_FH)
+#define nlm4_fbig __constant_htonl(NLM_FBIG)
+#define nlm4_failed __constant_htonl(NLM_FAILED)
+
int nlm4svc_decode_testargs(struct svc_rqst *, u32 *, struct nlm_args *);
extern struct inode *fat_build_inode(struct super_block*,struct msdos_dir_entry*,int,int*);
extern struct super_block *fat_read_super(struct super_block *s, void *data, int silent, struct inode_operations *dir_ops);
extern void msdos_put_super(struct super_block *sb);
-extern int fat_statfs(struct super_block *sb,struct statfs *buf, int);
+extern int fat_statfs(struct super_block *sb,struct statfs *buf);
extern void fat_write_inode(struct inode *inode);
/* dir.c */
int nfsd_read(struct svc_rqst *, struct svc_fh *,
loff_t, char *, unsigned long *);
int nfsd_write(struct svc_rqst *, struct svc_fh *,
- loff_t, char *, unsigned long, int);
+ loff_t, char *, unsigned long, int *);
int nfsd_readlink(struct svc_rqst *, struct svc_fh *,
char *, int *);
int nfsd_symlink(struct svc_rqst *, struct svc_fh *,
void nfsd_lockd_unexport(struct svc_client *);
-#ifndef makedev
-#define makedev(maj, min) (((maj) << 8) | (min))
-#endif
-
/*
- * These variables contain pre-xdr'ed values for faster operation.
- * FIXME: should be replaced by macros for big-endian machines.
+ * These macros provide pre-xdr'ed values for faster operation.
*/
-extern u32 nfs_ok,
- nfserr_perm,
- nfserr_noent,
- nfserr_io,
- nfserr_nxio,
- nfserr_acces,
- nfserr_exist,
- nfserr_xdev,
- nfserr_nodev,
- nfserr_notdir,
- nfserr_isdir,
- nfserr_inval,
- nfserr_fbig,
- nfserr_nospc,
- nfserr_rofs,
- nfserr_mlink,
- nfserr_nametoolong,
- nfserr_notempty,
- nfserr_dquot,
- nfserr_stale,
- nfserr_remote,
- nfserr_badhandle,
- nfserr_notsync,
- nfserr_badcookie,
- nfserr_notsupp,
- nfserr_toosmall,
- nfserr_serverfault,
- nfserr_badtype,
- nfserr_jukebox;
+#define nfs_ok __constant_htonl(NFS_OK)
+#define nfserr_perm __constant_htonl(NFSERR_PERM)
+#define nfserr_noent __constant_htonl(NFSERR_NOENT)
+#define nfserr_io __constant_htonl(NFSERR_IO)
+#define nfserr_nxio __constant_htonl(NFSERR_NXIO)
+#define nfserr_acces __constant_htonl(NFSERR_ACCES)
+#define nfserr_exist __constant_htonl(NFSERR_EXIST)
+#define nfserr_xdev __constant_htonl(NFSERR_XDEV)
+#define nfserr_nodev __constant_htonl(NFSERR_NODEV)
+#define nfserr_notdir __constant_htonl(NFSERR_NOTDIR)
+#define nfserr_isdir __constant_htonl(NFSERR_ISDIR)
+#define nfserr_inval __constant_htonl(NFSERR_INVAL)
+#define nfserr_fbig __constant_htonl(NFSERR_FBIG)
+#define nfserr_nospc __constant_htonl(NFSERR_NOSPC)
+#define nfserr_rofs __constant_htonl(NFSERR_ROFS)
+#define nfserr_mlink __constant_htonl(NFSERR_MLINK)
+#define nfserr_nametoolong __constant_htonl(NFSERR_NAMETOOLONG)
+#define nfserr_notempty __constant_htonl(NFSERR_NOTEMPTY)
+#define nfserr_dquot __constant_htonl(NFSERR_DQUOT)
+#define nfserr_stale __constant_htonl(NFSERR_STALE)
+#define nfserr_remote __constant_htonl(NFSERR_REMOTE)
+#define nfserr_badhandle __constant_htonl(NFSERR_BADHANDLE)
+#define nfserr_notsync __constant_htonl(NFSERR_NOTSYNC)
+#define nfserr_badcookie __constant_htonl(NFSERR_BADCOOKIE)
+#define nfserr_notsupp __constant_htonl(NFSERR_NOTSUPP)
+#define nfserr_toosmall __constant_htonl(NFSERR_TOOSMALL)
+#define nfserr_serverfault __constant_htonl(NFSERR_SERVERFAULT)
+#define nfserr_badtype __constant_htonl(NFSERR_BADTYPE)
+#define nfserr_jukebox __constant_htonl(NFSERR_JUKEBOX)
/*
* Time of server startup
#define NFSSVC_XDRSIZE sizeof(union nfsd_xdrstore)
-void nfsd_xdr_init(void);
int nfssvc_decode_void(struct svc_rqst *, u32 *, void *);
int nfssvc_decode_fhandle(struct svc_rqst *, u32 *, struct svc_fh *);
#define PCI_DEVICE_ID_TTI_HPT366 0x0004
#define PCI_VENDOR_ID_VIA 0x1106
+#define PCI_DEVICE_ID_VIA_8371_0 0x0391
#define PCI_DEVICE_ID_VIA_8501_0 0x0501
+#define PCI_DEVICE_ID_VIA_8601_0 0x0601
#define PCI_DEVICE_ID_VIA_82C505 0x0505
#define PCI_DEVICE_ID_VIA_82C561 0x0561
#define PCI_DEVICE_ID_VIA_82C586_1 0x0571
#define PCI_DEVICE_ID_VIA_82C686_5 0x3058
#define PCI_DEVICE_ID_VIA_82C686_6 0x3068
#define PCI_DEVICE_ID_VIA_86C100A 0x6100
+#define PCI_DEVICE_ID_VIA_8231 0x8231
+#define PCI_DEVICE_ID_VIA_8371_1 0x8391
#define PCI_DEVICE_ID_VIA_8501_1 0x8501
#define PCI_DEVICE_ID_VIA_82C597_1 0x8597
#define PCI_DEVICE_ID_VIA_82C598_1 0x8598
+#define PCI_DEVICE_ID_VIA_8601_1 0x8601
#define PCI_VENDOR_ID_SMC2 0x1113
#define PCI_DEVICE_ID_SMC2_1211TX 0x1211
*/
#define SHMMAX 0x2000000 /* max shared seg size (bytes) */
-#define SHMMIN 1 /* really PAGE_SIZE */ /* min shared seg size (bytes) */
+#define SHMMIN 0 /* min shared seg size (bytes) */
#define SHMMNI 128 /* max num of segs system wide */
#define SHMALL (SHMMAX/PAGE_SIZE*SHMMNI) /* max shm system wide (pages) */
#define SHMSEG SHMMNI /* max shared segs per process */
KERN_SPARC_STOP_A=44, /* int: Sparc Stop-A enable */
KERN_SHMMNI=45, /* int: shm array identifiers */
KERN_OVERFLOWUID=46, /* int: overflow UID */
- KERN_OVERFLOWGID=47 /* int: overflow GID */
+ KERN_OVERFLOWGID=47, /* int: overflow GID */
+ KERN_SHMPATH=48, /* string: path to shm fs */
};
+++ /dev/null
-#ifndef _NET_DN_RAW_H
-#define _NET_DN_RAW_H
-
-#include <linux/config.h>
-
-#ifdef CONFIG_DECNET_RAW
-
-extern struct proto_ops dn_raw_proto_ops;
-
-extern void dn_raw_rx_nsp(struct sk_buff *skb);
-extern void dn_raw_rx_routing(struct sk_buff *skb);
-
-#ifdef CONFIG_DECNET_MOP
-extern void dn_raw_rx_mop(struct sk_buff *skb);
-#endif /* CONFIG_DECNET_MOP */
-
-#endif /* CONFIG_DECNET_RAW */
-
-#endif /* _NET_DN_RAW_H */
#if HZ == 20
# define TCP_TW_RECYCLE_TICK (5+2-TCP_TW_RECYCLE_SLOTS_LOG)
+#elif HZ == 64
+# define TCP_TW_RECYCLE_TICK (6+2-TCP_TW_RECYCLE_SLOTS_LOG)
#elif HZ == 100 || HZ == 128
# define TCP_TW_RECYCLE_TICK (7+2-TCP_TW_RECYCLE_SLOTS_LOG)
-#elif HZ == 1024
+#elif HZ == 1024 || HZ == 1000
# define TCP_TW_RECYCLE_TICK (10+2-TCP_TW_RECYCLE_SLOTS_LOG)
#else
-# error HZ != 20 && HZ != 100 && HZ != 1024.
+# error HZ != 20 && HZ != 64 && HZ != 100 && HZ != 1000 && HZ != 1024
#endif
/*
* avoid vmalloc and make shmmax, shmall, shmmni sysctl'able,
* Christoph Rohland <hans-christoph.rohland@sap.com>
* Shared /dev/zero support, Kanoj Sarcar <kanoj@sgi.com>
+ * make it a file system, Christoph Rohland <hans-christoph.rohland@sap.com>
+ *
+ * The filesystem has the following restrictions/bugs:
+ * 1) It only can handle one directory.
+ * 2) Because the directory is represented by the SYSV shm array it
+ * can only be mounted one time.
+ * 3) This again leads to SYSV shm not working properly in a chrooted
+ * environment
+ * 4) Read and write are not implemented (should they?)
+ * 5) No special nodes are supported
*/
#include <linux/config.h>
#include <linux/swap.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
+#include <linux/locks.h>
+#include <linux/file.h>
+#include <linux/mman.h>
#include <linux/vmalloc.h>
#include <linux/pagemap.h>
#include <linux/proc_fs.h>
#include "util.h"
+static struct super_block *shm_read_super(struct super_block *,void *, int);
+static void shm_put_super (struct super_block *);
+static int shm_remount_fs (struct super_block *, int *, char *);
+static void shm_read_inode (struct inode *);
+static void shm_write_inode(struct inode *);
+static int shm_statfs (struct super_block *, struct statfs *, int);
+static int shm_create (struct inode *,struct dentry *,int);
+static struct dentry *shm_lookup (struct inode *,struct dentry *);
+static int shm_unlink (struct inode *,struct dentry *);
+static int shm_setattr (struct dentry *dent, struct iattr *attr);
+static void shm_delete (struct inode *);
+static int shm_mmap (struct file *, struct vm_area_struct *);
+static int shm_readdir (struct file *, void *, filldir_t);
+
+char shm_path[256] = "/var/shm";
+
+#define SHM_NAME_LEN NAME_MAX
+#define SHM_FMT ".IPC_%08x"
+#define SHM_FMT_LEN 13
+
struct shmid_kernel /* private to the kernel */
{
struct kern_ipc_perm shm_perm;
size_t shm_segsz;
- time_t shm_atime;
- time_t shm_dtime;
- time_t shm_ctime;
- pid_t shm_cpid;
- pid_t shm_lpid;
unsigned long shm_nattch;
unsigned long shm_npages; /* size of segment (pages) */
- pte_t **shm_dir; /* ptr to array of ptrs to frames -> SHMMAX */
- struct vm_area_struct *attaches; /* descriptors for attaches */
- int id; /* backreference to id for shm_close */
- struct semaphore sem;
+ pte_t **shm_dir; /* ptr to arr of ptrs to frames */
+ int id;
+ union permap {
+ struct shmem {
+ time_t atime;
+ time_t dtime;
+ time_t ctime;
+ pid_t cpid;
+ pid_t lpid;
+ int nlen;
+ char nm[0];
+ } shmem;
+ struct zero {
+ struct semaphore sema;
+ struct list_head list;
+ } zero;
+ } permap;
};
+#define shm_atim permap.shmem.atime
+#define shm_dtim permap.shmem.dtime
+#define shm_ctim permap.shmem.ctime
+#define shm_cprid permap.shmem.cpid
+#define shm_lprid permap.shmem.lpid
+#define shm_namelen permap.shmem.nlen
+#define shm_name permap.shmem.nm
+#define zsem permap.zero.sema
+#define zero_list permap.zero.list
+
static struct ipc_ids shm_ids;
#define shm_lock(id) ((struct shmid_kernel*)ipc_lock(&shm_ids,id))
#define shm_buildid(id, seq) \
ipc_buildid(&shm_ids, id, seq)
-static int newseg (key_t key, int shmflg, size_t size);
-static int shm_map (struct vm_area_struct *shmd);
-static void killseg (int shmid);
+static int newseg (key_t key, const char *name, int namelen, int shmflg, size_t size);
+static void killseg_core(struct shmid_kernel *shp, int doacc);
static void shm_open (struct vm_area_struct *shmd);
static void shm_close (struct vm_area_struct *shmd);
static struct page * shm_nopage(struct vm_area_struct *, unsigned long, int);
static void zmap_unuse(swp_entry_t entry, struct page *page);
static void shmzero_open(struct vm_area_struct *shmd);
static void shmzero_close(struct vm_area_struct *shmd);
+static struct page *shmzero_nopage(struct vm_area_struct * shmd, unsigned long address, int no_share);
static int zero_id;
static struct shmid_kernel zshmid_kernel;
+static struct dentry *zdent;
+
+#define SHM_FS_MAGIC 0x02011994
+
+static struct super_block * shm_sb;
+
+static struct file_system_type shm_fs_type = {
+ "shm",
+ 0,
+ shm_read_super,
+ NULL
+};
+
+static struct super_operations shm_sops = {
+ read_inode: shm_read_inode,
+ write_inode: shm_write_inode,
+ delete_inode: shm_delete,
+ put_super: shm_put_super,
+ statfs: shm_statfs,
+ remount_fs: shm_remount_fs,
+};
+
+static struct file_operations shm_root_operations = {
+ readdir: shm_readdir,
+};
+
+static struct inode_operations shm_root_inode_operations = {
+ create: shm_create,
+ lookup: shm_lookup,
+ unlink: shm_unlink,
+};
+
+static struct file_operations shm_file_operations = {
+ mmap: shm_mmap,
+};
+
+static struct inode_operations shm_inode_operations = {
+ setattr: shm_setattr,
+};
+
+static struct vm_operations_struct shm_vm_ops = {
+ open: shm_open, /* callback for a new vm-area open */
+ close: shm_close, /* callback for when the vm-area is released */
+ nopage: shm_nopage,
+ swapout:shm_swapout,
+};
size_t shm_ctlmax = SHMMAX;
-int shm_ctlall = SHMALL;
-int shm_ctlmni = SHMMNI;
+
+/* These parameters should be part of the superblock */
+static int shm_ctlall;
+static int shm_ctlmni;
+static int shm_mode;
static int shm_tot = 0; /* total number of shared memory pages */
static int shm_rss = 0; /* number of shared memory pages that are in memory */
pagecache_lock
shm_lock()/shm_lockall()
kernel lock
- shp->sem
+ inode->i_sem
sem_ids.sem
mmap_sem
/* some statistics */
static ulong swap_attempts = 0;
static ulong swap_successes = 0;
+static ulong used_segs = 0;
void __init shm_init (void)
{
- ipc_init_ids(&shm_ids, shm_ctlmni);
+ ipc_init_ids(&shm_ids, 1);
+
+ register_filesystem (&shm_fs_type);
#ifdef CONFIG_PROC_FS
create_proc_read_entry("sysvipc/shm", 0, 0, sysvipc_shm_read_proc, NULL);
#endif
- zero_id = ipc_addid(&shm_ids, &zshmid_kernel.shm_perm, shm_ctlmni);
+ zero_id = ipc_addid(&shm_ids, &zshmid_kernel.shm_perm, 1);
shm_unlock(zero_id);
+ zdent = d_alloc_root(get_empty_inode());
return;
}
+static int shm_parse_options(char *options)
+{
+ int blocks = shm_ctlall;
+ int inodes = shm_ctlmni;
+ umode_t mode = shm_mode;
+ char *this_char, *value;
+
+ this_char = NULL;
+ if ( options )
+ this_char = strtok(options,",");
+ for ( ; this_char; this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL)
+ *value++ = 0;
+ if (!strcmp(this_char,"nr_blocks")) {
+ if (!value || !*value)
+ return 1;
+ blocks = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"nr_inodes")) {
+ if (!value || !*value)
+ return 1;
+ inodes = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"mode")) {
+ if (!value || !*value)
+ return 1;
+ mode = simple_strtoul(value,&value,8);
+ if (*value)
+ return 1;
+ }
+ else
+ return 1;
+ }
+ shm_ctlmni = inodes;
+ shm_ctlall = blocks;
+ shm_mode = mode;
+
+ return 0;
+}
+
+static struct super_block *shm_read_super(struct super_block *s,void *data,
+ int silent)
+{
+ struct inode * root_inode;
+
+ if (shm_sb) {
+ printk ("shm fs already mounted\n");
+ return NULL;
+ }
+
+ lock_super(s);
+ shm_ctlall = SHMALL;
+ shm_ctlmni = SHMMNI;
+ shm_mode = S_IRWXUGO | S_ISVTX;
+ if (shm_parse_options (data)) {
+ printk ("shm fs invalid option\n");
+ goto out_unlock;
+ }
+
+ s->s_blocksize = PAGE_SIZE;
+ s->s_blocksize_bits = PAGE_SHIFT;
+ s->s_magic = SHM_FS_MAGIC;
+ s->s_op = &shm_sops;
+ root_inode = iget (s, SEQ_MULTIPLIER);
+ if (!root_inode)
+ goto out_no_root;
+ root_inode->i_op = &shm_root_inode_operations;
+ root_inode->i_sb = s;
+ root_inode->i_nlink = 2;
+ root_inode->i_mode = S_IFDIR | shm_mode;
+ s->s_root = d_alloc_root(root_inode);
+ if (!s->s_root)
+ goto out_no_root;
+ s->u.generic_sbp = (void*) shm_sb;
+ shm_sb = s;
+ unlock_super(s);
+ return s;
+
+out_no_root:
+ printk("proc_read_super: get root inode failed\n");
+ iput(root_inode);
+out_unlock:
+ s->s_dev = 0;
+ unlock_super(s);
+ return NULL;
+}
+
+static int shm_remount_fs (struct super_block *sb, int *flags, char *data)
+{
+ if (shm_parse_options (data))
+ return -EINVAL;
+ return 0;
+}
+
+static void shm_put_super(struct super_block *sb)
+{
+ struct super_block **p = &shm_sb;
+ int i;
+ struct shmid_kernel *shp;
+
+ while (*p != sb) {
+ if (!*p) /* should never happen */
+ return;
+ p = (struct super_block **)&(*p)->u.generic_sbp;
+ }
+ *p = (struct super_block *)(*p)->u.generic_sbp;
+ down(&shm_ids.sem);
+ for(i = 0; i <= shm_ids.max_id; i++) {
+ if (i == zero_id)
+ continue;
+ if (!(shp = shm_lock (i)))
+ continue;
+ if (shp->shm_nattch)
+ printk ("shm_nattch = %ld\n", shp->shm_nattch);
+ shp = shm_rmid(i);
+ shm_unlock(i);
+ killseg_core(shp, 1);
+ }
+ dput (sb->s_root);
+ up(&shm_ids.sem);
+}
+
+static int shm_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+ struct statfs tmp;
+
+ tmp.f_type = 0;
+ tmp.f_bsize = PAGE_SIZE;
+ tmp.f_blocks = shm_ctlall;
+ tmp.f_bavail = tmp.f_bfree = shm_ctlall - shm_tot;
+ tmp.f_files = shm_ctlmni;
+ tmp.f_ffree = shm_ctlmni - used_segs;
+ tmp.f_namelen = SHM_NAME_LEN;
+ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+}
+
+static void shm_write_inode(struct inode * inode)
+{
+}
+
+static void shm_read_inode(struct inode * inode)
+{
+ int id;
+ struct shmid_kernel *shp;
+
+ id = inode->i_ino;
+ inode->i_op = NULL;
+ inode->i_mode = 0;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+
+ if (id < SEQ_MULTIPLIER) {
+ if (!(shp = shm_lock (id)))
+ return;
+ inode->i_mode = shp->shm_perm.mode | S_IFREG;
+ inode->i_uid = shp->shm_perm.uid;
+ inode->i_gid = shp->shm_perm.gid;
+ inode->i_size = shp->shm_segsz;
+ shm_unlock (id);
+ inode->i_op = &shm_inode_operations;
+ inode->i_fop = &shm_file_operations;
+ return;
+ }
+ inode->i_op = &shm_root_inode_operations;
+ inode->i_fop = &shm_root_operations;
+ inode->i_sb = shm_sb;
+ inode->i_nlink = 2;
+ inode->i_mode = S_IFDIR | shm_mode;
+ inode->i_uid = inode->i_gid = 0;
+
+}
+
+static int shm_create (struct inode *dir, struct dentry *dent, int mode)
+{
+ int id, err;
+ struct inode * inode;
+
+ down(&shm_ids.sem);
+ err = id = newseg (IPC_PRIVATE, dent->d_name.name, dent->d_name.len, mode, 0);
+ if (err < 0)
+ goto out;
+
+ err = -ENOMEM;
+ inode = iget (shm_sb, id % SEQ_MULTIPLIER);
+ if (!inode)
+ goto out;
+
+ err = 0;
+ down (&inode->i_sem);
+ inode->i_mode = mode | S_IFREG;
+ inode->i_op = &shm_inode_operations;
+ d_instantiate(dent, inode);
+ up (&inode->i_sem);
+
+out:
+ up(&shm_ids.sem);
+ return err;
+}
+
+static int shm_readdir (struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode * inode = filp->f_dentry->d_inode;
+ struct shmid_kernel *shp;
+ off_t nr;
+
+ nr = filp->f_pos;
+
+ switch(nr)
+ {
+ case 0:
+ if (filldir(dirent, ".", 1, nr, inode->i_ino) < 0)
+ return 0;
+ filp->f_pos = ++nr;
+ /* fall through */
+ case 1:
+ if (filldir(dirent, "..", 2, nr, inode->i_ino) < 0)
+ return 0;
+ filp->f_pos = ++nr;
+ /* fall through */
+ default:
+ down(&shm_ids.sem);
+ for (; nr-2 <= shm_ids.max_id; nr++ ) {
+ if (!(shp = shm_get (nr-2)))
+ continue;
+ if (shp->shm_perm.mode & SHM_DEST)
+ continue;
+ if (filldir(dirent, shp->shm_name, shp->shm_namelen, nr, nr) < 0 )
+ break;;
+ }
+ filp->f_pos = nr;
+ up(&shm_ids.sem);
+ break;
+ }
+
+ UPDATE_ATIME(inode);
+ return 0;
+}
+
+static struct dentry *shm_lookup (struct inode *dir, struct dentry *dent)
+{
+ int i, err = 0;
+ struct shmid_kernel* shp;
+ struct inode *inode = NULL;
+
+ if (dent->d_name.len > SHM_NAME_LEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ down(&shm_ids.sem);
+ for(i = 0; i <= shm_ids.max_id; i++) {
+ if (!(shp = shm_lock(i)))
+ continue;
+ if (!(shp->shm_perm.mode & SHM_DEST) &&
+ dent->d_name.len == shp->shm_namelen &&
+ strncmp(dent->d_name.name, shp->shm_name, shp->shm_namelen) == 0)
+ goto found;
+ shm_unlock(i);
+ }
+
+ /*
+ * prevent the reserved names as negative dentries.
+ * This also prevents object creation through the filesystem
+ */
+ if (dent->d_name.len == SHM_FMT_LEN &&
+ memcmp (SHM_FMT, dent->d_name.name, SHM_FMT_LEN - 8) == 0)
+ err = -EINVAL; /* EINVAL to give IPC_RMID the right error */
+
+ goto out;
+
+found:
+ shm_unlock(i);
+ inode = iget(dir->i_sb, i);
+
+ if (!inode)
+ err = -EACCES;
+out:
+ if (err == 0)
+ d_add (dent, inode);
+ up (&shm_ids.sem);
+ return ERR_PTR(err);
+}
+
+static int shm_unlink (struct inode *dir, struct dentry *dent)
+{
+ struct inode * inode = dent->d_inode;
+ struct shmid_kernel *shp;
+
+ down (&shm_ids.sem);
+ if (!(shp = shm_lock (inode->i_ino)))
+ BUG();
+ shp->shm_perm.mode |= SHM_DEST;
+ shp->shm_perm.key = IPC_PRIVATE; /* Do not find it any more */
+ shm_unlock (inode->i_ino);
+ up (&shm_ids.sem);
+ inode->i_nlink -= 1;
+ d_delete (dent);
+ return 0;
+}
+
#define SHM_ENTRY(shp, index) (shp)->shm_dir[(index)/PTRS_PER_PTE][(index)%PTRS_PER_PTE]
static pte_t **shm_alloc(unsigned long pages)
unsigned short last = pages % PTRS_PER_PTE;
pte_t **ret, **ptr;
+ if (pages == 0)
+ return NULL;
+
ret = kmalloc ((dir+1) * sizeof(pte_t *), GFP_KERNEL);
if (!ret)
- goto out;
+ goto nomem;
for (ptr = ret; ptr < ret+dir ; ptr++)
{
goto free;
memset (*ptr, 0, last*sizeof(pte_t));
}
-out:
return ret;
free:
free_page ((unsigned long)*ptr);
kfree (ret);
- return NULL;
+nomem:
+ return ERR_PTR(-ENOMEM);
}
-
static void shm_free(pte_t** dir, unsigned long pages)
{
pte_t **ptr = dir+pages/PTRS_PER_PTE;
+ if (!dir)
+ return;
+
/* first the last page */
if (pages%PTRS_PER_PTE)
kfree (*ptr);
/* now the whole pages */
while (--ptr >= dir)
- free_page ((unsigned long)*ptr);
+ if (*ptr)
+ free_page ((unsigned long)*ptr);
/* Now the indirect block */
kfree (dir);
}
-static int shm_revalidate(struct shmid_kernel* shp, int shmid, int pagecount, int flg)
+static int shm_setattr (struct dentry *dentry, struct iattr *attr)
{
- struct shmid_kernel* new;
- new = shm_lock(shmid);
- if(new==NULL) {
- return -EIDRM;
- }
- if(new!=shp || shm_checkid(shp, shmid) || shp->shm_npages != pagecount) {
- shm_unlock(shmid);
- return -EIDRM;
- }
- if (ipcperms(&shp->shm_perm, flg)) {
- shm_unlock(shmid);
- return -EACCES;
+ int error;
+ struct inode *inode = dentry->d_inode;
+ struct shmid_kernel *shp;
+ unsigned long new_pages, old_pages;
+ pte_t **new_dir, **old_dir;
+
+ if ((error = inode_change_ok(inode, attr)))
+ return error;
+ if (!(attr->ia_valid & ATTR_SIZE))
+ goto set_attr;
+ if (attr->ia_size > shm_ctlmax)
+ return -EFBIG;
+
+ /* We set old_pages and old_dir for easier cleanup */
+ old_pages = new_pages = (attr->ia_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ if (shm_tot + new_pages >= shm_ctlall)
+ return -ENOSPC;
+ if (IS_ERR(old_dir = new_dir = shm_alloc(new_pages)))
+ return PTR_ERR(new_dir);
+
+ if (!(shp = shm_lock(inode->i_ino)))
+ BUG();
+ if (shp->shm_segsz == attr->ia_size)
+ goto out;
+ old_dir = shp->shm_dir;
+ old_pages = shp->shm_npages;
+ if (old_dir){
+ pte_t *swap;
+ int i,j;
+ i = old_pages < new_pages ? old_pages : new_pages;
+ j = i % PTRS_PER_PTE;
+ i /= PTRS_PER_PTE;
+ if (j)
+ memcpy (new_dir[i], old_dir[i], j * sizeof (pte_t));
+ while (i--) {
+ swap = new_dir[i];
+ new_dir[i] = old_dir[i];
+ old_dir[i] = swap;
+ }
}
+ shp->shm_dir = new_dir;
+ shp->shm_npages = new_pages;
+ shp->shm_segsz = attr->ia_size;
+out:
+ shm_unlock(inode->i_ino);
+ shm_lockall();
+ shm_tot += new_pages - old_pages;
+ shm_unlockall();
+ shm_free (old_dir, old_pages);
+set_attr:
+ inode_setattr(inode, attr);
return 0;
}
-static inline struct shmid_kernel *newseg_alloc(int numpages)
+static inline struct shmid_kernel *newseg_alloc(int numpages, size_t namelen)
{
struct shmid_kernel *shp;
- shp = (struct shmid_kernel *) kmalloc (sizeof (*shp), GFP_KERNEL);
+ shp = (struct shmid_kernel *) kmalloc (sizeof (*shp) + namelen, GFP_KERNEL);
if (!shp)
return 0;
return 0;
}
shp->shm_npages = numpages;
- shp->attaches = NULL;
shp->shm_nattch = 0;
- init_MUTEX(&shp->sem);
+ shp->shm_namelen = namelen;
return(shp);
}
-static int newseg (key_t key, int shmflg, size_t size)
+static int newseg (key_t key, const char *name, int namelen,
+ int shmflg, size_t size)
{
struct shmid_kernel *shp;
int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
int id;
- if (size < SHMMIN)
- return -EINVAL;
+ if (namelen > SHM_NAME_LEN)
+ return -ENAMETOOLONG;
if (size > shm_ctlmax)
return -EINVAL;
if (shm_tot + numpages >= shm_ctlall)
return -ENOSPC;
- if (!(shp = newseg_alloc(numpages)))
+ if (!(shp = newseg_alloc(numpages, namelen ? namelen : SHM_FMT_LEN + 1)))
return -ENOMEM;
- id = ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni);
+ id = ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni+1);
if(id == -1) {
shm_free(shp->shm_dir,numpages);
kfree(shp);
shp->shm_perm.key = key;
shp->shm_perm.mode = (shmflg & S_IRWXUGO);
shp->shm_segsz = size;
- shp->shm_cpid = current->pid;
- shp->shm_lpid = 0;
- shp->shm_atime = shp->shm_dtime = 0;
- shp->shm_ctime = CURRENT_TIME;
+ shp->shm_cprid = current->pid;
+ shp->shm_lprid = 0;
+ shp->shm_atim = shp->shm_dtim = 0;
+ shp->shm_ctim = CURRENT_TIME;
shp->id = shm_buildid(id,shp->shm_perm.seq);
+ if (namelen != 0) {
+ shp->shm_namelen = namelen;
+ memcpy (shp->shm_name, name, namelen);
+ } else {
+ shp->shm_namelen = sprintf (shp->shm_name, SHM_FMT, shp->id);
+ }
shm_tot += numpages;
+ used_segs++;
shm_unlock(id);
-
- return shm_buildid(id,shp->shm_perm.seq);
+
+ return shp->id;
}
asmlinkage long sys_shmget (key_t key, size_t size, int shmflg)
struct shmid_kernel *shp;
int err, id = 0;
+ if (!shm_sb) {
+ printk ("shmget: shm filesystem not mounted\n");
+ return -EINVAL;
+ }
+
+ if (size < SHMMIN)
+ return -EINVAL;
+
down(&shm_ids.sem);
if (key == IPC_PRIVATE) {
- err = newseg(key, shmflg, size);
+ err = newseg(key, NULL, 0, shmflg, size);
} else if ((id = ipc_findkey(&shm_ids,key)) == -1) {
if (!(shmflg & IPC_CREAT))
err = -ENOENT;
else
- err = newseg(key, shmflg, size);
+ err = newseg(key, NULL, 0, shmflg, size);
} else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) {
err = -EEXIST;
} else {
shp = shm_lock(id);
if(shp==NULL)
BUG();
- if (ipcperms(&shp->shm_perm, shmflg))
+ if (shp->shm_segsz < size)
+ err = -EINVAL;
+ else if (ipcperms(&shp->shm_perm, shmflg))
err = -EACCES;
else
err = shm_buildid(id, shp->shm_perm.seq);
shm_rss -= rss;
shm_swp -= swp;
shm_tot -= numpages;
+ used_segs--;
shm_unlockall();
}
}
-/*
- * Only called after testing nattch and SHM_DEST.
- * Here pages, pgtable and shmid_kernel are freed.
- */
-static void killseg (int shmid)
+static void shm_delete (struct inode *ino)
{
+ int shmid = ino->i_ino;
struct shmid_kernel *shp;
down(&shm_ids.sem);
shp = shm_lock(shmid);
if(shp==NULL) {
-out_up:
- up(&shm_ids.sem);
- return;
- }
- if(shm_checkid(shp,shmid) || shp->shm_nattch > 0 ||
- !(shp->shm_perm.mode & SHM_DEST)) {
- shm_unlock(shmid);
- goto out_up;
+ BUG();
}
shp = shm_rmid(shmid);
- if(shp==NULL)
- BUG();
- if (!shp->shm_dir)
- BUG();
shm_unlock(shmid);
up(&shm_ids.sem);
killseg_core(shp, 1);
-
- return;
+ clear_inode(ino);
}
static inline unsigned long copy_shmid_to_user(void *buf, struct shmid64_ds *in, int version)
}
}
+char * shm_getname(int id)
+{
+ char *result;
+
+ result = __getname ();
+ if (IS_ERR(result))
+ return result;
+
+ sprintf (result, "%s/" SHM_FMT, shm_path, id);
+ return result;
+}
+
asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
{
struct shm_setbuf setbuf;
struct shmid_kernel *shp;
int err, version;
+ if (!shm_sb) {
+ printk ("shmctl: shm filesystem not mounted\n");
+ return -EINVAL;
+ }
+
if (cmd < 0 || shmid < 0)
return -EINVAL;
{
struct shmid64_ds tbuf;
int result;
+ if ((shmid % SEQ_MULTIPLIER) == zero_id)
+ return -EINVAL;
memset(&tbuf, 0, sizeof(tbuf));
shp = shm_lock(shmid);
if(shp==NULL)
return -EINVAL;
- if (shp == &zshmid_kernel) {
- shm_unlock(shmid);
- return -EINVAL;
- }
if(cmd==SHM_STAT) {
err = -EINVAL;
if (shmid > shm_ids.max_id)
goto out_unlock;
kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm);
tbuf.shm_segsz = shp->shm_segsz;
- tbuf.shm_atime = shp->shm_atime;
- tbuf.shm_dtime = shp->shm_dtime;
- tbuf.shm_ctime = shp->shm_ctime;
- tbuf.shm_cpid = shp->shm_cpid;
- tbuf.shm_lpid = shp->shm_lpid;
+ tbuf.shm_atime = shp->shm_atim;
+ tbuf.shm_dtime = shp->shm_dtim;
+ tbuf.shm_ctime = shp->shm_ctim;
+ tbuf.shm_cpid = shp->shm_cprid;
+ tbuf.shm_lpid = shp->shm_lprid;
tbuf.shm_nattch = shp->shm_nattch;
shm_unlock(shmid);
if(copy_shmid_to_user (buf, &tbuf, version))
/* Should the pages be faulted in here or leave it to user? */
/* need to determine interaction with current->swappable */
struct kern_ipc_perm *ipcp;
+ if ((shmid % SEQ_MULTIPLIER)== zero_id)
+ return -EINVAL;
if (!capable(CAP_IPC_LOCK))
return -EPERM;
shp = shm_lock(shmid);
if(shp==NULL)
return -EINVAL;
- if (shp == &zshmid_kernel) {
- shm_unlock(shmid);
- return -EINVAL;
- }
err=-EIDRM;
if(shm_checkid(shp,shmid))
goto out_unlock;
return err;
}
case IPC_RMID:
- case IPC_SET:
- break;
- default:
- return -EINVAL;
+ {
+ char *name;
+ if ((shmid % SEQ_MULTIPLIER)== zero_id)
+ return -EINVAL;
+ name = shm_getname(shmid);
+ if (IS_ERR(name))
+ return PTR_ERR(name);
+ lock_kernel();
+ err = do_unlink (name);
+ unlock_kernel();
+ putname (name);
+ if (err == -ENOENT)
+ err = -EINVAL;
+ return err;
}
- if (cmd == IPC_SET) {
+ case IPC_SET:
+ {
+ if ((shmid % SEQ_MULTIPLIER)== zero_id)
+ return -EINVAL;
+
if(copy_shmid_from_user (&setbuf, buf, version))
return -EFAULT;
- }
- down(&shm_ids.sem);
- shp = shm_lock(shmid);
- err=-EINVAL;
- if(shp==NULL)
- goto out_up;
- if (shp == &zshmid_kernel)
- goto out_unlock_up;
- err=-EIDRM;
- if(shm_checkid(shp,shmid))
- goto out_unlock_up;
- err=-EPERM;
- if (current->euid != shp->shm_perm.uid &&
- current->euid != shp->shm_perm.cuid &&
- !capable(CAP_SYS_ADMIN)) {
- goto out_unlock_up;
- }
+ down(&shm_ids.sem);
+ shp = shm_lock(shmid);
+ err=-EINVAL;
+ if(shp==NULL)
+ goto out_up;
+ err=-EIDRM;
+ if(shm_checkid(shp,shmid))
+ goto out_unlock_up;
+ err=-EPERM;
+ if (current->euid != shp->shm_perm.uid &&
+ current->euid != shp->shm_perm.cuid &&
+ !capable(CAP_SYS_ADMIN)) {
+ goto out_unlock_up;
+ }
- switch (cmd) {
- case IPC_SET:
shp->shm_perm.uid = setbuf.uid;
shp->shm_perm.gid = setbuf.gid;
shp->shm_perm.mode = (shp->shm_perm.mode & ~S_IRWXUGO)
| (setbuf.mode & S_IRWXUGO);
- shp->shm_ctime = CURRENT_TIME;
+ shp->shm_ctim = CURRENT_TIME;
break;
- case IPC_RMID:
- shp->shm_perm.mode |= SHM_DEST;
- if (shp->shm_nattch <= 0) {
- shm_unlock(shmid);
- up(&shm_ids.sem);
- killseg (shmid);
- return 0;
- }
}
+
+ default:
+ return -EINVAL;
+ }
+
err = 0;
out_unlock_up:
shm_unlock(shmid);
return err;
}
-/*
- * The per process internal structure for managing segments is
- * `struct vm_area_struct'.
- * A shmat will add to and shmdt will remove from the list.
- * shmd->vm_mm the attacher
- * shmd->vm_start virt addr of attach, multiple of SHMLBA
- * shmd->vm_end multiple of SHMLBA
- * shmd->vm_next next attach for task
- * shmd->vm_next_share next attach for segment
- * shmd->vm_pgoff offset into segment (in pages)
- * shmd->vm_private_data signature for this attach
- */
-
-static struct vm_operations_struct shm_vm_ops = {
- open: shm_open, /* open - callback for a new vm-area open */
- close: shm_close, /* close - callback for when the vm-area is released */
- nopage: shm_nopage,
- swapout: shm_swapout,
-};
+static inline void shm_inc (int id) {
+ struct shmid_kernel *shp;
-/* Insert shmd into the list shp->attaches */
-static inline void insert_attach (struct shmid_kernel * shp, struct vm_area_struct * shmd)
-{
- if((shmd->vm_next_share = shp->attaches) != NULL)
- shp->attaches->vm_pprev_share = &shmd->vm_next_share;
- shp->attaches = shmd;
- shmd->vm_pprev_share = &shp->attaches;
+ if(!(shp = shm_lock(id)))
+ BUG();
+ shp->shm_atim = CURRENT_TIME;
+ shp->shm_lprid = current->pid;
+ shp->shm_nattch++;
+ shm_unlock(id);
}
-/* Remove shmd from list shp->attaches */
-static inline void remove_attach (struct shmid_kernel * shp, struct vm_area_struct * shmd)
+static int shm_mmap(struct file * file, struct vm_area_struct * vma)
{
- if(shmd->vm_next_share)
- shmd->vm_next_share->vm_pprev_share = shmd->vm_pprev_share;
- *shmd->vm_pprev_share = shmd->vm_next_share;
-}
-
-/*
- * ensure page tables exist
- * mark page table entries with shm_sgn.
- */
-static int shm_map (struct vm_area_struct *shmd)
-{
- unsigned long tmp;
-
- /* clear old mappings */
- do_munmap(shmd->vm_start, shmd->vm_end - shmd->vm_start);
-
- /* add new mapping */
- tmp = shmd->vm_end - shmd->vm_start;
- if((current->mm->total_vm << PAGE_SHIFT) + tmp
- > (unsigned long) current->rlim[RLIMIT_AS].rlim_cur)
- return -ENOMEM;
- current->mm->total_vm += tmp >> PAGE_SHIFT;
- vmlist_modify_lock(current->mm);
- insert_vm_struct(current->mm, shmd);
- merge_segments(current->mm, shmd->vm_start, shmd->vm_end);
- vmlist_modify_unlock(current->mm);
-
+ if (!(vma->vm_flags & VM_SHARED))
+ return -EINVAL; /* we cannot do private mappings */
+ UPDATE_ATIME(file->f_dentry->d_inode);
+ vma->vm_ops = &shm_vm_ops;
+ shm_inc(file->f_dentry->d_inode->i_ino);
return 0;
}
*/
asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
{
- struct shmid_kernel *shp;
- struct vm_area_struct *shmd;
- int err;
unsigned long addr;
- unsigned long len;
- short flg = shmflg & SHM_RDONLY ? S_IRUGO : S_IRUGO|S_IWUGO;
+ struct file * file;
+ int err;
+ int flags;
+ char *name;
-
- if (shmid < 0)
+ if (!shm_sb || (shmid % SEQ_MULTIPLIER) == zero_id)
return -EINVAL;
- down(¤t->mm->mmap_sem);
- err = -EINVAL;
- shp = shm_lock(shmid);
- if (!shp)
- goto out_up;
- if (shp == &zshmid_kernel)
- goto out_unlock_up;
-
- err = -EACCES;
- if (ipcperms(&shp->shm_perm, flg))
- goto out_unlock_up;
-
- err = -EIDRM;
- if (shm_checkid(shp,shmid))
- goto out_unlock_up;
-
- if (!(addr = (ulong) shmaddr)) {
- if (shmflg & SHM_REMAP)
- goto out_unlock_up;
- err = -ENOMEM;
- addr = 0;
- again:
- if (!(addr = get_unmapped_area(addr, (unsigned long)shp->shm_segsz)))
- goto out_unlock_up;
- if(addr & (SHMLBA - 1)) {
- addr = (addr + (SHMLBA - 1)) & ~(SHMLBA - 1);
- goto again;
+ if ((addr = (ulong)shmaddr))
+ {
+ if(addr & (SHMLBA-1)) {
+ if (shmflg & SHM_RND)
+ addr &= ~(SHMLBA-1); /* round down */
+ else
+ return -EINVAL;
}
- } else if (addr & (SHMLBA-1)) {
- err=-EINVAL;
- if (shmflg & SHM_RND)
- addr &= ~(SHMLBA-1); /* round down */
- else
- goto out_unlock_up;
- }
- /*
- * Check if addr exceeds TASK_SIZE (from do_mmap)
- */
- len = PAGE_SIZE*shp->shm_npages;
- err = -EINVAL;
- if (addr >= TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE - len)
- goto out_unlock_up;
- /*
- * If shm segment goes below stack, make sure there is some
- * space left for the stack to grow (presently 4 pages).
- */
- if (addr < current->mm->start_stack &&
- addr > current->mm->start_stack - PAGE_SIZE*(shp->shm_npages + 4))
- goto out_unlock_up;
- if (!(shmflg & SHM_REMAP) && find_vma_intersection(current->mm, addr, addr + (unsigned long)shp->shm_segsz))
- goto out_unlock_up;
+ flags = MAP_SHARED | MAP_FIXED;
+ } else
+ flags = MAP_SHARED;
- shm_unlock(shmid);
- err = -ENOMEM;
- shmd = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
- err = shm_revalidate(shp, shmid, len/PAGE_SIZE,flg);
- if(err) {
- kmem_cache_free(vm_area_cachep, shmd);
- goto out_up;
- }
+ name = shm_getname(shmid);
+ if (IS_ERR (name))
+ return PTR_ERR (name);
- shmd->vm_private_data = shp;
- shmd->vm_start = addr;
- shmd->vm_end = addr + shp->shm_npages * PAGE_SIZE;
- shmd->vm_mm = current->mm;
- shmd->vm_page_prot = (shmflg & SHM_RDONLY) ? PAGE_READONLY : PAGE_SHARED;
- shmd->vm_flags = VM_SHM | VM_MAYSHARE | VM_SHARED
- | VM_MAYREAD | VM_MAYEXEC | VM_READ | VM_EXEC
- | ((shmflg & SHM_RDONLY) ? 0 : VM_MAYWRITE | VM_WRITE);
- shmd->vm_file = NULL;
- shmd->vm_pgoff = 0;
- shmd->vm_ops = &shm_vm_ops;
-
- shp->shm_nattch++; /* prevent destruction */
- shm_unlock(shp->id);
- err = shm_map (shmd);
- shm_lock(shmid); /* cannot fail */
- if (err)
- goto failed_shm_map;
-
- insert_attach(shp,shmd); /* insert shmd into shp->attaches */
-
- shp->shm_lpid = current->pid;
- shp->shm_atime = CURRENT_TIME;
-
- *raddr = addr;
- err = 0;
-out_unlock_up:
- shm_unlock(shmid);
-out_up:
- up(¤t->mm->mmap_sem);
+ file = filp_open (name, O_RDWR, 0);
+ putname (name);
+ if (IS_ERR (file))
+ goto bad_file;
+ lock_kernel();
+ *raddr = do_mmap (file, addr, file->f_dentry->d_inode->i_size,
+ (shmflg & SHM_RDONLY ? PROT_READ :
+ PROT_READ | PROT_WRITE), flags, 0);
+ unlock_kernel();
+ if (IS_ERR(*raddr))
+ err = PTR_ERR(*raddr);
+ else
+ err = 0;
+ fput (file);
return err;
-failed_shm_map:
- {
- int delete = 0;
- if (--shp->shm_nattch <= 0 && shp->shm_perm.mode & SHM_DEST)
- delete = 1;
- shm_unlock(shmid);
- up(¤t->mm->mmap_sem);
- kmem_cache_free(vm_area_cachep, shmd);
- if(delete)
- killseg(shmid);
- return err;
- }
+bad_file:
+ if ((err = PTR_ERR(file)) == -ENOENT)
+ return -EINVAL;
+ return err;
}
/* This is called by fork, once for every shm attach. */
static void shm_open (struct vm_area_struct *shmd)
{
- struct shmid_kernel *shp;
-
- shp = (struct shmid_kernel *) shmd->vm_private_data;
- if(shp != shm_lock(shp->id))
- BUG();
- insert_attach(shp,shmd); /* insert shmd into shp->attaches */
- shp->shm_nattch++;
- shp->shm_atime = CURRENT_TIME;
- shp->shm_lpid = current->pid;
- shm_unlock(shp->id);
+ shm_inc (shmd->vm_file->f_dentry->d_inode->i_ino);
}
/*
*/
static void shm_close (struct vm_area_struct *shmd)
{
+ int id = shmd->vm_file->f_dentry->d_inode->i_ino;
struct shmid_kernel *shp;
- int id;
/* remove from the list of attaches of the shm segment */
- shp = (struct shmid_kernel *) shmd->vm_private_data;
- if(shp != shm_lock(shp->id))
+ if(!(shp = shm_lock(id)))
BUG();
- remove_attach(shp,shmd); /* remove from shp->attaches */
- shp->shm_lpid = current->pid;
- shp->shm_dtime = CURRENT_TIME;
- id=-1;
- if (--shp->shm_nattch <= 0 && shp->shm_perm.mode & SHM_DEST)
- id=shp->id;
- shm_unlock(shp->id);
- if(id!=-1)
- killseg(id);
+ shp->shm_lprid = current->pid;
+ shp->shm_dtim = CURRENT_TIME;
+ shp->shm_nattch--;
+ shm_unlock(id);
}
/*
/*
* page not present ... go through shm_dir
*/
-static struct page * shm_nopage(struct vm_area_struct * shmd, unsigned long address, int no_share)
+static struct page * shm_nopage_core(struct shmid_kernel *shp, unsigned int idx, int *swp, int *rss)
{
pte_t pte;
- struct shmid_kernel *shp;
- unsigned int idx;
struct page * page;
- int is_shmzero;
-
- shp = (struct shmid_kernel *) shmd->vm_private_data;
- idx = (address - shmd->vm_start) >> PAGE_SHIFT;
- idx += shmd->vm_pgoff;
- is_shmzero = (shp->id == zero_id);
- /*
- * A shared mapping past the last page of the file is an error
- * and results in a SIGBUS, so logically a shared mapping past
- * the end of a shared memory segment should result in SIGBUS
- * as well.
- */
- if (idx >= shp->shm_npages) {
- return NULL;
- }
- down(&shp->sem);
- if ((shp != shm_lock(shp->id)) && (is_shmzero == 0))
- BUG();
+ if (idx >= shp->shm_npages)
+ goto sigbus;
pte = SHM_ENTRY(shp,idx);
if (!pte_present(pte)) {
if (!page)
goto oom;
clear_highpage(page);
- if ((shp != shm_lock(shp->id)) && (is_shmzero == 0))
+ if ((shp != shm_lock(shp->id)) && (shp->id != zero_id))
BUG();
} else {
swp_entry_t entry = pte_to_swp_entry(pte);
delete_from_swap_cache(page);
page = replace_with_highmem(page);
swap_free(entry);
- if ((shp != shm_lock(shp->id)) && (is_shmzero == 0))
+ if ((shp != shm_lock(shp->id)) && (shp->id != zero_id))
BUG();
- if (is_shmzero == 0) shm_swp--;
+ (*swp)--;
}
- if (is_shmzero == 0) shm_rss++;
+ (*rss)++;
pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
SHM_ENTRY(shp, idx) = pte;
} else
/* pte_val(pte) == SHM_ENTRY (shp, idx) */
get_page(pte_page(pte));
- shm_unlock(shp->id);
- up(&shp->sem);
current->min_flt++;
return pte_page(pte);
oom:
- up(&shp->sem);
return NOPAGE_OOM;
+sigbus:
+ return NOPAGE_SIGBUS;
+}
+
+static struct page * shm_nopage(struct vm_area_struct * shmd, unsigned long address, int no_share)
+{
+ struct page * page;
+ struct shmid_kernel *shp;
+ unsigned int idx;
+ struct inode * inode = shmd->vm_file->f_dentry->d_inode;
+
+ idx = (address - shmd->vm_start) >> PAGE_SHIFT;
+ idx += shmd->vm_pgoff;
+
+ down(&inode->i_sem);
+ if(!(shp = shm_lock(inode->i_ino)))
+ BUG();
+ page = shm_nopage_core(shp, idx, &shm_swp, &shm_rss);
+ shm_unlock(inode->i_ino);
+ up(&inode->i_sem);
+ return(page);
}
#define OKAY 0
int i, len = 0;
down(&shm_ids.sem);
- len += sprintf(buffer, " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime\n");
+ len += sprintf(buffer, " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime name\n");
- for(i = 0; i <= shm_ids.max_id; i++) {
- struct shmid_kernel* shp = shm_lock(i);
- if (shp == &zshmid_kernel) {
- shm_unlock(i);
+ for(i = 0; i <= shm_ids.max_id; i++) {
+ struct shmid_kernel* shp;
+
+ if (i == zero_id)
continue;
- }
+ shp = shm_lock(i);
if(shp!=NULL) {
-#define SMALL_STRING "%10d %10d %4o %10u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu\n"
-#define BIG_STRING "%10d %10d %4o %21u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu\n"
+#define SMALL_STRING "%10d %10d %4o %10u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu %.*s\n"
+#define BIG_STRING "%10d %10d %4o %21u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu %.*s\n"
char *format;
if (sizeof(size_t) <= sizeof(int))
format = SMALL_STRING;
else
format = BIG_STRING;
- len += sprintf(buffer + len, format,
+ len += sprintf(buffer + len, format,
shp->shm_perm.key,
shm_buildid(i, shp->shm_perm.seq),
shp->shm_perm.mode,
shp->shm_segsz,
- shp->shm_cpid,
- shp->shm_lpid,
+ shp->shm_cprid,
+ shp->shm_lprid,
shp->shm_nattch,
shp->shm_perm.uid,
shp->shm_perm.gid,
shp->shm_perm.cuid,
shp->shm_perm.cgid,
- shp->shm_atime,
- shp->shm_dtime,
- shp->shm_ctime);
+ shp->shm_atim,
+ shp->shm_dtim,
+ shp->shm_ctim,
+ shp->shm_namelen,
+ shp->shm_name);
shm_unlock(i);
pos += len;
}
#endif
-static struct shmid_kernel *zmap_list = 0;
+#define VMA_TO_SHP(vma) ((vma)->vm_file->private_data)
+
+static LIST_HEAD(zmap_list);
static spinlock_t zmap_list_lock = SPIN_LOCK_UNLOCKED;
static unsigned long zswap_idx = 0; /* next to swap */
-static struct shmid_kernel *zswap_shp = 0;
+static struct shmid_kernel *zswap_shp = (struct shmid_kernel *)&zmap_list;
+static int zshm_rss;
static struct vm_operations_struct shmzero_vm_ops = {
open: shmzero_open,
close: shmzero_close,
- nopage: shm_nopage,
+ nopage: shmzero_nopage,
swapout: shm_swapout,
};
+/*
+ * In this implementation, the "unuse" and "swapout" interfaces are
+ * interlocked out via the kernel_lock, as well as shm_lock(zero_id).
+ * "unuse" and "nopage/swapin", as well as "swapout" and "nopage/swapin"
+ * interlock via shm_lock(zero_id). All these interlocks can be based
+ * on a per mapping lock instead of being a global lock.
+ */
+/*
+ * Reference (existance) counting on the file/dentry/inode is done
+ * by generic vm_file code. The zero code does not hold any reference
+ * on the pseudo-file. This is possible because the open/close calls
+ * are bracketed by the file count update calls.
+ */
+static struct file *file_setup(struct file *fzero, struct shmid_kernel *shp)
+{
+ struct file *filp;
+ struct inode *inp;
+
+ if ((filp = get_empty_filp()) == 0)
+ return(filp);
+ if ((inp = get_empty_inode()) == 0) {
+ put_filp(filp);
+ return(0);
+ }
+ if ((filp->f_dentry = d_alloc(zdent, &(const struct qstr) { "dev/zero",
+ 8, 0 })) == 0) {
+ iput(inp);
+ put_filp(filp);
+ return(0);
+ }
+ d_instantiate(filp->f_dentry, inp);
+
+ /*
+ * Copy over /dev/zero dev/ino for benefit of procfs. Use
+ * ino to indicate seperate mappings.
+ */
+ filp->f_dentry->d_inode->i_dev = fzero->f_dentry->d_inode->i_dev;
+ filp->f_dentry->d_inode->i_ino = (unsigned long)shp;
+ fput(fzero); /* release /dev/zero file */
+ return(filp);
+}
+
int map_zero_setup(struct vm_area_struct *vma)
{
+ extern int vm_enough_memory(long pages);
struct shmid_kernel *shp;
+ struct file *filp;
- if (!(shp = newseg_alloc((vma->vm_end - vma->vm_start) / PAGE_SIZE)))
+ if (!vm_enough_memory((vma->vm_end - vma->vm_start) >> PAGE_SHIFT))
+ return -ENOMEM;
+ if (!(shp = newseg_alloc((vma->vm_end - vma->vm_start) / PAGE_SIZE, 0)))
+ return -ENOMEM;
+ if ((filp = file_setup(vma->vm_file, shp)) == 0) {
+ killseg_core(shp, 0);
return -ENOMEM;
- shp->id = zero_id; /* hack for shm_lock et al */
- vma->vm_private_data = shp;
+ }
+ vma->vm_file = filp;
+ VMA_TO_SHP(vma) = (void *)shp;
+ shp->id = zero_id;
+ init_MUTEX(&shp->zsem);
vma->vm_ops = &shmzero_vm_ops;
shmzero_open(vma);
spin_lock(&zmap_list_lock);
- shp->attaches = (struct vm_area_struct *)zmap_list;
- zmap_list = shp;
+ list_add(&shp->zero_list, &zmap_list);
spin_unlock(&zmap_list_lock);
return 0;
}
{
struct shmid_kernel *shp;
- shp = (struct shmid_kernel *) shmd->vm_private_data;
- down(&shp->sem);
+ shp = VMA_TO_SHP(shmd);
+ down(&shp->zsem);
shp->shm_nattch++;
- up(&shp->sem);
+ up(&shp->zsem);
}
static void shmzero_close(struct vm_area_struct *shmd)
{
int done = 0;
- struct shmid_kernel *shp, *prev, *cur;
+ struct shmid_kernel *shp;
- shp = (struct shmid_kernel *) shmd->vm_private_data;
- down(&shp->sem);
+ shp = VMA_TO_SHP(shmd);
+ down(&shp->zsem);
if (--shp->shm_nattch == 0)
done = 1;
- up(&shp->sem);
+ up(&shp->zsem);
if (done) {
spin_lock(&zmap_list_lock);
if (shp == zswap_shp)
- zswap_shp = (struct shmid_kernel *)(shp->attaches);
- if (shp == zmap_list)
- zmap_list = (struct shmid_kernel *)(shp->attaches);
- else {
- prev = zmap_list;
- cur = (struct shmid_kernel *)(prev->attaches);
- while (cur != shp) {
- prev = cur;
- cur = (struct shmid_kernel *)(prev->attaches);
- }
- prev->attaches = (struct vm_area_struct *)(shp->attaches);
- }
+ zswap_shp = list_entry(zswap_shp->zero_list.next,
+ struct shmid_kernel, zero_list);
+ list_del(&shp->zero_list);
spin_unlock(&zmap_list_lock);
killseg_core(shp, 0);
}
}
+static struct page * shmzero_nopage(struct vm_area_struct * shmd, unsigned long address, int no_share)
+{
+ struct page *page;
+ struct shmid_kernel *shp;
+ unsigned int idx;
+ int dummy;
+
+ idx = (address - shmd->vm_start) >> PAGE_SHIFT;
+ idx += shmd->vm_pgoff;
+
+ shp = VMA_TO_SHP(shmd);
+ down(&shp->zsem);
+ shm_lock(zero_id);
+ page = shm_nopage_core(shp, idx, &dummy, &zshm_rss);
+ shm_unlock(zero_id);
+ up(&shp->zsem);
+ return(page);
+}
+
static void zmap_unuse(swp_entry_t entry, struct page *page)
{
struct shmid_kernel *shp;
spin_lock(&zmap_list_lock);
- shp = zmap_list;
- while (shp) {
+ shm_lock(zero_id);
+ for (shp = list_entry(zmap_list.next, struct shmid_kernel, zero_list);
+ shp != (struct shmid_kernel *)&zmap_list;
+ shp = list_entry(shp->zero_list.next, struct shmid_kernel,
+ zero_list)) {
if (shm_unuse_core(shp, entry, page))
break;
- shp = (struct shmid_kernel *)shp->attaches;
}
+ shm_unlock(zero_id);
spin_unlock(&zmap_list_lock);
}
int counter;
struct page * page_map;
- counter = 10; /* maybe we should use zshm_rss */
+ counter = zshm_rss >> prio;
if (!counter)
return;
next:
return;
spin_lock(&zmap_list_lock);
- if (zmap_list == 0)
+ shm_lock(zero_id);
+ if (zmap_list.next == 0)
goto failed;
next_id:
- if ((shp = zswap_shp) == 0) {
+ if (zswap_shp == (struct shmid_kernel *)&zmap_list) {
if (loop) {
failed:
+ shm_unlock(zero_id);
spin_unlock(&zmap_list_lock);
__swap_free(swap_entry, 2);
return;
}
- zswap_shp = shp = zmap_list;
+ zswap_shp = list_entry(zmap_list.next, struct shmid_kernel,
+ zero_list);
zswap_idx = 0;
loop = 1;
}
+ shp = zswap_shp;
check_table:
idx = zswap_idx++;
if (idx >= shp->shm_npages) {
- zswap_shp = (struct shmid_kernel *)(zswap_shp->attaches);
+ zswap_shp = list_entry(zswap_shp->zero_list.next,
+ struct shmid_kernel, zero_list);
zswap_idx = 0;
goto next_id;
}
case RETRY: goto check_table;
case FAILED: goto failed;
}
+ shm_unlock(zero_id);
spin_unlock(&zmap_list_lock);
shm_swap_postop(page_map);
goto next;
return;
}
+
if(size > IPCMNI)
size = IPCMNI;
ids->size = size;
- if(size == 0)
- return;
-
ids->in_use = 0;
ids->max_id = -1;
ids->seq = 0;
*/
static int check_free_space(struct file *file)
{
- mm_segment_t fs;
struct statfs sbuf;
- struct super_block *sb;
int res = acct_active;
int act;
if (!file || !acct_needcheck)
return res;
- sb = file->f_dentry->d_inode->i_sb;
- if (!sb->s_op || !sb->s_op->statfs)
- return res;
-
- fs = get_fs();
- set_fs(KERNEL_DS);
/* May block */
- sb->s_op->statfs(sb, &sbuf, sizeof(struct statfs));
- set_fs(fs);
+ if (vfs_statfs(file->f_dentry->d_inode->i_sb, &sbuf))
+ return res;
if (sbuf.f_bavail <= SUSPEND * sbuf.f_blocks / 100)
act = -1;
EXPORT_SYMBOL(jiffies);
EXPORT_SYMBOL(xtime);
EXPORT_SYMBOL(do_gettimeofday);
+EXPORT_SYMBOL(do_settimeofday);
#ifndef __ia64__
EXPORT_SYMBOL(loops_per_sec);
#endif
#endif
#ifdef CONFIG_SYSVIPC
extern size_t shm_ctlmax;
-extern int shm_ctlall;
-extern int shm_ctlmni;
+extern char shm_path[];
extern int msg_ctlmax;
extern int msg_ctlmnb;
extern int msg_ctlmni;
{KERN_RTSIGMAX, "rtsig-max", &max_queued_signals, sizeof(int),
0644, NULL, &proc_dointvec},
#ifdef CONFIG_SYSVIPC
+ {KERN_SHMPATH, "shmpath", &shm_path, 256,
+ 0644, NULL, &proc_dostring, &sysctl_string },
{KERN_SHMMAX, "shmmax", &shm_ctlmax, sizeof (size_t),
0644, NULL, &proc_doulongvec_minmax},
- {KERN_SHMALL, "shmall", &shm_ctlall, sizeof (int),
- 0644, NULL, &proc_dointvec},
- {KERN_SHMMNI, "shmmni", &shm_ctlmni, sizeof (int),
- 0644, NULL, &proc_dointvec},
{KERN_MSGMAX, "msgmax", &msg_ctlmax, sizeof (int),
0644, NULL, &proc_dointvec},
{KERN_MSGMNI, "msgmni", &msg_ctlmni, sizeof (int),
atomic_dec(&file->f_dentry->d_inode->i_writecount);
correct_wcount = 1;
}
+ vma->vm_file = file;
+ get_file(file);
error = file->f_op->mmap(file, vma);
/* Fix up the count if necessary, then check for an error */
if (correct_wcount)
atomic_inc(&file->f_dentry->d_inode->i_writecount);
if (error)
goto unmap_and_free_vma;
- vma->vm_file = file;
- get_file(file);
}
/*
return addr;
unmap_and_free_vma:
+ vma->vm_file = NULL;
+ fput(file);
/* Undo any partial mapping done by a device driver. */
flush_cache_range(mm, vma->vm_start, vma->vm_end);
zap_page_range(mm, vma->vm_start, vma->vm_end - vma->vm_start);
source net/ipx/Config.in
fi
tristate 'Appletalk protocol support' CONFIG_ATALK
+tristate 'DECnet Support' CONFIG_DECNET
+if [ "$CONFIG_DECNET" != "n" ]; then
+ source net/decnet/Config.in
+fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate 'DECnet Support (EXPERIMENTAL)' CONFIG_DECNET
- if [ "$CONFIG_DECNET" != "n" ]; then
- source net/decnet/Config.in
- fi
tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool ' DECnet: router support (EXPERIMENTAL)' CONFIG_DECNET_ROUTER
if [ "$CONFIG_DECNET_ROUTER" = "y" ]; then
- bool ' DECnet: use FWMARK value as routing key' CONFIG_DECNET_ROUTE_FWMARK
+ bool ' DECnet: use FWMARK value as routing key (EXPERIMENTAL)' CONFIG_DECNET_ROUTE_FWMARK
fi
fi
-bool ' DECnet: raw socket support' CONFIG_DECNET_RAW
O_OBJS += dn_fib.o dn_rules.o dn_table.o
endif
-ifeq ($(CONFIG_DECNET_RAW),y)
-O_OBJS += dn_raw.o
-endif
-
ifeq ($(CONFIG_DECNET_FW),y)
O_OBJS += dn_fw.o
endif
o Proper timeouts on each neighbour (in routing mode) rather than
just the 60 second On-Ethernet cache value.
- o Misc. get/set_sockopt() functions [done for the time being, more later]
-
o Support for X.25 linklayer
o Support for DDCMP link layer
o PPP support (rfc1762)
- o sendmsg() in the raw socket layer (yes, its for sending routing messages)
-
- o Fix /proc for raw sockets
-
o Lots of testing with real applications
o Verify errors etc. against POSIX 1003.1g (draft)
#include <net/dn_dev.h>
#include <net/dn_route.h>
#include <net/dn_fib.h>
-#include <net/dn_raw.h>
#include <net/dn_neigh.h>
#define MAX(a,b) ((a)>(b)?(a):(b))
goto no_sock;
if (sock) {
-#ifdef CONFIG_DECNET_RAW
- if (sock->type == SOCK_RAW)
- sock->ops = &dn_raw_proto_ops;
- else
-#endif /* CONFIG_DECNET_RAW */
sock->ops = &dn_proto_ops;
}
sock_init_data(sock,sk);
break;
case SOCK_STREAM:
break;
-#ifdef CONFIG_DECNET_RAW
- case SOCK_RAW:
- if ((protocol != DNPROTO_NSP) &&
- (protocol != DNPROTO_ROU))
- return -EPROTONOSUPPORT;
- break;
-#endif /* CONFIG_DECNET_RAW */
default:
return -ESOCKTNOSUPPORT;
}
static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
+ struct dn_scp *scp = &sk->protinfo.dn;
int err = -EOPNOTSUPP;
unsigned long amount = 0;
struct sk_buff *skb;
case TIOCINQ:
lock_sock(sk);
- if ((skb = skb_peek(&sk->receive_queue)) != NULL)
+ if ((skb = skb_peek(&scp->other_receive_queue)) != NULL) {
amount = skb->len;
+ } else {
+ struct sk_buff *skb = sk->receive_queue.next;
+ for(;;) {
+ if (skb == (struct sk_buff *)&sk->receive_queue)
+ break;
+ amount += skb->len;
+ skb = skb->next;
+ }
+ }
release_sock(sk);
err = put_user(amount, (int *)arg);
break;
return len;
}
-#ifdef CONFIG_DECNET_RAW
-
-extern int dn_raw_get_info(char *, char **, off_t, int);
-#endif /* CONFIG_DECNET_RAW */
static struct net_proto_family dn_family_ops = {
AF_DECnet,
dn_create
void __init decnet_proto_init(struct net_proto *pro)
{
- printk(KERN_INFO "NET4: DECnet for Linux: V.2.3.38s (C) 1995-1999 Linux DECnet Project Team\n");
+ printk(KERN_INFO "NET4: DECnet for Linux: V.2.3.49s (C) 1995-2000 Linux DECnet Project Team\n");
sock_register(&dn_family_ops);
dev_add_pack(&dn_dix_packet_type);
register_netdevice_notifier(&dn_dev_notifier);
proc_net_create("decnet", 0, dn_get_info);
-#ifdef CONFIG_DECNET_RAW
- proc_net_create("decnet_raw", 0, dn_raw_get_info);
-#endif
+
dn_neigh_init();
dn_dev_init();
dn_route_init();
#endif /* CONFIG_DECNET_ROUTER */
proc_net_remove("decnet");
-#ifdef CONFIG_DECNET_RAW
- proc_net_remove("decnet_raw");
-#endif
dev_remove_pack(&dn_dix_packet_type);
sock_unregister(AF_DECnet);
#include <net/dn_nsp.h>
#include <net/dn_dev.h>
#include <net/dn_route.h>
-#include <net/dn_raw.h>
/*
struct dn_scp *scp = &sk->protinfo.dn;
unsigned short reason;
- if (skb->len < 2)
+ if (skb->len != 2)
goto out;
reason = dn_ntohs(*(__u16 *)skb->data);
if (decnet_debug_level & 2)
printk(KERN_DEBUG "dn_nsp_rx: Message type 0x%02x\n", (int)cb->nsp_flags);
-#ifdef CONFIG_DECNET_RAW
- dn_raw_rx_nsp(skb);
-#endif /* CONFIG_DECNET_RAW */
-
if (skb->len < 2)
goto free_out;
int ddl, unsigned char *dd, __u16 rem, __u16 loc)
{
struct sk_buff *skb = NULL;
- int size = 8 + ddl;
+ int size = 7 + ddl + ((msgflg == NSP_DISCINIT) ? 1 : 0);
unsigned char *msg;
if ((dst == NULL) || (rem == 0)) {
msg += 2;
*(__u16 *)msg = dn_htons(reason);
msg += 2;
- *msg++ = ddl;
+ if (msgflg == NSP_DISCINIT)
+ *msg++ = ddl;
if (ddl) {
memcpy(msg, dd, ddl);
+++ /dev/null
-/*
- * DECnet An implementation of the DECnet protocol suite for the LINUX
- * operating system. DECnet is implemented using the BSD Socket
- * interface as the means of communication with the user level.
- *
- * DECnet Raw Sockets Interface
- *
- * Author: Steve Whitehouse <SteveW@ACM.org>
- *
- *
- * Changes:
- * Steve Whitehouse - connect() function.
- * Steve Whitehouse - SMP changes, removed MOP stubs. MOP will
- * be userland only.
- */
-
-#include <linux/config.h>
-#include <linux/net.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <net/sock.h>
-#include <net/dst.h>
-#include <net/dn.h>
-#include <net/dn_raw.h>
-#include <net/dn_route.h>
-
-static rwlock_t dn_raw_hash_lock = RW_LOCK_UNLOCKED;
-static struct sock *dn_raw_nsp_sklist = NULL;
-static struct sock *dn_raw_routing_sklist = NULL;
-
-static void dn_raw_hash(struct sock *sk)
-{
- struct sock **skp;
-
- switch(sk->protocol) {
- case DNPROTO_NSP:
- skp = &dn_raw_nsp_sklist;
- break;
- case DNPROTO_ROU:
- skp = &dn_raw_routing_sklist;
- break;
- default:
- printk(KERN_DEBUG "dn_raw_hash: Unknown protocol\n");
- return;
- }
-
- write_lock_bh(&dn_raw_hash_lock);
- sk->next = *skp;
- sk->pprev = skp;
- *skp = sk;
- write_unlock_bh(&dn_raw_hash_lock);
-}
-
-static void dn_raw_unhash(struct sock *sk)
-{
- struct sock **skp = sk->pprev;
-
- if (skp == NULL)
- return;
-
- write_lock_bh(&dn_raw_hash_lock);
- while(*skp != sk)
- skp = &((*skp)->next);
- *skp = sk->next;
- write_unlock_bh(&dn_raw_hash_lock);
-
- sk->next = NULL;
- sk->pprev = NULL;
-}
-
-static void dn_raw_autobind(struct sock *sk)
-{
- dn_raw_hash(sk);
- sk->zapped = 0;
-}
-
-static int dn_raw_release(struct socket *sock)
-{
- struct sock *sk = sock->sk;
-
- if (sk == NULL)
- return 0;
-
- if (!sk->dead) sk->state_change(sk);
-
- sk->dead = 1;
- sk->socket = NULL;
- sock->sk = NULL;
-
- dn_raw_unhash(sk);
- sock_put(sk);
-
- return 0;
-}
-
-/*
- * Bind does odd things with raw sockets. Its basically used to filter
- * the incoming packets, but this differs with the different layers
- * at which you extract packets.
- *
- * For Routing layer sockets, the object name is a host ordered unsigned
- * short which is a mask for the 16 different types of possible routing
- * packet. I'd like to also select by destination address of the packets
- * but alas, this is rather too difficult to do at the moment.
- */
-static int dn_raw_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
-{
- struct sock *sk = sock->sk;
- struct sockaddr_dn *addr = (struct sockaddr_dn *)uaddr;
-
- if (addr_len != sizeof(struct sockaddr_dn))
- return -EINVAL;
-
- if (sk->zapped == 0)
- return -EINVAL;
-
- switch(sk->protocol) {
- case DNPROTO_ROU:
- if (dn_ntohs(addr->sdn_objnamel) && (dn_ntohs(addr->sdn_objnamel) != 2))
- return -EINVAL;
- /* Fall through here */
- case DNPROTO_NSP:
- if (dn_ntohs(addr->sdn_add.a_len) && (dn_ntohs(addr->sdn_add.a_len) != 2))
- return -EINVAL;
- break;
- default:
- return -EPROTONOSUPPORT;
- }
-
- if (dn_ntohs(addr->sdn_objnamel) > (DN_MAXOBJL-1))
- return -EINVAL;
-
- if (dn_ntohs(addr->sdn_add.a_len) > DN_MAXADDL)
- return -EINVAL;
-
- memcpy(&sk->protinfo.dn.addr, addr, sizeof(struct sockaddr_dn));
-
- dn_raw_autobind(sk);
-
- return 0;
-}
-
-/*
- * This is to allow send() and write() to work. You set the destination address
- * with this function.
- */
-static int dn_raw_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
-{
- struct sock *sk = sock->sk;
- struct dn_scp *scp = &sk->protinfo.dn;
- struct sockaddr_dn *saddr = (struct sockaddr_dn *)uaddr;
- int err;
-
- lock_sock(sk);
-
- err = -EINVAL;
- if (addr_len != sizeof(struct sockaddr_dn))
- goto out;
-
- if (saddr->sdn_family != AF_DECnet)
- goto out;
-
- if (dn_ntohs(saddr->sdn_objnamel) > (DN_MAXOBJL-1))
- goto out;
-
- if (dn_ntohs(saddr->sdn_add.a_len) > DN_MAXADDL)
- goto out;
-
- if (sk->zapped)
- dn_raw_autobind(sk);
-
- if ((err = dn_route_output(&sk->dst_cache, dn_saddr2dn(saddr), dn_saddr2dn(&scp->addr), 0)) < 0)
- goto out;
-
- memcpy(&scp->peer, saddr, sizeof(struct sockaddr_dn));
-out:
- release_sock(sk);
-
- return err;
-}
-
-/*
- * TBD.
- */
-static int dn_raw_sendmsg(struct socket *sock, struct msghdr *hdr, int size,
- struct scm_cookie *scm)
-{
- struct sock *sk = sock->sk;
-
- if (sk->zapped)
- dn_raw_autobind(sk);
-
- if (sk->protocol != DNPROTO_NSP)
- return -EOPNOTSUPP;
-
- return 0;
-}
-
-/*
- * This works fine, execpt that it doesn't report the originating address
- * or anything at the moment.
- */
-static int dn_raw_recvmsg(struct socket *sock, struct msghdr *msg, int size,
- int flags, struct scm_cookie *scm)
-{
- struct sock *sk = sock->sk;
- struct sk_buff *skb;
- int err = 0;
- int copied = 0;
-
- lock_sock(sk);
-
- if (sk->zapped)
- dn_raw_autobind(sk);
-
- if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err)) == NULL)
- goto out;
-
- copied = skb->len;
-
- if (copied > size) {
- copied = size;
- msg->msg_flags |= MSG_TRUNC;
- }
-
- if ((err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied)) != 0) {
- if (flags & MSG_PEEK)
- atomic_dec(&skb->users);
- else
- skb_queue_head(&sk->receive_queue, skb);
-
- goto out;
- }
-
- skb_free_datagram(sk, skb);
-
-out:
- release_sock(sk);
-
- return copied ? copied : err;
-}
-
-struct proto_ops dn_raw_proto_ops = {
- AF_DECnet,
-
- dn_raw_release,
- dn_raw_bind,
- dn_raw_connect,
- sock_no_socketpair,
- sock_no_accept,
- sock_no_getname,
- datagram_poll,
- sock_no_ioctl,
- sock_no_listen,
- sock_no_shutdown,
- sock_no_setsockopt,
- sock_no_getsockopt,
- sock_no_fcntl,
- dn_raw_sendmsg,
- dn_raw_recvmsg,
- sock_no_mmap
-};
-
-#ifdef CONFIG_PROC_FS
-int dn_raw_get_info(char *buffer, char **start, off_t offset, int length)
-{
- int len = 0;
- off_t pos = 0;
- off_t begin = 0;
- struct sock *sk;
-
- read_lock_bh(&dn_raw_hash_lock);
- for(sk = dn_raw_nsp_sklist; sk; sk = sk->next) {
- len += sprintf(buffer+len, "NSP\n");
-
- pos = begin + len;
-
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
-
- if (pos > offset + length)
- goto all_done;
-
- }
-
- for(sk = dn_raw_routing_sklist; sk; sk = sk->next) {
- len += sprintf(buffer+len, "ROU\n");
-
- pos = begin + len;
-
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
-
- if (pos > offset + length)
- goto all_done;
- }
-
-all_done:
- read_unlock_bh(&dn_raw_hash_lock);
-
- *start = buffer + (offset - begin);
- len -= (offset - begin);
-
- if (len > length) len = length;
-
- return(len);
-}
-#endif /* CONFIG_PROC_FS */
-
-void dn_raw_rx_nsp(struct sk_buff *skb)
-{
- struct sock *sk;
- struct sk_buff *skb2;
-
- read_lock(&dn_raw_hash_lock);
- for(sk = dn_raw_nsp_sklist; sk != NULL; sk = sk->next) {
- if (skb->len > sock_rspace(sk))
- continue;
- if (sk->dead)
- continue;
- if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
- skb_set_owner_r(skb2, sk);
- skb_queue_tail(&sk->receive_queue, skb2);
- sk->data_ready(sk, skb->len);
- }
- }
- read_unlock(&dn_raw_hash_lock);
-}
-
-void dn_raw_rx_routing(struct sk_buff *skb)
-{
- struct sock *sk;
- struct sk_buff *skb2;
- struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
- unsigned short rt_flagmask;
- unsigned short objnamel;
- struct dn_scp *scp;
-
- read_lock(&dn_raw_hash_lock);
- for(sk = dn_raw_routing_sklist; sk != NULL; sk = sk->next) {
- if (skb->len > sock_rspace(sk))
- continue;
- if (sk->dead)
- continue;
- scp = &sk->protinfo.dn;
-
- rt_flagmask = dn_ntohs(*(unsigned short *)scp->addr.sdn_objname);
- objnamel = dn_ntohs(scp->addr.sdn_objnamel);
-
- if ((objnamel == 2) && (!((1 << (cb->rt_flags & 0x0f)) & rt_flagmask)))
- continue;
-
- if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
- skb_set_owner_r(skb2, sk);
- skb_queue_tail(&sk->receive_queue, skb2);
- sk->data_ready(sk, skb->len);
- }
- }
- read_unlock(&dn_raw_hash_lock);
-}
-
#include <net/dn_route.h>
#include <net/dn_neigh.h>
#include <net/dn_fib.h>
-#include <net/dn_raw.h>
struct dn_rt_hash_bucket
{
(int)flags, (dev) ? dev->name : "???", len, skb->len,
padlen);
-#ifdef CONFIG_DECNET_RAW
- dn_raw_rx_routing(skb);
-#endif /* CONFIG_DECNET_RAW */
-
if (flags & DN_RT_PKT_CNTL) {
switch(flags & DN_RT_CNTL_MSK) {
case DN_RT_PKT_INIT:
int decnet_debug_level = 0;
int decnet_time_wait = 30;
-int decnet_dn_count = 3;
-int decnet_di_count = 5;
-int decnet_dr_count = 5;
+int decnet_dn_count = 1;
+int decnet_di_count = 3;
+int decnet_dr_count = 3;
#ifdef CONFIG_SYSCTL
extern int decnet_dst_gc_interval;
#
# Note 2! The CFLAGS definition is now in the main makefile...
+O_TARGET := econet.o
MOD_LIST_NAME := NET_MISC_MODULES
-O_OBJS :=
-M_OBJS :=
-
-ifeq ($(CONFIG_ECONET),y)
- O_OBJS += econet.o
-else
- ifeq ($(CONFIG_ECONET), m)
- M_OBJS += econet.o
- endif
-endif
+O_OBJS := af_econet.o sysctl_net_ec.o
+M_OBJS := $(O_TARGET)
include $(TOPDIR)/Rules.make
--- /dev/null
+/*
+ * An implementation of the Acorn Econet and AUN protocols.
+ * Philip Blundell <philb@gnu.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/route.h>
+#include <linux/inet.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/inet_common.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/if_ec.h>
+#include <net/udp.h>
+#include <net/ip.h>
+#include <linux/inetdevice.h>
+#include <linux/spinlock.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+static struct proto_ops econet_ops;
+static struct sock *econet_sklist;
+
+/* Since there are only 256 possible network numbers (or fewer, depends
+ how you count) it makes sense to use a simple lookup table. */
+static struct net_device *net2dev_map[256];
+
+#ifdef CONFIG_ECONET_AUNUDP
+static spinlock_t aun_queue_lock;
+static struct socket *udpsock;
+#define AUN_PORT 0x8000
+
+#define EC_PORT_IP 0xd2
+
+struct aunhdr
+{
+ unsigned char code; /* AUN magic protocol byte */
+ unsigned char port;
+ unsigned char cb;
+ unsigned char pad;
+ unsigned long handle;
+};
+
+static unsigned long aun_seq = 0;
+
+/* Queue of packets waiting to be transmitted. */
+static struct sk_buff_head aun_queue;
+static struct timer_list ab_cleanup_timer;
+
+#endif /* CONFIG_ECONET_AUNUDP */
+
+/* Per-packet information */
+struct ec_cb
+{
+ struct sockaddr_ec sec;
+ unsigned long cookie; /* Supplied by user. */
+#ifdef CONFIG_ECONET_AUNUDP
+ int done;
+ unsigned long seq; /* Sequencing */
+ unsigned long timeout; /* Timeout */
+ unsigned long start; /* jiffies */
+#endif
+#ifdef CONFIG_ECONET_NATIVE
+ void (*sent)(struct sk_buff *, int result);
+#endif
+};
+
+/*
+ * Pull a packet from our receive queue and hand it to the user.
+ * If necessary we block.
+ */
+
+static int econet_recvmsg(struct socket *sock, struct msghdr *msg, int len,
+ int flags, struct scm_cookie *scm)
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int copied, err;
+
+ msg->msg_namelen = sizeof(struct sockaddr_ec);
+
+ /*
+ * Call the generic datagram receiver. This handles all sorts
+ * of horrible races and re-entrancy so we can forget about it
+ * in the protocol layers.
+ *
+ * Now it will return ENETDOWN, if device have just gone down,
+ * but then it will block.
+ */
+
+ skb=skb_recv_datagram(sk,flags,flags&MSG_DONTWAIT,&err);
+
+ /*
+ * An error occurred so return it. Because skb_recv_datagram()
+ * handles the blocking we don't see and worry about blocking
+ * retries.
+ */
+
+ if(skb==NULL)
+ goto out;
+
+ /*
+ * You lose any data beyond the buffer you gave. If it worries a
+ * user program they can ask the device for its MTU anyway.
+ */
+
+ copied = skb->len;
+ if (copied > len)
+ {
+ copied=len;
+ msg->msg_flags|=MSG_TRUNC;
+ }
+
+ /* We can't use skb_copy_datagram here */
+ err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
+ if (err)
+ goto out_free;
+ sk->stamp=skb->stamp;
+
+ if (msg->msg_name)
+ memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+
+ /*
+ * Free or return the buffer as appropriate. Again this
+ * hides all the races and re-entrancy issues from us.
+ */
+ err = copied;
+
+out_free:
+ skb_free_datagram(sk, skb);
+out:
+ return err;
+}
+
+/*
+ * Bind an Econet socket.
+ */
+
+static int econet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr;
+ struct sock *sk=sock->sk;
+
+ /*
+ * Check legality
+ */
+
+ if (addr_len < sizeof(struct sockaddr_ec))
+ return -EINVAL;
+ if (sec->sec_family != AF_ECONET)
+ return -EINVAL;
+
+ sk->protinfo.af_econet->cb = sec->cb;
+ sk->protinfo.af_econet->port = sec->port;
+ sk->protinfo.af_econet->station = sec->addr.station;
+ sk->protinfo.af_econet->net = sec->addr.net;
+
+ return 0;
+}
+
+/*
+ * Queue a transmit result for the user to be told about.
+ */
+
+static void tx_result(struct sock *sk, unsigned long cookie, int result)
+{
+ struct sk_buff *skb = alloc_skb(0, GFP_ATOMIC);
+ struct ec_cb *eb;
+ struct sockaddr_ec *sec;
+
+ if (skb == NULL)
+ {
+ printk(KERN_DEBUG "ec: memory squeeze, transmit result dropped.\n");
+ return;
+ }
+
+ eb = (struct ec_cb *)&skb->cb;
+ sec = (struct sockaddr_ec *)&eb->sec;
+ memset(sec, 0, sizeof(struct sockaddr_ec));
+ sec->cookie = cookie;
+ sec->type = ECTYPE_TRANSMIT_STATUS | result;
+ sec->sec_family = AF_ECONET;
+
+ if (sock_queue_rcv_skb(sk, skb) < 0)
+ kfree_skb(skb);
+}
+
+#ifdef CONFIG_ECONET_NATIVE
+/*
+ * Called by the Econet hardware driver when a packet transmit
+ * has completed. Tell the user.
+ */
+
+static void ec_tx_done(struct sk_buff *skb, int result)
+{
+ struct ec_cb *eb = (struct ec_cb *)&skb->cb;
+ tx_result(skb->sk, eb->cookie, result);
+}
+#endif
+
+/*
+ * Send a packet. We have to work out which device it's going out on
+ * and hence whether to use real Econet or the UDP emulation.
+ */
+
+static int econet_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+ struct scm_cookie *scm)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_ec *saddr=(struct sockaddr_ec *)msg->msg_name;
+ struct net_device *dev;
+ struct ec_addr addr;
+ int err;
+ unsigned char port, cb;
+ struct sk_buff *skb;
+ struct ec_cb *eb;
+#ifdef CONFIG_ECONET_NATIVE
+ unsigned short proto = 0;
+#endif
+#ifdef CONFIG_ECONET_AUNUDP
+ struct msghdr udpmsg;
+ struct iovec iov[msg->msg_iovlen+1];
+ struct aunhdr ah;
+ struct sockaddr_in udpdest;
+ __kernel_size_t size;
+ int i;
+ mm_segment_t oldfs;
+#endif
+
+ /*
+ * Check the flags.
+ */
+
+ if (msg->msg_flags&~MSG_DONTWAIT)
+ return(-EINVAL);
+
+ /*
+ * Get and verify the address.
+ */
+
+ if (saddr == NULL) {
+ addr.station = sk->protinfo.af_econet->station;
+ addr.net = sk->protinfo.af_econet->net;
+ port = sk->protinfo.af_econet->port;
+ cb = sk->protinfo.af_econet->cb;
+ } else {
+ if (msg->msg_namelen < sizeof(struct sockaddr_ec))
+ return -EINVAL;
+ addr.station = saddr->addr.station;
+ addr.net = saddr->addr.net;
+ port = saddr->port;
+ cb = saddr->cb;
+ }
+
+ /* Look for a device with the right network number. */
+ dev = net2dev_map[addr.net];
+
+ /* If not directly reachable, use some default */
+ if (dev == NULL)
+ {
+ dev = net2dev_map[0];
+ /* No interfaces at all? */
+ if (dev == NULL)
+ return -ENETDOWN;
+ }
+
+ if (dev->type == ARPHRD_ECONET)
+ {
+ /* Real hardware Econet. We're not worthy etc. */
+#ifdef CONFIG_ECONET_NATIVE
+ atomic_inc(&dev->refcnt);
+
+ skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+ if (skb==NULL)
+ goto out_unlock;
+
+ skb_reserve(skb, (dev->hard_header_len+15)&~15);
+ skb->nh.raw = skb->data;
+
+ eb = (struct ec_cb *)&skb->cb;
+
+ eb->cookie = saddr->cookie;
+ eb->sec = *saddr;
+ eb->sent = ec_tx_done;
+
+ if (dev->hard_header) {
+ int res;
+ struct ec_framehdr *fh;
+ err = -EINVAL;
+ res = dev->hard_header(skb, dev, ntohs(proto),
+ &addr, NULL, len);
+ /* Poke in our control byte and
+ port number. Hack, hack. */
+ fh = (struct ec_framehdr *)(skb->data);
+ fh->cb = cb;
+ fh->port = port;
+ if (sock->type != SOCK_DGRAM) {
+ skb->tail = skb->data;
+ skb->len = 0;
+ } else if (res < 0)
+ goto out_free;
+ }
+
+ /* Copy the data. Returns -EFAULT on error */
+ err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
+ skb->protocol = proto;
+ skb->dev = dev;
+ skb->priority = sk->priority;
+ if (err)
+ goto out_free;
+
+ err = -ENETDOWN;
+ if (!(dev->flags & IFF_UP))
+ goto out_free;
+
+ /*
+ * Now send it
+ */
+
+ dev_queue_xmit(skb);
+ dev_put(dev);
+ return(len);
+
+ out_free:
+ kfree_skb(skb);
+ out_unlock:
+ if (dev)
+ dev_put(dev);
+#else
+ err = -EPROTOTYPE;
+#endif
+ return err;
+ }
+
+#ifdef CONFIG_ECONET_AUNUDP
+ /* AUN virtual Econet. */
+
+ if (udpsock == NULL)
+ return -ENETDOWN; /* No socket - can't send */
+
+ /* Make up a UDP datagram and hand it off to some higher intellect. */
+
+ memset(&udpdest, 0, sizeof(udpdest));
+ udpdest.sin_family = AF_INET;
+ udpdest.sin_port = htons(AUN_PORT);
+
+ /* At the moment we use the stupid Acorn scheme of Econet address
+ y.x maps to IP a.b.c.x. This should be replaced with something
+ more flexible and more aware of subnet masks. */
+ {
+ struct in_device *idev = in_dev_get(dev);
+ unsigned long network = 0;
+ if (idev) {
+ read_lock(&idev->lock);
+ if (idev->ifa_list)
+ network = ntohl(idev->ifa_list->ifa_address) &
+ 0xffffff00; /* !!! */
+ read_unlock(&idev->lock);
+ in_dev_put(idev);
+ }
+ udpdest.sin_addr.s_addr = htonl(network | addr.station);
+ }
+
+ ah.port = port;
+ ah.cb = cb & 0x7f;
+ ah.code = 2; /* magic */
+ ah.pad = 0;
+
+ /* tack our header on the front of the iovec */
+ size = sizeof(struct aunhdr);
+ iov[0].iov_base = (void *)&ah;
+ iov[0].iov_len = size;
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ void *base = msg->msg_iov[i].iov_base;
+ size_t len = msg->msg_iov[i].iov_len;
+ /* Check it now since we switch to KERNEL_DS later. */
+ if ((err = verify_area(VERIFY_READ, base, len)) < 0)
+ return err;
+ iov[i+1].iov_base = base;
+ iov[i+1].iov_len = len;
+ size += len;
+ }
+
+ /* Get a skbuff (no data, just holds our cb information) */
+ if ((skb = sock_alloc_send_skb(sk, 0, 0,
+ msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
+ return err;
+
+ eb = (struct ec_cb *)&skb->cb;
+
+ eb->cookie = saddr->cookie;
+ eb->timeout = (5*HZ);
+ eb->start = jiffies;
+ ah.handle = aun_seq;
+ eb->seq = (aun_seq++);
+ eb->sec = *saddr;
+
+ skb_queue_tail(&aun_queue, skb);
+
+ udpmsg.msg_name = (void *)&udpdest;
+ udpmsg.msg_namelen = sizeof(udpdest);
+ udpmsg.msg_iov = &iov[0];
+ udpmsg.msg_iovlen = msg->msg_iovlen + 1;
+ udpmsg.msg_control = NULL;
+ udpmsg.msg_controllen = 0;
+ udpmsg.msg_flags=0;
+
+ oldfs = get_fs(); set_fs(KERNEL_DS); /* More privs :-) */
+ err = sock_sendmsg(udpsock, &udpmsg, size);
+ set_fs(oldfs);
+#else
+ err = -EPROTOTYPE;
+#endif
+ return err;
+}
+
+/*
+ * Look up the address of a socket.
+ */
+
+static int econet_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *uaddr_len, int peer)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr;
+
+ if (peer)
+ return -EOPNOTSUPP;
+
+ sec->sec_family = AF_ECONET;
+ sec->port = sk->protinfo.af_econet->port;
+ sec->addr.station = sk->protinfo.af_econet->station;
+ sec->addr.net = sk->protinfo.af_econet->net;
+
+ *uaddr_len = sizeof(*sec);
+ return 0;
+}
+
+static void econet_destroy_timer(unsigned long data)
+{
+ struct sock *sk=(struct sock *)data;
+
+ if (!atomic_read(&sk->wmem_alloc) && !atomic_read(&sk->rmem_alloc)) {
+ sk_free(sk);
+ MOD_DEC_USE_COUNT;
+ return;
+ }
+
+ sk->timer.expires=jiffies+10*HZ;
+ add_timer(&sk->timer);
+ printk(KERN_DEBUG "econet socket destroy delayed\n");
+}
+
+/*
+ * Close an econet socket.
+ */
+
+static int econet_release(struct socket *sock)
+{
+ struct sk_buff *skb;
+ struct sock *sk = sock->sk;
+
+ if (!sk)
+ return 0;
+
+ sklist_remove_socket(&econet_sklist, sk);
+
+ /*
+ * Now the socket is dead. No more input will appear.
+ */
+
+ sk->state_change(sk); /* It is useless. Just for sanity. */
+
+ sock->sk = NULL;
+ sk->socket = NULL;
+ sk->dead = 1;
+
+ /* Purge queues */
+
+ while ((skb=skb_dequeue(&sk->receive_queue))!=NULL)
+ kfree_skb(skb);
+
+ if (atomic_read(&sk->rmem_alloc) || atomic_read(&sk->wmem_alloc)) {
+ sk->timer.data=(unsigned long)sk;
+ sk->timer.expires=jiffies+HZ;
+ sk->timer.function=econet_destroy_timer;
+ add_timer(&sk->timer);
+ return 0;
+ }
+
+ sk_free(sk);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Create an Econet socket
+ */
+
+static int econet_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ int err;
+
+ /* Econet only provides datagram services. */
+ if (sock->type != SOCK_DGRAM)
+ return -ESOCKTNOSUPPORT;
+
+ sock->state = SS_UNCONNECTED;
+ MOD_INC_USE_COUNT;
+
+ err = -ENOBUFS;
+ sk = sk_alloc(PF_ECONET, GFP_KERNEL, 1);
+ if (sk == NULL)
+ goto out;
+
+ sk->reuse = 1;
+ sock->ops = &econet_ops;
+ sock_init_data(sock,sk);
+
+ sk->protinfo.af_econet = kmalloc(sizeof(struct econet_opt), GFP_KERNEL);
+ if (sk->protinfo.af_econet == NULL)
+ goto out_free;
+ memset(sk->protinfo.af_econet, 0, sizeof(struct econet_opt));
+ sk->zapped=0;
+ sk->family = PF_ECONET;
+ sk->num = protocol;
+
+ sklist_insert_socket(&econet_sklist, sk);
+ return(0);
+
+out_free:
+ sk_free(sk);
+out:
+ MOD_DEC_USE_COUNT;
+ return err;
+}
+
+/*
+ * Handle Econet specific ioctls
+ */
+
+static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void *arg)
+{
+ struct ifreq ifr;
+ struct ec_device *edev;
+ struct net_device *dev;
+ struct sockaddr_ec *sec;
+
+ /*
+ * Fetch the caller's info block into kernel space
+ */
+
+ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+ return -EFAULT;
+
+ if ((dev = dev_get_by_name(ifr.ifr_name)) == NULL)
+ return -ENODEV;
+
+ sec = (struct sockaddr_ec *)&ifr.ifr_addr;
+
+ switch (cmd)
+ {
+ case SIOCSIFADDR:
+ edev = dev->ec_ptr;
+ if (edev == NULL)
+ {
+ /* Magic up a new one. */
+ edev = kmalloc(GFP_KERNEL, sizeof(struct ec_device));
+ if (edev == NULL) {
+ printk("af_ec: memory squeeze.\n");
+ dev_put(dev);
+ return -ENOMEM;
+ }
+ memset(edev, 0, sizeof(struct ec_device));
+ dev->ec_ptr = edev;
+ }
+ else
+ net2dev_map[edev->net] = NULL;
+ edev->station = sec->addr.station;
+ edev->net = sec->addr.net;
+ net2dev_map[sec->addr.net] = dev;
+ if (!net2dev_map[0])
+ net2dev_map[0] = dev;
+ dev_put(dev);
+ return 0;
+
+ case SIOCGIFADDR:
+ edev = dev->ec_ptr;
+ if (edev == NULL)
+ {
+ dev_put(dev);
+ return -ENODEV;
+ }
+ memset(sec, 0, sizeof(struct sockaddr_ec));
+ sec->addr.station = edev->station;
+ sec->addr.net = edev->net;
+ sec->sec_family = AF_ECONET;
+ dev_put(dev);
+ if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ return -EFAULT;
+ return 0;
+ }
+
+ dev_put(dev);
+ return -EINVAL;
+}
+
+/*
+ * Handle generic ioctls
+ */
+
+static int econet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+ int err;
+ int pid;
+
+ switch(cmd)
+ {
+ case FIOSETOWN:
+ case SIOCSPGRP:
+ err = get_user(pid, (int *) arg);
+ if (err)
+ return err;
+ if (current->pid != pid && current->pgrp != -pid && !suser())
+ return -EPERM;
+ sk->proc = pid;
+ return(0);
+ case FIOGETOWN:
+ case SIOCGPGRP:
+ return put_user(sk->proc, (int *)arg);
+ case SIOCGSTAMP:
+ if(sk->stamp.tv_sec==0)
+ return -ENOENT;
+ err = -EFAULT;
+ if (!copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)))
+ err = 0;
+ return err;
+ case SIOCGIFFLAGS:
+ case SIOCSIFFLAGS:
+ case SIOCGIFCONF:
+ case SIOCGIFMETRIC:
+ case SIOCSIFMETRIC:
+ case SIOCGIFMEM:
+ case SIOCSIFMEM:
+ case SIOCGIFMTU:
+ case SIOCSIFMTU:
+ case SIOCSIFLINK:
+ case SIOCGIFHWADDR:
+ case SIOCSIFHWADDR:
+ case SIOCSIFMAP:
+ case SIOCGIFMAP:
+ case SIOCSIFSLAVE:
+ case SIOCGIFSLAVE:
+ case SIOCGIFINDEX:
+ case SIOCGIFNAME:
+ case SIOCGIFCOUNT:
+ case SIOCSIFHWBROADCAST:
+ return(dev_ioctl(cmd,(void *) arg));
+
+ case SIOCSIFADDR:
+ case SIOCGIFADDR:
+ return ec_dev_ioctl(sock, cmd, (void *)arg);
+ break;
+
+ default:
+ return(dev_ioctl(cmd,(void *) arg));
+ }
+ /*NOTREACHED*/
+ return 0;
+}
+
+static struct net_proto_family econet_family_ops = {
+ PF_ECONET,
+ econet_create
+};
+
+static struct proto_ops SOCKOPS_WRAPPED(econet_ops) = {
+ PF_ECONET,
+
+ econet_release,
+ econet_bind,
+ sock_no_connect,
+ sock_no_socketpair,
+ sock_no_accept,
+ econet_getname,
+ datagram_poll,
+ econet_ioctl,
+ sock_no_listen,
+ sock_no_shutdown,
+ sock_no_setsockopt,
+ sock_no_getsockopt,
+ sock_no_fcntl,
+ econet_sendmsg,
+ econet_recvmsg,
+ sock_no_mmap
+};
+
+#include <linux/smp_lock.h>
+SOCKOPS_WRAP(econet, PF_ECONET);
+
+/*
+ * Find the listening socket, if any, for the given data.
+ */
+
+struct sock *ec_listening_socket(unsigned char port, unsigned char
+ station, unsigned char net)
+{
+ struct sock *sk = econet_sklist;
+
+ while (sk)
+ {
+ struct econet_opt *opt = sk->protinfo.af_econet;
+ if ((opt->port == port || opt->port == 0) &&
+ (opt->station == station || opt->station == 0) &&
+ (opt->net == net || opt->net == 0))
+ return sk;
+
+ sk = sk->next;
+ }
+
+ return NULL;
+}
+
+#ifdef CONFIG_ECONET_AUNUDP
+
+/*
+ * Send an AUN protocol response.
+ */
+
+static void aun_send_response(__u32 addr, unsigned long seq, int code, int cb)
+{
+ struct sockaddr_in sin;
+ struct iovec iov;
+ struct aunhdr ah;
+ struct msghdr udpmsg;
+ int err;
+ mm_segment_t oldfs;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(AUN_PORT);
+ sin.sin_addr.s_addr = addr;
+
+ ah.code = code;
+ ah.pad = 0;
+ ah.port = 0;
+ ah.cb = cb;
+ ah.handle = seq;
+
+ iov.iov_base = (void *)&ah;
+ iov.iov_len = sizeof(ah);
+
+ udpmsg.msg_name = (void *)&sin;
+ udpmsg.msg_namelen = sizeof(sin);
+ udpmsg.msg_iov = &iov;
+ udpmsg.msg_iovlen = 1;
+ udpmsg.msg_control = NULL;
+ udpmsg.msg_controllen = 0;
+ udpmsg.msg_flags=0;
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ err = sock_sendmsg(udpsock, &udpmsg, sizeof(ah));
+ set_fs(oldfs);
+}
+
+/*
+ * Queue a received packet for a socket.
+ */
+
+static int ec_queue_packet(struct sock *sk, struct sk_buff *skb,
+ unsigned char stn, unsigned char net,
+ unsigned char cb, unsigned char port)
+{
+ struct ec_cb *eb = (struct ec_cb *)&skb->cb;
+ struct sockaddr_ec *sec = (struct sockaddr_ec *)&eb->sec;
+
+ memset(sec, 0, sizeof(struct sockaddr_ec));
+ sec->sec_family = AF_ECONET;
+ sec->type = ECTYPE_PACKET_RECEIVED;
+ sec->port = port;
+ sec->cb = cb;
+ sec->addr.net = net;
+ sec->addr.station = stn;
+
+ return sock_queue_rcv_skb(sk, skb);
+}
+
+/*
+ * Handle incoming AUN packets. Work out if anybody wants them,
+ * and send positive or negative acknowledgements as appropriate.
+ */
+
+static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len)
+{
+ struct iphdr *ip = skb->nh.iph;
+ unsigned char stn = ntohl(ip->saddr) & 0xff;
+ struct sock *sk;
+ struct sk_buff *newskb;
+ struct ec_device *edev = skb->dev->ec_ptr;
+
+ if (! edev)
+ goto bad;
+
+ if ((sk = ec_listening_socket(ah->port, stn, edev->net)) == NULL)
+ goto bad; /* Nobody wants it */
+
+ newskb = alloc_skb((len - sizeof(struct aunhdr) + 15) & ~15,
+ GFP_ATOMIC);
+ if (newskb == NULL)
+ {
+ printk(KERN_DEBUG "AUN: memory squeeze, dropping packet.\n");
+ /* Send nack and hope sender tries again */
+ goto bad;
+ }
+
+ memcpy(skb_put(newskb, len - sizeof(struct aunhdr)), (void *)(ah+1),
+ len - sizeof(struct aunhdr));
+
+ if (ec_queue_packet(sk, newskb, stn, edev->net, ah->cb, ah->port))
+ {
+ /* Socket is bankrupt. */
+ kfree_skb(newskb);
+ goto bad;
+ }
+
+ aun_send_response(ip->saddr, ah->handle, 3, 0);
+ return;
+
+bad:
+ aun_send_response(ip->saddr, ah->handle, 4, 0);
+}
+
+/*
+ * Handle incoming AUN transmit acknowledgements. If the sequence
+ * number matches something in our backlog then kill it and tell
+ * the user. If the remote took too long to reply then we may have
+ * dropped the packet already.
+ */
+
+static void aun_tx_ack(unsigned long seq, int result)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+ struct ec_cb *eb;
+
+ spin_lock_irqsave(&aun_queue_lock, flags);
+ skb = skb_peek(&aun_queue);
+ while (skb && skb != (struct sk_buff *)&aun_queue)
+ {
+ struct sk_buff *newskb = skb->next;
+ eb = (struct ec_cb *)&skb->cb;
+ if (eb->seq == seq)
+ goto foundit;
+
+ skb = newskb;
+ }
+ spin_unlock_irqrestore(&aun_queue_lock, flags);
+ printk(KERN_DEBUG "AUN: unknown sequence %ld\n", seq);
+ return;
+
+foundit:
+ tx_result(skb->sk, eb->cookie, result);
+ skb_unlink(skb);
+ spin_unlock_irqrestore(&aun_queue_lock, flags);
+ kfree_skb(skb);
+}
+
+/*
+ * Deal with received AUN frames - sort out what type of thing it is
+ * and hand it to the right function.
+ */
+
+static void aun_data_available(struct sock *sk, int slen)
+{
+ int err;
+ struct sk_buff *skb;
+ unsigned char *data;
+ struct aunhdr *ah;
+ struct iphdr *ip;
+ size_t len;
+
+ while ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL) {
+ if (err == -EAGAIN) {
+ printk(KERN_ERR "AUN: no data available?!");
+ return;
+ }
+ printk(KERN_DEBUG "AUN: recvfrom() error %d\n", -err);
+ }
+
+ data = skb->h.raw + sizeof(struct udphdr);
+ ah = (struct aunhdr *)data;
+ len = skb->len - sizeof(struct udphdr);
+ ip = skb->nh.iph;
+
+ switch (ah->code)
+ {
+ case 2:
+ aun_incoming(skb, ah, len);
+ break;
+ case 3:
+ aun_tx_ack(ah->handle, ECTYPE_TRANSMIT_OK);
+ break;
+ case 4:
+ aun_tx_ack(ah->handle, ECTYPE_TRANSMIT_NOT_LISTENING);
+ break;
+#if 0
+ /* This isn't quite right yet. */
+ case 5:
+ aun_send_response(ip->saddr, ah->handle, 6, ah->cb);
+ break;
+#endif
+ default:
+ printk(KERN_DEBUG "unknown AUN packet (type %d)\n", data[0]);
+ }
+
+ skb_free_datagram(sk, skb);
+}
+
+/*
+ * Called by the timer to manage the AUN transmit queue. If a packet
+ * was sent to a dead or nonexistent host then we will never get an
+ * acknowledgement back. After a few seconds we need to spot this and
+ * drop the packet.
+ */
+
+static void ab_cleanup(unsigned long h)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&aun_queue_lock, flags);
+ skb = skb_peek(&aun_queue);
+ while (skb && skb != (struct sk_buff *)&aun_queue)
+ {
+ struct sk_buff *newskb = skb->next;
+ struct ec_cb *eb = (struct ec_cb *)&skb->cb;
+ if ((jiffies - eb->start) > eb->timeout)
+ {
+ tx_result(skb->sk, eb->cookie,
+ ECTYPE_TRANSMIT_NOT_PRESENT);
+ skb_unlink(skb);
+ kfree_skb(skb);
+ }
+ skb = newskb;
+ }
+ spin_unlock_irqrestore(&aun_queue_lock, flags);
+
+ mod_timer(&ab_cleanup_timer, jiffies + (HZ*2));
+}
+
+static int __init aun_udp_initialise(void)
+{
+ int error;
+ struct sockaddr_in sin;
+
+ skb_queue_head_init(&aun_queue);
+ spin_lock_init(&aun_queue_lock);
+ init_timer(&ab_cleanup_timer);
+ ab_cleanup_timer.expires = jiffies + (HZ*2);
+ ab_cleanup_timer.function = ab_cleanup;
+ add_timer(&ab_cleanup_timer);
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_port = htons(AUN_PORT);
+
+ /* We can count ourselves lucky Acorn machines are too dim to
+ speak IPv6. :-) */
+ if ((error = sock_create(PF_INET, SOCK_DGRAM, 0, &udpsock)) < 0)
+ {
+ printk("AUN: socket error %d\n", -error);
+ return error;
+ }
+
+ udpsock->sk->reuse = 1;
+ udpsock->sk->allocation = GFP_ATOMIC; /* we're going to call it
+ from interrupts */
+
+ error = udpsock->ops->bind(udpsock, (struct sockaddr *)&sin,
+ sizeof(sin));
+ if (error < 0)
+ {
+ printk("AUN: bind error %d\n", -error);
+ goto release;
+ }
+
+ udpsock->sk->data_ready = aun_data_available;
+
+ return 0;
+
+release:
+ sock_release(udpsock);
+ udpsock = NULL;
+ return error;
+}
+#endif
+
+#ifdef CONFIG_ECONET_NATIVE
+
+/*
+ * Receive an Econet frame from a device.
+ */
+
+static int econet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct ec_framehdr *hdr = (struct ec_framehdr *)skb->data;
+ struct sock *sk;
+ struct ec_device *edev = dev->ec_ptr;
+
+ if (! edev)
+ {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (skb->len < sizeof(struct ec_framehdr))
+ {
+ /* Frame is too small to be any use */
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* First check for encapsulated IP */
+ if (hdr->port == EC_PORT_IP)
+ {
+ skb->protocol = htons(ETH_P_IP);
+ skb_pull(skb, sizeof(struct ec_framehdr));
+ netif_rx(skb);
+ return 0;
+ }
+
+ sk = ec_listening_socket(hdr->port, hdr->src_stn, hdr->src_net);
+ if (!sk)
+ {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ return ec_queue_packet(sk, skb, edev->net, hdr->src_stn, hdr->cb,
+ hdr->port);
+}
+
+struct packet_type econet_packet_type=
+{
+ 0,
+ NULL,
+ econet_rcv,
+ NULL,
+ NULL
+};
+
+static void econet_hw_initialise(void)
+{
+ econet_packet_type.type = htons(ETH_P_ECONET);
+ dev_add_pack(&econet_packet_type);
+}
+
+#endif
+
+static int econet_notifier(struct notifier_block *this, unsigned long msg, void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct ec_device *edev;
+
+ switch (msg) {
+ case NETDEV_UNREGISTER:
+ /* A device has gone down - kill any data we hold for it. */
+ edev = dev->ec_ptr;
+ if (edev)
+ {
+ if (net2dev_map[0] == dev)
+ net2dev_map[0] = 0;
+ net2dev_map[edev->net] = NULL;
+ kfree(edev);
+ dev->ec_ptr = NULL;
+ }
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+struct notifier_block econet_netdev_notifier={
+ econet_notifier,
+ NULL,
+ 0
+};
+
+void __exit econet_proto_exit(void)
+{
+ extern void econet_sysctl_unregister(void);
+#ifdef CONFIG_ECONET_AUNUDP
+ del_timer(&ab_cleanup_timer);
+ if (udpsock)
+ sock_release(udpsock);
+#endif
+ unregister_netdevice_notifier(&econet_netdev_notifier);
+ sock_unregister(econet_family_ops.family);
+#ifdef CONFIG_SYSCTL
+ econet_sysctl_unregister();
+#endif
+}
+
+int __init econet_proto_init(struct net_proto *pro)
+{
+ extern void econet_sysctl_register(void);
+ spin_lock_init(&aun_queue_lock);
+ sock_register(&econet_family_ops);
+#ifdef CONFIG_ECONET_AUNUDP
+ aun_udp_initialise();
+#endif
+#ifdef CONFIG_ECONET_NATIVE
+ econet_hw_initialise();
+#endif
+ register_netdevice_notifier(&econet_netdev_notifier);
+#ifdef CONFIG_SYSCTL
+ econet_sysctl_register();
+#endif
+ return 0;
+}
+
+#ifdef MODULE
+module_init(econet_proto_init);
+module_exit(econet_proto_exit);
+#endif
+++ /dev/null
-/*
- * An implementation of the Acorn Econet and AUN protocols.
- * Philip Blundell <philb@gnu.org>
- *
- * Fixes:
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/in.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/if_ether.h>
-#include <linux/netdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/route.h>
-#include <linux/inet.h>
-#include <linux/etherdevice.h>
-#include <linux/if_arp.h>
-#include <linux/wireless.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <net/inet_common.h>
-#include <linux/stat.h>
-#include <linux/init.h>
-#include <linux/if_ec.h>
-#include <net/udp.h>
-#include <net/ip.h>
-#include <linux/spinlock.h>
-
-static struct proto_ops econet_ops;
-static struct sock *econet_sklist;
-
-static spinlock_t aun_queue_lock;
-
-#ifdef CONFIG_ECONET_AUNUDP
-static struct socket *udpsock;
-#define AUN_PORT 0x8000
-
-struct aunhdr
-{
- unsigned char code; /* AUN magic protocol byte */
- unsigned char port;
- unsigned char cb;
- unsigned char pad;
- unsigned long handle;
-};
-
-static unsigned long aun_seq = 0;
-
-/* Queue of packets waiting to be transmitted. */
-static struct sk_buff_head aun_queue;
-static struct timer_list ab_cleanup_timer;
-
-#endif /* CONFIG_ECONET_AUNUDP */
-
-/* Per-packet information */
-struct ec_cb
-{
- struct sockaddr_ec sec;
- unsigned long cookie; /* Supplied by user. */
-#ifdef CONFIG_ECONET_AUNUDP
- int done;
- unsigned long seq; /* Sequencing */
- unsigned long timeout; /* Timeout */
- unsigned long start; /* jiffies */
-#endif
-#ifdef CONFIG_ECONET_NATIVE
- void (*sent)(struct sk_buff *, int result);
-#endif
-};
-
-struct ec_device
-{
- struct net_device *dev; /* Real device structure */
- unsigned char station, net; /* Econet protocol address */
- struct ec_device *prev, *next; /* Linked list */
-};
-
-static struct ec_device *edevlist = NULL;
-
-static spinlock_t edevlist_lock;
-
-/*
- * Faster version of edev_get - call with IRQs off
- */
-
-static __inline__ struct ec_device *__edev_get(struct net_device *dev)
-{
- struct ec_device *edev;
- for (edev = edevlist; edev; edev = edev->next)
- {
- if (edev->dev == dev)
- break;
- }
- return edev;
-}
-
-/*
- * Find an Econet device given its `dev' pointer. This is IRQ safe.
- *
- * Against what is it safe? --ANK
- */
-
-static struct ec_device *edev_get(struct net_device *dev)
-{
- struct ec_device *edev;
- unsigned long flags;
- spin_lock_irqsave(&edevlist_lock, flags);
- edev = __edev_get(dev);
- spin_unlock_irqrestore(&edevlist_lock, flags);
- return edev;
-}
-
-/*
- * Pull a packet from our receive queue and hand it to the user.
- * If necessary we block.
- */
-
-static int econet_recvmsg(struct socket *sock, struct msghdr *msg, int len,
- int flags, struct scm_cookie *scm)
-{
- struct sock *sk = sock->sk;
- struct sk_buff *skb;
- int copied, err;
-
- msg->msg_namelen = sizeof(struct sockaddr_ec);
-
- /*
- * Call the generic datagram receiver. This handles all sorts
- * of horrible races and re-entrancy so we can forget about it
- * in the protocol layers.
- *
- * Now it will return ENETDOWN, if device have just gone down,
- * but then it will block.
- */
-
- skb=skb_recv_datagram(sk,flags,flags&MSG_DONTWAIT,&err);
-
- /*
- * An error occurred so return it. Because skb_recv_datagram()
- * handles the blocking we don't see and worry about blocking
- * retries.
- */
-
- if(skb==NULL)
- goto out;
-
- /*
- * You lose any data beyond the buffer you gave. If it worries a
- * user program they can ask the device for its MTU anyway.
- */
-
- copied = skb->len;
- if (copied > len)
- {
- copied=len;
- msg->msg_flags|=MSG_TRUNC;
- }
-
- /* We can't use skb_copy_datagram here */
- err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
- if (err)
- goto out_free;
- sk->stamp=skb->stamp;
-
- if (msg->msg_name)
- memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
-
- /*
- * Free or return the buffer as appropriate. Again this
- * hides all the races and re-entrancy issues from us.
- */
- err = copied;
-
-out_free:
- skb_free_datagram(sk, skb);
-out:
- return err;
-}
-
-/*
- * Bind an Econet socket.
- */
-
-static int econet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
-{
- struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr;
- struct sock *sk=sock->sk;
-
- /*
- * Check legality
- */
-
- if (addr_len < sizeof(struct sockaddr_ec))
- return -EINVAL;
- if (sec->sec_family != AF_ECONET)
- return -EINVAL;
-
- sk->protinfo.af_econet->cb = sec->cb;
- sk->protinfo.af_econet->port = sec->port;
- sk->protinfo.af_econet->station = sec->addr.station;
- sk->protinfo.af_econet->net = sec->addr.net;
-
- return 0;
-}
-
-/*
- * Queue a transmit result for the user to be told about.
- */
-
-static void tx_result(struct sock *sk, unsigned long cookie, int result)
-{
- struct sk_buff *skb = alloc_skb(0, GFP_ATOMIC);
- struct ec_cb *eb;
- struct sockaddr_ec *sec;
-
- if (skb == NULL)
- {
- printk(KERN_DEBUG "ec: memory squeeze, transmit result dropped.\n");
- return;
- }
-
- eb = (struct ec_cb *)&skb->cb;
- sec = (struct sockaddr_ec *)&eb->sec;
- memset(sec, 0, sizeof(struct sockaddr_ec));
- sec->cookie = cookie;
- sec->type = ECTYPE_TRANSMIT_STATUS | result;
- sec->sec_family = AF_ECONET;
-
- if (sock_queue_rcv_skb(sk, skb) < 0)
- kfree_skb(skb);
-}
-
-#ifdef CONFIG_ECONET_NATIVE
-/*
- * Called by the Econet hardware driver when a packet transmit
- * has completed. Tell the user.
- */
-
-static void ec_tx_done(struct sk_buff *skb, int result)
-{
- struct ec_cb *eb = (struct ec_cb *)&skb->cb;
- tx_result(skb->sk, eb->cookie, result);
-}
-#endif
-
-/*
- * Send a packet. We have to work out which device it's going out on
- * and hence whether to use real Econet or the UDP emulation.
- */
-
-static int econet_sendmsg(struct socket *sock, struct msghdr *msg, int len,
- struct scm_cookie *scm)
-{
- struct sock *sk = sock->sk;
- struct sockaddr_ec *saddr=(struct sockaddr_ec *)msg->msg_name;
- struct net_device *dev;
- struct ec_addr addr;
- struct ec_device *edev;
- int err;
- unsigned char port, cb;
- struct sk_buff *skb;
- struct ec_cb *eb;
-#ifdef CONFIG_ECONET_NATIVE
- unsigned short proto = 0;
-#endif
-#ifdef CONFIG_ECONET_AUNUDP
- struct msghdr udpmsg;
- struct iovec iov[msg->msg_iovlen+1];
- struct aunhdr ah;
- struct sockaddr_in udpdest;
- __kernel_size_t size;
- int i;
- mm_segment_t oldfs;
-#endif
-
- /*
- * Check the flags.
- */
-
- if (msg->msg_flags&~MSG_DONTWAIT)
- return(-EINVAL);
-
- /*
- * Get and verify the address.
- */
-
- if (saddr == NULL) {
- addr.station = sk->protinfo.af_econet->station;
- addr.net = sk->protinfo.af_econet->net;
- port = sk->protinfo.af_econet->port;
- cb = sk->protinfo.af_econet->cb;
- } else {
- if (msg->msg_namelen < sizeof(struct sockaddr_ec))
- return -EINVAL;
- addr.station = saddr->addr.station;
- addr.net = saddr->addr.net;
- port = saddr->port;
- cb = saddr->cb;
- }
-
- /* Look for a device with the right network number. */
- for (edev = edevlist; edev && (edev->net != addr.net);
- edev = edev->next);
-
- /* Bridge? What's that? */
- if (edev == NULL)
- return -ENETUNREACH;
-
- dev = edev->dev;
-
- if (dev->type == ARPHRD_ECONET)
- {
- /* Real hardware Econet. We're not worthy etc. */
-#ifdef CONFIG_ECONET_NATIVE
- atomic_inc(&dev->refcnt);
-
- skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0,
- msg->msg_flags & MSG_DONTWAIT, &err);
- if (skb==NULL)
- goto out_unlock;
-
- skb_reserve(skb, (dev->hard_header_len+15)&~15);
- skb->nh.raw = skb->data;
-
- eb = (struct ec_cb *)&skb->cb;
-
- eb->cookie = saddr->cookie;
- eb->sec = *saddr;
- eb->sent = ec_tx_done;
-
- if (dev->hard_header) {
- int res;
- err = -EINVAL;
- res = dev->hard_header(skb, dev, ntohs(proto), &addr, NULL, len);
- if (sock->type != SOCK_DGRAM) {
- skb->tail = skb->data;
- skb->len = 0;
- } else if (res < 0)
- goto out_free;
- }
-
- /* Copy the data. Returns -EFAULT on error */
- err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
- skb->protocol = proto;
- skb->dev = dev;
- skb->priority = sk->priority;
- if (err)
- goto out_free;
-
- err = -ENETDOWN;
- if (!(dev->flags & IFF_UP))
- goto out_free;
-
- /*
- * Now send it
- */
-
- dev_queue_xmit(skb);
- dev_put(dev);
- return(len);
-
- out_free:
- kfree_skb(skb);
- out_unlock:
- if (dev)
- dev_put(dev);
-#else
- err = -EPROTOTYPE;
-#endif
- return err;
- }
-
-#ifdef CONFIG_ECONET_AUNUDP
- /* AUN virtual Econet. */
-
- if (udpsock == NULL)
- return -ENETDOWN; /* No socket - can't send */
-
- /* Make up a UDP datagram and hand it off to some higher intellect. */
-
- memset(&udpdest, 0, sizeof(udpdest));
- udpdest.sin_family = AF_INET;
- udpdest.sin_port = htons(AUN_PORT);
-
- /* At the moment we use the stupid Acorn scheme of Econet address
- y.x maps to IP a.b.c.x. This should be replaced with something
- more flexible and more aware of subnet masks. */
- {
- struct in_device *idev = in_dev_get(dev);
- unsigned long network = 0;
- if (idev) {
- read_lock(&idev->lock);
- if (idev->ifa_list)
- network = ntohl(idev->ifa_list->ifa_address) &
- 0xffffff00; /* !!! */
- read_unlock(&idev->lock);
- in_dev_put(idev);
- }
- udpdest.sin_addr.s_addr = htonl(network | addr.station);
- }
-
- ah.port = port;
- ah.cb = cb & 0x7f;
- ah.code = 2; /* magic */
- ah.pad = 0;
-
- /* tack our header on the front of the iovec */
- size = sizeof(struct aunhdr);
- iov[0].iov_base = (void *)&ah;
- iov[0].iov_len = size;
- for (i = 0; i < msg->msg_iovlen; i++) {
- void *base = msg->msg_iov[i].iov_base;
- size_t len = msg->msg_iov[i].iov_len;
- /* Check it now since we switch to KERNEL_DS later. */
- if ((err = verify_area(VERIFY_READ, base, len)) < 0)
- return err;
- iov[i+1].iov_base = base;
- iov[i+1].iov_len = len;
- size += len;
- }
-
- /* Get a skbuff (no data, just holds our cb information) */
- if ((skb = sock_alloc_send_skb(sk, 0, 0,
- msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
- return err;
-
- eb = (struct ec_cb *)&skb->cb;
-
- eb->cookie = saddr->cookie;
- eb->timeout = (5*HZ);
- eb->start = jiffies;
- ah.handle = aun_seq;
- eb->seq = (aun_seq++);
- eb->sec = *saddr;
-
- skb_queue_tail(&aun_queue, skb);
-
- udpmsg.msg_name = (void *)&udpdest;
- udpmsg.msg_namelen = sizeof(udpdest);
- udpmsg.msg_iov = &iov[0];
- udpmsg.msg_iovlen = msg->msg_iovlen + 1;
- udpmsg.msg_control = NULL;
- udpmsg.msg_controllen = 0;
- udpmsg.msg_flags=0;
-
- oldfs = get_fs(); set_fs(KERNEL_DS); /* More privs :-) */
- err = sock_sendmsg(udpsock, &udpmsg, size);
- set_fs(oldfs);
-#else
- err = -EPROTOTYPE;
-#endif
- return err;
-}
-
-/*
- * Look up the address of a socket.
- */
-
-static int econet_getname(struct socket *sock, struct sockaddr *uaddr,
- int *uaddr_len, int peer)
-{
- struct sock *sk = sock->sk;
- struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr;
-
- if (peer)
- return -EOPNOTSUPP;
-
- sec->sec_family = AF_ECONET;
- sec->port = sk->protinfo.af_econet->port;
- sec->addr.station = sk->protinfo.af_econet->station;
- sec->addr.net = sk->protinfo.af_econet->net;
-
- *uaddr_len = sizeof(*sec);
- return 0;
-}
-
-static void econet_destroy_timer(unsigned long data)
-{
- struct sock *sk=(struct sock *)data;
-
- if (!atomic_read(&sk->wmem_alloc) && !atomic_read(&sk->rmem_alloc)) {
- sk_free(sk);
- MOD_DEC_USE_COUNT;
- return;
- }
-
- sk->timer.expires=jiffies+10*HZ;
- add_timer(&sk->timer);
- printk(KERN_DEBUG "econet socket destroy delayed\n");
-}
-
-/*
- * Close an econet socket.
- */
-
-static int econet_release(struct socket *sock)
-{
- struct sk_buff *skb;
- struct sock *sk = sock->sk;
-
- if (!sk)
- return 0;
-
- sklist_remove_socket(&econet_sklist, sk);
-
- /*
- * Now the socket is dead. No more input will appear.
- */
-
- sk->state_change(sk); /* It is useless. Just for sanity. */
-
- sock->sk = NULL;
- sk->socket = NULL;
- sk->dead = 1;
-
- /* Purge queues */
-
- while ((skb=skb_dequeue(&sk->receive_queue))!=NULL)
- kfree_skb(skb);
-
- if (atomic_read(&sk->rmem_alloc) || atomic_read(&sk->wmem_alloc)) {
- sk->timer.data=(unsigned long)sk;
- sk->timer.expires=jiffies+HZ;
- sk->timer.function=econet_destroy_timer;
- add_timer(&sk->timer);
- return 0;
- }
-
- sk_free(sk);
- MOD_DEC_USE_COUNT;
- return 0;
-}
-
-/*
- * Create an Econet socket
- */
-
-static int econet_create(struct socket *sock, int protocol)
-{
- struct sock *sk;
- int err;
-
- /* Econet only provides datagram services. */
- if (sock->type != SOCK_DGRAM)
- return -ESOCKTNOSUPPORT;
-
- sock->state = SS_UNCONNECTED;
- MOD_INC_USE_COUNT;
-
- err = -ENOBUFS;
- sk = sk_alloc(PF_ECONET, GFP_KERNEL, 1);
- if (sk == NULL)
- goto out;
-
- sk->reuse = 1;
- sock->ops = &econet_ops;
- sock_init_data(sock,sk);
-
- sk->protinfo.af_econet = kmalloc(sizeof(struct econet_opt), GFP_KERNEL);
- if (sk->protinfo.af_econet == NULL)
- goto out_free;
- memset(sk->protinfo.af_econet, 0, sizeof(struct econet_opt));
- sk->zapped=0;
- sk->family = PF_ECONET;
- sk->num = protocol;
-
- sklist_insert_socket(&econet_sklist, sk);
- return(0);
-
-out_free:
- sk_free(sk);
-out:
- MOD_DEC_USE_COUNT;
- return err;
-}
-
-/*
- * Handle Econet specific ioctls
- */
-
-static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void *arg)
-{
- struct ifreq ifr;
- struct ec_device *edev;
- struct net_device *dev;
- unsigned long flags;
- struct sockaddr_ec *sec;
-
- /*
- * Fetch the caller's info block into kernel space
- */
-
- if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
- return -EFAULT;
-
- if ((dev = dev_get_by_name(ifr.ifr_name)) == NULL)
- return -ENODEV;
-
- sec = (struct sockaddr_ec *)&ifr.ifr_addr;
-
- switch (cmd)
- {
- case SIOCSIFADDR:
- spin_lock_irqsave(&edevlist_lock, flags);
- edev = __edev_get(dev);
- if (edev == NULL)
- {
- /* Magic up a new one. */
- printk("Get fascist grenade!!!\n");
- *(int*)0 = 0;
- /* Note to author: please, remove this spinlock.
- You do not change edevlist from interrupts,
- so that such aggressive protection is redundant.
-
- BTW not all scans of edev_list are protected.
- */
- edev = kmalloc(GFP_KERNEL, sizeof(struct ec_device));
- if (edev == NULL) {
- printk("af_ec: memory squeeze.\n");
- spin_unlock_irqrestore(&edevlist_lock, flags);
- dev_put(dev);
- return -ENOMEM;
- }
- memset(edev, 0, sizeof(struct ec_device));
- edev->dev = dev;
- edev->next = edevlist;
- edevlist = edev;
- }
- edev->station = sec->addr.station;
- edev->net = sec->addr.net;
- spin_unlock_irqrestore(&edevlist_lock, flags);
- dev_put(dev);
- return 0;
-
- case SIOCGIFADDR:
- spin_lock_irqsave(&edevlist_lock, flags);
- edev = __edev_get(dev);
- if (edev == NULL)
- {
- spin_unlock_irqrestore(&edevlist_lock, flags);
- dev_put(dev);
- return -ENODEV;
- }
- memset(sec, 0, sizeof(struct sockaddr_ec));
- sec->addr.station = edev->station;
- sec->addr.net = edev->net;
- sec->sec_family = AF_ECONET;
- spin_unlock_irqrestore(&edevlist_lock, flags);
- dev_put(dev);
- if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
- return -EFAULT;
- return 0;
- }
-
- dev_put(dev);
- return -EINVAL;
-}
-
-/*
- * Handle generic ioctls
- */
-
-static int econet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
- struct sock *sk = sock->sk;
- int err;
- int pid;
-
- switch(cmd)
- {
- case FIOSETOWN:
- case SIOCSPGRP:
- err = get_user(pid, (int *) arg);
- if (err)
- return err;
- if (current->pid != pid && current->pgrp != -pid && !suser())
- return -EPERM;
- sk->proc = pid;
- return(0);
- case FIOGETOWN:
- case SIOCGPGRP:
- return put_user(sk->proc, (int *)arg);
- case SIOCGSTAMP:
- if(sk->stamp.tv_sec==0)
- return -ENOENT;
- err = -EFAULT;
- if (!copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)))
- err = 0;
- return err;
- case SIOCGIFFLAGS:
- case SIOCSIFFLAGS:
- case SIOCGIFCONF:
- case SIOCGIFMETRIC:
- case SIOCSIFMETRIC:
- case SIOCGIFMEM:
- case SIOCSIFMEM:
- case SIOCGIFMTU:
- case SIOCSIFMTU:
- case SIOCSIFLINK:
- case SIOCGIFHWADDR:
- case SIOCSIFHWADDR:
- case SIOCSIFMAP:
- case SIOCGIFMAP:
- case SIOCSIFSLAVE:
- case SIOCGIFSLAVE:
- case SIOCGIFINDEX:
- case SIOCGIFNAME:
- case SIOCGIFCOUNT:
- case SIOCSIFHWBROADCAST:
- return(dev_ioctl(cmd,(void *) arg));
-
- case SIOCSIFADDR:
- case SIOCGIFADDR:
- return ec_dev_ioctl(sock, cmd, (void *)arg);
- break;
-
- default:
- return(dev_ioctl(cmd,(void *) arg));
- }
- /*NOTREACHED*/
- return 0;
-}
-
-static struct net_proto_family econet_family_ops = {
- PF_ECONET,
- econet_create
-};
-
-static struct proto_ops SOCKOPS_WRAPPED(econet_ops) = {
- PF_ECONET,
-
- econet_release,
- econet_bind,
- sock_no_connect,
- sock_no_socketpair,
- sock_no_accept,
- econet_getname,
- datagram_poll,
- econet_ioctl,
- sock_no_listen,
- sock_no_shutdown,
- sock_no_setsockopt,
- sock_no_getsockopt,
- sock_no_fcntl,
- econet_sendmsg,
- econet_recvmsg,
- sock_no_mmap
-};
-
-#include <linux/smp_lock.h>
-SOCKOPS_WRAP(econet, PF_ECONET);
-
-/*
- * Find the listening socket, if any, for the given data.
- */
-
-static struct sock *ec_listening_socket(unsigned char port, unsigned char
- station, unsigned char net)
-{
- struct sock *sk = econet_sklist;
-
- while (sk)
- {
- struct econet_opt *opt = sk->protinfo.af_econet;
- if ((opt->port == port || opt->port == 0) &&
- (opt->station == station || opt->station == 0) &&
- (opt->net == net || opt->net == 0))
- return sk;
-
- sk = sk->next;
- }
-
- return NULL;
-}
-
-#ifdef CONFIG_ECONET_AUNUDP
-
-/*
- * Send an AUN protocol response.
- */
-
-static void aun_send_response(__u32 addr, unsigned long seq, int code, int cb)
-{
- struct sockaddr_in sin;
- struct iovec iov;
- struct aunhdr ah;
- struct msghdr udpmsg;
- int err;
- mm_segment_t oldfs;
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_port = htons(AUN_PORT);
- sin.sin_addr.s_addr = addr;
-
- ah.code = code;
- ah.pad = 0;
- ah.port = 0;
- ah.cb = cb;
- ah.handle = seq;
-
- iov.iov_base = (void *)&ah;
- iov.iov_len = sizeof(ah);
-
- udpmsg.msg_name = (void *)&sin;
- udpmsg.msg_namelen = sizeof(sin);
- udpmsg.msg_iov = &iov;
- udpmsg.msg_iovlen = 1;
- udpmsg.msg_control = NULL;
- udpmsg.msg_controllen = 0;
- udpmsg.msg_flags=0;
-
- oldfs = get_fs(); set_fs(KERNEL_DS);
- err = sock_sendmsg(udpsock, &udpmsg, sizeof(ah));
- set_fs(oldfs);
-}
-
-/*
- * Handle incoming AUN packets. Work out if anybody wants them,
- * and send positive or negative acknowledgements as appropriate.
- */
-
-static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len)
-{
- struct ec_device *edev = edev_get(skb->dev);
- struct iphdr *ip = skb->nh.iph;
- unsigned char stn = ntohl(ip->saddr) & 0xff;
- struct sock *sk;
- struct sk_buff *newskb;
- struct ec_cb *eb;
- struct sockaddr_ec *sec;
-
- if (edev == NULL)
- return; /* Device not configured for AUN */
-
- if ((sk = ec_listening_socket(ah->port, stn, edev->net)) == NULL)
- goto bad; /* Nobody wants it */
-
- newskb = alloc_skb((len - sizeof(struct aunhdr) + 15) & ~15,
- GFP_ATOMIC);
- if (newskb == NULL)
- {
- printk(KERN_DEBUG "AUN: memory squeeze, dropping packet.\n");
- /* Send nack and hope sender tries again */
- goto bad;
- }
-
- eb = (struct ec_cb *)&newskb->cb;
- sec = (struct sockaddr_ec *)&eb->sec;
- memset(sec, 0, sizeof(struct sockaddr_ec));
- sec->sec_family = AF_ECONET;
- sec->type = ECTYPE_PACKET_RECEIVED;
- sec->port = ah->port;
- sec->cb = ah->cb;
- sec->addr.net = edev->net;
- sec->addr.station = stn;
-
- memcpy(skb_put(newskb, len - sizeof(struct aunhdr)), (void *)(ah+1),
- len - sizeof(struct aunhdr));
-
- if (sock_queue_rcv_skb(sk, newskb) < 0)
- {
- /* Socket is bankrupt. */
- kfree_skb(newskb);
- goto bad;
- }
-
- aun_send_response(ip->saddr, ah->handle, 3, 0);
- return;
-
-bad:
- aun_send_response(ip->saddr, ah->handle, 4, 0);
-}
-
-/*
- * Handle incoming AUN transmit acknowledgements. If the sequence
- * number matches something in our backlog then kill it and tell
- * the user. If the remote took too long to reply then we may have
- * dropped the packet already.
- */
-
-static void aun_tx_ack(unsigned long seq, int result)
-{
- struct sk_buff *skb;
- unsigned long flags;
- struct ec_cb *eb;
-
- spin_lock_irqsave(&aun_queue_lock, flags);
- skb = skb_peek(&aun_queue);
- while (skb && skb != (struct sk_buff *)&aun_queue)
- {
- struct sk_buff *newskb = skb->next;
- eb = (struct ec_cb *)&skb->cb;
- if (eb->seq == seq)
- goto foundit;
-
- skb = newskb;
- }
- spin_unlock_irqrestore(&aun_queue_lock, flags);
- printk(KERN_DEBUG "AUN: unknown sequence %ld\n", seq);
- return;
-
-foundit:
- tx_result(skb->sk, eb->cookie, result);
- skb_unlink(skb);
- spin_unlock_irqrestore(&aun_queue_lock, flags);
-}
-
-/*
- * Deal with received AUN frames - sort out what type of thing it is
- * and hand it to the right function.
- */
-
-static void aun_data_available(struct sock *sk, int slen)
-{
- int err;
- struct sk_buff *skb;
- unsigned char *data;
- struct aunhdr *ah;
- struct iphdr *ip;
- size_t len;
-
- while ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL) {
- if (err == -EAGAIN) {
- printk(KERN_ERR "AUN: no data available?!");
- return;
- }
- printk(KERN_DEBUG "AUN: recvfrom() error %d\n", -err);
- }
-
- data = skb->h.raw + sizeof(struct udphdr);
- ah = (struct aunhdr *)data;
- len = skb->len - sizeof(struct udphdr);
- ip = skb->nh.iph;
-
- switch (ah->code)
- {
- case 2:
- aun_incoming(skb, ah, len);
- break;
- case 3:
- aun_tx_ack(ah->handle, ECTYPE_TRANSMIT_OK);
- break;
- case 4:
- aun_tx_ack(ah->handle, ECTYPE_TRANSMIT_NOT_LISTENING);
- break;
-#if 0
- /* This isn't quite right yet. */
- case 5:
- aun_send_response(ip->saddr, ah->handle, 6, ah->cb);
- break;
-#endif
- default:
- printk(KERN_DEBUG "unknown AUN packet (type %d)\n", data[0]);
- }
-
- skb_free_datagram(sk, skb);
-}
-
-/*
- * Called by the timer to manage the AUN transmit queue. If a packet
- * was sent to a dead or nonexistent host then we will never get an
- * acknowledgement back. After a few seconds we need to spot this and
- * drop the packet.
- */
-
-
-static void ab_cleanup(unsigned long h)
-{
- struct sk_buff *skb;
- unsigned long flags;
-
- spin_lock_irqsave(&aun_queue_lock, flags);
- skb = skb_peek(&aun_queue);
- while (skb && skb != (struct sk_buff *)&aun_queue)
- {
- struct sk_buff *newskb = skb->next;
- struct ec_cb *eb = (struct ec_cb *)&skb->cb;
- if ((jiffies - eb->start) > eb->timeout)
- {
- tx_result(skb->sk, eb->cookie,
- ECTYPE_TRANSMIT_NOT_PRESENT);
- skb_unlink(skb);
- }
- skb = newskb;
- }
- spin_unlock_irqrestore(&aun_queue_lock, flags);
-
- mod_timer(&ab_cleanup_timer, jiffies + (HZ*2));
-}
-
-static int __init aun_udp_initialise(void)
-{
- int error;
- struct sockaddr_in sin;
-
- skb_queue_head_init(&aun_queue);
- spin_lock_init(&aun_queue_lock);
- init_timer(&ab_cleanup_timer);
- ab_cleanup_timer.expires = jiffies + (HZ*2);
- ab_cleanup_timer.function = ab_cleanup;
- add_timer(&ab_cleanup_timer);
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_port = htons(AUN_PORT);
-
- /* We can count ourselves lucky Acorn machines are too dim to
- speak IPv6. :-) */
- if ((error = sock_create(PF_INET, SOCK_DGRAM, 0, &udpsock)) < 0)
- {
- printk("AUN: socket error %d\n", -error);
- return error;
- }
-
- udpsock->sk->reuse = 1;
- udpsock->sk->allocation = GFP_ATOMIC; /* we're going to call it
- from interrupts */
-
- error = udpsock->ops->bind(udpsock, (struct sockaddr *)&sin,
- sizeof(sin));
- if (error < 0)
- {
- printk("AUN: bind error %d\n", -error);
- goto release;
- }
-
- udpsock->sk->data_ready = aun_data_available;
-
- return 0;
-
-release:
- sock_release(udpsock);
- udpsock = NULL;
- return error;
-}
-#endif
-
-static int econet_notifier(struct notifier_block *this, unsigned long msg, void *data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct ec_device *edev;
- unsigned long flags;
-
- switch (msg) {
- case NETDEV_UNREGISTER:
- /* A device has gone down - kill any data we hold for it. */
- spin_lock_irqsave(&edevlist_lock, flags);
- for (edev = edevlist; edev; edev = edev->next)
- {
- if (edev->dev == dev)
- {
- if (edev->prev)
- edev->prev->next = edev->next;
- else
- edevlist = edev->next;
- if (edev->next)
- edev->next->prev = edev->prev;
- kfree(edev);
- break;
- }
- }
- spin_unlock_irqrestore(&edevlist_lock, flags);
- break;
- }
-
- return NOTIFY_DONE;
-}
-
-struct notifier_block econet_netdev_notifier={
- econet_notifier,
- NULL,
- 0
-};
-
-#ifdef MODULE
-void cleanup_module(void)
-{
-#ifdef CONFIG_ECONET_AUNUDP
- del_timer(&ab_cleanup_timer);
- if (udpsock)
- sock_release(udpsock);
-#endif
- unregister_netdevice_notifier(&econet_netdev_notifier);
- sock_unregister(econet_family_ops.family);
- return;
-}
-
-int init_module(void)
-#else
-void __init econet_proto_init(struct net_proto *pro)
-#endif
-{
- spin_lock_init(&edevlist_lock);
- spin_lock_init(&aun_queue_lock);
- /* Stop warnings from happening on UP systems. */
- (void)edevlist_lock;
- (void)aun_queue_lock;
- sock_register(&econet_family_ops);
-#ifdef CONFIG_ECONET_AUNUDP
- aun_udp_initialise();
-#endif
- register_netdevice_notifier(&econet_netdev_notifier);
-#ifdef MODULE
- return 0;
-#endif
-}
--- /dev/null
+/*
+ * An implementation of the Acorn Econet and AUN protocols.
+ * Philip Blundell <philb@gnu.org>
+ *
+ * Fixes:
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+
+ctl_table econet_table[] = {
+ {0}
+};
+
+static struct ctl_table_header *econet_sysctl_header;
+
+static ctl_table econet_net_table[] = {
+ {NET_ECONET, "econet", NULL, 0, 0555, econet_table},
+ {0}
+};
+
+static ctl_table econet_root_table[] = {
+ {CTL_NET, "net", NULL, 0, 0555, econet_net_table},
+ {0}
+};
+
+void econet_sysctl_register(void)
+{
+ econet_sysctl_header = register_sysctl_table(econet_root_table, 0);
+}
+
+#ifdef MODULE
+void econet_sysctl_unregister(void)
+{
+ unregister_sysctl_table(econet_sysctl_header);
+}
+#endif /* MODULE */
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp.c,v 1.163 2000/02/08 21:27:13 davem Exp $
+ * Version: $Id: tcp.c,v 1.164 2000/03/08 19:36:40 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
tp->listen_opt = lopt;
write_unlock_bh(&tp->syn_wait_lock);
+ /* There is race window here: we announce ourselves listening,
+ * but this transition is still not validated by get_port().
+ * It is OK, because this socket enters to hash table only
+ * after validation is complete.
+ */
sk->state = TCP_LISTEN;
- if (sk->num == 0) {
- if (sk->prot->get_port(sk, 0) != 0) {
- sk->state = TCP_CLOSE;
- write_lock_bh(&tp->syn_wait_lock);
- tp->listen_opt = NULL;
- write_unlock_bh(&tp->syn_wait_lock);
- kfree(lopt);
- return -EAGAIN;
- }
+ if (sk->prot->get_port(sk, sk->num) == 0) {
sk->sport = htons(sk->num);
- } else {
- if (sk->prev)
- ((struct tcp_bind_bucket*)sk->prev)->fastreuse = 0;
- }
- sk_dst_reset(sk);
- sk->prot->hash(sk);
- sk->socket->flags |= SO_ACCEPTCON;
- sk->write_space = tcp_listen_write_space;
+ sk->write_space = tcp_listen_write_space;
+ sk_dst_reset(sk);
+ sk->prot->hash(sk);
+ sk->socket->flags |= SO_ACCEPTCON;
- return 0;
+ return 0;
+ }
+
+ sk->state = TCP_CLOSE;
+ write_lock_bh(&tp->syn_wait_lock);
+ tp->listen_opt = NULL;
+ write_unlock_bh(&tp->syn_wait_lock);
+ kfree(lopt);
+ return -EADDRINUSE;
}
/*
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp_ipv4.c,v 1.200 2000/02/11 22:27:26 davem Exp $
+ * Version: $Id: tcp_ipv4.c,v 1.201 2000/03/08 19:36:42 davem Exp $
*
* IPv4 specific functions
*
break;
}
if (tb != NULL && tb->owners != NULL) {
- if (tb->fastreuse != 0 && sk->reuse != 0) {
+ if (tb->fastreuse != 0 && sk->reuse != 0 && sk->state != TCP_LISTEN) {
goto success;
} else {
struct sock *sk2 = tb->owners;
int sk_reuse = sk->reuse;
for( ; sk2 != NULL; sk2 = sk2->bind_next) {
- if (sk->bound_dev_if == sk2->bound_dev_if) {
+ if (sk != sk2 &&
+ sk->bound_dev_if == sk2->bound_dev_if) {
if (!sk_reuse ||
!sk2->reuse ||
sk2->state == TCP_LISTEN) {
tb->fastreuse = 0;
success:
sk->num = snum;
- if ((sk->bind_next = tb->owners) != NULL)
- tb->owners->bind_pprev = &sk->bind_next;
- tb->owners = sk;
- sk->bind_pprev = &tb->owners;
- sk->prev = (struct sock *) tb;
+ if (sk->prev == NULL) {
+ if ((sk->bind_next = tb->owners) != NULL)
+ tb->owners->bind_pprev = &sk->bind_next;
+ tb->owners = sk;
+ sk->bind_pprev = &tb->owners;
+ sk->prev = (struct sock *) tb;
+ } else {
+ BUG_TRAP(sk->prev == (struct sock *) tb);
+ }
ret = 0;
fail_unlock:
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: tcp_ipv6.c,v 1.120 2000/02/27 19:51:49 davem Exp $
+ * $Id: tcp_ipv6.c,v 1.121 2000/03/08 19:36:47 davem Exp $
*
* Based on:
* linux/net/ipv4/tcp.c
break;
}
if (tb != NULL && tb->owners != NULL) {
- if (tb->fastreuse != 0 && sk->reuse != 0) {
+ if (tb->fastreuse != 0 && sk->reuse != 0 && sk->state != TCP_LISTEN) {
goto success;
} else {
struct sock *sk2 = tb->owners;
/* We must walk the whole port owner list in this case. -DaveM */
for( ; sk2 != NULL; sk2 = sk2->bind_next) {
- if (sk->bound_dev_if == sk2->bound_dev_if) {
+ if (sk != sk2 &&
+ sk->bound_dev_if == sk2->bound_dev_if) {
if (!sk_reuse ||
!sk2->reuse ||
sk2->state == TCP_LISTEN) {
success:
sk->num = snum;
- if ((sk->bind_next = tb->owners) != NULL)
- tb->owners->bind_pprev = &sk->bind_next;
- tb->owners = sk;
- sk->bind_pprev = &tb->owners;
- sk->prev = (struct sock *) tb;
+ if (sk->prev == NULL) {
+ if ((sk->bind_next = tb->owners) != NULL)
+ tb->owners->bind_pprev = &sk->bind_next;
+ tb->owners = sk;
+ sk->bind_pprev = &tb->owners;
+ sk->prev = (struct sock *) tb;
+ } else {
+ BUG_TRAP(sk->prev == (struct sock *) tb);
+ }
ret = 0;
fail_unlock:
extern ctl_table tr_table[];
#endif
+#ifdef CONFIG_ECONET
+extern ctl_table econet_table[];
+#endif
+
ctl_table net_table[] = {
{NET_CORE, "core", NULL, 0, 0555, core_table},
#ifdef CONFIG_UNIX
#endif
#ifdef CONFIG_TR
{NET_TR, "token-ring", NULL, 0, 0555, tr_table},
+#endif
+#ifdef CONFIG_ECONET
+ {NET_ECONET, "econet", NULL, 0, 0555, econet_table},
#endif
{0}
};